No valid tokens found in contract

I have succesfully minted an Semi fungible token to a wallet but when I try to import the contract index on the same wallet that minted the token it shows that No valid tokens found in contract.

I am making use of the semi fungible smart contract https://github.com/Concordium/concordium-rust-smart-contracts/tree/main/examples/cis2-multi.

This is the txn hash f20ab169061a41c1483a0d92f8a06ed13022124731485bf13ac1582ce655cf5c

and this is the contract_mint function

   fn mint(
        &mut self,
        token_id: &ContractTokenId,
        metadata_url: &MetadataUrl,
        owner: &Address,
        amount: ContractTokenAmount,
        state_builder: &mut StateBuilder,
    ) -> MetadataUrl {
        let token_metadata = self.tokens.get(token_id).map(|x| x.to_owned());
        if token_metadata.is_none() {
            self.tokens.insert(*token_id, metadata_url.to_owned());
        }

        let mut owner_state =
            self.state.entry(*owner).or_insert_with(|| AddressState::empty(state_builder));

        let mut owner_balance = owner_state.balances.entry(*token_id).or_insert(0.into());

        *owner_balance += amount;

        if let Some(token_metadata) = token_metadata {
            token_metadata
        } else {
            metadata_url.clone()
        }
    }

It might be that you don’t log required events as specified by the CIS2 standard.
https://proposals.concordium.software/CIS/cis-2.html

The wallet will look for such events to realize that a new token was minted in the smart contract.
Your metadata URL should link to a valid file that includes the metadata to your token.

    // Event for minted token.
    logger.log(&Cis2Event::Mint(MintEvent {
        token_id: params.token_id,
        amount:   state.mint_airdrop,
        owner:    params.owner,
    }))?;

    // Metadata URL for the token.
    logger.log(&Cis2Event::TokenMetadata::<_, ContractTokenAmount>(TokenMetadataEvent {
        token_id:     params.token_id,
        metadata_url: token_metadata,
    }))?;
1 Like

As @Doris said, from the function you posted above you are not logging any events when minting new tokens.

1 Like

Hello @Doris, I’ve addressed the problem by updating the reference to a valid metadata. However, the issue persists. I’ve provided a transaction hash (74b33aa67de3daccebf2e116bd35574fef91730081a851341988407433382284 ) that points to a valid metadata, yet I’m still unable to import the token.

I suspect that the mint/metadata events are not logged according to the standard. One way to investigate the issue further would be to embed the event schema in the contract so that the block explorer can decode the event details (currently the logs are hex stings). That would help us to understand which events your contract are logging.

Contract <8136,0>

How to embed the event schema:

Then compile the contract with the embedded schema flag:

cargo concordium build -e

You can then deploy the module with the embedded schema to chain and initialize a contract, after minting some tokens in the new contract, the blockexplorer should display the events in a human-readable fashion.

There are two tokens picked up. Do you have problems importing these two tokenIDs (tokenID 01 and tokenID 02)?

https://testnet.ccdscan.io/contracts?dcount=1&dentity=contract&dcontractAddressIndex=8136&dcontractAddressSubIndex=0

Hi @Doris, I have tried many times to resolve this, the issue isn’t from the event as I can see that this is available on the contract_init.

this is what is available on the mint function and it has log that emits the event


fn mint(
    params: MintParams,
    host: &mut Host<State>,
    logger: &mut impl HasLogger,
) -> ContractResult<()> {
    let is_blacklisted = host.state().blacklist.contains(&get_canonical_address(params.owner)?);

    // Check token owner is not blacklisted.
    ensure!(!is_blacklisted, CustomContractError::Blacklisted.into());

    // Check that contract is not paused.
    ensure!(!host.state().paused, CustomContractError::Paused.into());

    let (state, builder) = host.state_and_builder();

    // ensure!(
    //     !state.tokens.contains_key(&params.token_id),
    //     CustomContractError::AlreadyMinted.into()
    // );

    // Mint the token in the state.
    let token_metadata = state.mint(
        &params.token_id,
        &params.metadata_url,
        &params.owner,
        params.amount,
        builder,
    );

    // Event for minted token.
    logger.log(&Cis2Event::Mint(MintEvent {
        token_id: params.token_id,
        amount:   params.amount,
        owner:    params.owner,
    }))?;

    // Metadata URL for the token.
    logger.log(&Cis2Event::TokenMetadata::<_, ContractTokenAmount>(TokenMetadataEvent {
        token_id:     params.token_id,
        metadata_url: token_metadata,
    }))?;

    Ok(())
}

additionally, I have this fn mint

  fn mint(
        &mut self,
        token_id: &ContractTokenId,
        metadata_url: &MetadataUrl,
        owner: &Address,
        amount: ContractTokenAmount,
        state_builder: &mut StateBuilder,
    ) -> MetadataUrl {
        let token_metadata = self.tokens.get(token_id).map(|x| x.to_owned());
        if token_metadata.is_none() {
            self.tokens.insert(*token_id, metadata_url.to_owned());
        }

        let mut owner_state =
            self.state.entry(*owner).or_insert_with(|| AddressState::empty(state_builder));

        let mut owner_balance = owner_state.balances.entry(*token_id).or_insert(0.into());

        *owner_balance += amount;

        if let Some(token_metadata) = token_metadata {
            token_metadata
        } else {
            metadata_url.clone()
        }
    }

I think this is help in regards to checking if something was not done right.

I tried to import your contract on the Android Concordium testnet wallet and its error is “invalid checksum”. Can you please douple-check the hash in the metadata if it is correct?

[
  {
    "hash": {
      "Some": [
        "f982b4603b9c116598d381d1aee90a7b5332377b209b6bbd8d7a4dfab18f2a80"
      ]
    },
    "url": "https://ipfs.io/ipfs/QmdXoCHXLCh7ATA899wgCsM5qW1gMUYueN8E5v6TZhRqae"
  }
]

I’m currently using sha256 from crypto-js/sha256 to hash the MetaData

 const metaData = {
      "name": "01",
      "unique": true,
      "description": "description",
      "display": {
          "url": "https://ipfs.io/ipfs/QmUV8MenT1t2gxbzXpNgRkcqh97fNmPC4GxvykJNcc9q3t"
      },
      "attributes": [
          {
              "type": "01",
              "name": "01",
              "value": "1710431580000"
          }
      ]
  }

    const hash = await getContentAndCalculateHash( metaData );

    const mintData = {
      token_id,
      metadata_url: {
        url: https://ipfs.io/ipfs/${params?.hash},
        hash: { Some: [hash] },
      },
      owner: {
        Account: [caccount],
      },
      amount: '1',
    };

This is what I currently use and the hash is generated before performing a mint action.

Please investigate the smart contract <8364, 0>.

Thank you!.

Using the CIS-2 spec to decode the checksum hash for tx_hash 992584364c9cd89843e8ac3a23a8a50e27917fd6b266024bfb2db7b777f595fa , I get:

b'\xf9\x82\xb4`;\x9c\x11e\x98\xd3\x81\xd1\xae\xe9\n{S27{ \x9bk\xbd\x8dzM\xfa\xb1\x8f*\x80'

which does seem a bit odd? I can’t decode this.

Can you give a detailed explanation to what you’re trying to say?, from my conversation with @Doris, we are trying to figure out why the wallets are not able to import the index of an SFT contract. And the metadata of the txn is hashed using sha256.

I think your checksum generation is off and doesn’t adhere to the standard (as also indicate by the error message in the wallet)?

Referencing my solution to generating a hash using sha256, do you think I’m doing anything wrong, this is the most recent transactions I have that uses the auto generated hash from the metaData

15b13a90a53296536711c5f61aaee2fa4cccec3ec7f0830186e8e663a0009b2a

@Doris and @sderuiter, this is the smart contract <8364, 0>

@Doris , to add to it, I just deployed another contract with the embedded schema <8416, 0>, with the txn hash of 85da0900c0877d2810abdecce83029761880e4a5429c329dd920cf4dcdbf783b

The checksum I can read from this tx is:
4ea5c508a6566e76240543f8feb06fd457777be39549c4016436afda65d2330e
Does that match yours?

You can either calculate the hash:

{
    "hash": {
          "Some": [
            "0361ed63df8887211aa9278446c1c3fd4ef5f4e5269091343b536882e2079329"
          ]
    },
    "url": "https://ipfs.io/ipfs/QmRP2Uuz3xjFCYbzYLrwmRiaGULQjncKk8nMQcjWZxbsjS"
  }

or use None:

{
    "hash": {
          "None": [ ]
    },
    "url": "https://ipfs.io/ipfs/QmRP2Uuz3xjFCYbzYLrwmRiaGULQjncKk8nMQcjWZxbsjS"
  }

Hi @Doris, I used none, but there must be something wrong in using the Some hash, may I know how you generated the hash from your end?

const hashObject = (val: string | undefined) =>
    crypto.subtle
        .digest('SHA-256', new TextEncoder().encode(val))
        .then(h => {
            let hexes = [],
                view = new DataView(h);
            for (let i = 0; i < view.byteLength; i += 4)
                hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
            return hexes.join('');
        });
let test = { "name": "01", "unique": true, "description": "", "display": { "url": "https://ipfs.io/ipfs/QmUV8MenT1t2gxbzXpNgRkcqh97fNmPC4GxvykJNcc9q3t" }, "attributes": [{ "type": "01", "name": "01", "value": "1711201200000" }] }
hashObject(
     JSON.stringify(test)
).then(console.log);
1 Like