PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-coin-avaxp/test/unit/lib

Просмотр файла: permissionlessValidatorTxBuilder.ts

import { BitGoAPI } from '@bitgo/sdk-api';
import { BaseTransaction, HalfSignedAccountTransaction, TransactionType } from '@bitgo/sdk-core';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { coins } from '@bitgo/statics';
import assert from 'assert';
import { AvaxP, TavaxP } from '../../../src';
import * as AvaxpLib from '../../../src/lib';
import { TransactionBuilderFactory } from '../../../src/lib';
import { PermissionlessValidatorTxBuilder } from '../../../src/lib/permissionlessValidatorTxBuilder';
import * as testData from '../../resources/avaxp';
// import { pvm } from '@bitgo-forks/avalanchejs';

describe('AvaxP permissionlessValidatorTxBuilder', () => {
  let basecoin;
  let bitgo: TestBitGoAPI;
  const factory = new TransactionBuilderFactory(coins.get('tavaxp'));

  before(() => {
    bitgo = TestBitGo.decorate(BitGoAPI, {
      env: 'mock',
    });
    bitgo.initializeTestVars();
    bitgo.safeRegister('avaxp', AvaxP.createInstance);
    bitgo.safeRegister('tavaxp', TavaxP.createInstance);
    basecoin = bitgo.coin('tavaxp');
  });

  it('should create transaction builder from hex', () => {
    const txBuilder = factory.from(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.halfSignedTx);
    txBuilder.should.be.an.instanceOf(PermissionlessValidatorTxBuilder);
  });

  describe('Transaction readable', () => {
    let tx: BaseTransaction;
    before(async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(
        testData.ADD_VALIDATOR_ID_SAMPLE.fullsigntxHex
      );
      tx = await txBuilder.build();
    });

    it('Should json stringifiy addPermissionlessValidator transaction', async () => {
      const txJson = tx.toJson();
      assert(typeof JSON.stringify(tx.toJson()), 'string');
      txJson.id.should.equal(testData.ADD_VALIDATOR_ID_SAMPLE.txid);
    });

    it('Should get a txid', async () => {
      tx.id.should.equal(testData.ADD_VALIDATOR_ID_SAMPLE.txid);
    });
  });

  describe('should explains transaction', () => {
    it('should explains a Signed AddPermissionlessValidatorTx', async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(
        // https://testnet.snowtrace.io/pvm/tx/2tt3KE6gAG7qpMLL6xoynSyk7ht4egQ74wcF7AuGm25vQ3QNWB?chainId=43113
        '0x000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000baeb90000000000000000000000000200000003ac03c0ca71a1dde84702d272fbdd08600186701242e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1ee000000024f194d8e066b11dfe92f593cfa5c2fa1ae450927ecd5b093952e61834f4d8aa4000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000023a4c180000000030000000200000000000000014f194d8e066b11dfe92f593cfa5c2fa1ae450927ecd5b093952e61834f4d8aa4000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000023a4c180000000030000000200000000000000010000000094a8b8179f0b6e7e7ce55b4d6ec5ad56dae1de9f0000000062bb03e60000000062e32556000000003b9aca0000000000000000000000000000000000000000000000000000000000000000000000001c8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e1586a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000200000003ac03c0ca71a1dde84702d272fbdd08600186701242e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1ee0000000b00000000000000000000000200000003ac03c0ca71a1dde84702d272fbdd08600186701242e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1ee0000000b00000000000000000000000200000003ac03c0ca71a1dde84702d272fbdd08600186701242e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1ee00030d40000000020000000900000002b631e9553a8721978bffed3e778de7eb904e167599c1d26d5a8d3c46158df661164c6ca6029f24988020ccfcd673e5c19817ff74d012651f245d009a749591160170eae73d7e19b045671d41050b11791643567d1bc9cc51fa226d2be95a65689f19cda06a242a9749f6a150fcc2a5b50af12a1bc3564d1b76ac1e3826b5e21361010000000900000002b631e9553a8721978bffed3e778de7eb904e167599c1d26d5a8d3c46158df661164c6ca6029f24988020ccfcd673e5c19817ff74d012651f245d009a749591160170eae73d7e19b045671d41050b11791643567d1bc9cc51fa226d2be95a65689f19cda06a242a9749f6a150fcc2a5b50af12a1bc3564d1b76ac1e3826b5e2136101'
      );
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      txExplain.outputAmount.should.equal(testData.ADDVALIDATOR_SAMPLES.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddPermissionlessValidator);
      txExplain.outputs[0].address.should.equal(testData.ADDVALIDATOR_SAMPLES.nodeID);
    });

    it('should explains a Signed AddPermissionlessValidatorTx and order inputs', async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('avaxp')).from(
        // https://testnet.snowtrace.io/pvm/tx/2tt3KE6gAG7qpMLL6xoynSyk7ht4egQ74wcF7AuGm25vQ3QNWB?chainId=43113
        '0x0000000000190000000100000000000000000000000000000000000000000000000000000000000000000000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000700000000773594000000000000000000000000020000000393ca93e4cb3b6c10d167d41fbabeb8e94ed2deb5dc787323d722ff40fdfe56040fb98f7aee2075eff4dd7a368ddb6ef7132bff693cbfd9ba30f17c18000000046f8bdc3c893edd2c7f24a6190d6233fda5ccf517d96dd6c08d134179abb2d14e0000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000047036aaa00000000020000000100000002865cc8568b9c4c7d36bb96e086619d8c85218d653ff56d051334f839e58977c30000000221e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000005fb5d590000000020000000100000002865cc8568b9c4c7d36bb96e086619d8c85218d653ff56d051334f839e58977c30000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000007ae61e378000000020000000100000002865cc8568b9c4c7d36bb96e086619d8c85218d653ff56d051334f839e58977c30000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000003ffb6db3f6900000002000000010000000200000000953cc95e84f29dc6ee7589ef6acb01c9d561cba3000000006616c6750000000066560e750000044e51280e7100000000000000000000000000000000000000000000000000000000000000000000001ca7a2f29ca580c4254e3beabce2047e04ea8c3c7c69a5817b5461c2239593561a3843bc7cb14576390378b145135301478ff8cb5a651e3d082fd6cd1e1777edeabd5b0924ca648f3890d82ebd6259b3cca00e30faa249482fef10293f42e28afb15f0a5dd3f37e5524cd5de3f3c2b909df49873b8196af60004d3f3a84baa46ba08df7baf8e486b0b79a9c207095e1a430000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000044e51280e710000000000000000000000020000000393ca93e4cb3b6c10d167d41fbabeb8e94ed2deb5dc787323d722ff40fdfe56040fb98f7aee2075eff4dd7a368ddb6ef7132bff693cbfd9ba30f17c180000000b0000000000000000000000020000000393ca93e4cb3b6c10d167d41fbabeb8e94ed2deb5dc787323d722ff40fdfe56040fb98f7aee2075eff4dd7a368ddb6ef7132bff693cbfd9ba30f17c180000000b0000000000000000000000020000000393ca93e4cb3b6c10d167d41fbabeb8e94ed2deb5dc787323d722ff40fdfe56040fb98f7aee2075eff4dd7a368ddb6ef7132bff693cbfd9ba30f17c1800004e2000000004000000090000000208304e8841ab96169ea2b28dc358df3594523b3ce87593f1f579b51b7bfb844d49d690f71edb4e6530b8800436a4ef7357046a2155c5b52c15414ae76ca956c8007bbd8dbea9071c1bf1788f853472e08362b1afadc9d0a4412b13b0ea43b703e010df1204d88c6fe0484140b1bb91fdbf7a032d262475a419f80829411a04114001000000090000000208304e8841ab96169ea2b28dc358df3594523b3ce87593f1f579b51b7bfb844d49d690f71edb4e6530b8800436a4ef7357046a2155c5b52c15414ae76ca956c8007bbd8dbea9071c1bf1788f853472e08362b1afadc9d0a4412b13b0ea43b703e010df1204d88c6fe0484140b1bb91fdbf7a032d262475a419f80829411a04114001000000090000000208304e8841ab96169ea2b28dc358df3594523b3ce87593f1f579b51b7bfb844d49d690f71edb4e6530b8800436a4ef7357046a2155c5b52c15414ae76ca956c8007bbd8dbea9071c1bf1788f853472e08362b1afadc9d0a4412b13b0ea43b703e010df1204d88c6fe0484140b1bb91fdbf7a032d262475a419f80829411a04114001000000090000000208304e8841ab96169ea2b28dc358df3594523b3ce87593f1f579b51b7bfb844d49d690f71edb4e6530b8800436a4ef7357046a2155c5b52c15414ae76ca956c8007bbd8dbea9071c1bf1788f853472e08362b1afadc9d0a4412b13b0ea43b703e010df1204d88c6fe0484140b1bb91fdbf7a032d262475a419f80829411a0411400127efec04'
      );
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      txExplain.inputs[0].id.should.equal('r8JUUYFv9NWcNe5cxMFdBRjqkL6FZewDf2GtkttmxWCdeFFMK:0');
      txExplain.inputs[1].id.should.equal('22B7GH7fDarBqyw8W7atC8ED3euVnefTiEWBPBmaSXrAhAQ4Lk:0');
      txExplain.inputs[2].id.should.equal('22B7GH7fDarBqyw8W7atC8ED3euVnefTiEWBPBmaSXrAhAQ4Lk:1');
      txExplain.inputs[3].id.should.equal('22B7GH7fDarBqyw8W7atC8ED3euVnefTiEWBPBmaSXrAhAQ4Lk:2');
    });

    it('should explains a Signed AddPermissionlessValidatorTx from raw', async () => {
      const oldHex =
        '0x000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000000b9f76c00000000000000000000000020000000342e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1eeac03c0ca71a1dde84702d272fbdd086001867012000000024f194d8e066b11dfe92f593cfa5c2fa1ae450927ecd5b093952e61834f4d8aa4000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000023a4c1800000000200000000000000014f194d8e066b11dfe92f593cfa5c2fa1ae450927ecd5b093952e61834f4d8aa4000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000023a4c1800000000200000000000000010000000094a8b8179f0b6e7e7ce55b4d6ec5ad56dae1de9f0000000062bb03e60000000062e32556000000003b9aca0000000000000000000000000000000000000000000000000000000000000000000000001c8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e1586a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca000000000000000000000000020000000342e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1eeac03c0ca71a1dde84702d272fbdd0860018670120000000b0000000000000000000000020000000342e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1eeac03c0ca71a1dde84702d272fbdd0860018670120000000b0000000000000000000000020000000342e19ad9a617fb95dcf8843ade66e06e578f549a8be54f016ab748127ab1a184626cba44c748a1eeac03c0ca71a1dde84702d272fbdd08600186701200004e2000000002000000090000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008be54f016ab748127ab1a184626cba44c748a1ee000000090000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008be54f016ab748127ab1a184626cba44c748a1ee964888d4';
      const sortedBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(oldHex);
      const sortedTx = await sortedBuilder.build();
      const txExplain = sortedTx.explainTransaction();
      // this tx is sorted and can be broadcasted
      const newHex = sortedTx.toBroadcastFormat();
      assert(newHex === oldHex);
      txExplain.outputAmount.should.equal(testData.ADDVALIDATOR_SAMPLES.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddPermissionlessValidator);
      txExplain.outputs[0].address.should.equal('NodeID-EZ38CcWHoSyoEfAkDN9zaieJ5Yq64YePY');
    });

    it('should explain AddPermissionlessValidatorTx with 1 input', async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(
        '0x000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca00000000000000000000000002000000032a00839550f75ee8879cacd97ff350a51d47ae14db94335f2f668b9707c78903c3b5ac54c6f4054783c4a6ecd5b18fa07692d35ee4c3f8b74d425721000000014f194d8e066b11dfe92f593cfa5c2fa1ae450927ecd5b093952e61834f4d8aa4000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000007735940000000002000000000000000100000000e25c9a09eb9c68d4807a39f97893facd9a6a7da100000000660d750900000000660ec689000000003b9aca0000000000000000000000000000000000000000000000000000000000000000000000001c8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e1586a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca00000000000000000000000002000000032a00839550f75ee8879cacd97ff350a51d47ae14db94335f2f668b9707c78903c3b5ac54c6f4054783c4a6ecd5b18fa07692d35ee4c3f8b74d4257210000000b000000000000000000000002000000032a00839550f75ee8879cacd97ff350a51d47ae14db94335f2f668b9707c78903c3b5ac54c6f4054783c4a6ecd5b18fa07692d35ee4c3f8b74d4257210000000b000000000000000000000002000000032a00839550f75ee8879cacd97ff350a51d47ae14db94335f2f668b9707c78903c3b5ac54c6f4054783c4a6ecd5b18fa07692d35ee4c3f8b74d42572100030d40000000010000000900000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000db94335f2f668b9707c78903c3b5ac54c6f4054700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c5543b0'
      );
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      txExplain.outputAmount.should.equal(testData.ADDVALIDATOR_SAMPLES.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddPermissionlessValidator);
      txExplain.outputs[0].address.should.equal('NodeID-MdteS9U987PY7iwA5Pcz3sKVprJAbAvE7');
    });

    it('should explains a Half Signed AddPermissionlessValidatorTx', async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(
        testData.ADDVALIDATOR_SAMPLES.halfsigntxHex
      );
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      txExplain.outputAmount.should.equal(testData.ADDVALIDATOR_SAMPLES.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.ADDVALIDATOR_SAMPLES.nodeID);
    });

    it('should explains a unsigned AddPermissionlessValidatorTx', async () => {
      const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(
        testData.ADDVALIDATOR_SAMPLES.unsignedTxHex
      );
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      txExplain.outputAmount.should.equal(testData.ADDVALIDATOR_SAMPLES.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.ADDVALIDATOR_SAMPLES.nodeID);
    });
  });

  describe('Sign Transaction', () => {
    it('build and sign an AddPermissionlessValidator transaction and broadcast', async () => {
      /*

    const keyMapping: Record<string, any> = {
      f45d1c9bf57d3c02299a727d6e5d8d4e76498846b1a289b4e31a2ecccbc212fb: {
        source: 'user',
        encoded: 'BhjEzaXMSFj4aQBhw27Dsn73b8rR9gCTX',
        pub: '02abb451c0cc240e9a4fb97a130846d90a87247264e57014e41dc226ebf4965724',
        prv: 'f45d1c9bf57d3c02299a727d6e5d8d4e76498846b1a289b4e31a2ecccbc212fb',
        address: 'P-fuji1w4jjs4qfugq0m3nu0d3htx6662dlq87zq5nz33',
      },
      '2e0ffe6efbfcc27e9aaf4bce6af78d4c66e89b0955b85f02d7e15910e16ed4cd': {
        source: 'backup',
        encoded: 'BFzPJNifqdf6Z8H2P76gTEbd8RmCjMSzL',
        pub: '025523fa48a5b0c8ed4a8344aab8396061fb42cb4f168ba5d3ae19f034c4ea2d4d',
        prv: '2e0ffe6efbfcc27e9aaf4bce6af78d4c66e89b0955b85f02d7e15910e16ed4cd',
        address: 'P-fuji1wzr0hsm8zlr6gpaakxvt67f999nj044ete07uw',
      },
      '5a360c848021024528dfc02e79ba441f1727c765b4d0dcec288ffbd2772cd432': {
        source: 'bitgo',
        encoded: 'HT6zoSecYyqJX63Hqaj1usvyqJbrnc8ia',
        pub: '03d71cda3b1c32eed044866ec96c2ac54af43e7edb8f1535df51f341e49674ffaa',
        prv: '5a360c848021024528dfc02e79ba441f1727c765b4d0dcec288ffbd2772cd432',
        address: 'P-fuji1k3ca2njcunzl4hhxy24avca7htf66cmjjukl0n',
      },
    };
       */
      const unixNow = BigInt(Math.round(new Date().getTime() / 1000));
      const startTime = unixNow + BigInt(60);
      const endTime = startTime + BigInt(60 * 60 * 24 + 600); // 24 hours + 10 minutes

      // const AVAX_PUBLIC_URL = 'https://api.avax-test.network';
      // const pvmapi = new pvm.PVMApi(AVAX_PUBLIC_URL);
      // const { utxos } = await pvmapi.getUTXOs({
      //   addresses: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.bitgoAddresses,
      // });
      // console.log(utxos);

      const recoveryMode = false;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get('tavaxp'))
        .getPermissionlessValidatorTxBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.bitgoAddresses)
        .startTime(startTime.toString())
        .endTime(endTime.toString())
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.nodeId)
        .blsPublicKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsPublicKey)
        .blsSignature(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsSignature)
        .utxos(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.utxos);
      const tx = await txBuilder.build();
      const txExplain = tx.explainTransaction();
      const txJson = tx.toJson();
      assert(txExplain.outputAmount === testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.stakeAmount);
      txExplain.inputs[0].id.should.equal('WyjYJHa7Ye1KMsBZiQ8hQJzY7YYxTJTTyMkxcVrKRDiNytTY8:0');
      txExplain.inputs[1].id.should.equal('WyjYJHa7Ye1KMsBZiQ8hQJzY7YYxTJTTyMkxcVrKRDiNytTY8:1');
      txExplain.inputs[2].id.should.equal('s92SjoZQemgG97HocX9GgyFy6ZKmapgcgqQ3y5J2uwP3qWBUy:0');
      txExplain.fee.fee.should.equal('1000000');
      assert(txJson.outputs[0].value === testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.stakeAmount);
      tx.type.should.equal(TransactionType.AddPermissionlessValidator);
      console.log(tx.toBroadcastFormat());

      const txBuilder2 = new TransactionBuilderFactory(coins.get('tavaxp'))
        .from(tx.toBroadcastFormat())
        .recoverMode(false);
      const tx2 = await txBuilder2.build();
      tx2.type.should.equal(TransactionType.AddPermissionlessValidator);
      // Test sign with user key
      txBuilder2.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.userPrivateKey });
      const halfSigned = await txBuilder2.build();
      const halfSignedTxHex = halfSigned.toBroadcastFormat();
      assert(txBuilder['_stakeAmount'] === txBuilder2['_stakeAmount']);
      console.log(halfSignedTxHex);

      // const txBuilder2 = factory.from(halfSignedTxHex);
      // txBuilder2.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.backupPrivateKey });
      // const fullSignedTx = await txBuilder2.build();
      // console.log(fullSignedTx.toJson());

      // const res = await pvmapi.issueTx({ tx: fullSignedTx.toBroadcastFormat() });
      // const txSigned =
      //   '0x000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005c81a40000000000000000000000002000000037086fbc36717c7a407bdb198bd7925296727d6b97565285409e200fdc67c7b63759b5ad29bf01fc2b471d54e58e4c5fadee622abd663bebad3ad6372000000024411af9d9877c52c1be3f77825a74ca11869128478c4145910e92695a24c7d80000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca0000000002000000010000000271d8644c9199e30b9fef53af56aef243e5431c6b1b09d5f11a6622c86db3f39d000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005c81a4000000002000000010000000200000000129a271ff407b1101f35aec82a2f715c5239bd3900000000660f4ab50000000066109e8d000000003b9aca0000000000000000000000000000000000000000000000000000000000000000000000001cad9e9476b701edec88e53b1c314456053b3cf846a1192117872e41455f440c074d6ee89530d45e88f79ac0eda06f2887a94d6182edbd953516b262f17565a65d98f5741549cd70d2423abff750bb4b8d982d482376b189142ff8aa4705615fee14be6174610860e9c003aa4aeaa613b1732abf3cd0c9c42fa5856345644068c0d1f9fa1d9af32e20b14fca02983260bc000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca00000000000000000000000002000000037086fbc36717c7a407bdb198bd7925296727d6b97565285409e200fdc67c7b63759b5ad29bf01fc2b471d54e58e4c5fadee622abd663bebad3ad63720000000b000000000000000000000002000000037086fbc36717c7a407bdb198bd7925296727d6b97565285409e200fdc67c7b63759b5ad29bf01fc2b471d54e58e4c5fadee622abd663bebad3ad63720000000b000000000000000000000002000000037086fbc36717c7a407bdb198bd7925296727d6b97565285409e200fdc67c7b63759b5ad29bf01fc2b471d54e58e4c5fadee622abd663bebad3ad637200030d4000000002000000090000000228a59420971953aa61b4b4c90b934ef76e6e9c8ad256e9417e9a6b5017d836813b2b5cad2a890c66c8295f104d74192c4964cf289e2a45adb57a7b05b5e9529e004ec2dae5c48ba6fa4d1e0b1c6b9d9bbe79c481ff16848c1fa3fa3a6ed47431a12af4b2d9779bb192bb3f254408731798b50c8f03f25b7e7d7313738892e71d6601000000090000000228a59420971953aa61b4b4c90b934ef76e6e9c8ad256e9417e9a6b5017d836813b2b5cad2a890c66c8295f104d74192c4964cf289e2a45adb57a7b05b5e9529e004ec2dae5c48ba6fa4d1e0b1c6b9d9bbe79c481ff16848c1fa3fa3a6ed47431a12af4b2d9779bb192bb3f254408731798b50c8f03f25b7e7d7313738892e71d660151aa291f';
      // const txBuilder = new TransactionBuilderFactory(coins.get('tavaxp')).from(tx);
      // const decode = await txBuilder.build();
      // console.log(decode.toJson());

      // const res = await pvmapi.issueTx({ tx: txSigned });
      // console.log(res);
    });

    it('build and sign an AddPermissionlessValidator transaction', async () => {
      const recoveryMode = false;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get('tavaxp'))
        .getPermissionlessValidatorTxBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.bitgoAddresses)
        .startTime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.startTime)
        .endTime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.endTime)
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.nodeId)
        .blsPublicKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.blsPublicKey)
        .blsSignature(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.blsSignature)
        .utxos(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.utxos);
      const tx = await txBuilder.build();
      tx.type.should.equal(TransactionType.AddPermissionlessValidator);

      // Test sign with user key
      txBuilder.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.userPrivateKey });
      txBuilder.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.backupPrivateKey });
      const fullSignedTx = await txBuilder.build();
      console.log(fullSignedTx.toJson());
    });

    it('build and sign a transaction in recovery mode', async () => {
      const recoveryMode = true;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get('tavaxp'))
        .getPermissionlessValidatorTxBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.bitgoAddresses)
        .startTime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.startTime)
        .endTime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.endTime)
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.nodeId)
        .blsPublicKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.blsPublicKey)
        .blsSignature(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.blsSignature)
        .utxos(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.utxos);
      const tx = await txBuilder.build();

      let txHex = tx.toBroadcastFormat();
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.unsignedRawtxRecovery);

      const privateKey = recoveryMode
        ? testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.backupPrivateKey
        : testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.userPrivateKey;

      const params = {
        txPrebuild: {
          txHex: tx.toBroadcastFormat(),
        },
        prv: privateKey,
      };

      const halfSignedTransaction = await basecoin.signTransaction(params);
      txHex = (halfSignedTransaction as HalfSignedAccountTransaction)?.halfSigned?.txHex;
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_SAMPLE.halfSignedRawTxRecovery);
    });

    it('build and sign an AddPermissionlessValidator transaction and broadcast in recovery mode', async () => {
      const unixNow = BigInt(Math.round(new Date().getTime() / 1000));
      const startTime = unixNow + BigInt(60);
      const endTime = startTime + BigInt(60 * 60 * 24 + 600);

      const recoveryMode = true;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get('tavaxp'))
        .getPermissionlessValidatorTxBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.bitgoAddresses)
        .startTime(startTime.toString())
        .endTime(endTime.toString())
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.nodeId)
        .blsPublicKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsPublicKey)
        .blsSignature(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsSignature)
        .utxos(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.utxos);
      const tx = await txBuilder.build();
      tx.type.should.equal(TransactionType.AddPermissionlessValidator);

      // Test sign with user key
      txBuilder.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.backupPrivateKey });
      const halfSigned = await txBuilder.build();
      const halfSignedTxHex = halfSigned.toBroadcastFormat();

      const txBuilder2 = factory.from(halfSignedTxHex);
      txBuilder2.sign({ key: testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.userPrivateKey });
      const fullSignedTx = await txBuilder2.build();
      console.log(fullSignedTx.toJson());
    });
  });
  it('Should fail to build if utxos change output 0', async () => {
    const unixNow = BigInt(Math.round(new Date().getTime() / 1000));
    const startTime = unixNow + BigInt(60);
    const endTime = startTime + BigInt(60 * 60 * 24 + 600);

    const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get('tavaxp'))
      .getPermissionlessValidatorTxBuilder()
      .threshold(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.threshold)
      .locktime(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.locktime)
      .recoverMode(false)
      .fromPubKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.bitgoAddresses)
      .startTime(startTime.toString())
      .endTime(endTime.toString())
      .stakeAmount(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.stakeAmountNoOutput)
      .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.delegationFeeRate)
      .nodeID(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.nodeId)
      .blsPublicKey(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsPublicKey)
      .blsSignature(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.blsSignature)
      .utxos(testData.BUILD_AND_SIGN_ADD_PERMISSIONLESS_VALIDATOR_SAMPLE.utxos);
    const tx = await txBuilder.build();
    const txJson = tx.toJson();
    const txExplain = tx.explainTransaction();
    txJson.changeOutputs.length.should.equal(0);
    txExplain.changeOutputs.length.should.equal(0);
    txExplain.changeAmount.should.equal('0');
  });
});

Выполнить команду


Для локальной разработки. Не используйте в интернете!