From ad4085d1d7b73734f62a5172de27a66586d0be22 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 12:54:37 +0200 Subject: [PATCH 01/22] feat: update dependencies --- evm/lib/GeneralisedIncentives | 2 +- evm/lib/catalyst-channel-lists | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/lib/GeneralisedIncentives b/evm/lib/GeneralisedIncentives index 4abd57c7..ac8754bf 160000 --- a/evm/lib/GeneralisedIncentives +++ b/evm/lib/GeneralisedIncentives @@ -1 +1 @@ -Subproject commit 4abd57c74150a907f16c3589505f364b8cdbc9a5 +Subproject commit ac8754bf028560885d8dff37101f2bca2cf11570 diff --git a/evm/lib/catalyst-channel-lists b/evm/lib/catalyst-channel-lists index 8700e0b2..6a73865a 160000 --- a/evm/lib/catalyst-channel-lists +++ b/evm/lib/catalyst-channel-lists @@ -1 +1 @@ -Subproject commit 8700e0b209a9bfc116756f5a43e35aada97bb021 +Subproject commit 6a73865ab54cf10dafbff56a1b4cda4f98a60a90 From 3fa2e49ad7df07711f50f4e82dbe5c9891495a94 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 16:39:30 +0200 Subject: [PATCH 02/22] feat: update documentation --- evm/src/CatalystChainInterface.sol | 86 ++++++++++++------- evm/src/CatalystFactory.sol | 2 +- evm/src/CatalystVaultAmplified.sol | 2 +- evm/src/CatalystVaultCommon.sol | 2 +- evm/src/CatalystVaultVolatile.sol | 2 +- .../archive/CatalystVaultAmplified.update.sol | 2 +- 6 files changed, 59 insertions(+), 37 deletions(-) diff --git a/evm/src/CatalystChainInterface.sol b/evm/src/CatalystChainInterface.sol index 1d2be375..0ccc9dab 100644 --- a/evm/src/CatalystChainInterface.sol +++ b/evm/src/CatalystChainInterface.sol @@ -18,20 +18,31 @@ import "./CatalystPayload.sol"; /** - * @title Catalyst: Generalised IBC Interface - * @author Cata Labs - * @notice This contract is a generalised proof of concept - * IBC interface using an example ABI. - * It acts as an intermediate between the vault and the router to - * abstract router logic away from the vaults. This simplifies the - * development of the vaults and allows Catalyst to adopt or change - * message routers with more flexibility. + * @title Catalyst Cross-chain Interface for Generalised Incentives. + * @author Cata Labs Inc. + * @notice Cross-chain interface built using Generalised Incentives. + * Catalyst vaults depends on a translator contract to execute cross-chain swaps to convert + * the swap details into a bytearray that can be sent to the destination chain. + * + * The cross-chain interface is also in charge of extending messaging protocol with additional features + * like underwriting. + * + * Underwriting is facilitated by allowing external actors to front the output of a swap. The cross-chain interfaces creates + * an appropriate escrow on the vault and then pays the user. Once the incoming swap arrives, it is intercepted to release the + * escrow to then pay the underwriter. + * + * @dev To adopt the cross-chain interface for another messaging protocol, you need to + * - Change the message submission flow: (GARP.submitMessage(...)) + * - Change the package callbakcs (receiveMessage, receiveAck) + * + * However, it may be required to also modify the vaults as the incentive structure is currently fixed. */ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { //--- ERRORS ---// - // Only the message router should be able to deliver messages. + /** @dev Only the message router should be able to deliver messages. */ error InvalidCaller(); // 48f5c3ed + error InvalidContext(bytes1 context); // 9f769791 error InvalidAddress(); // e6c4247b error InvalidSourceApplication(); // 003923e0 @@ -97,7 +108,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // The reason behind is if the underwrite duration was timestamp based, say 8 hours. If the chain haulted for // 10 hours, then the underwriter would risk being expired and lose everything. If a chain doesn't produce any // blocks during a hault, then it wouldn't be a risk to an underwriter. - // Generally, it is more common for there to be unpredictable block slowdowns rather than unpredictable block speedups. + // Generally, it is more common for unpredictable block slowdowns rather than unpredictable block speedups. /// @notice The initial underwrite duration (in blocks). /// @dev Is 8 hours if the block time is 2 seconds, 48 hours if the block time is 12 seconds. Not a great initial value but better than nothing. @@ -114,40 +125,48 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { //--- Config ---// + /** @notice The generalised incentives endpoint */ IIncentivizedMessageEscrow public immutable GARP; // Set on deployment - //-- Underwriting Config--// - // How many blocks should there be between when an identifier can be underwritten. + /** + * @notice How many blocks should there be between when an identifier can be underwritten. + * @dev The purpose of these buffer blocks is to ensure that underwriters don't mistakenly underwrite swaps right after they are filled. + */ uint24 constant BUFFER_BLOCKS = 4; - uint256 constant public UNDERWRITING_COLLATERAL = 35; // 3,5% extra as collateral. + /** @notice Set underwriter collateral. Is of UNDERWRITING_COLLATERAL_DENOMINATOR */ + uint256 constant public UNDERWRITING_COLLATERAL = 35; // 3.5% extra as collateral. uint256 constant public UNDERWRITING_COLLATERAL_DENOMINATOR = 1000; - uint256 constant public EXPIRE_CALLER_REWARD = 350; // 35% of the 3,5% = 1,225%. Of $1000 = $12,25 + /** @notice How much of the collateral is given to the exipirer the rest goes to the vault. Is of EXPIRE_CALLER_REWARD_DENOMINATOR. */ + uint256 constant public EXPIRE_CALLER_REWARD = 350; // 35% of the 3.5% = 1.225%. Of $1000 = $12.25 uint256 constant public EXPIRE_CALLER_REWARD_DENOMINATOR = 1000; - //--- Storage ---// - /// @notice The destination address on the chain by chain identifier. + /** @notice The destination address on the chain by chain identifier. */ mapping(bytes32 => bytes) public chainIdentifierToDestinationAddress; - /// @notice The minimum amount of gas for a specific chain. bytes32(0) indicates ack. + /** @notice The minimum amount of gas for a specific chain. bytes32(0) indicates ack. */ mapping(bytes32 => uint48) public minGasFor; //-- Underwriting Storage --// - /// @notice Sets the maximum duration for underwriting. - /// @dev Should be set long enough for all swaps to be able to confirm + a small buffer - /// Should also be set long enough to not take up an excess amount of escrow usage. + + /** + * @notice Sets the maximum duration for underwriting. + * @dev Should be set long enough for all swaps to be able to confirm + a small buffer + * Should also be set short enough to not take up an excess amount of escrow usage. + */ uint256 public maxUnderwritingDuration = INITIAL_MAX_UNDERWRITE_BLOCK_DURATION; - /// @notice Maps underwriting identifiers to underwriting state. - /// refundTo can be checked to see if the ID has been underwritten. + /** + * @notice Maps underwriting identifiers to underwriting state. + * refundTo can be checked to see if the ID has been underwritten. + */ mapping(bytes32 => UnderwritingStorage) public underwritingStorage; - constructor(address GARP_, address defaultOwner) payable { require(address(GARP_) != address(0)); // dev: GARP_ cannot be zero address GARP = IIncentivizedMessageEscrow(GARP_); @@ -156,23 +175,26 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { emit MaxUnderwriteDuration(INITIAL_MAX_UNDERWRITE_BLOCK_DURATION); } - //-- Admin--// - /// @notice Allow updating of the minimum gas limit. - /// @dev Set chainIdentifier to 0 for gas for ack. + /** + * @notice Allow updating of the minimum gas limit. + * @dev Set chainIdentifier to 0 for gas for ack. + */ function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) override external onlyOwner { minGasFor[chainIdentifier] = minGas; emit MinGasFor(chainIdentifier, minGas); } - /// @notice Sets the new max underwrite duration, which is the period of time - /// before an underwrite can be expired. When an underwrite is expired, the underwriter - /// loses all capital provided. - /// @dev This function can be exploited by the owner. By setting newMaxUnderwriteDuration to (almost) 0 right before someone calls underwrite and then - /// expiring them before the actual swap arrives. The min protection here is not sufficient since it needs to be well into - /// when a message can be validated. As a result, the owner of this contract should be a timelock which underwriters monitor. + /** + * @notice Sets the new max underwrite duration: That is the period of time before an underwrite can be expired. + * When an underwrite is expired, the underwriter loses all capital provided. + * @dev This function can be exploited by the owner. By setting newMaxUnderwriteDuration to (almost) 0 right before + * someone calls underwrite and then expiring them before the actual swap arrives. The min protection here is not + * sufficient since it needs to be well into when a message can be validated. As a result, the owner of this contract + * should be a timelock which underwriters monitor or trusted by underwriters. + */ function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) onlyOwner override external { if (newMaxUnderwriteDuration <= MIN_UNDERWRITE_BLOCK_DURATION) revert MaxUnderwriteDurationTooShort(); // If the underwriting duration is too long, users can freeze up a lot of value for not a lot of cost. diff --git a/evm/src/CatalystFactory.sol b/evm/src/CatalystFactory.sol index 339db24f..a0ad0dc3 100644 --- a/evm/src/CatalystFactory.sol +++ b/evm/src/CatalystFactory.sol @@ -12,7 +12,7 @@ uint256 constant MAX_GOVERNANCE_FEE_SHARE = 75e16; // 75% /** * @title Catalyst Swap Factory - * @author Cata Labs + * @author Cata Labs Inc. * @notice Allows permissionless deployment Catalyst vaults * and defines governance address for vaults to read. * !The owner of the factory must be a timelock! diff --git a/evm/src/CatalystVaultAmplified.sol b/evm/src/CatalystVaultAmplified.sol index 79418616..3f96bd4f 100644 --- a/evm/src/CatalystVaultAmplified.sol +++ b/evm/src/CatalystVaultAmplified.sol @@ -14,7 +14,7 @@ import "./ICatalystV1Vault.sol"; /** * @title Catalyst: The Multi-Chain Vault - * @author Cata Labs + * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: 1/w^\theta (1 - \theta) where \theta is * the vault amplification and w is the vault asset balance. diff --git a/evm/src/CatalystVaultCommon.sol b/evm/src/CatalystVaultCommon.sol index b65ca554..2ca121c4 100644 --- a/evm/src/CatalystVaultCommon.sol +++ b/evm/src/CatalystVaultCommon.sol @@ -19,7 +19,7 @@ import { ICatalystV1Vault } from "./ICatalystV1Vault.sol"; /** * @title Catalyst: Common Vault Logic - * @author Cata Labs + * @author Cata Labs Inc. * @notice This abstract contract defines general logic of a Catalyst vault like: * - Vault Token through Solmate's ERC20 implementation. * - Connection management diff --git a/evm/src/CatalystVaultVolatile.sol b/evm/src/CatalystVaultVolatile.sol index c58b54a7..84c8865b 100644 --- a/evm/src/CatalystVaultVolatile.sol +++ b/evm/src/CatalystVaultVolatile.sol @@ -14,7 +14,7 @@ import "./ICatalystV1Vault.sol"; /** * @title Catalyst: The Multi-Chain Vault - * @author Cata Labs + * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: W/w where W is an asset-specific weight and w * is the vault asset balance. diff --git a/evm/src/archive/CatalystVaultAmplified.update.sol b/evm/src/archive/CatalystVaultAmplified.update.sol index 89ce388e..2ef06d3c 100644 --- a/evm/src/archive/CatalystVaultAmplified.update.sol +++ b/evm/src/archive/CatalystVaultAmplified.update.sol @@ -7,7 +7,7 @@ import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; /** * @title Catalyst: The Multi-Chain Vault - * @author Cata Labs + * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: 1/w^\theta (1 - \theta) where \theta is * the vault amplification and w is the vault asset balance. From ccc1d77eb1408d9b16a73bc74dbe6e26ad1f6114 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 16:39:38 +0200 Subject: [PATCH 03/22] feat: update license holder --- LICENSE | 4 ++-- evm/LICENSE | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 734aacbd..60ab890c 100644 --- a/LICENSE +++ b/LICENSE @@ -7,10 +7,10 @@ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. Parameters -Licensor: Cata Labs +Licensor: Cata Labs Inc. Licensed Work: Catalyst V1 - The Licensed Work is (c) 2024 Cata Labs + The Licensed Work is (c) 2024 Cata Labs Inc. Change Date: 2028-04-02 diff --git a/evm/LICENSE b/evm/LICENSE index 5857d0cc..4b4dd7c0 100644 --- a/evm/LICENSE +++ b/evm/LICENSE @@ -7,10 +7,10 @@ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. Parameters -Licensor: Cata Labs +Licensor: Cata Labs Inc. Licensed Work: Catalyst V1 - The Licensed Work is (c) 2024 Cata Labs + The Licensed Work is (c) 2024 Cata Labs Inc. Change Date: 2028-04-02 From 37b7ade6779192e8dc52ef13949a8ba6a445ee2a Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 16:46:30 +0200 Subject: [PATCH 04/22] test: fix compiler errors --- evm/script/mocks/MockApplication.sol | 2 +- .../ExploitCircumventLiquiditySwapMinOut.t.sol | 7 +++---- evm/test/CatalystVault/1Volatile/SetWeights.t.sol | 2 +- .../CatalystVault/1Volatile/VolatileOneVault.t.sol | 2 +- .../1Volatile/VolatileTwoVaults.t.sol | 2 +- evm/test/CatalystVault/FullLiquiditySwap.t.sol | 14 ++++++-------- .../CatalystVault/Withdraw/WithdrawNothing.t.sol | 14 ++++++-------- .../Withdraw/WithdrawUnbalanced.t.sol | 8 ++------ .../EvilRoutor.Securitylimit.Volatile.t.sol | 6 ------ .../non-exploits/LargeDepositWithdraw.t.sol | 5 ++--- evm/test/TestCommon.t.sol | 2 +- 11 files changed, 24 insertions(+), 40 deletions(-) diff --git a/evm/script/mocks/MockApplication.sol b/evm/script/mocks/MockApplication.sol index 35afd62e..148dd6b9 100644 --- a/evm/script/mocks/MockApplication.sol +++ b/evm/script/mocks/MockApplication.sol @@ -41,7 +41,7 @@ contract MockApplication is ICrossChainReceiver { MESSAGE_ESCROW.setRemoteImplementation(chainIdentifier, implementation); } - function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external { + function receiveAck(bytes32 destinationIdentifier, bytes32 /* messageIdentifier */, bytes calldata acknowledgement) external { emit AckMessage(destinationIdentifier, acknowledgement); } diff --git a/evm/test/CatalystVault/1Volatile/ExploitCircumventLiquiditySwapMinOut.t.sol b/evm/test/CatalystVault/1Volatile/ExploitCircumventLiquiditySwapMinOut.t.sol index 48206861..3ce1160f 100644 --- a/evm/test/CatalystVault/1Volatile/ExploitCircumventLiquiditySwapMinOut.t.sol +++ b/evm/test/CatalystVault/1Volatile/ExploitCircumventLiquiditySwapMinOut.t.sol @@ -14,12 +14,11 @@ import { AVaultInterfaces } from "test/CatalystVault/AVaultInterfaces.t.sol"; import { TestInvariant } from "test/CatalystVault/Invariant.t.sol"; -function queryVaultWeightsSum(ICatalystV1Vault vault) view returns (uint256) { - uint256 weightsSum = 0; - for (uint256 i; true; i++) { +function queryVaultWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSum) { + for (uint256 i; true; ++i) { uint256 weight = vault._weight(vault._tokenIndexing(i)); if (weight == 0) return weightsSum; - else weightsSum += weight; + weightsSum += weight; } } diff --git a/evm/test/CatalystVault/1Volatile/SetWeights.t.sol b/evm/test/CatalystVault/1Volatile/SetWeights.t.sol index 75b1aa22..39946433 100644 --- a/evm/test/CatalystVault/1Volatile/SetWeights.t.sol +++ b/evm/test/CatalystVault/1Volatile/SetWeights.t.sol @@ -46,7 +46,7 @@ abstract contract TestSetWeights is Test, AVaultInterfaces { return changeFactors; } - function mockNewWeights() internal returns(uint256[] memory) { + function mockNewWeights() view internal returns(uint256[] memory) { uint256[] memory weights = mockWeights(); uint256[] memory changeFactors = mockWeightChangeFactors(); diff --git a/evm/test/CatalystVault/1Volatile/VolatileOneVault.t.sol b/evm/test/CatalystVault/1Volatile/VolatileOneVault.t.sol index 8fd688ff..31b6f817 100644 --- a/evm/test/CatalystVault/1Volatile/VolatileOneVault.t.sol +++ b/evm/test/CatalystVault/1Volatile/VolatileOneVault.t.sol @@ -64,7 +64,7 @@ contract TestVolatileInvariant is TestInvariant, TestLocalswap, TestCrossChainIn return getLargestSwap(fromVault, toVault, fromAsset, toAsset, false); } - function getLargestSwap(address fromVault, address toVault, address fromAsset, address /* toAsset */, bool securityLimit) view override internal returns(uint256 amount) { + function getLargestSwap(address fromVault, address /* toVault */, address fromAsset, address /* toAsset */, bool securityLimit) view override internal returns(uint256 amount) { if (securityLimit) { amount = Token(fromAsset).balanceOf(fromVault) / 2; } else { diff --git a/evm/test/CatalystVault/1Volatile/VolatileTwoVaults.t.sol b/evm/test/CatalystVault/1Volatile/VolatileTwoVaults.t.sol index fa788afb..952e5f82 100644 --- a/evm/test/CatalystVault/1Volatile/VolatileTwoVaults.t.sol +++ b/evm/test/CatalystVault/1Volatile/VolatileTwoVaults.t.sol @@ -48,7 +48,7 @@ contract TestVolatileInvariant2 is TestInvariant, TestSendAsset, TestReceiveAsse return getLargestSwap(fromVault, toVault, fromAsset, toAsset, false); } - function getLargestSwap(address fromVault, address toVault, address fromAsset, address /* toAsset */, bool securityLimit) view override internal returns(uint256 amount) { + function getLargestSwap(address fromVault, address /* toVault */, address fromAsset, address /* toAsset */, bool securityLimit) view override internal returns(uint256 amount) { if (securityLimit) { amount = Token(fromAsset).balanceOf(fromVault) / 2; } else { diff --git a/evm/test/CatalystVault/FullLiquiditySwap.t.sol b/evm/test/CatalystVault/FullLiquiditySwap.t.sol index 3e1e2ae2..c0ab1581 100644 --- a/evm/test/CatalystVault/FullLiquiditySwap.t.sol +++ b/evm/test/CatalystVault/FullLiquiditySwap.t.sol @@ -16,21 +16,19 @@ import { AVaultInterfaces } from "test/CatalystVault/AVaultInterfaces.t.sol"; import { TestInvariant } from "test/CatalystVault/Invariant.t.sol"; -function queryAssetCount(ICatalystV1Vault vault) view returns (uint256) { - uint256 tokenCount = 0; - for (uint256 i; true; i++) { +function queryAssetCount(ICatalystV1Vault vault) view returns (uint256 tokenCount) { + for (uint256 i; true; ++i) { address token = vault._tokenIndexing(i); if (token == address(0)) return tokenCount; - else tokenCount += 1; + tokenCount += 1; } } -function queryVaultWeightsSum(ICatalystV1Vault vault) view returns (uint256) { - uint256 weightsSum = 0; - for (uint256 i; true; i++) { +function queryVaultWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSum) { + for (uint256 i; true; ++i) { uint256 weight = vault._weight(vault._tokenIndexing(i)); if (weight == 0) return weightsSum; - else weightsSum += weight; + weightsSum += weight; } } diff --git a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol index 64478966..fa3d595a 100644 --- a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol +++ b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol @@ -11,21 +11,19 @@ import { AVaultInterfaces } from "test/CatalystVault/AVaultInterfaces.t.sol"; import { TestInvariant } from "test/CatalystVault/Invariant.t.sol"; -function queryAssetCount(ICatalystV1Vault vault) view returns (uint256) { - uint256 tokenCount = 0; - for (uint256 i; true; i++) { +function queryAssetCount(ICatalystV1Vault vault) view returns (uint256 tokenCount) { + for (uint256 i; true; ++i) { address token = vault._tokenIndexing(i); if (token == address(0)) return tokenCount; - else tokenCount += 1; + tokenCount += 1; } } -function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256) { - uint256 weightsSum = 0; - for (uint256 i; true; i++) { +function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSum) { + for (uint256 i; true; ++i) { uint256 weight = vault._weight(vault._tokenIndexing(i)); if (weight == 0) return weightsSum; - else weightsSum += weight; + weightsSum += weight; } } diff --git a/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol b/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol index 6606a8ab..9e4c4f7b 100644 --- a/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol +++ b/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol @@ -31,7 +31,7 @@ function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256) { } } -function queryVaultBalances(ICatalystV1Vault vault) returns (uint256[] memory) { +function queryVaultBalances(ICatalystV1Vault vault) view returns (uint256[] memory) { uint256 assetCount = queryAssetCount(vault); @@ -44,7 +44,7 @@ function queryVaultBalances(ICatalystV1Vault vault) returns (uint256[] memory) { return balances; } -function queryVaultWeights(ICatalystV1Vault vault) returns (uint256[] memory) { +function queryVaultWeights(ICatalystV1Vault vault) view returns (uint256[] memory) { uint256 assetCount = queryAssetCount(vault); @@ -159,8 +159,6 @@ abstract contract TestWithdrawUnbalanced is TestCommon, AVaultInterfaces { 0 ); - uint256[] memory afterSwapBalances = queryVaultBalances(vault); - // Execute the withdrawal uint256 withdrawAmount = totalSupply * withdrawalPercentage / type(uint32).max; @@ -227,8 +225,6 @@ abstract contract TestWithdrawUnbalanced is TestCommon, AVaultInterfaces { 0 ); - uint256[] memory afterSwapBalances = queryVaultBalances(vault); - uint256 withdrawAmount = totalSupply * withdrawalPercentage / type(uint32).max; Token(address(vault)).transfer(user, withdrawAmount); diff --git a/evm/test/CatalystVault/non-exploits/EvilRoutor.Securitylimit.Volatile.t.sol b/evm/test/CatalystVault/non-exploits/EvilRoutor.Securitylimit.Volatile.t.sol index 87685f39..46a162a4 100644 --- a/evm/test/CatalystVault/non-exploits/EvilRoutor.Securitylimit.Volatile.t.sol +++ b/evm/test/CatalystVault/non-exploits/EvilRoutor.Securitylimit.Volatile.t.sol @@ -135,9 +135,6 @@ abstract contract TestEvilRouterExploitVolatile is TestCommon, AVaultInterfaces true ); - uint256 target_token_index = 0; - address target_token = ICatalystV1Vault(vault)._tokenIndexing(target_token_index); - // Get the maximum allowed capacity. uint256 units = ICatalystV1Vault(vault).getUnitCapacity(); @@ -175,9 +172,6 @@ abstract contract TestEvilRouterExploitVolatile is TestCommon, AVaultInterfaces true ); - uint256 target_token_index = 0; - address target_token = ICatalystV1Vault(vault)._tokenIndexing(target_token_index); - // Get the maximum allowed capacity. uint256 units = ICatalystV1Vault(vault).getUnitCapacity() + 1; diff --git a/evm/test/CatalystVault/non-exploits/LargeDepositWithdraw.t.sol b/evm/test/CatalystVault/non-exploits/LargeDepositWithdraw.t.sol index 0ba87ca7..29d4db0c 100644 --- a/evm/test/CatalystVault/non-exploits/LargeDepositWithdraw.t.sol +++ b/evm/test/CatalystVault/non-exploits/LargeDepositWithdraw.t.sol @@ -12,12 +12,11 @@ import { AVaultInterfaces } from "test/CatalystVault/AVaultInterfaces.t.sol"; import { TestInvariant } from "test/CatalystVault/Invariant.t.sol"; -function queryAssetCount(ICatalystV1Vault vault) view returns (uint256) { - uint256 tokenCount = 0; +function queryAssetCount(ICatalystV1Vault vault) view returns (uint256 tokenCount) { for (uint256 i; true; i++) { address token = vault._tokenIndexing(i); if (token == address(0)) return tokenCount; - else tokenCount += 1; + tokenCount += 1; } } diff --git a/evm/test/TestCommon.t.sol b/evm/test/TestCommon.t.sol index e9fde865..a52643dc 100644 --- a/evm/test/TestCommon.t.sol +++ b/evm/test/TestCommon.t.sol @@ -199,7 +199,7 @@ contract TestCommon is Test, Bytes65, IMessageEscrowStructs, TestTokenFunctions, message = constructSendAsset(sourceVault, destinationVault, toAccount, units, toAssetIndex, 0, 0, address(0), uint32(block.number), uint16(0), hex""); } - function constructSendLiquidity(address sourceVault, address destinationVault, address toAccount, uint256 units, uint256[2] memory minOuts, uint256 fromAmount, uint32 blocknumber, uint16 underwriteIncentiveX16, bytes memory cdata) pure internal returns(bytes memory message) { + function constructSendLiquidity(address sourceVault, address destinationVault, address toAccount, uint256 units, uint256[2] memory minOuts, uint256 fromAmount, uint32 blocknumber, uint16 /* underwriteIncentiveX16 */, bytes memory cdata) pure internal returns(bytes memory message) { message = abi.encodePacked( CTX1_LIQUIDITY_SWAP, abi.encodePacked( From ca42640679e100f9a654f0c3d53d34664479f4f8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 17:42:50 +0200 Subject: [PATCH 05/22] feat: fix the last compiler warnings --- .../GasProfiling/localswap.gas.t.sol | 2 +- .../SecurityLimit.ReceiveLiquidity.t.sol | 3 --- .../CatalystVault/Withdraw/WithdrawNothing.t.sol | 2 +- .../Withdraw/WithdrawUnbalanced.t.sol | 16 +++++++--------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/evm/test/CatalystRouter/GasProfiling/localswap.gas.t.sol b/evm/test/CatalystRouter/GasProfiling/localswap.gas.t.sol index 349a4c82..50968e70 100644 --- a/evm/test/CatalystRouter/GasProfiling/localswap.gas.t.sol +++ b/evm/test/CatalystRouter/GasProfiling/localswap.gas.t.sol @@ -52,7 +52,7 @@ contract TestRouterLocalswapProfile is TestCommon { } function test_profile_localswap() external { - (address fromVault, address toVault) = pool1(); + (address fromVault, ) = pool1(); address fromAsset = ICatalystV1Vault(fromVault)._tokenIndexing(0); address toAsset = ICatalystV1Vault(fromVault)._tokenIndexing(1); diff --git a/evm/test/CatalystVault/SecurityLimit.ReceiveLiquidity.t.sol b/evm/test/CatalystVault/SecurityLimit.ReceiveLiquidity.t.sol index 5840d803..2d19f101 100644 --- a/evm/test/CatalystVault/SecurityLimit.ReceiveLiquidity.t.sol +++ b/evm/test/CatalystVault/SecurityLimit.ReceiveLiquidity.t.sol @@ -28,9 +28,6 @@ abstract contract TestSecurityLimitLiquiditySwap is TestCommon, AVaultInterfaces true ); - uint256 target_token_index = 0; - address target_token = ICatalystV1Vault(vault)._tokenIndexing(target_token_index); - bytes memory fake_payload = constructSendLiquidity(vault, vault, toAccount, units); fake_payload = addGARPContext(keccak256(fake_payload), address(CCI), address(CCI), fake_payload); diff --git a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol index fa3d595a..aebdb812 100644 --- a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol +++ b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol @@ -27,7 +27,7 @@ function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSu } } -function getEvenWithdrawRatios(uint256 assetCount) returns (uint256[] memory) { +function getEvenWithdrawRatios(uint256 assetCount) view returns (uint256[] memory) { uint256[] memory ratios = new uint256[](assetCount); diff --git a/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol b/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol index 9e4c4f7b..fb910cbe 100644 --- a/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol +++ b/evm/test/CatalystVault/Withdraw/WithdrawUnbalanced.t.sol @@ -13,21 +13,19 @@ import { AVaultInterfaces } from "test/CatalystVault/AVaultInterfaces.t.sol"; import { TestInvariant } from "test/CatalystVault/Invariant.t.sol"; -function queryAssetCount(ICatalystV1Vault vault) view returns (uint256) { - uint256 tokenCount = 0; - for (uint256 i; true; i++) { +function queryAssetCount(ICatalystV1Vault vault) view returns (uint256 tokenCount) { + for (uint256 i; true; ++i) { address token = vault._tokenIndexing(i); if (token == address(0)) return tokenCount; - else tokenCount += 1; + tokenCount += 1; } } -function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256) { - uint256 weightsSum = 0; - for (uint256 i; true; i++) { +function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSum) { + for (uint256 i; true; ++i) { uint256 weight = vault._weight(vault._tokenIndexing(i)); if (weight == 0) return weightsSum; - else weightsSum += weight; + weightsSum += weight; } } @@ -67,7 +65,7 @@ abstract contract TestWithdrawUnbalanced is TestCommon, AVaultInterfaces { function calculateExpectedEqualWithdrawal( ICatalystV1Vault vault, uint256 withdrawAmount - ) private returns (uint256[] memory) { + ) view private returns (uint256[] memory) { uint256 totalSupply = Token(address(vault)).totalSupply(); uint256[] memory vaultBalances = queryVaultBalances(vault); From a4f56afc8c597af777adde0f3e40ca1530c406cf Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 18:05:16 +0200 Subject: [PATCH 06/22] feat: finish documentation overview of CatalystChainInterface and test fixes --- evm/README.md | 6 +- evm/src/CatalystChainInterface.sol | 143 +++++++++--------- .../Withdraw/WithdrawNothing.t.sol | 5 +- evm/test/ExampleTest.t.sol | 2 +- 4 files changed, 79 insertions(+), 77 deletions(-) diff --git a/evm/README.md b/evm/README.md index 0be33ca7..878ee774 100644 --- a/evm/README.md +++ b/evm/README.md @@ -334,7 +334,7 @@ import { ICatalystV1Vault, ICatalystV1Structs } from "../src/ICatalystV1Vault.so contract ExampleTest is TestCommon { address vault1; address vault2; - bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0))); + bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0xdead))); function setUp() public override {...} @@ -390,7 +390,7 @@ import { ICatalystV1Vault, ICatalystV1Structs } from "../src/ICatalystV1Vault.so contract ExampleTest is TestCommon { address vault1; address vault2; - bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0))); + bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0xdead))); function setUp() public override {...} @@ -449,7 +449,7 @@ import { ICatalystV1Vault, ICatalystV1Structs } from "../src/ICatalystV1Vault.so contract ExampleTest is TestCommon { address vault1; address vault2; - bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0))); + bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0xdead))); function setUp() public override {...} diff --git a/evm/src/CatalystChainInterface.sol b/evm/src/CatalystChainInterface.sol index 0ccc9dab..0f89a842 100644 --- a/evm/src/CatalystChainInterface.sol +++ b/evm/src/CatalystChainInterface.sol @@ -122,7 +122,6 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { /// @dev Is 10 minutes if the block time is 2 seconds, 1 hour if the block time is 12 seconds. uint256 constant MIN_UNDERWRITE_BLOCK_DURATION = 1 hours / 12 seconds; - //--- Config ---// /** @notice The generalised incentives endpoint */ @@ -205,9 +204,13 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { emit MaxUnderwriteDuration(newMaxUnderwriteDuration); } + /** + * @notice Check that the incentives are set. This is important for the system work + * without external monitoring. Especially the ack gas limit is important. + * @dev Is it enforced that the gas price of ack has to be 10% higher than the gas price spent on the submitted transaction. + */ modifier checkRouteDescription(ICatalystV1Vault.RouteDescription calldata routeDescription) { // -- Check incentives -- // - ICatalystV1Vault.IncentiveDescription calldata incentive = routeDescription.incentive; // 1. Gas limits uint48 minGasChainIdentifier = minGasFor[routeDescription.chainIdentifier]; @@ -220,7 +223,6 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { if (incentive.priceOfAckGas < tx.gasprice * 11 / 10) revert NotEnoughIncentives(tx.gasprice * 11 / 10, incentive.priceOfAckGas); // -- Check Address Lengths -- // - // toAccount if (!_checkBytes65(routeDescription.toAccount)) revert InvalidBytes65Address(); @@ -242,17 +244,22 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { //-- Transparent viewer --// - /// @notice Estimate the addition verification cost beyond the - /// cost paid to the relayer. + /** + * @notice Estimate the addition verification cost beyond the cost paid to the relayer. + * @dev This is implement as a simple lookup on GARP. + */ function estimateAdditionalCost() override external view returns(address asset, uint256 amount) { (asset, amount) = GARP.estimateAdditionalCost(); } //-- Functions --// - /// @notice matches the hash of error calldata to common revert functions - /// and then reverts a relevant ack which can be exposed on the origin to provide information - /// about why the transaction didn't execute as expected. + /** + * @notice matches the hash of error calldata to common revert functions + * and then reverts a relevant ack which can be exposed on the origin to provide information + * about why the transaction didn't execute as expected. + * @param err The error in bytes + */ function _handleError(bytes memory err) pure internal returns (bytes1) { // To only get the error identifier, only use the first 8 bytes. This lets us add additional error // data for easier debugger on trace. @@ -260,17 +267,19 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // We can use memory slices to get better insight into exactly the error which occured. // This would also allow us to reuse events. // However, it looks like it will significantly increase gas costs so this works for now. - // It looks like Solidity will improve their error catch implementation which will replace this. + // It looks like Solidity will improve their error catch implementation which can replace this. if (bytes8(abi.encodeWithSelector(ExceedsSecurityLimit.selector)) == errorIdentifier) return 0x11; if (bytes8(abi.encodeWithSelector(ReturnInsufficient.selector)) == errorIdentifier) return 0x12; if (bytes8(abi.encodeWithSelector(VaultNotConnected.selector)) == errorIdentifier) return 0x13; return 0x10; // unknown error. } - /// @notice Connects this CCI with another contract on another chain. - /// @dev To simplify the implementation, each chain can only be setup once. This reduces governance risks. - /// @param remoteCCI The bytes65 encoded address on the destination chain. - /// @param remoteGARP The messaging router encoded address on the destination chain. + /** + * @notice Connects this CCI with another contract on another chain. + * @dev Each chain can only be setup once. This reduces governance risks. + * @param remoteCCI The bytes65 encoded address on the destination chain. + * @param remoteGARP The messaging router encoded address on the destination chain. + */ function connectNewChain(bytes32 chainIdentifier, bytes calldata remoteCCI, bytes calldata remoteGARP) onlyOwner checkBytes65Address(remoteCCI) override external { // Check if the chain has already been set. // If it has, we don't allow setting it as another. This would impact existing pools. @@ -287,8 +296,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { /** * @notice Packs cross-chain swap information into a bytearray and sends it to the target vault with IBC. - * @dev Callable by anyone but this cannot be abused since the connection management ensures no - * wrong messages enter a healthy vault. + * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault. * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param toAssetIndex The index of the asset the user wants to buy in the target vault. * @param U The calculated liquidity reference. (Units) @@ -297,7 +305,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { * @param fromAsset Escrow related value. The asset that was sold. * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max) * @param calldata_ Data field if a call should be made on the target chain. - * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). + * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). */ function sendCrossChainAsset( ICatalystV1Vault.RouteDescription calldata routeDescription, @@ -310,8 +318,8 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { bytes calldata calldata_ ) checkRouteDescription(routeDescription) override external payable { // We need to ensure that all information is in the correct places. This ensures that calls to this contract - // will always be decoded semi-correctly even if the input is very incorrect. This also checks that the user - // inputs into the swap contracts are correct while making the cross-chain interface flexible for future implementations. + // will always be decoded semi-correctly even if the inputs are incorrect. This also checks that the user inputs + // into the swap contracts are correct while making the cross-chain interface flexible for future implementations. // These checks are done by the modifier. // Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket @@ -355,14 +363,13 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { /** * @notice Packs cross-chain swap information into a bytearray and sends it to the target vault with IBC. - * @dev Callable by anyone but this cannot be abused since the connection management ensures no - * wrong messages enter a healthy vault. + * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault. * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param U The calculated liquidity reference. (Units) * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets] * @param fromAmount Escrow related value. The amount returned if the swap fails. * @param calldata_ Data field if a call should be made on the target chain. - * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). + * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). */ function sendCrossChainLiquidity( ICatalystV1Vault.RouteDescription calldata routeDescription, @@ -372,8 +379,8 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { bytes calldata calldata_ ) checkRouteDescription(routeDescription) override external payable { // We need to ensure that all information is in the correct places. This ensures that calls to this contract - // will always be decoded semi-correctly even if the input is very incorrect. This also checks that the user - // inputs into the swap contracts are correct while making the cross-chain interface flexible for future implementations. + // will always be decoded semi-correctly even if the input is incorrect. This also checks that the user inputs + // into the swap contracts are correct while making the cross-chain interface flexible for future implementations. // These checks are done by the modifier. // Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket @@ -382,11 +389,11 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // Encode payload. See CatalystPayload.sol for the payload definition bytes memory data = bytes.concat( CTX1_LIQUIDITY_SWAP, - bytes.concat( + // bytes.concat( bytes1(uint8(20)), // EVM addresses are 20 bytes. bytes32(0), // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more - bytes32(uint256(uint160(msg.sender))) // Use abi.encode to encode address into 32 bytes - ), + bytes32(uint256(uint160(msg.sender))), // Use abi.encode to encode address into 32 bytes + // ) routeDescription.toVault, // Length is expected to be pre-encoded. routeDescription.toAccount, // Length is expected to be pre-encoded. bytes32(U), @@ -418,7 +425,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { if (context == CTX0_ASSET_SWAP) { ICatalystV1Vault(fromVault).onSendAssetSuccess( - destinationIdentifier, // connectionId + destinationIdentifier, // connectionId data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount @@ -429,7 +436,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } if (context == CTX1_LIQUIDITY_SWAP) { ICatalystV1Vault(fromVault).onSendLiquiditySuccess( - destinationIdentifier, // connectionId + destinationIdentifier, // connectionId data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount @@ -451,25 +458,23 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { address fromVault = address(bytes20(data[ FROM_VAULT_START_EVM : FROM_VAULT_END ])); if (context == CTX0_ASSET_SWAP) { - ICatalystV1Vault(fromVault).onSendAssetFailure( - destinationIdentifier, // connectionId + return ICatalystV1Vault(fromVault).onSendAssetFailure( + destinationIdentifier, // connectionId data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount address(bytes20(data[ CTX0_FROM_ASSET_START_EVM : CTX0_FROM_ASSET_END ])), // fromAsset uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ])) // block number ); - return; } if (context == CTX1_LIQUIDITY_SWAP) { - ICatalystV1Vault(fromVault).onSendLiquidityFailure( - destinationIdentifier, // connectionId + return ICatalystV1Vault(fromVault).onSendLiquidityFailure( + destinationIdentifier, // connectionId data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ])) // block number ); - return; } // A proper message should never get here. If the message got here, we are never going to be able to properly process it. revert InvalidContext(context); @@ -483,7 +488,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { */ function receiveAck(bytes32 destinationIdentifier, bytes32 /* messageIdentifier */, bytes calldata acknowledgement) onlyGARP override external { // If the transaction executed but some logic failed, an ack is sent back with an error acknowledgement. - // This is known as "fail on ack". The package should be failed. + // We refer to this as "fail on ack". The package should be failed. // The acknowledgement is prepended the message, so we need to fetch it. // Then, we need to ignore it when passing the data to the handlers. bytes1 swapStatus = acknowledgement[0]; @@ -505,6 +510,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { function receiveMessage(bytes32 sourceIdentifier, bytes32 /* messageIdentifier */, bytes calldata fromApplication, bytes calldata message) onlyGARP verifySourceChainAddress(sourceIdentifier, fromApplication) override external returns (bytes memory acknowledgement) { bytes1 swapStatus = _receiveMessage(sourceIdentifier, message); + // We will send the original message back. While this is not ideal from a size perspective, it makes it very easy to manage. return acknowledgement = bytes.concat( swapStatus, message @@ -513,7 +519,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { /** * @notice Message handler - * @param data The IBC packet + * @param data The data packet we originally sent. * @return acknowledgement The status of the transaction after execution */ function _receiveMessage(bytes32 sourceIdentifier, bytes calldata data) internal virtual returns (bytes1 acknowledgement) { @@ -534,13 +540,12 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { return acknowledgement = _handleReceiveLiquidity(sourceIdentifier, data); } /* revert InvalidContext(context); */ - // No return here. Instead, another implementation can override this implementation. It should just keep adding ifs with returns inside: + // No revert here. Instead, another implementation can override this implementation. It should just keep adding ifs with returns inside: // acknowledgement = super._receiveMessage(...) // if (acknowledgement == 0x01) { if (context == CTXX) ...} return acknowledgement = 0x01; } - function _handleReceiveAssetFallback(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 status) { // We don't know how from_vault is encoded. So we load it as bytes. Including the length. bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ]; @@ -548,17 +553,17 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ])); try ICatalystV1Vault(toVault).receiveAsset( - sourceIdentifier, // connectionId - fromVault, // fromVault + sourceIdentifier, // connectionId + fromVault, // fromVault uint8(data[CTX0_TO_ASSET_INDEX_POS]), // toAssetIndex - address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])), // toAccount + address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])), // toAccount uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units uint256(bytes32(data[ CTX0_MIN_OUT_START : CTX0_MIN_OUT_END ])), // minOut uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount bytes(data[ CTX0_FROM_ASSET_LENGTH_POS : CTX0_FROM_ASSET_END ]), // fromAsset uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ])) // blocknumber ) returns(uint256 purchasedTokens) { - uint16 dataLength = uint16(bytes2(data[CTX0_DATA_LENGTH_START:CTX0_DATA_LENGTH_END])); + uint16 dataLength = uint16(bytes2(data[CTX0_DATA_LENGTH_START : CTX0_DATA_LENGTH_END])); if (dataLength != 0) { address dataTarget = address(bytes20(data[ CTX0_DATA_START : CTX0_DATA_START+20 ])); bytes calldata dataArguments = data[ CTX0_DATA_START+20 : CTX0_DATA_START+dataLength ]; @@ -592,7 +597,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ])) // blocknumber ) returns (uint256 purchasedVaultTokens) { - uint16 dataLength = uint16(bytes2(data[CTX1_DATA_LENGTH_START:CTX1_DATA_LENGTH_END])); + uint16 dataLength = uint16(bytes2(data[CTX1_DATA_LENGTH_START : CTX1_DATA_LENGTH_END])); if (dataLength != 0) { address dataTarget = address(bytes20(data[ CTX1_DATA_START : CTX1_DATA_START+20 ])); bytes calldata dataArguments = data[ CTX1_DATA_START+20 : CTX1_DATA_START+dataLength ]; @@ -608,7 +613,6 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } catch (bytes memory err) { return _handleError(err); } - } //--- Underwriting ---// @@ -617,7 +621,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // by pre-executing the latter part and then reserving the swap result in the escrow. /** - * @notice Returns the underwriting identifier for a Catalyst swap. + * @notice Computes the underwriting identifier for a Catalyst swap. */ function _getUnderwriteIdentifier( address targetVault, @@ -642,7 +646,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } /** - * @notice Returns the underwriting identifier for a Catalyst swap. + * @notice Computes the underwriting identifier for a Catalyst swap. */ function getUnderwriteIdentifier( address targetVault, @@ -664,7 +668,6 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { ); } - /** * @notice Underwrites a swap and check if there is a connection. * @dev sourceIdentifier and fromVault are not verified that they belong to the message. @@ -704,18 +707,18 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { * does not fill the underwrite but instead fails on ack and releases the input on the source chain. * You can use the similar function underwriteAndCheckConnection to also check the connection. * - * 2. You are underwriting the specific instance of the transaction not the inclusion of the transaction. + * 2. You are underwriting the specific instance of the transaction NOT the inclusion of the transaction. * What this means for you, is that if the block of the transaction is lost/abandon/re-entered, then - * the underwriting will not be noted unless the transaction is re-executed almost EXACTLY as it was before. + * the underwriting will not be noted unless the transaction is re-executed EXACTLY as it was before. * The most important parameter is U which is volatile and any change to the vault balances on the source chain * will cause U to be different. - * In other words, if that transaction is re-executed before or after another swap which wasn't the case before + * In other words, if that transaction is re-executed before or after another swap and it wasn't the case before, * then it won't fill the underwrite anymore and either be exeucted as an ordinary swap (to the user) or fail - * with an ack and release the original funds back to the user. + * on ack and release the original funds back to the user. * * 3. The execution of the underwrite is dependent on the correct execution of both * the minout but also the additional logic. If either fails, then the swap is not - * underwritable. As a result, it is important that the underwrite is simulated before executed. + * underwritable. To avoid wasting gas, it is important that the underwrite is simulated before executed. */ function underwrite( address targetVault, // -- Swap information @@ -746,21 +749,23 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // In these cases, it would not be possible to underwrite the second swap (or further swaps) // until after the first swap has arrived. This can be counteracted by either: // 1. Changing U. The tail of U is **volatile**. As a result, to get overlapping identifiers, - // it would have to be done deliberatly. + // it would have to be deliberatly. // 2. Add random noise to minOut or underwriteIncentiveX16. For tokens with 18 decimals, this noise can - // be as small as 1e-12 then the chance that 2 swaps collide would be 1 in a million'th. (literally 1/(1e(18-12)) = 1/1e6) + // be as small as 1e-12 then the chance that 2 swaps collide would be 1 in a million. (literally 1/(1e(18-12)) = 1/1e6) // 3. Add either a counter or noise to cdata. // For most implementations, the observation can be ignored because of the strength of point 1. // Check if the associated underwrite just arrived and has already been matched. // This is an issue when the swap was JUST underwriten, JUST arrived (and matched), AND someone else JUST underwrote the swap. - // To give the user a bit more protection, we add a buffer of size `BUFFER_BLOCKS`. - // SwapAlreadyUnderwritten vs SwapRecentlyUnderwritten: It is very likely that this block is trigger not because a swap was fulfilled but because it has already been underwritten. That is because (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) WILL ALWAYS be true when it is the case - // and SwapRecentlyUnderwritten will be the error. You might have expected the error "SwapAlreadyUnderwritten". However, we never get there so it - // cannot emit. We also cannot move that check up here, since then an external call would be made between a state check and a state modification. (reentry) - // As a result, SwapRecentlyUnderwritten will be emitted when a swap has already been underwritten EXCEPT when underwriting a swap through reentry. - // Then the reentry will underwrite the swap and the main call will fail with SwapAlreadyUnderwritten. + // To give underwriters a bit more protection, we add a buffer of size `BUFFER_BLOCKS`. + // Error SwapAlreadyUnderwritten vs Error SwapRecentlyUnderwritten: It is very likely that this block is triggered not because a swap was + // fulfilled but because it has already been underwritten. + // That is because (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) WILL ALWAYS be true when a swap has been underwritten, thus + // SwapRecentlyUnderwritten will be the error. You might have expected the error "SwapAlreadyUnderwritten". However, we never get there so + // it cannot emit. We also cannot move that check up here, since then an external call would be made between a state check and a + // state modification (reentry). As a result, SwapRecentlyUnderwritten will be emitted when a swap has already been underwritten EXCEPT + // when underwriting a swap through reentry. Then the reentry will underwrite the swap and the main call will fail with SwapAlreadyUnderwritten. UnderwritingStorage storage underwriteState = underwritingStorage[identifier]; unchecked { // Get the last touch block. For most underwrites it is going to be 0. @@ -774,7 +779,6 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { if (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) { // Check that uint96(block.number) hasn't overflowed and this is an old reference. We don't care about the underflow // as that will always return false. - // First however, we need to check that this won't underflow. if (lastTouchBlock - BUFFER_BLOCKS <= uint96(block.number)) revert SwapRecentlyUnderwritten(); } } else { @@ -783,9 +787,8 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } } - // Get the number of purchased units from the vault. This uses a custom call which doesn't return - // any assets. - // This calls escrows the purchasedTokens on the vault. + // Get the number of purchased units from the vault. + // This uses a custom call that doesn't return any assets. The cal escrows purchasedTokens on the vault. // Importantly! The connection is not checked here. Instead it is checked when the // message arrives. As a result, the underwriter should verify that a message is good. uint256 purchasedTokens = ICatalystV1Vault(targetVault).underwriteAsset( @@ -798,7 +801,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // The following number of lines act as re-entry protection. Do not add any external call inbetween these lines. // Ensure the swap hasn't already been underwritten by checking if refundTo is set. - // Notice that this is very unlikely to ever get emitted. Instead, read the comment about SwapRecentlyUnderwritten. + // This line is very unlikely to ever get emitted. Instead, read the comment about SwapRecentlyUnderwritten. if (underwriteState.refundTo != address(0)) revert SwapAlreadyUnderwritten(); uint96 underwriteExpiry = uint96(uint256(block.number) + uint256(maxUnderwritingDuration)); // Should never overflow. @@ -884,7 +887,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { if (refundAddress == address(0)) revert UnderwriteDoesNotExist(identifier); // Check that the underwriting can be expired. If the msg.sender is the refundTo address, then it can be expired at any time. - // This lets the underwriter reclaim *some* of the collateral they provided if they change their mind or observed an issue. + // This lets the underwriter reclaim _some_ of the collateral they provided if they change their mind or observed an issue. // Load the associated storage slot. uint256 underWrittenTokens = underwriteState.tokens; uint256 expiryTime = uint256(underwriteState.expiry); @@ -928,11 +931,10 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { expireShare ); } - // The underwriting storage has already been deleted. } - // It is important that any call to this functions has pre-checked the vault connection. + /** @dev It is important that any call to this functions has pre-checked the vault connection. */ function _matchUnderwrite( bytes32 identifier, address toAsset, @@ -956,8 +958,9 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { // Delete escrow information and send swap tokens directly to the underwriter. ICatalystV1Vault(vault).releaseUnderwriteAsset(refundTo, identifier, underwrittenTokenAmount, toAsset, sourceIdentifier, fromVault); - // We know only need to handle the collateral and underwriting incentive. - // We also don't have to check that the vault didn't lie to us about underwriting. + + // We now only need to handle the collateral and underwriting incentive. + // We don't have to check that the vault didn't lie to us about underwriting. // Also refund the collateral. uint256 refundAmount = underwrittenTokenAmount * ( diff --git a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol index aebdb812..c7dfee50 100644 --- a/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol +++ b/evm/test/CatalystVault/Withdraw/WithdrawNothing.t.sol @@ -27,9 +27,8 @@ function queryWeightsSum(ICatalystV1Vault vault) view returns (uint256 weightsSu } } -function getEvenWithdrawRatios(uint256 assetCount) view returns (uint256[] memory) { - - uint256[] memory ratios = new uint256[](assetCount); +function getEvenWithdrawRatios(uint256 assetCount) pure returns (uint256[] memory ratios) { + ratios = new uint256[](assetCount); for (uint256 i; i < assetCount; i++) { ratios[i] = Math.WAD / (assetCount - i); diff --git a/evm/test/ExampleTest.t.sol b/evm/test/ExampleTest.t.sol index b88ac056..9f561fab 100644 --- a/evm/test/ExampleTest.t.sol +++ b/evm/test/ExampleTest.t.sol @@ -11,7 +11,7 @@ contract ExampleTest is TestCommon { address vault1; address vault2; - bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0))); + bytes32 FEE_RECIPITANT = bytes32(uint256(uint160(0xdead))); function setUp() public override { // Calls setup() on testCommon From 3ca64076f14b56c2e910ef71fca860600ee38b0c Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 18:05:30 +0200 Subject: [PATCH 07/22] forge install: openzeppelin-contracts v5.0.2 --- evm/lib/openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/lib/openzeppelin-contracts b/evm/lib/openzeppelin-contracts index fd81a96f..92224533 160000 --- a/evm/lib/openzeppelin-contracts +++ b/evm/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 +Subproject commit 92224533b1263772b0774eec3134e132a3d7b2a6 From c5084623f4e27781fe2244dcec984e256c10aa86 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 18:22:53 +0200 Subject: [PATCH 08/22] feat: use new version of OZ in preperation of adding timelock to the repository --- evm/src/registry/CatalystDescriber.sol | 4 +- .../registry/CatalystDescriberRegistry.sol | 4 +- evm/test/mocks/token.sol | 57 +++++++------------ 3 files changed, 25 insertions(+), 40 deletions(-) diff --git a/evm/src/registry/CatalystDescriber.sol b/evm/src/registry/CatalystDescriber.sol index 15725309..f786bd0b 100644 --- a/evm/src/registry/CatalystDescriber.sol +++ b/evm/src/registry/CatalystDescriber.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import "openzeppelin-contracts/contracts/access/Ownable.sol"; +import { Ownable } from "solady/auth/Ownable.sol"; import "../interfaces/ICatalystV1VaultImmutables.sol"; import "./interfaces/ICatalystMathLibCommon.sol"; import "../interfaces/ICatalystV1Factory.sol"; @@ -52,7 +52,7 @@ contract CatalystDescriber is Contains, Ownable { constructor(address defaultOwner) payable { - _transferOwnership(defaultOwner); + _initializeOwner(defaultOwner); initBlock = block.number; } diff --git a/evm/src/registry/CatalystDescriberRegistry.sol b/evm/src/registry/CatalystDescriberRegistry.sol index d1ffc051..6458cd53 100644 --- a/evm/src/registry/CatalystDescriberRegistry.sol +++ b/evm/src/registry/CatalystDescriberRegistry.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import "openzeppelin-contracts/contracts/access/Ownable.sol"; +import { Ownable } from "solady/auth/Ownable.sol"; import { Contains } from "./lib/Contains.sol"; /** @@ -31,7 +31,7 @@ contract CatalystDescriberRegistry is Contains, Ownable { mapping(address => uint256) private _describer_version; constructor(address defaultOwner) { - _transferOwnership(defaultOwner); + _initializeOwner(defaultOwner); initBlock = block.number; } diff --git a/evm/test/mocks/token.sol b/evm/test/mocks/token.sol index c54e9b28..905d6761 100644 --- a/evm/test/mocks/token.sol +++ b/evm/test/mocks/token.sol @@ -2,54 +2,39 @@ pragma solidity ^0.8.19; -import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol"; -import "openzeppelin-contracts/contracts/access/Ownable.sol"; -import "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; +import { ERC20 } from 'solady/tokens/ERC20.sol'; -contract Token is ERC20Permit, ERC20Burnable, Ownable { - uint8 private _decimals; - address private _minter; +contract Token is ERC20 { - event MinterTransferred( - address indexed previousMinter, - address indexed newMinter - ); + string internal _name; + string internal _symbol; + uint8 internal _decimals; + + function name() public view override returns(string memory) { + return _name; + } + + function symbol() public view override returns(string memory) { + return _symbol; + } constructor( - string memory name, - string memory symbol, + string memory name_, + string memory symbol_, uint8 decimals_, uint256 initialSupply - ) ERC20(name, symbol) ERC20Permit(name) { - _mint(msg.sender, initialSupply * 10**decimals_); - _setMinter(_msgSender()); + ) { + _name = name_; + _symbol = symbol_; _decimals = decimals_; + _mint(msg.sender, initialSupply * 10**decimals_); } - function decimals() public view virtual override returns (uint8) { + function decimals() public view override returns(uint8) { return _decimals; } - function minter() public view virtual returns (address) { - return _minter; - } - - modifier onlyMinter() { - require(minter() == _msgSender(), "Ownable: caller is not the minter"); - _; - } - - function transferMinter(address newMinter) public onlyOwner { - _setMinter(newMinter); - } - - function mint(address _to, uint256 _amount) public onlyMinter { + function mint(address _to, uint256 _amount) public { _mint(_to, _amount); } - - function _setMinter(address newMinter) private { - address oldMinter = _minter; - _minter = newMinter; - emit MinterTransferred(oldMinter, newMinter); - } } From 6b2aae794d0b2157c54a30466f5dfc8e9a1caeec Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 15 May 2024 18:38:13 +0200 Subject: [PATCH 09/22] test: fix some tests that fell through the cracks --- evm/test/CatalystRouter/GasProfiling/sendasset.fail.t.sol | 2 +- evm/test/CatalystRouter/GasProfiling/sendasset.gas.t.sol | 2 +- evm/test/CatalystVault/Setup/Setup.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/test/CatalystRouter/GasProfiling/sendasset.fail.t.sol b/evm/test/CatalystRouter/GasProfiling/sendasset.fail.t.sol index e7955ccb..5d42ca65 100644 --- a/evm/test/CatalystRouter/GasProfiling/sendasset.fail.t.sol +++ b/evm/test/CatalystRouter/GasProfiling/sendasset.fail.t.sol @@ -98,7 +98,7 @@ contract TestRouterSendassetFailProfile is TestCommon { Vm.Log[] memory entries = vm.getRecordedLogs(); - (bytes32 destinationIdentifier, bytes memory recipitent, bytes memory messageWithContext) = abi.decode(entries[4].data, (bytes32, bytes, bytes)); + (bytes32 destinationIdentifier, bytes memory recipitent, bytes memory messageWithContext) = abi.decode(entries[3].data, (bytes32, bytes, bytes)); (bytes memory _metadata, bytes memory toExecuteMessage) = getVerifiedMessage(address(GARP), messageWithContext); diff --git a/evm/test/CatalystRouter/GasProfiling/sendasset.gas.t.sol b/evm/test/CatalystRouter/GasProfiling/sendasset.gas.t.sol index 2a8f7c07..ca0ad451 100644 --- a/evm/test/CatalystRouter/GasProfiling/sendasset.gas.t.sol +++ b/evm/test/CatalystRouter/GasProfiling/sendasset.gas.t.sol @@ -100,7 +100,7 @@ contract TestRouterSendassetProfile is TestCommon { Vm.Log[] memory entries = vm.getRecordedLogs(); - (bytes32 destinationIdentifier, bytes memory recipitent, bytes memory messageWithContext) = abi.decode(entries[4].data, (bytes32, bytes, bytes)); + (bytes32 destinationIdentifier, bytes memory recipitent, bytes memory messageWithContext) = abi.decode(entries[3].data, (bytes32, bytes, bytes)); (bytes memory _metadata, bytes memory toExecuteMessage) = getVerifiedMessage(address(GARP), messageWithContext); diff --git a/evm/test/CatalystVault/Setup/Setup.t.sol b/evm/test/CatalystVault/Setup/Setup.t.sol index 7b62b574..71eb9909 100644 --- a/evm/test/CatalystVault/Setup/Setup.t.sol +++ b/evm/test/CatalystVault/Setup/Setup.t.sol @@ -51,7 +51,7 @@ abstract contract TestSetup is TestCommon, AVaultInterfaces { // Verify the 'VaultDeployed' event Vm.Log[] memory logs = vm.getRecordedLogs(); - Vm.Log memory vaultDeployedLog = logs[12]; + Vm.Log memory vaultDeployedLog = logs[logs.length - 1]; assertEq(vaultDeployedLog.topics[1], bytes32(uint256(uint160(vaultTemplate)))); assertEq(vaultDeployedLog.topics[2], bytes32(uint256(uint160(address(CCI))))); From 624fc649ab4d17bca65736bec00d306276752bce Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 16 May 2024 07:34:45 +0200 Subject: [PATCH 10/22] feat: timelock deployment script --- evm/script/TimeLock.s.sol | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 evm/script/TimeLock.s.sol diff --git a/evm/script/TimeLock.s.sol b/evm/script/TimeLock.s.sol new file mode 100644 index 00000000..aadc999b --- /dev/null +++ b/evm/script/TimeLock.s.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; + +import { BaseMultiChainDeployer } from "./BaseMultiChainDeployer.s.sol"; + +import { TimelockController } from "openzeppelin-contracts/contracts/governance/TimelockController.sol"; + +contract DeployTimelock is BaseMultiChainDeployer { + + uint256 MIN_DELAY = 1 days; + + function _deployTimelockController(bytes32 salt, uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) internal returns(TimelockController) { + return new TimelockController{salt: salt}(minDelay, proposers, executors, admin); + } + + function deployTimelockController(bytes32 salt, address initalProposer) public returns(TimelockController) { + address[] memory proposers = new address[](1); + proposers[0] = initalProposer; + address[] memory executors = new address[](1); + executors[0] = address(0); // Let anyone execute. + address admin = address(0); + + return _deployTimelockController(salt, MIN_DELAY, proposers, executors, admin); + } + + function run() public { + bytes32 salt = bytes32(0); + address initalProposer = address(0xE759cBa7dE5bF6E024BcbdD01941fc3b1713D2FC); + deployTimelockController(salt, initalProposer); + } +} + From 38df64b43eca96c4f9c81b830375ddbf58368c36 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 16 May 2024 18:32:42 +0200 Subject: [PATCH 11/22] feat: further proof reading --- evm/script/TimeLock.s.sol | 2 +- evm/src/CatalystChainInterface.sol | 4 +- evm/src/CatalystFactory.sol | 60 +++++-- evm/src/CatalystPayload.sol | 2 +- evm/src/CatalystVaultAmplified.sol | 241 +++++++++++++++-------------- 5 files changed, 184 insertions(+), 125 deletions(-) diff --git a/evm/script/TimeLock.s.sol b/evm/script/TimeLock.s.sol index aadc999b..dc63f137 100644 --- a/evm/script/TimeLock.s.sol +++ b/evm/script/TimeLock.s.sol @@ -26,7 +26,7 @@ contract DeployTimelock is BaseMultiChainDeployer { } function run() public { - bytes32 salt = bytes32(0); + bytes32 salt = bytes32(0x67e6613ce8cbbe2cbdf83a49a42f7506ba4f34ef5ce61e83c3935e85cac6a4a6); address initalProposer = address(0xE759cBa7dE5bF6E024BcbdD01941fc3b1713D2FC); deployTimelockController(salt, initalProposer); } diff --git a/evm/src/CatalystChainInterface.sol b/evm/src/CatalystChainInterface.sol index 0f89a842..3b99ebd0 100644 --- a/evm/src/CatalystChainInterface.sol +++ b/evm/src/CatalystChainInterface.sol @@ -295,7 +295,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } /** - * @notice Packs cross-chain swap information into a bytearray and sends it to the target vault with IBC. + * @notice Packs cross-chain swap information into a bytearray to send to the destination cross-chain interface. * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault. * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param toAssetIndex The index of the asset the user wants to buy in the target vault. @@ -362,7 +362,7 @@ contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 { } /** - * @notice Packs cross-chain swap information into a bytearray and sends it to the target vault with IBC. + * @notice Packs cross-chain swap information into a bytearray and sends it to the destination cross-chain interface. * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault. * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param U The calculated liquidity reference. (Units) diff --git a/evm/src/CatalystFactory.sol b/evm/src/CatalystFactory.sol index a0ad0dc3..6e1ff43a 100644 --- a/evm/src/CatalystFactory.sol +++ b/evm/src/CatalystFactory.sol @@ -13,35 +13,69 @@ uint256 constant MAX_GOVERNANCE_FEE_SHARE = 75e16; // 75% /** * @title Catalyst Swap Factory * @author Cata Labs Inc. - * @notice Allows permissionless deployment Catalyst vaults - * and defines governance address for vaults to read. + * @notice Allows permissionless deployment Catalyst vaults and defines governance address for vaults to read. + * Importantly, this vault allows anyone to deploy a vault using any vault template and vault cross-chain interface. + * As a result, just because a vault was deployed using this contract does not imply that it is safe. Vaults should + * be cross-checked for their template, cross-chain interface, and if they are setup correctly. It may even be + * that some vault templates only work with some cross-chain interfaces. + * + * Using the reference Catalyst Vault Templates, the owner of the factory is also the _owner_ of the Vaults. + * They have certain privilege that may be able to be abused depending on the vault. One of these is configurating + * fees. As a result: * !The owner of the factory must be a timelock! */ contract CatalystFactory is Ownable, ICatalystV1Factory { - /// @notice A mapping which describes if a vault has been created by this factory. Indexed by chainInterface then vault address. + error InvalidAssetCount(); + error InvalidWeightCount(); + error FeeDestinationAddress0(); + error MaximumGovernanceFeeShare(); + + /** + * @notice A mapping which describes if a vault has been created by this factory. + * Indexed by chainInterface then vault address. + */ mapping(address => mapping(address => bool)) public isCreatedByFactory; - /// @notice Default governance fee. When a vault is created, this is the governance fee applied to that vault. + /** + * @notice Default governance fee. When a vault is created, this is the governance fee applied to that vault. + */ uint64 public _defaultGovernanceFee; + /** + * @notice The address to send governance fees to. + * @dev Not enforced by the factory but vaults are expected to follow it. + */ address public _governanceFeeDestination; + // The 2 above storage slots are packed together. + constructor(address defaultOwner) payable { _initializeOwner(defaultOwner); _governanceFeeDestination = defaultOwner; } + /** + * @notice Set default governance fee share. + * @dev The set governance fee only applies to newly created vaults. Vaults have to be individual modified post creation. + * Is in WAD, (1e18 terms). So 1e16 is 1%. Cannot be set larger than 75% (75e16). + */ function setDefaultGovernanceFee(uint64 fee) override public onlyOwner { - require(fee <= MAX_GOVERNANCE_FEE_SHARE); // dev: Maximum GovernanceFeeSare exceeded. + if (fee > MAX_GOVERNANCE_FEE_SHARE) revert MaximumGovernanceFeeShare(); emit SetDefaultGovernanceFee(fee); _defaultGovernanceFee = fee; } + /** + * @notice Set the recipient of the governance. + * @dev It is expected that vaults read this value and send their governance fees here. + * This contract has no way to enforce if vaults honour this value. + * Cannot be set to address(0). If wish to burn, set to 0xdead. + */ function setGovernanceFeeDestination(address feeDestination) override public onlyOwner { - require(feeDestination != address(0), "Fee destination cannot be address(0)"); + if (feeDestination == address(0)) revert FeeDestinationAddress0(); emit SetGovernanceFeeDestination(feeDestination); _governanceFeeDestination = feeDestination; @@ -49,7 +83,15 @@ contract CatalystFactory is Ownable, ICatalystV1Factory { /** * @notice Deploys a Catalyst vault, funds the vault with tokens, and calls setup. - * @dev The deployer needs to set approvals for this contract before calling deployVault + * When deploying vaults, there are 2 stages that needs to happen: + * 1. We need to setup the vaults with the correct configuration. + * 2. We need to set the vault swap curve. This consists of setting assets, vaults, amplification, etc. + * The reason it is done in 2 steps is because of the stack limit. By spreading it across 2 calls, it is + * cheaper gas wise. + * This is done in a safe way by expecting both of these init. calls to be done in a single transaction. + * As a result, the vaults are never left in a vulnerable state. It is expected that the latter call + * (initializeSwapCurves) completes initialization and blocks the setup functions from being called again. + * @dev The deployer needs to set relevant approvals to this contract before calling deployVault. * @param vaultTemplate The template the transparent proxy should target. * @param assets The list of assets the vault should support. * @param init_balances The initial balances of the vault. (Should be approved) @@ -73,9 +115,9 @@ contract CatalystFactory is Ownable, ICatalystV1Factory { address chainInterface ) override external returns (address vault) { // Check if an invalid asset count has been provided - require(assets.length != 0); // dev: invalid asset count + if (assets.length == 0) revert InvalidAssetCount(); // Check if an invalid weight count has been provided - require(weights.length == assets.length); //dev: invalid weight count + if (weights.length != assets.length) revert InvalidWeightCount(); // init_balances length not checked: if shorter than assets, the funds transfer loop // will fail. If longer, values will just be ignored. diff --git a/evm/src/CatalystPayload.sol b/evm/src/CatalystPayload.sol index 859baa54..a51124d9 100644 --- a/evm/src/CatalystPayload.sol +++ b/evm/src/CatalystPayload.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -// Catalyst IBC payload structure *********************************************************************************************** +// Catalyst Messaging payload structure *********************************************************************************************** // Note: Addresses have 65 bytes reserved, however, the first byte should only be used for the address size. // // Common Payload (beginning) diff --git a/evm/src/CatalystVaultAmplified.sol b/evm/src/CatalystVaultAmplified.sol index 3f96bd4f..e7dd00ee 100644 --- a/evm/src/CatalystVaultAmplified.sol +++ b/evm/src/CatalystVaultAmplified.sol @@ -34,32 +34,39 @@ import "./ICatalystV1Vault.sol"; * After deployment of the proxy, call setup(...) AND initializeSwapCurves(...). * This will initialize the vault and prepare it for cross-chain transactions. * However, only the Catalyst factory is allowed to perform these functions. + * It is important that both setup(...) AND initializeSwapCurves(...) are called + * in the same transaciton, as otherwise the vault is not fully finalised and + * may be configured incorrectly by a third-party. * * If connected to a supported cross-chain interface, call * setConnection to connect the vault with vaults on other chains. * - * Finally, call finishSetup to give up the creators's control + * Finally, call finishSetup to give up the creator's control * over the vault. - * !If finishSetup is not called, the vault can be drained by the creators! + * !If finishSetup is not called, the vault can be drained by the creator! */ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { //--- Storage ---// - /// @notice Adjustment used to remove a certain escrow amount from the balance 0 computation - /// because the units already have been subtracted. + /** + * @notice Adjustment used to remove a certain escrow amount from the balance 0 computation + */ mapping(address => uint256) public _underwriteEscrowMatchBalance0; //--- ERRORS ---// // Errors are defined in interfaces/ICatalystV1VaultErrors.sol - //--- Config ---// - // Minimum time parameter adjustments can be made over. + + /** @notice Minimum time parameter adjustments can be made over. */ uint256 constant MIN_ADJUSTMENT_TIME = 7 days; - // When the swap is a very small size of the vault, the swaps - // returns slightly more. To counteract this, an additional fee - // slightly larger than the error is added. The below constants - // determines when this fee is added and the size. + + /** + * @dev When the swap is a very small size of the vault, the + * swaps returns slightly more. To counteract this, an additional + * fee slightly larger than the error is added. The below + * constants determines when this fee is added and the size. + */ uint256 constant SMALL_SWAP_RATIO = 1e12; uint256 constant SMALL_SWAP_RETURN = 95e16; @@ -68,9 +75,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { //-- Variables --// int64 public _oneMinusAmp; int64 public _targetAmplification; - - // To keep track of pool ownership, the vault needs to keep track of - // the local unit balance. That is, do other vaults own or owe assets to this vault? + + /** + * @dev To keep track of pool ownership, the vault needs to keep track of + * the local unit balance. That is, do other vaults own or owe assets to this vault? + */ int256 public _unitTracker; constructor(address factory_, address mathlib_) payable CatalystVaultCommon(factory_, mathlib_) {} @@ -82,10 +91,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * done atomically! * * If 0 of a token in assets is provided, the setup reverts. - * @param assets A list of the token addresses associated with the vault + * @param assets A list of token addresses to be associated with the vault. * @param weights Weights brings the price into a true 1:1 swap. That is: * i_t \cdot W_i = j_t \cdot W_j \forall i, j when P_i(i_t) = P_j(j_t). - * in other words, weights are used to compensate for the difference in decimals. (or non 1:1 swaps.) + * in other words, weights are used to compensate for the difference in decimals. (or non 1:1.) * @param amp Amplification factor. Should be < 10**18. * @param depositor The address depositing the initial token balances. */ @@ -106,7 +115,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // (and the vault shouldn't be used by anyone until its configuration has been finalised). // In any case, the factory does check for valid assets/weights arguments to prevent erroneous configurations. // Note Since assets.len != 0 is not checked, the initial depositor may invoke this function many times, resulting - // on vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is + // in vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is // an arbitrary number; the value of the vault tokens is determined by the ratio of the vault asset balances and vault // tokens supply once setup has finalized. Furthermore, the vault should not be used until setup has finished and the // vault configuration has been verified. @@ -133,6 +142,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // The contract expects the tokens to have been sent to it before setup is // called. Make sure the vault has more than 0 tokens. // Reverts if tokenAddress is address(0). + // This contract uses safeTransferLib from Solady. When "safeTransfering", there is no + // check for smart contract code. This could be an issue if non-tokens are allowed to enter + // the pool, as then the pool could be expoited by later deploying an address to the addres. + // The below check ensure that there is a token deployed to the contract. uint256 balanceOfSelf = ERC20(tokenAddress).balanceOf(address(this)); require(balanceOfSelf != 0); // dev: 0 tokens provided in setup. initialBalances[it] = balanceOfSelf; @@ -166,7 +179,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } /** - * @notice Re-computes the security limit incase funds have been sents to the vault + * @notice Re-computes the security limit incase funds have been sent to the vault. */ function updateMaxUnitCapacity() external { uint256 maxUnitCapacity; @@ -204,14 +217,12 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256 A = ERC20(fromAsset).balanceOf(address(this)); uint256 W = _weight[fromAsset]; - // If 'fromAsset' is not part of the vault (i.e. W is 0) or if 'amount' and // the vault asset balance (i.e. 'A') are both 0 this will revert, since 0**p is // implemented as exp(ln(0) * p) and ln(0) is undefined. uint256 U = _calcPriceCurveArea(amount, A, W, _oneMinusAmp); - // If the swap is a very small portion of the vault - // Add an additional fee. This covers mathematical errors. + // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors. unchecked { //SMALL_SWAP_RATIO is not zero, and if U * SMALL_SWAP_RETURN overflows, less is returned to the user. // Also U * SMALL_SWAP_RETURN cannot overflow, since U depends heavily on amount/A. If this number is small (which it is in this case) then U is also "small". if (A/SMALL_SWAP_RATIO >= amount) return U * SMALL_SWAP_RETURN / FixedPointMathLib.WAD; @@ -222,7 +233,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Computes the output of ReceiveAsset excluding fees. - * @dev Reverts if 'toAsset' is not a token in the vault + * @dev Reverts if 'toAsset' is not a token in the vault. * Does not contain the swap fee. * @param toAsset The address of the token to buy. * @param U The number of units to convert. @@ -236,7 +247,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256 B = ERC20(toAsset).balanceOf(address(this)) - _escrowedTokens[toAsset]; uint256 W = _weight[toAsset]; - // If someone were to purchase a token which is not part of the vault on setup + // If someone were to purchase a token that is not part of the vault on setup // they would just add value to the vault. We don't care about it. // However, it will revert since the solved integral contains U/W and when // W = 0 then U/W returns division by 0 error. @@ -265,17 +276,16 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256 W_B = _weight[toAsset]; int256 oneMinusAmp = _oneMinusAmp; - output = _calcPriceCurveLimit(_calcPriceCurveArea(amount, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp); // _calcCombinedPriceCurves(amount, A, B, W_A, W_B, oneMinusAmp); + output = _calcPriceCurveLimit(_calcPriceCurveArea(amount, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp); - // If the swap is a very small portion of the vault - // Add an additional fee. This covers mathematical errors. + // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors. unchecked { //SMALL_SWAP_RATIO is not zero, and if output * SMALL_SWAP_RETURN overflows, less is returned to the user if (A/SMALL_SWAP_RATIO >= amount) return output * SMALL_SWAP_RETURN / FixedPointMathLib.WAD; } } /** - * @notice Deposits a user-configurable amount of tokens. + * @notice Deposits a user-configurable amount of tokens. * @dev The swap fee is imposed on deposits. * Requires approvals for all tokens within the vault. * It is advised that the deposit matches the vault's %token distribution. @@ -329,11 +339,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { { // wa^(1-k) is required twice. It is F(A) in the - // sendAsset equation and part of the wa_0^(1-k) calculation + // sendAsset equation and part of the wa_0^(1-k) calculation. // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. int256 wab = 0; - if (weightAssetBalance != 0){ - // calculate balance 0 with the underwritten amount gone. + if (weightAssetBalance != 0) { + // calculate balance 0 with the underwritten amount subtracted. wab = FixedPointMathLib.powWad( int256((weightAssetBalance - _underwriteEscrowMatchBalance0[token] * weight) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp @@ -349,8 +359,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } // This line is the origin of the stack too deep issue. - // since it implies we cannot move intU += before this section. - // which would solve the issue. + // Moving intU += before this section would solve the issue but it is not possible since it would evaluate incorrectly. // Save gas if the user provides no tokens, as the rest of the loop has no effect in that case if (tokenAmount == 0) { unchecked { @@ -360,10 +369,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } // int_A^{A+x} f(w) dw = F(A+x) - F(A). - // This is -F(A). Since we are subtracting first, - // U (i.e. intU) must be able to go negative. unchecked { - // |intU| < weightedAssetBalanceSum since U F(A+x) is added to intU in the lines after this. + // This is -F(A). Since we are subtracting first, U (i.e. intU) must be able to go negative. + // |intU| < weightedAssetBalanceSum since F(A+x) is added to intU in the lines after this. intU -= wab; } } @@ -371,9 +379,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Add F(A+x). // This computation will not revert, since we know tokenAmount != 0. intU += FixedPointMathLib.powWad( - int256((weightAssetBalance + weight * tokenAmount) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails + int256((weightAssetBalance + weight * tokenAmount) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); + } assetDepositSum += tokenAmount * weight; @@ -394,7 +403,6 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // While one may assume _usedUnitCapacity < _maxUnitCapacity, this is not always the case. As such, this remains checked. _usedUnitCapacity += assetDepositSum; - // Compute the reference liquidity. // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus @@ -402,10 +410,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. // The result will be correct once it is casted to uint256. - it_times_walpha_amped = uint256(weightedAssetBalanceSum - _unitTracker); // By design, weightedAssetBalanceSum > _unitTracker + it_times_walpha_amped = uint256(weightedAssetBalanceSum - _unitTracker); // By design, weightedAssetBalanceSum > _unitTracker // Notice that we are not dividing by it. That is because we would then later have to multiply by it. } - } // Subtract fee from U (intU). This prevents people from using deposit and withdrawal as a method of swapping. @@ -417,8 +424,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // U (intU) is generally small, so the below equation should not overflow. // If it does, it has to be (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee)) that overflows. // In which case, something close to 0 will be returned. When divided by FixedPointMathLib.WAD - // it will return 0. - // The casting to int256 is then 0. + // it will return 0. The casting to int256 is then 0. intU = int256( // intU shouldn't be negative but the above check ensures it is ALWAYS positive. (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee))/FixedPointMathLib.WAD @@ -427,14 +433,12 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { int256 oneMinusAmpInverse = WADWAD / oneMinusAmp; - // On totalSupply(). Do not add escrow amount, as higher amount - // results in a larger return. - vaultTokens = _calcPriceCurveLimitShare(uint256(intU), totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); // uint256: intU is positive by design. + // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return. + vaultTokens = _calcPriceCurveLimitShare(uint256(intU), totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); // uint256: intU is positive by design. // Check that the minimum output is honoured. if (minOut > vaultTokens) revert ReturnInsufficient(vaultTokens, minOut); - // Mint the desired number of vault tokens to the user. _mint(msg.sender, vaultTokens); @@ -448,7 +452,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev This is the cheapest way to withdraw and only way to withdraw 100% of the liquidity. * @param vaultTokens The number of vault tokens to burn. * @param minOut The minimum token output. If less is returned, the transaction reverts. - * @return amounts memory An array containing the amounts withdrawn. + * @return amounts An array containing the amounts withdrawn. */ function withdrawAll( uint256 vaultTokens, @@ -512,11 +516,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will // be correct once it is casted to uint256. - walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it; // By design, weightedAssetBalanceSum > _unitTracker + walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it; // By design, weightedAssetBalanceSum > _unitTracker } } - // For later event logging, the amounts transferred from the vault are stored. amounts = new uint256[](MAX_ASSETS); @@ -532,7 +535,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256 pt_fraction = ((ts - vaultTokens) * FixedPointMathLib.WAD) / ts; // If pt_fraction == 0 => 0^oneMinusAmp = powWad(0, oneMinusAmp) => exp(ln(0) * oneMinusAmp) which is undefined. - // However, we know what 0^oneMinusAmp is: 0^oneMinusAmp is: 0!. So we just set it to 0. + // However, we know what 0^oneMinusAmp is: 0!. So we just set it to 0. innerdiff = pt_fraction == 0 ? walpha_0_ampped : FixedPointMathLib.mulWad( walpha_0_ampped, FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value @@ -570,12 +573,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // the transaction reverts. If that is the case, use withdrawAll. // This quirk is "okay", since it means fewer tokens are always returned. - // Since tokens are withdrawn, the change is negative. As such, multiply the - // equation by -1. + // Since tokens are withdrawn, the change is negative. As such, multiply the equation by -1. weightedTokenAmount = FixedPointMathLib.mulWad( weightedTokenAmount, FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // The inner is between 0 and 1. Power of < 1 is always between 0 and 1. - int256(FixedPointMathLib.divWadUp( // 0 < innerdiff < ampWeightAssetBalance => < 1 thus casting never overflows. + int256(FixedPointMathLib.divWadUp( // 0 < innerdiff < ampWeightAssetBalance => < 1 thus casting never overflows. ampWeightAssetBalance - innerdiff, ampWeightAssetBalance )), @@ -617,7 +619,6 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { _usedUnitCapacity -= totalWithdrawn; } } - // Emit the event emit VaultWithdraw(msg.sender, vaultTokens, amounts); @@ -639,18 +640,17 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256[] calldata minOut ) nonReentrant external override returns(uint256[] memory amounts) { // _updateAmplification(); - // Burn the desired number of vault tokens to the user. - // If they don't have it, it saves gas. + // Burn the desired number of vault tokens to the user. If they don't have it, it saves gas. // * Remember to add vaultTokens when accessing totalSupply() _burn(msg.sender, vaultTokens); // (For everyone else, it is probably cheaper to burn last. However, burning here makes - // the implementation more similar to the volatile one) + // the implementation more similar to the volatile one). int256 oneMinusAmp = _oneMinusAmp; // Cache weights and balances. address[MAX_ASSETS] memory tokenIndexed; - uint256[MAX_ASSETS] memory effAssetBalances; // The 'effective' balances (compensated with the escrowed balances) + uint256[MAX_ASSETS] memory effAssetBalances; // The 'effective' balances (compensated with the escrowed balances) uint256 U = 0; // Compute walpha_0 to find the reference balances. This lets us evaluate the @@ -678,14 +678,13 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Later we need to use the asset balances. Since it is for a withdrawal, we should subtract the escrowed tokens // such that less is returned. effAssetBalances[U] = ab - _escrowedTokens[token]; - + // subtract _underwriteEscrowMatchBalance0 since this is used for balance0. uint256 weightAssetBalance = weight * (ab - _underwriteEscrowMatchBalance0[token]); - // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0. int256 wab = 0; - if (weightAssetBalance != 0){ + if (weightAssetBalance != 0) { wab = FixedPointMathLib.powWad( int256(weightAssetBalance * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp @@ -706,7 +705,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { unchecked { // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will // be correct once it is casted to uint256. - walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / U; // By design, weightedAssetBalanceSum > _unitTracker + walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / U; // By design, weightedAssetBalanceSum > _unitTracker } // set U = number of tokens in the vault. But that is exactly what it is. @@ -723,8 +722,8 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // FixedPointMathLib.WAD is moved in front to make U positive. U *= FixedPointMathLib.mulWad( walpha_0_ampped, - FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value - int256(pt_fraction), // If casting overflows to a negative number, powWad fails + FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value + int256(pt_fraction), // If casting overflows to a negative number, powWad fails oneMinusAmp )) ); @@ -759,7 +758,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Ensure the output satisfies the user. if (minOut[it] > tokenAmount) revert ReturnInsufficient(tokenAmount, minOut[it]); - // Store amount for withdraw event + // Store amount for withdraw event. amounts[it] = tokenAmount; // Transfer the released tokens to the user. @@ -772,7 +771,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { ++it; } } - // Ensure all units are used. This should be done by setting at least one withdrawRatio to 1. + // Ensure all units are used. This should be done by setting at least one withdrawRatio to 1 (WAD). if (U != 0) revert UnusedUnitsAfterWithdrawal(U); // Decrease the security limit by the amount withdrawn. @@ -793,8 +792,8 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice A swap between 2 assets within the vault. Is atomic. * @param fromAsset The asset the user wants to sell. - * @param toAsset The asset the user wants to buy - * @param amount The amount of fromAsset the user wants to sell + * @param toAsset The asset the user wants to buy. + * @param amount The amount of fromAsset the user wants to sell. * @param minOut The minimum output the user wants. Otherwise, the transaction reverts. * @return out The number of tokens purchased. */ @@ -815,15 +814,14 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Transfer tokens to the user and collect tokens from the user. // The order doesn't matter, since the function is reentrant protected. - // The transaction which is most likly to revert is first. + // The transaction that is most likly to revert is first. SafeTransferLib.safeTransferFrom(fromAsset, msg.sender, address(this), amount); SafeTransferLib.safeTransfer(toAsset, msg.sender, out); // Collect potential governance fee _collectGovernanceFee(fromAsset, fee); - // For amplified vaults, the security limit is based on the sum of the tokens - // in the vault. + // For amplified vaults, the security limit is based on the sum of the tokens in the vault. uint256 weightedAmount = amount * _weight[fromAsset]; uint256 weightedOut = out * _weight[toAsset]; // The if statement ensures the independent calculations never under or overflow. @@ -836,7 +834,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { emit LocalSwap(msg.sender, fromAsset, toAsset, amount, out); } - /// @notice Handles common logic between both sendAsset implementations + /** @notice Handles common logic between both sendAsset implementations */ function _sendAsset( RouteDescription calldata routeDescription, address fromAsset, @@ -869,7 +867,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { ); // Store the escrow information. For that, an index is required. Since we need this index twice, we store it. - // Only information which is relevant for the escrow has to be hashed. (+ some extra for randomisation) + // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation) // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData bytes32 sendAssetHash = _computeSendAssetHash( routeDescription.toAccount, // Ensures no collisions between different users @@ -913,15 +911,15 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Initiate a cross-chain swap by purchasing units and transfering the units to the target vault. - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset The asset the user wants to sell. * @param toAssetIndex The index of the asset the user wants to buy in the target vault. * @param amount The number of fromAsset to sell to the vault. * @param minOut The minimum number output of tokens on the target chain. - * @param fallbackUser If the transaction fails, send the escrowed funds to this address - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max) + * @param fallbackUser If the transaction fails, send the escrowed funds to this address. + * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. - * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. + * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). At maximum 65535 bytes can be passed. * @return U The number of units bought. */ function sendAsset( @@ -949,6 +947,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Calculate the units bought. U = calcSendAsset(fromAsset, amount - fee); + // Execute the common sendAsset logic. _sendAsset( routeDescription, fromAsset, @@ -965,17 +964,19 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units. - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @dev This function is intended to match an existing underwrite. Since normal sendAssets aren't "exact" in regards to U, + * this functions makes it easier to hit a specific underwrite. + * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. * @param fromAsset The asset the user wants to sell. * @param toAssetIndex The index of the asset the user wants to buy in the target vault. * @param amount The number of fromAsset to sell to the vault. * @param minOut The minimum number output of tokens on the target chain. * @param minU The minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max) + * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. - * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed.. - * @return minU The number of units minted. + * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). At maximum 65535 bytes can be passed.. + * @return U Always equal to minOut, as that is the number of units to be used on the destination chain. */ function sendAssetFixedUnit( ICatalystV1Structs.RouteDescription calldata routeDescription, @@ -987,7 +988,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ - ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256) { + ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. // It would also be a silly fallback address. require(fallbackUser != address(0)); @@ -1000,15 +1001,18 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the units bought. - uint256 U = calcSendAsset(fromAsset, amount - fee); + U = calcSendAsset(fromAsset, amount - fee); if (U < minU) revert ReturnInsufficient(U, minU); + // The set number of units bought to minU. + U = minU; + // Execute the common sendAsset logic. _sendAsset( routeDescription, fromAsset, toAssetIndex, - minU, + U, amount, fee, minOut, @@ -1016,13 +1020,16 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { underwriteIncentiveX16, calldata_ ); - - return minU; } /** - * @notice Completes a cross-chain swap by converting units to the desired token. - * @dev Internal function that implement the majority of swap logic. + * @notice Handles common logic associated with the completion of a cross-chain swap. + * This function convert incoming Units (expected to be from an incoming cross-chain swap) into a specific token. + * @dev This function is intended to finalise a receiveAsset call. + * @param toAsset The asset to buy with the units. + * @param U Incoming units to be turned into vaults tokens. + * @param minOut The minimum number of tokens to purchase. Will revert if less. + * @return purchasedTokens The number of toAsset bought. Is greater than minOut. */ function _receiveAsset( address toAsset, @@ -1034,7 +1041,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Calculate the swap return value. Fee is always taken on the sending token. purchasedTokens = calcReceiveAsset(toAsset, U); - // Check if the swap is according to the swap limits + // Check if the swap is according to the swap limits. uint256 deltaSecurityLimit = purchasedTokens * _weight[toAsset]; if (_maxUnitCapacity <= deltaSecurityLimit) revert ExceedsSecurityLimit(); unchecked { @@ -1099,9 +1106,8 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } //--- Liquidity swapping ---// - // Because of the way vault tokens work in a pool, there - // needs to be a way for users to easily get a distributed stake. - // Liquidity swaps is a macro implemented at the smart contract level equivalent to: + // Because of the way vault tokens work in a pool, there needs to be a way for users to easily get + // a distributed stake. Liquidity swaps is a macro implemented at the smart contract level equivalent to: // 1. Withdraw tokens. // 2. Convert tokens to units & transfer to target vault. // 3. Convert units to an even mix of tokens. @@ -1113,13 +1119,14 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev Whenever balance0 is computed, the true balance should be used instead of the one * modifed by the escrow. This is because balance0 is constant during swaps. Thus, if the * balance was modified, it would not be constant during swaps. - * The function also returns the vault asset count as it is always used in conjunction with walpha_0_ampped. The external function does not. + * The function also returns the vault asset count as it is always used in conjunction with walpha_0_ampped. + * The external function does not. * @return walpha_0_ampped Balance0**(1-amp) * @return it the vault asset count */ function _computeBalance0(int256 oneMinusAmp) internal view returns(uint256 walpha_0_ampped, uint256 it) { // Compute walpha_0 to find the reference balances. This lets us evaluate the - // number of tokens the vault should have If the price in the pool is 1:1. + // number of tokens the vault should have IF the price in the pool is 1:1. // This is a balance0 implementation. The balance 0 implementation here is reference. int256 weightedAssetBalanceSum = 0; @@ -1158,8 +1165,12 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } /** - * @notice Computes balance0 - * @dev Is constant for swaps + * @notice Computes balance0 for the pool. + * @dev This can be used as a local invariant. Is constant (or slowly increasing) for swaps. + * Deposits and withdrawals change balance0 and as such, it cannot be used to examine if a vault is secure + * by it self. + * This function does not return balance0 as it, is returns a weighted amplified form. + * For pretty much any real world usage of the function, this is the relevant form. * @return walpha_0 Balance0**(1-amp) */ function computeBalance0() external view returns(uint256 walpha_0) { @@ -1178,7 +1189,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units. * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted * directly into units through the following equation: U = N · wa^(1-k) · (((PT + pt)/PT)^(1-k) - 1) - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault, and relaying incentive. * @param vaultTokens The number of vault tokens to exchange. * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. @@ -1202,9 +1213,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { require(toAccount.length == 65); // dev: Account addresses are uint8 + 64 bytes. */ - // Update amplification // _updateAmplification(); + // When accesssing totalSupply, remember that we have already burnt the incoming vaultTokens. _burn(msg.sender, vaultTokens); int256 oneMinusAmp = _oneMinusAmp; @@ -1220,13 +1231,13 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { U = it * FixedPointMathLib.mulWad( walpha_0_ampped, - uint256(FixedPointMathLib.powWad( // Always casts a positive value - int256(pt_fraction), // If casting overflows to a negative number, powWad fails + uint256(FixedPointMathLib.powWad( // Always casts a positive value + int256(pt_fraction), // If casting overflows to a negative number, powWad fails oneMinusAmp )) - FixedPointMathLib.WAD ); // onSendLiquiditySuccess requires casting U to int256 to update the _unitTracker and must never revert. Check for overflow here. - require(U < uint256(type(int256).max)); // int256 max fits in uint256 + require(U < uint256(type(int256).max)); // int256 max fits in uint256 _unitTracker += int256(U); } @@ -1240,7 +1251,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { ); // Store the escrow information. For that, an index is required. Since we need this index twice, we store it. - // Only information which is relevant for the escrow has to be hashed. (+ some extra for randomisation) + // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation) // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData bytes32 sendLiquidityHash = _computeSendLiquidityHash( routeDescription.toAccount, // Ensures no collisions between different users @@ -1271,8 +1282,13 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } /** - * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing. - * @dev Internal function that implement the majority of swap logic. + * @notice Handles common logic assocaited with the completion of a cross-chain liquidity swap. + * This function convert incoming units directly to vault tokens. + * @dev This function is meant to finalise a receiveLiquidity call. + * @param U Incoming units to be turned into vault tokens. + * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). + * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). + * @return vaultTokens Minted vault tokens. */ function _receiveLiquidity( uint256 U, @@ -1294,7 +1310,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return. vaultTokens = _calcPriceCurveLimitShare(U, totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); - // Check if more than the minimum output is returned. + // Check if more vault tokens than the minimum can be minted. if (minVaultTokens > vaultTokens) revert ReturnInsufficient(vaultTokens, minVaultTokens); // Then check if the minimum number of reference assets is honoured. if (minReferenceAsset != 0) { @@ -1303,7 +1319,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { oneMinusAmpInverse )); // Add escrow to ensure that even if all ongoing transaction revert, the user gets their expected amount. - // Add vault tokens because they are going to be minted. + // Add vault tokens because they are going to be minted (We want the reference value after mint). uint256 walpha_0_owned = ((walpha_0 * vaultTokens) / (totalSupply() + _escrowedVaultTokens + vaultTokens)) / FixedPointMathLib.WAD; if (minReferenceAsset > walpha_0_owned) revert ReturnInsufficient(walpha_0_owned, minReferenceAsset); } @@ -1319,12 +1335,12 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // And the below calculation doesn't work. if (it_times_walpha_amped <= U) revert ExceedsSecurityLimit(); uint256 vaultTokenEquiv = FixedPointMathLib.mulWadUp( - uint256(FixedPointMathLib.powWad( // Always casts a positive value - int256(it_times_walpha_amped), // If casting overflows to a negative number, powWad fails + uint256(FixedPointMathLib.powWad( // Always casts a positive value + int256(it_times_walpha_amped), // If casting overflows to a negative number, powWad fails oneMinusAmpInverse )), - FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // powWad is always <= 1, as 'base' is always <= 1 - int256(FixedPointMathLib.divWad( // Casting never overflows, as division result is always <= 1 + FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // powWad is always <= 1, as 'base' is always <= 1 + int256(FixedPointMathLib.divWad( // Casting never overflows, as division result is always <= 1 it_times_walpha_amped - U, it_times_walpha_amped )), @@ -1344,11 +1360,12 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @param channelId The source chain identifier. * @param fromVault The source vault. * @param toAccount The recipient. - * @param U Incoming units. - * @param minVaultTokens The minimum number of vault tokens to mint on target vault. Otherwise: Reject - * @param minReferenceAsset The minimum number of reference asset the vaults tokens are worth. Otherwise: Reject + * @param U Incoming units to be turned into vault tokens. + * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). + * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). * @param fromAmount Used to match cross-chain swap events. The input amount on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. + * @return purchasedVaultTokens Minted vault tokens. */ function receiveLiquidity( bytes32 channelId, @@ -1376,7 +1393,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Deletes and releases escrowed tokens to the vault and updates the security limit. - * @dev Should never revert! + * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. * @param toAccount The recipient of the transaction on the target chain. From 9ce702a5d28f657c2f17dd17a102a68fe0ba305f Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 16 May 2024 19:48:09 +0200 Subject: [PATCH 12/22] feat: volatile template & interfaces --- evm/src/CatalystVaultAmplified.sol | 171 +++++++-------- evm/src/CatalystVaultVolatile.sol | 197 +++++++++--------- .../interfaces/ICatalystV1FactoryEvents.sol | 6 +- .../interfaces/ICatalystV1Underwriting.sol | 2 +- .../ICatalystV1VaultAdministration.sol | 7 +- .../interfaces/ICatalystV1VaultDerived.sol | 6 +- evm/src/interfaces/ICatalystV1VaultEvents.sol | 10 +- .../interfaces/ICatalystV1VaultImmutables.sol | 11 +- .../ICatalystV1VaultPermissionless.sol | 68 +++--- evm/src/interfaces/ICatalystV1VaultState.sol | 32 +-- .../ICatalystV1VaultSuccessFailure.sol | 6 +- 11 files changed, 261 insertions(+), 255 deletions(-) diff --git a/evm/src/CatalystVaultAmplified.sol b/evm/src/CatalystVaultAmplified.sol index e7dd00ee..8f28284d 100644 --- a/evm/src/CatalystVaultAmplified.sol +++ b/evm/src/CatalystVaultAmplified.sol @@ -13,7 +13,7 @@ import { ICatalystReceiver} from "./interfaces/IOnCatalyst.sol"; import "./ICatalystV1Vault.sol"; /** - * @title Catalyst: The Multi-Chain Vault + * @title Catalyst: Fixed rate cross-chain swap vault. * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: 1/w^\theta (1 - \theta) where \theta is @@ -41,15 +41,14 @@ import "./ICatalystV1Vault.sol"; * If connected to a supported cross-chain interface, call * setConnection to connect the vault with vaults on other chains. * - * Finally, call finishSetup to give up the creator's control - * over the vault. + * Finally, call finishSetup to give up the creator's control over the vault. * !If finishSetup is not called, the vault can be drained by the creator! */ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { //--- Storage ---// /** - * @notice Adjustment used to remove a certain escrow amount from the balance 0 computation + * @notice Adjustment used to remove a certain escrow amount from the balance * computation */ mapping(address => uint256) public _underwriteEscrowMatchBalance0; @@ -96,7 +95,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * i_t \cdot W_i = j_t \cdot W_j \forall i, j when P_i(i_t) = P_j(j_t). * in other words, weights are used to compensate for the difference in decimals. (or non 1:1.) * @param amp Amplification factor. Should be < 10**18. - * @param depositor The address depositing the initial token balances. + * @param depositor The address to mint tokens to. */ function initializeSwapCurves( address[] calldata assets, @@ -136,7 +135,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { _tokenIndexing[it] = tokenAddress; uint256 weight = weights[it]; - require(weight != 0); // dev: invalid 0-valued weight provided + require(weight != 0); // dev: invalid 0-valued weight provided _weight[tokenAddress] = weight; // The contract expects the tokens to have been sent to it before setup is @@ -162,7 +161,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // storage should be the current balance. _maxUnitCapacity = maxUnitCapacity; - // Mint vault tokens for vault creator. + // Mint vault tokens to the vault creator. _mint(depositor, INITIAL_MINT_AMOUNT); emit VaultDeposit(depositor, INITIAL_MINT_AMOUNT, initialBalances); @@ -205,8 +204,8 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev Reverts if 'fromAsset' is not a token in the vault or if * 'amount' and the vault asset balance are both 0. * Does not contain the swap fee. - * @param fromAsset The address of the token to sell. - * @param amount The amount of from token to sell. + * @param fromAsset Address of the token to sell. + * @param amount Amount of from token to sell. * @return uint256 Units. */ function calcSendAsset( @@ -235,9 +234,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Computes the output of ReceiveAsset excluding fees. * @dev Reverts if 'toAsset' is not a token in the vault. * Does not contain the swap fee. - * @param toAsset The address of the token to buy. - * @param U The number of units to convert. - * @return uint256 Number of purchased tokens. + * @param toAsset Address of the token to buy. + * @param U Number of units to convert. + * @return uint256 Ppurchased tokens. */ function calcReceiveAsset( address toAsset, @@ -260,9 +259,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * Reverts if either 'fromAsset' or 'toAsset' is not in the vault, or if the vault 'fromAsset' * balance and 'amount' are both 0. * Does not contain the swap fee. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. - * @param amount The amount of from token to sell for to token. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. + * @param amount Amount of from token to sell for to token. * @return output Output denominated in toAsset. */ function calcLocalSwap( @@ -292,9 +291,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * Deposit is done by converting tokenAmounts into units and then using * the macro for units to vault tokens. (_calcPriceCurveLimitShare). * The elements of tokenAmounts correspond to _tokenIndexing[0...N]. - * @param tokenAmounts An array of the tokens amounts to be deposited. - * @param minOut The minimum number of vault tokens to be minted. - * @return vaultTokens The number of minted vault tokens. + * @param tokenAmounts Array of the tokens amounts to be deposited. + * @param minOut Minimum number of vault tokens to be minted. + * @return vaultTokens Number of minted vault tokens. */ function depositMixed( uint256[] memory tokenAmounts, @@ -450,9 +449,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Burns vault tokens and releases the symmetrical share of tokens to the burner. * This can impact the vault prices. * @dev This is the cheapest way to withdraw and only way to withdraw 100% of the liquidity. - * @param vaultTokens The number of vault tokens to burn. - * @param minOut The minimum token output. If less is returned, the transaction reverts. - * @return amounts An array containing the amounts withdrawn. + * @param vaultTokens Number of vault tokens to burn. + * @param minOut Minimum token output. If less is returned, the transaction reverts. + * @return amounts Array containing the amounts withdrawn. */ function withdrawAll( uint256 vaultTokens, @@ -625,14 +624,14 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { } /** - * @notice Burns vaultTokens and release a token distribution which can be set by the user. + * @notice Burns vaultTokens and release a token distribution set by the user. * @dev It is advised that the withdrawal matches the vault's %token distribution. * Notice the special scheme for the ratios used. This is done to optimise gas since it doesn't require a sum or ratios. * Cannot be used to withdraw all liquidity. For that, withdrawAll should be used. - * @param vaultTokens The number of vault tokens to withdraw. - * @param withdrawRatio The percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. - * @param minOut The minimum number of tokens withdrawn. - * @return amounts An array containing the amounts withdrawn. + * @param vaultTokens Number of vault tokens to withdraw. + * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. + * @param minOut Minimum number of tokens withdrawn. + * @return amounts Array containing the amounts withdrawn. */ function withdrawMixed( uint256 vaultTokens, @@ -791,10 +790,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice A swap between 2 assets within the vault. Is atomic. - * @param fromAsset The asset the user wants to sell. - * @param toAsset The asset the user wants to buy. - * @param amount The amount of fromAsset the user wants to sell. - * @param minOut The minimum output the user wants. Otherwise, the transaction reverts. + * @param fromAsset Asset the user wants to sell. + * @param toAsset Asset the user wants to buy. + * @param amount Amount of fromAsset the user wants to sell. + * @param minOut Minimum output the user wants. Otherwise, the transaction reverts. * @return out The number of tokens purchased. */ function localSwap( @@ -834,7 +833,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { emit LocalSwap(msg.sender, fromAsset, toAsset, amount, out); } - /** @notice Handles common logic between both sendAsset implementations */ + /** @notice Common logic between sendAsset implementations */ function _sendAsset( RouteDescription calldata routeDescription, address fromAsset, @@ -911,16 +910,16 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Initiate a cross-chain swap by purchasing units and transfering the units to the target vault. - * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number output of tokens on the target chain. + * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number output of tokens on the target chain. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max). + * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). At maximum 65535 bytes can be passed. - * @return U The number of units bought. + * @return U Number of units bought. */ function sendAsset( RouteDescription calldata routeDescription, @@ -932,16 +931,8 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint16 underwriteIncentiveX16, bytes calldata calldata_ ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { - // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. - // It would also be a silly fallback address. - require(fallbackUser != address(0)); - // Correct address format is checked on the cross-chain interface. As a result, the below snippit is not needed. - /* - require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. - require(toAccount.length == 65); // dev: Account addresses are uint8 + 64 bytes. - */ - // _updateAmplification(); + uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the units bought. @@ -966,14 +957,14 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units. * @dev This function is intended to match an existing underwrite. Since normal sendAssets aren't "exact" in regards to U, * this functions makes it easier to hit a specific underwrite. - * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number output of tokens on the target chain. - * @param minU The minimum and exact number of units sent. + * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number output of tokens on the target chain. + * @param minU Minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max). + * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max). * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(
)), ). At maximum 65535 bytes can be passed.. * @return U Always equal to minOut, as that is the number of units to be used on the destination chain. @@ -989,14 +980,7 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { uint16 underwriteIncentiveX16, bytes calldata calldata_ ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { - // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. - // It would also be a silly fallback address. - require(fallbackUser != address(0)); - // Correct address format is checked on the cross-chain interface. As a result, the below snippit is not needed. - /* - require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. - require(toAccount.length == 65); // dev: Account addresses are uint8 + 64 bytes. - */ + // _updateAmplification(); uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); @@ -1026,10 +1010,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Handles common logic associated with the completion of a cross-chain swap. * This function convert incoming Units (expected to be from an incoming cross-chain swap) into a specific token. * @dev This function is intended to finalise a receiveAsset call. - * @param toAsset The asset to buy with the units. + * @param toAsset Asset to buy with the units. * @param U Incoming units to be turned into vaults tokens. - * @param minOut The minimum number of tokens to purchase. Will revert if less. - * @return purchasedTokens The number of toAsset bought. Is greater than minOut. + * @param minOut Minimum number of tokens to purchase. Will revert if less. + * @return purchasedTokens Number of toAsset bought. */ function _receiveAsset( address toAsset, @@ -1060,15 +1044,16 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { /** * @notice Completes a cross-chain swap by converting units to the desired token. * @dev Security checks are performed by _receiveAsset. - * @param channelId The source chain identifier. - * @param fromVault The source vault. + * @param channelId Source chain identifier. + * @param fromVault Source vault. * @param toAssetIndex Index of the asset to be purchased. - * @param toAccount The recipient. + * @param toAccount Recipient of assets on destination chain. * @param U Incoming units. * @param minOut Minimum number of token to buy. Reverts back to the sending side. * @param fromAmount Used to match cross-chain swap events. The input amount minus fees on the sending chain. * @param fromAsset Used to match cross-chain swap events. The input asset on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. + * @param purchasedTokens Number of toAsset bought. */ function receiveAsset( bytes32 channelId, @@ -1189,13 +1174,13 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units. * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted * directly into units through the following equation: U = N · wa^(1-k) · (((PT + pt)/PT)^(1-k) - 1) - * @param routeDescription A cross-chain route description that contains the chainIdentifier, toAccount, toVault, and relaying incentive. - * @param vaultTokens The number of vault tokens to exchange. - * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. + * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault, and relaying incentive. + * @param vaultTokens Number of vault tokens to exchange. + * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. - * @return U The number of units bought. + * @return U Number of units bought. */ function sendLiquidity( RouteDescription calldata routeDescription, @@ -1207,15 +1192,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. // It would also be a silly fallback address. require(fallbackUser != address(0)); - // Correct address format is checked on the cross-chain interface. As a result, the below snippit is not needed. - /* - require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. - require(toAccount.length == 65); // dev: Account addresses are uint8 + 64 bytes. - */ + // Correct address format is checked on the cross-chain interface. // _updateAmplification(); - // When accesssing totalSupply, remember that we have already burnt the incoming vaultTokens. + // When accesssing totalSupply, remember that we already burnt the incoming vaultTokens. _burn(msg.sender, vaultTokens); int256 oneMinusAmp = _oneMinusAmp; @@ -1357,9 +1338,9 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev Security checks are performed by _receiveLiquidity. * While the description says units are converted to tokens and then deposited, units are converted * directly to vault tokens through the following equation: pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1) - * @param channelId The source chain identifier. - * @param fromVault The source vault. - * @param toAccount The recipient. + * @param channelId Source chain identifier. + * @param fromVault Source vault. + * @param toAccount Recipient of vault tokens. * @param U Incoming units to be turned into vault tokens. * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). @@ -1396,11 +1377,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. - * @param toAccount The recipient of the transaction on the target chain. - * @param U The number of units purchased. - * @param escrowAmount The number of tokens escrowed. - * @param escrowToken The token escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units purchased. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Token escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendAssetSuccess( bytes32 channelId, @@ -1452,11 +1433,11 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. - * @param toAccount The recipient of the transaction on the target chain. - * @param U The number of units acquired. - * @param escrowAmount The number of tokens escrowed. - * @param escrowToken The token escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units acquired. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Token escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendAssetFailure( bytes32 channelId, @@ -1484,10 +1465,10 @@ contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified { * @notice Deletes and releases liquidity escrowed tokens to the vault and updates the security limit. * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. - * @param toAccount The recipient of the transaction on the target chain. Encoded in bytes32. - * @param U The number of units initially acquired. - * @param escrowAmount The number of vault tokens escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. + * @param U Number of units initially acquired. + * @param escrowAmount Number of vault tokens escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquidityFailure( bytes32 channelId, diff --git a/evm/src/CatalystVaultVolatile.sol b/evm/src/CatalystVaultVolatile.sol index 84c8865b..3711cca5 100644 --- a/evm/src/CatalystVaultVolatile.sol +++ b/evm/src/CatalystVaultVolatile.sol @@ -13,7 +13,7 @@ import { IntegralsVolatile } from "./IntegralsVolatile.sol"; import "./ICatalystV1Vault.sol"; /** - * @title Catalyst: The Multi-Chain Vault + * @title Catalyst: Variable rate cross-chain swap vault. * @author Cata Labs Inc. * @notice Catalyst multi-chain vault using the asset specific * pricing curve: W/w where W is an asset-specific weight and w @@ -34,21 +34,22 @@ import "./ICatalystV1Vault.sol"; * After deployment of the proxy, call setup(...) AND initializeSwapCurves(...). * This will initialize the vault and prepare it for cross-chain transactions. * However, only the Catalyst factory is allowed to perform these functions. + * It is important that both setup(...) AND initializeSwapCurves(...) are called + * in the same transaciton, as otherwise the vault is not fully finalised and + * may be configured incorrectly by a third-party. * * If connected to a supported cross-chain interface, call * setConnection to connect the vault with vaults on other chains. * - * Finally, call finishSetup to give up the creators's control - * over the vault. + * Finally, call finishSetup to give up the creators's control over the vault. * !If finishSetup is not called, the vault can be drained by the creators! */ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { //--- ERRORS ---// // Errors are defined in interfaces/ICatalystV1VaultErrors.sol - //--- MATH ---// - //--- Config ---// + // Minimum time parameter adjustments can be made over. uint256 constant MIN_ADJUSTMENT_TIME = 7 days; @@ -66,12 +67,12 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * done atomically! * * If 0 of a token in assets is provided, the setup reverts. - * @param assets A list of the token addresses associated with the vault + * @param assets A list of token addresses to be associated with the vault. * @param weights The weights associated with the tokens. * If set to values with low resolution (<= 10*5), this should be viewed as * opt out of governance weight adjustment. This is not enforced. * @param amp Amplification factor. Set to 10**18 for this vault - * @param depositor The address depositing the initial token balances. + * @param depositor The address to mint tokens to. */ function initializeSwapCurves( address[] calldata assets, @@ -89,7 +90,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { // (and the vault shouldn't be used by anyone until its configuration has been finalised). // In any case, the factory does check for valid assets/weights arguments to prevent erroneous configurations. // Note Since assets.len != 0 is not checked, the initial depositor may invoke this function many times, resulting - // on vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is + // in vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is // an arbitrary number; the value of the vault tokens is determined by the ratio of the vault asset balances and vault // tokens supply once setup has finalized. Furthermore, the vault should not be used until setup has finished and the // vault configuration has been verified. @@ -104,12 +105,16 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { _tokenIndexing[it] = tokenAddress; uint256 weight = weights[it]; - require(weight != 0); // dev: invalid 0-valued weight provided + require(weight != 0); // dev: invalid 0-valued weight provided _weight[tokenAddress] = weight; // The contract expects the tokens to have been sent to it before setup is // called. Make sure the vault has more than 0 tokens. // Reverts if tokenAddress is address(0). + // This contract uses safeTransferLib from Solady. When "safeTransfering", there is no + // check for smart contract code. This could be an issue if non-tokens are allowed to enter + // the pool, as then the pool could be expoited by later deploying an address to the addres. + // The below check ensure that there is a token deployed to the contract. uint256 balanceOfSelf = ERC20(tokenAddress).balanceOf(address(this)); require(balanceOfSelf != 0); // dev: 0 tokens provided in setup. initialBalances[it] = balanceOfSelf; @@ -122,10 +127,10 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { } // The maximum unit flow is \sum Weights * ln(2). The value is multiplied by WAD - // since units are always WAD denominated (note WAD is already included in the LN2 factor). + // since units are always WAD denominated (WAD is included in the LN2 factor). _maxUnitCapacity = maxUnitCapacity * LN2; - // Mint vault tokens for vault creator. + // Mint vault tokens to the vault creator. _mint(depositor, INITIAL_MINT_AMOUNT); emit VaultDeposit(depositor, INITIAL_MINT_AMOUNT, initialBalances); @@ -138,9 +143,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * the governance is not allowed to change vault weights. This is because * the update function is not made for large step sizes (which the steps would be if * trades are infrequent or weights are small). - * Weights must not be set to 0. This allows someone to exploit the localSwap simplification - * with a token not belonging to the vault. (Set weight to 0, localSwap from token not part of - * the vault. Since 0 == 0 => use simplified swap curve. Swap goes through.) + * Weights must not be set to 0 since this would cause a problem with the swap equations. * @param targetTime Once reached, _weight[...] = newWeights[...] * @param newWeights The new weights to apply */ @@ -163,8 +166,8 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { uint256 newWeight = newWeights[it]; uint256 currentWeight = _weight[token]; require(newWeight != 0); // dev: newWeights must be greater than 0 to protect liquidity providers. - require(newWeight <= currentWeight*10); // dev: newWeights must be maximum a factor of 10 larger/smaller than the current weights to protect liquidity providers. - require(newWeight >= currentWeight/10); // dev: newWeights must be maximum a factor of 10 larger/smaller than the current weights to protect liquidity providers. + require(newWeight <= currentWeight*10); // dev: newWeights must be less than a factor of 10 larger than the current weights to protect liquidity providers. + require(newWeight >= currentWeight/10); // dev: newWeights must be more than a factor of 10 smaller than the current weights to protect liquidity providers. _targetWeight[token] = newWeight; unchecked { @@ -273,8 +276,8 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @notice Computes the return of SendAsset excluding fees. * @dev Returns 0 if 'fromAsset' is not a token in the vault * Does not contain the swap fee. - * @param fromAsset The address of the token to sell. - * @param amount The amount of from token to sell. + * @param fromAsset Address of the token to sell. + * @param amount Amount of from token to sell. * @return uint256 Units. */ function calcSendAsset( @@ -294,9 +297,9 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @notice Computes the output of ReceiveAsset excluding fees. * @dev Reverts if 'toAsset' is not a token in the vault * Does not contain the swap fee. - * @param toAsset The address of the token to buy. - * @param U The number of units to convert. - * @return uint256 Number of purchased tokens. + * @param toAsset Address of the token to buy. + * @param U Number of units to convert. + * @return uint256 Purchased tokens. */ function calcReceiveAsset( address toAsset, @@ -319,9 +322,9 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * If from or to is not part of the vault, the swap will either return 0 or revert respectively. * If both from and to are not part of the vault, the swap can actually return a positive value. * Does not contain the swap fee. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. - * @param amount The amount of from token to sell for to token. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. + * @param amount Amount of from token to sell for to token. * @return uint256 Output denominated in toAsset. */ function calcLocalSwap( @@ -356,9 +359,9 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * Deposit is done by converting tokenAmounts into units and then using * the macro for units to vault tokens. (_calcPriceCurveLimitShare). * The elements of tokenAmounts correspond to _tokenIndexing[0...N]. - * @param tokenAmounts An array of the tokens amounts to be deposited. - * @param minOut The minimum number of vault tokens to be minted. - * @return vaultTokens The number of minted vault tokens. + * @param tokenAmounts Array of the tokens amounts to be deposited. + * @param minOut Minimum number of vault tokens to be minted. + * @return vaultTokens Number of minted vault tokens. */ function depositMixed( uint256[] calldata tokenAmounts, @@ -430,9 +433,9 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @notice Burns vault tokens and releases the symmetrical share of tokens to the burner. * This doesn't change the vault price (for this implementation) * @dev This is the cheapest way to withdraw and only way to withdraw 100% of the liquidity. - * @param vaultTokens The number of vault tokens to burn. - * @param minOut The minimum token output. If less is returned, the transaction reverts. - * @return amounts An array containing the amounts withdrawn. + * @param vaultTokens Number of vault tokens to burn. + * @param minOut Minimum token output. If less is returned, the transaction reverts. + * @return amounts Array containing the amounts withdrawn. */ function withdrawAll( uint256 vaultTokens, @@ -477,7 +480,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { } /** - * @notice Burns vaultTokens and release a token distribution which can be set by the user. + * @notice Burns vaultTokens and release a token distribution set by the user. * @dev It is advised that the withdrawal matches the vault's %token distribution. * Notice the special scheme for the ratios used. This is done to optimise gas since it doesn't require a sum or ratios. * When weights are equal, the ratios can easily be computed with cumulative sums. If the weights aren't equal, the weights needs to be included. @@ -486,10 +489,10 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * For 3 tokens, this would look like withdrawRatio = [w_1 * WR_1 / (w_1 * WR_1 + w_2 * WR_2 + w_3 * WR_3), w_2 * WR_2 / (w_2 * WR_2 + w_3 * WR_3), w_3 * WR_3 / (w_3 * WR_3)] * Notice that the last index is 1. * Cannot be used to withdraw all liquidity. For that, withdrawAll should be used. - * @param vaultTokens The number of vault tokens to withdraw. - * @param withdrawRatio The percentage of units used to withdraw. In the following scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... To withdraw similarly to withdrawAll, the weights needs to be multiplied by weights. See @dev. - * @param minOut The minimum number of tokens withdrawn. - * @return amounts An array containing the amounts withdrawn. + * @param vaultTokens Number of vault tokens to withdraw. + * @param withdrawRatio Percentage of units used to withdraw. In the following scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... To withdraw similarly to withdrawAll, the weights needs to be multiplied by weights. See @dev. + * @param minOut Minimum number of tokens withdrawn. + * @return amounts Array containing the amounts withdrawn. */ function withdrawMixed( uint256 vaultTokens, @@ -510,7 +513,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { // Compute the unit worth of the vault tokens. // The following line implies that one cannot withdraw all liquidity using this function. uint256 U = uint256(FixedPointMathLib.lnWad( // uint256: ln computed of a value which is greater than > 1 is always positive - int256(FixedPointMathLib.divWad(initialTotalSupply, initialTotalSupply - vaultTokens) // int265: if vaultTokens is almost equal to initialTotalSupply this can overflow the cast. The result is a negative input to lnWad which fails. + int256(FixedPointMathLib.divWad(initialTotalSupply, initialTotalSupply - vaultTokens) // int265: if vaultTokens is almost equal to initialTotalSupply this can overflow the cast. The result is a negative input to lnWad which fails. ))) * wsum; // For later event logging, the amounts transferred to the vault are stored. @@ -532,7 +535,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { } continue; } - U -= U_i; // Subtract the number of units used. This will underflow for malicious withdrawRatios > 1. + U -= U_i; // Subtract the number of units used. This will underflow for malicious withdrawRatios > 1. // Withdrawals should returns less, so the escrowed tokens are subtracted. uint256 At = ERC20(token).balanceOf(address(this)) - _escrowedTokens[token]; @@ -562,11 +565,11 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { /** * @notice A swap between 2 assets within the vault. Is atomic. - * @param fromAsset The asset the user wants to sell. - * @param toAsset The asset the user wants to buy - * @param amount The amount of fromAsset the user wants to sell - * @param minOut The minimum output the user wants. Otherwise, the transaction reverts. - * @return out The number of tokens purchased. + * @param fromAsset Asset the user wants to sell. + * @param toAsset Asset the user wants to buy + * @param amount Amount of fromAsset the user wants to sell + * @param minOut Minimum output the user wants. Otherwise, the transaction reverts. + * @return out Number of tokens purchased. */ function localSwap( address fromAsset, @@ -595,7 +598,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { emit LocalSwap(msg.sender, fromAsset, toAsset, amount, out); } - /// @notice Handles common logic between both sendAsset implementations + /** @notice Common logic between sendAsset implementations */ function _sendAsset( RouteDescription calldata routeDescription, address fromAsset, @@ -623,7 +626,6 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { calldata_ ); - // Store the escrow information. For that, an index is required. Since we need this index twice, we store it. // Only information which is relevant for the escrow has to be hashed. (+ some extra for randomisation) // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData @@ -669,16 +671,16 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault. - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number output of tokens on the target chain. + * @param routeDescription Cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number output of tokens on the target chain. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max) + * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max) * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. - * @return U The number of units minted. + * @return U Number of units minted. */ function sendAsset( RouteDescription calldata routeDescription, @@ -697,6 +699,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { // Calculate the units bought. U = calcSendAsset(fromAsset, amount - fee); + // Execute the common sendAsset logic. _sendAsset( routeDescription, fromAsset, @@ -713,17 +716,17 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { /** * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units. - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number output of tokens on the target chain. - * @param minU The minimum and exact number of units sent. + * @param routeDescription Cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number output of tokens on the target chain. + * @param minU Minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max) * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. - * @return uint256 The number of units minted. + * @return U The number of units minted. */ function sendAssetFixedUnit( RouteDescription calldata routeDescription, @@ -735,21 +738,23 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { address fallbackUser, uint16 underwriteIncentiveX16, bytes calldata calldata_ - ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256) { + ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) { _updateWeights(); uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee); // Calculate the units bought. - uint256 U = calcSendAsset(fromAsset, amount - fee); + U = calcSendAsset(fromAsset, amount - fee); if (U < minU) revert ReturnInsufficient(U, minU); + // The set number of units bought to minU. + U = minU; _sendAsset( routeDescription, fromAsset, toAssetIndex, - minU, + U, amount, fee, minOut, @@ -757,14 +762,17 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { underwriteIncentiveX16, calldata_ ); - - return minU; } /** - * @notice Completes a cross-chain swap by converting units to the desired token. - * @dev Internal function that implement the majority of swap logic. + * @notice Handles common logic associated with the completion of a cross-chain swap. + * This function convert incoming Units (expected to be from an incoming cross-chain swap) into a specific token. + * @dev This function is intended to finalise a receiveAsset call. + * @param toAsset Asset to buy with the units. + * @param U Incoming units to be turned into vaults tokens. + * @param minOut Minimum number of tokens to purchase. Will revert if less. + * @return purchasedTokens Number of toAsset bought. */ function _receiveAsset( address toAsset, @@ -786,15 +794,16 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { /** * @notice Completes a cross-chain swap by converting units to the desired token. * @dev Security checks are performed by _receiveAsset. - * @param channelId The source chain identifier. - * @param fromVault The source vault. + * @param channelId Source chain identifier. + * @param fromVault Source vault. * @param toAssetIndex Index of the asset to be purchased. - * @param toAccount The recipient. + * @param toAccount Recipient of assets on destination chain. * @param U Incoming units. * @param minOut Minimum number of token to buy. Reverts back to the sending side. * @param fromAmount Used to match cross-chain swap events. The input amount minus fees on the sending chain. * @param fromAsset Used to match cross-chain swap events. The input asset on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. + * @param purchasedTokens Number of toAsset bought. */ function receiveAsset( bytes32 channelId, @@ -845,13 +854,13 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units. * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted * directly into units through the following equation: U = ln(PT/(PT-pt)) * \sum W_i - * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param vaultTokens The number of vault tokens to exchange. - * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. + * @param routeDescription Cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. + * @param vaultTokens Number of vault tokens to exchange. + * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. - * @return U The number of units bought. + * @return U Number of units bought. */ function sendLiquidity( RouteDescription calldata routeDescription, @@ -863,11 +872,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow. // It would also be a silly fallback address. require(fallbackUser != address(0)); - // Correct address format is checked on the cross-chain interface. As a result, the below snippit is not needed. - /* - require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. - require(toAccount.length == 65); // dev: Account addresses are uint8 + 64 bytes. - */ + // Correct address format is checked on the cross-chain interface. // Update weights _updateWeights(); @@ -927,8 +932,12 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { } /** - * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing. - * @dev Internal function that implement the majority of swap logic. + * @notice Handles common logic assocaited with the completion of a cross-chain liquidity swap. + * This function convert incoming units directly to vault tokens. + * @dev This function is meant to finalise a receiveLiquidity call. + * @param U Incoming units to be turned into vault tokens. + * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). + * @return vaultTokens Minted vault tokens. */ function _receiveLiquidity( uint256 U, @@ -995,7 +1004,7 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { // Find the fraction of the referenceAmount that the user owns. // Add escrow to ensure that even if all ongoing transaction revert, the user gets their expected amount. - // Add vault tokens because they are going to be minted. + // Add vault tokens because they are going to be minted. (We want the reference value after mint). referenceAmount = (referenceAmount * vaultTokens)/(totalSupply() + _escrowedVaultTokens + vaultTokens); if (minReferenceAsset > referenceAmount) revert ReturnInsufficient(referenceAmount, minReferenceAsset); } @@ -1006,12 +1015,12 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @dev Security checks are performed by _receiveLiquidity. * While the description says units are converted to tokens and then deposited, units are converted * directly to vault tokens through the following equation: pt = PT · (1 - exp(-U/sum W_i))/exp(-U/sum W_i) - * @param channelId The source chain identifier. - * @param fromVault The source vault. - * @param toAccount The recipient. + * @param channelId Source chain identifier. + * @param fromVault Source vault. + * @param toAccount Recipient of vault tokens. * @param U Incoming units. - * @param minVaultTokens The minimum number of vault tokens to mint on target vault. Otherwise: Reject - * @param minReferenceAsset The minimum number of reference asset the vaults tokens are worth. Otherwise: Reject + * @param minVaultTokens Minimum number of vault tokens to mint (revert if less). + * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less). * @param fromAmount Used to match cross-chain swap events. The input amount on the source chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain. */ @@ -1044,11 +1053,11 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. - * @param toAccount The recipient of the transaction on the target chain. - * @param U The number of units purchased. - * @param escrowAmount The number of tokens escrowed. - * @param escrowToken The token escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units purchased. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Token escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendAssetSuccess( bytes32 channelId, @@ -1084,10 +1093,10 @@ contract CatalystVaultVolatile is CatalystVaultCommon, IntegralsVolatile { * @dev Should never revert! * The base implementation exists in CatalystVaultCommon. The function adds security limit * adjustment to the implementation to swap volume supported. - * @param toAccount The recipient of the transaction on the target chain. - * @param U The number of units acquired. - * @param escrowAmount The number of vault tokens escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units acquired. + * @param escrowAmount Number of vault tokens escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquiditySuccess( bytes32 channelId, diff --git a/evm/src/interfaces/ICatalystV1FactoryEvents.sol b/evm/src/interfaces/ICatalystV1FactoryEvents.sol index b896300a..a3a347ab 100644 --- a/evm/src/interfaces/ICatalystV1FactoryEvents.sol +++ b/evm/src/interfaces/ICatalystV1FactoryEvents.sol @@ -1,8 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Events emitted by Catalyst v1 Factory -/// @notice Contains all events emitted by the Factory +/** + * @title Events emitted by Catalyst v1 Factory + * @notice Contains all events emitted by the Factory +*/ interface ICatalystV1FactoryEvents { /** * @notice Describes the deployment of a new vault as a proxy of the given vault template. diff --git a/evm/src/interfaces/ICatalystV1Underwriting.sol b/evm/src/interfaces/ICatalystV1Underwriting.sol index 7146feb9..6b659bee 100644 --- a/evm/src/interfaces/ICatalystV1Underwriting.sol +++ b/evm/src/interfaces/ICatalystV1Underwriting.sol @@ -1,7 +1,7 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Extensions to vaults which supports underwriting. +/** @title Extensions to vaults which supports underwriting. */ interface ICatalystV1Underwriting { function underwriteAsset( bytes32 identifier, diff --git a/evm/src/interfaces/ICatalystV1VaultAdministration.sol b/evm/src/interfaces/ICatalystV1VaultAdministration.sol index ce9ccea1..1b65cb4b 100644 --- a/evm/src/interfaces/ICatalystV1VaultAdministration.sol +++ b/evm/src/interfaces/ICatalystV1VaultAdministration.sol @@ -1,8 +1,9 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; - -/// @title Administrative actions defined by Catalyst v1 Vaults -/// @notice Contains all functions which can only be called by privileged users. +/** + * @title Administrative actions defined by Catalyst v1 Vaults + * @notice Contains all functions which can only be called by privileged users. + */ interface ICatalystV1VaultAdministration { function setFeeAdministrator(address administrator) external; diff --git a/evm/src/interfaces/ICatalystV1VaultDerived.sol b/evm/src/interfaces/ICatalystV1VaultDerived.sol index e7e5a138..5bab61b3 100644 --- a/evm/src/interfaces/ICatalystV1VaultDerived.sol +++ b/evm/src/interfaces/ICatalystV1VaultDerived.sol @@ -1,8 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Derived Vault state -/// @notice Contains all vault state which is derived from vault storage +/** + * @title Derived Vault state + * @notice Contains all vault state which is derived from vault storage + */ interface ICatalystV1VaultDerived { /** @notice Returns the current cross-chain unit capacity. */ function getUnitCapacity() external view returns (uint256); diff --git a/evm/src/interfaces/ICatalystV1VaultEvents.sol b/evm/src/interfaces/ICatalystV1VaultEvents.sol index 0d10690c..d05b81bb 100644 --- a/evm/src/interfaces/ICatalystV1VaultEvents.sol +++ b/evm/src/interfaces/ICatalystV1VaultEvents.sol @@ -1,10 +1,12 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Events emitted by Catalyst v1 Vaults -/// @notice Contains all events emitted by the vault -/// @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique. -/// If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping +/** + * @title Events emitted by Catalyst v1 Vaults + * @notice Contains all events emitted by the vault + * @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique. + * If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping + */ interface ICatalystV1VaultEvents { /** * @notice Describes an atomic swap between the 2 tokens: _fromAsset and _toAsset. diff --git a/evm/src/interfaces/ICatalystV1VaultImmutables.sol b/evm/src/interfaces/ICatalystV1VaultImmutables.sol index d8a877f1..d3745034 100644 --- a/evm/src/interfaces/ICatalystV1VaultImmutables.sol +++ b/evm/src/interfaces/ICatalystV1VaultImmutables.sol @@ -1,8 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Immutable vault state -/// @notice Contains all vault state which doesn't change once set. +/** + * @title Immutable vault state + * @notice Contains all vault state which doesn't change once set. + */ interface ICatalystV1VaultImmutables { function _chainInterface() external view returns (address); @@ -10,6 +12,9 @@ interface ICatalystV1VaultImmutables { function MATHLIB() external view returns (address); - /// @notice To indicate which token is desired on the target vault, the _toAsset is an integer from 0 to MAX_ASSETS indicating which asset the vault should purchase with units. + /** + * @notice To indicate which token is desired on the target vault,the _toAsset is an integer + * from 0 to MAX_ASSETS indicating which asset the vault should purchase with units. + */ function _tokenIndexing(uint256 tokenIndex) external view returns (address); } diff --git a/evm/src/interfaces/ICatalystV1VaultPermissionless.sol b/evm/src/interfaces/ICatalystV1VaultPermissionless.sol index 3c889cb4..b73c3064 100644 --- a/evm/src/interfaces/ICatalystV1VaultPermissionless.sol +++ b/evm/src/interfaces/ICatalystV1VaultPermissionless.sol @@ -8,11 +8,11 @@ interface ICatalystV1VaultPermissionless { * @notice Setup a vault. * @param name_ Name of the Vault token. * @param symbol_ Symbol for the Vault token. - * @param chainInterface The cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.) - * @param vaultFee The vault fee. - * @param governanceFee The governance fee share. - * @param feeAdministrator The account that can modify the fees. - * @param setupMaster The short-term owner of the vault (until finishSetup is called). + * @param chainInterface Cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.) + * @param vaultFee Vault fee. + * @param governanceFee Governance fee share. + * @param feeAdministrator Account that can modify the fees. + * @param setupMaster Short-term owner of the vault (until finishSetup is called). */ function setup( string calldata name_, @@ -32,8 +32,8 @@ interface ICatalystV1VaultPermissionless { * Volatile: It is advised that the deposit matches the vault's %token distribution. * Amplified: It is advised that the deposit is as close to 1,1,... as possible. * Otherwise between 1,1,... and the vault's %token distribution. - * @param tokenAmounts An array of the tokens amounts to be deposited. - * @param minOut The minimum number of vault tokens to be minted. + * @param tokenAmounts Array of the tokens amounts to be deposited. + * @param minOut Minimum number of vault tokens to be minted. */ function depositMixed(uint256[] calldata tokenAmounts, uint256 minOut) external returns(uint256); @@ -41,7 +41,7 @@ interface ICatalystV1VaultPermissionless { /** * @notice Burns baseAmount and releases the symmetrical share * of tokens to the burner. This doesn't change the vault price. - * @param baseAmount The number of vault tokens to burn. + * @param baseAmount Number of vault tokens to burn. */ function withdrawAll(uint256 baseAmount, uint256[] calldata minOut) external returns(uint256[] memory); @@ -52,9 +52,9 @@ interface ICatalystV1VaultPermissionless { * Volatile: It is advised that the deposit matches the vault's %token distribution. * Amplified: It is advised that the deposit matches the vault's %token distribution. * Otherwise it should be weighted towards the tokens the vault has more of. - * @param vaultTokens The number of vault tokens to withdraw. - * @param withdrawRatio The percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. - * @param minOut The minimum number of tokens minted. + * @param vaultTokens Number of vault tokens to withdraw. + * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD. + * @param minOut Minimum number of tokens minted. */ function withdrawMixed( uint256 vaultTokens, @@ -66,10 +66,10 @@ interface ICatalystV1VaultPermissionless { /** * @notice A swap between 2 assets which both are inside the vault. Is atomic. - * @param fromAsset The asset the user wants to sell. - * @param toAsset The asset the user wants to buy. - * @param amount The amount of fromAsset the user wants to sell. - * @param minOut The minimum output of _toAsset the user wants. + * @param fromAsset Asset the user wants to sell. + * @param toAsset Asset the user wants to buy. + * @param amount Amount of fromAsset the user wants to sell. + * @param minOut Minimum output of _toAsset the user wants. */ function localSwap( address fromAsset, @@ -83,12 +83,12 @@ interface ICatalystV1VaultPermissionless { * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as: * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode()) * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number of returned tokens to the toAccount on the target chain. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number of returned tokens to the toAccount on the target chain. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16.max)) + * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max)) * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. * @return uint256 The number of units minted. @@ -109,16 +109,16 @@ interface ICatalystV1VaultPermissionless { * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as: * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode()) * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param fromAsset The asset the user wants to sell. - * @param toAssetIndex The index of the asset the user wants to buy in the target vault. - * @param amount The number of fromAsset to sell to the vault. - * @param minOut The minimum number of returned tokens to the toAccount on the target chain. - * @param minU The minimum and exact number of units sent. + * @param fromAsset Asset the user wants to sell. + * @param toAssetIndex Index of the asset the user wants to buy in the target vault. + * @param amount Number of fromAsset to sell to the vault. + * @param minOut Minimum number of returned tokens to the toAccount on the target chain. + * @param minU Minimum and exact number of units sent. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(
), ) - * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16.max)) - * @return uint256 The number of units minted. + * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max)) + * @return uint256 Number of units minted. */ function sendAssetFixedUnit( ICatalystV1Structs.RouteDescription calldata routeDescription, @@ -163,12 +163,12 @@ interface ICatalystV1VaultPermissionless { * directly into units through the following equation: * U = ln(PT/(PT-pt)) * \sum W_i * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive. - * @param vaultTokens The number of vault tokens to exchange. - * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. + * @param vaultTokens Number of vault tokens to exchange. + * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]. * @param fallbackUser If the transaction fails, send the escrowed funds to this address. * @param calldata_ Data field if a call should be made on the target chain. * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(
), ). At maximum 65535 bytes can be passed. - * @return uint256 The number of units minted. + * @return uint256 Number of units minted. */ function sendLiquidity( ICatalystV1Structs.RouteDescription calldata routeDescription, @@ -181,11 +181,11 @@ interface ICatalystV1VaultPermissionless { /** * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing. * @dev Called exclusively by the chainInterface. - * @param fromVault The source vault - * @param toAccount The recipient of the vault tokens + * @param fromVault Source vault + * @param toAccount Recipient of the vault tokens * @param U Number of units to convert into vault tokens. - * @param minVaultTokens The minimum number of vault tokens to mint on target vault. Otherwise: Reject - * @param minReferenceAsset The minimum number of reference asset the vaults tokens are worth. Otherwise: Reject + * @param minVaultTokens Minimum number of vault tokens to mint on target vault. Otherwise: Reject + * @param minReferenceAsset Minimum number of reference asset the vaults tokens are worth. Otherwise: Reject * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain. * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side. */ diff --git a/evm/src/interfaces/ICatalystV1VaultState.sol b/evm/src/interfaces/ICatalystV1VaultState.sol index c5c247fb..811ee504 100644 --- a/evm/src/interfaces/ICatalystV1VaultState.sol +++ b/evm/src/interfaces/ICatalystV1VaultState.sol @@ -5,10 +5,10 @@ import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMes interface ICatalystV1Structs is IMessageEscrowStructs { /** - * @param chainIdentifier The target chain identifier. - * @param toVault The target vault on the target chain. Encoded in 64 + 1 bytes. - * @param toAccount The recipient of the transaction on the target chain. Encoded in 64 + 1 bytes. - * @param incentive The cross-chain relaying incentive description. + * @param chainIdentifier target chain identifier. + * @param toVault Target vault on the target chain. Encoded in 64 + 1 bytes. + * @param toAccount Recipient of the transaction on the target chain. Encoded in 64 + 1 bytes. + * @param incentive Cross-chain relaying incentive description. */ struct RouteDescription { bytes32 chainIdentifier; @@ -19,42 +19,44 @@ interface ICatalystV1Structs is IMessageEscrowStructs { } } -/// @title Vault state -/// @notice Contains all vault storage which depends on the vault state. +/** + * @title Vault state + * @notice Contains all vault storage which depends on the vault state. + */ interface ICatalystV1VaultState { - /// @notice The token weights. Used for maintaining a non symmetric vault asset balance. + /** @notice Token weights. Used for maintaining a non symmetric vault asset balance. */ function _weight(address token) external view returns (uint256); function _adjustmentTarget() external view returns (uint48); function _lastModificationTime() external view returns (uint48); - /// @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) + /** @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) */ function _vaultFee() external view returns (uint64); function _governanceFeeShare() external view returns (uint64); - /// @notice The address of the responsible for adjusting the fees. + /** @notice The address of the responsible for adjusting the fees. */ function _feeAdministrator() external view returns (address); - /// @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. + /** @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. */ function _setupMaster() external view returns (address); // Security limit - /// @notice The max incoming liquidity flow from the router. + /** @notice The max incoming liquidity flow from the router. */ function _maxUnitCapacity() external view returns (uint256); // Escrow reference - /// @notice Total current escrowed tokens + /** @notice Total current escrowed tokens */ function _escrowedTokens(address token) external view returns (uint256); - /// @notice Find escrow information. Used for both normal swaps and liquidity swaps. + /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */ function _escrowLookup(bytes32 sendAssetHash) external view returns (address); - /// @notice Total current escrowed vault tokens + /** @notice Total current escrowed vault tokens */ function _escrowedVaultTokens() external view returns (uint256); - /// @notice Checks if there is a connection to the described vault + /** @notice Checks if there is a connection to the described vault */ function _vaultConnection(bytes32 sourceIdentifier, bytes calldata fromVault) external view returns (bool); function factoryOwner() external view returns (address); diff --git a/evm/src/interfaces/ICatalystV1VaultSuccessFailure.sol b/evm/src/interfaces/ICatalystV1VaultSuccessFailure.sol index bd2d199d..bbc19e75 100644 --- a/evm/src/interfaces/ICatalystV1VaultSuccessFailure.sol +++ b/evm/src/interfaces/ICatalystV1VaultSuccessFailure.sol @@ -1,8 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -/// @title Escrow related functions defined by Catalyst v1 Vaults -/// @notice Contains the functions used to manage escrows by the cross-chain interface. +/** + * @title Escrow related functions defined by Catalyst v1 Vaults + * @notice Contains the functions used to manage escrows by the cross-chain interface. + */ interface ICatalystV1VaultSuccessFailure { /** @notice Release the escrowed tokens into the vault. */ function onSendAssetSuccess( From 243df534043092f4ece53ad717ab33d360cbffdf Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 12:11:16 +0200 Subject: [PATCH 13/22] feat: finish review of contracts --- evm/script/Registry.s.sol | 12 +- evm/script/config/config_contracts.json | 10 +- evm/src/CatalystVaultCommon.sol | 433 ++++++++++++------ evm/src/IntegralsAmplified.sol | 62 ++- evm/src/IntegralsVolatile.sol | 40 +- evm/src/interfaces/ICatalystV1VaultErrors.sol | 14 +- evm/src/registry/CatalystDescriber.sol | 260 ++++------- .../registry/CatalystDescriberRegistry.sol | 17 +- evm/src/registry/CatalystMathAmp.sol | 81 ++-- evm/src/registry/CatalystMathVol.sol | 64 +-- .../interfaces/ICatalystDescriber.sol | 66 +-- evm/src/registry/lib/Contains.sol | 2 +- 12 files changed, 557 insertions(+), 504 deletions(-) diff --git a/evm/script/Registry.s.sol b/evm/script/Registry.s.sol index 1e4c99cb..284c0242 100644 --- a/evm/script/Registry.s.sol +++ b/evm/script/Registry.s.sol @@ -117,10 +117,10 @@ contract Registry is BaseMultiChainDeployer { CatalystDescriberRegistry reg = CatalystDescriberRegistry(registry.describer_registry); // Check what is the current describer. - address[] memory describers = reg.get_vault_describers(); + CatalystDescriberRegistry.AddressAndVersion[] memory describers = reg.getVaultDescribers(); bool contains = false; for (uint256 i = 0; i < describers.length; ++i) { - if (describers[i] == registry.describer) contains = true; + if (describers[i].addr == registry.describer) contains = true; } if (!contains) reg.modifyDescriber(registry.describer, version); } @@ -128,14 +128,14 @@ contract Registry is BaseMultiChainDeployer { function setDescriber() internal { CatalystDescriber desc = CatalystDescriber(registry.describer); // Set (or update) the templates - address current_volatile_template = desc.version_to_template("volatile"); + address current_volatile_template = desc.versionToTemplate("volatile"); if (current_volatile_template != contracts.volatile_template) desc.modifyWhitelistedTemplate(contracts.volatile_template, "volatile"); - address current_amplified_template = desc.version_to_template("amplified"); + address current_amplified_template = desc.versionToTemplate("amplified"); if (current_amplified_template != contracts.amplified_template) desc.modifyWhitelistedTemplate(contracts.amplified_template, "amplified"); // Set (or update) the factory. - address current_factory = desc.version_to_factory("v1"); + address current_factory = desc.versionToFactory("v1"); if (current_factory != contracts.factory) desc.modifyWhitelistedFactory(contracts.factory, "v1"); // Set (or update) the cross-chain interfaces @@ -147,7 +147,7 @@ contract Registry is BaseMultiChainDeployer { } address excepted_cci = abi.decode(config_interfaces.parseRaw(string.concat(".", incentiveVersion, ".", currentChainKey, ".interface")), (address)); - address current_cci = desc.version_to_cci(incentiveVersion); + address current_cci = desc.versionToCCI(incentiveVersion); if (current_cci != excepted_cci) desc.modifyWhitelistedCCI(excepted_cci, incentiveVersion); } } diff --git a/evm/script/config/config_contracts.json b/evm/script/config/config_contracts.json index 6832cb5c..af13195a 100644 --- a/evm/script/config/config_contracts.json +++ b/evm/script/config/config_contracts.json @@ -1,10 +1,10 @@ { "contracts": { - "amplified_mathlib": "0xc090dcdC90178c86CFB643f6ce64aBecD3360247", - "amplified_template": "0x65E17EB8Eb46a7FA97d9E5316893C95D5e84bf35", - "factory": "0x343A85b1e0383A50D65adB5ed88B06cCF4187606", - "volatile_mathlib": "0x33D494F7AC4E506D31F37e3fD75CaE3ca87A3916", - "volatile_template": "0x35D30159b7A9B9098C1048cbe29168EffE7d3D02" + "amplified_mathlib": "0xBB5a61795DF139d79818A34B350e64DAF5ABA263", + "amplified_template": "0x227eDab609f7369291290e4f8bfB1Ab5ab09C4BD", + "factory": "0xa957Ab873c06933b389D32a58131f13e60E488c5", + "volatile_mathlib": "0x8a550acf727f484d93B1b18A6Fc87cE9081cE260", + "volatile_template": "0x6E288f366141CbbabeEB8F3d8efc2eE9D28c1fF2" }, "registry": { "describer": "0x5514d9b55CdCbA70A6aF19Ca1E3443b1abEa104A", diff --git a/evm/src/CatalystVaultCommon.sol b/evm/src/CatalystVaultCommon.sol index 2ca121c4..f1cc10a8 100644 --- a/evm/src/CatalystVaultCommon.sol +++ b/evm/src/CatalystVaultCommon.sol @@ -33,6 +33,13 @@ import { ICatalystV1Vault } from "./ICatalystV1Vault.sol"; * _ prefixed functions are internal. * Unless otherwise required, variables are exposed directly. Such that storage functions are * prefixed with _. + * + * Upon deleting the escrow, this contract special logic in case of refunds. We want to ensure that + * any acks does not revert to clear up the escrows. However, some tokens can revert on demand (blacklist tokens) + * For these tokens, we make an optimistic call to the token to send the assets to the user. We don't + * care if it actually goes through. Cases where this call would fail the user does not get anything. + * A caveat of the implementation is that tokens that revert by PANIC'ing or spending all gas, are not supported + * since there is a catch of OOO gas that reverts. It is then dependent on replaying the ack to release the escrow. */ abstract contract CatalystVaultCommon is Initializable, @@ -41,27 +48,35 @@ abstract contract CatalystVaultCommon is ERC20, ICatalystV1Vault { + /** @notice The fallback user used as a FALLBACK placeholder when underwrite escrows are set. */ + address constant UNDERWRITE_FALLBACK_USER = address(uint160(1)); + //--- Config ---// // The following section contains the configurable variables. - /// @notice Determines how fast the security limit decreases. - /// @dev Needs to be long enough for vault token providers to be notified of a breach but short enough for volatility to not soft-freeze the vault. + /** + * @notice Determines how fast the security limit decreases. + * @dev Needs to be long enough for vault token providers to be notified of a breach but short enough for volatility to not soft-freeze the vault. + */ uint256 constant DECAY_RATE = 1 days; - /// @notice Number of decimals used by the vault's vault tokens + /** @notice Number of decimals used by the vault's vault tokens */ uint8 constant DECIMALS = 18; - /// @notice The vault tokens initially minted to the user who set up the vault. - /// @dev The initial deposit along with this value determines the base value of a vault token. - uint256 constant INITIAL_MINT_AMOUNT = 1e18; // 10**decimals + /** + * @notice The vault tokens initially minted to the user who set up the vault. + * @dev The initial deposit along with this value determines the base value of a vault token. + */ + uint256 constant INITIAL_MINT_AMOUNT = 1e18; // 10**decimals - /// @notice Maximum number of assets supported - /// @dev Impacts the cost of some for loops. Can be changed without breaking compatibility. + /** + * @notice Maximum number of assets supported + * @dev Impacts the cost of some for loops. Can be changed without breaking compatibility. + */ uint8 constant MAX_ASSETS = 3; - //-- Variables --// + //-- ERC20 --// - // ERC20 string _name; string _symbol; @@ -73,25 +88,30 @@ abstract contract CatalystVaultCommon is return _symbol; } - // END ERC20 + //-- Variables --// // immutable variables can be read by proxies, thus it is safe to set this on the constructor. address public immutable FACTORY; address public _chainInterface; - /// @notice The approved connections for this vault, stored as _vaultConnection[connectionId][toVault] - /// @dev to vault is encoded as 64 + 1 bytes. + /** + * @notice The approved connections for this vault, stored as _vaultConnection[connectionId][toVault] + * @dev to vault is encoded as 64 + 1 bytes. + */ mapping(bytes32 => mapping(bytes => bool)) public _vaultConnection; - /// @notice To indicate which token is desired on the target vault, - /// the desired tokens are provided as an integer which maps to the - /// asset address. This variable is the map. + /** + * @notice To indicate which token is desired on the target vault, + * the desired tokens are provided as an integer which maps to the + * asset address. This variable is the map. + */ mapping(uint256 => address) public _tokenIndexing; - /// @notice The token weights. Used for maintaining a non-symmetric vault asset balance. + /** @notice The token weights. Used for maintaining a non-symmetric vault asset balance. */ mapping(address => uint256) public _weight; //-- Parameter Flow & Change variables --// + // Use getUnitCapacity to indirectly access these variables uint256 _usedUnitCapacity; uint48 public _adjustmentTarget; @@ -100,18 +120,27 @@ abstract contract CatalystVaultCommon is uint48 _usedUnitCapacityTimestamp; //-- Vault fee variables --// - /// @notice The total vault fee. Multiplied by 10**18. - /// @dev Implementation of fee: FixedPointMathLib.mulWad(amount, _vaultFee); + + /** + * @notice The total vault fee. Multiplied by 10**18. + * @dev To compute the respective fee, use mulWad: FixedPointMathLib.mulWad(amount, _vaultFee); + */ uint64 public _vaultFee; - /// @notice The governance's cut of _vaultFee. - /// @dev FixedPointMathLib.mulWad(FixedPointMathLib.mulWad(amount, _vaultFee), _governanceFeeShare); + + /** + * @notice The governance's cut of _vaultFee. + * @dev Usage: FixedPointMathLib.mulWad(FixedPointMathLib.mulWad(amount, _vaultFee), _governanceFeeShare); + */ uint64 public _governanceFeeShare; - /// @notice The vault fee can be changed. _feeAdministrator is the address allowed to change it + + /** @notice The vault fee can be changed. _feeAdministrator is the address allowed to change it */ address public _feeAdministrator; - /// @notice The setupMaster is the short-term owner of the vault. - /// They can connect the vault to vaults on other chains. - /// @dev !Can extract all of the vault value! Should be set to address(0) once setup is complete via 'finishSetup()'. + /** + * @notice The setupMaster is the short-term owner of the vault. + * They can connect the vault to vaults on other chains. + * @dev !Can extract all of the vault value! Should be set to address(0) once setup is complete via 'finishSetup()'. + */ address public _setupMaster; //--- Messaging router limit ---// @@ -119,24 +148,28 @@ abstract contract CatalystVaultCommon is // imposed on the DECAY_RATE-ly unidirectional liquidity flow. That is: // if the vault observes more than _maxUnitCapacity of incoming // units, then it will not accept further incoming units. This means the router - // can only drain a prefigured percentage of the vault every DECAY_RATE + // can only drain a prefigured percentage of the vault every DECAY_RATE. // For amplified vaults, the security limit is denominated in assets rather than Units. // Outgoing flow is subtracted from incoming flow until 0. - /// @notice The max incoming liquidity flow from the router. + /** @notice The max incoming liquidity flow from the router. */ uint256 public _maxUnitCapacity; // Escrow reference - /// @notice Total current escrowed tokens + /** @notice Total current escrowed tokens. */ mapping(address => uint256) public _escrowedTokens; - /// @notice Total current escrowed vault tokens + /** @notice Total current escrowed vault tokens. */ uint256 public _escrowedVaultTokens; - /// @notice Find escrow information. Used for both normal swaps and liquidity swaps. + /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */ mapping(bytes32 => address) public _escrowLookup; - /// @notice A mathematical lib which describes various properties of this contract. These helper functions are not contained the swap template, since they notisably inflate the contract side which reduceses the number of optimizer runs => increase the gas cost. + /** + * @notice A mathematical lib that describes various properties of this contract. + * These helper functions are not contained in the swap template, since they notisably inflate + * the contract side which reduceses the number of optimizer runs => increase the gas cost. + */ address immutable public MATHLIB; constructor(address factory_, address mathlib) payable { @@ -151,21 +184,23 @@ abstract contract CatalystVaultCommon is _disableInitializers(); } + /** @notice Get the factory owner. That is the owner that can configure this vault post finishSetup(). */ function factoryOwner() public view override returns (address) { return Ownable(FACTORY).owner(); } + /** @notice Governance fee destination. This is the address the governance fee is sent to. */ function governanceFeeDestination() public view override returns (address) { return ICatalystV1Factory(FACTORY)._governanceFeeDestination(); } /** * @notice Only allow Governance to change vault parameters - * @dev Because of dangerous permissions (setConnection, weight changes, amplification changes): + * @dev Because of dangerous permissions (setConnection, weight changes): * !CatalystFactory(_factory).owner() must be set to a timelock! */ modifier onlyFactoryOwner() { - require(msg.sender == factoryOwner()); // dev: Only factory owner + require(msg.sender == factoryOwner()); // dev: Only factory owner _; } @@ -196,7 +231,17 @@ abstract contract CatalystVaultCommon is // -- Setup Functions -- // - /** @notice Setup a vault. */ + /** + * @notice Setup a vault. + * @dev Is initializer. + * @param name_ Name of the vault token. + * @param symbol_ Symbol of the vault token. + * @param chainInterface The chain interface to use. Set address(0) to disable cross-chain swaps. + * @param vaultFee Initial vault fee, that is the fee charged on swaps. + * @param governanceFee Initial governance fee, that is the percentage of the vault fee that is sent to designated address. + * @param feeAdministrator Special address that can modify the pool fees. + * @param setupMaster User that is configuring the vault. + */ function setup( string calldata name_, string calldata symbol_, @@ -216,18 +261,17 @@ abstract contract CatalystVaultCommon is _setGovernanceFee(governanceFee); _setFeeAdministrator(feeAdministrator); - // Name the ERC20 vault token // + // Name the ERC20 vault token _name = name_; _symbol = symbol_; - // END ERC20 // } /** - * @notice Creates a connection to toVault on the channel_channelId. + * @notice Creates a connection to toVault on the channelId. * @dev Encoding addresses in 64 + 1 bytes for EVM. - * For Solidity, this can be done as abi.encodePacket(uint8(20), bytes32(0), abi.encode(toAddress)) - * @param channelId Target chain identifier. + * For Solidity, this can be done as bytes.concat(bytes1(0x14), bytes32(0), abi.encode(toAddress)) + * @param channelId Target chain identifier. Varies from AMB to AMB. * @param toVault 64 + 1 bytes representation of the target vault. * @param state Boolean indicating if the connection should be open or closed. */ @@ -237,7 +281,7 @@ abstract contract CatalystVaultCommon is bool state ) external override { require(msg.sender == _setupMaster); // dev: No auth - require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. + require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes. _vaultConnection[channelId][toVault] = state; @@ -247,7 +291,7 @@ abstract contract CatalystVaultCommon is /** * @notice Gives up short-term ownership of the vault. This makes the vault unstoppable. * @dev This function should ALWAYS be called before other liquidity providers deposit liquidity. - * While it is not recommended, the escrow should ensure it is relativly safe trading through it (assuming a minimum output is set). + * While it is not recommended, swapping should be relativly safe since because of the escrow (assuming a minimum output is set). */ function finishSetup() external override { require(msg.sender == _setupMaster); // dev: No auth @@ -259,18 +303,29 @@ abstract contract CatalystVaultCommon is /** * @notice View function to signal if a vault is safe to use. - * @dev Checks if the setup master has been set to ZERO_ADDRESS. - * In other words, has finishSetup been called? + * @dev Checks if setupMaster has been set to ZERO_ADDRESS. In other words, has finishSetup been called? + * This function is not "safe()". To properly verify if a pool is "ready", verify: + * - The vault template is trusted. + * - The cross-chain interfce is trusted. + * - All connections are correctly set. (Valid chains, valid vaults) + * + * If you are providing liquidity to a vault, furthermore check: + * - All assets in the pool are trusted. + * + * The above checks have to be done on every vault in the pool. */ function ready() external view override returns (bool) { - // _setupMaster == address(0) ensures the pool is safe. The setup master can drain the pool! + // _setupMaster == address(0) ensures a safe pool cannot be made unsafe. The setup master can drain the pool! // _tokenIndexing[0] != address(0) check if the pool has been initialized correctly. // The additional check is there to ensure that the initial deployment returns false. return _setupMaster == address(0) && _tokenIndexing[0] != address(0); } - /** @notice Returns the current cross-chain swap capacity. */ + /** + * @notice Returns the current cross-chain swap capacity. + * @dev can be overridden to implement other limit curves. + */ function getUnitCapacity() public view virtual override returns (uint256) { uint256 MUC = _maxUnitCapacity; @@ -279,28 +334,29 @@ abstract contract CatalystVaultCommon is unchecked { // block.timestamp >= _usedUnitCapacityTimestamp, always. // MUC is generally low. - unitCapacityReleased = (block.timestamp - _usedUnitCapacityTimestamp); - } - unitCapacityReleased *= MUC; - unchecked { + unitCapacityReleased = block.timestamp - _usedUnitCapacityTimestamp; + + // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until + // some assets are withdrawn. As a fix, let if overflow. If it overflows, then + // unitCapacityReleased becomes smaller so there are no security implications. + unitCapacityReleased *= MUC; + // DECAY_RATE != 0. unitCapacityReleased /= DECAY_RATE; } uint256 UC = _usedUnitCapacity; - // If the change is greater than the units which have passed through - // return maximum. We do not want (MUC - (UC - unitCapacityReleased) > MUC) + // If the change is greater than the units than the spent security limit return maximum. + //If we computed it as (MUC - UC + unitCapacityReleased > MUC) it would obviously be wrong. if (UC <= unitCapacityReleased) return MUC; // Amplified vaults can have MUC <= UC since MUC is modified when swapping. unchecked { - // we know that UC > unitCapacityReleased + // We know UC > unitCapacityReleased. if (MUC <= UC - unitCapacityReleased) return 0; - // we know UC > unitCapacityReleased - // and because of the above if statement, we know - // MUC > (UC - unitCapacityReleased) - // Thus we can compute the difference unchecked. + // We know UC > unitCapacityReleased and with the above statement we know + // MUC > (UC - unitCapacityReleased). Thus we can compute the difference unchecked. return MUC - (UC - unitCapacityReleased); } @@ -309,7 +365,7 @@ abstract contract CatalystVaultCommon is // -- Utils -- // /** - * @notice Checks if the vault supports an inflow of units and decreases + * @notice Check if the vault supports an inflow of units and decrease * unit capacity by the inflow. * @dev Implement a lot of similar logic to getUnitCapacity. * @param Units The number of units to check and set. @@ -323,75 +379,99 @@ abstract contract CatalystVaultCommon is // block.timestamp > _usedUnitCapacityTimestamp, always. // MUC is generally low. unitCapacityReleased = (block.timestamp - _usedUnitCapacityTimestamp); - } - unitCapacityReleased *= MUC; - unchecked { + + // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until + // some assets are withdrawn. As a fix, let if overflow. If it overflows, then + // unitCapacityReleased becomes smaller so there are no security implications. + unitCapacityReleased *= MUC; + // DECAY_RATE != 0. unitCapacityReleased /= DECAY_RATE; } uint256 UC = _usedUnitCapacity; - // If the change is greater than the units which have passed through the limit is max + // If the change is greater than the units than the spent security limit it is at max. if (UC <= unitCapacityReleased) { + // The new limit is MUC, check if more units than MUC are getting spent. if (Units > MUC) revert ExceedsSecurityLimit(); - _usedUnitCapacityTimestamp = uint48(block.timestamp); // Set last change to block.timestamp. + // Set last change and the new spent security limit. + _usedUnitCapacityTimestamp = uint48(block.timestamp); _usedUnitCapacity = Units; return; } - uint256 newUnitFlow = UC + Units; // (UC + units) - unitCapacityReleased + // Compute the new spent security limit. Start by adding used unit capacity + // and to be spent units + uint256 newUnitFlow = UC + Units; unchecked { - // We know that UC > unitCapacityReleased + // Then subtract the units that have been decayed. We know UC + Units >= UC > unitCapacityReleased newUnitFlow -= unitCapacityReleased; } + // Then check if the new spent security limit is larger than maximum. if (newUnitFlow > MUC) revert ExceedsSecurityLimit(); - _usedUnitCapacityTimestamp = uint48(block.timestamp); // Set last change to block.timestamp. + + // Set last change and the new spent security limit. + _usedUnitCapacityTimestamp = uint48(block.timestamp); _usedUnitCapacity = newUnitFlow; - return; } // -- Governance Functions -- // - /// @notice Sets a new fee administrator that can configure vault fees. - /// @dev The fee administrator is responsible for modifying vault fees. + /** @notice Sets a new fee administrator that can configure vault fees. */ function _setFeeAdministrator(address administrator) internal { _feeAdministrator = administrator; emit SetFeeAdministrator(administrator); } - /// @notice Sets a new vault fee, taken from input amount. + /** + * @notice Sets a new vault fee, taken from input amount. + * @param fee Fee in WAD terms. 12e16 is 12%. + */ function _setVaultFee(uint64 fee) internal { - require(fee <= 1e18); // dev: VaultFee is maximum 100%. + require(fee <= FixedPointMathLib.WAD); // dev: VaultFee is maximum 100%. _vaultFee = fee; emit SetVaultFee(fee); } - /// @notice Sets a new governance fee. Taken out of the vault fee. + /** + * @notice Sets a new governance fee. Taken of the vault fee. + * @param fee Fee in WAD terms. 12e16 is 12%. + */ function _setGovernanceFee(uint64 fee) internal { require(fee <= MAX_GOVERNANCE_FEE_SHARE); // dev: Maximum GovernanceFeeSare exceeded. _governanceFeeShare = fee; emit SetGovernanceFee(fee); } - /// @notice Allows the factory owner to set a new fee administrator + /** @notice Allows the factory owner to set a new fee administrator. */ function setFeeAdministrator(address administrator) public override onlyFactoryOwner { _setFeeAdministrator(administrator); } - /// @notice Allows the factory owner to set a new the governance fee + /** + * @notice Allows the factory owner to set the governance fee. + * @dev Can only be called by factory owner. + * @param fee Fee in WAD terms. 12e16 is 12%. + */ function setGovernanceFee(uint64 fee) public override onlyFactoryOwner { _setGovernanceFee(fee); } - /// @notice Allows the factory owner to modify the vault fee + /** + * @notice Allows the feeAdministrator to modify the vault fee. + * @dev Can only be called by feeAdministrator + * @param fee Fee in WAD terms. 12e16 is 12%. + */ function setVaultFee(uint64 fee) public override { require(msg.sender == _feeAdministrator); // dev: Only feeAdministrator can set new fee _setVaultFee(fee); } - /// @notice Collect the governance fee share of the specified vault fee - /// @dev The governance fee share is transfered to the factory owner. + /** + * @notice Collect the governance fee share of the specified vault fee. + * @dev The governance fee share is transfered to governanceFeeDestination. + */ function _collectGovernanceFee(address asset, uint256 vaultFeeAmount) internal { uint256 governanceFeeShare = _governanceFeeShare; @@ -404,8 +484,16 @@ abstract contract CatalystVaultCommon is //-- Escrow Functions --// - /// @notice Creates a token escrow for a swap. - /// @param fallbackUser The user who the escrow belongs to. Do not set to address(0). + /** + * @notice Create a token escrow for a swap. + * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost. + * @param sendAssetHash The escrow context hash. Will be used to recover the escrow. + * From a implementation / usage perspective, if this hash contains fromAsset and amount + * it will improves by allowing on to verify these values independently. + * @param fallbackUser The user who the escrow belongs to. Do not set to address(0). + * @param fromAsset Asset to escrow. + * @param amount Amount to escrow. + */ function _setTokenEscrow( bytes32 sendAssetHash, address fallbackUser, @@ -420,8 +508,15 @@ abstract contract CatalystVaultCommon is } } - /// @notice Creates a liquidity escrow for a swap. - /// @param fallbackUser The user who the escrow belongs to. Do not set to address(0). + /** + * @notice Create a liquidity escrow for a swap. + * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost. + * @param sendLiquidityHash The escrow context hash. Will be used to recover the escrow. + * From a implementation / usage perspective, if this hash contains vaultTokens + * it will improves by allowing on to verify these values independently. + * @param fallbackUser The user who the escrow belongs to. Do not set to address(0). + * @param vaultTokens Number of vault tokens to escrow. + */ function _setLiquidityEscrow( bytes32 sendLiquidityHash, address fallbackUser, @@ -429,56 +524,62 @@ abstract contract CatalystVaultCommon is ) internal { if (_escrowLookup[sendLiquidityHash] != address(0)) revert EscrowAlreadyExists(); _escrowLookup[sendLiquidityHash] = fallbackUser; + + // Escrow vault tokens are first burned and then escrowed. As a result, this may overflow unlike _escrowedTokens. _escrowedVaultTokens += vaultTokens; } - /// @notice Returns the fallbackUser for the escrow and cleans up the escrow information. - /// @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. + /** + * @notice Returns the fallbackUser for the escrow and cleans up the escrow information. + * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. + */ function _releaseAssetEscrow( bytes32 sendAssetHash, uint256 escrowAmount, address escrowToken ) internal returns(address) { - - address fallbackUser = _escrowLookup[sendAssetHash]; // Passing in an invalid swapHash returns address(0) - require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. - delete _escrowLookup[sendAssetHash]; // Stops timeout and further acks from being called + address fallbackUser = _escrowLookup[sendAssetHash]; // Passing in an invalid swapHash returns address(0). + require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. + delete _escrowLookup[sendAssetHash]; // Stops timeout and further acks from being called. unchecked { - // escrowAmount \subseteq _escrowedTokens => escrowAmount <= _escrowedTokens. Cannot be called twice since the 3 lines before ensure this can only be reached once. + // escrowAmount \subseteq _escrowedTokens => escrowAmount <= _escrowedTokens. + // Cannot be called twice since the 3 lines before ensure this can only be reached once. _escrowedTokens[escrowToken] -= escrowAmount; } return fallbackUser; } - /// @notice Returns the fallbackUser for the escrow and cleans up the escrow information. - /// @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. + /** + * @notice Returns the fallbackUser for the escrow and cleans up the escrow information. + * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once. + */ function _releaseLiquidityEscrow( bytes32 sendLiquidityHash, uint256 escrowAmount ) internal returns(address) { - - address fallbackUser = _escrowLookup[sendLiquidityHash]; // Passing in an invalid swapHash returns address(0) - require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. - delete _escrowLookup[sendLiquidityHash]; // Stops timeout and further acks from being called + address fallbackUser = _escrowLookup[sendLiquidityHash]; // Passing in an invalid swapHash returns address(0). + require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist. + delete _escrowLookup[sendLiquidityHash]; // Stops timeout and further acks from being called. unchecked { - // escrowAmount \subseteq _escrowedVaultTokens => escrowAmount <= _escrowedVaultTokens. Cannot be called twice since the 3 lines before ensure this can only be reached once. + // escrowAmount \subseteq _escrowedVaultTokens => escrowAmount <= _escrowedVaultTokens. + // Cannot be called twice since the 3 lines before ensure this can only be reached once. _escrowedVaultTokens -= escrowAmount; } - + return fallbackUser; } /** * @notice Implements basic ack logic: Deletes and releases tokens to the vault - * @dev Should never revert! For security limit adjustments, the implementation should be overwritten. - * @param toAccount The recipient of the transaction on the target chain. Encoded in bytes32. - * @param U The number of units initially purchased. - * @param escrowAmount The number of tokens escrowed. - * @param escrowToken The token escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @dev Should never revert! For security limit adjustments, the implementation may have to be overwritten. + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units initially purchased. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Address of the escrowed token. + * @param blockNumberMod Block number when the transaction was commited (mod 32) */ function onSendAssetSuccess( bytes32 channelId, @@ -488,10 +589,9 @@ abstract contract CatalystVaultCommon is address escrowToken, uint32 blockNumberMod ) onlyChainInterface public override virtual { - // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. - // To save a bit of gas, this hash is computed and saved and then used. + // To save a bit of gas, this hash is computed, cached, and then used. bytes32 sendAssetHash = _computeSendAssetHash( // Computing the hash doesn't revert. toAccount, // Ensures no collisions between different users U, // Used to randomise the hash @@ -502,7 +602,7 @@ abstract contract CatalystVaultCommon is _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow - emit SendAssetSuccess( // Never reverts. + emit SendAssetSuccess( channelId, toAccount, U, @@ -515,11 +615,16 @@ abstract contract CatalystVaultCommon is /** * @notice Implements basic timeout logic: Deletes and sends tokens to the user. * @dev Should never revert! - * @param toAccount The recipient of the transaction on the target chain. Encoded in bytes32. - * @param U The number of units initially purchased. - * @param escrowAmount The number of tokens escrowed. - * @param escrowToken The token escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * For blacklist tokens, this function contains custom logic to support failing ERC20 transfer. + * If an ERC20 transfer fails (say because of a blocklist), we only revert if it was because of OOO. + * This implies that if an ERC20 transfer fails, then the escrow is lost. This shouldn't be the cast + * except if a token is paused, blacklisted, or the vault is exploited such that it has less assets + * than the escrow. + * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. + * @param U Number of units initially purchased. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Token escrowed. + * @param blockNumberMod Block number of the transaction that commited the swap (mod 32) */ function onSendAssetFailure( bytes32 channelId, @@ -529,7 +634,6 @@ abstract contract CatalystVaultCommon is address escrowToken, uint32 blockNumberMod ) onlyChainInterface public override virtual { - // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. // To save a bit of gas, this hash is computed and saved and then used. @@ -544,22 +648,25 @@ abstract contract CatalystVaultCommon is // This call provides re-entry protection against re-entering this call. Otherwise, this call can always be called. address fallbackAddress = _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow, - // We are going to make a low-level call. It may revert (see comment below) but it should not revert if it runs out of gas (that should be raised). As such, get the current gas in the contract. + // We are going to make a low-level call. It may revert (see comment below) but it should not revert if it runs out of gas (that should be raised). + // As such, get the current gas in the contract. uint256 gasLeftBeforeCall = gasleft(); - // Make a low level call such that the transfer never fails. This is important for tokens - // that use block lists. - // This also implies that if you get blacklisted between when you initiated the swap and the swap failed, you - // would lose the tokens. + bool success; + // Make a low level call such that the transfer never fails. This is important for tokens using block lists. + // This also implies that if you get blacklisted between when you initiated the swap and the swap failed, you would lose the tokens. bytes memory payload = abi.encodeWithSignature("transfer(address,uint256)", fallbackAddress, escrowAmount); assembly ("memory-safe") { - let success := call(0x8000000000000000000000000000000000000000000000000000000000000000, escrowToken, 0, add(payload, 0x20), mload(payload), 0, 0) + // The gas limit is set to 0x8000000000000000000000000000000000000000000000000000000000000000. + // This is essentially all gas since the actual gas forwarded is min(gasForwarded, gasleft * 63/64). + success := call(0x8000000000000000000000000000000000000000000000000000000000000000, escrowToken, 0, add(payload, 0x20), mload(payload), 0, 0) // SafeTransferLib.safeTransferFrom(escrowToken, fallbackAddress, escrowAmount); } - // Check that the call didn't use all of its gas. - if(gasleft() < gasLeftBeforeCall * 1 / 63) revert NotEnoughGas(); - emit SendAssetFailure( // Never reverts. + // If the call failed, check if it failed with OOO (If the call used all of the gas it has available). + if (!success) if (gasleft() < gasLeftBeforeCall * 1 / 63) revert NotEnoughGas(); + + emit SendAssetFailure( channelId, toAccount, U, @@ -572,10 +679,10 @@ abstract contract CatalystVaultCommon is /** * @notice Implements basic liquidity ack logic: Deletes and releases vault tokens to the vault. * @dev Should never revert! For security limit adjustments, the implementation should be overwritten. - * @param toAccount The recipient of the transaction on the target chain. Encoded in bytes32. - * @param U The number of units initially acquired. - * @param escrowAmount The number of vault tokens escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. + * @param U Number of units initially acquired. + * @param escrowAmount Number of vault tokens escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquiditySuccess( bytes32 channelId, @@ -588,16 +695,16 @@ abstract contract CatalystVaultCommon is // We need to find the location of the escrow using the information below. // We need to do this twice: 1. Get the address. 2. Delete the escrow. // To save a bit of gas, this hash is computed and saved and then used. - bytes32 sendLiquidityHash = _computeSendLiquidityHash( // Computing the hash doesn't revert. + bytes32 sendLiquidityHash = _computeSendLiquidityHash( toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); - _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Only reverts for missing escrow + _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow - emit SendLiquiditySuccess( // Never reverts. + emit SendLiquiditySuccess( channelId, toAccount, U, @@ -609,10 +716,10 @@ abstract contract CatalystVaultCommon is /** * @notice Implements basic liquidity timeout logic: Deletes and sends vault tokens to the user. * @dev Should never revert! - * @param toAccount The recipient of the transaction on the target chain. Encoded in bytes32. - * @param U The number of units initially acquired. - * @param escrowAmount The number of vault tokens escrowed. - * @param blockNumberMod The block number at which the swap transaction was commited (mod 32) + * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32. + * @param U Number of units initially acquired. + * @param escrowAmount Number of vault tokens escrowed. + * @param blockNumberMod Block number at which the swap transaction was commited (mod 32) */ function onSendLiquidityFailure( bytes32 channelId, @@ -622,19 +729,19 @@ abstract contract CatalystVaultCommon is uint32 blockNumberMod ) onlyChainInterface public override virtual { - bytes32 sendLiquidityHash = _computeSendLiquidityHash( // Computing the hash doesn't revert. + bytes32 sendLiquidityHash = _computeSendLiquidityHash( toAccount, // Ensures no collisions between different users U, // Used to randomise the hash escrowAmount, // Required! to validate release escrow data blockNumberMod // Used to randomize the hash. ); - // This call provides re-entry protection against re-entering this call. Otherwise, this call can always be called. - address fallbackAddress = _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Only reverts for missing escrow + // This function only allows entering this function once. Once called, it can never be called without reverting again. + address fallbackAddress = _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow _mint(fallbackAddress, escrowAmount); // Never reverts. - emit SendLiquidityFailure( // Never reverts. + emit SendLiquidityFailure( channelId, toAccount, U, @@ -643,8 +750,10 @@ abstract contract CatalystVaultCommon is ); } - /// @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. - /// However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. + /** + * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. + * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. + */ function _computeSendAssetHash( bytes calldata toAccount, uint256 U, @@ -654,17 +763,19 @@ abstract contract CatalystVaultCommon is ) internal pure returns(bytes32) { return keccak256( bytes.concat( - toAccount, // Ensures no collisions between different users - bytes32(U), // Used to randomise the hash - bytes32(amount), // Required! to validate release escrow data - bytes20(fromAsset), // Required! to validate release escrow data - bytes4(blockNumberMod) // Used to randomize the hash. + toAccount, // Ensures no collisions between different users. + bytes32(U), // Used to randomise the hash. + bytes32(amount), // Required! to validate release escrow data. + bytes20(fromAsset), // Required! to validate release escrow data. + bytes4(blockNumberMod) // Used to randomize the hash. ) ); } - /// @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. - /// However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. + /** + * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain. + * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations. + */ function _computeSendLiquidityHash( bytes calldata toAccount, uint256 U, @@ -673,10 +784,10 @@ abstract contract CatalystVaultCommon is ) internal pure returns(bytes32) { return keccak256( bytes.concat( - toAccount, // Ensures no collisions between different users - bytes32(U), // Used to randomise the hash - bytes32(amount), // Required! to validate release escrow data - bytes4(blockNumberMod) // Used to randomize the hash. + toAccount, // Ensures no collisions between different users. + bytes32(U), // Used to randomise the hash. + bytes32(amount), // Required! to validate release escrow data. + bytes4(blockNumberMod) // Used to randomize the hash. ) ); } @@ -689,11 +800,13 @@ abstract contract CatalystVaultCommon is uint256 U, uint256 minOut ) onlyChainInterface virtual public returns (uint256 purchasedTokens) { - purchasedTokens = _receiveAsset(toAsset, U, minOut); // msg.sender is cheaper than sload. + // Simulate a receiveAsset call. This gets us the purchased tokens. + purchasedTokens = _receiveAsset(toAsset, U, minOut); + // Set the escrow. _setTokenEscrow( identifier, - address(uint160(1)), + UNDERWRITE_FALLBACK_USER, toAsset, purchasedTokens ); @@ -706,6 +819,15 @@ abstract contract CatalystVaultCommon is ); } + /** + * @notice Release assets associated with an underwrite escrow. + * @param refundTo Released assets are sent to this address. + * @param identifier Underwriting identifier. Is used to index the storage for valid escrows. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Escrowed token address. + * @param sourceIdentifier The source chain identifier. + * @param fromVault The originating vault. + */ function releaseUnderwriteAsset( address refundTo, bytes32 identifier, @@ -714,20 +836,27 @@ abstract contract CatalystVaultCommon is bytes32 sourceIdentifier, bytes calldata fromVault ) onlyChainInterface onlyConnectedPool(sourceIdentifier, fromVault) virtual public { - _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Only reverts for missing escrow + _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow. // Send the assets to the user. SafeTransferLib.safeTransfer(escrowToken, refundTo, escrowAmount); } - /// @dev The unsued parameter U is used for overwrites. (see CataulystVaultAmplified.sol) + /** + * @notice Delete an underwrite escrow without releasing any tokens. + * @dev The unsued parameter U is used for overwrites. (see CataulystVaultAmplified.sol) + * @param identifier Underwriting identifier. Is used to index the storage for valid escrows. + * param U Number of underwritten units. Is used for Amplified vaults to modify the unit tracker. + * @param escrowAmount Number of tokens escrowed. + * @param escrowToken Escrowed token address. + */ function deleteUnderwriteAsset( bytes32 identifier, uint256 /* U */, uint256 escrowAmount, address escrowToken ) onlyChainInterface virtual public { - _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Only reverts for missing escrow + _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow. } } diff --git a/evm/src/IntegralsAmplified.sol b/evm/src/IntegralsAmplified.sol index 7e20dcec..9e8a29a8 100644 --- a/evm/src/IntegralsAmplified.sol +++ b/evm/src/IntegralsAmplified.sol @@ -7,7 +7,7 @@ import { WADWAD } from "./utils/MathConstants.sol"; /** * @title Catalyst: Amplified Integrals - * @author Catalyst Labs + * @author Catalyst Labs Inc. */ contract IntegralsAmplified { /** @@ -16,10 +16,10 @@ contract IntegralsAmplified { * The value is returned as units, which is always WAD. * @dev All input amounts should be the raw numbers and not WAD. * Since units are always denominated in WAD, the function should be treated as mathematically *native*. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param W The weight of the x token. - * @param oneMinusAmp The amplification. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param W Weight of the x token. + * @param oneMinusAmp Amplification. Provided as (1-k). * @return uint256 Units (units are **always** WAD). */ function _calcPriceCurveArea( @@ -31,23 +31,23 @@ contract IntegralsAmplified { // Will revert if W = 0. // Or if A + input == 0. int256 calc = FixedPointMathLib.powWad( - int256(W * (A + input) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails + int256(W * (A + input) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails. oneMinusAmp ); // If the vault contains 0 assets, the below computation will fail. This is bad. - // Instead, check if A is 0. If it is then skip because:: (W · A)^(1-k) = (W · 0)^(1-k) = 0 + // Instead, check if A is 0. If it is then skip because: (W · A)^(1-k) = (W · 0)^(1-k) = 0 if (A != 0) { unchecked { // W * A * FixedPointMathLib.WAD < W * (A + input) * FixedPointMathLib.WAD calc -= FixedPointMathLib.powWad( - int256(W * A * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails + int256(W * A * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails oneMinusAmp ); } } - return uint256(calc); // Casting always safe, as calc always > =0 + return uint256(calc); // Casting always safe, as calc always > =0 } /** @@ -58,11 +58,10 @@ contract IntegralsAmplified { * ) * The value is returned as output token. (not WAD) * @dev All input amounts should be the raw numbers and not WAD. - * Since units are always multiplied by WAD, the function - * should be treated as mathematically *native*. + * Since units are always multiplied by WAD, the function should be treated as mathematically *native*. * @param U Incoming vault specific units. - * @param B The current vault balance of the y token. - * @param W The weight of the y token. + * @param B Current vault balance of the y token. + * @param W Weight of the y token. * @return uint25 Output denominated in output token. (not WAD) */ function _calcPriceCurveLimit( @@ -71,20 +70,19 @@ contract IntegralsAmplified { uint256 W, int256 oneMinusAmp ) internal pure returns (uint256) { - // W_B · B^(1-k) is repeated twice and requires 1 power. - // As a result, we compute it and cache it. - uint256 W_BxBtoOMA = uint256( // Always casts a positive value + // W_B · B^(1-k) is repeated twice and requires 1 power. As a result, we compute it and cache it. + uint256 W_BxBtoOMA = uint256( // Always casts a positive value. FixedPointMathLib.powWad( - int256(W * B * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails + int256(W * B * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails. oneMinusAmp ) ); return FixedPointMathLib.mulWad( B, - FixedPointMathLib.WAD - uint256( // Always casts a positive value + FixedPointMathLib.WAD - uint256( // Always casts a positive value FixedPointMathLib.powWad( - int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)), // Casting never overflows, as division result is always < 1 + int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)), // Casting never overflows, as division result is always < 1 WADWAD / oneMinusAmp ) ) @@ -98,16 +96,15 @@ contract IntegralsAmplified { * (wB^(1-k) - (wA+wx)^(1-k) - wA^(1-k)) / (wB^(1-k)) * )^(1/(1-k)) * ) - * * Alternatively, the integral can be computed through: * _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, amp), B, W_B, amp). * @dev All input amounts should be the raw numbers and not WAD. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param B The current vault balance of the y token. - * @param W_A The weight of the x token. - * @param W_B The weight of the y token. - * @param oneMinusAmp The amplification. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param B Current vault balance of the y token. + * @param W_A Weight of the x token. + * @param W_B Weight of the y token. + * @param oneMinusAmp Amplification. * @return uint256 Output denominated in output token. */ function _calcCombinedPriceCurves( @@ -138,23 +135,22 @@ contract IntegralsAmplified { return _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp); } - /** * @notice Converts units into vault tokens with the below formula * pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1) * @dev The function leaves a lot of computation to the external implementation. This is done to avoid recomputing values several times. - * @param U Then number of units to convert into vault tokens. - * @param ts The current vault token supply. The escrowed vault tokens should not be added, since the function then returns more. + * @param U Number of units to convert into vault tokens. + * @param ts Current vault token supply. The escrowed vault tokens should not be added, since the function then returns more. * @param it_times_walpha_amped wa_0^(1-k) - * @param oneMinusAmpInverse The vault amplification. + * @param oneMinusAmpInverse Vault amplification. * @return uint256 Output denominated in vault tokens. */ function _calcPriceCurveLimitShare(uint256 U, uint256 ts, uint256 it_times_walpha_amped, int256 oneMinusAmpInverse) internal pure returns (uint256) { uint256 vaultTokens = FixedPointMathLib.mulWad( ts, - uint256( // Always casts a positive value, as powWad >= 1, hence powWad - WAD >= 0 - FixedPointMathLib.powWad( // poWad always >= 1, as the 'base' is always >= 1 - int256(FixedPointMathLib.divWad( // If casting overflows to a negative number, powWad fails + uint256( // Always casts a positive value, as powWad >= 1, hence powWad - WAD >= 0 + FixedPointMathLib.powWad( // poWad always >= 1, as the 'base' is always >= 1 + int256(FixedPointMathLib.divWad( // If casting overflows to a negative number, powWad fails it_times_walpha_amped + U, it_times_walpha_amped )), diff --git a/evm/src/IntegralsVolatile.sol b/evm/src/IntegralsVolatile.sol index 6bd56b69..124a9659 100644 --- a/evm/src/IntegralsVolatile.sol +++ b/evm/src/IntegralsVolatile.sol @@ -5,7 +5,7 @@ import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; /** * @title Catalyst: Volatile Integrals - * @author Catalyst Labs + * @author Catalyst Labs Inc. */ contract IntegralsVolatile { /** @@ -13,9 +13,9 @@ contract IntegralsVolatile { * The value is returned as units, which is always WAD. * @dev All input amounts should be the raw numbers and not WAD. * Since units are always denominated in WAD, the function should be treated as mathematically *native*. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param W The weight of the x token. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param W Weight of the x token. * @return uint256 Units (units are **always** WAD). */ function _calcPriceCurveArea( @@ -25,18 +25,17 @@ contract IntegralsVolatile { ) internal pure returns (uint256) { // Notice, A + in and A are not WAD but divWadDown is used anyway. // That is because lnWad requires a scaled number. - return W * uint256(FixedPointMathLib.lnWad(int256(FixedPointMathLib.divWad(A + input, A)))); // int256 casting is safe. If overflows, it returns negative. lnWad fails on negative numbers. If the vault balance is high, this is unlikely. + return W * uint256(FixedPointMathLib.lnWad(int256(FixedPointMathLib.divWad(A + input, A)))); // int256 casting is safe. If overflows, it returns negative. lnWad fails on negative numbers. If the vault balance is high, this is unlikely. } /** * @notice Solves the equation U = \int_{B-y}^{B} W/w dw for y = B · (1 - exp(-U/W)) * The value is returned as output token. (not WAD) * @dev All input amounts should be the raw numbers and not WAD. - * Since units are always multiplied by WAD, the function - * should be treated as mathematically *native*. + * Since units are always multiplied by WAD, the function should be treated as mathematically *native*. * @param U Incoming vault specific units. - * @param B The current vault balance of the y token. - * @param W The weight of the y token. + * @param B Current vault balance of the y token. + * @param W Weight of the y token. * @return uint25 Output denominated in output token. (not WAD) */ function _calcPriceCurveLimit( @@ -52,16 +51,15 @@ contract IntegralsVolatile { /** * @notice Solves the equation - * \int_{A}^{A+x} W_a/w dw = \int_{B-y}^{B} W_b/w dw for y = B · (1 - ((A+x)/A)^(-W_a/W_b)) - * + * \int_{A}^{A+x} W_a/w dw = \int_{B-y}^{B} W_b/w dw for y = B · (1 - ((A+x)/A)^(-W_a/W_b)) for y. * Alternatively, the integral can be computed through: * _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A), B, W_B). * @dev All input amounts should be the raw numbers and not WAD. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param B The current vault balance of the y token. - * @param W_A The weight of the x token. - * @param W_B The weight of the y token. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param B Current vault balance of the y token. + * @param W_A Weight of the x token. + * @param W_B Weight of the y token. * @return uint256 Output denominated in output token. */ function _calcCombinedPriceCurves( @@ -76,10 +74,9 @@ contract IntegralsVolatile { /** * @notice Solves the generalised swap integral. - * @dev Based on _calcPriceCurveLimit but the multiplication by the - * specific token is never done. + * @dev Based on _calcPriceCurveLimit but the multiplication by the specific token is never done. * @param U Input units. - * @param W The generalised weights. + * @param W Generalised weights. * @return uint256 Output denominated in vault share. */ function _calcPriceCurveLimitShare( @@ -87,10 +84,9 @@ contract IntegralsVolatile { uint256 W ) internal pure returns (uint256) { // Compute the non vault ownership share. (1 - vault ownership share) - uint256 npos = uint256(FixedPointMathLib.expWad(-int256(U / W))); // int256 casting is initially not safe. If overflow, the equation becomes: exp(U/W). In this case, when subtracted from 1 (later), Solidity's built-in safe math protection catches the overflow since exp(U/W) > 1. + uint256 npos = uint256(FixedPointMathLib.expWad(-int256(U / W))); // int256 casting is initially not safe. If overflow, the equation becomes: exp(U/W). In this case, when subtracted from 1 (later), Solidity's built-in safe math protection catches the overflow since exp(U/W) > 1. - // Compute the vault owner share before liquidity has been added. - // (solve share = pt/(PT+pt) for pt.) + // Compute the vault owner share before liquidity has been added. (solve share = pt/(PT+pt) for pt.) return FixedPointMathLib.divWad(FixedPointMathLib.WAD - npos, npos); } } diff --git a/evm/src/interfaces/ICatalystV1VaultErrors.sol b/evm/src/interfaces/ICatalystV1VaultErrors.sol index 745405f6..b1f23ace 100644 --- a/evm/src/interfaces/ICatalystV1VaultErrors.sol +++ b/evm/src/interfaces/ICatalystV1VaultErrors.sol @@ -1,10 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -error ExceedsSecurityLimit(); // 7c1e66d -error ReturnInsufficient(uint256 result, uint256 minimum); // 24557f0 -error VaultNotConnected(); // 2c64c1b -error WithdrawRatioNotZero(); // b8003bf -error UnusedUnitsAfterWithdrawal(uint256 Units); // 0289311 -error EscrowAlreadyExists(); // ed77877 -error NotEnoughGas(); // dd629f86 \ No newline at end of file +error EscrowAlreadyExists(); // 0xed778779 +error ExceedsSecurityLimit(); // 0x7c1e66d0 +error NotEnoughGas(); // 0xdd629f86 +error ReturnInsufficient(uint256,uint256); // 0x24557f05 +error UnusedUnitsAfterWithdrawal(uint256); // 0x0289311f +error VaultNotConnected(); // 0x2c64c1b2 +error WithdrawRatioNotZero(); // 0xb8003bfa \ No newline at end of file diff --git a/evm/src/registry/CatalystDescriber.sol b/evm/src/registry/CatalystDescriber.sol index f786bd0b..c9de1b84 100644 --- a/evm/src/registry/CatalystDescriber.sol +++ b/evm/src/registry/CatalystDescriber.sol @@ -8,7 +8,7 @@ import "../interfaces/ICatalystV1Factory.sol"; import { Contains } from "./lib/Contains.sol"; /** * @title Catalyst: Catalyst Describer - * @author Catalyst Labs + * @author Catalyst Labs Inc. * @notice This contract describes the a Catalyst implementation and serves to simplify off-chain queries. * As a result, the contract is not optimised for on-chain queries but rather easy of use off-chain. */ @@ -20,21 +20,21 @@ contract CatalystDescriber is Contains, Ownable { string version; } - /// @notice Emitted when a vault template is whitelisted and unwhitelisted. + /** @notice Emitted when a vault template is whitelisted and unwhitelisted. */ event ModifyTemplate( - address template_address, + address templateAddress, string version ); - /// @notice Emitted when a cross-chain interface is whitelisted and unwhitelisted. + /** @notice Emitted when a cross-chain interface is whitelisted and unwhitelisted. */ event ModifyWhitelistedCCI( - address cci_address, + address cciAddress, string version ); - /// @notice Emitted when vault is added or removed from the describer. + /** @notice Emitted when vault is added or removed from the describer. */ event ModifyWhitelistedFactory( - address factory_address, + address factoryAddress, string version ); @@ -42,13 +42,13 @@ contract CatalystDescriber is Contains, Ownable { address public latestRouter; - string[] public template_versions; - string[] public cci_versions; - string[] public factory_versions; + string[] public templateVersions; + string[] public CCIVersions; + string[] public factoryVersions; - mapping(string => address) public version_to_template; - mapping(string => address) public version_to_cci; - mapping(string => address) public version_to_factory; + mapping(string => address) public versionToTemplate; + mapping(string => address) public versionToCCI; + mapping(string => address) public versionToFactory; constructor(address defaultOwner) payable { @@ -57,34 +57,22 @@ contract CatalystDescriber is Contains, Ownable { } //--- Router ---// - function set_latest_router(address newRouter) external onlyOwner { + function setLatestRouter(address newRouter) external onlyOwner { latestRouter = newRouter; } //--- Getters ---// - /** - * @notice Return an array of whitelisted templates. The index in the array matches the index of template_versions. - * @dev Will not contain address(0). - */ - function get_whitelisted_templates() external view returns (address[] memory whitelistedTemplates) { - whitelistedTemplates = new address[](template_versions.length); - for (uint256 i = 0; i < template_versions.length; ++i) { - string memory version = template_versions[i]; - whitelistedTemplates[i] = version_to_template[version]; - } - } - /** * @notice Return an array of whitelisted templates along with their respective version. - * @dev Will not contain address(0), the index in the array matches the index of template_versions. + * @dev Will not contain address(0), the index in the array matches the index of templateVersions. */ function getWhitelistedTemplates() external view returns (AddressAndVersion[] memory whitelistedTemplates) { - whitelistedTemplates = new AddressAndVersion[](template_versions.length); - for (uint256 i = 0; i < template_versions.length; ++i) { - string memory version = template_versions[i]; + whitelistedTemplates = new AddressAndVersion[](templateVersions.length); + for (uint256 i = 0; i < templateVersions.length; ++i) { + string memory version = templateVersions[i]; whitelistedTemplates[i] = AddressAndVersion({ - addr: version_to_template[version], + addr: versionToTemplate[version], version: version }); } @@ -92,56 +80,29 @@ contract CatalystDescriber is Contains, Ownable { /** * @notice Return an array of whitelisted CCIs along with their respective version. - * @dev Will not contain address(0), the index in the array matches the index of cci_versions. - */ - function get_whitelisted_CCI() external view returns (AddressAndVersion[] memory whitelistedCCI) { - whitelistedCCI = new AddressAndVersion[](cci_versions.length); - for (uint256 i = 0; i < cci_versions.length; ++i) { - string memory version = cci_versions[i]; - whitelistedCCI[i] = AddressAndVersion({ - addr: version_to_cci[version], - version: version - }); - } - } - - /** - * @notice Return an array of whitelisted CCIs along with their respective version. - * @dev Will not contain address(0), the index in the array matches the index of cci_versions. + * @dev Will not contain address(0), the index in the array matches the index of CCIVersions. */ function getWhitelistedCCI() external view returns (AddressAndVersion[] memory whitelistedCCI) { - whitelistedCCI = new AddressAndVersion[](cci_versions.length); - for (uint256 i = 0; i < cci_versions.length; ++i) { - string memory version = cci_versions[i]; + whitelistedCCI = new AddressAndVersion[](CCIVersions.length); + for (uint256 i = 0; i < CCIVersions.length; ++i) { + string memory version = CCIVersions[i]; whitelistedCCI[i] = AddressAndVersion({ - addr: version_to_cci[version], + addr: versionToCCI[version], version: version }); } } - /** - * @notice Returns an array of whitelisted factories. The index in the array matches the index of factory_versions. - * @dev Will not contain address(0). - */ - function get_vault_factories() external view returns (address[] memory vaultFactories) { - vaultFactories = new address[](factory_versions.length); - for (uint256 i = 0; i < factory_versions.length; ++i) { - string memory version = factory_versions[i]; - vaultFactories[i] = version_to_factory[version]; - } - } - /** * @notice Return an array of whitelisted factories along with their respective version. - * @dev Will not contain address(0), the index in the array matches the index of factory_versions. + * @dev Will not contain address(0), the index in the array matches the index of factoryVersions. */ function getWhitelistedFactories() external view returns (AddressAndVersion[] memory vaultFactories) { - vaultFactories = new AddressAndVersion[](factory_versions.length); - for (uint256 i = 0; i < factory_versions.length; ++i) { - string memory version = factory_versions[i]; + vaultFactories = new AddressAndVersion[](factoryVersions.length); + for (uint256 i = 0; i < factoryVersions.length; ++i) { + string memory version = factoryVersions[i]; vaultFactories[i] = AddressAndVersion({ - addr: version_to_factory[version], + addr: versionToFactory[version], version: version }); } @@ -149,89 +110,62 @@ contract CatalystDescriber is Contains, Ownable { /** * @notice Returns the number of whitelisted templates. - * @dev Returns the length of template_versions which should contain no empty entries. - */ - function get_num_whitelisted_templates() external view returns(uint256) { - return template_versions.length; - } - - /** - * @notice Returns the number of whitelisted templates. - * @dev Returns the length of template_versions which should contain no empty entries. + * @dev Returns the length of templateVersions which should contain no empty entries. */ function getNumWhitelistedTemplates() external view returns(uint256) { - return template_versions.length; - } - - /** - * @notice Returns the number of whitelisted CCIs. - * @dev Returns the length of cci_versions which should contain no empty entries. - */ - function get_num_whitelisted_ccis() external view returns(uint256) { - return cci_versions.length; + return templateVersions.length; } /** * @notice Returns the number of whitelisted CCIs. - * @dev Returns the length of cci_versions which should contain no empty entries. + * @dev Returns the length of CCIVersions which should contain no empty entries. */ function getNumWhitelistedCcis() external view returns(uint256) { - return cci_versions.length; + return CCIVersions.length; } - /** * @notice Returns the number of whitelisted factories. - * @dev Returns the length of factory_versions which should contain no empty entries. - */ - function get_num_vault_factories() external view returns (uint256) { - return factory_versions.length; - } - - /** - * @notice Returns the number of whitelisted factories. - * @dev Returns the length of factory_versions which should contain no empty entries. + * @dev Returns the length of factoryVersions which should contain no empty entries. */ function getNumVaultFactories() external view returns (uint256) { - return factory_versions.length; + return factoryVersions.length; } - //--- Modifiers ---// - // -- Templates -- // /** * @notice Sets or modifies a template with a (new) address. - * @dev template_address cannot be 0 Instead, use the remove version. - * @param template_address The address of the template which should be whitelisted. - * @param version The version which the new template should be set to. + * @dev templateAddress cannot be 0 Instead, use the remove version. + * @param templateAddress Address of the template that should be whitelisted. + * @param version Version that the new template should be set to. */ - function modifyWhitelistedTemplate(address template_address, string calldata version) external onlyOwner { - if (template_address == address(0)) revert ZeroAddress(); + function modifyWhitelistedTemplate(address templateAddress, string calldata version) external onlyOwner { + if (templateAddress == address(0)) revert ZeroAddress(); // Update version table - uint256 indexOfVersion = _contains(version, template_versions); - if (indexOfVersion == type(uint256).max) template_versions.push(version); + uint256 indexOfVersion = _contains(version, templateVersions); + if (indexOfVersion == type(uint256).max) templateVersions.push(version); - version_to_template[version] = template_address; + versionToTemplate[version] = templateAddress; - emit ModifyTemplate(template_address, version); + emit ModifyTemplate(templateAddress, version); } - function removeWhitelistedTemplate(address template_to_remove, string calldata version) external onlyOwner { - address read_template = version_to_template[version]; - if (read_template != template_to_remove) revert IncorrectAddress(read_template, template_to_remove); + function removeWhitelistedTemplate(address templateToRemove, string calldata version) external onlyOwner { + address read_template = versionToTemplate[version]; + if (read_template != templateToRemove) revert IncorrectAddress(read_template, templateToRemove); - uint256 indexOfVersion = _contains(version, template_versions); + uint256 indexOfVersion = _contains(version, templateVersions); if (indexOfVersion == type(uint256).max) revert DoesNotExist(); - if (template_versions.length > 1) { + if (templateVersions.length > 1) { // swap the last element into this element's place. - template_versions[indexOfVersion] = template_versions[template_versions.length - 1]; - template_versions.pop(); + templateVersions[indexOfVersion] = templateVersions[templateVersions.length - 1]; + templateVersions.pop(); } - version_to_template[version] = address(0); + versionToTemplate[version] = address(0); emit ModifyTemplate(address(0), version); } @@ -240,36 +174,36 @@ contract CatalystDescriber is Contains, Ownable { /** * @notice Sets or modifies a cci with a (new) address. - * @dev cci_address cannot be 0 Instead, use the remove version. - * @param cci_address The address of the cci which should be whitelisted. - * @param version The version which the new cci should be set to. + * @dev cciAddress cannot be 0 Instead, use the remove version. + * @param cciAddress Address of the cci which should be whitelisted. + * @param version Version that the new cci should be set to. */ - function modifyWhitelistedCCI(address cci_address, string calldata version) external onlyOwner { - if (cci_address == address(0)) revert ZeroAddress(); + function modifyWhitelistedCCI(address cciAddress, string calldata version) external onlyOwner { + if (cciAddress == address(0)) revert ZeroAddress(); // Update version table - uint256 indexOfVersion = _contains(version, cci_versions); - if (indexOfVersion == type(uint256).max) cci_versions.push(version); + uint256 indexOfVersion = _contains(version, CCIVersions); + if (indexOfVersion == type(uint256).max) CCIVersions.push(version); - version_to_cci[version] = cci_address; + versionToCCI[version] = cciAddress; - emit ModifyWhitelistedCCI(cci_address, version); + emit ModifyWhitelistedCCI(cciAddress, version); } - function removeWhitelistedCCI(address cci_to_remove, string calldata version) external onlyOwner { - address read_cci = version_to_cci[version]; - if (read_cci != cci_to_remove) revert IncorrectAddress(read_cci, cci_to_remove); + function removeWhitelistedCCI(address cciToRemove, string calldata version) external onlyOwner { + address read_cci = versionToCCI[version]; + if (read_cci != cciToRemove) revert IncorrectAddress(read_cci, cciToRemove); - uint256 indexOfVersion = _contains(version, template_versions); + uint256 indexOfVersion = _contains(version, templateVersions); if (indexOfVersion == type(uint256).max) revert DoesNotExist(); - if (cci_versions.length > 1) { + if (CCIVersions.length > 1) { // swap the last element into this element's place. - cci_versions[indexOfVersion] = cci_versions[cci_versions.length - 1]; - cci_versions.pop(); + CCIVersions[indexOfVersion] = CCIVersions[CCIVersions.length - 1]; + CCIVersions.pop(); } - version_to_cci[version] = address(0); + versionToCCI[version] = address(0); emit ModifyWhitelistedCCI(address(0), version); } @@ -278,37 +212,37 @@ contract CatalystDescriber is Contains, Ownable { /** * @notice Sets or modifies a factory with a (new) address. - * @dev factory_address cannot be 0 Instead, use the remove version. - * @param factory_address The address of the cci which should be whitelisted. + * @dev factoryAddress cannot be 0 Instead, use the remove version. + * @param factoryAddress The address of the cci which should be whitelisted. * @param version The version which the new cci should be set to. */ - function modifyWhitelistedFactory(address factory_address, string calldata version) external onlyOwner { - if (factory_address == address(0)) revert ZeroAddress(); + function modifyWhitelistedFactory(address factoryAddress, string calldata version) external onlyOwner { + if (factoryAddress == address(0)) revert ZeroAddress(); // Update version table - uint256 indexOfVersion = _contains(version, factory_versions); - if (indexOfVersion == type(uint256).max) factory_versions.push(version); + uint256 indexOfVersion = _contains(version, factoryVersions); + if (indexOfVersion == type(uint256).max) factoryVersions.push(version); - version_to_factory[version] = factory_address; + versionToFactory[version] = factoryAddress; - emit ModifyWhitelistedFactory(factory_address, version); + emit ModifyWhitelistedFactory(factoryAddress, version); } - function removeWhitelistedFactory(address factory_to_remove, string calldata version) external onlyOwner { - address read_factory = version_to_factory[version]; - if (read_factory != factory_to_remove) revert IncorrectAddress(read_factory, factory_to_remove); + function removeWhitelistedFactory(address factoryToRemove, string calldata version) external onlyOwner { + address read_factory = versionToFactory[version]; + if (read_factory != factoryToRemove) revert IncorrectAddress(read_factory, factoryToRemove); - uint256 indexOfVersion = _contains(version, factory_versions); + uint256 indexOfVersion = _contains(version, factoryVersions); if (indexOfVersion == type(uint256).max) revert DoesNotExist(); - if (factory_versions.length > 1) { + if (factoryVersions.length > 1) { // swap the last element into this element's place. - factory_versions[indexOfVersion] = factory_versions[factory_versions.length - 1]; - factory_versions.pop(); + factoryVersions[indexOfVersion] = factoryVersions[factoryVersions.length - 1]; + factoryVersions.pop(); } - version_to_factory[version] = address(0); + versionToFactory[version] = address(0); // Emit event emit ModifyWhitelistedFactory(address(0), version); @@ -320,9 +254,9 @@ contract CatalystDescriber is Contains, Ownable { * @notice Returns a vault’s factory. * @dev This is fetched by asking the vault which factory deployed it, then checking with the factory. * Returns address(0) if the `address` lies. - * @param vault The address of the vault + * @param vault Address of the vault */ - function get_factory_of_vault(address vault) external view returns (address factory) { + function getFactoryOfVault(address vault) external view returns (address factory) { factory = ICatalystV1VaultImmutables(vault).FACTORY(); address cci = ICatalystV1VaultImmutables(vault)._chainInterface(); // Check if the factory agree @@ -333,9 +267,9 @@ contract CatalystDescriber is Contains, Ownable { * @notice Returns the vault's tokens * @dev Is found by iterating over _tokenIndexing until it returns address(0). Then resizing the array so it doesn't * contain address(0). - * @param vault The vault to get tokens of. + * @param vault Vault to get tokens of. */ - function get_vault_tokens(address vault) public view returns (address[] memory vaultTokens) { + function getVaultTokens(address vault) public view returns (address[] memory vaultTokens) { address[] memory tempVaultTokens = new address[](MAX_MEMORY_LIMIT); uint256 it; for (it = 0; true; ++it) { @@ -353,9 +287,9 @@ contract CatalystDescriber is Contains, Ownable { /** * @notice Returns a mathematical library which implements helpers for the contract. * @dev Queries the vault for the mathematical library. - * @param vault The vault to get the mathematical lib of. + * @param vault Vault to get the mathematical lib of. */ - function get_vault_mathematical_lib(address vault) public view returns (address math_lib) { + function getVaultMathematicalLib(address vault) public view returns (address math_lib) { math_lib = ICatalystV1VaultImmutables(vault).MATHLIB(); } @@ -364,14 +298,14 @@ contract CatalystDescriber is Contains, Ownable { * @dev To compute a token price, the ratio of 2 elements within the array has to be compared. * For example, to get the value of X token 1 in token 2, one should compute: quotes[1]/quptes[0]*X * This works cross-chain, where the output token should be numerator and input should be denominator. - * Is implemented through get_vault_tokens and get_vault_mathematical_lib. - * @param vault The valut to get a price list of. - * @return quotes A list of the price quotes for the tokens (get_vault_tokens). Is resized. + * Is implemented through getVaultTokens and get_vault_mathematical_lib. + * @param vault Valut to get a price list of. + * @return quotes A list of the price quotes for the tokens (getVaultTokens). Is resized. */ - function get_vault_prices(address vault) external view returns (uint256[] memory quotes) { - address[] memory tokens = get_vault_tokens(vault); + function getVaultPrices(address vault) external view returns (uint256[] memory quotes) { + address[] memory tokens = getVaultTokens(vault); quotes = new uint256[](tokens.length); - address math_lib = get_vault_mathematical_lib(vault); + address math_lib = getVaultMathematicalLib(vault); if (math_lib == address(0)) return quotes; for (uint256 it; it < tokens.length; ++it) { address token = tokens[it]; diff --git a/evm/src/registry/CatalystDescriberRegistry.sol b/evm/src/registry/CatalystDescriberRegistry.sol index 6458cd53..d4c8e9e5 100644 --- a/evm/src/registry/CatalystDescriberRegistry.sol +++ b/evm/src/registry/CatalystDescriberRegistry.sol @@ -6,8 +6,8 @@ import { Contains } from "./lib/Contains.sol"; /** * @title Catalyst: Describer Registry - * @author Catalyst Labs - * @notice This contract serves as an index of Catalyst Describer. + * @author Catalyst Labs Inc. + * @notice This contract serves as an index of Catalyst Describers. */ contract CatalystDescriberRegistry is Contains, Ownable { struct AddressAndVersion { @@ -15,7 +15,7 @@ contract CatalystDescriberRegistry is Contains, Ownable { string version; } - /// @notice Describes the catalyst version and the associated describer. + /** @notice Describes the catalyst version and the associated describer. */ event ModifyDescriber( address catalystDescriber, string version @@ -37,17 +37,6 @@ contract CatalystDescriberRegistry is Contains, Ownable { //--- Getters ---// - /** - * @notice Returns all describers. - */ - function get_vault_describers() public view returns (address[] memory catalystDescribers) { - catalystDescribers = new address[](describer_versions.length); - for (uint256 i = 0; i < describer_versions.length; ++i) { - string memory version = describer_versions[i]; - catalystDescribers[i] = version_to_describer[version]; - } - } - /** * @notice Return an array of describers along with their respective version. * @dev Will not contain address(0), the index in the array matches the index of describer_versions. diff --git a/evm/src/registry/CatalystMathAmp.sol b/evm/src/registry/CatalystMathAmp.sol index fd0b08b9..aeb16c5d 100644 --- a/evm/src/registry/CatalystMathAmp.sol +++ b/evm/src/registry/CatalystMathAmp.sol @@ -11,23 +11,26 @@ import "../IntegralsAmplified.sol"; /** * @title Catalyst: Amplified mathematics implementation - * @author Catalyst Labs + * @author Catalyst Labs Inc. * @notice This contract is not optimised for on-chain calls and serves to aid in off-chain quering. */ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { - // When the swap is a very small size of the vault, the swaps - // returns slightly more. To counteract this, an additional fee - // slightly larger than the error is added. The below constants - // determines when this fee is added and the size. + /** + * @dev When the swap is a very small size of the vault, the + * swaps returns slightly more. To counteract this, an additional + * fee slightly larger than the error is added. The below + * constants determines when this fee is added and the size. + */ uint256 constant public SMALL_SWAP_RATIO = 1e12; uint256 constant public SMALL_SWAP_RETURN = 95e16; /** * @notice Helper function which returns the true amplification. If amp is being adjusted, the pure vault amp might be inaccurate. - * @dev If amplification is being changed, the amplification read directly from the vaults are only updated when they are needed. (swaps, balance changes, etc) + * @dev This function is unused. + * If amplification is being changed, the amplification read directly from the vaults are only updated when they are needed. (swaps, balance changes, etc) * This function implements the amp change logic (almost exactly), such that one can read the amplifications if one were to execute a balance change. - * @param vault The address of the vault to fetch the amp for. + * @param vault Address of the vault to fetch the amp for. * @return uint256 Returns the (estimated) true amp. */ function getTrueAmp(address vault) public view returns(int256) { @@ -76,7 +79,7 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Helper function which returns the amount after fee. - * @dev The fee is taken from the input amount + * @dev The fee is taken from the input amount. * @param vault Vault to read vault fee. * @param amount Input swap amount * @return uint256 Input amount after vault fee. @@ -93,10 +96,10 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { * The value is returned as units, which is always WAD. * @dev All input amounts should be the raw numbers and not WAD. * Since units are always denominated in WAD, the function should be treated as mathematically *native*. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param W The weight of the x token. - * @param oneMinusAmp The amplification. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param W Weight of the x token. + * @param oneMinusAmp Amplification as (1-k) * @return uint256 Units (units are **always** WAD). */ function calcPriceCurveArea( @@ -119,8 +122,8 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { * Since units are always multiplied by WAD, the function * should be treated as mathematically *native*. * @param U Incoming vault specific units. - * @param B The current vault balance of the y token. - * @param W The weight of the y token. + * @param B Current vault balance of the y token. + * @param W Weight of the y token. * @return uint25 Output denominated in output token. (not WAD) */ function calcPriceCurveLimit( @@ -144,11 +147,11 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { * _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, amp), B, W_B, amp). * @dev All input amounts should be the raw numbers and not WAD. * @param input The input amount. - * @param A The current vault balance of the _in token. - * @param B The current vault balance of the _out token. - * @param W_A The vault weight of the _in token. - * @param W_B The vault weight of the _out token. - * @param oneMinusAmp The amplification. + * @param A Current vault balance of the _in token. + * @param B Current vault balance of the _out token. + * @param W_A Vault weight of the _in token. + * @param W_B Vault weight of the _out token. + * @param oneMinusAmp Amplification as (1-k). * @return uint256 Output denominated in output token. */ function calcCombinedPriceCurves( @@ -166,10 +169,10 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { * @notice Converts units into vault tokens with the below formula * pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1) * @dev The function leaves a lot of computation to the external implementation. This is done to avoid recomputing values several times. - * @param U Then number of units to convert into vault tokens. - * @param ts The current vault token supply. The escrowed vault tokens should not be added, since the function then returns more. + * @param U Number of units to convert into vault tokens. + * @param ts Current vault token supply. The escrowed vault tokens should not be added, since the function then returns more. * @param it_times_walpha_amped wa_0^(1-k) - * @param oneMinusAmpInverse The vault amplification. + * @param oneMinusAmpInverse Vault amplification as 1/(1-k) * @return uint256 Output denominated in vault tokens. */ function calcPriceCurveLimitShare(uint256 U, uint256 ts, uint256 it_times_walpha_amped, int256 oneMinusAmpInverse) external pure returns (uint256) { @@ -186,9 +189,9 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Computes the exchange of assets to units. This is the first part of a swap. * @dev Reverts if fromAsset is not in the vault. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param amount The amount of from token to sell. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param amount Amount of from token to sell. * @return uint256 Units. */ function calcSendAsset( @@ -217,8 +220,8 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Computes the exchange of units to assets. This is the second and last part of a swap. * @dev Reverts if toAsset is not in the vault. - * @param vault The vault address to examine. - * @param toAsset The address of the token to buy. + * @param vault Vault address to examine. + * @param toAsset Address of the token to buy. * @return uint256 tokens. */ function calcReceiveAsset( @@ -241,10 +244,10 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { * @notice Computes the output of localSwap. * @dev Reverts if either from or to is not in the vault, * or if the vault 'fromAsset' balance and 'amount' are both 0. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. - * @param amount The amount of from token to sell for to token. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. + * @param amount Amount of from token to sell for to token. * @return uint256 Output denominated in toAsset. */ function calcLocalSwap( @@ -280,8 +283,8 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Computes part of the mid price. calcCurrentPriceTo can be used to compute the pairwise price. * @dev Alternativly, dividing calcAsyncPriceFrom by another calcAsyncPriceFrom, results in the pairwise price. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. */ function calcAsyncPriceFrom( address vault, @@ -300,8 +303,8 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Computes a pairwise mid price. Requires input from calcAsyncPriceFrom. - * @param vault The vault address to examine. - * @param toAsset The address of the token to buy. + * @param vault Vault address to examine. + * @param toAsset Address of the token to buy. * @param calcAsyncPriceFromQuote The output of calcAsyncPriceFrom. * @return uint256 The pairwise mid price. */ @@ -325,10 +328,10 @@ contract CatalystMathAmp is IntegralsAmplified, ICatalystMathLibAmp { /** * @notice Computes the current mid price. This is the current marginal price between the 2 assets. - * @dev The mid price cannot be traded on, since the fees acts as the spread. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. + * @dev The mid price cannot be traded on, since the fees acts as spread. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. * @return uint256 Output denominated in toAsset. */ function calcCurrentPrice( diff --git a/evm/src/registry/CatalystMathVol.sol b/evm/src/registry/CatalystMathVol.sol index ba210124..7f806334 100644 --- a/evm/src/registry/CatalystMathVol.sol +++ b/evm/src/registry/CatalystMathVol.sol @@ -11,7 +11,7 @@ import "../IntegralsVolatile.sol"; /** * @title Catalyst: Volatile mathematics implementation - * @author Catalyst Labs + * @author Catalyst Labs Inc. * @notice This contract is not optimised for on-chain calls and serves to aid in off-chain quering. */ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { @@ -20,8 +20,8 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * @notice Helper function which returns the true weight. If weights are being adjusted, the pure vault weights might be inaccurate. * @dev If weights are being changed, the weights read directly from the vaults are only updated when they are needed. (swaps, balance changes, etc) * This function implements the weight change logic (almost exactly), such that one can read the weight if one were to execute a balance change. - * @param vault The address of the vault to fetch the weight for. - * @param asset The asset to get the weight for. + * @param vault Address of the vault to fetch the weight for. + * @param asset Asset to get the weight for. * @return uint256 Returns the (estimated) true weight. */ function getTrueWeight(address vault, address asset) public view returns(uint256) { @@ -58,7 +58,7 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { /** * @notice Helper function which returns the amount after fee. - * @dev The fee is taken from the input amount + * @dev Fee is taken from the input amount * @param vault Vault to read vault fee. * @param amount Input swap amount * @return uint256 Input amount after vault fee. @@ -74,9 +74,9 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * The value is returned as units, which is always WAD. * @dev All input amounts should be the raw numbers and not WAD. * Since units are always denominated in WAD, the function should be treated as mathematically *native*. - * @param input The input amount. - * @param A The current vault balance of the x token. - * @param W The weight of the x token. + * @param input Input amount. + * @param A Current vault balance of the x token. + * @param W Weight of the x token. * @return uint256 Units (units are **always** WAD). */ function calcPriceCurveArea( @@ -94,8 +94,8 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * Since units are always multiplied by WAD, the function * should be treated as mathematically *native*. * @param U Incoming vault specific units. - * @param B The current vault balance of the y token. - * @param W The weight of the y token. + * @param B Current vault balance of the y token. + * @param W Weight of the y token. * @return uint25 Output denominated in output token. (not WAD) */ function calcPriceCurveLimit( @@ -111,10 +111,10 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * \int_{A}^{A+x} W_a/w dw = \int_{B-y}^{B} W_b/w dw for y = B · (1 - ((A+x)/A)^(-W_a/W_b)) * @dev All input amounts should be the raw numbers and not WAD. Is computed through calcPriceCurveLimit(calcPriceCurveArea). * @param input The input amount. - * @param A The current vault balance of the x token. - * @param B The current vault balance of the y token. - * @param W_A The weight of the x token. - * @param W_B TThe weight of the y token. + * @param A Current vault balance of the x token. + * @param B Current vault balance of the y token. + * @param W_A Weight of the x token. + * @param W_B Weight of the y token. * @return uint256 Output denominated in output token. */ function calcCombinedPriceCurves( @@ -132,7 +132,7 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * @dev Based on _calcPriceCurveLimit but the multiplication by the * specific token is never done. * @param U Input units. - * @param W The generalised weights. + * @param W Generalised weights. * @return uint256 Output denominated in vault share. */ function calcPriceCurveLimitShare( @@ -166,9 +166,9 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * @dev Returns 0 if from is not a token in the vault * Should also be exposed from any vault. For on-chain calls, it is cheaper to call this function rather than the vault. * However, it is not optimised for on-chain calls. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param amount The amount of from token to sell. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param amount Amount of from token to sell. * @return uint256 Units. */ function calcSendAsset( @@ -189,9 +189,9 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * @notice Computes the exchange of units to assets. This is the second and last part of a swap. * @dev Reverts if to is not a token in the vault. For on-chain calls, it is cheaper to call this function rather than the vault. * However, it is not optimised for on-chain calls. - * @param vault The vault address to examine. - * @param toAsset The address of the token to buy. - * @param U The number of units used to buy to. + * @param vault Vault address to examine. + * @param toAsset Address of the token to buy. + * @param U Number of units used to buy to. * @return uint256 Number of purchased tokens. */ function calcReceiveAsset( @@ -217,10 +217,10 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { * If both from and to are not part of the vault, the swap can actually return a positive value. * For on-chain calls, it is cheaper to call this function rather than the vault. * However, it is not optimised for on-chain calls. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. - * @param amount The amount of from token to sell for to token. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. + * @param amount Amount of from token to sell for to token. * @return uint256 Output denominated in toAsset. */ function calcLocalSwap( @@ -257,8 +257,8 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { /** * @notice Computes part of the mid price. calcCurrentPriceTo can be used to compute the pairwise price. * @dev Alternativly, dividing calcAsyncPriceFrom by another calcAsyncPriceFrom, results in the pairwise price. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. */ function calcAsyncPriceFrom( address vault, @@ -273,10 +273,10 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { /** * @notice Computes a pairwise mid price. Requires input from calcAsyncPriceFrom. - * @param vault The vault address to examine. - * @param toAsset The address of the token to buy. + * @param vault Vault address to examine. + * @param toAsset Address of the token to buy. * @param calcAsyncPriceFromQuote The output of calcAsyncPriceFrom. - * @return uint256 The pairwise mid price. + * @return uint256 Pairwise mid price. */ function calcCurrentPriceTo( address vault, @@ -293,9 +293,9 @@ contract CatalystMathVol is IntegralsVolatile, ICatalystMathLibVol { /** * @notice Computes the current mid price. This is the current marginal price between the 2 assets. * @dev The mid price cannot be traded on, since the fees acts as the spread. - * @param vault The vault address to examine. - * @param fromAsset The address of the token to sell. - * @param toAsset The address of the token to buy. + * @param vault Vault address to examine. + * @param fromAsset Address of the token to sell. + * @param toAsset Address of the token to buy. * @return uint256 Output denominated in toAsset. */ function calcCurrentPrice( diff --git a/evm/src/registry/interfaces/ICatalystDescriber.sol b/evm/src/registry/interfaces/ICatalystDescriber.sol index 4daae3f0..f2ae897f 100644 --- a/evm/src/registry/interfaces/ICatalystDescriber.sol +++ b/evm/src/registry/interfaces/ICatalystDescriber.sol @@ -3,34 +3,40 @@ pragma solidity ^0.8.17; interface ICatalystDescriber { - // Returns an array of whitelisted vault templates. - function get_whitelisted_templates() external view returns (address[] memory whitelistedTemplates); - - // Returns an array of whitelisted CCIs. - function get_whitelisted_CCI() external view returns (address[] memory whitelistedCCI); - - // Returns an array of vault factories. - function get_vault_factories() external view returns (address[] memory vaultFactories); - - // Returns a vault’s factory. - // This is fetched by asking the vault which factory deployed it, then checking with the factory. - // Returns address(0) if the `address` lies. - function get_vault_factory(address vault) external view returns (address factory); - - // Returns the code hash of the address. - function get_vault_type(address vault) external view returns (bytes32); - - // Returns the abi_version using a whitelisted table. Returns -1 if the address is not known - function get_vault_abi_version(address) external view returns (int256 abi_version); - - // Returns the vault tokens supported by a vault by iterating over _tokenIndexing until it returns 0 - function get_vault_tokens(address vault) external view returns (address[] memory vaultTokens); - - // Returns an address which implements and exposes the vault’s mathematical methods. - // Uses get_vault_type to find a mathematical lib. Returns address(0) if no mathematical lib is set. (whitelist) - function get_vault_mathematical_lib(address) external view returns (address); - - // Returns a list of token prices. The first element is always a reference balance. (what is “1”) - // Requires get_vault_mathematical_lib() ≠ address(0) - function get_vault_prices(address) external view returns (uint256[] memory); + /** @notice Returns an array of whitelisted vault templates. */ + function getWhitelistedTemplates() external view returns (address[] memory whitelistedTemplates); + + /** @notice Returns an array of whitelisted CCIs. */ + function getWhitelistedCCI() external view returns (address[] memory whitelistedCCI); + + /** @notice Returns an array of vault factories. */ + function getVaultFactories() external view returns (address[] memory vaultFactories); + + /** + * @notice Returns a vault's factory. + * This is fetched by asking the vault which factory deployed it, then checking with the factory. + * Returns address(0) if the `address` lies. + */ + function getFactoryOfVault(address vault) external view returns (address factory); + + /** @notice Returns the code hash of the address. */ + function getVaultType(address vault) external view returns (bytes32); + + /** @notice Returns the abi_version using a whitelisted table. Returns -1 if the address is not known */ + function getVaultABIVersion(address) external view returns (int256 abi_version); + + /** @notice Returns the vault tokens supported by a vault by iterating over _tokenIndexing until it returns 0 */ + function getVaultTokens(address vault) external view returns (address[] memory vaultTokens); + + /** + * @notice Returns an address which implements and exposes the vault’s mathematical methods. + * Uses getVaultType to find a mathematical lib. Returns address(0) if no mathematical lib is set. (whitelist) + */ + function getVaultMathematicalLib(address) external view returns (address); + + /** + * Returns a list of token prices. The first element is always a reference balance. (what is “1”) + * Requires getVaultMathematicalLib() ≠ address(0) + */ + function getVaultPrices(address) external view returns (uint256[] memory); } \ No newline at end of file diff --git a/evm/src/registry/lib/Contains.sol b/evm/src/registry/lib/Contains.sol index 66a214da..8447f55f 100644 --- a/evm/src/registry/lib/Contains.sol +++ b/evm/src/registry/lib/Contains.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; /** * @title Catalyst: Catalyst Describer - * @author Catalyst Labs + * @author Catalyst Labs Inc. * @notice This contract describes the a Catalyst implementation and serves to simplify off-chain queries. * As a result, the contract is not optimised for on-chain queries but rather easy of use off-chain. */ From c309199852319f7596d69f68afff1bb68358f6d5 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 15:00:28 +0200 Subject: [PATCH 14/22] feat: new salts --- evm/.env.example | 2 +- evm/lib/GeneralisedIncentives | 2 +- evm/script/DeployContracts.s.sol | 10 +++++----- evm/script/TimeLock.s.sol | 3 ++- evm/script/config/config_contracts.json | 16 ++++++++-------- evm/script/config/config_interfaces.json | 24 ++++++++++++++---------- 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/evm/.env.example b/evm/.env.example index a4f6f517..40728a5a 100644 --- a/evm/.env.example +++ b/evm/.env.example @@ -1,4 +1,4 @@ -CATALYST_ADDRESS="0x0000007aAAC54131e031b3C0D6557723f9365A5B" +CATALYST_ADDRESS="0x00000000042A0e90f74f036587d7912EA32EF5cC" ROUTER_DEPLOYER= DEPLOYER_PK= diff --git a/evm/lib/GeneralisedIncentives b/evm/lib/GeneralisedIncentives index ac8754bf..8ede6a34 160000 --- a/evm/lib/GeneralisedIncentives +++ b/evm/lib/GeneralisedIncentives @@ -1 +1 @@ -Subproject commit ac8754bf028560885d8dff37101f2bca2cf11570 +Subproject commit 8ede6a3427a706cfafbfc51c576ba1690a3e6dda diff --git a/evm/script/DeployContracts.s.sol b/evm/script/DeployContracts.s.sol index 5062ff19..2eaab8ae 100644 --- a/evm/script/DeployContracts.s.sol +++ b/evm/script/DeployContracts.s.sol @@ -98,13 +98,13 @@ contract DeployContracts is Script { admin = admin_; - deployFactory(bytes32(uint256(251))); + deployFactory(bytes32(0xe810ad7329bc18a3661da69243c260bddf93c02cb86f51d82cef8f77664b1dc1)); - deploy_volatile_mathlib(bytes32(uint256(251))); - deploy_amplified_mathlib(bytes32(uint256(251))); + deploy_volatile_mathlib(bytes32(0xeadb0c4c623a1504e49ea876afaf2e1a24a663e81e05ac04fc82ab1a53974533)); + deploy_amplified_mathlib(bytes32(0x178f2263b6b48c743c92919210db1f9ffdd16b679f9a49a60db831ee745c1b53)); - deploy_volatile_template(bytes32(uint256(251))); - deploy_amplified_template(bytes32(uint256(251))); + deploy_volatile_template(bytes32(0xab571b0631b64d37caa8e7d85912d505bf434d66e2b9df4088447821dac531d8)); + deploy_amplified_template(bytes32(0x4aa36fb7e5e332061b6ad20a3b0de53dd4cf41391c9bf553367ba5165b60c5cd)); } } diff --git a/evm/script/TimeLock.s.sol b/evm/script/TimeLock.s.sol index dc63f137..34abbe97 100644 --- a/evm/script/TimeLock.s.sol +++ b/evm/script/TimeLock.s.sol @@ -12,6 +12,7 @@ contract DeployTimelock is BaseMultiChainDeployer { uint256 MIN_DELAY = 1 days; function _deployTimelockController(bytes32 salt, uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) internal returns(TimelockController) { + vm.broadcast(); return new TimelockController{salt: salt}(minDelay, proposers, executors, admin); } @@ -28,7 +29,7 @@ contract DeployTimelock is BaseMultiChainDeployer { function run() public { bytes32 salt = bytes32(0x67e6613ce8cbbe2cbdf83a49a42f7506ba4f34ef5ce61e83c3935e85cac6a4a6); address initalProposer = address(0xE759cBa7dE5bF6E024BcbdD01941fc3b1713D2FC); - deployTimelockController(salt, initalProposer); + deployTimelockController(salt, initalProposer); // 0x00000000042A0e90f74f036587d7912EA32EF5cC } } diff --git a/evm/script/config/config_contracts.json b/evm/script/config/config_contracts.json index af13195a..4f3f03c6 100644 --- a/evm/script/config/config_contracts.json +++ b/evm/script/config/config_contracts.json @@ -1,14 +1,14 @@ { "contracts": { - "amplified_mathlib": "0xBB5a61795DF139d79818A34B350e64DAF5ABA263", - "amplified_template": "0x227eDab609f7369291290e4f8bfB1Ab5ab09C4BD", - "factory": "0xa957Ab873c06933b389D32a58131f13e60E488c5", - "volatile_mathlib": "0x8a550acf727f484d93B1b18A6Fc87cE9081cE260", - "volatile_template": "0x6E288f366141CbbabeEB8F3d8efc2eE9D28c1fF2" + "amplified_mathlib": "0x0000005243d4f6c3123F922eb3A1B832E171F972", + "amplified_template": "0x00000000b02189eA1fB645bD0127f23B65aE8D1E", + "factory": "0x00000000bA0074B396954A4d17293BFcD818215C", + "volatile_mathlib": "0x0000007A30bD219B5B7FDcaA8a47784F301505C4", + "volatile_template": "0x0000000004B96454e2d7890E6dd8448E511250b1" }, "registry": { - "describer": "0x5514d9b55CdCbA70A6aF19Ca1E3443b1abEa104A", - "describer_registry": "0x56dCAa213D0da8dC0eb21ac71fB63EF28D497fd8", - "lens": "0x7E511493d23c0ad9dc1a6137D24BE5aa273773eE" + "describer": "0x02C76C5E0D6F84CEdcDA11c20E982A1939d1de3E", + "describer_registry": "0xe4D78617900e4c2241ccA45FFe0aA1F3A37B5164", + "lens": "0xAfdF93959eeE61Fc7AB577d3D26e840FA21CD7c9" } } \ No newline at end of file diff --git a/evm/script/config/config_interfaces.json b/evm/script/config/config_interfaces.json index cfd17dc7..7e62e878 100644 --- a/evm/script/config/config_interfaces.json +++ b/evm/script/config/config_interfaces.json @@ -1,24 +1,28 @@ { "Wormhole": { "mumbai": { - "interface": "0x74e46a8113F2E06db0e07D592F6Cf5B1A59756EE", - "escrow": "0xFb4B1475e61642b3930d762B66d9DD4E08B14367" + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" }, "sepolia": { - "interface": "0x169e7E77E463FfE86b30Afd1605A09A632BeA5B0", - "escrow": "0x45C140Dd2526E4bfD1c2A5Bb0Aa6aA1DB00b1744" + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" }, "basesepolia": { - "interface": "0xa71ae9298Ba6DD5f2Aa96415bC1eC33e62980751", - "escrow": "0xEA2F1d38e31fBE501c0b42BA25E217a47132D8fa" + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" }, "arbitrumsepolia": { - "interface": "0xf779D8e1B07F1e3dF141A81c4f1f7D65c0A38611", - "escrow": "0xdF25f1BdE09Cee5ac1e6ef8dFA7113addBd58B28" + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" }, "optimismsepolia": { - "interface": "0x2603e874D589373A0c4766808B93e3D4b63164C8", - "escrow": "0xbDFD9163d8Cee1368698B023369f9A5Fd319A40F" + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" + }, + "blasttestnet": { + "interface": "0x0000000000000000000000000000000000000000", + "escrow": "0x0000000000000000000000000000000000000000" } }, "Polymer": { From 699c919d78d8ec6e35481af57fbb13661bfba0ef Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 15:17:41 +0200 Subject: [PATCH 15/22] test: set correct deployer --- evm/test/TestDeployAddresses.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/test/TestDeployAddresses.t.sol b/evm/test/TestDeployAddresses.t.sol index d5ae70b3..01950aa2 100644 --- a/evm/test/TestDeployAddresses.t.sol +++ b/evm/test/TestDeployAddresses.t.sol @@ -26,7 +26,7 @@ contract TestDeployedCorrectAddresses is Test, DeployContracts { verify = false; vm.startBroadcast(123); - deployAllContracts(0x0000007aAAC54131e031b3C0D6557723f9365A5B); + deployAllContracts(0x00000000042A0e90f74f036587d7912EA32EF5cC); vm.stopBroadcast(); From 408b3aea9a8a4b8ce69a0f65b2a1839a9b7f08e8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 15:18:07 +0200 Subject: [PATCH 16/22] feat: deploy interfaces --- evm/script/config/config_interfaces.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/evm/script/config/config_interfaces.json b/evm/script/config/config_interfaces.json index 7e62e878..5a9121a3 100644 --- a/evm/script/config/config_interfaces.json +++ b/evm/script/config/config_interfaces.json @@ -5,12 +5,12 @@ "escrow": "0x0000000000000000000000000000000000000000" }, "sepolia": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" + "interface": "0xB61759BD2De4b9D08FBd589848c5DC57aef0B608", + "escrow": "0x294F41D30D058C9e5A71810A6C758E595b7aC170" }, "basesepolia": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" + "interface": "0x9E2FDcf89e6A2536F4d7DEE99430d582a263441a", + "escrow": "0x63B4E24DC9814fAcDe241fB4dEFcA04d5fc6d763" }, "arbitrumsepolia": { "interface": "0x0000000000000000000000000000000000000000", @@ -21,8 +21,8 @@ "escrow": "0x0000000000000000000000000000000000000000" }, "blasttestnet": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" + "interface": "0xE4F88fa3b7D5B54A2B7FA1B9DBffBb68c942b365", + "escrow": "0x9524ACA1fF46fAd177160F0a803189Cb552A3780" } }, "Polymer": { From d8d8804ded49830f89e41ac06df1c40795851a35 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 16:56:26 +0200 Subject: [PATCH 17/22] feat: update channel list --- evm/lib/catalyst-channel-lists | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/lib/catalyst-channel-lists b/evm/lib/catalyst-channel-lists index 6a73865a..80e33af3 160000 --- a/evm/lib/catalyst-channel-lists +++ b/evm/lib/catalyst-channel-lists @@ -1 +1 @@ -Subproject commit 6a73865ab54cf10dafbff56a1b4cda4f98a60a90 +Subproject commit 80e33af39af4d6ec8aec33980b2e3fecb4b31cd9 From a051f8cb28c7717ca76853aac256d51bc25bafe7 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 16:56:39 +0200 Subject: [PATCH 18/22] feat: correctly set the deployer admin --- evm/script/DeployInterfaces.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/script/DeployInterfaces.s.sol b/evm/script/DeployInterfaces.s.sol index 88469ff5..08d5f0c2 100644 --- a/evm/script/DeployInterfaces.s.sol +++ b/evm/script/DeployInterfaces.s.sol @@ -131,7 +131,7 @@ contract DeployInterfaces is MultiChainDeployer { function _deploy(string[] memory bridges) internal { - admin = vm.envAddress("CATALYST_ADDRESS"); + admin = vm.addr(pk); deployCCI(bridges); } From 9effe5f865a3f6272a52b43b9dc245c9b20386d3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 17:40:41 +0200 Subject: [PATCH 19/22] feat: fix incorrect deployment --- evm/.env.example | 2 +- evm/script/DeployContracts.s.sol | 10 +++++----- evm/script/TimeLock.s.sol | 4 ++-- evm/script/config/config_contracts.json | 10 +++++----- evm/test/TestDeployAddresses.t.sol | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/evm/.env.example b/evm/.env.example index 40728a5a..2e810336 100644 --- a/evm/.env.example +++ b/evm/.env.example @@ -1,4 +1,4 @@ -CATALYST_ADDRESS="0x00000000042A0e90f74f036587d7912EA32EF5cC" +CATALYST_ADDRESS="0x0000000099263f0735D03bB2787cE8FB84f6ED6E" ROUTER_DEPLOYER= DEPLOYER_PK= diff --git a/evm/script/DeployContracts.s.sol b/evm/script/DeployContracts.s.sol index 2eaab8ae..a5b13936 100644 --- a/evm/script/DeployContracts.s.sol +++ b/evm/script/DeployContracts.s.sol @@ -98,13 +98,13 @@ contract DeployContracts is Script { admin = admin_; - deployFactory(bytes32(0xe810ad7329bc18a3661da69243c260bddf93c02cb86f51d82cef8f77664b1dc1)); + deployFactory(bytes32(0x05624c1ea3c3ccda5de1f660f08f273485a6705c2a16ba15faa6dc4bc6ab08e7)); - deploy_volatile_mathlib(bytes32(0xeadb0c4c623a1504e49ea876afaf2e1a24a663e81e05ac04fc82ab1a53974533)); - deploy_amplified_mathlib(bytes32(0x178f2263b6b48c743c92919210db1f9ffdd16b679f9a49a60db831ee745c1b53)); + deploy_volatile_mathlib(bytes32(0xe5a3f4676abc23027a0cd4359d4c97e42f4220eafcab86d68659524867949a45)); + deploy_amplified_mathlib(bytes32(0xb6b5fa2553b5e78058a72aa1df20ead9538ff71011621b994ed3114a4e573361)); - deploy_volatile_template(bytes32(0xab571b0631b64d37caa8e7d85912d505bf434d66e2b9df4088447821dac531d8)); - deploy_amplified_template(bytes32(0x4aa36fb7e5e332061b6ad20a3b0de53dd4cf41391c9bf553367ba5165b60c5cd)); + deploy_volatile_template(bytes32(0x8add0c36c676f99a85b05f50ebf5306c7e0ca540a6985e18a339013c1f1dbc38)); + deploy_amplified_template(bytes32(0xe902feb8d6b508a0bf6dabc08b2fd6a8ea2e64253f957e95108794a339201fbd)); } } diff --git a/evm/script/TimeLock.s.sol b/evm/script/TimeLock.s.sol index 34abbe97..2223256b 100644 --- a/evm/script/TimeLock.s.sol +++ b/evm/script/TimeLock.s.sol @@ -27,9 +27,9 @@ contract DeployTimelock is BaseMultiChainDeployer { } function run() public { - bytes32 salt = bytes32(0x67e6613ce8cbbe2cbdf83a49a42f7506ba4f34ef5ce61e83c3935e85cac6a4a6); + bytes32 salt = bytes32(0xe28b85a3a6cd3c7b941c273688ed961305638df07108218fc6e68bcc364e7f37); address initalProposer = address(0xE759cBa7dE5bF6E024BcbdD01941fc3b1713D2FC); - deployTimelockController(salt, initalProposer); // 0x00000000042A0e90f74f036587d7912EA32EF5cC + deployTimelockController(salt, initalProposer); // 0x0000000099263f0735D03bB2787cE8FB84f6ED6E } } diff --git a/evm/script/config/config_contracts.json b/evm/script/config/config_contracts.json index 4f3f03c6..6b66c2d2 100644 --- a/evm/script/config/config_contracts.json +++ b/evm/script/config/config_contracts.json @@ -1,10 +1,10 @@ { "contracts": { - "amplified_mathlib": "0x0000005243d4f6c3123F922eb3A1B832E171F972", - "amplified_template": "0x00000000b02189eA1fB645bD0127f23B65aE8D1E", - "factory": "0x00000000bA0074B396954A4d17293BFcD818215C", - "volatile_mathlib": "0x0000007A30bD219B5B7FDcaA8a47784F301505C4", - "volatile_template": "0x0000000004B96454e2d7890E6dd8448E511250b1" + "amplified_mathlib": "0x000000575b0D9cc6ddbd8990db4d845fe480281f", + "amplified_template": "0x000000004aBe0D620b25b8B06B0712BDcff21899", + "factory": "0x00000000E5E81E25aeaD7fCCb4C9560C6b5b718F", + "volatile_mathlib": "0x000000992f6Bd813E0De5C595099C44d67ea28eD", + "volatile_template": "0x0000000003b8C9BFeB9351933CFC301Eea92073F" }, "registry": { "describer": "0x02C76C5E0D6F84CEdcDA11c20E982A1939d1de3E", diff --git a/evm/test/TestDeployAddresses.t.sol b/evm/test/TestDeployAddresses.t.sol index 01950aa2..6033902e 100644 --- a/evm/test/TestDeployAddresses.t.sol +++ b/evm/test/TestDeployAddresses.t.sol @@ -26,7 +26,7 @@ contract TestDeployedCorrectAddresses is Test, DeployContracts { verify = false; vm.startBroadcast(123); - deployAllContracts(0x00000000042A0e90f74f036587d7912EA32EF5cC); + deployAllContracts(0x0000000099263f0735D03bB2787cE8FB84f6ED6E); vm.stopBroadcast(); From 89eecdc465ee6e072f5cc64323c2f9ae17087841 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 18:49:02 +0200 Subject: [PATCH 20/22] feat: blast deployment --- evm/script/Registry.s.sol | 8 ++++---- evm/script/config/config_contracts.json | 6 +++--- evm/script/config/config_interfaces.json | 18 +++--------------- evm/script/config/config_tokens.json | 3 +++ 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/evm/script/Registry.s.sol b/evm/script/Registry.s.sol index 284c0242..b683836d 100644 --- a/evm/script/Registry.s.sol +++ b/evm/script/Registry.s.sol @@ -70,25 +70,25 @@ contract Registry is BaseMultiChainDeployer { function deploy(string[] memory chains) iter_chains_string(chains) broadcast external { verify = true; - admin = vm.envAddress("CATALYST_ADDRESS"); + admin = vm.addr(pk); _deploy(); } function deployAll() iter_chains(chain_list) broadcast external { verify = true; - admin = vm.envAddress("CATALYST_ADDRESS"); + admin = vm.addr(pk); _deploy(); } function deployAllLegacy() iter_chains(chain_list_legacy) broadcast external { verify = true; - admin = vm.envAddress("CATALYST_ADDRESS"); + admin = vm.addr(pk); _deploy(); } function getAddresses() external { get = true; - admin = vm.envAddress("CATALYST_ADDRESS"); + admin = vm.addr(vm.envUint("DEPLOYER_PK")); vm.startBroadcast(uint256(1)); diff --git a/evm/script/config/config_contracts.json b/evm/script/config/config_contracts.json index 6b66c2d2..9b33f375 100644 --- a/evm/script/config/config_contracts.json +++ b/evm/script/config/config_contracts.json @@ -7,8 +7,8 @@ "volatile_template": "0x0000000003b8C9BFeB9351933CFC301Eea92073F" }, "registry": { - "describer": "0x02C76C5E0D6F84CEdcDA11c20E982A1939d1de3E", - "describer_registry": "0xe4D78617900e4c2241ccA45FFe0aA1F3A37B5164", - "lens": "0xAfdF93959eeE61Fc7AB577d3D26e840FA21CD7c9" + "describer": "0x3F578971672d5469D28B39ec2b367CbeBF238a11", + "describer_registry": "0x39AcfC1Edd61Eb08fB96523A3b5419B83A603820", + "lens": "0x7363003E709EE4Ce16c32D2DEE8B7616d91D51d5" } } \ No newline at end of file diff --git a/evm/script/config/config_interfaces.json b/evm/script/config/config_interfaces.json index 5a9121a3..4bec86e3 100644 --- a/evm/script/config/config_interfaces.json +++ b/evm/script/config/config_interfaces.json @@ -1,27 +1,15 @@ { "Wormhole": { - "mumbai": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" - }, "sepolia": { - "interface": "0xB61759BD2De4b9D08FBd589848c5DC57aef0B608", + "interface": "0xEF04d76391C4b6a0c8BB5262f0f2214c261C4AD2", "escrow": "0x294F41D30D058C9e5A71810A6C758E595b7aC170" }, "basesepolia": { - "interface": "0x9E2FDcf89e6A2536F4d7DEE99430d582a263441a", + "interface": "0xC7D9815055Ea739B78E2D10D0055d97010396b78", "escrow": "0x63B4E24DC9814fAcDe241fB4dEFcA04d5fc6d763" }, - "arbitrumsepolia": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" - }, - "optimismsepolia": { - "interface": "0x0000000000000000000000000000000000000000", - "escrow": "0x0000000000000000000000000000000000000000" - }, "blasttestnet": { - "interface": "0xE4F88fa3b7D5B54A2B7FA1B9DBffBb68c942b365", + "interface": "0x39629dDb16a11E11fB62Bf46f34F63fF1c42B090", "escrow": "0x9524ACA1fF46fAd177160F0a803189Cb552A3780" } }, diff --git a/evm/script/config/config_tokens.json b/evm/script/config/config_tokens.json index d1c0c326..649db42f 100644 --- a/evm/script/config/config_tokens.json +++ b/evm/script/config/config_tokens.json @@ -13,5 +13,8 @@ }, "optimismsepolia": { "WETH": "0x1BDD24840e119DC2602dCC587Dd182812427A5Cc" + }, + "blasttestnet": { + "WETH": "0x4300000000000000000000000000000000000004" } } \ No newline at end of file From 4a1275fe336984f15a949ed90c8ec7888857fe08 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 20:25:34 +0200 Subject: [PATCH 21/22] feat: optimism deployment --- evm/script/config/config_interfaces.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm/script/config/config_interfaces.json b/evm/script/config/config_interfaces.json index 4bec86e3..fe8ccd0b 100644 --- a/evm/script/config/config_interfaces.json +++ b/evm/script/config/config_interfaces.json @@ -4,6 +4,10 @@ "interface": "0xEF04d76391C4b6a0c8BB5262f0f2214c261C4AD2", "escrow": "0x294F41D30D058C9e5A71810A6C758E595b7aC170" }, + "optimismsepolia": { + "interface": "0xdB321B1E304eA155B1977768904De998818F986E", + "escrow": "0x198cDD55d90277726f3222D5A8111AdB8b0af9ee" + }, "basesepolia": { "interface": "0xC7D9815055Ea739B78E2D10D0055d97010396b78", "escrow": "0x63B4E24DC9814fAcDe241fB4dEFcA04d5fc6d763" From 22bc4b7895d192a203e1aabb53fe6071f4750d7e Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 17 May 2024 22:43:44 +0200 Subject: [PATCH 22/22] feat: configure blast deployment --- evm/lib/GeneralisedIncentives | 2 +- evm/script/BaseMultiChainDeployer.s.sol | 4 +-- evm/script/DeployRouter.s.sol | 2 +- evm/script/config/config_tokens.json | 2 +- evm/script/config/config_vaults.json | 44 ++++++------------------- 5 files changed, 15 insertions(+), 39 deletions(-) diff --git a/evm/lib/GeneralisedIncentives b/evm/lib/GeneralisedIncentives index 8ede6a34..f9de918e 160000 --- a/evm/lib/GeneralisedIncentives +++ b/evm/lib/GeneralisedIncentives @@ -1 +1 @@ -Subproject commit 8ede6a3427a706cfafbfc51c576ba1690a3e6dda +Subproject commit f9de918e6cb58ee2db73755b4371fc0c9d00e3c5 diff --git a/evm/script/BaseMultiChainDeployer.s.sol b/evm/script/BaseMultiChainDeployer.s.sol index b65f27c7..f3073e02 100644 --- a/evm/script/BaseMultiChainDeployer.s.sol +++ b/evm/script/BaseMultiChainDeployer.s.sol @@ -11,8 +11,6 @@ contract MultiChainDeployer is BaseMultiChainDeployer { mapping(string => string) wrappedGas; constructor() BaseMultiChainDeployer() { - wrappedGas[chainKey[Chains.Mumbai]] = "WMATIC"; - wrappedGas[chainKey[Chains.Sepolia]] = "WETH10"; wrappedGas[chainKey[Chains.BaseSepolia]] = "WETH"; @@ -20,6 +18,8 @@ contract MultiChainDeployer is BaseMultiChainDeployer { wrappedGas[chainKey[Chains.ArbitrumSepolia]] = "WETH"; wrappedGas[chainKey[Chains.OptimismSepolia]] = "WETH"; + + wrappedGas[chainKey[Chains.BlastTestnet]] = "WETH"; } } diff --git a/evm/script/DeployRouter.s.sol b/evm/script/DeployRouter.s.sol index a3d728c4..c5102bcd 100644 --- a/evm/script/DeployRouter.s.sol +++ b/evm/script/DeployRouter.s.sol @@ -12,7 +12,7 @@ import { RouterParameters } from "../src/router/base/RouterImmutables.sol"; contract DeployRouter is MultiChainDeployer { using stdJson for string; - address expectedRouterAddress = address(0x0000009586123fBAAe1929B0768feb8BDBB0145d); + address expectedRouterAddress = address(0x00000029e6005863Bb2E1686a17C4ae0D1723669); address expectedPermit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3); string config_token; diff --git a/evm/script/config/config_tokens.json b/evm/script/config/config_tokens.json index 649db42f..c6a3a661 100644 --- a/evm/script/config/config_tokens.json +++ b/evm/script/config/config_tokens.json @@ -15,6 +15,6 @@ "WETH": "0x1BDD24840e119DC2602dCC587Dd182812427A5Cc" }, "blasttestnet": { - "WETH": "0x4300000000000000000000000000000000000004" + "WETH": "0x4200000000000000000000000000000000000006" } } \ No newline at end of file diff --git a/evm/script/config/config_vaults.json b/evm/script/config/config_vaults.json index 63400949..ebe5b1c4 100644 --- a/evm/script/config/config_vaults.json +++ b/evm/script/config/config_vaults.json @@ -1,20 +1,10 @@ { - "WormholeSepoliaOptimismArbitrum": { + "WormholeOptimismBaseBlast": { "cci_version": "Wormhole", - "sepolia": { - "cci": "0x169e7e77e463ffe86b30afd1605a09a632bea5b0", - "address": "0xbd529ff730dae029fa85b38d98d6046721215fa4", - "weights": [ - 1 - ], - "fee": 500000000000000, - "tokens": { - "WGAS": 10000000000000000 - } - }, + "amplification": 250000000000000000, "optimismsepolia": { - "cci": "0x2603e874d589373a0c4766808b93e3d4b63164c8", - "address": "0xbd529ff730dae029fa85b38d98d6046721215fa4", + "cci": "0xdb321b1e304ea155b1977768904de998818f986e", + "address": "0x06d4b5289b981933e34af10817f352061bad6353", "weights": [ 1 ], @@ -23,23 +13,9 @@ "WGAS": 10000000000000000 } }, - "arbitrumsepolia": { - "cci": "0xf779d8e1b07f1e3df141a81c4f1f7d65c0a38611", - "address": "0xbd529ff730dae029fa85b38d98d6046721215fa4", - "weights": [ - 1 - ], - "fee": 500000000000000, - "tokens": { - "WGAS": 10000000000000000 - } - } - }, - "PolymerOPBASEsepolia": { - "cci_version": "Polymer", - "optimismsepolia": { - "cci": "0x622ebf7b017b42cec6e30f47b4895f198b9a7364", - "address": "0x52ddaf2596c653bd6e0422336fe4a8dfb3418141", + "basesepolia": { + "cci": "0xc7d9815055ea739b78e2d10d0055d97010396b78", + "address": "0x06d4b5289b981933e34af10817f352061bad6353", "weights": [ 1 ], @@ -48,9 +24,9 @@ "WGAS": 10000000000000000 } }, - "basesepolia": { - "cci": "0x7d2193429c1ae9ba1b36ffb8d5ee467b4ce0efc3", - "address": "0xcdfef148155ee1d5d908d4d7030de865e5720d1f", + "blasttestnet": { + "cci": "0x39629ddb16a11e11fb62bf46f34f63ff1c42b090", + "address": "0x06d4b5289b981933e34af10817f352061bad6353", "weights": [ 1 ],