1

I am currently trying to setup a project where I intend to have multiple contracts call on each other. Since I am using the openbrush standard (mostly to implement an NFT using PSP34) I thought it would make sense to adhere to the recommended project structure by the openbrush team, since it also makes it easier to cross contract call, compared to what I have seen before when only ink! is used.

However, I now have some problems actually implementing it. For starters (and mostly as an example), I just wanted to implement a simple contract which prints an NFT when deployed and allows the caller to print new ones, if the caller already owns an NFT. So you would need to be "invited" by someone who already has an NFT.

I looked at the example-project-structure which is provided on github and tried to model my approach after that. However, I do not understand a few things, especially in the 'impls' folder, which is supposed to contain the actual implementations of the contract methods.

I get bombarded with a lot of Error messages, some of them I could fix, but some of them I could not figure out the problem.

Here is the code:

pub use crate::traits::nft::*;

use ink_prelude::{
    vec::Vec
};

use openbrush::{
    contracts::traits::psp34::{
        PSP34Ref,
        Id
    },
    traits::{
        AccountId,
    }
};


impl <T> Buy for T {

    default fn _emit_nft_bought(&mut self, _owner: AccountId, _id: Vec<u8>){}

    default fn buy(
        &mut self,
        to: AccountId,
        id: Vec<u8>
    ) -> Result<(), BuyError> {

        if id_exists(&self){
            PSP34Ref::_mint_to(to, Id::Bytes(id.clone()))?;
            self._emit_nft_bought(to, id);
            Ok(())
        }else{
            return Err(BuyError::NoBalance)
        }
    }
}

/// this function just serves as an example
fn id_exists <T> (instance: &T) -> bool{
    if PSP34Ref::balance_of(&instance, Self::env().caller()) > 0{
        true
    }else{
        false
    }
}

And here the errors:

error[E0433]: failed to resolve: use of undeclared type `Self`
  --> contracts/impls/nft/buy.rs:43:40
   |
43 |     if PSP34Ref::balance_of(&instance, Self::env().caller()) > 0{
   |                                        ^^^^ use of undeclared type `Self`

error[E0599]: no associated item named `_mint_to` found for trait object `(dyn PSP34Wrapper + 'static)` in the current scope
  --> contracts/impls/nft/buy.rs:29:23
   |
29 |             PSP34Ref::_mint_to(to, Id::Bytes(id.clone()))?;
   |                       ^^^^^^^^ associated item not found in `(dyn PSP34Wrapper + 'static)`

error[E0277]: the trait bound `&T: PSP34Wrapper` is not satisfied
  --> contracts/impls/nft/buy.rs:43:29
   |
43 |     if PSP34Ref::balance_of(&instance, Self::env().caller()) > 0{
   |                             ^^^^^^^^^ the trait `PSP34Wrapper` is not implemented for `&T`
   |
   = help: the trait `PSP34Wrapper` is implemented for `<DefaultEnvironment as Environment>::AccountId`
   = note: required for the cast to the object type `dyn PSP34Wrapper`

In the example provided by the team behind openbrush, they are using Self::env().caller(), so I don't really understand why this is not accepted here. The other two errors I am a bit lost, where the origin of the error lies and what is necessary to fix them.

1 Answer 1

1

The first error you get is because the function id_exists is not described in an implementation of a struct, therefore no Self is found.

The second error means that there is no function _mint_to in PSP34Wrapper. PSP34Wrapper is a way how to do a cross-contract call in OB, but you can only do calls to public functions of a contract, _mint_to is a private function in PSP34. You can add the mint functionality to your contract with the PSP34Mintable extension (but since you only want to call this function from the contract you don't need that, you just need to specify that T is PSP34).

The third error means you are providing a wrong argument to the PSP34Wrapper. The first argument to a wrapper call in OB is the reference to the account id we want to make the call to.

So I guess you want to do something like this:

impl <T: PSP34> Buy for T {

    default fn _emit_nft_bought(&mut self, _owner: AccountId, _id: Vec<u8>){}

    default fn buy(
        &mut self,
        to: AccountId,
        id: Vec<u8>
    ) -> Result<(), BuyError> {
        if self.id_exists() {
            self._mint_to(to, Id::Bytes(id.clone()))?;
            self._emit_nft_bought(to, id);
            Ok(())
        } else {
            return Err(BuyError::NoBalance)
        }
    }

    /// this function just serves as an example
    default fn id_exists (&self, account: AccountId) -> bool{
        if self.balance_of(Self::env().caller()) > 0 {
            true
        } else {
            false
        }
    }
}

I haven't tried to compile it, so it may not, but it could serve as a good jump-off point for you hopefully :D

So what we did there is that we specify that T also implements PSP34, and now we can call PSP34 functions on self. You don't need cross-contract calls (correct me if I'm wrong). Anyway, if you want for example to mint another nft, you would add the mintable extension to the nft and in your contract create a wrapper

pub type Wrapper = dyn PSP34Mintable

by which you can then call the public functions from PSP34Mintable for example like this

Wrapper::mint(&account_id_of_nft, to, id)

which would call the mint function of the specified contract (if there is such function). The mint extension is here if you would be interested.

Hope I helped at least a little :D

4
  • The answer you provided set me on the right track to get the contract to compile. However I don't want to have the id_exists function public. I want it to be only callable by this contract. The only thing which needs to be changed is importing contracts::psp34::Internal and declaring T: PSP34 + Internal
    – rajohs
    Sep 9, 2022 at 23:27
  • For the Self::env().caller() it is also necessary to import trats::DefaultEnv
    – rajohs
    Sep 9, 2022 at 23:29
  • if you do not define the function as #[ink(message)] it will not be public, however using your approach you need to define that T is PSP34 in the id_exists fn, you can then call instance.balance_of(T::env().caller()), but if you want to use env access for T, it also needs to implement the Storage trait from openbrush
    – kriko727
    Sep 10, 2022 at 0:01
  • Your correct about that. Thx.
    – rajohs
    Sep 10, 2022 at 0:07

Your Answer

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

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