1

I want to make token/nft transfer inside smart contracts. I saw some examples of token transfers and taking that reference, I tried to replicate something similar to that. here is the code.

#![no_std]

pub use erc721::Erc721;
use ink_lang as ink;

#[ink::contract]
mod erc721{
    use scale::{Encode, Decode};
    pub type TokenId = u32;

    #[ink(storage)]
    pub struct Erc721{}

    #[derive(Encode, Decode, Debug, PartialEq, Eq, Copy, Clone)]
    #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
    pub enum Error {
        NotOwner,
        NotApproved,
        TokenExists,
        TokenNotFound,
        CannotInsert,
        CannotFetchValue,
        NotAllowed,
    }

    impl Erc721{
        #[ink(constructor)]
        pub fn new() -> Self{
            unimplemented!()
        }
        #[ink(message)]
        pub fn balance_of(&self, _owner: AccountId) -> u32 {
            unimplemented!()
        }

        /// Returns the owner of the token.
        #[ink(message)]
        pub fn owner_of(&self, _id: TokenId) -> Option<AccountId> {
            unimplemented!()
        }

        /// Returns the approved account ID for this token if any.
        #[ink(message)]
        pub fn get_approved(&self, _id: TokenId) -> Option<AccountId> {
            unimplemented!()
        }

        /// Returns `true` if the operator is approved by the owner.
        #[ink(message)]
        pub fn is_approved_for_all(&self, _owner: AccountId, _operator: AccountId) -> bool {
            unimplemented!()
        }

        /// Approves or disapproves the operator for all tokens of the caller.
        #[ink(message)]
        pub fn set_approval_for_all(
            &mut self,
            _to: AccountId,
            _approved: bool,
        ) -> Result<(), Error> {
            unimplemented!()
        }

        /// Approves the account to transfer the specified token on behalf of the caller.
        #[ink(message)]
        pub fn approve(&mut self, _to: AccountId, _id: TokenId) -> Result<(), Error> {
            unimplemented!()
        }

        /// Transfers the token from the caller to the given destination.
        #[ink(message)]
        pub fn transfer(
            &mut self,
            _destination: AccountId,
            _id: TokenId,
        ) -> Result<(), Error> {
            unimplemented!()
        }

        /// Transfer approved or owned token.
        #[ink(message)]
        pub fn transfer_from(
            &mut self,
            _from: AccountId,
            _to: AccountId,
            _id: TokenId,
        ) -> Result<(), Error> {
            unimplemented!()
        }

        /// Creates a new token.
        #[ink(message)]
        pub fn mint(&mut self, _id: TokenId) -> Result<(), Error> {
            unimplemented!()
        }

        /// Deletes an existing token. Only the owner can burn the token.
        #[ink(message)]
        pub fn burn(&mut self, _id: TokenId) -> Result<(), Error> {
            unimplemented!()
        }
    }
}

I'm getting error on this part let token: Erc721 = FromAccountId::from_account_id(contract);

here is the error

the trait bound `Erc721: FromAccountId<_>` is not satisfied
the trait `FromAccountId<_>` is not implemented for `Erc721`

here is my contract that is calling the function.

#![no_std]

use ink_lang as ink;

#[ink::contract]
mod ebisu {
    use erc721::Erc721;
    use ink_env::call::FromAccountId;
    use ink_storage::{Mapping, traits::SpreadAllocate};
    type TokenId = u32;

    #[ink(storage)]
    #[derive(SpreadAllocate)]
    pub struct Ebisu{
        assets: Mapping<(AccountId, TokenId), AccountId>
    }

    impl Ebisu{
        #[ink(constructor)]
        pub fn init() -> Self{
            ink_lang::utils::initialize_contract(|_|{

            })
        }

        #[ink(message)]
        pub fn check(&self, contract: AccountId) -> AccountId{
            let token: Erc721 = FromAccountId::from_account_id(contract);
            contract
        }
    }
}
5
  • If I understand correctly your error is in instantiating the contract, could you show that part of code? And btw if you are writing ink! smart contracts, I recommend you use OpenBrush library
    – kriko.eth
    Commented May 17, 2022 at 9:00
  • can you please point out, where is the error? I don't have any idea which part is generating error.
    – pramitgaha
    Commented May 17, 2022 at 9:12
  • are you saying that, error is getting generated due to missing implementation of trait FromAccountId ?
    – pramitgaha
    Commented May 17, 2022 at 9:15
  • If the answer is yes, I saw other contracts also doing the same. They were also not implementing that trait.
    – pramitgaha
    Commented May 17, 2022 at 9:18
  • I've edited the post, added the code that is calling the contract. should I share Cargo.toml code too? @Coreggon
    – pramitgaha
    Commented May 17, 2022 at 9:50

1 Answer 1

4

The answer to your question:

Right now ink! generates a separate structure for cross-contract calls. It has the name {}Ref where {} is the name of the contract struct. To use it, you need to export that:

...
pub use erc721::{ Erc721, Erc721Ref };
use ink_lang as ink;

#[ink::contract]
mod erc721{
...
}

And use it:

...
#[ink::contract]
mod ebisu {
    use erc721::Erc721Ref;
    use ink_env::call::FromAccountId;
    ...

    #[ink(storage)]
    #[derive(SpreadAllocate)]
    pub struct Ebisu{
        assets: Mapping<(AccountId, TokenId), AccountId>
    }

    impl Ebisu{
        ...

        #[ink(message)]
        pub fn check(&self, contract: AccountId) -> AccountId{
            let token: Erc721Ref = FromAccountId::from_account_id(contract);
            ...
        }
    }
}

But I suggest you use the PSP34 standard for non-fungible tokens to be compatible with other NFT in the network. If all NFT implements the same interface anyone can work with a pre-defined API.

As a variant, you can use compatible to PSP the trait from OpenBrush. You can implements the trait manually, or you can reuse the implementation, or you can use that trait for cross contract calls.

With wapper feature you can do cross-contract calls to any NFT:


...
#[ink::contract]
mod ebisu {
    use brush::contracts::traits::psp34::PSP34Ref;
    use ink_env::call::FromAccountId;
    ...

    #[ink(storage)]
    #[derive(SpreadAllocate)]
    pub struct Ebisu{
        assets: Mapping<(AccountId, TokenId), AccountId>
    }

    impl Ebisu{
        ...

        #[ink(message)]
        pub fn check_balance(&self, nft: AccountId, some_account: AccountId) -> AccountId{
            let balance = PSP34Ref::balance_of(&nft, some_account);
            ...
        }
    }
}
6
  • I saw that in psp34 contract there is no transfer_from function. how would I create a deposit function for user to deposit into smart contract ? can you give me idea?
    – pramitgaha
    Commented May 17, 2022 at 12:35
  • You don't need transfer_from. You can read the PSP description. NFT has only one owner. If someone tries to transfer it, we already know from who we need to transfer=) Commented May 17, 2022 at 15:22
  • rust pub fn deposit(&mut self, nft_contract: AccountId, token_id: u32, to: AccountId) { let res = PSP34Ref::transfer(&nft_contract, to, Id::u32(token_id), vec![99, 99]); } so if I create a function like above, transfer function will be called by the function caller by itself?
    – pramitgaha
    Commented May 17, 2022 at 15:50
  • No, the function is called by your contract. Your contract sends a cross-contract call, so ink_end::caller() is Ebisu contract. If Ebisu has the allowance to do a transfer, it will be done. Commented May 17, 2022 at 17:30
  • if the user calls the above function that I have written, will user get the message for accepting the transfer made by smart contract on behalf of the user ?
    – pramitgaha
    Commented May 18, 2022 at 5:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.