Skip to content

Commit

Permalink
optimization: support burn onbehalf of other and uniform the signatur…
Browse files Browse the repository at this point in the history
…e of mint and burn function (#14)
  • Loading branch information
chefburger authored Apr 12, 2024
1 parent b41cd40 commit a3cf6ea
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
102920
102898
Original file line number Diff line number Diff line change
@@ -1 +1 @@
101419
101660
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#Vault.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7180
7314
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#collectFee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
25531
25576
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#registerPoolManager.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24506
24484
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#testLock_NoOp.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
11232
11327
6 changes: 3 additions & 3 deletions src/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ contract Vault is IVault, VaultToken, Ownable {
}

/// @inheritdoc IVault
function mint(Currency currency, address to, uint256 amount) external override isLocked {
function mint(address to, Currency currency, uint256 amount) external override isLocked {
SettlementGuard.accountDelta(msg.sender, currency, amount.toInt128());
_mint(to, currency, amount);
}
Expand Down Expand Up @@ -161,9 +161,9 @@ contract Vault is IVault, VaultToken, Ownable {
}

/// @inheritdoc IVault
function burn(Currency currency, uint256 amount) external override isLocked {
function burn(address from, Currency currency, uint256 amount) external override isLocked {
SettlementGuard.accountDelta(msg.sender, currency, -(amount.toInt128()));
_burn(msg.sender, currency, amount);
_burnFrom(from, currency, amount);
}

/// @inheritdoc IVault
Expand Down
9 changes: 9 additions & 0 deletions src/VaultToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,13 @@ abstract contract VaultToken is IVaultToken {

emit Transfer(msg.sender, sender, address(0), currency, amount);
}

function _burnFrom(address from, Currency currency, uint256 amount) internal virtual {
if (msg.sender != from && !isOperator[from][msg.sender]) {
uint256 allowed = allowance[from][msg.sender][currency];
if (allowed != type(uint256).max) allowance[from][msg.sender][currency] = allowed - amount;
}

_burn(from, currency, amount);
}
}
4 changes: 2 additions & 2 deletions src/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ interface IVault is IVaultToken {
function collectFee(Currency currency, uint256 amount, address recipient) external;

/// @notice Called by the user to store surplus tokens in the vault
function mint(Currency currency, address to, uint256 amount) external;
function mint(address to, Currency currency, uint256 amount) external;

/// @notice Called by the user to use surplus tokens for payment settlement
function burn(Currency currency, uint256 amount) external;
function burn(address from, Currency currency, uint256 amount) external;
}
8 changes: 4 additions & 4 deletions test/pool-bin/helpers/BinSwapHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ contract BinSwapHelper {
} else {
// the received hook on this transfer will burn the tokens
vault.transferFrom(data.sender, address(this), data.key.currency0, uint128(delta.amount0()));
vault.burn(data.key.currency0, uint128(delta.amount0()));
vault.burn(address(this), data.key.currency0, uint128(delta.amount0()));
}
}
if (delta.amount1() < 0) {
if (data.testSettings.withdrawTokens) {
vault.take(data.key.currency1, data.sender, uint128(-delta.amount1()));
} else {
vault.mint(data.key.currency1, data.sender, uint128(-delta.amount1()));
vault.mint(data.sender, data.key.currency1, uint128(-delta.amount1()));
}
}
} else {
Expand All @@ -107,14 +107,14 @@ contract BinSwapHelper {
} else {
// the received hook on this transfer will burn the tokens
vault.transferFrom(data.sender, address(this), data.key.currency1, uint128(delta.amount1()));
vault.burn(data.key.currency1, uint128(delta.amount1()));
vault.burn(address(this), data.key.currency1, uint128(delta.amount1()));
}
}
if (delta.amount0() < 0) {
if (data.testSettings.withdrawTokens) {
vault.take(data.key.currency0, data.sender, uint128(-delta.amount0()));
} else {
vault.mint(data.key.currency0, data.sender, uint128(-delta.amount0()));
vault.mint(data.sender, data.key.currency0, uint128(-delta.amount0()));
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions test/pool-cl/helpers/CLPoolManagerRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,14 @@ contract CLPoolManagerRouter {
} else {
// the received hook on this transfer will burn the tokens
vault.transferFrom(data.sender, address(this), data.key.currency0, uint128(delta.amount0()));
vault.burn(data.key.currency0, uint128(delta.amount0()));
vault.burn(address(this), data.key.currency0, uint128(delta.amount0()));
}
}
if (delta.amount1() < 0) {
if (data.testSettings.withdrawTokens) {
vault.take(data.key.currency1, data.sender, uint128(-delta.amount1()));
} else {
vault.mint(data.key.currency1, data.sender, uint128(-delta.amount1()));
vault.mint(data.sender, data.key.currency1, uint128(-delta.amount1()));
}
}
} else {
Expand All @@ -183,14 +183,14 @@ contract CLPoolManagerRouter {
} else {
// the received hook on this transfer will burn the tokens
vault.transferFrom(data.sender, address(this), data.key.currency1, uint128(delta.amount1()));
vault.burn(data.key.currency1, uint128(delta.amount1()));
vault.burn(address(this), data.key.currency1, uint128(delta.amount1()));
}
}
if (delta.amount0() < 0) {
if (data.testSettings.withdrawTokens) {
vault.take(data.key.currency0, data.sender, uint128(-delta.amount0()));
} else {
vault.mint(data.key.currency0, data.sender, uint128(-delta.amount0()));
vault.mint(data.sender, data.key.currency0, uint128(-delta.amount0()));
}
}
}
Expand Down
20 changes: 14 additions & 6 deletions test/vault/FakePoolManagerRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,30 @@ contract FakePoolManagerRouter {
// mint
uint256 amt = poolKey.currency0.balanceOf(address(vault));
vault.settle(poolKey.currency0);
vault.mint(poolKey.currency0, address(this), amt);
vault.mint(address(this), poolKey.currency0, amt);
} else if (data[0] == 0x14) {
// mint to someone else, poolKey.currency1 for example
uint256 amt = poolKey.currency0.balanceOf(address(vault));
vault.settle(poolKey.currency0);
vault.mint(poolKey.currency0, Currency.unwrap(poolKey.currency1), amt);
vault.mint(Currency.unwrap(poolKey.currency1), poolKey.currency0, amt);
} else if (data[0] == 0x15) {
// burn

uint256 amt = poolKey.currency0.balanceOf(address(vault));
vault.settle(poolKey.currency0);
vault.mint(poolKey.currency0, address(this), amt);
vault.mint(address(this), poolKey.currency0, amt);

vault.burn(poolKey.currency0, amt);
vault.burn(address(this), poolKey.currency0, amt);
vault.take(poolKey.currency0, address(this), amt);
} else if (data[0] == 0x16) {
// burn half if possible

uint256 amt = poolKey.currency0.balanceOf(address(vault));
vault.settle(poolKey.currency0);

vault.mint(poolKey.currency0, address(this), amt);
vault.mint(address(this), poolKey.currency0, amt);

vault.burn(poolKey.currency0, amt / 2);
vault.burn(address(this), poolKey.currency0, amt / 2);
vault.take(poolKey.currency0, address(this), amt / 2);
} else if (data[0] == 0x17) {
// settle ETH
Expand All @@ -140,6 +140,14 @@ contract FakePoolManagerRouter {
/// try to call settleAndMintRefund should not revert
vault.settleAndMintRefund(poolKey.currency1, address(this));
vault.take(poolKey.currency1, address(this), 3 ether);
} else if (data[0] == 0x20) {
// burn on behalf of someone else
uint256 amt = poolKey.currency0.balanceOf(address(vault));
vault.settle(poolKey.currency0);
vault.mint(address(0x01), poolKey.currency0, amt);

vault.burn(address(0x01), poolKey.currency0, amt);
vault.take(poolKey.currency0, address(this), amt);
}

return "";
Expand Down
68 changes: 68 additions & 0 deletions test/vault/Vault.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,74 @@ contract VaultTest is Test, GasSnapshot {
assertEq(vault.balanceOf(address(fakePoolManagerRouter), currency0), amt - amt / 2);
}

function testVaultFuzz_burnFrom_withoutApprove(uint256 amt) public {
amt = bound(amt, 0, 10 ether);
// make sure router has enough tokens
currency0.transfer(address(vault), amt);

if (amt != 0) {
vm.expectRevert();
}

vm.startPrank(address(fakePoolManagerRouter));
vault.lock(hex"20");
vm.stopPrank();

assertEq(vault.balanceOf(address(fakePoolManagerRouter), currency0), 0);
}

function testVaultFuzz_burnFrom_withApprove(uint256 amt) public {
amt = bound(amt, 0, 10 ether);
// make sure router has enough tokens
currency0.transfer(address(vault), amt);

vm.prank(address(0x01));
vault.approve(address(fakePoolManagerRouter), currency0, amt);
assertEq(vault.allowance(address(0x01), address(fakePoolManagerRouter), currency0), amt);

vm.startPrank(address(fakePoolManagerRouter));
vault.lock(hex"20");
vm.stopPrank();

assertEq(vault.balanceOf(address(fakePoolManagerRouter), currency0), 0);
assertEq(vault.allowance(address(0x01), address(fakePoolManagerRouter), currency0), 0);

// approve max
{
currency0.transfer(address(vault), amt);

vm.prank(address(0x01));
vault.approve(address(fakePoolManagerRouter), currency0, type(uint256).max);

vm.startPrank(address(fakePoolManagerRouter));
vault.lock(hex"20");
vm.stopPrank();

assertEq(vault.balanceOf(address(fakePoolManagerRouter), currency0), 0);
assertEq(vault.allowance(address(0x01), address(fakePoolManagerRouter), currency0), type(uint256).max);
}

// operator
{
currency0.transfer(address(vault), amt);

// set a insufficient allowance
vm.prank(address(0x01));
vault.approve(address(fakePoolManagerRouter), currency0, 1);

vm.prank(address(0x01));
vault.setOperator(address(fakePoolManagerRouter), true);

vm.startPrank(address(fakePoolManagerRouter));
vault.lock(hex"20");
vm.stopPrank();

assertEq(vault.balanceOf(address(fakePoolManagerRouter), currency0), 0);
// transfer from operator don't consume allowance
assertEq(vault.allowance(address(0x01), address(fakePoolManagerRouter), currency0), 1);
}
}

function testLockInSufficientBalanceWhenMoreThanOnePoolManagers() public {
// router => vault.lock
// vault.lock => periphery.lockAcquired
Expand Down
8 changes: 4 additions & 4 deletions test/vault/VaultInvariant.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ contract VaultPoolManager is Test {
BalanceDelta delta = toBalanceDelta(-(int128(action.amt0)), -(int128(action.amt1)));
vault.accountPoolBalanceDelta(poolKey, delta, address(this));

vault.mint(currency0, address(this), action.amt0);
vault.mint(currency1, address(this), action.amt1);
vault.mint(address(this), currency0, action.amt0);
vault.mint(address(this), currency1, action.amt1);
totalMintedCurrency0 += action.amt0;
totalMintedCurrency1 += action.amt1;
} else if (action.actionType == ActionType.Settle) {
Expand Down Expand Up @@ -212,8 +212,8 @@ contract VaultPoolManager is Test {
BalanceDelta delta = toBalanceDelta(int128(action.amt0), int128(action.amt1));
vault.accountPoolBalanceDelta(poolKey, delta, address(this));

vault.burn(currency0, action.amt0);
vault.burn(currency1, action.amt1);
vault.burn(address(this), currency0, action.amt0);
vault.burn(address(this), currency1, action.amt1);
totalMintedCurrency0 -= action.amt0;
totalMintedCurrency1 -= action.amt1;
}
Expand Down
6 changes: 3 additions & 3 deletions test/vault/VaultReentrancy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ contract VaultReentrancyTest is Test, TokenFixture {
vm.prank(callerAddr);
vault.settle(currency0);
vm.prank(callerAddr);
vault.mint(currency0, address(callerAddr), 1 ether);
vault.mint(address(callerAddr), currency0, 1 ether);

vaultTokenBalance[i] = vault.balanceOf(callerAddr, currency0);
}
Expand Down Expand Up @@ -194,14 +194,14 @@ contract VaultReentrancyTest is Test, TokenFixture {
} else if (i % 6 == 2) {
// mint
vm.prank(callerAddr);
vault.mint(currency0, callerAddr, paidAmount);
vault.mint(callerAddr, currency0, paidAmount);

currencyDelta[i % SETTLERS_AMOUNT] += int256(paidAmount);
vaultTokenBalance[i % SETTLERS_AMOUNT] += paidAmount;
} else if (i % 6 == 3) {
// burn
vm.prank(callerAddr);
vault.burn(currency0, paidAmount);
vault.burn(callerAddr, currency0, paidAmount);

currencyDelta[i % SETTLERS_AMOUNT] -= int256(paidAmount);
vaultTokenBalance[i % SETTLERS_AMOUNT] -= paidAmount;
Expand Down

0 comments on commit a3cf6ea

Please sign in to comment.