IBC information and ICS20 interface

Channel lifecycle management

Channel State Machine

ICS20 interface

1. ink! interface

        /// Token transfer. This allows us to transfer exactly one native token.
        pub fn execute_transfer(
            &self,
            msg: TransferMsg,
            amount: Amount,
            sender: Addr,
        ) -> Result<Response, Error> ;


        /// Return the port ID bound by this contract.
        pub fn query_port(&self) -> PortResponse ;

2. PSP37 interface

(open branch ink! ERC-1155: Multi Token Standard for Substrate's contracts pallet)

/// Contract module that provides a basic implementation of multiple token types.
/// A single deployed contract can include any combination of fungible tokens,
/// non-fungible tokens, or other configurations (e.g., semi-fungible tokens).
#[openbrush::trait_definition]
pub trait PSP37 {
    /// Returns the number of tokens of the token type `id` owned by `account`.
    ///
    /// If `id` is `None`, it returns the total number of tokens owned by `owner`.
    #[ink(message)]
    fn balance_of(&self, owner: AccountId, id: Option<Id>) -> Balance;

    /// Returns the total number of tokens of type `id` in the supply.
    ///
    /// If `id` is `None`, it returns the total number of tokens.
    #[ink(message)]
    fn total_supply(&self, id: Option<Id>) -> Balance;

    /// Returns the amount of `id` token that `owner` allows `operator` to withdraw.
    /// If `id` is `None`, it returns the allowance `Balance::MAX` for all tokens of `owner`.
    #[ink(message)]
    fn allowance(&self, owner: AccountId, operator: AccountId, id: Option<Id>) -> Balance;

    /// Allows `operator` to withdraw the `id` token from the caller's account
    /// multiple times, up to the `value` amount.
    /// If this function is called again, it overwrites the current allowance with `value`.
    /// If `id` is `None`, it approves or disapproves the operator for all tokens of the caller.
    ///
    /// An `Approval` event is emitted.
    #[ink(message)]
    fn approve(&mut self, operator: AccountId, id: Option<Id>, value: Balance) -> Result<(), PSP37Error>;

    /// Transfers the `value` of the `id` token from the `caller` to `to`.
    ///
    /// On success, a `TransferSingle` event is emitted.
    ///
    /// # Errors
    ///
    /// Returns a `TransferToZeroAddress` error if the recipient is a zero account.
    ///
    /// Returns a `NotAllowed` error if the transfer is not approved.
    ///
    /// Returns an `InsufficientBalance` error if the `caller` doesn't have enough balance.
    ///
    /// Returns a `SafeTransferCheckFailed` error if `to` doesn't accept the transfer.
    #[ink(message)]
    fn transfer(&mut self, to: AccountId, id: Id, value: Balance, data: Vec<u8>) -> Result<(), PSP37Error>;

    /// Transfers `amount` tokens of the token type `id` from `from` to `to`. Additional `data` can also be passed.
    ///
    /// On success, a `TransferSingle` event is emitted.
    ///
    /// # Errors
    ///
    /// Returns a `TransferToZeroAddress` error if the recipient is a zero account.
    ///
    /// Returns a `NotAllowed` error if the transfer is not approved.
    ///
    /// Returns an `InsufficientBalance` error if `from` doesn't have enough balance.
    ///
    /// Returns a `SafeTransferCheckFailed` error if `to` doesn't accept the transfer.
    #[ink(message)]
    fn transfer_from(
        &mut self,
        from: AccountId,
        to: AccountId,
        id: Id,
        amount: Balance,
        data: Vec<u8>,
    ) -> Result<(), PSP37Error>;
}

3. Struct

    pub struct Addr(String);

    pub struct Coin {
        pub denom: String,
        pub amount: u128,
    }

    pub struct Cw20Coin {
        pub address: String,
        pub amount: u128,
    }

    pub struct TransferMsg {
        /// The local channel on which to send the packets.
        pub channel: String,
        /// The remote address to which the data should be sent.
        /// Do not use HumanAddress as it might have a different Bech32 prefix than we use,
        /// and it cannot be validated locally.
        pub remote_address: String,
        /// The duration (in seconds) for which the packet remains alive. If not specified, use default_timeout.
        pub timeout: Option<u64>,
        /// An optional memo to add to the IBC transfer.
        pub memo: Option<String>,
    }


    pub enum Amount {
        Native(Coin),
        Cw20(Cw20Coin),
    }

    impl Amount {
        pub fn from_parts(denom: String, amount: u128) -> Self {
            if denom.starts_with("cw20:") {
                let address = denom.get(5..).unwrap().into();
                Amount::Cw20(Cw20Coin { address, amount })
            } else {
                Amount::Native(Coin { denom, amount })
            }
        }

        pub fn cw20(amount: u128, addr: &str) -> Self {
            Amount::Cw20(Cw20Coin {
                address: addr.into(),
                amount: amount,
            })
        }

        pub fn native(amount: u128, denom: &str) -> Self {
            Amount::Native(Coin {
                denom: denom.to_string(),
                amount: amount,
            })
        }
    }

    impl Amount {
        pub fn denom(&self) -> String {
            match self {
                Amount::Native(c) => c.denom.clone(),
                Amount::Cw20(c) => "cw20:".to_owned() + c.address.as_str(),
            }
        }

        pub fn amount(&self) -> u128 {
            match self {
                Amount::Native(c) => c.amount,
                Amount::Cw20(c) => c.amount,
            }
        }

        /// Convert the amount into u64.
        pub fn u64_amount(&self) -> Result<u64, Error> {
            Ok(self.amount().try_into().unwrap())
        }

        pub fn is_empty(&self) -> bool {
            match self {
                Amount::Native(c) => c.amount == 0,
                Amount::Cw20(c) => c.amount == 0,
            }
        }
    }

4. Errors

pub enum Error {
        /// StdError
        StdError,
        /// PaymentError
        PaymentError,
        /// AdminError
        AdminError,
        /// #[error("Channel doesn't exist: {id}")]
        NoSuchChannel { id: String },
        /// #[error("Didn't send any funds")]
        NoFunds {},
        /// #[error("Amount larger than 2**64, not supported by ics20 packets")]
        AmountOverflow {},
        /// #[error("Only supports channel with ibc version ics20-1, got {version}")]
        InvalidIbcVersion { version: String },
        /// #[error("Only supports unordered channel")]
        OnlyOrderedChannel {},
        /// #[error("Insufficient funds to redeem voucher on channel")]
        InsufficientFunds {},
        /// #[error("Only accepts tokens that originate on this chain, not native tokens of remote chain")]
        NoForeignTokens {},
        /// #[error("Parsed port from denom ({port}) doesn't match packet")]
        FromOtherPort { port: String },
        /// #[error("Parsed channel from denom ({channel}) doesn't match packet")]
        FromOtherChannel { channel: String },
        /// #[error("Cannot migrate from different contract type: {previous_contract}")]
        CannotMigrate { previous_contract: String },
        /// #[error("Cannot migrate from unsupported version: {previous_version}")]
        CannotMigrateVersion { previous_version: String },
        /// #[error("Got a submessage reply with unknown id: {id}")]
        UnknownReplyId { id: u64 },
        /// #[error("You cannot lower the gas limit for a contract on the allow list")]
        CannotLowerGas,
        /// #[error("Only the governance contract can do this")]
        Unauthorized,
        /// #[error("You can only send cw20 tokens that have been explicitly allowed by governance")]
        NotOnAllowList,
    }

References

IBC Interfaces for CosmWasm Contracts

Contracts Semantics

Introduction to CosmWasm

Channel and Packet Semantics

PSP37 Protocol

PSP37 Trait

results matching ""

    No results matching ""