Skip to content

Instantly share code, notes, and snippets.

@yrong
Forked from xcaptain/offline_transfer.js
Created May 15, 2025 02:34
Show Gist options
  • Select an option

  • Save yrong/13e095d3ccbcd7b4606a72a2bd43f97a to your computer and use it in GitHub Desktop.

Select an option

Save yrong/13e095d3ccbcd7b4606a72a2bd43f97a to your computer and use it in GitHub Desktop.

Revisions

  1. @xcaptain xcaptain created this gist Nov 17, 2021.
    127 changes: 127 additions & 0 deletions offline_transfer.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,127 @@
    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()