Crosschain
Crosschain
EeseeAssetHub / EeseeAssetSpoke
!WARNING! While our EeseeAssetHub / EeseeAssetSpoke contracts support fee-on-transfer tokens, please never use ERC20s with deflationary mechanics, or you will lose them.
Architechure
Eesee utilizes crosschain architecture to save gas for our users. We use Chainlink CCIP or Axelar to do crosschain communications and wrap assets between all supported chains.
For our architecture we use the hub and spoke model, where EeseeAssetHub acts as a hub, and EeseeAssetSpokes on multiple EVM chains act as spokes. Because of this, it is easy for us to add or change EeseeAssetSpokes without having centralized access control.
EeseeAssetSpoke
EeseeAssetSpoke is used to wrap ERC721/ERC1155/ERC20/Native tokens to ERC1155 tokens on L2 chain. It can also be used to call arbitrary functions on L2 chain. This can be used to create new lots in eesee from any chain on L2. EeseeAssetSpoke acts as a vault, securely and trustlessly holding bridged assets until their corresponding wrapped asset on L2 is unwraped and burned.
EeseeAssetHub
EeseeAssetHub is a hub contract deployed on L2 chain that receives messages from EeseeAssetSpokes. EeseeAssetHub is also an ERC1155 contract, which mints new tokens whennever it receives a message.
Note that EeseeAssetHub does not have means of access control for received messages, which means anyone can mint tokens through it (even if they are not backed up by real liquidity). However, all tokens have the sender address and a chain stored in their tokenURI, which allows identifying counterfeit tokens minted by unauthentic senders.
!WARNING! Never send any tokens to EeseeAssetHub contract from an EOA, or by contract in a separate transaction or they will be lost. Only send funds to it if you spend them by calling unwrap() function in the same transaction.
Royalties
If an asset you are wrapping includes royalties, EeseeAssetSpoke, in conjunction with EeseeAssetHub, ensures that the wrapped ERC1155 tokens also collect the same amount of royalties for the same addresses, but on a different blockchain. However, please be aware that due to the nature of cross-chain messaging, it is not possible to update the royalties on the destination chain after the wrapping process has taken place. Therefore, if the royalties of the assets on the source chain are updated, the royalties of their wrapped counterparts on the destination chain may become different until the asset is unwrapped and wrapped back.
Bridged ERC-721 and ERC-1155 tokens from EeseeAssetSpoke to EeseeAssetHub will maintain the same token royalty data on the hub chain as what is listed on the spoke chain. However, the royalty receiver on the spoke chain may be unable to access the royalty fees on the hub chain. Specifically, if the royalty receiver is a contract on the spoke chain, there is a high probability that the exact contract is not deployed at the same address on the hub chain. In this case, the royalty fees are unclaimable by the royalty receiver, or the royalty fees are not collected at all during the token sale.
A General Flow
Wrapping
If a user intends to wrap and send their asset to L2 chain, they can call wrap
function in EeseeAssetSpoke and pass the following parameters:
Asset[] assets
- Array of assets to lock in this contract and wrap to L2 chain as ERC1155 tokens. To calculate tokenId before it was minted, simply callgetTokenIdL2(Asset asset)
function in EeseeAssetSpoke orgetTokenId(AddressHashWithSource assetHashWithSource)
in EeseeAssetHub.Call[] crosschainCalls
- Address + bytes payload of calls to make on the destination chain.address fallbackRecipient
- In case crosschainCalls revert on the destination chain, this is the address that will receive the tokens instead.bytes additionalData
- Additional data required for crosschain message. For Chainlink this would beabi.encode(['uint256'], [gasLimit])
. For Axelar -0x
.
After the CCIP or Axelar has finished bridging the assets, it executes all functions provided in crosschainCalls and transfers all leftover tokens to a fallbackRecipient.
Unwrapping
Now, if a user wants to unwrap their tokens back, they have to call unwrap
function with the following parameters:
uint256[] tokenIds
- Array of tokenIds to burn and unwrap. All tokens must have the same source. Note that the caller must either own those tokens, or have them transfered to EeseeAssetHub contract beforehand. !WARNING! Never send any tokens to this contract from an EOA, or by contract in a separate transaction or they will be lost. Only send funds to this contract if you spend them by calling unwrap function in the same transaction.uint256[] amounts
- Amounts of tokens to burn and unwrap.address recipient
- The recipient of assets on destination chain.bytes additionalData
- Additional data required for crosschain message. For Chainlink this would beabi.encode(['uint256'], [gasLimit])
. For Axelar -0x
.
Advanced Flow
Transfer wrapped asset to recipient via CCIP.
! Untested code, use with caution !
List asset on eesee after wrapping it via CCIP
Users can utilize crosschain functionality together with Eesee.sol smart contract.
Example: Wrap asset and create lot in Eesee with it. ! Untested code, use with caution !
Collect and Unwrap via CCIP
Users can also collect assets from eesee and unwrap them to another chain in a single transaction by utilizing eesee's multicall functionality together with callExternal function.
Example: Claim asset and unwrap it to the source chain. ! Untested code, use with caution !
Error Handling and Asset Retrieval
Our team made sure that no assets will be lost if something unexpected happens. In case there is an error during the execution of crosschainCalls on EeseeAssetHub's chain, or the crosschain call execution did not spend all minted ERC1155 tokens, then all leftover ERC1155 tokens will be sent directly to the fallbackRecipient address. In case fallbackRecipient is unable to receive ERC1155 tokens minted by EeseeAssetHub, those tokens will get transfered to a special contract called EeseeVault, where they can be retrieved later by calling function unstuck(uint256[] calldata tokenIds, address recipient)
from fallbackRecipient account. In case an asset got stuck because it failed to get transfered to the recipient during EeseeAssetHub => EeseeAssetSpoke call, the recipient can reclaim it in the EeseeAssetSpoke contract by calling function unstuck(bytes32 stuckAssetHash, address recipient)
.
Wrapped Token Data Resolution
To save on gas for data transfers between chains, all wrapped ERC1155 tokens in EeseeAssetHub collection have the same default image and minimal data. If you would like to retrieve underlying token address, tokenId, amount and assetType, you would have to:
Call
uri(uint256 tokenId)
in EeseeAssetHub. You can also callassetHashesWithSources(uint256 tokenId)
function directly and skip 2.Decode its base64 representation.
Receive
sourceChain
sourceSpoke
andassetHash
parameters from the resulting JSON.Verify
sourceChain
andsourceSpoke
to be authentic contracts build or approved by eesee team.Call
assetsStorage(bytes32 assetHash)
function insourceSpoke
on thesourceChain
with theassetHash
from the previous step. That would produce Asset struct with all the necessary data.In case the received asset is of ERC1155 or ERC721 asset types, you will be able to call
token
'stokenURI()
to receive the token image.
EeseeExpress
While having EeseeAssetHub and EeseeAssetSpoke allows us to transfer any type of assets between chains, the transfers can take up to 30 minutes to process, which might be problematic for cases other than simply wrapping NFTs or creating lots on eesee. To address this problem, we implemented a contract called EeseeExpress, which acts as a wrapper for SquidRouter contract. The wrapper exists to add ERC-2612 permit and to allow transferring tokens to the contract before callBridgeCall function call. Using this contract, users can transfer ERC20 tokens (without wrapping) and arbitrary data for function calls in about 2-3 minutes.
EeseeExpress allows transfering predetermined tokens between chains. If used together with other DEXes it allows the transfer of almost any token on any chain.
!WARNING! Never send any tokens to this contract from an EOA, or by contract in a separate transaction or they will be lost. Only send funds to this contract if you spend them by calling callBridgeCall function in the same transaction.
Interface
To transfer ERC20 tokens and call arbitrary data on both chains, users have to call callBridgeCall function:
AddressWithChain squidRouter - Struct with destination chain and the address of SquidRouter on that chain.
string tokenToSymbol - Token to use in this crosschain transfer.
TokenData tokenFrom - Token to provide to this function to get swapped for tokenTo. Includes abi-encoded permit containing approveAmount, deadline, v, r and s. Set to empty bytes to skip permit.
ISquidMulticall.Call[] calls - Calls to execute on this chain to swap tokenFrom to tokenTo.
ISquidMulticall.Call[] crosschainCalls - Calls to execute on destination chain.
address refundRecipient - In case calls on destination chain fail, this is the address that will receive the tokens instead.
Advanced Actions
Wrap USDC to aUSDC, transfer and unwrap back to USDC
! Untested code, use with caution !
Crosschain Aggregation
Remember we mentioned eesee can act as an NFT marketplace aggrgator? Well, now it can also act a crosschain aggregator using the following flow: ! Untested code, use with caution !
Last updated