Sidechains
Fee collection on sidechains works similarly to that on the Ethereum mainnet. Collected fees are sent to a fee receiver contract and then burned. On most sidechains, tokens are burnt for MIM, as it's an easy asset to bridge back to the mainnet. These proxy contracts have a bridge() function to bridge the tokens to the Ethereum mainnet.
MIM is then burnt for 3CRV on Ethereum and sent to the FeeDistributor.
The contract owner can bridge any token in any quantity, other accounts can only bridge approved tokens, where the balance exceeds a minimum amount defined by the owner. This prevents bridging tokens when the amount is so small that claiming on the root chain becomes economically unfeasible.
▶Proxy Source Code▼
@external
def bridge(_coin: address):
"""
@notice Transfer a coin to the root chain via the bridging contract.
@dev The contract owner can bridge any token in any quantity,
other accounts can only bridge approved tokens, where
the balance exceeds a minimum amount defined by the owner.
This prevents bridging tokens when the amount is so small
that claiming on the root chain becomes economically unfeasible.
@param _coin Address of the coin to be bridged.
"""
bridging_contract: address = self.bridging_contract
amount: uint256 = ERC20(_coin).balanceOf(self)
if amount > 0:
response: Bytes[32] = raw_call(
_coin,
_abi_encode(bridging_contract, amount, method_id=method_id("transfer(address,uint256)")),
max_outsize=32,
)
if msg.sender != self.admin:
minimum: uint256 = self.bridge_minimums[_coin]
assert minimum != 0, "Coin not approved for bridging"
assert minimum <= ERC20(_coin).balanceOf(bridging_contract), "Balance below minimum bridge amount"
Bridger(bridging_contract).bridge(_coin)
Bridging
The methods to burn and bridge assets might slightly vary based on the chain. The examples down below are taken from Optimism.
bridge
Bridge.bridge(coin: address) -> boolThis function is only callable by the owner of the contract. Additionally, only TOKEN can be bridged.
Function to bridge the entire balance of _coin to the root chain. This function must be called from its proxy contract, as its the owner.
Returns: true (bool).
Emits: AssetBridged
| Input | Type | Description |
|---|---|---|
_coin | address | coin to bridge |
<>Source code▼
event AssetBridged:
token: indexed(address)
amount: uint256
@external
def bridge(coin: address) -> bool:
assert msg.sender == self.owner and coin == TOKEN
start: uint256 = self.start
assert block.timestamp > start + 1800
amount: uint256 = ERC20(coin).balanceOf(self)
if amount == 0:
amount = ERC20(coin).balanceOf(msg.sender)
assert ERC20(coin).transferFrom(msg.sender, self, amount)
if start == 0:
ERC20(coin).transfer(msg.sender, ERC20(coin).balanceOf(self) - amount / 100)
amount = amount / 100
self.start = block.timestamp
receiver: address = self.receiver
adapter_params: Bytes[128] = concat(
b"\x00\x02",
convert(100_000, bytes32),
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
convert(receiver, bytes32)
)
fee: uint256 = ProxyOFT(PROXY_OFT).estimateSendFee(
ETH_CHAIN_ID, convert(receiver, bytes32), amount, False, adapter_params,
)
assert ERC20(coin).approve(PROXY_OFT, amount)
ProxyOFT(PROXY_OFT).sendFrom(
self,
ETH_CHAIN_ID,
convert(receiver, bytes32),
amount,
CallParams({
refund: self,
zero_payer: empty(address),
adapter_params: adapter_params,
}),
value=fee
)
log AssetBridged(coin, amount)
return True
▶Example▼
>>> Bridge.bridge('0xB153FB3d196A8eB25522705560ac152eeEc57901')
Contract Info Methods
PROXY_OFT
Bridge.PROXY_OFT() -> address: viewGetter for the OFT bridger.
<>Source code▼
PROXY_OFT: public(immutable(address))
@external
def __init__(proxy_oft: address, receiver: address, token: address):
self.owner = msg.sender
self.receiver = receiver
PROXY_OFT = proxy_oft
TOKEN = token
log AcceptOwnership(msg.sender)
▶Example▼
>>> Bridge.PROXY_OFT()
'0x48686c24697fe9042531B64D792304e514E74339'
TOKEN
Bridge.TOKEN() -> address: viewGetter for the bridge token.
<>Source code▼
TOKEN: public(immutable(address))
@external
def __init__(proxy_oft: address, receiver: address, token: address):
self.owner = msg.sender
self.receiver = receiver
PROXY_OFT = proxy_oft
TOKEN = token
log AcceptOwnership(msg.sender)
▶Example▼
>>> Bridge.TOKEN()
'0xB153FB3d196A8eB25522705560ac152eeEc57901'
Receiver
Receiver of the bridged funds is the 0xECB contract on Ethereum Mainnet.
receiver
Bridge.receiver() -> address: viewGetter for the receiver address of the bridged funds.
Receiver is the 0xECB contract (FeeCollector/Proxy on Ethereum Mainnet).
<>Source code▼
PROXY_OFT: public(immutable(address))
@external
def __init__(proxy_oft: address, receiver: address, token: address):
self.owner = msg.sender
self.receiver = receiver
PROXY_OFT = proxy_oft
TOKEN = token
log AcceptOwnership(msg.sender)
▶Example▼
>>> Bridge.receiver()
'0xeCb456EA5365865EbAb8a2661B0c503410e9B347'
set_root_receiver
Bridge.set_root_receiver(receiver: address)This function is only callable by the owner of the contract.
Function to set a new receiver address for the bridged funds.
| Input | Type | Description |
|---|---|---|
receiver | address | new receiver address |
<>Source code▼
@external
def set_root_receiver(receiver: address):
assert msg.sender == self.owner
assert receiver != empty(address)
self.receiver = receiver
▶Example▼
>>> Bridge.set_root_receiver('0x0000000000000000000000000000000000000000')