PHP WebShell

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

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

import should from 'should';

import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';
import { Sui, TokenTransferTransaction, TransferTransaction, Tsui } from '../../src';
import * as testData from '../resources/sui';
import _ from 'lodash';
import sinon from 'sinon';
import BigNumber from 'bignumber.js';
import assert from 'assert';
import { SuiTransactionType } from '../../src/lib/iface';
import { getBuilderFactory } from './getBuilderFactory';
import { keys } from '../resources/sui';
import { Buffer } from 'buffer';

describe('SUI:', function () {
  let bitgo: TestBitGoAPI;
  let basecoin;
  let newTxPrebuild;
  let newTxParams;

  const txPrebuild = {
    txHex: Buffer.from(testData.TRANSFER, 'base64').toString('hex'),
    txInfo: {},
  };

  const txParams = {
    recipients: testData.recipients,
  };

  before(function () {
    bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' });
    bitgo.safeRegister('sui', Sui.createInstance);
    bitgo.safeRegister('tsui', Tsui.createInstance);
    bitgo.initializeTestVars();
    basecoin = bitgo.coin('tsui');
    newTxPrebuild = () => {
      return _.cloneDeep(txPrebuild);
    };
    newTxParams = () => {
      return _.cloneDeep(txParams);
    };
  });

  it('should return the right info', function () {
    const sui = bitgo.coin('sui');
    const tsui = bitgo.coin('tsui');

    sui.getChain().should.equal('sui');
    sui.getFamily().should.equal('sui');
    sui.getFullName().should.equal('Sui');
    sui.getBaseFactor().should.equal(1e9);

    tsui.getChain().should.equal('tsui');
    tsui.getFamily().should.equal('sui');
    tsui.getFullName().should.equal('Testnet Sui');
    tsui.getBaseFactor().should.equal(1e9);
  });

  describe('Verify transaction: ', () => {
    it('should succeed to verify transaction', async function () {
      const txPrebuild = newTxPrebuild();
      const txParams = newTxParams();
      const verification = {};
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify transaction when recipients amount are numbers', async function () {
      const txPrebuild = newTxPrebuild();
      const txParamsWithNumberAmounts = newTxParams();
      txParamsWithNumberAmounts.recipients = txParamsWithNumberAmounts.recipients.map(({ address, amount }) => {
        return { address, amount: Number(amount) };
      });
      const verification = {};
      const isTransactionVerified = await basecoin.verifyTransaction({
        txParams: txParamsWithNumberAmounts,
        txPrebuild,
        verification,
      });
      isTransactionVerified.should.equal(true);
    });

    it('should fail to verify transaction with invalid param', async function () {
      const txPrebuild = {};
      const txParams = newTxParams();
      txParams.recipients = undefined;
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.rejectedWith('missing required tx prebuild property txHex');
    });

    it('should verify a split transaction', async function () {
      const factory = getBuilderFactory('tsui');
      const txBuilder = factory.getTransferBuilder();
      txBuilder.type(SuiTransactionType.Transfer);
      txBuilder.sender(testData.sender.address);
      const amount = 1000000000;
      const recipients = new Array(100).fill({ address: testData.sender.address, amount: amount.toString() });
      txBuilder.send(recipients);
      txBuilder.gasData(testData.gasData);
      const tx = await txBuilder.build();

      const txPrebuild = {
        txHex: Buffer.from(tx.toBroadcastFormat(), 'base64').toString('hex'),
        txInfo: {},
      };
      const txParams = {
        recipients,
      };
      const verify = await basecoin.verifyTransaction({
        txParams,
        txPrebuild,
      });
      verify.should.equal(true);
    });
  });

  describe('Explain Transaction: ', () => {
    it('should explain a transfer transaction', async function () {
      const explainedTransaction = await basecoin.explainTransaction({
        txHex: Buffer.from(testData.TRANSFER, 'base64').toString('hex'),
      });
      explainedTransaction.should.deepEqual({
        displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'],
        id: 'BxoeGXbBCuw6VFEcgwHHUAKrCoAsGanPB39kdVVKZZcR',
        outputs: [
          {
            address: testData.recipients[0].address,
            amount: testData.recipients[0].amount,
          },
          {
            address: testData.recipients[1].address,
            amount: testData.recipients[1].amount,
          },
        ],
        outputAmount: `${testData.AMOUNT * 2}`,
        changeOutputs: [],
        changeAmount: '0',
        fee: { fee: testData.gasData.budget.toString() },
        type: 0,
      });
    });

    it('should explain a split transfer transaction', async function () {
      const explainedTransaction = await basecoin.explainTransaction({
        txHex:
          '000065000800ca9a3b000000000020574895fe83b409b009e2e0433ad1823ec0d538af7fea52390a198bdfe7676f13000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000000800ca9a3b00000000c801020001010000010102000001010002000101020001010202000101000200010103000101020400010100020001010400010102060001010002000101050001010208000101000200010106000101020a000101000200010107000101020c000101000200010108000101020e000101000200010109000101021000010100020001010a000101021200010100020001010b000101021400010100020001010c000101021600010100020001010d000101021800010100020001010e000101021a00010100020001010f000101021c000101000200010110000101021e00010100020001011100010102200001010002000101120001010222000101000200010113000101022400010100020001011400010102260001010002000101150001010228000101000200010116000101022a000101000200010117000101022c000101000200010118000101022e000101000200010119000101023000010100020001011a000101023200010100020001011b000101023400010100020001011c000101023600010100020001011d000101023800010100020001011e000101023a00010100020001011f000101023c000101000200010120000101023e00010100020001012100010102400001010002000101220001010242000101000200010123000101024400010100020001012400010102460001010002000101250001010248000101000200010126000101024a000101000200010127000101024c000101000200010128000101024e000101000200010129000101025000010100020001012a000101025200010100020001012b000101025400010100020001012c000101025600010100020001012d000101025800010100020001012e000101025a00010100020001012f000101025c000101000200010130000101025e00010100020001013100010102600001010002000101320001010262000101000200010133000101026400010100020001013400010102660001010002000101350001010268000101000200010136000101026a000101000200010137000101026c000101000200010138000101026e000101000200010139000101027000010100020001013a000101027200010100020001013b000101027400010100020001013c000101027600010100020001013d000101027800010100020001013e000101027a00010100020001013f000101027c000101000200010140000101027e00010100020001014100010102800001010002000101420001010282000101000200010143000101028400010100020001014400010102860001010002000101450001010288000101000200010146000101028a000101000200010147000101028c000101000200010148000101028e000101000200010149000101029000010100020001014a000101029200010100020001014b000101029400010100020001014c000101029600010100020001014d000101029800010100020001014e000101029a00010100020001014f000101029c000101000200010150000101029e00010100020001015100010102a000010100020001015200010102a200010100020001015300010102a400010100020001015400010102a600010100020001015500010102a800010100020001015600010102aa00010100020001015700010102ac00010100020001015800010102ae00010100020001015900010102b000010100020001015a00010102b200010100020001015b00010102b400010100020001015c00010102b600010100020001015d00010102b800010100020001015e00010102ba00010100020001015f00010102bc00010100020001016000010102be00010100020001016100010102c000010100020001016200010102c200010100020001016300010102c400010100020001016400010102c600010100574895fe83b409b009e2e0433ad1823ec0d538af7fea52390a198bdfe7676f1303034f403ff9b73b5794b79a89ebbcd810331fea52878db0b4dc1a1c88f4c0dc5616000000000000002003caf7e677c72b241d61d00e600b6df25639c582987d0aa78ceea0b9953eac52a60b0e51f9ffa398f6ee1d3101d6c9efa13d1800e412d7e0832e2616f1793fd79ce91700000000002050435a49f0635bc2f2c3bbdce05a16b24faf0368edaa25c8fbce16ef62602da5cda44ae47f380f45d3cd4911880f4e9a24b52fe424683c66cf3c7769dd3ca22c1000000000000000200c78fa20236bb86871eacfe2b4f2158eaa68f4f4565d8cf9865ee9f279e4bd40574895fe83b409b009e2e0433ad1823ec0d538af7fea52390a198bdfe7676f13e80300000000000000e1f5050000000000',
      });
      explainedTransaction.outputs.length.should.equal(100);
    });

    it('should explain a staking transaction', async function () {
      const explainedTransaction = await basecoin.explainTransaction({
        txHex: Buffer.from(testData.ADD_STAKE, 'base64').toString('hex'),
      });
      explainedTransaction.should.deepEqual({
        displayOrder: [
          'id',
          'outputs',
          'outputAmount',
          'changeOutputs',
          'changeAmount',
          'fee',
          'type',
          'module',
          'function',
          'validatorAddress',
        ],
        id: 'bP78boZ48sDdJsg2V1tJahpGyBwaC9GSTL2rvyADnsh',
        outputs: [
          {
            address: testData.requestAddStake.validatorAddress,
            amount: testData.requestAddStake.amount.toString(),
          },
        ],
        outputAmount: testData.STAKING_AMOUNT.toString(),
        changeOutputs: [],
        changeAmount: '0',
        fee: { fee: testData.gasData.budget.toString() },
        type: 25,
      });
    });

    it('should fail to explain transaction with missing params', async function () {
      try {
        await basecoin.explainTransaction({});
      } catch (error) {
        should.equal(error.message, 'Invalid transaction');
      }
    });

    it('should fail to explain transaction with invalid params', async function () {
      try {
        await basecoin.explainTransaction({ txHex: 'randomString' });
      } catch (error) {
        should.equal(error.message, 'Invalid transaction');
      }
    });
  });

  describe('Parse Transactions: ', () => {
    const transferInputsResponse = [
      {
        address: testData.recipients[0].address,
        amount: new BigNumber(testData.AMOUNT).plus(testData.AMOUNT).plus(testData.gasData.budget).toFixed(),
      },
    ];

    const transferOutputsResponse = [
      {
        address: testData.recipients[0].address,
        amount: testData.recipients[0].amount,
      },
      {
        address: testData.recipients[1].address,
        amount: testData.recipients[1].amount,
      },
    ];

    it('should parse a transfer transaction', async function () {
      const parsedTransaction = await basecoin.parseTransaction({
        txHex: Buffer.from(testData.TRANSFER, 'base64').toString('hex'),
      });

      parsedTransaction.should.deepEqual({
        inputs: transferInputsResponse,
        outputs: transferOutputsResponse,
        fee: new BigNumber(20000000),
      });
    });

    it('should fail to parse a transfer transaction when explainTransaction response is undefined', async function () {
      const stub = sinon.stub(Sui.prototype, 'explainTransaction');
      stub.resolves(undefined);
      await basecoin.parseTransaction({ txHex: testData.TRANSFER }).should.be.rejectedWith('Invalid transaction');
      stub.restore();
    });
  });

  describe('Address Validation', () => {
    let keychains;
    let commonKeychain;

    before(function () {
      commonKeychain =
        '19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781';
      keychains = [
        {
          id: '6424c353eaf78d000766e95949868468',
          source: 'user',
          type: 'tss',
          commonKeychain:
            '19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
          encryptedPrv:
            '{"iv":"cZd5i7L4RxtwrALW2rK7UA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"5zgoH1Bd3Fw=","ct":"9vVlnXFRtrM9FVEo+d2chbGHlM9lFZemueBuAs3BIkPo33Fo7jzwwNK/kIWkEyg+NmEBd5IaqAS157nvvvwzzsmMWlQdUz9qbmXNv3pg987cXFR08exS+4uhwP1YNOjJTRvRNcO9ZqHb46d4fmyJ/yC9/susCge7r/EsbaN5C3afv1dzybuq912FwaQElZLYYp5BICudFOMZ9k0UDMfKM/PMDkH7WexoGHr9GKq/bgCH2B39TZZyHKU6Uy47lXep2s6h0DrMwHOrnmiL3DZjOj88Ynvphlzxuo4eOlD2UHia2+nvIaISYs29Pr0DAvREutchvcBpExj1kWWPv7hQYrv8F0NAdatsbWl3w+xKyfiMKo1USlrwyJviypGtQtXOJyw0XPN0rv2+L5lW8BbjpzHfYYN13fJTedlGTFhhkzVtbbPAKE02kx7zCJcjYaiexdSTsrDLScYNT9/Jhdt27KpsooehwVohLfSKz4vbFfRu2MPZw3/+c/hfiJNgtz6esWbnxGrcE8U2IwPYCaK+Ghk4DcqWNIni59RI5B5kAsQOToII40qPN510uTgxBSPO7q7MHgkxdd4CqBq+ojr9j0P7oao8E5Y+CBDJrojDoCh1oCCDW9vo2dXlVcD8SIbw7U/9AfvEbA4xyE/5md1M7CIwLnWs2Ynv0YtaKoqhdS9x6FmHlMDhN/DKHinrwmowtrTT82fOkpO5g9saSmgU7Qy3gLt8t+VwdEyeFeQUKRSyci8qgqXQaZIg4+aXgaSOnlCFMtmB8ekYxEhTY5uzRfrNgS4s1QeqFBpNtUF+Ydi297pbVXnJoXAN+SVWd80GCx+yI2dpVC89k3rOWK9WeyqlnzuLJWp2RIOB9cdW8GFv/fN+QAJpYeVxOE4+nZDsKnsj8nKcg9t4Dlx1G6gLM1/Vq9YxNLbuzuRC0asUYvdMnoMvszmpm++TxndYisgNYscpZSoz7wvcazJNEPfhPVjEkd6tUUuN4GM35H0DmKCUQNT+a6B6hmHlTZvjxiyGAg5bY59hdjvJ+22QduazlEEC6LI3HrA7uK0TpplWzS1tCIFvTMUhj65DEZmNJ2+ZY9bQ4vsMf+DRR3OOG4t+DMlNfjOd3zNv3QoY95BjfWpryFwPzDq7bCP67JDsoj7j2TY5FRSrRkD77H0Ewlux2cWfjRTwcMHcdQxxuV0OP0aNjGDjybFN"}',
        },
        {
          id: '6424c353eaf78d000766e96137d4404b',
          source: 'backup',
          type: 'tss',
          commonKeychain:
            '19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
          encryptedPrv:
            '{"iv":"vi0dPef/Rx7kG/pRySQi6Q==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"9efhQsiEvVs=","ct":"Gw6atvf6gxKzsjtl3xseipO3rAxp1mAz7Yu1ihFsi5/lf2vMZegApgZx+pyILFS9KKLHbNF3U6WgSYdrr2t4vzdLsXkH1WIxfHS+cd2C5N59yADZDnPJBT6pv/IRvaYelP0Ck3nIYQ2hSMm8op+VOWC/SzHeh7slYDqwEHTGan0Wigfvk1yRd7CCJTaEAomnc/4eFi2NY3X3gt/3opy9IAgknnwUFohn96EWpEQ0F6pbzH/Z8VF6gF+DUcrrByAxExUPnHQZiFk3YHU/vVV4FxBU/mVAE8xBsBn5ul5e5SUMPfc7TBuJWv4BByTNg9xDShF/91Yx2nbfUm5d9QmM8lpKgzzQvcK8POAPk87gRCuKnsGh5vNS0UppkHc+ocfzRQlGA6jze7QyyQO0rMj5Ly8kWjwk2vISvKYHYS1NR7VU549UIXo7NXjatunKSc3+IreoRUHIshiaLg6hl+pxCCuc0qQ43V0mdIfCjTN8gkGWLNk8R7tAGPz9jyapQPcPEGHgEz0ATIi6yMNWCsibS2eLiE1uVEJONoM4lk6FPl3Q2CHbW2MeEbqjY8hbaw18mNb2xSBH/Fwpiial+Tvi2imqgnCO4ZpO9bllKftZPcQy0stN+eGBlb5ufyflKkDSiChHYroGjEpmiFicdde48cJszF52uKNnf1q67fA9/S2FAHQab3EXojxH2Gbk+kkV2h/TYKFFZSWC3vi4e8mO+vjMUcR0AdsgPFyEIz0SCGuba3CnTLNdEuZwsauAeHkx2vUTnRgJPVgNeeuXmsVG76Sy2ggJHuals0Hj8U2Xda0qO1RuFfoCWfss9wn6HGRwPPkhSB/8oNguAqmRVGKkd8Zwt3IvrTd9fk0/rFFDJKGz7WyNHkYgUmNiGcItD12v0jx7FZ52EJzl3Av1RyJUQK18+8EYPh3SGiU9dt7VX0aF0uo6JouKhOeldUvMP+AugQz8fUclwTQsbboVg27Yxo0DyATVwThW5a56R6Qf5ZiQJluFuzs5y98rq0S5q046lE6o3vVmJpEdwjeSCJoET5CL4nTgkXyWvhm4eB8u/e66l3o0qbaSx8q9YYmT9EpRcl5TP4ThLBKETYdzVvg4exjQfektMatk5EyUpEIhZPXh5vXpJZesdfO9LJ8zTaHBsBjDPU7cdNgQMbebpataRi8A0el2/IJXl+E+olgAz5zC4i2O1Q=="}',
        },
        {
          id: '6424c353eaf78d000766e9510b125fba',
          source: 'bitgo',
          type: 'tss',
          commonKeychain:
            '19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
          verifiedVssProof: true,
          isBitGo: true,
        },
      ];
    });

    it('should return true when validating a well formatted address', async function () {
      const address = 'f941ae3cbe5645dccc15da8346b533f7f91f202089a5521653c062b2ff10b304';
      basecoin.isValidAddress(address).should.equal(true);
    });

    it('should return true when validating a well formatted address prefixed with 0x', async function () {
      const address = '0xf941ae3cbe5645dccc15da8346b533f7f91f202089a5521653c062b2ff10b304';
      basecoin.isValidAddress(address).should.equal(true);
    });

    it('should return false when validating an old address', async function () {
      const address = '0x2959bfc3fdb7dc23fed8deba2fafb70f3e606a59';
      basecoin.isValidAddress(address).should.equal(false);
    });

    it('should return false when validating an incorrectly formatted', async function () {
      const address = 'wrongaddress';
      basecoin.isValidAddress(address).should.equal(false);
    });

    it('should return true for isWalletAddress with valid address for index 4', async function () {
      const newAddress = '0x8b3c7807730d75792dd6c49732cf9f014d6984a9c77d386bdb1072a9e537d8d8';
      const index = 4;

      const params = { commonKeychain, address: newAddress, index, keychains };
      (await basecoin.isWalletAddress(params)).should.equal(true);
    });

    it('should throw error for isWalletAddress when keychains is missing', async function () {
      const address = '0x2959bfc3fdb7dc23fed8deba2fafb70f3e606a59';
      const index = 0;

      const params = { commonKeychain, address, index };
      await assert.rejects(async () => basecoin.isWalletAddress(params));
    });

    it('should throw error for isWalletAddress when new address is invalid', async function () {
      const wrongAddress = 'badAddress';
      const index = 0;

      const params = { commonKeychain, address: wrongAddress, index };
      await assert.rejects(async () => basecoin.isWalletAddress(params), {
        message: `invalid address: ${wrongAddress}`,
      });
    });
  });

  describe('Recover Transactions:', () => {
    const sandBox = sinon.createSandbox();
    const senderAddress0 = '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3';
    const recoveryDestination = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';
    let getBalanceStub: sinon.SinonStub;
    let getInputCoinsStub: sinon.SinonStub;
    let getFeeEstimateStub: sinon.SinonStub;

    beforeEach(() => {
      getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      getBalanceStub.withArgs(senderAddress0).resolves('1900000000');

      getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      getInputCoinsStub.withArgs(senderAddress0).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0xc05c765e26e6ae84c78fa245f38a23fb20406a5cf3f61b57bd323a0df9d98003',
          version: '195',
          digest: '7BJLb32LKN7wt5uv4xgXW4AbFKoMNcPE76o41TQEvUZb',
          balance: new BigNumber('1900000000'),
        },
      ]);

      getFeeEstimateStub = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
      getFeeEstimateStub
        .withArgs(
          'AAACAAgA0klrAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgABAQAAAQECAAABAQCR8l4je4OgCmJyT9xKgeQ/SU3GtBoSQUkoJtNuTRMdowHAXHZeJuauhMePokXziiP7IEBqXPP2G1e9MjoN+dmAA8MAAAAAAAAAIFvJiJBdEAhi14cxcSr/HUIhBZMbLMd4rczUTCMIb3UmkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaPoAwAAAAAAAADh9QUAAAAAAA=='
        )
        .resolves(new BigNumber(1997880));
    });

    afterEach(() => {
      sandBox.restore();
    });

    it('should recover a txn for non-bitgo recovery', async function () {
      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(0);
      tx.recoveryAmount.should.equal('1897802332');
      tx.serializedTx.should.equal(
        'AAACAAhcKh5xAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgABAQAAAQECAAABAQCR8l4je4OgCmJyT9xKgeQ/SU3GtBoSQUkoJtNuTRMdowHAXHZeJuauhMePokXziiP7IEBqXPP2G1e9MjoN+dmAA8MAAAAAAAAAIFvJiJBdEAhi14cxcSr/HUIhBZMbLMd4rczUTCMIb3UmkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaPoAwAAAAAAAKSIIQAAAAAAAA=='
      );

      const NonBitGoTxnDeserialize = new TransferTransaction(basecoin);
      NonBitGoTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const NonBitGoTxnJson = NonBitGoTxnDeserialize.toJson();

      should.equal(NonBitGoTxnJson.id, 'FDCBUNqLUAW4qBTnFjTCPZazL8VnRBP2gS3GmEnwtEcg');
      should.equal(NonBitGoTxnJson.sender, senderAddress0);
      sandBox.assert.callCount(basecoin.getBalance, 1);
      sandBox.assert.callCount(basecoin.getInputCoins, 1);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a txn for unsigned sweep recovery', async function () {
      const res = await basecoin.recover({
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
      });

      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000200085c2a1e7100000000002000e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e38902020001010000010102000001010091f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da301c05c765e26e6ae84c78fa245f38a23fb20406a5cf3f61b57bd323a0df9d98003c300000000000000205bc988905d100862d78731712aff1d422105931b2cc778adccd44c23086f752691f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3e803000000000000a48821000000000000',
                  scanIndex: 0,
                  coin: 'tsui',
                  signableHex: '873748a31e766f5f8f0077d8d0003548fae4f4c1344067a7c3799cfa73808fb7',
                  derivationPath: 'm/0',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3',
                        valueString: '1897802332',
                        value: new BigNumber(1897802332),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389',
                        valueString: '1897802332',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '1897802332',
                    type: 'Transfer',
                  },
                  feeInfo: { fee: 2197668, feeString: '2197668' },
                  coinSpecific: {
                    commonKeychain:
                      '3b89eec9d2d2f3b049ecda2e7b5f47827f7927fe6618d6e8b13f64e7c95f4b00b9577ab01395ecf8eeb804b590cedae14ff5fd3947bf3b7a95b9327c49e27c54',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
        ],
      });

      const unsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      const serializedTxHex = res.txRequests[0].transactions[0].unsignedTx.serializedTx;
      const serializedTxBase64 = Buffer.from(serializedTxHex, 'hex').toString('base64');
      unsignedSweepTxnDeserialize.fromRawTransaction(serializedTxBase64);
      const unsignedSweepTxnJson = unsignedSweepTxnDeserialize.toJson();
      should.equal(unsignedSweepTxnJson.id, 'FDCBUNqLUAW4qBTnFjTCPZazL8VnRBP2gS3GmEnwtEcg');
      should.equal(unsignedSweepTxnJson.sender, senderAddress0);

      sandBox.assert.callCount(basecoin.getBalance, 1);
      sandBox.assert.callCount(basecoin.getInputCoins, 1);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a txn for unsigned sweep recovery with multiple input coins', async function () {
      const senderAddress = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
      getBalanceStub.withArgs(senderAddress).resolves('1798002120');
      getInputCoinsStub.withArgs(senderAddress).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x60aefaffa35daa32a1e561f2ba9c18753057d2feb502f32804e573ea2875a39c',
          version: '195',
          digest: 'jKjduy8gHDE244ZJWcP3JXfXPKjqY67avMqhzHw98CL',
          balance: new BigNumber('98002120'),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x9a363c91d29b50832ab98094b6c1933941280fc298a2ec232739f14ce31e2582',
          version: '197',
          digest: 'ELnbgmW7crPYr3B9pWVqfg9uLeJt43KPofkfdR6LWftu',
          balance: new BigNumber('1700000000'),
        },
      ]);
      getFeeEstimateStub
        .withArgs(
          'AAACAAjIdDVlAAAAAAAgMtjlfubZHlVY2gZ3FUwvCFeVNI4xf5Wsye+t4bQRL8wCAgABAQAAAQECAAABAQAA5OqmopH+ApGEUuZFtWU80mCl/A+zX2GT1YCRaqnjiQKaNjyR0ptQgyq5gJS2wZM5QSgPwpii7CMnOfFM4x4lgsUAAAAAAAAAIMY5hfUFzuMVaY3+LwmZTvypRSAJRLNXocRYR9yKalXyYK76/6NdqjKh5WHyupwYdTBX0v61AvMoBOVz6ih1o5zDAAAAAAAAACAK15PtO6gwSNx0bHksVJOcN96AYyc+3YVpJzaYMMhHbQDk6qaikf4CkYRS5kW1ZTzSYKX8D7NfYZPVgJFqqeOJ6AMAAAAAAAAA4fUFAAAAAAA='
        )
        .resolves(new BigNumber(1997880));
      const res = await basecoin.recover({
        bitgoKey:
          '7d91f69c285c0b3b0a0d6371020f194d45956ee556289e7854f6d114e07805720eec673c6a2\n72b7da4137db7b1289f38af4f4b824ef0de65c5f53e3e66617617',
        recoveryDestination: '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc',
      });

      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '000002000824cd096b00000000002032d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc02020001010000010102000001010000e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389029a363c91d29b50832ab98094b6c1933941280fc298a2ec232739f14ce31e2582c50000000000000020c63985f505cee315698dfe2f09994efca945200944b357a1c45847dc8a6a55f260aefaffa35daa32a1e561f2ba9c18753057d2feb502f32804e573ea2875a39cc300000000000000200ad793ed3ba83048dc746c792c54939c37de8063273edd856927369830c8476d00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389e803000000000000a48821000000000000',
                  scanIndex: 0,
                  coin: 'tsui',
                  signableHex: '3eec395ccaee7d6a0b9fa5488dd00bfeb89dc4bd37e3139ebebb62c2e60bc00d',
                  derivationPath: 'm/0',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389',
                        valueString: '1795804452',
                        value: new BigNumber(1795804452),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc',
                        valueString: '1795804452',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '1795804452',
                    type: 'Transfer',
                  },
                  feeInfo: { fee: 2197668, feeString: '2197668' },
                  coinSpecific: {
                    commonKeychain:
                      '7d91f69c285c0b3b0a0d6371020f194d45956ee556289e7854f6d114e07805720eec673c6a272b7da4137db7b1289f38af4f4b824ef0de65c5f53e3e66617617',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
        ],
      });

      const unsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      const serializedTxHex = res.txRequests[0].transactions[0].unsignedTx.serializedTx;
      const serializedTxBase64 = Buffer.from(serializedTxHex, 'hex').toString('base64');
      unsignedSweepTxnDeserialize.fromRawTransaction(serializedTxBase64);
      const unsignedSweepTxnJson = unsignedSweepTxnDeserialize.toJson();
      should.equal(unsignedSweepTxnJson.id, '7Jyx3KUVXxs1Q2i1RqXtj4hFgUgBon6PUQAovL84Y3eP');
      should.equal(unsignedSweepTxnJson.sender, senderAddress);

      sandBox.assert.callCount(basecoin.getBalance, 1);
      sandBox.assert.callCount(basecoin.getInputCoins, 1);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });
  });

  describe('Recover Token Transactions:', () => {
    const sandBox = sinon.createSandbox();
    const coinType = '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP';
    const senderAddress0 = '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3';
    const senderAddressColdWallet = '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421';
    const recoveryDestination = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';
    const tokenContractAddress = '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8';
    let getBalanceStub: sinon.SinonStub;
    let getInputCoinsStub: sinon.SinonStub;
    let getFeeEstimateStub: sinon.SinonStub;

    beforeEach(() => {
      getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      getFeeEstimateStub = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
    });

    afterEach(() => {
      sandBox.restore();
    });

    it('should recover a token txn for non-bitgo recovery', async function () {
      getBalanceStub.withArgs(senderAddress0, coinType).resolves('1000');
      getInputCoinsStub.withArgs(senderAddress0, coinType).resolves([
        {
          coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
          objectId: '0x924ab69ebba304f2975a588372b41e4e1f5db7fa824868f84199eeb1e0a15a2d',
          version: '34696807',
          digest: '7XRbWQTiwAUCjLLsZVpJMrABCheJBkzKVfCr7aTZZVkd',
          balance: new BigNumber(1000),
        },
      ]);
      getInputCoinsStub.withArgs(senderAddress0).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x9146928f557cb8ab1915a5886c1362435a05b4709b586bb01d4c70e85bb53161',
          version: '239',
          digest: 'GLSzR6HJ319nPKAFm5x3TWHcaHZzCFSBCqhvZ1qwT5wr',
          balance: new BigNumber('1230261076'),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x93f700dc82e229f699f47b167859c5108288b78f54068800cc290900d0de8429',
          version: '149',
          digest: '2wXDxt2ZxekBxN6oW3BqVDXZcX49C87n1AN26Nyu7kPR',
          balance: new BigNumber('169611024'),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0xb869b45744ca2c4a663c6e8679db5a632bead0364273842d3990d7ff65d57b1e',
          version: '237',
          digest: 'F1xWKPVHV5WL72ErjgQxrjn3RNH2GHjyN9jTgwW7Qphk',
          balance: new BigNumber('101976'),
        },
      ]);
      getFeeEstimateStub
        .withArgs(
          'AAADAQCSSraeu6ME8pdaWINytB5OH123+oJIaPhBme6x4KFaLWduEQIAAAAAIGDxWv07uqv0hEZiOG0FC/xG830WBmRDZLOLmMp61gdSAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECAJHyXiN7g6AKYnJP3EqB5D9JTca0GhJBSSgm025NEx2jAZFGko9VfLirGRWliGwTYkNaBbRwm1hrsB1McOhbtTFh7wAAAAAAAAAg49q55eJGGM5xtoJzY233UnL2dj1Kb5N90YxZ70QopW2R8l4je4OgCmJyT9xKgeQ/SU3GtBoSQUkoJtNuTRMdo+gDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber(2345504));

      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
        tokenContractAddress,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(0);
      tx.recoveryAmount.should.equal('1000');
      tx.serializedTx.should.equal(
        'AAADAQCSSraeu6ME8pdaWINytB5OH123+oJIaPhBme6x4KFaLWduEQIAAAAAIGDxWv07uqv0hEZiOG0FC/xG830WBmRDZLOLmMp61gdSAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECAJHyXiN7g6AKYnJP3EqB5D9JTca0GhJBSSgm025NEx2jAZFGko9VfLirGRWliGwTYkNaBbRwm1hrsB1McOhbtTFh7wAAAAAAAAAg49q55eJGGM5xtoJzY233UnL2dj1Kb5N90YxZ70QopW2R8l4je4OgCmJyT9xKgeQ/SU3GtBoSQUkoJtNuTRMdo+gDAAAAAAAAVl4nAAAAAAAA'
      );

      const NonBitGoTxnDeserialize = new TokenTransferTransaction(basecoin);
      NonBitGoTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const NonBitGoTxnJson = NonBitGoTxnDeserialize.toJson();

      should.equal(NonBitGoTxnJson.id, 'DYW9mA8AZGQntk7HGQUEoEdy8BaH8Hh9Ts294EnqGTEr');
      should.equal(NonBitGoTxnJson.sender, senderAddress0);
      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a token txn for unsigned sweep recovery', async function () {
      getBalanceStub.withArgs(senderAddressColdWallet).resolves('298980240');
      getBalanceStub.withArgs(senderAddressColdWallet, coinType).resolves('1000');

      getInputCoinsStub.withArgs(senderAddressColdWallet, coinType).resolves([
        {
          coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
          objectId: '0x4c1cccc6d03510bd74a95f81b2b7da08119cb18e2c565435e7ad715e6aadc5a7',
          version: '34696809',
          digest: 'Afy4LW46tuczj341TbCLeeynxrupj9Lw2qBQUpndHc1c',
          balance: new BigNumber(1000),
        },
      ]);
      getInputCoinsStub.withArgs(senderAddressColdWallet).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x98114f2ddefe3f16d9d2e016194b52c4c3af430c1601bdb3539cbd237cbca068',
          version: '34696809',
          digest: 'DBtoqvWtrcYHE1HjQUo4igTkHYxY7D83iHSoKXi9A85W',
          balance: new BigNumber(200000000),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x9ec13440647b0bee74f45409589ad2ff5a18c9615f99d5d914f988b834da493e',
          version: '239',
          digest: '9d3uVsQ4p37TGnzTpnXXw2pFoggbVfVp9CdC4jpZq664',
          balance: new BigNumber(98980240),
        },
      ]);
      getFeeEstimateStub
        .withArgs(
          'AAADAQBMHMzG0DUQvXSpX4Gyt9oIEZyxjixWVDXnrXFeaq3Fp2luEQIAAAAAII+0ozS4Kazey/oqMoZCr6JNb5U3QPdoMIDuDks9OpYvAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECAAFkld3adIwRaiGCQoNdNkcJ1sXuXIoe1oTE70M9MUQhAZgRTy3e/j8W2dLgFhlLUsTDr0MMFgG9s1OcvSN8vKBoaW4RAgAAAAAgtRahW1A0vNQkJUQMVkzzb0OVQviGce95nd4GYeeJoAkBZJXd2nSMEWohgkKDXTZHCdbF7lyKHtaExO9DPTFEIegDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber(2345504));

      const res = await basecoin.recover({
        bitgoKey: keys.bitgoKeyColdWallet,
        recoveryDestination,
        tokenContractAddress,
      });

      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000301004c1cccc6d03510bd74a95f81b2b7da08119cb18e2c565435e7ad715e6aadc5a7696e110200000000208fb4a334b829acdecbfa2a328642afa24d6f953740f7683080ee0e4b3d3a962f0008e803000000000000002000e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e3890202010000010101000101020000010200016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d3144210198114f2ddefe3f16d9d2e016194b52c4c3af430c1601bdb3539cbd237cbca068696e11020000000020b516a15b5034bcd42425440c564cf36f439542f88671ef799dde0661e789a009016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421e803000000000000565e27000000000000',
                  scanIndex: 0,
                  coin: 'tsui:deep',
                  signableHex: 'e3547a58b9c48840a2774aeb71b8652a70b67ee59c3fa5b1f743738d8b295199',
                  derivationPath: 'm/0',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                        valueString: '1000',
                        value: new BigNumber(1000),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389',
                        valueString: '1000',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '1000',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 2580054,
                    feeString: '2580054',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
        ],
      });

      const unsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      const serializedTxHex = res.txRequests[0].transactions[0].unsignedTx.serializedTx;
      const serializedTxBase64 = Buffer.from(serializedTxHex, 'hex').toString('base64');
      unsignedSweepTxnDeserialize.fromRawTransaction(serializedTxBase64);
      const unsignedSweepTxnJson = unsignedSweepTxnDeserialize.toJson();
      should.equal(unsignedSweepTxnJson.id, 'F8wrUjZYf6xvDW2LzW9DohAKyJFcWgGEvjMoKLxCajmV');
      should.equal(unsignedSweepTxnJson.sender, senderAddressColdWallet);

      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a token txn for unsigned sweep recovery with multiple input coins', async function () {
      getBalanceStub.withArgs(senderAddressColdWallet).resolves('298980240');
      getBalanceStub.withArgs(senderAddressColdWallet, coinType).resolves('11000');
      getInputCoinsStub.withArgs(senderAddressColdWallet, coinType).resolves([
        {
          coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
          objectId: '0x4c1cccc6d03510bd74a95f81b2b7da08119cb18e2c565435e7ad715e6aadc5a7',
          version: '34696809',
          digest: 'Afy4LW46tuczj341TbCLeeynxrupj9Lw2qBQUpndHc1c',
          balance: new BigNumber('1000'),
        },
        {
          coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
          objectId: '0x72ad99130abd3790db7bac8908d50d3412a800a8fcd10c1cfbd64b8215537558',
          version: '34696865',
          digest: 'X8uaNDkSfYKa6PBzJJUe9StP4nRWR9RZoa2nJHqv5mn',
          balance: new BigNumber('10000'),
        },
      ]);
      getInputCoinsStub.withArgs(senderAddressColdWallet).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x98114f2ddefe3f16d9d2e016194b52c4c3af430c1601bdb3539cbd237cbca068',
          version: '34696809',
          digest: 'DBtoqvWtrcYHE1HjQUo4igTkHYxY7D83iHSoKXi9A85W',
          balance: new BigNumber('200000000'),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x9ec13440647b0bee74f45409589ad2ff5a18c9615f99d5d914f988b834da493e',
          version: '239',
          digest: '9d3uVsQ4p37TGnzTpnXXw2pFoggbVfVp9CdC4jpZq664',
          balance: new BigNumber('98980240'),
        },
      ]);
      getFeeEstimateStub
        .withArgs(
          'AAAEAQByrZkTCr03kNt7rIkI1Q00EqgAqPzRDBz71kuCFVN1WKFuEQIAAAAAIAe4XTi24bSF8UVZMNJylhCAgcqs12ZMSNfV9IVy7iu9AQBMHMzG0DUQvXSpX4Gyt9oIEZyxjixWVDXnrXFeaq3Fp2luEQIAAAAAII+0ozS4Kazey/oqMoZCr6JNb5U3QPdoMIDuDks9OpYvAAj4KgAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kDAwEAAAEBAQACAQAAAQECAAEBAgEAAQMAAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCEBmBFPLd7+PxbZ0uAWGUtSxMOvQwwWAb2zU5y9I3y8oGhpbhECAAAAACC1FqFbUDS81CQlRAxWTPNvQ5VC+IZx73md3gZh54mgCQFkld3adIwRaiGCQoNdNkcJ1sXuXIoe1oTE70M9MUQh6AMAAAAAAAAA4fUFAAAAAAA='
        )
        .resolves(new BigNumber(1036328));
      const res = await basecoin.recover({
        bitgoKey: keys.bitgoKeyColdWallet,
        recoveryDestination,
        tokenContractAddress,
      });

      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '000004010072ad99130abd3790db7bac8908d50d3412a800a8fcd10c1cfbd64b8215537558a16e1102000000002007b85d38b6e1b485f1455930d27296108081caacd7664c48d7d5f48572ee2bbd01004c1cccc6d03510bd74a95f81b2b7da08119cb18e2c565435e7ad715e6aadc5a7696e110200000000208fb4a334b829acdecbfa2a328642afa24d6f953740f7683080ee0e4b3d3a962f0008f82a000000000000002000e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e38903030100000101010002010000010102000101020100010300016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d3144210198114f2ddefe3f16d9d2e016194b52c4c3af430c1601bdb3539cbd237cbca068696e11020000000020b516a15b5034bcd42425440c564cf36f439542f88671ef799dde0661e789a009016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421e803000000000000f86411000000000000',
                  scanIndex: 0,
                  coin: 'tsui:deep',
                  signableHex: 'cd028c401f815dab7f6c5e8a33976879c5c799ae7c9fde01cafd0aee0075a174',
                  derivationPath: 'm/0',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                        valueString: '11000',
                        value: new BigNumber('11000'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389',
                        valueString: '11000',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '11000',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 1139960,
                    feeString: '1139960',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
        ],
      });

      const unsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      const serializedTxHex = res.txRequests[0].transactions[0].unsignedTx.serializedTx;
      const serializedTxBase64 = Buffer.from(serializedTxHex, 'hex').toString('base64');
      unsignedSweepTxnDeserialize.fromRawTransaction(serializedTxBase64);
      const unsignedSweepTxnJson = unsignedSweepTxnDeserialize.toJson();
      should.equal(unsignedSweepTxnJson.id, '4qeXJP7pTa6pmyAKuJZG9AkGsKM53SDqHVcPjRMFHjc5');
      should.equal(unsignedSweepTxnJson.sender, senderAddressColdWallet);

      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });
  });

  describe('Recover Transactions for wallet with multiple addresses:', () => {
    const sandBox = sinon.createSandbox();
    const senderAddress0 = '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3';
    const senderAddress1 = '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc';
    const recoveryDestination = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';

    beforeEach(function () {
      let callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      callBack.withArgs(senderAddress0).resolves('0').withArgs(senderAddress1).resolves('1800000000');

      callBack = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      callBack.withArgs(senderAddress1).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0xff93adc2f516fcaa0c6040e01f50027a23f9b1767f5040eb2282790a6900ce7f',
          version: '196',
          digest: 'XrjRM9ZM98xdNWigHYQjCpGoWt6aZLpqXdSEixnhb4p',
          balance: new BigNumber('1800000000'),
        },
      ]);

      callBack = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
      callBack
        .withArgs(
          'AAACAAgA8VNlAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgABAQAAAQECAAABAQAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzAH/k63C9Rb8qgxgQOAfUAJ6I/mxdn9QQOsignkKaQDOf8QAAAAAAAAAIAfnp90gMsdBGGz1tW/sFQlArhkRmYjdiXTXx+CvxHjVMtjlfubZHlVY2gZ3FUwvCFeVNI4xf5Wsye+t4bQRL8zoAwAAAAAAAADh9QUAAAAAAA=='
        )
        .resolves(new BigNumber(1997880));
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should recover a txn for non-bitgo recoveries at address 1 but search from address 0', async function () {
      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(1);
      tx.recoveryAmount.should.equal('1797802332');
      tx.serializedTx.should.equal(
        'AAACAAhcSShrAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgABAQAAAQECAAABAQAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzAH/k63C9Rb8qgxgQOAfUAJ6I/mxdn9QQOsignkKaQDOf8QAAAAAAAAAIAfnp90gMsdBGGz1tW/sFQlArhkRmYjdiXTXx+CvxHjVMtjlfubZHlVY2gZ3FUwvCFeVNI4xf5Wsye+t4bQRL8zoAwAAAAAAAKSIIQAAAAAAAA=='
      );

      const UnsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      UnsignedSweepTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const UnsignedSweepTxnJson = UnsignedSweepTxnDeserialize.toJson();

      should.equal(UnsignedSweepTxnJson.id, 'BqHcCR51mqUyi5GJYEED3cw9AgpJ3SraTrt5aE9nLkLj');
      should.equal(UnsignedSweepTxnJson.sender, senderAddress1);
      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 1);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a txn for non-bitgo recoveries at address 1 but search from address 1', async function () {
      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
        startingScanIndex: 1,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(1);
      tx.recoveryAmount.should.equal('1797802332');
      tx.serializedTx.should.equal(
        'AAACAAhcSShrAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgABAQAAAQECAAABAQAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzAH/k63C9Rb8qgxgQOAfUAJ6I/mxdn9QQOsignkKaQDOf8QAAAAAAAAAIAfnp90gMsdBGGz1tW/sFQlArhkRmYjdiXTXx+CvxHjVMtjlfubZHlVY2gZ3FUwvCFeVNI4xf5Wsye+t4bQRL8zoAwAAAAAAAKSIIQAAAAAAAA=='
      );

      const UnsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      UnsignedSweepTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const UnsignedSweepTxnJson = UnsignedSweepTxnDeserialize.toJson();

      should.equal(UnsignedSweepTxnJson.id, 'BqHcCR51mqUyi5GJYEED3cw9AgpJ3SraTrt5aE9nLkLj');
      should.equal(UnsignedSweepTxnJson.sender, senderAddress1);
      sandBox.assert.callCount(basecoin.getBalance, 1);
      sandBox.assert.callCount(basecoin.getInputCoins, 1);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });
  });

  describe('Recover Token Transactions for wallet with multiple addresses:', () => {
    const sandBox = sinon.createSandbox();
    const senderAddress0 = '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3';
    const senderAddress1 = '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc';
    const recoveryDestination = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';
    const packageId = '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8';
    const coinType = `${packageId}::deep::DEEP`;
    let getBalanceStub: sinon.SinonStub;
    let getInputCoinsStub: sinon.SinonStub;
    let getFeeEstimateStub: sinon.SinonStub;

    beforeEach(function () {
      getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      getBalanceStub
        .withArgs(senderAddress0)
        .resolves('706875692')
        .withArgs(senderAddress0, coinType)
        .resolves('0')
        .withArgs(senderAddress1)
        .resolves('120101976')
        .withArgs(senderAddress1, coinType)
        .resolves('1000');

      getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      getInputCoinsStub.withArgs(senderAddress1, coinType).resolves([
        {
          coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
          objectId: '0xcdba76bdc3f460ed0d6af834fc17b082eb9eabd0b886f9af17cd4291f2f862fa',
          version: '34696866',
          digest: '7vbSgaGytATtnQ2fLRQo3VtCwYr9KjxsuxLLMj2dfCsU',
          balance: new BigNumber('1000'),
        },
      ]);
      getInputCoinsStub.withArgs(senderAddress1).resolves([
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x0eca78901e342b2515fbf99be243b3f00ade0a7a50675b37307f44cc195c9046',
          version: '34696875',
          digest: 'APE3nj4zYm4hY2Xn9C2n3ynyPiYqxgTRejYT7ParhpU7',
          balance: new BigNumber('120000000'),
        },
        {
          coinType: '0x2::sui::SUI',
          objectId: '0x761222340db80dd9da144d55098870bc81a788205e0bbdd34c2f8df12cf45aeb',
          version: '227',
          digest: '9bwyjaSH3X8anrjGUpD1NqMrLFUtGSCK9LFuLbhjX69E',
          balance: new BigNumber('101976'),
        },
      ]);

      getFeeEstimateStub = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
      getFeeEstimateStub
        .withArgs(
          'AAADAQDNuna9w/Rg7Q1q+DT8F7CC656r0LiG+a8XzUKR8vhi+qJuEQIAAAAAIGbg2VphuEezzTVLyBTrQGC+GJWMRSV5YJWka7fwmD9LAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECADLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/MAQ7KeJAeNCslFfv5m+JDs/AK3gp6UGdbNzB/RMwZXJBGq24RAgAAAAAgi2q2sUgd0frgxWZVkgajJAb1V5f1VzaTrXEYIxkLqDAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzOgDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber(2345504));
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should recover a token txn for non-bitgo recoveries at address 1 but search from address 0', async function () {
      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
        tokenContractAddress: packageId,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(1);
      tx.recoveryAmount.should.equal('1000');
      tx.serializedTx.should.equal(
        'AAADAQDNuna9w/Rg7Q1q+DT8F7CC656r0LiG+a8XzUKR8vhi+qJuEQIAAAAAIGbg2VphuEezzTVLyBTrQGC+GJWMRSV5YJWka7fwmD9LAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECADLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/MAQ7KeJAeNCslFfv5m+JDs/AK3gp6UGdbNzB/RMwZXJBGq24RAgAAAAAgi2q2sUgd0frgxWZVkgajJAb1V5f1VzaTrXEYIxkLqDAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzOgDAAAAAAAAVl4nAAAAAAAA'
      );

      const UnsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      UnsignedSweepTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const UnsignedSweepTxnJson = UnsignedSweepTxnDeserialize.toJson();

      should.equal(UnsignedSweepTxnJson.id, 'GFuk1VKy3wzTFeAUtrmUe6sxRhtezzrGDfKdpQTxv9so');
      should.equal(UnsignedSweepTxnJson.sender, senderAddress1);
      sandBox.assert.callCount(basecoin.getBalance, 4);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });

    it('should recover a token txn for non-bitgo recoveries at address 1 but search from address 1', async function () {
      const res = await basecoin.recover({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        recoveryDestination,
        walletPassphrase,
        tokenContractAddress: packageId,
        startingScanIndex: 1,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('transactions');
      const tx = res.transactions[0];
      tx.scanIndex.should.equal(1);
      tx.recoveryAmount.should.equal('1000');
      tx.serializedTx.should.equal(
        'AAADAQDNuna9w/Rg7Q1q+DT8F7CC656r0LiG+a8XzUKR8vhi+qJuEQIAAAAAIGbg2VphuEezzTVLyBTrQGC+GJWMRSV5YJWka7fwmD9LAAjoAwAAAAAAAAAgAOTqpqKR/gKRhFLmRbVlPNJgpfwPs19hk9WAkWqp44kCAgEAAAEBAQABAQIAAAECADLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/MAQ7KeJAeNCslFfv5m+JDs/AK3gp6UGdbNzB/RMwZXJBGq24RAgAAAAAgi2q2sUgd0frgxWZVkgajJAb1V5f1VzaTrXEYIxkLqDAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzOgDAAAAAAAAVl4nAAAAAAAA'
      );

      const UnsignedSweepTxnDeserialize = new TransferTransaction(basecoin);
      UnsignedSweepTxnDeserialize.fromRawTransaction(tx.serializedTx);
      const UnsignedSweepTxnJson = UnsignedSweepTxnDeserialize.toJson();

      should.equal(UnsignedSweepTxnJson.id, 'GFuk1VKy3wzTFeAUtrmUe6sxRhtezzrGDfKdpQTxv9so');
      should.equal(UnsignedSweepTxnJson.sender, senderAddress1);
      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 1);
    });
  });

  describe('Recover Consolidation Transactions', () => {
    const sandBox = sinon.createSandbox();
    const receiveAddress1 = '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc';
    const receiveAddress2 = '0xdf407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';

    const seedReceiveAddress1 = '0xd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a';
    const seedReceiveAddress2 = '0x2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9';

    beforeEach(function () {
      let callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      callBack
        .withArgs(receiveAddress1)
        .resolves('200101976')
        .withArgs(receiveAddress2)
        .resolves('200000000')
        .withArgs(seedReceiveAddress1)
        .resolves('500000000')
        .withArgs(seedReceiveAddress2)
        .resolves('200000000');

      callBack = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      callBack
        .withArgs(receiveAddress1)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x996aab365d4551b6d1274f520bbfa7b0a566d548b2d590b5565c623812e7e76d',
            version: '201',
            digest: 'HXpNTfx9TBdxFcXHi4RziZsQuDAHavRasK6Ri15rVwuA',
            balance: new BigNumber('200000000'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xb39c5f380208cce7fe1ba1258c8d19befb02a80f14952617ed37098dbd4d2df0',
            version: '199',
            digest: 'mqk37hXLkiUYgkYxk2MyqNykCkCXwe97uMus7bDPhe2',
            balance: new BigNumber('101976'),
          },
        ])
        .withArgs(receiveAddress2)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xfa04105eedebdabf729dccecf01d0cf5f1b770892fac2ed8f1e69d71a32a2d24',
            version: '202',
            digest: 'DeApRVSrTa9ttXvNyLexT4PJcAkyyxSpi3JQeUg4ua8Q',
            balance: new BigNumber('200000000'),
          },
        ])
        .withArgs(seedReceiveAddress1)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x86e728fd7242b3be60e9c1941add2c47fb655779108c3500a216310218748e2d',
            version: '147',
            digest: '6PJeyEX4L8RkjsNaF5GSMCwRjmFGYrQB7fbmJPFMMPHL',
            balance: '500000000',
          },
        ])
        .withArgs(seedReceiveAddress2)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xfd66dbfe7a1497f210747b0532f62e6926cb144c75e519f2dfff17a4f6e515fc',
            version: '148',
            digest: 'PJ3jfS1ERShSMtFWZgma4h6duWX8nm5Bei1z3BszyBk',
            balance: '200000000',
          },
        ]);

      callBack = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
      callBack
        .withArgs(
          'AAACAAhYb/cFAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgABAQAAAQECAAABAQAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzAKZaqs2XUVRttEnT1ILv6ewpWbVSLLVkLVWXGI4EufnbckAAAAAAAAAIPWf+f1EklW1ggi3rwo5Jw3Lftu9W/UsyrZrI0FYsBTZs5xfOAIIzOf+G6EljI0ZvvsCqA8UlSYX7TcJjb1NLfDHAAAAAAAAACALfKsZ4T9Y2y1rrlBxQQ2BouS9VFjBgVfvbbFf9U+f8zLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/M6AMAAAAAAAAA4fUFAAAAAAA='
        )
        .resolves(new BigNumber('1019760'))
        .withArgs(
          'AAACAAgA4fUFAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgABAQAAAQECAAABAQDfQH4+JelAD5d5rHVxU3wjYWhBlPGqXbEmqPV0te2FHAH6BBBe7evav3KdzOzwHQz18bdwiS+sLtjx5p1xoyotJMoAAAAAAAAAILvR1XjlKKylRGh9pX4UpBFBsCv5At6RBn+XXDAbnfRB30B+PiXpQA+Xeax1cVN8I2FoQZTxql2xJqj1dLXthRzoAwAAAAAAAADh9QUAAAAAAA=='
        )
        .resolves(new BigNumber('1997880'))
        .withArgs(
          'AAACAAgAhNcXAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgABAQAAAQECAAABAQDSAVZuagvAIP0ub3LpveIiP1UNZNqmE5jNkXwvdQEySgGG5yj9ckKzvmDpwZQa3SxH+2VXeRCMNQCiFjECGHSOLZMAAAAAAAAAIFABLLLjMJc51R0P8DHy0PbXwsYUD0b5gvVmiZsupafr0gFWbmoLwCD9Lm9y6b3iIj9VDWTaphOYzZF8L3UBMkroAwAAAAAAAADh9QUAAAAAAA=='
        )
        .resolves(new BigNumber('1997880'))
        .withArgs(
          'AAACAAgA4fUFAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgABAQAAAQECAAABAQAvpdg5S9a+xVJblVC/Q74HW4NCLQEHwFxwCUTj6uwm+QH9Ztv+ehSX8hB0ewUy9i5pJssUTHXlGfLf/xek9uUV/JQAAAAAAAAAIAW2DZdVWJ9ENnvL4Z2POOHfK2Z74DgSgDQci60MdQuXL6XYOUvWvsVSW5VQv0O+B1uDQi0BB8BccAlE4+rsJvnoAwAAAAAAAADh9QUAAAAAAA=='
        )
        .resolves(new BigNumber('1997880'));
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should build signed consolidation transactions for hot wallet', async function () {
      const res = await basecoin.recoverConsolidations({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        walletPassphrase,
        startingScanIndex: 1,
        endingScanIndex: 3,
      });

      const transactions = res.transactions;
      transactions.length.should.equal(2);
      const txn1 = transactions[0];
      txn1.scanIndex.should.equal(1);
      txn1.recoveryAmount.should.equal('198980240');
      txn1.serializedTx.should.equal(
        'AAACAAiQMtwLAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgABAQAAAQECAAABAQAy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzAKZaqs2XUVRttEnT1ILv6ewpWbVSLLVkLVWXGI4EufnbckAAAAAAAAAIPWf+f1EklW1ggi3rwo5Jw3Lftu9W/UsyrZrI0FYsBTZs5xfOAIIzOf+G6EljI0ZvvsCqA8UlSYX7TcJjb1NLfDHAAAAAAAAACALfKsZ4T9Y2y1rrlBxQQ2BouS9VFjBgVfvbbFf9U+f8zLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/M6AMAAAAAAADIHREAAAAAAAA='
      );

      const txn2 = transactions[1];
      txn2.scanIndex.should.equal(2);
      txn2.recoveryAmount.should.equal('197802332');
      txn2.serializedTx.should.equal(
        'AAACAAhcOcoLAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgABAQAAAQECAAABAQDfQH4+JelAD5d5rHVxU3wjYWhBlPGqXbEmqPV0te2FHAH6BBBe7evav3KdzOzwHQz18bdwiS+sLtjx5p1xoyotJMoAAAAAAAAAILvR1XjlKKylRGh9pX4UpBFBsCv5At6RBn+XXDAbnfRB30B+PiXpQA+Xeax1cVN8I2FoQZTxql2xJqj1dLXthRzoAwAAAAAAAKSIIQAAAAAAAA=='
      );

      res.lastScanIndex.should.equal(2);

      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });

    it('should build unsigned consolidation transactions for cold wallet', async function () {
      const res = await basecoin.recoverConsolidations({
        bitgoKey: keys.bitgoKey,
        startingScanIndex: 1,
        endingScanIndex: 3,
      });
      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000200089032dc0b00000000002091f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da302020001010000010102000001010032d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc02996aab365d4551b6d1274f520bbfa7b0a566d548b2d590b5565c623812e7e76dc90000000000000020f59ff9fd449255b58208b7af0a39270dcb7edbbd5bf52ccab66b234158b014d9b39c5f380208cce7fe1ba1258c8d19befb02a80f14952617ed37098dbd4d2df0c700000000000000200b7cab19e13f58db2d6bae5071410d81a2e4bd5458c18157ef6db15ff54f9ff332d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcce803000000000000c81d11000000000000',
                  scanIndex: 1,
                  coin: 'tsui',
                  signableHex: 'ab84c9d09c678c038439f63fcd40d26535cc1485151257f4f175b6e8a0e94316',
                  derivationPath: 'm/1',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc',
                        valueString: '198980240',
                        value: new BigNumber('198980240'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3',
                        valueString: '198980240',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '198980240',
                    type: 'Transfer',
                  },
                  feeInfo: {
                    fee: 1121736,
                    feeString: '1121736',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '3b89eec9d2d2f3b049ecda2e7b5f47827f7927fe6618d6e8b13f64e7c95f4b00b9577ab01395ecf8eeb804b590cedae14ff5fd3947bf3b7a95b9327c49e27c54',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000200085c39ca0b00000000002091f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3020200010100000101020000010100df407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c01fa04105eedebdabf729dccecf01d0cf5f1b770892fac2ed8f1e69d71a32a2d24ca0000000000000020bbd1d578e528aca544687da57e14a41141b02bf902de91067f975c301b9df441df407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851ce803000000000000a48821000000000000',
                  scanIndex: 2,
                  coin: 'tsui',
                  signableHex: '66d650b4c4dedf5ab33c9381ee46140b30bb896cf2c66b7b241cae70d1414f16',
                  derivationPath: 'm/2',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0xdf407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c',
                        valueString: '197802332',
                        value: new BigNumber('197802332'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3',
                        valueString: '197802332',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '197802332',
                    type: 'Transfer',
                  },
                  feeInfo: {
                    fee: 2197668,
                    feeString: '2197668',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '3b89eec9d2d2f3b049ecda2e7b5f47827f7927fe6618d6e8b13f64e7c95f4b00b9577ab01395ecf8eeb804b590cedae14ff5fd3947bf3b7a95b9327c49e27c54',
                    lastScanIndex: 2,
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
        ],
      });

      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });

    it('should build unsigned consolidation transactions for cold wallet with seed', async function () {
      const res = await basecoin.recoverConsolidations({
        bitgoKey: keys.bitgoKeyWithSeed,
        startingScanIndex: 1,
        endingScanIndex: 3,
        seed: '123',
      });
      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000200085cdcab1d00000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404020200010100000101020000010100d201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a0186e728fd7242b3be60e9c1941add2c47fb655779108c3500a216310218748e2d93000000000000002050012cb2e3309739d51d0ff031f2d0f6d7c2c6140f46f982f566899b2ea5a7ebd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324ae803000000000000a48821000000000000',
                  scanIndex: 1,
                  coin: 'tsui',
                  signableHex: '6d7a4c4882745707c2cd40b0c57566601a2a4d269f5cb0a692828551212cf452',
                  derivationPath: 'm/999999/94862622/157363509/1',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0xd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a',
                        valueString: '497802332',
                        value: new BigNumber(497802332),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                        valueString: '497802332',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '497802332',
                    type: 'Transfer',
                  },
                  feeInfo: {
                    fee: 2197668,
                    feeString: '2197668',
                  },
                  coinSpecific: {
                    commonKeychain:
                      'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000200085c39ca0b00000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b24040202000101000001010200000101002fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f901fd66dbfe7a1497f210747b0532f62e6926cb144c75e519f2dfff17a4f6e515fc94000000000000002005b60d9755589f44367bcbe19d8f38e1df2b667be0381280341c8bad0c750b972fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9e803000000000000a48821000000000000',
                  scanIndex: 2,
                  coin: 'tsui',
                  signableHex: '32a00a6d892630467c7d0666390d3b27caaf4aefee3dda9e0c97c3f8bc06bcea',
                  derivationPath: 'm/999999/94862622/157363509/2',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9',
                        valueString: '197802332',
                        value: new BigNumber(197802332),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                        valueString: '197802332',
                        coinName: 'tsui',
                      },
                    ],
                    spendAmount: '197802332',
                    type: 'Transfer',
                  },
                  feeInfo: {
                    fee: 2197668,
                    feeString: '2197668',
                  },
                  coinSpecific: {
                    commonKeychain:
                      'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                    lastScanIndex: 2,
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui',
          },
        ],
      });

      sandBox.assert.callCount(basecoin.getBalance, 2);
      sandBox.assert.callCount(basecoin.getInputCoins, 2);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });
  });

  describe('Recover Token Consolidation Transactions', () => {
    const sandBox = sinon.createSandbox();
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';

    const seedReceiveAddress1 = '0xd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a';
    const seedReceiveAddress2 = '0x2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9';

    const packageId = '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8';
    const coinType = `${packageId}::deep::DEEP`;
    const hotWalletReceiveAddress1 = '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc';
    const hotWalletReceiveAddress2 = '0xdf407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c';
    const coldWalletReceiveAddress1 = '0xa992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675f';
    const coldWalletReceiveAddress2 = '0xc3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae39';

    let getBalanceStub: sinon.SinonStub;
    let getInputCoinsStub: sinon.SinonStub;
    let getFeeEstimateStub: sinon.SinonStub;

    beforeEach(function () {
      getBalanceStub = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      getInputCoinsStub = sandBox.stub(Sui.prototype, 'getInputCoins' as keyof Sui);
      getFeeEstimateStub = sandBox.stub(Sui.prototype, 'getFeeEstimate' as keyof Sui);
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should build signed token consolidation transactions for hot wallet', async function () {
      getBalanceStub
        .withArgs(hotWalletReceiveAddress1)
        .resolves('116720144')
        .withArgs(hotWalletReceiveAddress1, coinType)
        .resolves('1500')
        .withArgs(hotWalletReceiveAddress2)
        .resolves('120101976')
        .withArgs(hotWalletReceiveAddress2, coinType)
        .resolves('2000');
      getInputCoinsStub
        .withArgs(hotWalletReceiveAddress1, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0x8d2da577227e9b7166dbb9b3cce480968cf549b448c662771d1124d6420bf792',
            version: '34696878',
            digest: 'BniWiG3v71ozzRirb3hNKEP16pTu3CpNt3voy1uzgMi9',
            balance: new BigNumber('1500'),
          },
        ])
        .withArgs(hotWalletReceiveAddress1)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x0eca78901e342b2515fbf99be243b3f00ade0a7a50675b37307f44cc195c9046',
            version: '34696877',
            digest: 'D8XqJXrkVGoer952GvHBdBDXpof8H4F4pRJJ1wSnESNW',
            balance: new BigNumber('116618168'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x761222340db80dd9da144d55098870bc81a788205e0bbdd34c2f8df12cf45aeb',
            version: '227',
            digest: '9bwyjaSH3X8anrjGUpD1NqMrLFUtGSCK9LFuLbhjX69E',
            balance: new BigNumber('101976'),
          },
        ])
        .withArgs(hotWalletReceiveAddress2, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0x8e18bd14c34e33f81c494f4d6c18d07c686c7f3619682d2066b3f55ecfe707a2',
            version: '34696868',
            digest: '6jZFu8PRWLJYPewW7K3o4GLVPYnRyNo3kP6454eLuHZm',
            balance: new BigNumber('2000'),
          },
        ])
        .withArgs(hotWalletReceiveAddress2)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x811a75066134945e3b033f4ac54ec885ec0384f42a80994d88a48d7d18c4260a',
            version: '236',
            digest: '32N9ZyferGDEV7PY3ykijVVKhLjEFSL6itqcfRPCy4oW',
            balance: new BigNumber('101976'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xf85a738a30566d558800228f2d5c193a5ca3ddbbddca2a65a48a9332306715cc',
            version: '34696876',
            digest: '7gJgCMDEPdQaMeuXZ5CFNonFKMPQyw6k5sT1RpaHEXpe',
            balance: new BigNumber('120000000'),
          },
        ]);
      getFeeEstimateStub
        .withArgs(
          'AAADAQCNLaV3In6bcWbbubPM5ICWjPVJtEjGYncdESTWQgv3kq5uEQIAAAAAIKBK8Zjq4yz/PTFZLg6jf6gDTsogig63pduXeiKgzMVKAAjcBQAAAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgEAAAEBAQABAQIAAAECADLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/MAQ7KeJAeNCslFfv5m+JDs/AK3gp6UGdbNzB/RMwZXJBGrW4RAgAAAAAgtDor6b9EJHOSWdJZCQdLkKpS2cPoBc5b28KpX1rg7lsy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzOgDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'))
        .withArgs(
          'AAADAQCOGL0Uw04z+BxJT01sGNB8aGx/NhloLSBms/Vez+cHoqRuEQIAAAAAIFUxTTQyTKM6bVjeFHHT8KVrV2fgTOCX7Uoevk2r5JPcAAjQBwAAAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgEAAAEBAQABAQIAAAECAN9Afj4l6UAPl3msdXFTfCNhaEGU8apdsSao9XS17YUcAfhac4owVm1ViAAijy1cGTpco9273coqZaSKkzIwZxXMrG4RAgAAAAAgYzfCWiypCmme8IJZhqn5nT8+zoP3S7sfQKfezW99YWvfQH4+JelAD5d5rHVxU3wjYWhBlPGqXbEmqPV0te2FHOgDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'));

      const res = await basecoin.recoverConsolidations({
        userKey: keys.userKey,
        backupKey: keys.backupKey,
        bitgoKey: keys.bitgoKey,
        walletPassphrase,
        tokenContractAddress: packageId,
        startingScanIndex: 1,
        endingScanIndex: 3,
      });

      const transactions = res.transactions;
      transactions.length.should.equal(2);
      const txn1 = transactions[0];
      txn1.scanIndex.should.equal(1);
      txn1.recoveryAmount.should.equal('1500');
      txn1.serializedTx.should.equal(
        'AAADAQCNLaV3In6bcWbbubPM5ICWjPVJtEjGYncdESTWQgv3kq5uEQIAAAAAIKBK8Zjq4yz/PTFZLg6jf6gDTsogig63pduXeiKgzMVKAAjcBQAAAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgEAAAEBAQABAQIAAAECADLY5X7m2R5VWNoGdxVMLwhXlTSOMX+VrMnvreG0ES/MAQ7KeJAeNCslFfv5m+JDs/AK3gp6UGdbNzB/RMwZXJBGrW4RAgAAAAAgtDor6b9EJHOSWdJZCQdLkKpS2cPoBc5b28KpX1rg7lsy2OV+5tkeVVjaBncVTC8IV5U0jjF/lazJ763htBEvzOgDAAAAAAAAVl4nAAAAAAAA'
      );

      const txn2 = transactions[1];
      txn2.scanIndex.should.equal(2);
      txn2.recoveryAmount.should.equal('2000');
      txn2.serializedTx.should.equal(
        'AAADAQCOGL0Uw04z+BxJT01sGNB8aGx/NhloLSBms/Vez+cHoqRuEQIAAAAAIFUxTTQyTKM6bVjeFHHT8KVrV2fgTOCX7Uoevk2r5JPcAAjQBwAAAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMCAgEAAAEBAQABAQIAAAECAN9Afj4l6UAPl3msdXFTfCNhaEGU8apdsSao9XS17YUcAfhac4owVm1ViAAijy1cGTpco9273coqZaSKkzIwZxXMrG4RAgAAAAAgYzfCWiypCmme8IJZhqn5nT8+zoP3S7sfQKfezW99YWvfQH4+JelAD5d5rHVxU3wjYWhBlPGqXbEmqPV0te2FHOgDAAAAAAAAVl4nAAAAAAAA'
      );

      res.lastScanIndex.should.equal(2);

      sandBox.assert.callCount(basecoin.getBalance, 4);
      sandBox.assert.callCount(basecoin.getInputCoins, 4);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });

    it('should build unsigned token consolidation transactions for cold wallet', async function () {
      getBalanceStub
        .withArgs(coldWalletReceiveAddress1)
        .resolves('116720144')
        .withArgs(coldWalletReceiveAddress1, coinType)
        .resolves('4000')
        .withArgs(coldWalletReceiveAddress2)
        .resolves('120101976')
        .withArgs(coldWalletReceiveAddress2, coinType)
        .resolves('6000');
      getInputCoinsStub
        .withArgs(coldWalletReceiveAddress1, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0x9e2618685fff38b999355e93a4693c4bd18f68dd6b654ff6555f1f4ad229dcdb',
            version: '34696876',
            digest: 'F5iGjs95CnScCtXHFuTJhdZkfLyagWR1Qm1zDH6SngMG',
            balance: new BigNumber('4000'),
          },
        ])
        .withArgs(coldWalletReceiveAddress1)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x8f3f7c8961997327166b1571117ff67232ccd1267ec52a7e973677ba0431ffad',
            version: '226',
            digest: 'AWh7tPmS9xmH4a2zYLCG73YDiskCf8uizbLjpCStHhNV',
            balance: new BigNumber('98980240'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x916e35a0ac6b525b7d38a44b667394aaee7cde41dffc876980cfbc8845590cdd',
            version: '34696878',
            digest: '14PfNwUvfcR3mNAS1bcNXToaooVLnXQHDdGxPFRJQtY',
            balance: new BigNumber('120000000'),
          },
        ])
        .withArgs(coldWalletReceiveAddress2, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0xc548c220454d20e0712bd2858b07260c829a1c847f3818b47805961b574813ab',
            version: '34696877',
            digest: '8mANdoskG8eLLy7zVU5rmHRAXVHUyJPABfS1mYHSmAYt',
            balance: new BigNumber('6000'),
          },
        ])
        .withArgs(coldWalletReceiveAddress2)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x396ab09ee58fb2c9fbcca887c00ef01a33d5bfe55a5d50514e32a59f948e9a05',
            version: '234',
            digest: 'HWKW56dt8BNxSTvXiW7dtDVmM2gxEexFyNVzGj8HPVp4',
            balance: new BigNumber('101976'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xd4bfefa532827d7619a58279a87db81632e267cc48e6b94ab81a4367f02cbefa',
            version: '34696879',
            digest: 'GJnp1oXNjVXLcYY6hqRV8Rm7G7obmk3s8Nhj1bFsfkUo',
            balance: new BigNumber('120000000'),
          },
        ]);
      getFeeEstimateStub
        .withArgs(
          'AAADAQCeJhhoX/84uZk1XpOkaTxL0Y9o3WtlT/ZVXx9K0inc26xuEQIAAAAAINE4mxs1hGMmovTU5dfnFoouWOPdVn0nRDPbuXymALPLAAigDwAAAAAAAAAgAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCECAgEAAAEBAQABAQIAAAECAKmScJWR3rdHH7MN2g8znberVI0zkaidPx+gxy0gkmdfAZFuNaCsa1JbfTikS2ZzlKrufN5B3/yHaYDPvIhFWQzdrm4RAgAAAAAgAAPVeleb8pMu7o/cp9MwtGsUMwKA28CviO+5N79foNGpknCVkd63Rx+zDdoPM523q1SNM5GonT8foMctIJJnX+gDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'))
        .withArgs(
          'AAADAQDFSMIgRU0g4HEr0oWLByYMgpochH84GLR4BZYbV0gTq61uEQIAAAAAIHNR9lF7b7NdhWgutklx5vwLmihijROI6T24ctzx9g6tAAhwFwAAAAAAAAAgAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCECAgEAAAEBAQABAQIAAAECAMOsKobjW2K/rag/Y4j/J8fdpwks8Lgp0tH0woE+KK45AdS/76Uygn12GaWCeah9uBYy4mfMSOa5SrgaQ2fwLL76r24RAgAAAAAg42350Lqqaanub5TpjJnYlKiupjF2Xc+qgDIoiPOkU6jDrCqG41tiv62oP2OI/yfH3acJLPC4KdLR9MKBPiiuOegDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'));

      const res = await basecoin.recoverConsolidations({
        bitgoKey: keys.bitgoKeyColdWallet,
        tokenContractAddress: packageId,
        startingScanIndex: 1,
        endingScanIndex: 3,
      });
      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '00000301009e2618685fff38b999355e93a4693c4bd18f68dd6b654ff6555f1f4ad229dcdbac6e11020000000020d1389b1b35846326a2f4d4e5d7e7168a2e58e3dd567d274433dbb97ca600b3cb0008a00f0000000000000020016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d3144210202010000010101000101020000010200a992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675f01916e35a0ac6b525b7d38a44b667394aaee7cde41dffc876980cfbc8845590cddae6e110200000000200003d57a579bf2932eee8fdca7d330b46b14330280dbc0af88efb937bf5fa0d1a992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675fe803000000000000565e27000000000000',
                  scanIndex: 1,
                  coin: 'tsui:deep',
                  signableHex: 'cedade6c1c99e3531c90536fa228f0fb148f35605d1a4edfbe8eb739bdc99a48',
                  derivationPath: 'm/1',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0xa992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675f',
                        valueString: '4000',
                        value: new BigNumber('4000'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                        valueString: '4000',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '4000',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 2580054,
                    feeString: '2580054',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '0000030100c548c220454d20e0712bd2858b07260c829a1c847f3818b47805961b574813abad6e110200000000207351f6517b6fb35d85682eb64971e6fc0b9a28628d1388e93db872dcf1f60ead000870170000000000000020016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d3144210202010000010101000101020000010200c3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae3901d4bfefa532827d7619a58279a87db81632e267cc48e6b94ab81a4367f02cbefaaf6e11020000000020e36df9d0baaa69a9ee6f94e98c99d894a8aea631765dcfaa80322888f3a453a8c3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae39e803000000000000565e27000000000000',
                  scanIndex: 2,
                  coin: 'tsui:deep',
                  signableHex: 'd0551409d248accdd894713a4c03a6d8f240349a5e85b90aef019b5f426062c8',
                  derivationPath: 'm/2',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0xc3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae39',
                        valueString: '6000',
                        value: new BigNumber('6000'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                        valueString: '6000',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '6000',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 2580054,
                    feeString: '2580054',
                  },
                  coinSpecific: {
                    commonKeychain:
                      '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                    lastScanIndex: 2,
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
        ],
      });

      sandBox.assert.callCount(basecoin.getBalance, 4);
      sandBox.assert.callCount(basecoin.getInputCoins, 4);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });

    it('should build unsigned token consolidation transactions for cold wallet with seed', async function () {
      getBalanceStub
        .withArgs(seedReceiveAddress1)
        .resolves('120199788')
        .withArgs(seedReceiveAddress1, coinType)
        .resolves('1500')
        .withArgs(seedReceiveAddress2)
        .resolves('120199788')
        .withArgs(seedReceiveAddress2, coinType)
        .resolves('2000');

      getInputCoinsStub
        .withArgs(seedReceiveAddress1, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0xa7a8c91028035ab49977e72b084e145eb1c1ddcfe2e06925f0f833cef2d1f69f',
            version: '34696878',
            digest: 'CKiuXkFMaVkn12iKinZCr9aDHfGpCQiPdyZUQzNDJ9Qx',
            balance: new BigNumber('1500'),
          },
        ])
        .withArgs(seedReceiveAddress1)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x5eea0353f078016b71a84cb494e6f791e97cb8de5344def3baedffc89c27a4b5',
            version: '34696879',
            digest: '6KqrwjKHhJCe4DXfdH2yMLG8bXGcSQoTeJy4shghDbTZ',
            balance: new BigNumber('120000000'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0x86e728fd7242b3be60e9c1941add2c47fb655779108c3500a216310218748e2d',
            version: '148',
            digest: 'B9zJfEYkiXnAuLE6q3XFJeLbPc3K7qmjgDrHVvJnT5wa',
            balance: new BigNumber('199788'),
          },
        ])
        .withArgs(seedReceiveAddress2, coinType)
        .resolves([
          {
            coinType: '0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP',
            objectId: '0xe191f166e71b1ef20ffda0c91b24da95ecc93b657c464d8a06f561d4692722f0',
            version: '34696880',
            digest: '4KyhzfPQcTUrnp4UBvR3GsPE3DLccVDEPgBjDxaWVRcr',
            balance: new BigNumber('2000'),
          },
        ])
        .withArgs(seedReceiveAddress2)
        .resolves([
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xd32f94889d0bfc05c24ed6c023d5d3d3654294a837d850d3738b0e352e1f4867',
            version: '34696880',
            digest: '5Po12YiuL3NkNTAvyWvRwtR55JviyPVbcDfTvCwQrTkr',
            balance: new BigNumber('120000000'),
          },
          {
            coinType: '0x2::sui::SUI',
            objectId: '0xfd66dbfe7a1497f210747b0532f62e6926cb144c75e519f2dfff17a4f6e515fc',
            version: '149',
            digest: '5eVcbbm3NTF6jqadTb4GjZMzn75KDZBqcspRz3sVrpbR',
            balance: new BigNumber('199788'),
          },
        ]);

      getFeeEstimateStub
        .withArgs(
          'AAADAQCnqMkQKANatJl35ysIThRescHdz+LgaSXw+DPO8tH2n65uEQIAAAAAIKg8ZMeF2XLLbBdPQyPmfu8bYqCB2z/uaR5sG7Fobl7VAAjcBQAAAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgEAAAEBAQABAQIAAAECANIBVm5qC8Ag/S5vcum94iI/VQ1k2qYTmM2RfC91ATJKAV7qA1PweAFrcahMtJTm95HpfLjeU0Te87rt/8icJ6S1r24RAgAAAAAgTx4i+RgcLdA5q+vymM+WETpvCnqEqR4oY7RqQdNj2ozSAVZuagvAIP0ub3LpveIiP1UNZNqmE5jNkXwvdQEySugDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'))
        .withArgs(
          'AAADAQDhkfFm5xse8g/9oMkbJNqV7Mk7ZXxGTYoG9WHUaSci8LBuEQIAAAAAIDFvokinBxoWyeL64lPM9mJVOpB0UOjZjDu+J1skPcQPAAjQBwAAAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgEAAAEBAQABAQIAAAECAC+l2DlL1r7FUluVUL9Dvgdbg0ItAQfAXHAJROPq7Cb5AdMvlIidC/wFwk7WwCPV09NlQpSoN9hQ03OLDjUuH0hnsG4RAgAAAAAgQUWJ0/tPZt++q7KnLYmAPWRRonn/W/XGYxzxY689zK8vpdg5S9a+xVJblVC/Q74HW4NCLQEHwFxwCUTj6uwm+egDAAAAAAAAAOH1BQAAAAAA'
        )
        .resolves(new BigNumber('2345504'));

      const res = await basecoin.recoverConsolidations({
        bitgoKey: keys.bitgoKeyWithSeed,
        tokenContractAddress: packageId,
        startingScanIndex: 1,
        endingScanIndex: 3,
        seed: '123',
      });
      res.should.deepEqual({
        txRequests: [
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '0000030100a7a8c91028035ab49977e72b084e145eb1c1ddcfe2e06925f0f833cef2d1f69fae6e11020000000020a83c64c785d972cb6c174f4323e67eef1b62a081db3fee691e6c1bb1686e5ed50008dc05000000000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b24040202010000010101000101020000010200d201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a015eea0353f078016b71a84cb494e6f791e97cb8de5344def3baedffc89c27a4b5af6e110200000000204f1e22f9181c2dd039abebf298cf96113a6f0a7a84a91e2863b46a41d363da8cd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324ae803000000000000565e27000000000000',
                  scanIndex: 1,
                  coin: 'tsui:deep',
                  signableHex: '22defa9101ea3a3839c3c0026b27a9837165c07fcecff8e94353bcf3d5a01328',
                  derivationPath: 'm/999999/94862622/157363509/1',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0xd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a',
                        valueString: '1500',
                        value: new BigNumber('1500'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                        valueString: '1500',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '1500',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 2580054,
                    feeString: '2580054',
                  },
                  coinSpecific: {
                    commonKeychain:
                      'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
          {
            transactions: [
              {
                unsignedTx: {
                  serializedTx:
                    '0000030100e191f166e71b1ef20ffda0c91b24da95ecc93b657c464d8a06f561d4692722f0b06e11020000000020316fa248a7071a16c9e2fae253ccf662553a907450e8d98c3bbe275b243dc40f0008d007000000000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b240402020100000101010001010200000102002fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f901d32f94889d0bfc05c24ed6c023d5d3d3654294a837d850d3738b0e352e1f4867b06e11020000000020414589d3fb4f66dfbeabb2a72d89803d6451a279ff5bf5c6631cf163af3dccaf2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9e803000000000000565e27000000000000',
                  scanIndex: 2,
                  coin: 'tsui:deep',
                  signableHex: '2e6bb17166ee197102729bda47b06b7c0b5962bf431bbb0562b91e267b75129d',
                  derivationPath: 'm/999999/94862622/157363509/2',
                  parsedTx: {
                    inputs: [
                      {
                        address: '0x2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9',
                        valueString: '2000',
                        value: new BigNumber('2000'),
                      },
                    ],
                    outputs: [
                      {
                        address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                        valueString: '2000',
                        coinName: 'tsui:deep',
                      },
                    ],
                    spendAmount: '2000',
                    type: 'TokenTransfer',
                  },
                  feeInfo: {
                    fee: 2580054,
                    feeString: '2580054',
                  },
                  coinSpecific: {
                    commonKeychain:
                      'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                    lastScanIndex: 2,
                  },
                },
                signatureShares: [],
              },
            ],
            walletCoin: 'tsui:deep',
          },
        ],
      });

      sandBox.assert.callCount(basecoin.getBalance, 4);
      sandBox.assert.callCount(basecoin.getInputCoins, 4);
      sandBox.assert.callCount(basecoin.getFeeEstimate, 2);
    });
  });

  describe('Create Broadcastable MPC Transaction', () => {
    it('create broadcastable MPC transaction for OVC signed sweep transaction', async function () {
      const signatureShares = {
        signatureShares: [
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '0000020008c88b902f000000000020df407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c020200010100000101020000010100016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421014553b0f6f79ed95942141835de3ab086bf38d1f1d96272981e1390b84fb86b36c900000000000000200b1de8fdfbbb8a6fa2c65ec79a55703c9a9f0c2685a768f2b81a0426bc932ec3016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421e803000000000000a48821000000000000',
                    scanIndex: 0,
                    coin: 'tsui',
                    signableHex: 'a4ce8eb11362cd45c09936e745044dc78b2689e5d8147b9ea9a7de43ee43923a',
                    derivationPath: 'm/0',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                          valueString: '798002120',
                          value: {
                            s: 1,
                            e: 8,
                            c: [798002120],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0xdf407e3e25e9400f9779ac7571537c2361684194f1aa5db126a8f574b5ed851c',
                          valueString: '798002120',
                          coinName: 'tsui',
                        },
                      ],
                      spendAmount: '798002120',
                      type: 'Transfer',
                    },
                    feeInfo: {
                      fee: 2197668,
                      feeString: '2197668',
                    },
                    coinSpecific: {
                      commonKeychain:
                        '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      '6f470906df88c33b27c8d113ed944ed0a4fc499ba6cc76f00b8924942812d7835792ee455cbc8941faba7bcc93b15a9cd198a20dc3f0b629f5fdceab34e66100',
                    publicShare: '284496ca04cc603823f7993aef13f72f331d97085b2184d7a6a463a90aa927d0',
                  },
                },
              ],
              walletCoin: 'tsui',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: '284496ca04cc603823f7993aef13f72f331d97085b2184d7a6a463a90aa927d0',
                  R: '6f470906df88c33b27c8d113ed944ed0a4fc499ba6cc76f00b8924942812d783',
                  sigma: '5c23a8c72ce192731e702da460aea79ed66334b4a783dcf6dd2758347d6c9f0d',
                },
              },
            ],
          },
        ],
      };

      const res = await basecoin.createBroadcastableSweepTransaction(signatureShares);

      res.should.deepEqual({
        transactions: [
          {
            serializedTx:
              'AAACAAjIi5AvAAAAAAAg30B+PiXpQA+Xeax1cVN8I2FoQZTxql2xJqj1dLXthRwCAgABAQAAAQECAAABAQABZJXd2nSMEWohgkKDXTZHCdbF7lyKHtaExO9DPTFEIQFFU7D2957ZWUIUGDXeOrCGvzjR8dlicpgeE5C4T7hrNskAAAAAAAAAIAsd6P37u4pvosZex5pVcDyanwwmhado8rgaBCa8ky7DAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCHoAwAAAAAAAKSIIQAAAAAAAA==',
            scanIndex: 0,
            signature:
              'AG9HCQbfiMM7J8jRE+2UTtCk/Embpsx28AuJJJQoEteDXCOoxyzhknMecC2kYK6nntZjNLSng9z23SdYNH1snw0oRJbKBMxgOCP3mTrvE/cvMx2XCFshhNempGOpCqkn0A==',
            recoveryAmount: '798002120',
          },
        ],
        lastScanIndex: 0,
      });
    });

    it('create broadcastable MPC token transaction for OVC signed sweep transaction', async function () {
      const signatureShares = {
        signatureShares: [
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '000004010072ad99130abd3790db7bac8908d50d3412a800a8fcd10c1cfbd64b8215537558a16e1102000000002007b85d38b6e1b485f1455930d27296108081caacd7664c48d7d5f48572ee2bbd01004c1cccc6d03510bd74a95f81b2b7da08119cb18e2c565435e7ad715e6aadc5a7696e110200000000208fb4a334b829acdecbfa2a328642afa24d6f953740f7683080ee0e4b3d3a962f0008f82a000000000000002091f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da303030100000101010002010000010102000101020100010300016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d3144210198114f2ddefe3f16d9d2e016194b52c4c3af430c1601bdb3539cbd237cbca068696e11020000000020b516a15b5034bcd42425440c564cf36f439542f88671ef799dde0661e789a009016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421e803000000000000f86411000000000000',
                    scanIndex: 0,
                    coin: 'tsui:deep',
                    signableHex: '449ade19590685b1eb0cefcb2ec41f4c68e37d8eaf75687c45965e288e71caa4',
                    derivationPath: 'm/0',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                          valueString: '11000',
                          value: {
                            s: 1,
                            e: 4,
                            c: [11000],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3',
                          valueString: '11000',
                          coinName: 'tsui:deep',
                        },
                      ],
                      spendAmount: '11000',
                      type: 'TokenTransfer',
                    },
                    feeInfo: {
                      fee: 1139960,
                      feeString: '1139960',
                    },
                    coinSpecific: {
                      commonKeychain:
                        '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      '23c53e54f271386578d99a6932ff708960900188ab3e5bce1aec1f019e53a85d3197f8b64a9feadbedbbd37aa42e4444609f000743adc0a94ffcc721c7057d04',
                    publicShare: '284496ca04cc603823f7993aef13f72f331d97085b2184d7a6a463a90aa927d0',
                  },
                },
              ],
              walletCoin: 'tsui:deep',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: '284496ca04cc603823f7993aef13f72f331d97085b2184d7a6a463a90aa927d0',
                  R: '23c53e54f271386578d99a6932ff708960900188ab3e5bce1aec1f019e53a85d',
                  sigma: '9b52b45c4d539fad906805f9fcbab6fb5e4152243caee252855c36c34048cc09',
                },
              },
            ],
          },
        ],
      };

      const res = await basecoin.createBroadcastableSweepTransaction(signatureShares);

      res.should.deepEqual({
        transactions: [
          {
            serializedTx:
              'AAAEAQByrZkTCr03kNt7rIkI1Q00EqgAqPzRDBz71kuCFVN1WKFuEQIAAAAAIAe4XTi24bSF8UVZMNJylhCAgcqs12ZMSNfV9IVy7iu9AQBMHMzG0DUQvXSpX4Gyt9oIEZyxjixWVDXnrXFeaq3Fp2luEQIAAAAAII+0ozS4Kazey/oqMoZCr6JNb5U3QPdoMIDuDks9OpYvAAj4KgAAAAAAAAAgkfJeI3uDoApick/cSoHkP0lNxrQaEkFJKCbTbk0THaMDAwEAAAEBAQACAQAAAQECAAEBAgEAAQMAAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCEBmBFPLd7+PxbZ0uAWGUtSxMOvQwwWAb2zU5y9I3y8oGhpbhECAAAAACC1FqFbUDS81CQlRAxWTPNvQ5VC+IZx73md3gZh54mgCQFkld3adIwRaiGCQoNdNkcJ1sXuXIoe1oTE70M9MUQh6AMAAAAAAAD4ZBEAAAAAAAA=',
            scanIndex: 0,
            signature:
              'ACPFPlTycThleNmaaTL/cIlgkAGIqz5bzhrsHwGeU6hdm1K0XE1Tn62QaAX5/Lq2+15BUiQ8ruJShVw2w0BIzAkoRJbKBMxgOCP3mTrvE/cvMx2XCFshhNempGOpCqkn0A==',
            recoveryAmount: '11000',
          },
        ],
        lastScanIndex: 0,
      });
    });

    it('create broadcastable MPC transaction for OVC signed consolidation transactions', async function () {
      const signatureShares = {
        signatureShares: [
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '000002000820c2d605000000000020016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421020200010100000101020000010100a992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675f01568beffe7651033a081a7375a3bb43d4d2a8e290a396e826236e73d0973b49b5d000000000000000208bc0764a454de7538b5ecea6fd0bd221c9c85207af297612944a967d43e8dd3ba992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675fe803000000000000a48821000000000000',
                    scanIndex: 1,
                    coin: 'tsui',
                    signableHex: '857a2f26687c011a61a060a41d4c80c10b2fd31e98fe6704d6bcda26f57b03bb',
                    derivationPath: 'm/1',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0xa992709591deb7471fb30dda0f339db7ab548d3391a89d3f1fa0c72d2092675f',
                          valueString: '97960480',
                          value: {
                            s: 1,
                            e: 7,
                            c: [97960480],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                          valueString: '97960480',
                          coinName: 'tsui',
                        },
                      ],
                      spendAmount: '97960480',
                      type: 'Transfer',
                    },
                    feeInfo: {
                      fee: 2197668,
                      feeString: '2197668',
                    },
                    coinSpecific: {
                      commonKeychain:
                        '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      'cdfe15d7c0f80f8aad89d878706673ce242947b126852c00ee309ef05f7a8301b2e902b90d37ea7dfe86ce79c7a99b2b27a175c3f714c310fd9e10ea877cc60f',
                    publicShare: 'e445bd092a467df577001cfbd6081b1445ca0f75147233561ed2f53f6a30e45a',
                  },
                },
              ],
              walletCoin: 'tsui',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: 'e445bd092a467df577001cfbd6081b1445ca0f75147233561ed2f53f6a30e45a',
                  R: 'cdfe15d7c0f80f8aad89d878706673ce242947b126852c00ee309ef05f7a8301',
                  sigma: '2a31329b2e5ef8c3f222eb91cd9d70e716f229ee19d3ca9417c8acc16b1da100',
                },
              },
            ],
          },
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '000002000800c2eb0b000000000020016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421020200010100000101020000010100c3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae3901d68feb4780886ccfa81a37036958ec9c8bfabfe75a0bb7c4d1a4587992343f5bcf00000000000000205ad4f631a378f8b09542d4df66942b6dabb2f65cb20cf8f628c91c64f9352e4ec3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae39e803000000000000a48821000000000000',
                    scanIndex: 2,
                    coin: 'tsui',
                    signableHex: 'f2b56a74e787b2a98decf189e71b1e0c5d4ccee88697b3991a48de2910676020',
                    derivationPath: 'm/2',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0xc3ac2a86e35b62bfada83f6388ff27c7dda7092cf0b829d2d1f4c2813e28ae39',
                          valueString: '200000000',
                          value: {
                            s: 1,
                            e: 8,
                            c: [200000000],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0x016495ddda748c116a218242835d364709d6c5ee5c8a1ed684c4ef433d314421',
                          valueString: '200000000',
                          coinName: 'tsui',
                        },
                      ],
                      spendAmount: '200000000',
                      type: 'Transfer',
                    },
                    feeInfo: {
                      fee: 2197668,
                      feeString: '2197668',
                    },
                    coinSpecific: {
                      commonKeychain:
                        '79d4b9b594df028fee3725a6af51ae3ab6a3519e9d2c322f2c8fd815b96496323c5aba7ea874c102f966f1a61d3c9a42b5f3177c6a85712cf313715afddf83d8',
                      lastScanIndex: 20,
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      '3518613ea5cefa36cd22e1b092742d8d052744188518040397f1a65a754f9e95324889312d70146811dbdc2a96245c518e16fc61bd1a5a2b53b53ebcec5d4a03',
                    publicShare: '4b143a12835bdda04831a9ed851f9eadd7ba5b46e9c07fd087b751f46a3f364d',
                  },
                },
              ],
              walletCoin: 'tsui',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: '4b143a12835bdda04831a9ed851f9eadd7ba5b46e9c07fd087b751f46a3f364d',
                  R: '3518613ea5cefa36cd22e1b092742d8d052744188518040397f1a65a754f9e95',
                  sigma: '2df48ed60ff62b0e8689c5ebe57318f3dad76288647524b420acf7b1a84cc000',
                },
              },
            ],
          },
        ],
      };

      const res = await basecoin.createBroadcastableSweepTransaction(signatureShares);

      res.should.deepEqual({
        transactions: [
          {
            serializedTx:
              'AAACAAggwtYFAAAAAAAgAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCECAgABAQAAAQECAAABAQCpknCVkd63Rx+zDdoPM523q1SNM5GonT8foMctIJJnXwFWi+/+dlEDOggac3Wju0PU0qjikKOW6CYjbnPQlztJtdAAAAAAAAAAIIvAdkpFTedTi17Opv0L0iHJyFIHryl2EpRKln1D6N07qZJwlZHet0cfsw3aDzOdt6tUjTORqJ0/H6DHLSCSZ1/oAwAAAAAAAKSIIQAAAAAAAA==',
            scanIndex: 1,
            signature:
              'AM3+FdfA+A+KrYnYeHBmc84kKUexJoUsAO4wnvBfeoMBKjEymy5e+MPyIuuRzZ1w5xbyKe4Z08qUF8iswWsdoQDkRb0JKkZ99XcAHPvWCBsURcoPdRRyM1Ye0vU/ajDkWg==',
            recoveryAmount: '97960480',
          },
          {
            serializedTx:
              'AAACAAgAwusLAAAAAAAgAWSV3dp0jBFqIYJCg102RwnWxe5cih7WhMTvQz0xRCECAgABAQAAAQECAAABAQDDrCqG41tiv62oP2OI/yfH3acJLPC4KdLR9MKBPiiuOQHWj+tHgIhsz6gaNwNpWOyci/q/51oLt8TRpFh5kjQ/W88AAAAAAAAAIFrU9jGjePiwlULU32aUK22rsvZcsgz49ijJHGT5NS5Ow6wqhuNbYr+tqD9jiP8nx92nCSzwuCnS0fTCgT4orjnoAwAAAAAAAKSIIQAAAAAAAA==',
            scanIndex: 2,
            signature:
              'ADUYYT6lzvo2zSLhsJJ0LY0FJ0QYhRgEA5fxplp1T56VLfSO1g/2Kw6GicXr5XMY89rXYohkdSS0IKz3sahMwABLFDoSg1vdoEgxqe2FH56t17pbRunAf9CHt1H0aj82TQ==',
            recoveryAmount: '200000000',
          },
        ],
        lastScanIndex: 20,
      });
    });

    it('create broadcastable MPC transaction for OVC signed token consolidation transactions', async function () {
      const signatureShares = {
        signatureShares: [
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '0000030100a7a8c91028035ab49977e72b084e145eb1c1ddcfe2e06925f0f833cef2d1f69fae6e11020000000020a83c64c785d972cb6c174f4323e67eef1b62a081db3fee691e6c1bb1686e5ed50008dc05000000000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b24040202010000010101000101020000010200d201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a015eea0353f078016b71a84cb494e6f791e97cb8de5344def3baedffc89c27a4b5af6e110200000000204f1e22f9181c2dd039abebf298cf96113a6f0a7a84a91e2863b46a41d363da8cd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324ae803000000000000565e27000000000000',
                    scanIndex: 1,
                    coin: 'tsui:deep',
                    signableHex: '22defa9101ea3a3839c3c0026b27a9837165c07fcecff8e94353bcf3d5a01328',
                    derivationPath: 'm/999999/94862622/157363509/1',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0xd201566e6a0bc020fd2e6f72e9bde2223f550d64daa61398cd917c2f7501324a',
                          valueString: '1500',
                          value: {
                            s: 1,
                            e: 3,
                            c: [1500],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                          valueString: '1500',
                          coinName: 'tsui:deep',
                        },
                      ],
                      spendAmount: '1500',
                      type: 'TokenTransfer',
                    },
                    feeInfo: {
                      fee: 2580054,
                      feeString: '2580054',
                    },
                    coinSpecific: {
                      commonKeychain:
                        'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      '1989420ce800e35e1c590907f2017b5529945484b3ff884f8e88390da88936e6a5844e4860df0f489e2a0975519c1d6f5f0255dd091edbf859c74063a7f1e20e',
                    publicShare: '408c7a11967152efee953f3eb16191004946685fdc4dd8087b91e3afcb77c916',
                  },
                },
              ],
              walletCoin: 'tsui:deep',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: '408c7a11967152efee953f3eb16191004946685fdc4dd8087b91e3afcb77c916',
                  R: '1989420ce800e35e1c590907f2017b5529945484b3ff884f8e88390da88936e6',
                  sigma: 'c2ce528f1f323fe9af256abb57428973b2e97e4fa54fa6f50c3943036815860e',
                },
              },
            ],
          },
          {
            txRequest: {
              transactions: [
                {
                  unsignedTx: {
                    serializedTx:
                      '0000030100e191f166e71b1ef20ffda0c91b24da95ecc93b657c464d8a06f561d4692722f0b06e11020000000020316fa248a7071a16c9e2fae253ccf662553a907450e8d98c3bbe275b243dc40f0008d007000000000000002088c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b240402020100000101010001010200000102002fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f901d32f94889d0bfc05c24ed6c023d5d3d3654294a837d850d3738b0e352e1f4867b06e11020000000020414589d3fb4f66dfbeabb2a72d89803d6451a279ff5bf5c6631cf163af3dccaf2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9e803000000000000565e27000000000000',
                    scanIndex: 2,
                    coin: 'tsui:deep',
                    signableHex: '2e6bb17166ee197102729bda47b06b7c0b5962bf431bbb0562b91e267b75129d',
                    derivationPath: 'm/999999/94862622/157363509/2',
                    parsedTx: {
                      inputs: [
                        {
                          address: '0x2fa5d8394bd6bec5525b9550bf43be075b83422d0107c05c700944e3eaec26f9',
                          valueString: '2000',
                          value: {
                            s: 1,
                            e: 3,
                            c: [2000],
                          },
                        },
                      ],
                      outputs: [
                        {
                          address: '0x88c99e0db89cba1db97cbadd7f37b2a4b2a2b4022594a0c04b2bcc837e4b2404',
                          valueString: '2000',
                          coinName: 'tsui:deep',
                        },
                      ],
                      spendAmount: '2000',
                      type: 'TokenTransfer',
                    },
                    feeInfo: {
                      fee: 2580054,
                      feeString: '2580054',
                    },
                    coinSpecific: {
                      commonKeychain:
                        'ca0a014ba6f11106a155ef8e2cab2f76d277e4f01cffa591a9b40848343823b3d910752a49c96bf5813985206e23c9f9cd3a78f1cccf5cf88def52b573cedc93',
                      lastScanIndex: 20,
                    },
                  },
                  signatureShares: [],
                  signatureShare: {
                    from: 'backup',
                    to: 'user',
                    share:
                      '9e1d0fd6640780cf4dc209fa6ed8957d6958e0d0272d8a2fd48438c011f67a2200de1a78e23bc2a7d261d0d02a422145301aa9ce97d3d306b3501b1f44d95606',
                    publicShare: '362d716a5a230d683b60d29ece941197e09627c234c71449facab514c3a7abaf',
                  },
                },
              ],
              walletCoin: 'tsui:deep',
            },
            tssVersion: '0.0.1',
            ovc: [
              {
                eddsaSignature: {
                  y: '362d716a5a230d683b60d29ece941197e09627c234c71449facab514c3a7abaf',
                  R: '9e1d0fd6640780cf4dc209fa6ed8957d6958e0d0272d8a2fd48438c011f67a22',
                  sigma: '15a82f7c4aedb9ebd4ab481b4281d6fcadb2a8422635f16e4e31967097001806',
                },
              },
            ],
          },
        ],
      };

      const res = await basecoin.createBroadcastableSweepTransaction(signatureShares);

      res.should.deepEqual({
        transactions: [
          {
            serializedTx:
              'AAADAQCnqMkQKANatJl35ysIThRescHdz+LgaSXw+DPO8tH2n65uEQIAAAAAIKg8ZMeF2XLLbBdPQyPmfu8bYqCB2z/uaR5sG7Fobl7VAAjcBQAAAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgEAAAEBAQABAQIAAAECANIBVm5qC8Ag/S5vcum94iI/VQ1k2qYTmM2RfC91ATJKAV7qA1PweAFrcahMtJTm95HpfLjeU0Te87rt/8icJ6S1r24RAgAAAAAgTx4i+RgcLdA5q+vymM+WETpvCnqEqR4oY7RqQdNj2ozSAVZuagvAIP0ub3LpveIiP1UNZNqmE5jNkXwvdQEySugDAAAAAAAAVl4nAAAAAAAA',
            scanIndex: 1,
            signature:
              'ABmJQgzoAONeHFkJB/IBe1UplFSEs/+IT46IOQ2oiTbmws5Sjx8yP+mvJWq7V0KJc7Lpfk+lT6b1DDlDA2gVhg5AjHoRlnFS7+6VPz6xYZEASUZoX9xN2Ah7keOvy3fJFg==',
            recoveryAmount: '1500',
          },
          {
            serializedTx:
              'AAADAQDhkfFm5xse8g/9oMkbJNqV7Mk7ZXxGTYoG9WHUaSci8LBuEQIAAAAAIDFvokinBxoWyeL64lPM9mJVOpB0UOjZjDu+J1skPcQPAAjQBwAAAAAAAAAgiMmeDbicuh25fLrdfzeypLKitAIllKDASyvMg35LJAQCAgEAAAEBAQABAQIAAAECAC+l2DlL1r7FUluVUL9Dvgdbg0ItAQfAXHAJROPq7Cb5AdMvlIidC/wFwk7WwCPV09NlQpSoN9hQ03OLDjUuH0hnsG4RAgAAAAAgQUWJ0/tPZt++q7KnLYmAPWRRonn/W/XGYxzxY689zK8vpdg5S9a+xVJblVC/Q74HW4NCLQEHwFxwCUTj6uwm+egDAAAAAAAAVl4nAAAAAAAA',
            scanIndex: 2,
            signature:
              'AJ4dD9ZkB4DPTcIJ+m7YlX1pWODQJy2KL9SEOMAR9noiFagvfErtuevUq0gbQoHW/K2yqEImNfFuTjGWcJcAGAY2LXFqWiMNaDtg0p7OlBGX4JYnwjTHFEn6yrUUw6errw==',
            recoveryAmount: '2000',
          },
        ],
        lastScanIndex: 20,
      });
    });
  });

  describe('Recover Transaction Failures:', () => {
    const sandBox = sinon.createSandbox();
    const senderAddress0 = '0x91f25e237b83a00a62724fdc4a81e43f494dc6b41a1241492826d36e4d131da3';
    const recoveryDestination = '0x00e4eaa6a291fe02918452e645b5653cd260a5fc0fb35f6193d580916aa9e389';
    const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';

    afterEach(function () {
      sandBox.restore();
    });

    it('should fail to recover due to non-zero fund but insufficient funds address', async function () {
      const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      callBack.withArgs(senderAddress0).resolves('9800212');

      await basecoin
        .recover({
          userKey: keys.userKey,
          backupKey: keys.backupKey,
          bitgoKey: keys.bitgoKey,
          recoveryDestination,
          walletPassphrase,
          startingScanIndex: '0',
          scan: 1,
        })
        .should.rejectedWith(
          'Did not find an address with sufficient funds to recover. Please start the next scan at address index 1. If it is token transaction, please keep sufficient Sui balance in the address for the transaction fee.'
        );

      sandBox.assert.callCount(basecoin.getBalance, 1);
    });

    it('should fail to recover due to not finding an address with funds', async function () {
      const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      callBack.resolves('0');

      await basecoin
        .recover({
          userKey: keys.userKey,
          backupKey: keys.backupKey,
          bitgoKey: keys.bitgoKey,
          recoveryDestination,
          walletPassphrase,
          scan: '10',
        })
        .should.rejectedWith(
          'Did not find an address with sufficient funds to recover. Please start the next scan at address index 10. If it is token transaction, please keep sufficient Sui balance in the address for the transaction fee.'
        );

      sandBox.assert.callCount(basecoin.getBalance, 10);
    });
  });

  describe('Consolidation Transaction Failures:', () => {
    it('should fail due to insufficient funds in receive address', async function () {
      const sandBox = sinon.createSandbox();
      const receiveAddress1 = '0x32d8e57ee6d91e5558da0677154c2f085795348e317f95acc9efade1b4112fcc';
      const walletPassphrase = 'p$Sw<RjvAgf{nYAYI2xM';

      const callBack = sandBox.stub(Sui.prototype, 'getBalance' as keyof Sui);
      callBack.withArgs(receiveAddress1).resolves('1');

      await basecoin
        .recoverConsolidations({
          userKey: keys.userKey,
          backupKey: keys.backupKey,
          bitgoKey: keys.bitgoKey,
          walletPassphrase,
          startingScanIndex: '1',
          endingScanIndex: '2',
        })
        .should.rejectedWith(
          'Did not find an address with sufficient funds to recover. Please start the next scan at address index 2.'
        );

      sandBox.assert.callCount(basecoin.getBalance, 1);
    });
  });
});

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


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