This bridge project is the first step of Aave in Starknet ecosystem. The bridge allows users to deposit or withdraw their aTokens, and only aTokens, on Ethereum side, then mints or burns them wrapped aTokens named static_a_tokens on Starknet side. static_a_tokens are equivalent to aTokens except that the former grow in value when the latter grow in balance.
Holding L1 aTokens lets you earn more tokens via two different mechanisms: (i) the amount of aTokens you hold increases over time and (ii) holding aTokens allows you to claim an accruing amount of Aave reward tokens. This bridge offers both mechanisms thanks to static_a_tokens on L2, and the equivalent of L1 Aave reward token on L2.
The bridge is also shaped for liquidity providers who are able to assume the Ethereum gas cost of deposits and withdrawals as they transact large enough amounts. They will deposit on Aave Ethereum, bridge the static_a_tokens to Starknet and make them available for users there to buy and hold, thus accruing yield from L1.
We assume that L1 tokens approved by the bridge are pre-validated tokens, and that they are not deflationary.
L1
Bridge- handles deposit ofaTokenson L1, withdrawal ofstatic_a_tokensfrom L2, and update of L2 rewards index. L1 deposits and withdrawals can be done withaTokenor with their underlying asset.
L2
static_a_token- exchange-rate-increasing wrapper ofaTokenson L2.incentivized_erc20- ERC20-compliant token, tracks claimable rewards and stores the last updated rewards index for eachstatic_a_tokenholder.rewAAVE- ERC20 representing Aave reward token on L2.bridge- bridge responsible for:- minting and burning
static_a_tokenson message from L1. - bridging
rewAAVEtokens back to L1. - updating
rewards_indexfor eachstatic_a_tokenon message from L1.
- minting and burning
proxy- generic implementation of a proxy in cairo.
These static_a_tokens are a starting point for almost any cross-chain liquidity development to minimize “active” communication between chains. By design, a holder of those tokens - on Ethereum or after bridging somewhere else - will be passively accumulating yield from Aave on Ethereum.
More precisely, static_a_tokens are wrapped aTokens that grow in value while aTokens grow in balance. Such behavior is possible because static_a_tokens are backed by increasing amounts of aTokens locked in the L1 part of the bridge. static_a_tokens living on L2 can be bridged back to aTokens.
Each of the following contracts is deployed behind a proxy:
bridgeon L2static_a_tokenon L2rewAAVEtoken on L2Bridgeon L1
static_a_tokendeployed contracts are controlled by L2bridge.rewAAVEtoken is owned by L2bridge.
L1 aTokens and their corresponding L2 static_a_tokens are approved on L1 bridge in initialize function. The function _approveBridgeTokens is called internally to approve an array of aTokens with their corresponding static_a_tokens on L2.
Users can either deposit their aTokens (let's say aDai) or deposit the corresponding underlying asset (i.e Dai). Users first have to approve the bridge to spend the tokens - aTokens or the underlying asset. Calling deposit function, the following actions happen:
-
If the user deposits underlying
asset:assettokens will be transferred from the user account to L1 bridge.- The bridge will convert
assettokens to aTokens - by depositing in Aave's lending pool. - A message will be sent to L2 bridge with the amount of
static_a_tokento be minted, L1 token address, L2 recipient address, L1 block number and L1 rewards index. - L2 bridge will mint to L2 recipient the given amount of corresponding
static_a_tokens.
-
If the user deposits
aToken:aTokenswill be transferred from the user account to L1 bridge.- A message will be sent to L2 bridge with the amount of
static_a_tokento be minted, L1 token address, L2 recipient address, L1 block number and L1 rewards index. - L2 bridge will mint to L2 recipient the given amount of corresponding
static_a_tokens.
To bridge their static_a_tokens back to L1, users should initiate a withdrawal on the L2 bridge. Calling initiate_withdraw results in the following:
- The amount of
static_a_tokensto withdraw will be burned by L2 bridge. - A message will be sent to L1 with L1 aToken address, L1 recipient, L2 rewards index and the amount.
- L1 bridge will then transfer
aTokensto the L1 recipient. - L1 bridge will also check for any difference in the L1/L2 rewards index and transfer any unclaimed rewards to L1 recipient.
Starknet users will keep enjoying the same rewards as on L1 after bridging their assets. To do so, L1 rewards index is stored in the state of static_a_tokens. The index is updated every time a user deposits or withdraw the corresponding aToken, and can also be updated in a permissionless manner by calling the function updateL2State in L1 bridge. Rewards on L1 are sent to L1 recipient either when withdrawing static_a_tokens from L2 or when calling and then bridging rewards on L2 as described below.
To claim rewards, an L2 user should call claim_rewards on static_a_token contract which calls L2 bridge in return. L2 bridge then mints due rewAAVE tokens to the L2 user.
Calling bridge_rewards on L2 token bridge results in:
- The bridged amount of
rewAAVEtokens will be burned. - L1 bridge receives the bridging message and claims the rewards amount to
self by calling
claimRewardson AaveIncentivesControllercontract. - Rewards are then transferred to L1 recipient.
Install Node 16
Our codebase relies on Node 16. To install it, you can first install nvm and then run the following commands:
nvm install 16
nvm use 16Install Python 3.7.12
Our codebase relies on Python 3.7.12. To install it, you can first install pyenv and then run the following commands:
pyenv install 3.7.12
pyenv local 3.7.12Install GMP (needed for Cairo)
Before installing Cairo you need to install GMP. Run one of the following command depending on your OS.
sudo apt install -y libgmp3-dev # linux
brew install gmp # macInstall Node dependencies
Let's install all our project dependencies:
yarn installInstall Python dependencies
Let’s create a virtual environment to isolate your project’s requirements from your global Python environment.
python -m venv .venv
source .venv/bin/activateInstall poetry for dependencies management
python -m pip install --upgrade pip
pip install poetry
poetry installSolidity files are automatically compiled before running the tests, but Cairo files are not. To compile them, run:
yarn compile:l2We recommend to run L1 and L2 testnets in different terminals.
Start L2 testnet
In a terminal where venv is activated, run:
yarn testnet:l2Start L1 testnet
Create a .env file from the sample (cp .env.sample .env), and fill a value for the variable ALCHEMY KEY - you can get one here. Then, load all the environment variables.
source .envAnd start L1 testnet in the same terminal by running:
yarn testnet:l1The project is tested using hardhat, the starknet hardhat plugin and starknet-devnet. We created a Docker Compose file to run tests easily: we start L1 and L2 test networks in two separate containers and run the tests from a third one. To run all tests, simply run the following commands:
docker compose up --build
docker exec -ti $(docker ps -f name=test-runner -q) bash
yarn test
First make sure to set the aTokens addresses to be approved on the bridge as well as the metadata related to the staticATokens to be deployed on l2 in ./scripts/allowlistedTokens.ts.
yarn deploy-bridge:testnet #deploys bridge on l1 & l2 testnetsContributors
