-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(withdrawer): bridged ERC20 token withdrawals (#1149)
## Summary implement withdrawals of ERC20 tokens that are of type `AstriaBridgeableERC20` (see implemented contract). ## Background we want to be able to withdraw ERC20 tokens that are bridged to a rollup. ## Changes - implement `AstriaBridgeableERC20` which is a standard `ERC20` contract with additional functionality for minting (not used by the withdrawer, implemented/tested here astriaorg/astria-geth#20) as well as functionality for withdrawing - implement `IAstriaWithdrawer` which is implemented by both `AstriaWithdrawer` and `AstriaMintableERC20`. - the withdrawer now interacts with a `IAstriaWithdrawer`. both native assets and ERC20 withdrawals have the same event signatures, so no additional code was needed for the withdrawer itself. - to use the withdrawer with an `AstriaBridgeableERC20`, the `ASTRIA_BRIDGE_WITHDRAWER_ETHEREUM_CONTRACT_ADDRESS` is set to some `AstriaMintableERC20`, and `ASTRIA_BRIDGE_WITHDRAWER_ROLLUP_ASSET_DENOMINATION` is set to the rollup asset's denomination as represented on the sequencer. for example, if the asset is represented on the sequencer is `transfer/channel-1/usdc`, that is the rollup asset denomination. the `name/symbol` of the ERC20 contract are not relevant. - also update the build script to write the generated abigen contract bindings to files, this is easier for debugging and unit testing. ## Testing unit tests ## Related Issues closes #924
- Loading branch information
Showing
22 changed files
with
3,177 additions
and
72 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
crates/astria-core/src/generated/** linguist-generated=true | ||
crates/astria-bridge-withdrawer/src/withdrawer/ethereum/generated/** linguist-generated=true | ||
crates/astria-bridge-withdrawer/ethereum/out/** linguist-generated=true | ||
specs/** linguist-documentation=true |
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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[submodule "crates/astria-bridge-withdrawer/ethereum/lib/forge-std"] | ||
path = crates/astria-bridge-withdrawer/ethereum/lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[submodule "crates/astria-bridge-withdrawer/ethereum/lib/openzeppelin-contracts"] | ||
path = crates/astria-bridge-withdrawer/ethereum/lib/openzeppelin-contracts | ||
url = https://github.com/OpenZeppelin/openzeppelin-contracts |
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 |
---|---|---|
@@ -1,4 +1,32 @@ | ||
use ethers::contract::Abigen; | ||
|
||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
astria_build_info::emit("bridge-withdrawer-v")?; | ||
|
||
println!("cargo:rerun-if-changed=ethereum/src/AstriaWithdrawer.sol"); | ||
println!("cargo:rerun-if-changed=ethereum/src/IAstriaWithdrawer.sol"); | ||
println!("cargo:rerun-if-changed=ethereum/src/AstriaBridgeableERC20.sol"); | ||
|
||
Abigen::new( | ||
"IAstriaWithdrawer", | ||
"./ethereum/out/IAstriaWithdrawer.sol/IAstriaWithdrawer.json", | ||
)? | ||
.generate()? | ||
.write_to_file("./src/withdrawer/ethereum/generated/astria_withdrawer_interface.rs")?; | ||
|
||
Abigen::new( | ||
"AstriaWithdrawer", | ||
"./ethereum/out/AstriaWithdrawer.sol/AstriaWithdrawer.json", | ||
)? | ||
.generate()? | ||
.write_to_file("./src/withdrawer/ethereum/generated/astria_withdrawer.rs")?; | ||
|
||
Abigen::new( | ||
"AstriaBridgeableERC20", | ||
"./ethereum/out/AstriaBridgeableERC20.sol/AstriaBridgeableERC20.json", | ||
)? | ||
.generate()? | ||
.write_to_file("./src/withdrawer/ethereum/generated/astria_bridgeable_erc20.rs")?; | ||
|
||
Ok(()) | ||
} |
1 change: 1 addition & 0 deletions
1
crates/astria-bridge-withdrawer/ethereum/lib/openzeppelin-contracts
Submodule openzeppelin-contracts
added at
dbb610
1 change: 1 addition & 0 deletions
1
...stria-bridge-withdrawer/ethereum/out/AstriaBridgeableERC20.sol/AstriaBridgeableERC20.json
Large diffs are not rendered by default.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
...es/astria-bridge-withdrawer/ethereum/out/AstriaMintableERC20.sol/AstriaMintableERC20.json
Large diffs are not rendered by default.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
crates/astria-bridge-withdrawer/ethereum/out/AstriaWithdrawer.sol/AstriaWithdrawer.json
Large diffs are not rendered by default.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
crates/astria-bridge-withdrawer/ethereum/out/IAstriaWithdrawer.sol/IAstriaWithdrawer.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
68 changes: 68 additions & 0 deletions
68
crates/astria-bridge-withdrawer/ethereum/src/AstriaBridgeableERC20.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// SPDX-License-Identifier: MIT or Apache-2.0 | ||
pragma solidity ^0.8.21; | ||
|
||
import {IAstriaWithdrawer} from "./IAstriaWithdrawer.sol"; | ||
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract AstriaBridgeableERC20 is IAstriaWithdrawer, ERC20 { | ||
// the `astriaBridgeSenderAddress` built into the astria-geth node | ||
address public immutable BRIDGE; | ||
|
||
// the divisor used to convert the rollup asset amount to the base chain denomination | ||
// | ||
// set to 10 ** (TOKEN_DECIMALS - BASE_CHAIN_ASSET_PRECISION) on contract creation | ||
uint256 private immutable DIVISOR; | ||
|
||
// emitted when tokens are minted from a deposit | ||
event Mint(address indexed account, uint256 amount); | ||
|
||
modifier onlyBridge() { | ||
require(msg.sender == BRIDGE, "AstriaBridgeableERC20: only bridge can mint"); | ||
_; | ||
} | ||
|
||
constructor( | ||
address _bridge, | ||
uint32 _baseChainAssetPrecision, | ||
string memory _name, | ||
string memory _symbol | ||
) ERC20(_name, _symbol) { | ||
uint8 decimals = decimals(); | ||
if (_baseChainAssetPrecision > decimals) { | ||
revert("AstriaBridgeableERC20: base chain asset precision must be less than or equal to token decimals"); | ||
} | ||
|
||
BASE_CHAIN_ASSET_PRECISION = _baseChainAssetPrecision; | ||
DIVISOR = 10 ** (decimals - _baseChainAssetPrecision); | ||
BRIDGE = _bridge; | ||
} | ||
|
||
modifier sufficientValue(uint256 amount) { | ||
require(amount / DIVISOR > 0, "AstriaBridgeableERC20: insufficient value, must be greater than 10 ** (TOKEN_DECIMALS - BASE_CHAIN_ASSET_PRECISION)"); | ||
_; | ||
} | ||
|
||
function mint(address _to, uint256 _amount) | ||
external | ||
onlyBridge | ||
{ | ||
_mint(_to, _amount); | ||
emit Mint(_to, _amount); | ||
} | ||
|
||
function withdrawToSequencer(uint256 _amount, address _destinationChainAddress) | ||
external | ||
sufficientValue(_amount) | ||
{ | ||
_burn(msg.sender, _amount); | ||
emit SequencerWithdrawal(msg.sender, _amount, _destinationChainAddress); | ||
} | ||
|
||
function withdrawToIbcChain(uint256 _amount, string calldata _destinationChainAddress, string calldata _memo) | ||
external | ||
sufficientValue(_amount) | ||
{ | ||
_burn(msg.sender, _amount); | ||
emit Ics20Withdrawal(msg.sender, _amount, _destinationChainAddress, _memo); | ||
} | ||
} |
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
25 changes: 25 additions & 0 deletions
25
crates/astria-bridge-withdrawer/ethereum/src/IAstriaWithdrawer.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// SPDX-License-Identifier: MIT or Apache-2.0 | ||
pragma solidity ^0.8.21; | ||
|
||
abstract contract IAstriaWithdrawer { | ||
// the precision of the asset on the base chain. | ||
// | ||
// the amount transferred on the base chain will be divided by 10 ^ (18 - BASE_CHAIN_ASSET_PRECISION). | ||
// | ||
// for example, if base chain asset is precision is 6, the divisor would be 10^12. | ||
uint32 public immutable BASE_CHAIN_ASSET_PRECISION; | ||
|
||
// emitted when a withdrawal to the sequencer is initiated | ||
// | ||
// the `sender` is the evm address that initiated the withdrawal | ||
// the `destinationChainAddress` is the address on the sequencer the funds will be sent to | ||
event SequencerWithdrawal(address indexed sender, uint256 indexed amount, address destinationChainAddress); | ||
|
||
// emitted when a withdrawal to the IBC origin chain is initiated. | ||
// the withdrawal is sent to the origin chain via IBC from the sequencer using the denomination trace. | ||
// | ||
// the `sender` is the evm address that initiated the withdrawal | ||
// the `destinationChainAddress` is the address on the origin chain the funds will be sent to | ||
// the `memo` is an optional field that will be used as the ICS20 packet memo | ||
event Ics20Withdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress, string memo); | ||
} |
6 changes: 0 additions & 6 deletions
6
crates/astria-bridge-withdrawer/src/withdrawer/ethereum/astria_withdrawer.rs
This file was deleted.
Oops, something went wrong.
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
Oops, something went wrong.