Cross chain transfer

Cross chain transfer is one of mostly used cases when it comes to cross chain verification. AElf already supports cross chain transfer functionality in contract. This section will explain how to transfer tokens across chains. It assumes a side chain is already deployed and been indexed by the main chain.

The transfer will always use the same contract methods and the following two steps: - initiate the transfer - receive the tokens

Prepare

Few preparing steps are required before cross chain transfer, which is to be done only once for one chain. Just ignore this preparing part if already completed.

Let’s say that you want to transfer token FOO from chain A to chain B. Note that please make sure you are already clear about how cross chain transaction verification works before you start. Any input containsMerklePath in the following steps means the cross chain verification processing is needed. See cross chain verification for more details.

  • Validate Token Contract address on chain A.

    Send transaction tx_1 to Genesis Contract with method ValidateSystemContractAddress. You should provide system_contract_hash_name and address of Token Contract . tx_1 would be packed in block successfully.

    rpc ValidateSystemContractAddress(ValidateSystemContractAddressInput) returns (google.protobuf.Empty){}
    
    message ValidateSystemContractAddressInput {
        aelf.Hash system_contract_hash_name = 1;
        aelf.Address address = 2;
    }
    
  • Register token contract address of chain A on chain B.

    Create a proposal, which is proposed to RegisterCrossChainTokenContractAddress, for the default parliament organization (check Parliament contract for more details) on chain B. Apart from cross chain verification context, you should also provide the origin data of tx_1 and Token Contract address on chain A.

    rpc RegisterCrossChainTokenContractAddress (RegisterCrossChainTokenContractAddressInput) returns (google.protobuf.Empty) {}
    
    message RegisterCrossChainTokenContractAddressInput{
        int32 from_chain_id = 1;
        int64 parent_chain_height = 2;
        bytes transaction_bytes = 3;
        aelf.MerklePath merkle_path = 4;
        aelf.Address token_contract_address = 5;
    }
    
  • Validate TokenInfo of FOO on chain A.

    Send transaction tx_2 to Token Contract with method ValidateTokenInfoExists on chain A. You should provide TokenInfo of FOO. tx_2 would be packed in block successfully.

    rpc ValidateTokenInfoExists(ValidateTokenInfoExistsInput) returns (google.protobuf.Empty){}
    
    message ValidateTokenInfoExistsInput{
        string symbol = 1;
        string token_name = 2;
        int64 total_supply = 3;
        int32 decimals = 4;
        aelf.Address issuer = 5;
        bool is_burnable = 6;
        int32 issue_chain_id = 7;
    }
    
  • Create token FOO on chain B.

    Send transaction tx_3 to Token Contract with method CrossChainCreateToken on chain B. You should provide the origin data of tx_2 and cross chain verification context of tx_2.

    rpc CrossChainCreateToken(CrossChainCreateTokenInput) returns (google.protobuf.Empty) {}
    
    message CrossChainCreateTokenInput {
        int32 from_chain_id = 1;
        int64 parent_chain_height = 2;
        bytes transaction_bytes = 3;
        aelf.MerklePath merkle_path = 4;
    }
    

Initiate the transfer

On the token contract of source chain, it’s the CrossChainTransfer method that is used to trigger the transfer:

rpc CrossChainTransfer (CrossChainTransferInput) returns (google.protobuf.Empty) { }

message CrossChainTransferInput {
    aelf.Address to = 1;
    string symbol = 2;
    sint64 amount = 3;
    string memo = 4;
    int32 to_chain_id = 5;
    int32 issue_chain_id = 6;
}

The fields of the input:

  • to - the target address to receive token
  • symbol - symbol of token to be transferred
  • amount - amount of token to be transferred
  • memo - memo field in this transfer
  • to_chain_id - destination chain id on which the tokens will be received
  • issue_chain_id - the chain on which the token was issued

Receive on the destination chain

On the destination chain tokens need to be received, it’s the CrossChainReceiveToken method that is used to trigger the reception:

rpc CrossChainReceiveToken (CrossChainReceiveTokenInput) returns (google.protobuf.Empty) { }

message CrossChainReceiveTokenInput {
    int32 from_chain_id = 1;
    int64 parent_chain_height = 2;
    bytes transfer_transaction_bytes = 3;
    aelf.MerklePath merkle_path = 4;
}

rpc GetBoundParentChainHeightAndMerklePathByHeight (aelf.Int64Value) returns (CrossChainMerkleProofContext) {
    option (aelf.is_view) = true;
}

message CrossChainMerkleProofContext {
    int64 bound_parent_chain_height = 1;
    aelf.MerklePath merkle_path_from_parent_chain = 2;
}

Let’s review the fields of the input

  • from_chain_id
the source chain id on which cross chain transfer launched
  • parent_chain_height

    • for the case of transfer from main chain to side chain: this parent_chain_height is the height of the block on the main chain that contains the CrossChainTransfer transaction.
    • for the case of transfer from side chain to side chain or side chain to main-chain: this parent_chain_height is the result of GetBoundParentChainHeightAndMerklePathByHeight (input is the height of the CrossChainTransfer, see cross chain verification) - accessible in the bound_parent_chain_height field.
  • transfer_transaction_bytes

    the serialized form of the CrossChainTransfer transaction.

  • merkle_path

    You should get this from the source chain but merkle path data construction differs among cases.

    • for the case of transfer from main chain to side chain
      • only need the merkle path from the main chain’s web api GetMerklePathByTransactionIdAsync (CrossChainTransfer transaction ID as input).
    • for the case of transfer from side chain to side chain or from side chain to main chain
      • the merkle path from the source chain’s web api GetMerklePathByTransactionIdAsync (CrossChainTransfer transaction ID as input).
      • the output of GetBoundParentChainHeightAndMerklePathByHeight method in Cross chain Contract (CrossChainTransfer transaction’s block height as input). The path nodes are in the merkle_path_from_parent_chain field of the CrossChainMerkleProofContext object.
      • Concat above two merkle path.