Skip to content

Instantly share code, notes, and snippets.

@xcaptain
Created November 17, 2021 13:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xcaptain/4d190232411dcf27441d9fadd7ff6988 to your computer and use it in GitHub Desktop.
Save xcaptain/4d190232411dcf27441d9fadd7ff6988 to your computer and use it in GitHub Desktop.
sign transaction offline and then submit to chain
const { ApiPromise, WsProvider } = require('@polkadot/api');
const { Keyring } = require('@polkadot/keyring');
const {
construct,
decode,
deriveAddress,
getRegistry,
methods,
PolkadotSS58Format,
} = require('@substrate/txwrapper-polkadot');
const { cryptoWaitReady } = require('@polkadot/util-crypto');
const fetch = require('node-fetch');
const { createMetadata, OptionsWithMeta } = require('@substrate/txwrapper-polkadot');
const { EXTRINSIC_VERSION } = require('@polkadot/types/extrinsic/v4/Extrinsic');
async function rpcToLocalNode(
method,
params
) {
return await fetch('http://localhost:9933', {
body: JSON.stringify({
id: 1,
jsonrpc: '2.0',
method,
params,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
})
.then((response) => response.json())
.then(({ error, result }) => {
if (error) {
throw new Error(
`${error.code} ${error.message}: ${JSON.stringify(error.data)}`
);
}
return result;
});
}
async function signWith(
pair,
signingPayload,
options
) {
const { registry, metadataRpc } = options;
// Important! The registry needs to be updated with latest metadata, so make
// sure to run `registry.setMetadata(metadata)` before signing.
registry.setMetadata(createMetadata(registry, metadataRpc));
const { signature } = registry
.createType('ExtrinsicPayload', signingPayload, {
version: EXTRINSIC_VERSION,
})
.sign(pair);
return signature;
}
async function test5() {
await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
// Add Alice to our keyring with a hard-deived path (empty phrase, so uses dev)
const alice = keyring.addFromUri('//Alice');
const bob = keyring.addFromUri('//Bob');
const { specVersion, transactionVersion, specName } = await rpcToLocalNode(
'state_getRuntimeVersion'
);
const { block } = await rpcToLocalNode('chain_getBlock');
const blockHash = await rpcToLocalNode('chain_getBlockHash');
const genesisHash = await rpcToLocalNode('chain_getBlockHash', [0]);
const metadataRpc = await rpcToLocalNode('state_getMetadata');
// const index = await httpRequest({ ...params, method: 'system_accountNextIndex', params: [pair.address] });
const index = await rpcToLocalNode('system_accountNextIndex', [alice.address]);
const registry = getRegistry({
chainName: 'Polkadot',
specName,
specVersion,
metadataRpc,
});
const unsigned = methods.balances.transfer(
{
dest: bob.address,
value: 100,
},
{
address: deriveAddress(alice.publicKey, PolkadotSS58Format.polkadot),
blockHash,
blockNumber: registry
.createType('BlockNumber', block.header.number)
.toNumber(),
eraPeriod: 64,
genesisHash,
metadataRpc,
nonce: index, // Assuming this is Alice's first tx on the chain
specVersion,
tip: 0,
transactionVersion,
},
{ metadataRpc, registry }
);
const signingPayload = construct.signingPayload(unsigned, { registry });
// On your offline device, sign the payload.
// const signature = myOfflineSigning(signingPayload);
const signature = await signWith(alice, signingPayload, {
metadataRpc,
registry,
});
// `tx` is ready to be broadcasted.
const tx = construct.signedTx(unsigned, signature, { metadataRpc, registry });
console.log('tx', tx);
const actualTxHash = await rpcToLocalNode('author_submitExtrinsic', [tx]);
console.log(`Actual Tx Hash: ${actualTxHash}`);
console.log('block_hash', blockHash, block, index);
}
test5()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment