Polkadot

You can use the TSM as a wallet for Polkadot and other blockchains based on the Substrate framework.

In the following example we use the polkadot.js library to transfer funds from an account on Westend, which is the Polkadot test network. We assume that credentials to a TSM is available in the file creds.json and that the TSM has Ed25519 signatures enabled. The example can easily be adapted for other Substrate networks like Kusama or Astar.

const crypto = require("crypto");

const { TSMClient, algorithms, curves } = require('@sepior/tsm');

const { Keyring } = require('@polkadot/keyring');
const { cryptoWaitReady, encodeAddress } = require('@polkadot/util-crypto');
const { ApiPromise } = require('@polkadot/api');
const { HttpProvider } = require('@polkadot/rpc-provider');
const { getRegistry } = require('@substrate/txwrapper-polkadot');

const example = async () => {

  const chainName = "westend";
  const chainAddressID = 42;
  const dstAddress = "5E5QrYML2MyZhNg1U72y2YqFC18NSuehU73mCxhducVFaq4R";

  // Initialize polkadot api.
  const provider = new HttpProvider(`https://${chainName}-rpc.dwellir.com`);
  const api = new ApiPromise({ provider: provider });
  await api.isReady;
  const block = await api.rpc.chain.getBlock();
  const blockHash = await api.rpc.chain.getBlockHash();
  const genesisHash = await api.genesisHash;
  const metadataRpc = await api.rpc.state.getMetadata();
  const runtimeVersion = await api.rpc.state.getRuntimeVersion();
  const registry = getRegistry({
    chainName: chainName,
    specName: runtimeVersion.specVersion.toNumber(),
    specVersion: runtimeVersion.transactionVersion.toNumber(),
    metadataRpc: metadataRpc.toHex(),
  });

  var TSMSigner = function (tsmClient, keyID, chainPath) {
    this._tsmClient = tsmClient;
    this._keyID = keyID;
    this._chainPath = chainPath;
  };

  TSMSigner.prototype.signPayload = async function (payload) {
    const unsigned = registry.createType('ExtrinsicPayload', payload, { version: payload.version });
    const msgToSign = unsigned.toU8a({ method: true});
    const tsmSignature = await this._tsmClient.sign(algorithms.EDDSA, this._keyID, this._chainPath, msgToSign);
    const signatureHex =  "0x00" + Buffer.from(tsmSignature).toString("hex");
    return { "signature": signatureHex };

  };

  TSMSigner.prototype.address = async function () {
    const pk = await this._tsmClient.publicKey(algorithms.EDDSA, this._keyID, this._chainPath);
    const pkHex = "0x" + Buffer.from(pk).toString("hex");
    return encodeAddress(pkHex, chainAddressID);
  }

  // Instantiate api with a TSM signer.
  const fs = require('fs');
  const creds = JSON.parse(fs.readFileSync('creds.json', 'utf8'));
  const tsmClient = await TSMClient.init(creds);
  // const keyID = await tsmClient.keygen(algorithms.EDDSA, curves.ED25519); // use this to generate a new key
  const keyID = "5fzw96UlChHpzBk1Va9408WvbR4C";  // or reuse an existing key
  const chainPath = new Uint32Array();
  const signer = new TSMSigner(tsmClient, keyID, chainPath);
  const apiWithSigner = new ApiPromise({ provider: provider, signer: signer, registry: registry });
  await apiWithSigner.isReady;

  // Generate and sign transaction.
  const srcAddress = await signer.address();
  const amount = 100000000000n; // 0.1 WND
  console.log("Sending", amount, "from", srcAddress, "to", dstAddress);
  transfer = apiWithSigner.tx.balances.transfer(dstAddress, amount);
  const signedTx = await transfer.signAsync(srcAddress, {});
  console.log("Signed transaction:", signedTx.toHuman());

  // Submit transaction to the blockchain.
  const txHash = await api.rpc.author.submitExtrinsic(signedTx);
  console.log("Transaction hash:", txHash.toHuman());

}

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