This repository contains the core protocol smart contracts of Superform.
See the official Foundry installation instructions.
Then, install the foundry toolchain installer (foundryup) with:
curl -L https://foundry.paradigm.xyz | bashNow that you've installed the foundryup binary,
anytime you need to get the latest forge or cast binaries,
you can run foundryup.
So, simply execute:
foundryup🎉 Foundry is installed! 🎉
Note: you should also install pre-commit and Foundry Pre-Commit Hook to format before commiting. Check https://github.com/0xYYY/foundry-pre-commit for more info
.
├── script
├── src
├── crosschain-data
├── crosschain-liquidity
├── forms
├── interfaces
├── libraries
├── payments
├── settings
├── types
├── utils
├── vendor
├── test
├── foundry.toml
└── README.md
It makes our project structure easily scannable:
srcare self-explanatory. All Smart Contract Codes are found inside /srcinterfacesare where we add our custom written interfaces /src/interfaces.typesis where all re-used types are written and used across different smart contracts. /src/typesvendoris where all externally written interfaces reside. /src/vendor
Step by step instructions on setting up the project and running it
-
Set the env variables as per the dev instructions
-
Install submodules and dependencies:
foundryup
forge install- Run
forge testto run some scenario tests against the contracts
$ forge test
- All external actions, except SuperForm creation, start in
SuperformRouter.sol. The user has to provide a "StateRequest" containing the amounts being actioned into each vault in each chain, as well as liquidity information, about how the actual deposit/withdraw process will be handled - Deposits/withdraws can be single or multiple destination, single or multi vault, cross-chain or direct chain. For deposit actions, the user can provide a different input token on a source chain and receive the actual underlying token (different than input token) on the destination chain, after swapping and bridging in a single call. Sometimes it is also needed to perform another extra swap at the destination for tokens with low bridge liquidity, through the usage of
DstSwapper.sol. For withdraw actions, user can choose to receive a different token than the one redeemed for from the vault, back at the source chain. - The vaults themselves are wrapped by Forms - code implementations that adapt to the needs of a given vault. This wrapping action leads to the creation of SuperForms (the triplet of superForm address, form id and chain id).
- Any user can wrap a vault into a SuperForm using the SuperForm Factory but only the protocol may add new Form implementations.
- Any individual tx must be of a specific kind, either all deposits or all withdraws, for all vaults and destinations
- Users are minted SuperPositions on successful deposits, a type of ERC-1155 we modified called ERC-1155S. On withdrawals these are burned.
The typical flow for a deposit xchain transaction is:
- Validation of the input data in
SuperformRouter.sol. - Dispatching the input tokens to the liquidity bridge using an implementation of a
BridgeValidator.solandLiquidityHandler.sol. - Creating the
AMBMessagewith the information about what is going to be deposited and by whom. - Messaging the information about the deposits to the vaults using
CoreStateRegistry.sol. Typically this is done with the combination of two different AMBs by splitting the message and the proof for added security. - Receive the information on the destination chain's
CoreStateRegistry.sol. At this step, a keeper updates the messaged amounts to-be deposited with the actual amounts received through the liquidity bridge using one of theupdatePayloadfunctions. - The keeper can then process the received message using
processPayload. Here the deposit action is try-catched for errors. Should the action pass, a message is sent back to source acknowledging the action and minting SuperPositions to the user. If the action fails, no message is sent back and no SuperPositions are minted. - Funds bridged can be automatically recovered by the keeper in case of error catching and sent back to source using one of
rescueFailedDepositfunctions.
The typical flow for a withdraw xchain transaction is:
- Validation of the input data in
SuperformRouter.sol. - Burning the corresponding SuperPositions owned by the user in accordance to the input data.
- Creating the
AMBMessagewith the information about what is going to be withdrawn and by whom. - Messaging the information about the withdraws to the vaults using
CoreStateRegistry.sol. The process follows the same pattern as above - Receive the information on the destination chain's
CoreStateRegistry.sol. - The keeper can then process the received message using
processPayload. Here the withdraw action is try-catched for errors. Should the action pass, the underlying obtained is bridged back to the user in the form of the desired tokens to be received. If the action fails, a message is sent back indicating that SuperPositions need to be re-minted for the user according to the original amounts that were burned.
| src/SuperFormFactory.sol:SuperFormFactory contract | |||||
|---|---|---|---|---|---|
| Deployment Cost | Deployment Size | ||||
| 2784387 | 14142 | ||||
| Function Name | min | avg | median | max | # calls |
| addFormBeacon | 4682 | 643178 | 637075 | 658975 | 2263 |
| changeFormBeaconPauseStatus | 5466 | 505673 | 505673 | 1005881 | 2 |
| createSuperForm | 725 | 320587 | 319818 | 341718 | 20107 |
| getAllSuperForms | 18856 | 18856 | 18856 | 18856 | 1 |
| getAllSuperFormsFromVault | 1993 | 1993 | 1993 | 1993 | 1 |
| getBytecodeFormBeacon | 15170 | 15170 | 15170 | 15170 | 1 |
| getFormBeacon | 635 | 930 | 635 | 2635 | 203 |
| getFormCount | 326 | 326 | 326 | 326 | 1 |
| getSuperForm | 459 | 459 | 459 | 459 | 31 |
| getSuperFormCount | 348 | 348 | 348 | 348 | 1 |
| isFormBeaconPaused | 1357 | 1357 | 1357 | 1357 | 1 |
| updateFormBeaconLogic | 2600 | 7681 | 6793 | 13416 | 5 |
| src/SuperFormRouter.sol:SuperFormRouter contract | |||||
|---|---|---|---|---|---|
| Deployment Cost | Deployment Size | ||||
| 4338555 | 22089 | ||||
| Function Name | min | avg | median | max | # calls |
| multiDstMultiVaultDeposit | 630421 | 1067055 | 1012200 | 1856786 | 11 |
| multiDstMultiVaultWithdraw | 563726 | 1190243 | 1500151 | 1506852 | 3 |
| multiDstSingleVaultDeposit | 465047 | 781205 | 773480 | 1377258 | 14 |
| multiDstSingleVaultWithdraw | 297130 | 354790 | 331307 | 460124 | 5 |
| singleDirectSingleVaultDeposit | 205460 | 252437 | 258455 | 302534 | 12 |
| singleDirectSingleVaultWithdraw | 24108 | 296786 | 140501 | 882035 | 4 |
| singleXChainMultiVaultDeposit | 256784 | 447997 | 455178 | 656408 | 13 |
| singleXChainMultiVaultWithdraw | 217273 | 238534 | 239594 | 257884 | 5 |
| singleXChainSingleVaultDeposit | 244953 | 331827 | 349072 | 397602 | 14 |
| singleXChainSingleVaultWithdraw | 142336 | 163485 | 162387 | 186831 | 4 |
| src/SuperPositions.sol:SuperPositions contract | |||||
|---|---|---|---|---|---|
| Deployment Cost | Deployment Size | ||||
| 2720972 | 14450 | ||||
| Function Name | min | avg | median | max | # calls |
| balanceOf | 642 | 1218 | 642 | 2642 | 493 |
| burnBatchSP | 6899 | 7812 | 8059 | 9218 | 13 |
| burnSingleSP | 3513 | 3697 | 3513 | 4391 | 19 |
| dynamicURI | 1299 | 1299 | 1299 | 1299 | 1 |
| mintBatchSP | 48618 | 48618 | 48618 | 48618 | 2 |
| mintSingleSP | 24512 | 24563 | 24512 | 25395 | 17 |
| setApprovalForOne | 2933 | 23333 | 24933 | 24933 | 55 |
| setDynamicURI | 2979 | 23379 | 23546 | 43446 | 4 |
| stateMultiSync | 10292 | 49599 | 51101 | 93825 | 37 |
| stateSync | 7010 | 24276 | 26910 | 27861 | 43 |
| supportsInterface | 548 | 548 | 548 | 548 | 1 |
| updateTxHistory | 23495 | 23850 | 23495 | 25495 | 107 |
| uri | 2332 | 2332 | 2332 | 2332 | 1 |
| src/crosschain-liquidity/DstSwapper.sol:DstSwapper contract | |||||
|---|---|---|---|---|---|
| Deployment Cost | Deployment Size | ||||
| 962913 | 5020 | ||||
| Function Name | min | avg | median | max | # calls |
| batchProcessTx | 122225 | 169121 | 173375 | 228495 | 17 |
| processTx | 71500 | 73199 | 71500 | 81119 | 12 |