From b4b4ca13b87dc607893e381e3adec1a7db763722 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 18:52:22 +0400 Subject: [PATCH 01/25] feat: add toBytes8 --- contracts/libraries/AssemblyLib.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/libraries/AssemblyLib.sol b/contracts/libraries/AssemblyLib.sol index 222d3734..8c8df9bb 100644 --- a/contracts/libraries/AssemblyLib.sol +++ b/contracts/libraries/AssemblyLib.sol @@ -118,6 +118,14 @@ library AssemblyLib { } } + function toBytes8(bytes memory raw) internal pure returns (bytes8 data) { + assembly { + data := mload(add(raw, 32)) + let shift := mul(sub(8, mload(raw)), 8) + data := shr(shift, data) + } + } + /** * @dev Safely casts an unsigned 128-bit integer into a signed 128-bit integer. * Reverts on overflow. From 6d3aa1dacdf044391881edc38daef7e877a07727 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 18:52:42 +0400 Subject: [PATCH 02/25] feat: update encodeClaim and decodeClaim --- contracts/libraries/FVMLib.sol | 35 ++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index 55f09089..ef711cdd 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -137,26 +137,37 @@ function decodeCreatePair(bytes calldata data) pure returns (address tokenAsset, } /** - * @dev Encodes a claim operation - * +--------------------------------------------------------------------+ - * | Description | CLAIM | poolId | power0 | amount0 | power1 | amount1 | - * +--------------------------------------------------------------------+ - * | Size (byte) | 1 | 8 | 1 | 16 | 1 | 16 | - * +--------------------------------------------------------------------+ - * | Index | 0 | 1 - 9 | 9 | 10 - 26 | 26 | 27 - 43 | - * +--------------------------------------------------------------------+ + * @dev Encodes a claim operation. + * + * FIXME: This function is not optimized! Using `encodePacked` is not ideal + * because it preserves all the trailing zeros for each type. + * An improved version should be made to reduce the calldata size. */ function encodeClaim(uint64 poolId, uint128 fee0, uint128 fee1) pure returns (bytes memory data) { (uint8 powerFee0, uint128 baseFee0) = AssemblyLib.fromAmount(fee0); (uint8 powerFee1, uint128 baseFee1) = AssemblyLib.fromAmount(fee1); - return abi.encodePacked(CLAIM, poolId, powerFee0, baseFee0, powerFee1, baseFee1); + return abi.encodePacked( + CLAIM, + uint8(10), // pointer to pointer1 + poolId, + uint8(28), // pointer to fee1 + powerFee0, + baseFee0, + powerFee1, + baseFee1 + ); } +/** + * @dev Decodes a claim operation + */ function decodeClaim(bytes calldata data) pure returns (uint64 poolId, uint128 fee0, uint128 fee1) { - poolId = uint64(bytes8(data[1:9])); - fee0 = AssemblyLib.toAmount(data[9:26]); - fee1 = AssemblyLib.toAmount(data[26:43]); + uint8 pointer0 = uint8(bytes1(data[1])); + poolId = uint64(AssemblyLib.toBytes8(data[2:pointer0])); + uint8 pointer1 = uint8(bytes1(data[pointer0])); + fee0 = AssemblyLib.toAmount(data[pointer0 + 1:pointer1]); + fee1 = AssemblyLib.toAmount(data[pointer1:data.length]); } function encodeCreatePool( From 05c606a07fc4c4e64c4c16bc9bcad95d7cfb74ad Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 18:53:17 +0400 Subject: [PATCH 03/25] feat: add test_decodeClaim --- test/TestFVMLib.t.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index 72743e7c..807c29fd 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -72,4 +72,13 @@ contract TestFVMLib is Test { assertEq(fee0, fee0_); assertEq(fee1, fee1_); } + + function test_decodeClaim() public { + bytes memory data = hex"1004002a0806052b03147bd7"; + (uint64 poolId, uint128 fee0, uint128 fee1) = target.doDecodeClaim(data); + + assertEq(poolId, 42); + assertEq(fee0, 1323 * 10 ** 6); + assertEq(fee1, 1342423 * 10 ** 3); + } } From f5baabc12d3598186c30fcbaf39a4e57419c3269 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 19:49:11 +0400 Subject: [PATCH 04/25] feat: add utils encoder --- encoder.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 encoder.js diff --git a/encoder.js b/encoder.js new file mode 100644 index 00000000..69059824 --- /dev/null +++ b/encoder.js @@ -0,0 +1,58 @@ +function toHex(input) { + return input.toString(16).length % 2 == 0 ? input.toString(16) : `0${input.toString(16)}`; +} + +function fromAmount(amount) { + let power = 0; + + while (amount % 10n == 0) { + amount /= 10n; + ++power; + } + + console.log(amount, power); + + return `${toHex(power)}${toHex(amount)}`; +} + +function encodeClaim(poolId, fee0, fee1) { + let data = ''; + + let encodedPoolId = toHex(poolId); + let encodedFee0 = fromAmount(fee0); + let encodedFee1 = fromAmount(fee1); + + data += toHex(16); + data += toHex((data.length + encodedPoolId.length) / 2 + 1); + data += encodedPoolId; + data += toHex((data.length + encodedFee0.length) / 2 + 1); + data += encodedFee0; + data += encodedFee1; + + return data; +} + +function encodeSwap( + useMax, + sellAsset, + poolId, + amount0, + amount1, +) { + let data = ''; + + let encodedPoolId = toHex(poolId); + let encodedAmount0 = fromAmount(amount0); + let encodedAmount1 = fromAmount(amount1); + + data += useMax ? '1' : '0'; + data += '5'; + data += sellAsset ? '01' : '00'; + data += toHex((data.length + encodedPoolId.length) / 2 + 1); + data += encodedPoolId; + data += toHex((data.length + encodedAmount0.length) / 2 + 1); + data += encodedAmount0; + data += encodedAmount1; + + return data; +} From beff891722c86e6956545ae33adfe42d75786137 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 19:49:27 +0400 Subject: [PATCH 05/25] feat: update encodeSwap, decodeSwap --- contracts/libraries/FVMLib.sol | 35 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index ef711cdd..d36e0251 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -235,24 +235,29 @@ function decodeDeallocate(bytes calldata data) pure returns (uint8 useMax, uint6 /** * @dev Encodes a swap operation - * +-------------------------------------------------------------------------------+ - * | Description | SWAP | poolId | power0 | amount0 | power1 | amount1 | sellAsset | - * +-------------+------+--------+--------+---------+--------+---------+-----------+ - * | Size (byte) | 1 | 8 | 1 | 16 | 1 | 16 | 1 | - * +-------------+------+--------+--------+---------+--------+---------+-----------+ - * | Index | 0 | 1 - 9 | 9 | 10 - 26 | 26 | 27 - 43 | 43 | - * +-------------------------------------------------------------------------------+ + * FIXME: Same issue as `encodeClaim`... This function is not optimized! */ function encodeSwap( uint8 useMax, uint64 poolId, - uint8 power0, uint128 amount0, - uint8 power1, uint128 amount1, uint8 sellAsset ) pure returns (bytes memory data) { - data = abi.encodePacked(AssemblyLib.pack(bytes1(useMax), SWAP), poolId, power0, amount0, power1, amount1, sellAsset); + (uint8 power0, uint128 base0) = AssemblyLib.fromAmount(amount0); + (uint8 power1, uint128 base1) = AssemblyLib.fromAmount(amount1); + + data = abi.encodePacked( + AssemblyLib.pack(bytes1(useMax), SWAP), + sellAsset, + uint8(11), // pointer to pointer1 + poolId, + uint8(29), + power0, + base0, + power1, + base1 + ); } function decodeSwap(bytes calldata data) @@ -260,8 +265,10 @@ function decodeSwap(bytes calldata data) returns (uint8 useMax, uint64 poolId, uint128 input, uint128 output, uint8 sellAsset) { useMax = uint8(data[0] >> 4); - poolId = uint64(bytes8(data[1:9])); - input = AssemblyLib.toAmount(data[9:26]); - output = AssemblyLib.toAmount(data[26:43]); - sellAsset = uint8(data[data.length - 1]); + sellAsset = uint8(data[1]); + uint8 pointer0 = uint8(data[2]); + poolId = uint64(AssemblyLib.toBytes8(data[3:pointer0])); + uint8 pointer1 = uint8(data[pointer0]); + input = AssemblyLib.toAmount(data[pointer0 + 1:pointer1]); + output = AssemblyLib.toAmount(data[pointer1:data.length]); } From 7537712cdcd53fe8af35fe525802c0c5acc55a44 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 19:49:40 +0400 Subject: [PATCH 06/25] test: update all swap related tests --- test/Setup.sol | 4 +-- test/TestFVMLib.t.sol | 52 ++++++++++++++++++++++++----------- test/TestPortfolioClaim.t.sol | 4 +-- test/TestPortfolioSwap.t.sol | 2 +- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/test/Setup.sol b/test/Setup.sol index 6ead7d6d..46c94881 100644 --- a/test/Setup.sol +++ b/test/Setup.sol @@ -238,7 +238,7 @@ contract Setup is Test { modifier swapSome(uint128 amt, bool sellAsset) { uint128 amtOut = subject().getAmountOut(ghost().poolId, sellAsset, amt).safeCastTo128(); subject().multiprocess( - FVM.encodeSwap(uint8(0), ghost().poolId, 0x0, amt, 0x0, amtOut, uint8(sellAsset ? 1 : 0)) + FVM.encodeSwap(uint8(0), ghost().poolId, amt, amtOut, uint8(sellAsset ? 1 : 0)) ); _; } @@ -250,7 +250,7 @@ contract Setup is Test { : amtOut - uint256(-amtOutDelta).safeCastTo128(); subject().multiprocess( - FVM.encodeSwap(uint8(0), ghost().poolId, 0x0, amt, 0x0, amtOut, uint8(sellAsset ? 1 : 0)) + FVM.encodeSwap(uint8(0), ghost().poolId, amt, amtOut, uint8(sellAsset ? 1 : 0)) ); _; } diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index 807c29fd..da76acbf 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -9,13 +9,11 @@ contract FVMLibTarget is Test { function doEncodeSwap( uint8 useMax, uint64 poolId, - uint8 power0, uint128 amount0, - uint8 power1, uint128 amount1, uint8 sellAsset ) external pure returns (bytes memory) { - return encodeSwap(useMax, poolId, power0, amount0, power1, amount1, sellAsset); + return encodeSwap(useMax, poolId, amount0, amount1, sellAsset); } function doDecodeSwap(bytes calldata data) @@ -41,28 +39,50 @@ contract TestFVMLib is Test { function testFuzz_encodeSwap( bool useMax, uint64 poolId, - uint8 power0, - uint64 amount0, - uint8 power1, - uint64 amount1, + uint128 amount0, + uint128 amount1, bool sellAsset ) public { - vm.assume(power0 <= 18); - vm.assume(power1 <= 18); - bytes memory data = target.doEncodeSwap( - useMax ? uint8(1) : uint8(0), poolId, power0, amount0, power1, amount1, sellAsset ? uint8(1) : uint8(0) + useMax ? uint8(1) : uint8(0), + poolId, + amount0, + amount1, + sellAsset ? uint8(1) : uint8(0) ); + console.logBytes(data); + (uint8 useMax_, uint64 poolId_, uint128 input_, uint128 output_, uint8 sellAsset_) = target.doDecodeSwap(data); assertEq(useMax ? uint8(1) : uint8(0), useMax_, "Wrong use max"); assertEq(poolId, poolId_); - assertEq(amount0 * 10 ** power0, input_); - assertEq(amount1 * 10 ** power1, output_); + assertEq(amount0, input_); + assertEq(amount1, output_); assertEq(sellAsset ? uint8(1) : uint8(0), sellAsset_, "Wrong sellAsset"); } + function test_decodeSwap() public { + // 0x15000000000000002a0600000000000000000000000000000008120000000000000000000000000000000401 + // 0x0500042a0709081204 + + bytes memory data = hex"0500042a0709081204"; + + ( + uint8 useMax, + uint64 poolId, + uint128 input, + uint128 output, + uint8 sellAsset + ) = target.doDecodeSwap(data); + + assertEq(useMax, 0); + assertEq(poolId, 42); + assertEq(input, 8000 * 10 ** 6); + assertEq(output, 4 * 10 ** 18); + assertEq(sellAsset, 0); + } + function testFuzz_encodeClaim(uint64 poolId, uint128 fee0, uint128 fee1) public { bytes memory data = target.doEncodeClaim(poolId, fee0, fee1); @@ -74,11 +94,11 @@ contract TestFVMLib is Test { } function test_decodeClaim() public { - bytes memory data = hex"1004002a0806052b03147bd7"; + bytes memory data = hex"10032a0609081204"; (uint64 poolId, uint128 fee0, uint128 fee1) = target.doDecodeClaim(data); assertEq(poolId, 42); - assertEq(fee0, 1323 * 10 ** 6); - assertEq(fee1, 1342423 * 10 ** 3); + assertEq(fee0, 8000 * 10 ** 6); + assertEq(fee1, 4 * 10 ** 18); } } diff --git a/test/TestPortfolioClaim.t.sol b/test/TestPortfolioClaim.t.sol index 0e789f85..93555a5a 100644 --- a/test/TestPortfolioClaim.t.sol +++ b/test/TestPortfolioClaim.t.sol @@ -155,7 +155,7 @@ contract TestPortfolioClaim is Setup { uint128 amountIn = 1500; uint128 amountOut = (subject().getAmountOut(ghost().poolId, true, amountIn) - 10).safeCastTo128(); // Subtract // small amount to get positive invariant growth (not an optimal trade). - subject().multiprocess(FVMLib.encodeSwap(uint8(0), ghost().poolId, 0x0, amountIn, 0x0, amountOut, uint8(1))); // trade + subject().multiprocess(FVMLib.encodeSwap(uint8(0), ghost().poolId, amountIn, amountOut, uint8(1))); // trade // in 1500 * 1% fee = 15 / 12_000 = 0.00125 fee growth per liquidity // save the total fee growth for the asset per liquidity. @@ -185,7 +185,7 @@ contract TestPortfolioClaim is Setup { uint128 amountIn = 10_000 wei; // 1% fees will generate 100 wei of asset fee growth uint128 amountOut = (subject().getAmountOut(ghost().poolId, true, amountIn) - 10).safeCastTo128(); // Subtract // small amount to get positive invariant growth (not an optimal trade). - subject().multiprocess(FVMLib.encodeSwap(uint8(0), ghost().poolId, 0x0, amountIn, 0x0, amountOut, uint8(1))); + subject().multiprocess(FVMLib.encodeSwap(uint8(0), ghost().poolId, amountIn, amountOut, uint8(1))); // withdraw all the liquidity after the swap, to sync fees. subject().multiprocess(FVMLib.encodeDeallocate(uint8(0), ghost().poolId, 0x0, delLiquidity)); diff --git a/test/TestPortfolioSwap.t.sol b/test/TestPortfolioSwap.t.sol index 68937939..b15f37b3 100644 --- a/test/TestPortfolioSwap.t.sol +++ b/test/TestPortfolioSwap.t.sol @@ -19,7 +19,7 @@ contract TestPortfolioSwap is Setup { uint256 prev = ghost().balance(address(this), ghost().quote().to_addr()); subject().multiprocess( - FVMLib.encodeSwap(uint8(0), ghost().poolId, 0x0, amtIn, 0x0, amtOut, uint8(sellAsset ? 1 : 0)) + FVMLib.encodeSwap(uint8(0), ghost().poolId, amtIn, amtOut, uint8(sellAsset ? 1 : 0)) ); uint256 post = ghost().balance(address(this), ghost().quote().to_addr()); From 7783f94a651cb12637f18c71928dc68185f65a92 Mon Sep 17 00:00:00 2001 From: clemlak Date: Thu, 2 Mar 2023 19:53:48 +0400 Subject: [PATCH 07/25] test: remove unused comments --- test/TestFVMLib.t.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index da76acbf..81925b47 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -63,9 +63,6 @@ contract TestFVMLib is Test { } function test_decodeSwap() public { - // 0x15000000000000002a0600000000000000000000000000000008120000000000000000000000000000000401 - // 0x0500042a0709081204 - bytes memory data = hex"0500042a0709081204"; ( From f2d539123c54fb394439f1c1a4b7f23189dbe4b1 Mon Sep 17 00:00:00 2001 From: clemlak Date: Mon, 6 Mar 2023 19:11:35 +0400 Subject: [PATCH 08/25] test: remove encode funcs in test target contract --- test/TestFVMLib.t.sol | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index 81925b47..ba5eb27c 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -6,17 +6,7 @@ import "forge-std/Vm.sol"; import "contracts/libraries/FVMLib.sol"; contract FVMLibTarget is Test { - function doEncodeSwap( - uint8 useMax, - uint64 poolId, - uint128 amount0, - uint128 amount1, - uint8 sellAsset - ) external pure returns (bytes memory) { - return encodeSwap(useMax, poolId, amount0, amount1, sellAsset); - } - - function doDecodeSwap(bytes calldata data) + function decodeSwap_(bytes calldata data) external pure returns (uint8 useMax, uint64 poolId, uint128 input, uint128 output, uint8 sellAsset) @@ -24,12 +14,12 @@ contract FVMLibTarget is Test { return decodeSwap(data); } - function doEncodeClaim(uint64 poolId, uint128 fee0, uint128 fee1) external pure returns (bytes memory data) { - return encodeClaim(poolId, fee0, fee1); + function decodeClaim_(bytes calldata data) external pure returns (uint64 poolId, uint128 fee0, uint128 fee1) { + return decodeClaim(data); } - function doDecodeClaim(bytes calldata data) external pure returns (uint64 poolId, uint128 fee0, uint128 fee1) { - return decodeClaim(data); + function decodePoolId_(bytes calldata data) external pure returns (uint64 poolId, uint24 pairId, uint8 isMutable, uint32 poolNonce) { + return decodePoolId(data); } } @@ -43,7 +33,7 @@ contract TestFVMLib is Test { uint128 amount1, bool sellAsset ) public { - bytes memory data = target.doEncodeSwap( + bytes memory data = encodeSwap( useMax ? uint8(1) : uint8(0), poolId, amount0, @@ -53,7 +43,7 @@ contract TestFVMLib is Test { console.logBytes(data); - (uint8 useMax_, uint64 poolId_, uint128 input_, uint128 output_, uint8 sellAsset_) = target.doDecodeSwap(data); + (uint8 useMax_, uint64 poolId_, uint128 input_, uint128 output_, uint8 sellAsset_) = target.decodeSwap_(data); assertEq(useMax ? uint8(1) : uint8(0), useMax_, "Wrong use max"); assertEq(poolId, poolId_); @@ -71,7 +61,7 @@ contract TestFVMLib is Test { uint128 input, uint128 output, uint8 sellAsset - ) = target.doDecodeSwap(data); + ) = target.decodeSwap_(data); assertEq(useMax, 0); assertEq(poolId, 42); @@ -81,9 +71,9 @@ contract TestFVMLib is Test { } function testFuzz_encodeClaim(uint64 poolId, uint128 fee0, uint128 fee1) public { - bytes memory data = target.doEncodeClaim(poolId, fee0, fee1); + bytes memory data = encodeClaim(poolId, fee0, fee1); - (uint64 poolId_, uint128 fee0_, uint128 fee1_) = target.doDecodeClaim(data); + (uint64 poolId_, uint128 fee0_, uint128 fee1_) = target.decodeClaim_(data); assertEq(poolId, poolId_); assertEq(fee0, fee0_); @@ -92,10 +82,12 @@ contract TestFVMLib is Test { function test_decodeClaim() public { bytes memory data = hex"10032a0609081204"; - (uint64 poolId, uint128 fee0, uint128 fee1) = target.doDecodeClaim(data); + (uint64 poolId, uint128 fee0, uint128 fee1) = target.decodeClaim_(data); assertEq(poolId, 42); assertEq(fee0, 8000 * 10 ** 6); assertEq(fee1, 4 * 10 ** 18); } + + } From b4b81aedb445be6ec88d75dad4af0b553b3519eb Mon Sep 17 00:00:00 2001 From: clemlak Date: Mon, 6 Mar 2023 19:13:02 +0400 Subject: [PATCH 09/25] test: add testFuzz_decodeCreatePair --- test/TestFVMLib.t.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index ba5eb27c..6e7307e3 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -21,6 +21,10 @@ contract FVMLibTarget is Test { function decodePoolId_(bytes calldata data) external pure returns (uint64 poolId, uint24 pairId, uint8 isMutable, uint32 poolNonce) { return decodePoolId(data); } + + function decodeCreatePair_(bytes calldata data) external pure returns (address tokenAsset, address tokenQuote) { + return decodeCreatePair(data); + } } contract TestFVMLib is Test { @@ -89,5 +93,10 @@ contract TestFVMLib is Test { assertEq(fee1, 4 * 10 ** 18); } - + function testFuzz_decodeCreatePair(address token0, address token1) public { + bytes memory data = encodeCreatePair(token0, token1); + (address token0_, address token1_) = target.decodeCreatePair_(data); + assertEq(token0, token0_); + assertEq(token1, token1_); + } } From 3e85fcbe8b73273d18e2c3256528a5e928015083 Mon Sep 17 00:00:00 2001 From: clemlak Date: Mon, 6 Mar 2023 19:15:46 +0400 Subject: [PATCH 10/25] test: add test_decodeCreatePair_RevertIfBadLength --- test/TestFVMLib.t.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index 6e7307e3..cf2dc156 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -99,4 +99,10 @@ contract TestFVMLib is Test { assertEq(token0, token0_); assertEq(token1, token1_); } + + function test_decodeCreatePair_RevertIfBadLength() public { + bytes memory data = hex"01"; + vm.expectRevert(); + target.decodeCreatePair_(data); + } } From 9c9cafe62deaf18ea8ef35c2de1ae998203a76f2 Mon Sep 17 00:00:00 2001 From: clemlak Date: Mon, 6 Mar 2023 19:21:44 +0400 Subject: [PATCH 11/25] test: add testFuzz_encodeCreatePool --- test/TestFVMLib.t.sol | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index cf2dc156..f6bdb51f 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -5,6 +5,18 @@ import "forge-std/Test.sol"; import "forge-std/Vm.sol"; import "contracts/libraries/FVMLib.sol"; +struct DecodedCreatePool { + uint24 pairId; + address controller; + uint16 priorityFee; + uint16 fee; + uint16 vol; + uint16 dur; + uint16 jit; + uint128 maxPrice; + uint128 price; +} + contract FVMLibTarget is Test { function decodeSwap_(bytes calldata data) external @@ -25,6 +37,30 @@ contract FVMLibTarget is Test { function decodeCreatePair_(bytes calldata data) external pure returns (address tokenAsset, address tokenQuote) { return decodeCreatePair(data); } + + function decodeCreatePool_(bytes calldata data) external pure returns (DecodedCreatePool memory pool) { + ( + uint24 pairId, + address controller, + uint16 priorityFee, + uint16 fee, + uint16 vol, + uint16 dur, + uint16 jit, + uint128 maxPrice, + uint128 price + ) = decodeCreatePool(data); + + pool.pairId = pairId; + pool.controller = controller; + pool.priorityFee = priorityFee; + pool.fee = fee; + pool.vol = vol; + pool.dur = dur; + pool.jit = jit; + pool.maxPrice = maxPrice; + pool.price = price; + } } contract TestFVMLib is Test { @@ -105,4 +141,40 @@ contract TestFVMLib is Test { vm.expectRevert(); target.decodeCreatePair_(data); } + + function testFuzz_encodeCreatePool( + uint24 pairId, + address controller, + uint16 priorityFee, + uint16 fee, + uint16 vol, + uint16 dur, + uint16 jit, + uint128 maxPrice, + uint128 price + ) public { + bytes memory data = encodeCreatePool( + pairId, + controller, + priorityFee, + fee, + vol, + dur, + jit, + maxPrice, + price + ); + + DecodedCreatePool memory pool = target.decodeCreatePool_(data); + + assertEq(pairId, pool.pairId); + assertEq(controller, pool.controller); + assertEq(priorityFee, pool.priorityFee); + assertEq(fee, pool.fee); + assertEq(vol, pool.vol); + assertEq(dur, pool.dur); + assertEq(jit, pool.jit); + assertEq(maxPrice, pool.maxPrice); + assertEq(price, pool.price); + } } From 9f22c9d667c2acde00a218a85b49a07e1e0d10c2 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:06:02 +0400 Subject: [PATCH 12/25] feat: use RLE for encodeCreatePool --- contracts/libraries/FVMLib.sol | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index d36e0251..03080d25 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -181,7 +181,24 @@ function encodeCreatePool( uint128 maxPrice, uint128 price ) pure returns (bytes memory data) { - data = abi.encodePacked(CREATE_POOL, pairId, controller, priorityFee, fee, vol, dur, jit, maxPrice, price); + (uint8 power0, uint128 base0) = AssemblyLib.fromAmount(maxPrice); + (uint8 power1, uint128 base1) = AssemblyLib.fromAmount(price); + + data = abi.encodePacked( + CREATE_POOL, + pairId, + controller, + priorityFee, + fee, + vol, + dur, + jit, + uint8(52), + power0, + base0, + power1, + base1 + ); } function decodeCreatePool(bytes calldata data) @@ -198,7 +215,7 @@ function decodeCreatePool(bytes calldata data) uint128 price ) { - if (data.length != 66) revert InvalidBytesLength(66, data.length); + // if (data.length != 66) revert InvalidBytesLength(66, data.length); pairId = uint24(bytes3(data[1:4])); controller = address(bytes20(data[4:24])); priorityFee = uint16(bytes2(data[24:26])); @@ -206,8 +223,9 @@ function decodeCreatePool(bytes calldata data) vol = uint16(bytes2(data[28:30])); dur = uint16(bytes2(data[30:32])); jit = uint16(bytes2(data[32:34])); - maxPrice = uint128(bytes16(data[34:50])); - price = uint128(bytes16(data[50:])); + uint8 pointer0 = uint8(bytes1(data[34])); + maxPrice = AssemblyLib.toAmount(data[35:pointer0]); + price = AssemblyLib.toAmount(data[pointer0:]); } function encodeAllocate(uint8 useMax, uint64 poolId, uint8 power, uint128 amount) pure returns (bytes memory data) { From d8e9e94c484c8a55c56b76fddfdf8e4ef2f322ff Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:29:25 +0400 Subject: [PATCH 13/25] test: update encodeAllocate to use RLE --- contracts/libraries/FVMLib.sol | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index 03080d25..745ef082 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -170,6 +170,10 @@ function decodeClaim(bytes calldata data) pure returns (uint64 poolId, uint128 f fee1 = AssemblyLib.toAmount(data[pointer1:data.length]); } +/** + * @dev Encodes a create pool operation. + * FIXME: Same issue as `encodeClaim`... This function is not optimized! + */ function encodeCreatePool( uint24 pairId, address controller, @@ -228,8 +232,19 @@ function decodeCreatePool(bytes calldata data) price = AssemblyLib.toAmount(data[pointer0:]); } -function encodeAllocate(uint8 useMax, uint64 poolId, uint8 power, uint128 amount) pure returns (bytes memory data) { - data = abi.encodePacked(AssemblyLib.pack(bytes1(useMax), ALLOCATE), poolId, power, amount); +/** + * @dev Encodes a allocate operation. + * FIXME: Same issue as `encodeClaim`... This function is not optimized! + */ +function encodeAllocate(uint8 useMax, uint64 poolId, uint128 deltaLiquidity) pure returns (bytes memory data) { + (uint8 power, uint128 base) = AssemblyLib.fromAmount(deltaLiquidity); + + data = abi.encodePacked( + AssemblyLib.pack(bytes1(useMax), ALLOCATE), + poolId, + power, + base + ); } function decodeAllocate(bytes calldata data) pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { From 3ef3acc3aab229fbbf600da783283a80a735c2ed Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:30:05 +0400 Subject: [PATCH 14/25] test: add encodeAllocate calls --- test/Setup.sol | 2 +- test/TestFVMLib.t.sol | 22 ++++++++++++++++++++++ test/TestPortfolioAllocate.t.sol | 16 ++++++++-------- test/TestPortfolioClaim.t.sol | 4 ++-- test/TestPortfolioDeallocate.t.sol | 2 +- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/test/Setup.sol b/test/Setup.sol index 46c94881..fd0a8527 100644 --- a/test/Setup.sol +++ b/test/Setup.sol @@ -226,7 +226,7 @@ contract Setup is Test { } modifier allocateSome(uint128 amt) { - subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, 0x0, amt)); + subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, amt)); _; } diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index f6bdb51f..3961201a 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -61,6 +61,10 @@ contract FVMLibTarget is Test { pool.maxPrice = maxPrice; pool.price = price; } + + function decodeAllocate_(bytes calldata data) external pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { + return decodeAllocate(data); + } } contract TestFVMLib is Test { @@ -177,4 +181,22 @@ contract TestFVMLib is Test { assertEq(maxPrice, pool.maxPrice); assertEq(price, pool.price); } + + function testFuzz_encodeAllocate( + bool useMax, + uint64 poolId, + uint128 deltaLiquidity + ) public { + bytes memory data = encodeAllocate( + useMax ? uint8(1) : uint8(0), + poolId, + deltaLiquidity + ); + + (uint8 useMax_, uint64 poolId_, uint128 deltaLiquidity_) = target.decodeAllocate_(data); + + assertEq(useMax ? uint8(1) : uint8(0), useMax_); + assertEq(poolId, poolId_); + assertEq(deltaLiquidity, deltaLiquidity_); + } } diff --git a/test/TestPortfolioAllocate.t.sol b/test/TestPortfolioAllocate.t.sol index 4fe136a5..3d9609b3 100644 --- a/test/TestPortfolioAllocate.t.sol +++ b/test/TestPortfolioAllocate.t.sol @@ -12,7 +12,7 @@ contract TestPortfolioAllocate is Setup { // Fetch the variable we are changing (pool.liquidity). uint256 prev = ghost().pool().liquidity; // Trigger the function being tested. - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, power: 0, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); // Fetch the variable changed. uint256 post = ghost().pool().liquidity; // Ghost assertions comparing the actual and expected deltas. @@ -30,7 +30,7 @@ contract TestPortfolioAllocate is Setup { uint64 xid = ghost().poolId; uint256 prev = ghost().pool().lastTimestamp; - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, power: 0, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); uint256 post = ghost().pool().lastTimestamp; assertEq(post, prev, "pool.lastTimestamp"); @@ -43,7 +43,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().reserve(ghost().asset().to_addr()); uint256 prev_quote = ghost().reserve(ghost().quote().to_addr()); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, power: 0, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); uint256 post_asset = ghost().reserve(ghost().asset().to_addr()); uint256 post_quote = ghost().reserve(ghost().quote().to_addr()); @@ -69,7 +69,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().reserve(ghost().asset().to_addr()); uint256 prev_quote = ghost().reserve(ghost().quote().to_addr()); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, power: 0, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); uint256 post_asset = ghost().reserve(ghost().asset().to_addr()); uint256 post_quote = ghost().reserve(ghost().quote().to_addr()); @@ -86,7 +86,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().asset().to_token().balanceOf(address(subject())); uint256 prev_quote = ghost().quote().to_token().balanceOf(address(subject())); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, power: 0, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); uint256 post_asset = ghost().asset().to_token().balanceOf(address(subject())); uint256 post_quote = ghost().quote().to_token().balanceOf(address(subject())); @@ -99,14 +99,14 @@ contract TestPortfolioAllocate is Setup { function test_allocate_non_existent_pool_reverts() public useActor { uint64 failureArg = 51; vm.expectRevert(abi.encodeWithSelector(NonExistentPool.selector, failureArg)); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: failureArg, power: 0, amount: 1 ether})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: failureArg, amount: 1 ether})); } function test_allocate_zero_liquidity_reverts() public defaultConfig useActor isArmed { uint256 failureArg = 0; vm.expectRevert(ZeroLiquidity.selector); subject().multiprocess( - FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, power: 0, amount: uint128(failureArg)}) + FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, amount: uint128(failureArg)}) ); } @@ -114,7 +114,7 @@ contract TestPortfolioAllocate is Setup { uint256 failureArg = uint256(type(uint128).max) + 1; vm.expectRevert(); // safeCastTo128 reverts with no message, so it's just an "Evm Error". subject().multiprocess( - FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, power: 0, amount: uint128(failureArg)}) + FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, amount: uint128(failureArg)}) ); } } diff --git a/test/TestPortfolioClaim.t.sol b/test/TestPortfolioClaim.t.sol index 93555a5a..8f23e2ca 100644 --- a/test/TestPortfolioClaim.t.sol +++ b/test/TestPortfolioClaim.t.sol @@ -148,7 +148,7 @@ contract TestPortfolioClaim is Setup { ghost().quote().to_token().approve(address(subject()), 100000); // eve provides minimal liquidity to the pool - subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, 0x0, uint128(startLiquidity / 5))); // 20% + subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, uint128(startLiquidity / 5))); // 20% // of pool, eve = 2000, total = 2000 + 10000 // eve waits for some swaps to happen. basicSwap will sell assets and increment asset fee growth. @@ -179,7 +179,7 @@ contract TestPortfolioClaim is Setup { function test_claim_succeeds() public noJit defaultConfig usePairTokens(10 ether) useActor isArmed { // add a tiny amount of liquidity so we can test easier uint128 delLiquidity = 100_000 wei; // with the pool params, asset reserves will be about 300 wei. - subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, 0x0, delLiquidity)); + subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, delLiquidity)); // swap a small amount so we generate fees uint128 amountIn = 10_000 wei; // 1% fees will generate 100 wei of asset fee growth diff --git a/test/TestPortfolioDeallocate.t.sol b/test/TestPortfolioDeallocate.t.sol index 4c07e378..fcb97240 100644 --- a/test/TestPortfolioDeallocate.t.sol +++ b/test/TestPortfolioDeallocate.t.sol @@ -6,7 +6,7 @@ import "./Setup.sol"; contract TestPortfolioDeallocate is Setup { function test_deallocate_max() public noJit defaultConfig useActor usePairTokens(10 ether) isArmed { uint128 amt = 1 ether; - subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, 0x0, amt)); + subject().multiprocess(FVMLib.encodeAllocate(uint8(0), ghost().poolId, amt)); // Deallocating liquidity can round down. uint256 prev = ghost().position(address(this)).freeLiquidity; From 006990100806376c3d9272f0a50b7a0152fa076b Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:38:44 +0400 Subject: [PATCH 15/25] feat: update encodeAllocate, encodeDealloacate --- contracts/libraries/FVMLib.sol | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index 745ef082..e3bba222 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -238,13 +238,7 @@ function decodeCreatePool(bytes calldata data) */ function encodeAllocate(uint8 useMax, uint64 poolId, uint128 deltaLiquidity) pure returns (bytes memory data) { (uint8 power, uint128 base) = AssemblyLib.fromAmount(deltaLiquidity); - - data = abi.encodePacked( - AssemblyLib.pack(bytes1(useMax), ALLOCATE), - poolId, - power, - base - ); + data = abi.encodePacked(AssemblyLib.pack(bytes1(useMax), ALLOCATE), poolId, power, base); } function decodeAllocate(bytes calldata data) pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { @@ -255,15 +249,17 @@ function decodeAllocate(bytes calldata data) pure returns (uint8 useMax, uint64 deltaLiquidity = AssemblyLib.toAmount(data[9:]); } -function encodeDeallocate(uint8 useMax, uint64 poolId, uint8 power, uint128 amount) pure returns (bytes memory data) { - data = abi.encodePacked(AssemblyLib.pack(bytes1(useMax), DEALLOCATE), poolId, power, amount); + +function encodeDeallocate(uint8 useMax, uint64 poolId, uint128 deltaLiquidity) pure returns (bytes memory data) { + (uint8 power, uint128 base) = AssemblyLib.fromAmount(deltaLiquidity); + data = abi.encodePacked(AssemblyLib.pack(bytes1(useMax), DEALLOCATE), poolId, power, base); } function decodeDeallocate(bytes calldata data) pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { if (data.length < 9) revert InvalidBytesLength(9, data.length); useMax = uint8(data[0] >> 4); poolId = uint64(bytes8(data[1:9])); - deltaLiquidity = uint128(AssemblyLib.toAmount(data[9:])); + deltaLiquidity = AssemblyLib.toAmount(data[9:]); } /** From 239d30a7b6399180db4e9b926bdf1db0a61ad301 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:39:03 +0400 Subject: [PATCH 16/25] test: update encodeDeallocate calls --- test/Setup.sol | 2 +- test/TestPortfolioAllocate.t.sol | 16 ++++++++-------- test/TestPortfolioClaim.t.sol | 2 +- test/TestPortfolioDeallocate.t.sol | 2 +- test/invariant/HandlerPortfolio.sol | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/Setup.sol b/test/Setup.sol index fd0a8527..68d6c231 100644 --- a/test/Setup.sol +++ b/test/Setup.sol @@ -231,7 +231,7 @@ contract Setup is Test { } modifier deallocateSome(uint128 amt) { - subject().multiprocess(FVMLib.encodeDeallocate(uint8(0), ghost().poolId, 0x0, amt)); + subject().multiprocess(FVMLib.encodeDeallocate(uint8(0), ghost().poolId, amt)); _; } diff --git a/test/TestPortfolioAllocate.t.sol b/test/TestPortfolioAllocate.t.sol index 3d9609b3..0fdbd41a 100644 --- a/test/TestPortfolioAllocate.t.sol +++ b/test/TestPortfolioAllocate.t.sol @@ -12,7 +12,7 @@ contract TestPortfolioAllocate is Setup { // Fetch the variable we are changing (pool.liquidity). uint256 prev = ghost().pool().liquidity; // Trigger the function being tested. - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, deltaLiquidity: amount})); // Fetch the variable changed. uint256 post = ghost().pool().liquidity; // Ghost assertions comparing the actual and expected deltas. @@ -30,7 +30,7 @@ contract TestPortfolioAllocate is Setup { uint64 xid = ghost().poolId; uint256 prev = ghost().pool().lastTimestamp; - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, deltaLiquidity: amount})); uint256 post = ghost().pool().lastTimestamp; assertEq(post, prev, "pool.lastTimestamp"); @@ -43,7 +43,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().reserve(ghost().asset().to_addr()); uint256 prev_quote = ghost().reserve(ghost().quote().to_addr()); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, deltaLiquidity: amount})); uint256 post_asset = ghost().reserve(ghost().asset().to_addr()); uint256 post_quote = ghost().reserve(ghost().quote().to_addr()); @@ -69,7 +69,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().reserve(ghost().asset().to_addr()); uint256 prev_quote = ghost().reserve(ghost().quote().to_addr()); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, deltaLiquidity: amount})); uint256 post_asset = ghost().reserve(ghost().asset().to_addr()); uint256 post_quote = ghost().reserve(ghost().quote().to_addr()); @@ -86,7 +86,7 @@ contract TestPortfolioAllocate is Setup { uint256 prev_asset = ghost().asset().to_token().balanceOf(address(subject())); uint256 prev_quote = ghost().quote().to_token().balanceOf(address(subject())); (uint256 delta0, uint256 delta1) = ghost().pool().getPoolLiquidityDeltas({deltaLiquidity: int128(amount)}); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, amount: amount})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: xid, deltaLiquidity: amount})); uint256 post_asset = ghost().asset().to_token().balanceOf(address(subject())); uint256 post_quote = ghost().quote().to_token().balanceOf(address(subject())); @@ -99,14 +99,14 @@ contract TestPortfolioAllocate is Setup { function test_allocate_non_existent_pool_reverts() public useActor { uint64 failureArg = 51; vm.expectRevert(abi.encodeWithSelector(NonExistentPool.selector, failureArg)); - subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: failureArg, amount: 1 ether})); + subject().multiprocess(FVMLib.encodeAllocate({useMax: uint8(0), poolId: failureArg, deltaLiquidity: 1 ether})); } function test_allocate_zero_liquidity_reverts() public defaultConfig useActor isArmed { uint256 failureArg = 0; vm.expectRevert(ZeroLiquidity.selector); subject().multiprocess( - FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, amount: uint128(failureArg)}) + FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, deltaLiquidity: uint128(failureArg)}) ); } @@ -114,7 +114,7 @@ contract TestPortfolioAllocate is Setup { uint256 failureArg = uint256(type(uint128).max) + 1; vm.expectRevert(); // safeCastTo128 reverts with no message, so it's just an "Evm Error". subject().multiprocess( - FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, amount: uint128(failureArg)}) + FVMLib.encodeAllocate({useMax: uint8(0), poolId: ghost().poolId, deltaLiquidity: uint128(failureArg)}) ); } } diff --git a/test/TestPortfolioClaim.t.sol b/test/TestPortfolioClaim.t.sol index 8f23e2ca..9881f26a 100644 --- a/test/TestPortfolioClaim.t.sol +++ b/test/TestPortfolioClaim.t.sol @@ -188,7 +188,7 @@ contract TestPortfolioClaim is Setup { subject().multiprocess(FVMLib.encodeSwap(uint8(0), ghost().poolId, amountIn, amountOut, uint8(1))); // withdraw all the liquidity after the swap, to sync fees. - subject().multiprocess(FVMLib.encodeDeallocate(uint8(0), ghost().poolId, 0x0, delLiquidity)); + subject().multiprocess(FVMLib.encodeDeallocate(uint8(0), ghost().poolId, delLiquidity)); // withdraw all internal balances uint256 bal0 = ghost().balance(actor(), ghost().asset().to_addr()); diff --git a/test/TestPortfolioDeallocate.t.sol b/test/TestPortfolioDeallocate.t.sol index fcb97240..f61080e2 100644 --- a/test/TestPortfolioDeallocate.t.sol +++ b/test/TestPortfolioDeallocate.t.sol @@ -10,7 +10,7 @@ contract TestPortfolioDeallocate is Setup { // Deallocating liquidity can round down. uint256 prev = ghost().position(address(this)).freeLiquidity; - subject().multiprocess(FVMLib.encodeDeallocate(uint8(1), ghost().poolId, 0x0, amt)); + subject().multiprocess(FVMLib.encodeDeallocate(uint8(1), ghost().poolId, amt)); uint256 post = ghost().position(address(this)).freeLiquidity; assertApproxEqAbs(post, prev - amt, 1, "liquidity-did-not-decrease"); diff --git a/test/invariant/HandlerPortfolio.sol b/test/invariant/HandlerPortfolio.sol index baec01f1..0e41bf53 100644 --- a/test/invariant/HandlerPortfolio.sol +++ b/test/invariant/HandlerPortfolio.sol @@ -360,7 +360,7 @@ contract HandlerPortfolio is HandlerBase { prev = fetchAccountingState(); ctx.subject().multiprocess( - FVM.encodeDeallocate(uint8(0), ctx.ghost().poolId, 0x0, deltaLiquidity.safeCastTo128()) + FVM.encodeDeallocate(uint8(0), ctx.ghost().poolId, deltaLiquidity.safeCastTo128()) ); AccountingState memory end = fetchAccountingState(); From 07b122212eaa9bf57fbaf56951fabd9d9c72f5a1 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 16:39:13 +0400 Subject: [PATCH 17/25] test: add testFuzz_encodeDeallocate --- test/TestFVMLib.t.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index 3961201a..b39f65b0 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -65,6 +65,10 @@ contract FVMLibTarget is Test { function decodeAllocate_(bytes calldata data) external pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { return decodeAllocate(data); } + + function decodeDeallocate_(bytes calldata data) external pure returns (uint8 useMax, uint64 poolId, uint128 deltaLiquidity) { + return decodeDeallocate(data); + } } contract TestFVMLib is Test { @@ -199,4 +203,22 @@ contract TestFVMLib is Test { assertEq(poolId, poolId_); assertEq(deltaLiquidity, deltaLiquidity_); } + + function testFuzz_encodeDeallocate( + bool useMax, + uint64 poolId, + uint128 deltaLiquidity + ) public { + bytes memory data = encodeDeallocate( + useMax ? uint8(1) : uint8(0), + poolId, + deltaLiquidity + ); + + (uint8 useMax_, uint64 poolId_, uint128 deltaLiquidity_) = target.decodeDeallocate_(data); + + assertEq(useMax ? uint8(1) : uint8(0), useMax_); + assertEq(poolId, poolId_); + assertEq(deltaLiquidity, deltaLiquidity_); + } } From a062ff44b554b7c24d8a08935e9d9a671abe6a9f Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:22:41 +0400 Subject: [PATCH 18/25] test: add testFuzz_encodePoolId --- test/TestFVMLib.t.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/TestFVMLib.t.sol b/test/TestFVMLib.t.sol index b39f65b0..ad84186d 100644 --- a/test/TestFVMLib.t.sol +++ b/test/TestFVMLib.t.sol @@ -221,4 +221,22 @@ contract TestFVMLib is Test { assertEq(poolId, poolId_); assertEq(deltaLiquidity, deltaLiquidity_); } + + /* + function testFuzz_encodePoolId( + uint24 pairId, + bool isMutable, + uint32 poolNonce + ) public { + uint64 poolId = encodePoolId(pairId, isMutable, poolNonce); + (uint64 poolId_, uint24 pairId_, uint8 isMutable_, uint32 poolNonce_) = target.decodePoolId_(abi.encode(poolId)); + + assertEq(poolId, poolId_); + assertEq(pairId, pairId_); + assertEq(isMutable ? uint8(1) : uint8(0), isMutable_); + assertEq(poolNonce, poolNonce_); + } + */ + + } From 62b10a2207a9659c1af21307ebbdfbf88df97c83 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:22:48 +0400 Subject: [PATCH 19/25] test: add testFuzz_isBetween --- test/TestAssemblyLib.t.sol | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/TestAssemblyLib.t.sol b/test/TestAssemblyLib.t.sol index e9f2ccb2..d3f4eda2 100644 --- a/test/TestAssemblyLib.t.sol +++ b/test/TestAssemblyLib.t.sol @@ -13,4 +13,15 @@ contract TestAssemblyLib is Test { assertEq(base, 5); } } + + function testFuzz_isBetween(uint256 value, uint256 lower, uint256 upper) public { + vm.assume(lower <= upper); + bool valid = AssemblyLib.isBetween(value, lower, upper); + + if (lower >= value && value <= upper) { + assertTrue(valid); + } else { + assertFalse(valid); + } + } } From e3350afe5fb920ee6ec0efac9f1c704c9948fb25 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:26:25 +0400 Subject: [PATCH 20/25] chore: add toBytes8 NatSpec --- contracts/libraries/AssemblyLib.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/libraries/AssemblyLib.sol b/contracts/libraries/AssemblyLib.sol index 8c8df9bb..21c708d4 100644 --- a/contracts/libraries/AssemblyLib.sol +++ b/contracts/libraries/AssemblyLib.sol @@ -118,6 +118,10 @@ library AssemblyLib { } } + /** + * @dev There's no explict casting from dynamic to fixed sized bytes, this function + * handles it for us. + */ function toBytes8(bytes memory raw) internal pure returns (bytes8 data) { assembly { data := mload(add(raw, 32)) From bc98eea66e5a287420595334e9c785aae4048968 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:26:41 +0400 Subject: [PATCH 21/25] test: add testFuzz_addSignedDelta --- test/TestAssemblyLib.t.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/TestAssemblyLib.t.sol b/test/TestAssemblyLib.t.sol index d3f4eda2..7e7f868f 100644 --- a/test/TestAssemblyLib.t.sol +++ b/test/TestAssemblyLib.t.sol @@ -24,4 +24,11 @@ contract TestAssemblyLib is Test { assertFalse(valid); } } + + function testFuzz_addSignedDelta(uint128 input, int128 delta) public { + assertEq( + AssemblyLib.addSignedDelta(input, delta), + delta < 0 ? uint128(-delta) : uint128(delta) + ); + } } From c9883a668d310e8b0ac97833151c5dbe5b0ffe7e Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:29:16 +0400 Subject: [PATCH 22/25] test: add testFuzz_separate --- test/TestAssemblyLib.t.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/TestAssemblyLib.t.sol b/test/TestAssemblyLib.t.sol index 7e7f868f..64112174 100644 --- a/test/TestAssemblyLib.t.sol +++ b/test/TestAssemblyLib.t.sol @@ -31,4 +31,13 @@ contract TestAssemblyLib is Test { delta < 0 ? uint128(-delta) : uint128(delta) ); } + + function testFuzz_separate(bytes1 a, bytes1 b) public { + vm.assume(a <= bytes1(uint8(15))); + vm.assume(b <= bytes1(uint8(15))); + bytes1 data = AssemblyLib.pack(a, b); + (bytes1 a_, bytes1 b_) = AssemblyLib.separate(data); + assertEq(a, a_); + assertEq(b, b_); + } } From 4fdbf2cb2d04e7058dfa40c2a572153eacdb1351 Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:39:06 +0400 Subject: [PATCH 23/25] chore: small NatSpac addition --- contracts/libraries/FVMLib.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/libraries/FVMLib.sol b/contracts/libraries/FVMLib.sol index e3bba222..42ab1948 100644 --- a/contracts/libraries/FVMLib.sol +++ b/contracts/libraries/FVMLib.sol @@ -289,6 +289,9 @@ function encodeSwap( ); } +/** + * @dev Decodes a swap operation. + */ function decodeSwap(bytes calldata data) pure returns (uint8 useMax, uint64 poolId, uint128 input, uint128 output, uint8 sellAsset) From 3285d22ec5dbd8a2cdd7a032fdc48d653e1ded3f Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:42:50 +0400 Subject: [PATCH 24/25] test: fix testFuzz_separate fuzzing --- test/TestAssemblyLib.t.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/TestAssemblyLib.t.sol b/test/TestAssemblyLib.t.sol index 64112174..c8af0223 100644 --- a/test/TestAssemblyLib.t.sol +++ b/test/TestAssemblyLib.t.sol @@ -32,12 +32,12 @@ contract TestAssemblyLib is Test { ); } - function testFuzz_separate(bytes1 a, bytes1 b) public { - vm.assume(a <= bytes1(uint8(15))); - vm.assume(b <= bytes1(uint8(15))); - bytes1 data = AssemblyLib.pack(a, b); + function testFuzz_separate(uint8 a, uint8 b) public { + vm.assume(a <= 15); + vm.assume(b <= 15); + bytes1 data = AssemblyLib.pack(bytes1(a), bytes1(b)); (bytes1 a_, bytes1 b_) = AssemblyLib.separate(data); - assertEq(a, a_); - assertEq(b, b_); + assertEq(a, uint8(a_)); + assertEq(b, uint8(b_)); } } From e43cd656eb2fc9a5e602c0eac7060cd2c6bfe80c Mon Sep 17 00:00:00 2001 From: clemlak Date: Tue, 7 Mar 2023 19:44:47 +0400 Subject: [PATCH 25/25] test: commenting out broken tests --- test/TestAssemblyLib.t.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/TestAssemblyLib.t.sol b/test/TestAssemblyLib.t.sol index c8af0223..29b76552 100644 --- a/test/TestAssemblyLib.t.sol +++ b/test/TestAssemblyLib.t.sol @@ -14,6 +14,9 @@ contract TestAssemblyLib is Test { } } + /* + FIXME: These tests are not working yet. + function testFuzz_isBetween(uint256 value, uint256 lower, uint256 upper) public { vm.assume(lower <= upper); bool valid = AssemblyLib.isBetween(value, lower, upper); @@ -31,6 +34,7 @@ contract TestAssemblyLib is Test { delta < 0 ? uint128(-delta) : uint128(delta) ); } + */ function testFuzz_separate(uint8 a, uint8 b) public { vm.assume(a <= 15);