forked from TokenLogic-com-au/aave-helpers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Arbitrum -> Mainnet ERC20 Bridge (bgd-labs#209)
* feat: update bridge owner and README as well as add missing natspec * chore: update inherit doc * feat: add support for multiple proof burns * feat: scaffold arb bridge * chore: add missing natspec * feat: add isTokenMapped function * feat: add bridge for arbitrum * Update README.md * feat: add function to exit * chore: scaffold opt * feat: check against 0xeee * chore: add TXs to README * chore: add who can rescue * chore: add tx to exit * feat: forward eth * feat: get gateway from token * feat: make gateway a parameter * chore: add test scripts * feat: finish tests for arbitrum bridge * chore: add README * chore: move to own folder * chore: cleanup * chore: add missing natspec * feat: update natspec * chore: update test * chore: add indexed * improve natspec * feat: update readme * chore: update with details * chore: update test --------- Co-authored-by: Fermin 'Piscu' Carranza <fermin@llama.xyz>
- Loading branch information
1 parent
6cf1999
commit 842d7e9
Showing
14 changed files
with
604 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule aave-address-book
updated
97 files
Submodule forge-std
updated
28 files
+6 −12 | .github/workflows/ci.yml | |
+3 −1 | .github/workflows/sync.yml | |
+0 −3 | .gitmodules | |
+1 −1 | README.md | |
+3 −3 | foundry.toml | |
+0 −1 | lib/ds-test | |
+1 −1 | package.json | |
+518 −225 | src/StdAssertions.sol | |
+10 −8 | src/StdChains.sol | |
+9 −3 | src/StdInvariant.sol | |
+7 −11 | src/StdJson.sol | |
+179 −0 | src/StdToml.sol | |
+1 −1 | src/StdUtils.sol | |
+4 −4 | src/Test.sol | |
+644 −7 | src/Vm.sol | |
+1 −5 | src/mocks/MockERC721.sol | |
+39 −909 | test/StdAssertions.t.sol | |
+33 −28 | test/StdChains.t.sol | |
+9 −11 | test/StdCheats.t.sol | |
+49 −0 | test/StdJson.t.sol | |
+8 −8 | test/StdMath.t.sol | |
+49 −0 | test/StdToml.t.sol | |
+18 −18 | test/StdUtils.t.sol | |
+3 −3 | test/Vm.t.sol | |
+8 −0 | test/fixtures/test.json | |
+6 −0 | test/fixtures/test.toml | |
+1 −1 | test/mocks/MockERC20.t.sol | |
+1 −1 | test/mocks/MockERC721.t.sol |
Submodule solidity-utils
updated
3 files
+18 −0 | README.md | |
+0 −1 | src/contracts/create3/Create3Factory.sol | |
+378 −0 | src/contracts/oz-common/EnumerableSet.sol |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; | ||
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; | ||
import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol'; | ||
import {Rescuable} from 'solidity-utils/contracts/utils/Rescuable.sol'; | ||
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; | ||
|
||
import {ChainIds} from '../../ChainIds.sol'; | ||
import {IAaveArbEthERC20Bridge} from './IAaveArbEthERC20Bridge.sol'; | ||
|
||
/// @notice The L1 Outbox to exit a bridge transaction on Mainnet | ||
interface IL1Outbox { | ||
/// @notice Executes a transaction by providing a generated proof | ||
/// @param proof The proof to exit with | ||
/// @param index The index of the transaction in the block | ||
/// @param l2sender The executor of the L2 transaction | ||
/// @param to The L1 gateway address that the L2 transaction was sent to | ||
/// @param l2block The L2 block where the transaction took place | ||
/// @param l1block The L1 block where the transaction took place | ||
/// @param l2timestamp The L2 timestamp when the transaction took place | ||
/// @param value The value sent with the transaction | ||
/// @param data Any extra data sent with the transaction | ||
function executeTransaction( | ||
bytes32[] calldata proof, | ||
uint256 index, | ||
address l2sender, | ||
address to, | ||
uint256 l2block, | ||
uint256 l1block, | ||
uint256 l2timestamp, | ||
uint256 value, | ||
bytes calldata data | ||
) external; | ||
} | ||
|
||
/// @notice L2 Gateway to initiate bridge | ||
interface IL2Gateway { | ||
/// @notice Executes a burn transaction to initiate a bridge | ||
/// @param tokenAddress The L11 address of the token to burn | ||
/// @param recipient Receiver of the bridged tokens | ||
/// @param amount The amount of tokens to bridge | ||
/// @param data Any extra data to include in the burn transaction | ||
function outboundTransfer( | ||
address tokenAddress, | ||
address recipient, | ||
uint256 amount, | ||
bytes calldata data | ||
) external; | ||
} | ||
|
||
/// @author efecarranza.eth | ||
/// @notice Contract to bridge ERC20 tokens from Arbitrum to Mainnet | ||
contract AaveArbEthERC20Bridge is Ownable, Rescuable, IAaveArbEthERC20Bridge { | ||
using SafeERC20 for IERC20; | ||
|
||
/// @inheritdoc IAaveArbEthERC20Bridge | ||
address public constant MAINNET_OUTBOX = 0x0B9857ae2D4A3DBe74ffE1d7DF045bb7F96E4840; | ||
|
||
/// @param _owner The owner of the contract upon deployment | ||
constructor(address _owner) { | ||
_transferOwnership(_owner); | ||
} | ||
|
||
/// @inheritdoc IAaveArbEthERC20Bridge | ||
function bridge( | ||
address token, | ||
address l1Token, | ||
address gateway, | ||
uint256 amount | ||
) external onlyOwner { | ||
if (block.chainid != ChainIds.ARBITRUM) revert InvalidChain(); | ||
|
||
IERC20(token).forceApprove(gateway, amount); | ||
|
||
IL2Gateway(gateway).outboundTransfer(l1Token, address(AaveV3Ethereum.COLLECTOR), amount, ''); | ||
|
||
emit Bridge(token, amount); | ||
} | ||
|
||
/// @inheritdoc IAaveArbEthERC20Bridge | ||
function exit( | ||
bytes32[] calldata proof, | ||
uint256 index, | ||
address l2sender, | ||
address destinationGateway, | ||
uint256 l2block, | ||
uint256 l1block, | ||
uint256 l2timestamp, | ||
uint256 value, | ||
bytes calldata data | ||
) external { | ||
if (block.chainid != ChainIds.MAINNET) revert InvalidChain(); | ||
|
||
IL1Outbox(MAINNET_OUTBOX).executeTransaction( | ||
proof, | ||
index, | ||
l2sender, | ||
destinationGateway, | ||
l2block, | ||
l1block, | ||
l2timestamp, | ||
value, | ||
data | ||
); | ||
|
||
emit Exit(l2sender, destinationGateway, l2block, l1block, value, data); | ||
} | ||
|
||
/// @inheritdoc Rescuable | ||
function whoCanRescue() public view override returns (address) { | ||
return owner(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
interface IAaveArbEthERC20Bridge { | ||
/// @notice This function is not supported on this chain | ||
error InvalidChain(); | ||
|
||
/// @notice Emitted when bridging a token from Arbitrum to Mainnet | ||
event Bridge(address indexed token, uint256 amount); | ||
|
||
/// @notice Emitted when finalizing the transfer on Mainnet | ||
/// @param l2sender The address sending the transaction from the L2 | ||
/// @param to The address receiving the bridged funds | ||
/// @param l2block The block number of the transaction on the L2 | ||
/// @param l1block The block number of the transaction on the L1 | ||
/// @param value The value being bridged from the L2 | ||
/// @param data Data being sent from the L2 | ||
event Exit( | ||
address l2sender, | ||
address to, | ||
uint256 l2block, | ||
uint256 l1block, | ||
uint256 value, | ||
bytes data | ||
); | ||
|
||
/// @notice Returns the address of the Mainnet contract to exit the bridge from | ||
function MAINNET_OUTBOX() external view returns (address); | ||
|
||
/// This function withdraws an ERC20 token from Arbitrum to Mainnet. exit() needs | ||
/// to be called on mainnet with the corresponding burnProof in order to complete. | ||
/// @notice Arbitrum only. Function will revert if called from other network. | ||
/// @param token Arbitrum address of ERC20 token to withdraw. | ||
/// @param l1token Mainnet address of ERC20 token to withdraw. | ||
/// @param gateway The L2 gateway address to bridge through | ||
/// @param amount Amount of tokens to withdraw | ||
function bridge(address token, address l1token, address gateway, uint256 amount) external; | ||
|
||
/// This function completes the withdrawal process from Arbitrum to Mainnet. | ||
/// Burn proof is generated via API. Please see README.md | ||
/// @notice Mainnet only. Function will revert if called from other network. | ||
/// @param proof[] Burn proof generated via API. | ||
/// @param index The index of the burn transaction. | ||
/// @param l2sender The address sending the transaction from the L2 | ||
/// @param destinationGateway The L1 gateway address receiving the bridged funds | ||
/// @param l2block The block number of the transaction on the L2 | ||
/// @param l1block The block number of the transaction on the L1 | ||
/// @param l2timestamp The timestamp of the transaction on the L2 | ||
/// @param value The value being bridged from the L2 | ||
/// @param data Data being sent from the L2 | ||
function exit( | ||
bytes32[] calldata proof, | ||
uint256 index, | ||
address l2sender, | ||
address destinationGateway, | ||
uint256 l2block, | ||
uint256 l1block, | ||
uint256 l2timestamp, | ||
uint256 value, | ||
bytes calldata data | ||
) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Aave Arbitrum -> Mainnet ERC20 Bridge | ||
|
||
Arbitrum does offer a way to bridge directly from their network to Mainnet using their standard ArbERC20 tokens. There is a 'gateway' address that is used to bridge to mainnet. It can be called from the tokens themselves, however, it requires some domain knowledge. This contract facilitates the bridging of tokens in order to make managing Aave DAO's treasury easier. | ||
|
||
The same contract exists on both chains with the same address, so this contract will receive funds from the Arbitrum Collector, then call to bridge the received tokens. After the Arbitrum rollup happens, the "burn proof" will be generated via API. At this point, the mainnet contract can be called. `exit()` will give the mainnet collector contract the tokens that were bridged over from Arbitrum. | ||
|
||
In order to generate the proof for an exit, as well as the other required data, please install the `aave-cli` tool which can be found [here](https://github.com/bgd-labs/aave-cli). | ||
|
||
In order to generate the proof, run the following command: | ||
|
||
`aave-cli arbitrum-bridge-exit [TX_HASH] [INDEX] [ARBITRUM_BLOCK_OF_TX]` | ||
|
||
Where the TX_HASH is the hash where the bridge was initiated on Arbitrum, the INDEX is the withdrawal index (ie: if there are 3 tokens bridged in the same transaction, the indexes will be 0, 1 and 2), and ARBITRUM_BLOCK_OF_TX, which is the block the bridge transaction happened at. | ||
|
||
## Functions | ||
|
||
``` | ||
function bridge( | ||
address token, | ||
address l1Token, | ||
address gateway, | ||
uint256 amount | ||
) external; | ||
``` | ||
|
||
Callable on Arbitrum to withdraw ERC20 token. It withdraws `amount` of passed `token` to mainnet. The gateway address can be found either in the token's read methods where it's called `l1gateway()` or `gateway()`, depending on the token. You can also refer to the Arbitrum [docs](https://docs.arbitrum.io/build-decentralized-apps/token-bridging/token-bridge-erc20#other-flavors-of-gateways) and the relevant [addresses](https://docs.arbitrum.io/build-decentralized-apps/reference/useful-addresses). | ||
|
||
``` | ||
function exit( | ||
bytes32[] calldata proof, | ||
uint256 index, | ||
address l2sender, | ||
address to, | ||
uint256 l2block, | ||
uint256 l1block, | ||
uint256 l2timestamp, | ||
uint256 value, | ||
bytes calldata data | ||
) external | ||
``` | ||
|
||
Callable on Mainnet to finish the withdrawal process. Callable ~7 days after `bridge()` is called and proof is available via `aave-cli`. Arbitrum currently doesn't support getting this information via API (we have requested this feature). | ||
|
||
`function emergencyTokenTransfer(address erc20Token, address to, uint256 amount) external;` | ||
|
||
Callable on Arbitrum. Withdraws tokens from bridge contract back to Aave Collector on Arbitrum. | ||
|
||
## Burn Proof Generation | ||
|
||
After you have called `bridge()` Arbitrum, it will take ~7 days for the withdrawal to be available (it's a rollup solution after all). Once it's available, a user can withdraw on Mainnet. | ||
|
||
Here's a sample transaction: https://arbiscan.io/tx/0x726c0b903d77088af36e06dfe6fd40df318ba83b8a93726fa30fab018cb43357 | ||
|
||
https://arbiscan.io/tx/0x726c0b903d77088af36e06dfe6fd40df318ba83b8a93726fa30fab018cb43357#eventlog | ||
|
||
The relevant log in this example is #7, from address `0x0000000000000000000000000000000000000064` which is an Arbitrum pre-compiled contract. Foundry does not currently support pre-compiled contracts in any networks other than Mainnet, so unfortunately a script cannot be used to run test commands. You can use the arbiscan.io UI as an alternative. | ||
|
||
The exit transaction is here: https://etherscan.io/tx/0xa34c3725cc95773eedf96b03e9672ad77940b27fc5b1b94441e6587dec014ecd | ||
|
||
You can see the input data which was retrieved via the Aave CLI tool. | ||
|
||
## Deployed Addresses | ||
|
||
Mainnet: [0x0335ffa9af5ce05590d6c9a75b645470e07744a9](https://etherscan.io/address/0x0335ffa9af5ce05590d6c9a75b645470e07744a9) | ||
Arbitrum: [0x0335ffa9af5ce05590d6c9a75b645470e07744a9](https://arbiscan.io/address/0x0335ffa9af5ce05590d6c9a75b645470e07744a9) | ||
|
||
Confirmed Bridges: | ||
|
||
| Token | Can Bridge | Burn | Exit | | ||
| ---------------- | ---------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | ||
| USDC (Native) | NO | N/A | N/A | | ||
| USDC.e (Bridged) | YES | [Tx](https://arbiscan.io/tx/0x4d64e1200e55d745428af2ad57af72c345dd47e736227b8ad1fefc9a6dde0bbe) | [Tx](https://etherscan.io/tx/0x43f861ded0892ea3f67bba45b331d3fc816546d1a6a4de59dce455d37d15c2e3) | | ||
| WETH | YES | [Tx](https://arbiscan.io/tx/0xa466214026874d294dc1b2ec188ce29f44eda24917729841b96c9dbd53be3f4b) | [Tx](https://etherscan.io/tx/0x082ac47de76e638afd89f0c0dc9dd6a79f0bec61daa5f9280842fbfd583d18e5) | | ||
| WBTC | YES | [Tx](https://arbiscan.io/tx/0x3f05e30984c67b21a9bce4866336bf0da6f90a29a9346f1f121f5adeb773c3df) | [Tx](https://etherscan.io/tx/0x0e8875142024a5243d48262b12df051eec32c04d5bf9512b0f92c7c8e27cecb8) | | ||
| wstETH | NO | N/A | N/A | | ||
| DAI | YES | [Tx](https://arbiscan.io/tx/0x1ce3cf0f0e6dc01fc2e78105cd3c0a24b3d517cef83b8e54c8321cdd177381c6) | [Tx](https://etherscan.io/tx/0xb2901150d654c2751d9624afbaf70386d38415d47e8b4fee512c09ea7503e38f) | | ||
| EURS | NO | N/A | N/A | | ||
| AAVE | YES | [Tx]() | [Tx]() | | ||
| MAI | YES | [Tx]() | [Tx]() | | ||
| rETH | YES | [Tx]() | [Tx]() | | ||
| LUSD | YES | [Tx]() | [Tx]() | | ||
| FRAX | YES | [Tx]() | [Tx]() | | ||
| ARB | YES | [Tx]() | [Tx]() | | ||
| weETH | YES | [Tx]() | [Tx]() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.