api
api copied to clipboard
How to decode extrinsic input using api-contract?
Background: Polkaholic would like to support wasm contract UI + indexing the on-chain contract-call. Is there a way to decode raw extrinsic input using the api-contract package if contract's metadata.json is known?
For example, I've deployed a sample psp22 contract on shiden network (https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.shiden.astar.network#/contracts)
Steps I took:
-
(1) Deploying the PSP22-META contract (contract metadata is published on gist) Extrinsic: https://polkaholic.io/tx/0x2c986a6cb47b94a9e50f5d3f660e0f37177989594eb087bf7309c2e15e2340c8 Deployed Contract Address:
Xd87RRvCB7JuF3LUYvhNRwgV9WfhkhUJApj45EP5dcDrSi1
-
(2) psp22 token transfer: Sending 1.23456e-13 WLK (decimals 18) from
Z4wzSczja5Va2wAsVJEoiLK9i882FUjWWdDnZDrtPzxYp8z
toWwjA4NLgbAKgapEvKf86LNk7gcqSQhsNUTN2AXSc6JPjQf4
Extrinsic: https://polkaholic.io/tx/0xa0c23aa9bbb27e18bf48d7df1b51d83fb8be73ed111bd375b40f2ab02f281554 Encode data from Extrinsic:0xdb20f9f52c8feeab5bd9a317375e01adb6cb959f1fea78c751936d556fa2e36ede425a4740e2010000000000000000000000000000
Question:
How to use api-contract package to decode contract:call
input like 0xdb20f9f52c8feeab5bd9a317375e01adb6cb959f1fea78c751936d556fa2e36ede425a4740e2010000000000000000000000000000
given the metadata/selector?
{
"args": [
{
"label": "to",
"type": {
"displayName": [
"psp22_external",
"TransferInput1"
],
"type": 2
}
},
{
"label": "value",
"type": {
"displayName": [
"psp22_external",
"TransferInput2"
],
"type": 0
}
},
{
"label": "data",
"type": {
"displayName": [
"psp22_external",
"TransferInput3"
],
"type": 16
}
}
],
"docs": [
" Transfers `value` amount of tokens from the caller's account to account `to`",
" with additional `data` in unspecified format.",
"",
" On success a `Transfer` event is emitted.",
"",
" # Errors",
"",
" Returns `InsufficientBalance` error if there are not enough tokens on",
" the caller's account Balance.",
"",
" Returns `ZeroSenderAddress` error if sender's address is zero.",
"",
" Returns `ZeroRecipientAddress` error if recipient's address is zero."
],
"label": "PSP22::transfer",
"mutates": true,
"payable": false,
"returnType": {
"displayName": [
"psp22_external",
"TransferOutput"
],
"type": 14
},
"selector": "0xdb20f9f5"
}
const {
ApiPromise,
WsProvider,
Keyring
} = require("@polkadot/api");
const {
CodePromise,
ContractPromise
} = require('@polkadot/api-contract');
const fs = require("fs");
async function main() {
let chainID = 22007;
let endpoint = "wss://shiden.api.onfinality.io/public-ws";
const provider = new WsProvider(endpoint);
provider.on('disconnected', () => {
console.log('CHAIN API DISCONNECTED', chainID);
});
provider.on('connected', () => console.log('chain API connected', chainID));
provider.on('error', (error) => console.log('chain API error', chainID, error));
//const options = require("@astar-network/astar-api");
let api = await ApiPromise.create({
provider: provider
});
console.log(`You are connected to ASTAR/SHIDEN chain ${chainID} endpoint=${endpoint} with options`);
let metadata = JSON.parse(fs.readFileSync("my_psp22/metadata.json"));
let wasm = fs.readFileSync("my_psp22/my_psp22_metadata.wasm");
// 2. read "val" (should be true) from contract address
let deployerAddress = "Z4wzSczja5Va2wAsVJEoiLK9i882FUjWWdDnZDrtPzxYp8z"
let address = "Xd87RRvCB7JuF3LUYvhNRwgV9WfhkhUJApj45EP5dcDrSi1"
console.log(address, metadata);
const contract = new ContractPromise(api, metadata, address);
let q = "psp22::balanceOf"
console.log(contract.query[q].meta)
let f = contract.query[q];
let gasLimit = 3000n * 1000000n;
let storageDepositLimit = null;
let from = "WwjA4NLgbAKgapEvKf86LNk7gcqSQhsNUTN2AXSc6JPjQf4";
const { gasRequired, storageDeposit, result, output } = await f(deployerAddress, { gasLimit: -1, storageDepositLimit }, from);
if ( result.isOk ) {
console.log('success', output.toHuman());
} else {
console.log('error', result.asErr.toHuman());
}
//How to decode contract.call (Z4wzSczja5Va2wAsVJEoiLK9i882FUjWWdDnZDrtPzxYp8z send 1.23456e-13 WLK (decimals 18) to WwjA4NLgbAKgapEvKf86LNk7gcqSQhsNUTN2AXSc6JPjQf4)
//`0xdb20f9f52c8feeab5bd9a317375e01adb6cb959f1fea78c751936d556fa2e36ede425a4740e2010000000000000000000000000000`
contract.abi.decodeMessage('0xdb20f9f52c8feeab5bd9a317375e01adb6cb959f1fea78c751936d556fa2e36ede425a4740e2010000000000000000000000000000')
/* Error msg:
Uncaught Error: Number can only safely store up to 53 bits
at assert (/root/node_modules/@polkadot/api-contract/node_modules/bn.js/lib/bn.js:6:21)
at BN.toNumber (/root/node_modules/@polkadot/api-contract/node_modules/bn.js/lib/bn.js:547:7)
at compactStripLength (/root/node_modules/@polkadot/api-contract/node_modules/@polkadot/util/cjs/compact/stripLength.js:27:33)
at Abi.#decodeMessage (/root/node_modules/@polkadot/api-contract/cjs/Abi/index.js:229:54)
at Abi.decodeMessage (/root/node_modules/@polkadot/api-contract/cjs/Abi/index.js:122:31)
}
*/
main()
.then(() => process.exit(0))
.catch((e) => {
console.error('ERROR', e);
process.exit(1);
});
The api-contract doc doesn't show how to decode it
-
Environment:
- [X] Node.js
- [ ] Browser
- [ ] Other (limited support for other environments)
-
Language:
- [X] JavaScript
- [ ] TypeScript (include tsc --version)
- [ ] Other
This issue has been open for 21 days with no activity and is not labelled as an enhancement. It will be closed in 7 days.
To decode the message, use the decodeMessage
method of the abi
parameter of ContractPromise
object, eg to decode
the transfer
contract call
https://polkaholic.io/tx/0x02e86275ad0f6129077b557042aca2fea5fad97c02fa75230ad17e08fd2b2eec
let address = "Xd87RRvCB7JuF3LUYvhNRwgV9WfhkhUJApj45EP5dcDrSi1"
const contract = new ContractPromise(api, metadata, address);
let decodedMessage = contract.abi.decodeMessage(compactAddLength(hexToU8a("0xdb20f9f5e6b912626c9dfa3cd9e65b4412b19eb9d123edb1aa22d492a58a88091c483a7a5304000000000000000000000000000020636f6e6772617473")));
decodedMessage.args.map( (a)=> {
console.log(a.toHuman());
})
which outputs the arguments to your transfer tx:
b9pHTRUj5uJqRTdX5pjixjigLxfEDP16Enu9EbPAot5guhx
1,107
congrats
It was necessary to look inside the api-contracts implementation of decodeMessage
to know that it was essential to use compactAddLength
and hexToU8a
. I imagine a more flexible implementation would accept hex strings maybe?
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue if you think you have a related problem or query.