contract ERC20Locker {
constructor(bytes memory nearTokenFactory, INearProver prover) public;
function lockToken(IERC20 token, uint256 amount, string memory accountId) public;
function unlockToken(bytes memory proofData, uint64 proofBlockHeader) public;
}
struct BridgeTokenFactory {
/// The account of the prover that we can use to prove
pub prover_account: AccountId,
/// Address of the Ethereum locker contract.
pub locker_address: [u8; 20],
/// Hashes of the events that were already used.
pub used_events: UnorderedSet<Vec<u8>>,
/// Mapping from Ethereum tokens to NEAR tokens.
pub tokens: UnorderedMap<EvmAddress, AccountId>;
}
impl BridgeTokenFactory {
/// Initializes the contract.
/// `prover_account`: NEAR account of the Near Prover contract;
/// `locker_address`: Ethereum address of the locker contract, in hex.
#[init]
pub fn new(prover_account: AccountId, locker_address: String) -> Self;
/// Relays the lock event from Ethereum.
/// Uses prover to validate that proof is correct and relies on a canonical Ethereum chain.
/// Send `mint` action to the token that is specified in the proof.
#[payable]
pub fn deposit(&mut self, proof: Proof);
/// A callback from BridgeToken contract deployed under this factory.
/// Is called after tokens are burned there to create an receipt result `(amount, token_address, recipient_address)` for Ethereum to unlock the token.
pub fn finish_withdraw(token_account: AccountId, amount: Balance, recipient: EvmAddress);
/// Transfers given NEP-21 token from `predecessor_id` to factory to lock.
/// On success, leaves a receipt result `(amount, token_address, recipient_address)`.
#[payable]
pub fn lock(&mut self, token: AccountId, amount: Balance, recipient: String);
/// Relays the unlock event from Ethereum.
/// Uses prover to validate that proof is correct and relies on a canonical Ethereum chain.
/// Uses NEP-21 `transfer` action to move funds to `recipient` account.
#[payable]
pub fn unlock(&mut self, proof: Proof);
/// Deploys BridgeToken contract for the given EVM address in hex code.
/// The name of new NEP21 compatible contract will be <hex(evm_address)>.<current_id>.
/// Expects ~35N attached to cover storage for BridgeToken.
#[payable]
pub fn deploy_bridge_token(address: String);
/// Checks if Bridge Token has been successfully deployed with `deploy_bridge_token`.
/// On success, returns the name of NEP21 contract associated with given address (<hex(evm_address)>.<current_id>).
/// Otherwise, returns "token do not exists" error.
pub fn get_bridge_token_account_id(&self, address: String) -> AccountId;
}
struct BridgeToken {
controller: AccountId,
token: Token, // uses https://github.com/ilblackdragon/balancer-near/tree/master/near-lib-rs
}
impl BridgeToken {
/// Setup the Token contract with given factory/controller.
pub fn new(controller: AccountId) -> Self;
/// Mint tokens to given user. Only can be called by the controller.
pub fn mint(&mut self, account_id: AccountId, amount: Balance);
/// Withdraw tokens from this contract.
/// Burns sender's tokens and calls controller to create event for relaying.
pub fn withdraw(&mut self, amount: U128, recipient: String) -> Promise;
}
impl FungibleToken for BridgeToken {
// see example https://github.com/ilblackdragon/balancer-near/blob/master/balancer-pool/src/lib.rs#L329
}
To setup token contract on NEAR side, anyone can call <bridge_token_factory>.deploy_bridge_token(<erc20>)
where <erc20>
is the address of the token.
With this call must attach the amount of $NEAR to cover storage for (at least 30 $NEAR currently).
This will create <<hex(erc20)>.<bridge_token_factory>>
NEP141-compatible contract.
- User sends
<erc20>.approve(<erc20locker>, <amount>)
Ethereum transaction. - User sends
<erc20locker>.lock(<erc20>, <amount>, <destination>)
Ethereum transaction. This transaction will createLocked
event. - Relayers will be sending Ethereum blocks to the
EthClient
on NEAR side. - After sufficient number of confirmations on top of the mined Ethereum block that contain the
lock
transaction, user or relayer can callBridgeTokenFactory.deposit(proof)
. Proof is the extracted information from the event on Ethereum side. BridgeTokenFactory.deposit
function will callEthProver
and verify that proof is correct and relies on a block with sufficient number of confirmations.EthProver
will return callback toBridgeTokenFactory
confirming that proof is correct.BridgeTokenFactory
will call<<hex(erc20)>.<bridge_token_factory>>.mint(<near_account_id>, <amount>)
.- User can use
<<hex(erc20)>.<bridge_token_factory>>
token in other applications now on NEAR.
token-locker
locks NEP141 tokens on NEAR side.
To deposit funds into the locker, call ft_transfer_call
where msg
contains Ethereum address the funds should arrive to.
This will emit <token: String, amount: u128, recipient address: EthAddress>
(which arrives to deposit
on Ethereum side).
Accepts Unlock(token: String, sender_id: EthAddress, amount: u256, recipient: String)
event from Ethereum side with a proof, verifies its correctness.
If recipient
contains ':' will split it into <recipient, msg>
and do ft_transfer_call(recipient, amount, None, msg)
. Otherwise will ft_transfer
to recipient
.
To get metadata of token to Ethereum, need to call log_metadata
, which will create a result <token: String, name: String, symbol: String, decimals: u8, blockHeight: u64>
.
erc20-bridge-token
-BridgeTokenFactory
andBridgeToken
Ethereum contracts.
BridgeTokenFactory
creates new BridgeToken
that correspond to specific token account id on NEAR side.
BridgeTokenFactory
receives deposit
with proof from NEAR, verify them and mint appropriate amounts on recipient addresses.
Calling withdraw
will burn tokens of this user and will generate event <token: String, sender_id: EthAddress, amount: u256, recipient: String>
that can be relayed to token-factory
.
Generally, this connector allows any account to call ft_transfer_call
opening for potential malicious tokens to be bridged to Ethereum.
The expectation here is that on Ethereum side, the token lists will handle this, as it's the same attack model as malicious tokens on Uniswap and other DEXs.
Using Ethereum BridgeTokenFactory
contract can always resolve Ethereum address of a contract back to NEAR one to check that it is indeed bridging token from NEAR and is created by this factory.
cd erc20-connector
yarn
yarn run test
make res/bridge_token_factory.wasm
cargo test --all