Calling nft methods from web

Hello,

I am using following tutorial Mint an NFT — Concordium documentation

I have the contract deployed and tested via cli. I am using following code to invoke the mint from frontend

async function testContractMinting() {
const client = createConcordiumClient(
node.testnet.concordium.com”,
Number(20000),
credentials.createInsecure()
);

const walletFile = readFileSync(“/home/yasir/concordium_dev/contracts/3SfHLNkmy61ZUQkAhMvAwKj47EYDBiUPbn3wHghFD6qGr8WDGc.export”, ‘utf8’);
const wallet = parseWallet(walletFile);
const sender = new AccountAddress(wallet.value.address);
const signer = buildAccountSigner(wallet);

const moduleRef = new ModuleReference(
‘97dc77b5de4c62a659aa31179e20f4968f25d0d1bb839d301b52a8a9f95b1ce7’
);
const maxCost = 30000n;
const contractName = ‘nft_review_2024-04-19’;
const receiveName = ‘nft_review_2024-04-19.mint’;
// const schema = await client.getEmbeddedSchema(moduleRef);

const rawModuleSchema = Buffer.from(readFileSync(
‘/home/yasir/concordium_dev/contracts/nft/nft-review/dist/cis2-nft/schema.bin’
));

const updateHeader: AccountTransactionHeader = {
expiry: new TransactionExpiry(new Date(Date.now() + 3600000)),
nonce: (await client.getNextAccountNonce(sender)).nonce,
sender,
};

const params = {
“owner”: {
“Account”: [“3SfHLNkmy61ZUQkAhMvAwKj47EYDBiUPbn3wHghFD6qGr8WDGc”]
},
“tokens”: [“00000003”]
};
// let params1 = “{ a:1 }”
// const params2 = Buffer.from(params);
const updateParams = serializeUpdateContractParameters(
contractName,
‘mint’,
params,
rawModuleSchema
);

// const inputParams = serializeInitContractParameters(
// contractName,
// params,
// rawModuleSchema,

// );

const updatePayload: UpdateContractPayload = {
amount: new CcdAmount(0n),
address: {“index”:BigInt(8665),“subindex”:BigInt(0)},
receiveName,
message: updateParams,
maxContractExecutionEnergy: maxCost,
};

const updateTransaction: AccountTransaction = {
header: updateHeader,
payload: updatePayload,
type: AccountTransactionType.Update,
};

const updateSignature = await signTransaction(updateTransaction, signer);
const updateTrxHash = await client.sendAccountTransaction(
updateTransaction,
updateSignature
);

console.log(‘Transaction submitted, waiting for finalization…’);

const updateStatus = await client.waitForTransactionFinalization(
updateTrxHash
);
console.dir(updateStatus, { depth: null, colors: true });
return updateStatus;

}

Express is listening at http://localhost:3000
string
/home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@protobuf-ts/grpc-transport/build/commonjs/grpc-transport.js:37
const e = new runtime_rpc_1.RpcError(err.details, grpc_js_1.status[err.code], util_1.metadataFromGrpc(err.metadata));
^

RpcError: Name resolution failed for target dns:node.testnet.concordium.com:20000
at Object.callback (/home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@protobuf-ts/grpc-transport/build/commonjs/grpc-transport.js:37:27)
at Object.onReceiveStatus (/home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@grpc/grpc-js/build/src/client.js:192:36)
at Object.onReceiveStatus (/home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
at Object.onReceiveStatus (/home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
at /home/yasir/concordium_dev/dapps/test-contract-v2/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
code: ‘UNAVAILABLE’,
meta: {},
methodName: ‘GetNextAccountSequenceNumber’,
serviceName: ‘concordium.v2.Queries’
}

I don’t know why the name resolution error happens, the URL seems to be correct and should work fine for the connection.

It appears you use an old version of the SDK though. Could you try to use @concordium/web-sdk@latest instead? This is compatible with nodejs as well, and you can construct a GRPC client by:

import {ConcordiumGRPCNodeClient} from '@concordium/web-sdk/nodejs';
const client = new ConcordiumGRPCNodeClient(...);

You can see the documentation for this here: ConcordiumGRPCNodeClient | Concordium JS-SDK

Hope this helps.

Hello, I decided to use websdk CIS2-Contracts | Concordium JS-SDK

However, I get following error on code

const contractAddressT = {index: 1234n, subindex: 0n};
const contract = await CIS2Contract.create(client, contractAddressT);

Argument of type ‘{ index: bigint; subindex: bigint; }’ is not assignable to parameter of type ‘ContractAddress’.
Property ‘__type’ is missing in type ‘{ index: bigint; subindex: bigint; }’ but required in type ‘ContractAddress’.ts(2345)

How, should I modify the above statement?

The error comes because you likely use a newer version of the @concordium/web-sdk (version 7.0.0 or above).

The required change is e.g.:

const contractAddressT =  ContractAddress.create(1234n, 0n);

Here is a PR where we upgrade to use this newer web-sdk library in case you run into other conversion/upgrade errors in the types:

Thx, that works can i call mint using CIS2Contract?

The CIs2Client supports the entrypoints defined in the CIS-2-standard (this is updateOperator/transfer functions mainly and several read/view functions).

Mint function is not standardized so it can not be part of the general CIS2Client provided in the SDK. You can generate a Contract Client specific to your contract alternatively.

I get following error

   POST https://node.testnet.concordium.com:2000/concordium.v2.Queries/GetInstanceInfo net::ERR_CONNECTION_TIMED_OUT

Code

const address =‘https://node.testnet.concordium.com
const port = Number(2000)

// const [address, port] = parseEndpoint(cli.flags.endpoint);

const client = new ConcordiumGRPCWebClient(
address,
port,
{ timeout: 15000 }
);

The port number should be 20000 (one extra 0).

thx, i get this error now https://node.testnet.concordium.com:20000/concordium.v2.Queries/GetInstanceInfo net::ERR_SSL_PROTOCOL_ERROR

The connection is established in following

const client = new ConcordiumGRPCWebClient(
address,
port,
{ timeout: 15000 }
);
console.log(client)

but when I try to get tokenmeta by using following code, I get the error above

const contractAddressT = ContractAddress.create(xyzn, 0n);
const contract = await CIS2Contract.create(client, contractAddressT);

const tokenId = '00000002'; // HEX string representing a token ID defined in the contract.
const metadataUrl = await contract.tokenMetadata(tokenId);

SSL is not in use on that node, so you need to use ‘http://node.testnet.concordium.com’ as the address (i.e. httpshttp).

1 Like

The tokenmeta works, however, I am trying to transfer command as explained here

const {
type, // The transaction type.
payload, // The transaction payload
parameter: {
json, // The parameter to be supplied to the contract receive function in JSON format.
hex, // The parameter to be supplied to the contract receive function as a hex encoded string
},
schema: {
value, // The contract schema for the parameter. This is needed to translate the JSON format to a byte array.
type, // The type of the schema. This is always ‘parameter’, meaning it can be used for the JSON formatted parameter only.
}
} = contract.createTransfer(
{ energy: 10000n },
transfer
);

I get Cannot redeclare block-scoped variable ‘type’.ts(2451)

The problem here is that when you are destructuring the return value of createTransfer(), the variable name type is being used for both the transaction type and the schema type. You can rename them like this:

const {
  type: transactionType, // The transaction type.
  payload, // The transaction payload
  parameter: {
    json, // The parameter to be supplied to the contract receive function in JSON format.
    hex, // The parameter to be supplied to the contract receive function as a hex encoded string
  },
  schema: {
    value, // The contract schema for the parameter. This is needed to translate the JSON format to a byte array.
    type: schemaType, // The type of the schema. This is always ‘parameter’, meaning it can be used for the JSON formatted parameter only.
  },
} = contract.createTransfer({ energy: 10000n }, transfer);

Thx, I get this

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘length’)
at Function.concat (@concordium_web-sdk.js?v=7f3b1306:470:29)
at serializeAddress (@concordium_web-sdk.js?v=7f3b1306:32172:33)
at serializeCIS2Transfer (@concordium_web-sdk.js?v=7f3b1306:32200:16)
at Array.map ()
at @concordium_web-sdk.js?v=7f3b1306:32195:53
at @concordium_web-sdk.js?v=7f3b1306:10629:47
at _CIS2Contract.createUpdateTransaction (@concordium_web-sdk.js?v=7f3b1306:18243:35)
at _CIS2Contract.createUpdateTransaction (@concordium_web-sdk.js?v=7f3b1306:18401:31)
at _CIS2Contract.createTransfer (@concordium_web-sdk.js?v=7f3b1306:32394:17)
at transferTokens (App.tsx:69:16)

Code

const tokenId = ‘00000003’; // HEX string representing a token ID defined in the contract.
const from = ‘3SfHLNkmy61ZUQkAhMvAwKj47EYDBiUPbn3wHghFD6qGr8WDGc’;
const to = ‘4GyTSko7UZ9iTeUHSLD2c4f3KR6C2nRyyNWbrFMWyC7rRLWDE9’; // An account receiver.
// const to = {address: {index: 1234n, subindex: 0n}, hookName: ‘someReceiveHookName’} // A contract receiver can be specified as such.
const tokenAmount = 100n;
const contractAddressT = ContractAddress.create(8665n, 0n);
const contract = await CIS2Contract.create(client, contractAddressT);

const transfer = { from, to, tokenAmount, tokenId };
// const transfer [{ from, to, tokenAmount, tokenId }, { from, to, tokenAmount, tokenId }] // Example of batch update.

const {
  type:t1, // The transaction type.
  payload, // The transaction payload
  parameter: {
      json, // The parameter to be supplied to the contract receive function in JSON format.
      hex, // The parameter to be supplied to the contract receive function as a hex encoded string
  },
  schema: {
      value, // The contract schema for the parameter. This is needed to translate the JSON format to a byte array.
      type:t2, // The type of the schema. This is always 'parameter', meaning it can be used for the JSON formatted parameter only.
  }

} = contract.createTransfer(
{ energy: 10000n },
transfer
);

The second argument of createTransfer has to be a list, but transfer here is an object. You could change the call to, for instance,

contract.createTransfer({energy: 10000n}, [transfer]);

Or alternatively, keep the call the same, but change the definition of transfer:

const transfer = [{ from, to, tokenAmount, tokenId }];

Thx, however same error

@concordium_web-sdk.js?v=7f3b1306:470 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘length’)
at Function.concat (@concordium_web-sdk.js?v=7f3b1306:470:29)
at serializeAddress (@concordium_web-sdk.js?v=7f3b1306:32172:33)
at serializeCIS2Transfer (@concordium_web-sdk.js?v=7f3b1306:32200:16)
at Array.map ()
at @concordium_web-sdk.js?v=7f3b1306:32195:53
at @concordium_web-sdk.js?v=7f3b1306:10629:47
at _CIS2Contract.createUpdateTransaction (@concordium_web-sdk.js?v=7f3b1306:18243:35)
at _CIS2Contract.createUpdateTransaction (@concordium_web-sdk.js?v=7f3b1306:18401:31)
at _CIS2Contract.createTransfer (@concordium_web-sdk.js?v=7f3b1306:32394:17)
at transferTokens (App.tsx:69:16)

changed call as advised,

const {
  type:t1, // The transaction type.
  payload, // The transaction payload
  parameter: {
      json, // The parameter to be supplied to the contract receive function in JSON format.
      hex, // The parameter to be supplied to the contract receive function as a hex encoded string
  },
  schema: {
      value, // The contract schema for the parameter. This is needed to translate the JSON format to a byte array.
      type:t2, // The type of the schema. This is always 'parameter', meaning it can be used for the JSON formatted parameter only.
  }

} = contract.createTransfer(
{ energy: 10000n },
[transfer]
);

I have a hard time understanding the error you get. You can see a working example by going to the unit tests related to the CIS2 client:

Hope this helps

Thx, I looked into transfer call, in unit test and formatted as follow:

const {
  type:t1, // The transaction type.
  payload, // The transaction payload
  parameter: {
      json, // The parameter to be supplied to the contract receive function in JSON format.
      hex, // The parameter to be supplied to the contract receive function as a hex encoded string
  },
  schema: {
      value, // The contract schema for the parameter. This is needed to translate the JSON format to a byte array.
      type:t2, // The type of the schema. This is always 'parameter', meaning it can be used for the JSON formatted parameter only.
  }

} = contract.createTransfer(
{ energy: Energy.create(1000000) },
{
tokenId: ‘00000003’,
to: AccountAddress.fromBase58(
‘4GyTSko7UZ9iTeUHSLD2c4f3KR6C2nRyyNWbrFMWyC7rRLWDE9’
),
from: AccountAddress.fromBase58(
‘3SfHLNkmy61ZUQkAhMvAwKj47EYDBiUPbn3wHghFD6qGr8WDGc’
),
tokenAmount: 1n,
}
);

There is no error but transfer does not occur either

The createTransfer method creates the necessary parts to send the transaction through a wallet. What is it you want to achieve?

i wish to transfer tokens between two accounts/wallets

Do you want users to be able do this from a web interface, or do you expect to have two wallet exports which you use in nodeJS?

In the case of having a web interface users interact with, check out this guide from the official documentation and try to go from there:

Setting up the front end — Concordium documentation. Make sure to follow the links in the documentation to the repository of the example, as this will include the code necessary to implement applications like this.

Hope this helps

1 Like