3

[Question]

  1. How can I make raw-transaction(unsigned-transaction) with essential parameter(fromAddress, toAddress, value, nonce, fee etc)? I want to know low-level algorithm.
  2. What kind of Cryptography is used on Polkadot? I think it use sr25519, isn't it?
  3. In below code log, which data on trsfRawTx is used for sign? And Which Cryptography-function(js) can I use?
  4. I'm using send() function for broadcasting signedTx. Is there other function to broadcast signTx by parameter?
  5. For signing, I'm using mnemonic & keyring, and I can get address & public-key from keyring. How can I get private-key? If it can, How Can I sign by private-key?

[Environment] I'm developing a nodejs program on shibuya testnet.

[Goal]

  1. I want to make raw-transaction(unsigned-transaction) for tranferring & staking token.
  2. I want to sign raw-transaction and get signed-transaction on offchain-environment.
  3. I want to broadcast signed-transaction to node(public-endpoint).

[Attempt]

  • I tried to make sampleTransfer SampleCode using provided sample-example.
  • Just using provided api, have no problem, but I have to migrate on my system.
const sampleTransfer = async () => {

    // Create a new instance of the api
    const provider = new WsProvider('wss://rpc.shibuya.astar.network');
    const api = new ApiPromise(options({ provider }));

    const testAcnt1 = 'VxJhKGJ7wsVwZvaikWQzhexb9VawgFUtBuXNAmDAhBBVi4U';
    const testAcnt2 = '5HBSNV13JH7LuEtJUBNuKgcaPgNaXGBZhCknUWrKmD9p8vhA';

    await api.isReady;

    console.log('===== STEP1-RawTx-Start =====');
    const trsfRawTx = api.tx.balances.transfer(testAcnt1, BigInt(200000000000000000));
    console.log(trsfRawTx);
    // Submittable { initialU8aLength: undefined, registry: TypeRegistry {} }
    console.log('===== ===== =====');
    console.log(`${trsfRawTx}`);
    // {"signature":{"signer":{"id":"VwJHxi1vTNdnJ2KXfQ7tWL8ERiUGv9bwhmf5RDWKGySQmsy"},"signature":{"ed25519":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"era":{"immortalEra":"0x00"},"nonce":0,"tip":0},"method":{"callIndex":"0x1f00","args":{"dest":{"id":"VxJhKGJ7wsVwZvaikWQzhexb9VawgFUtBuXNAmDAhBBVi4U"},"value":"0x000000000000000002c68af0bb140000"}}}
    console.log('===== STEP1-RawTx-End =====');

    console.log('===== STEP2-SignRawTx-Start =====');
    const keyring = new Keyring({ type: 'sr25519' });
    const testAcnt2Mnemonic = '...MNEMONIC...';
    const testAcnt2Key = keyring.addFromUri(testAcnt2Mnemonic);
    console.log(`testAcnt2Key : ${testAcnt2Key.address}`);
    // testAcnt2Key : 5HBSNV13JH7LuEtJUBNuKgcaPgNaXGBZhCknUWrKmD9p8vhA
    console.log('===== ===== =====');
    const signedTx = await trsfRawTx.signAsync(testAcnt2Key);
    console.log(signedTx);
    // Submittable { initialU8aLength: undefined, registry: TypeRegistry {} }
    console.log('===== ===== =====');
    console.log(`signedTx : ${signedTx}`);
    // signedTx : {"signature":{"signer":{"id":"b42omy85WkT84v8xUq2MLmrUj5hVUtJiQFviE4Bda9mjH2V"},"signature":{"sr25519":"0x424a05493a61a47b6952b3357bcc08e2d210b566be82ec7fe506840483a9831800c3d355eb4302f676cbeb7d4252602c1d99ddda58e8c292171b72094ed2ac86"},"era":{"mortalEra":"0xe400"},"nonce":6,"tip":0},"method":{"callIndex":"0x1f00","args":{"dest":{"id":"VxJhKGJ7wsVwZvaikWQzhexb9VawgFUtBuXNAmDAhBBVi4U"},"value":"0x000000000000000002c68af0bb140000"}}}
    console.log('===== STEP2-SignRawTx-End =====');

    console.log('===== STEP3-SendSignedTx-Start =====');
    const txhash = await signedTx.send();
    console.log(txhash);
    /*Type(32) [Uint8Array] [
        200,
        2,
        33,
        72,
        119,
        185,
        239,
        160,
        204,
        131,
        232,
        7,
        240,
        121,
        215,
        203,
        71,
        157,
        105,
        98,
        100,
        46,
        130,
        158,
        88,
        129,
        19,
        225,
        222,
        218,
        10,
        203,
        registry: TypeRegistry {},
        initialU8aLength: 32
      ]*/
    console.log('===== ===== =====');
    console.log(`txhash : ${txhash}`);
    // txhash : 0xc802214877b9efa0cc83e807f079d7cb479d6962642e829e588113e1deda0acb
    console.log('===== STEP3-SendSignedTx-End =====');
}

sampleTransfer().catch(console.error).finally(() => process.exit());

1 Answer 1

6
  1. In order to make an unsigned transaction you can create an object with the required chain metadata and payload information:
// create unsigned transaction payload
const unsigned = {
          specVersion: specVersion,
          transactionVersion: transactionVersion,
          address: address,
          blockHash: blockHash,
          blockNumber: blockNumber,
          era: api.registry.createType('ExtrinsicEra', {
            current: blockNumber,
            period: 64,
          }),
          genesisHash: genesisHash,
          method: api.tx.balances.transfer(testAcnt1, BigInt(200000000000000000)).method.toHex(),
          nonce: accountNonce,
          signedExtensions: [
            'CheckNonZeroSender',
            'CheckSpecVersion',
            'CheckTxVersion',
            'CheckGenesis',
            'CheckMortality',
            'CheckNonce',
            'CheckWeight',
            'ChargeTransactionPayment',
          ],
          tip: '0x00000000000000000000000000000000',
          version: 4,
      };

// create an extrinsic payload to sign and get the signature
    const extrinsicPayload = this.registry.createType('ExtrinsicPayload', unsigned, {
      version: unsigned.version
    })

// get signature by passing your account keypair to the sign method on the extrinsic payload
const signature = extrinsicPayload.sign(keypair);

// create extrinsic from the unsigned payload
const extrinsic = registry.createType(
        'Extrinsic',
        { method: unsigned.method },
        { version: unsigned.version }
    );

// with the received signature from the sign method on the ExtrinsicPayload,
    extrinsic.addSignature(unsigned.address, signature, unsigned);

// save the hex of the signed extrinsic to later send to the public node
const hex = extrinsic.toHex();
  1. Yes, Polkadot uses Schnorrkel/Ristretto x25519 ("sr25519") as its key derivation and signing algorithm. You can find more information on the hashing algorithm and how keypairs/signing are implemented here https://wiki.polkadot.network/docs/learn-cryptography.
  2. The private key of the account is used to sign the tx. The function you can use is tx.sign(keypair) passing in a keypair created using the keyring.
  3. In addition to the signAsync method which you’ve used in your example, the SubmittableExtrinsic has a send as well as signAndSend which accepts the from account, additional signer options such as the signer, and a status callback which can be used to check the status of the tx and its associated events. More on submitting transactions here: https://polkadot.js.org/docs/extension/cookbook#sign-and-send-a-transaction
  4. PolkadotJS doesn’t currently support accessing KeyPair private keys. The polkadot-js extension does allow you to save the private key on account creation in its user interface but there is no programmatic way to access the private key on the keyring.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.