Superform Smart Contracts

This repository contains the core protocol smart contracts of Superform.

codecov

Table of contents

Prerequisites

See the official Foundry installation instructions.

Then, install the foundry toolchain installer (foundryup) with:

curl -L https://foundry.paradigm.xyz | bash

Now 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

Project structure

.
├── 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:

  • src are self-explanatory. All Smart Contract Codes are found inside /src
  • interfaces are where we add our custom written interfaces /src/interfaces.
  • types is where all re-used types are written and used across different smart contracts. /src/types
  • vendor is where all externally written interfaces reside. /src/vendor

Installation

Step by step instructions on setting up the project and running it

  1. Set the env variables as per the dev instructions

  2. Install submodules and dependencies:

foundryup
forge install
  1. Run forge test to run some scenario tests against the contracts

Testing

$ forge test

Documentation

Contract Architecture

Superform v1 Smart Contract Architecture (4)
  1. 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
  2. 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.
  3. 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).
  4. Any user can wrap a vault into a SuperForm using the SuperForm Factory but only the protocol may add new Form implementations.
  5. Any individual tx must be of a specific kind, either all deposits or all withdraws, for all vaults and destinations
  6. Users are minted SuperPositions on successful deposits, a type of ERC-1155 we modified called ERC-1155S. On withdrawals these are burned.

Transaction Flow

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.sol and LiquidityHandler.sol.
  • Creating the AMBMessage with 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 the updatePayload functions.
  • 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 rescueFailedDeposit functions.

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 AMBMessage with 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.

Read more about our protocol

Gas Costs (28 July 2023)

src/SuperFormFactory.sol:SuperFormFactory contract
Deployment CostDeployment Size
278438714142
Function Nameminavgmedianmax# calls
addFormBeacon46826431786370756589752263
changeFormBeaconPauseStatus546650567350567310058812
createSuperForm72532058731981834171820107
getAllSuperForms188561885618856188561
getAllSuperFormsFromVault19931993199319931
getBytecodeFormBeacon151701517015170151701
getFormBeacon6359306352635203
getFormCount3263263263261
getSuperForm45945945945931
getSuperFormCount3483483483481
isFormBeaconPaused13571357135713571
updateFormBeaconLogic260076816793134165
src/SuperFormRouter.sol:SuperFormRouter contract
Deployment CostDeployment Size
433855522089
Function Nameminavgmedianmax# calls
multiDstMultiVaultDeposit63042110670551012200185678611
multiDstMultiVaultWithdraw5637261190243150015115068523
multiDstSingleVaultDeposit465047781205773480137725814
multiDstSingleVaultWithdraw2971303547903313074601245
singleDirectSingleVaultDeposit20546025243725845530253412
singleDirectSingleVaultWithdraw241082967861405018820354
singleXChainMultiVaultDeposit25678444799745517865640813
singleXChainMultiVaultWithdraw2172732385342395942578845
singleXChainSingleVaultDeposit24495333182734907239760214
singleXChainSingleVaultWithdraw1423361634851623871868314
src/SuperPositions.sol:SuperPositions contract
Deployment CostDeployment Size
272097214450
Function Nameminavgmedianmax# calls
balanceOf64212186422642493
burnBatchSP689978128059921813
burnSingleSP351336973513439119
dynamicURI12991299129912991
mintBatchSP486184861848618486182
mintSingleSP2451224563245122539517
setApprovalForOne293323333249332493355
setDynamicURI29792337923546434464
stateMultiSync1029249599511019382537
stateSync701024276269102786143
supportsInterface5485485485481
updateTxHistory23495238502349525495107
uri23322332233223321
src/crosschain-liquidity/DstSwapper.sol:DstSwapper contract
Deployment CostDeployment Size
9629135020
Function Nameminavgmedianmax# calls
batchProcessTx12222516912117337522849517
processTx7150073199715008111912