UNPKG

@ledgerhq/coin-tron

Version:
435 lines (432 loc) 18.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bignumber_js_1 = require("bignumber.js"); const invariant_1 = __importDefault(require("invariant")); const expect_1 = __importDefault(require("expect")); const index_1 = require("@ledgerhq/cryptoassets/index"); const currencies_1 = require("@ledgerhq/coin-framework/currencies"); const specs_1 = require("@ledgerhq/coin-framework/bot/specs"); // import { getUnfreezeData, getNextRewardDate } from "./react"; const devices_1 = require("@ledgerhq/devices"); const bot_deviceActions_1 = require("./bot-deviceActions"); const currency = (0, index_1.getCryptoCurrencyById)("tron"); const minimalAmount = (0, currencies_1.parseCurrencyUnit)(currency.units[0], "1"); const maxAccount = 10; /*const getDecimalPart = (value: BigNumber, magnitude: number) => value.minus(value.modulo(10 ** magnitude));*/ // FIXME TRON have a bug where the amounts from the API have imprecisions const expectedApproximate = (value, expected, delta = 50) => { if (value.minus(expected).abs().gt(delta)) { (0, expect_1.default)(value.toString()).toEqual(value.toString()); } }; const testDestination = ({ destination, operation, destinationBeforeTransaction, sendingOperation, }) => { const amount = sendingOperation.value.minus(sendingOperation.fee); (0, specs_1.botTest)("account balance increased with transaction amount", () => expectedApproximate(destination.balance, destinationBeforeTransaction.balance.plus(amount))); (0, specs_1.botTest)("operation amount is consistent with sendingOperation", () => expectedApproximate(operation.value, amount)); }; const tron = { name: "Tron", currency, appQuery: { model: devices_1.DeviceModelId.nanoSP, appName: "Tron", firmware: "1.1.1", appVersion: "0.5.0", }, genericDeviceAction: bot_deviceActions_1.acceptTransaction, testTimeout: 2 * 60 * 1000, minViableAmount: minimalAmount, mutations: [ { name: "move 50% to another account", feature: "send", maxRun: 1, testDestination, transaction: ({ account, siblings, bridge, maxSpendable }) => { (0, invariant_1.default)(maxSpendable.gt(minimalAmount), "balance is too low"); const sibling = (0, specs_1.pickSiblings)(siblings, maxAccount); const recipient = sibling.freshAddress; const amount = maxSpendable.div(2).integerValue(); return { transaction: bridge.createTransaction(account), updates: [ { recipient, }, { amount, }, ], }; }, test: ({ accountBeforeTransaction, operation, account }) => { (0, specs_1.botTest)("account spendable balance decreased with operation", () => expectedApproximate(account.spendableBalance, accountBeforeTransaction.spendableBalance.minus(operation.value))); }, }, { name: "send max to another account", feature: "sendMax", maxRun: 1, testDestination, transaction: ({ account, siblings, bridge, maxSpendable }) => { (0, invariant_1.default)(maxSpendable.gt(minimalAmount), "balance is too low"); const sibling = (0, specs_1.pickSiblings)(siblings, maxAccount); const recipient = sibling.freshAddress; return { transaction: bridge.createTransaction(account), updates: [ { recipient, }, { useAllAmount: true, }, ], }; }, test: ({ account }) => { (0, specs_1.botTest)("account spendable balance is zero", () => expectedApproximate(account.spendableBalance, new bignumber_js_1.BigNumber(0))); }, }, /* We do not manage staking anymore { name: "freeze 25% to bandwidth | energy", maxRun: 1, transaction: ({ siblings, account, bridge, maxSpendable }) => { expectSiblingsHaveSpendablePartGreaterThan(siblings, 0.5); invariant(maxSpendable.gt(minimalAmount), "balance is too low"); let amount = getDecimalPart( maxSpendable.div(4), currency.units[0].magnitude, ).integerValue(); if (amount.lt(minimalAmount)) { amount = minimalAmount; } const energy = get(account, `tronResources.energy`, new BigNumber(0)); return { transaction: bridge.createTransaction(account), updates: [ { mode: "freeze", }, { resource: energy.eq(0) ? "ENERGY" : "BANDWIDTH", }, { amount, }, ], }; }, test: ({ account, accountBeforeTransaction, transaction }) => { const resourceType = (transaction.resource || "").toLocaleLowerCase(); const resourceBeforeTransaction = get( accountBeforeTransaction, `tronResources.frozen.${resourceType}.amount`, new BigNumber(0), ); const expectedAmount = new BigNumber(transaction.amount).plus(resourceBeforeTransaction); const currentRessourceAmount = get( account, `tronResources.frozen.${resourceType}.amount`, new BigNumber(0), ); botTest("frozen amount is accumulated in resources", () => expect(expectedAmount.toString()).toBe(currentRessourceAmount.toString()), ); }, }, { name: "unfreeze bandwith / energy", maxRun: 1, transaction: ({ account, bridge }) => { const TP = new BigNumber(get(account, "tronResources.tronPower", "0")); invariant(TP.gt(0), "no frozen assets"); const { canUnfreezeBandwidth, canUnfreezeEnergy } = getUnfreezeData(account as TronAccount); invariant(canUnfreezeBandwidth || canUnfreezeEnergy, "freeze period not expired yet"); const resourceToUnfreeze = canUnfreezeBandwidth ? "BANDWIDTH" : "ENERGY"; return { transaction: bridge.createTransaction(account), updates: [ { mode: "unfreeze", }, { resource: resourceToUnfreeze, }, ], }; }, test: ({ account, accountBeforeTransaction, transaction }) => { const TxResource = (transaction.resource || "").toLocaleLowerCase(); const currentFrozen = get(account, `tronResources.frozen.${TxResource}`, undefined); botTest("no current frozen", () => expect(currentFrozen).toBeUndefined()); const TPBeforeTx = new BigNumber( get(accountBeforeTransaction, "tronResources.tronPower", 0), ); const currentTP = new BigNumber(get(account, "tronResources.tronPower", 0)); const expectedTronPower = TPBeforeTx.minus(transaction.amount); botTest("tron power", () => expectedApproximate(currentTP, expectedTronPower)); }, }, { name: "submit vote", maxRun: 1, transaction: ({ account, bridge, preloadedData }) => { const TP = new BigNumber(get(account, "tronResources.tronPower", "0")); invariant(TP.gt(0), "no tron power to vote"); const currentTPVoted = get(account, "tronResources.votes", []).reduce( (acc, curr) => acc.plus(new BigNumber(get(curr, "voteCount", 0))), new BigNumber(0), ); invariant(TP.gt(currentTPVoted), "you have no tron power left"); const { superRepresentatives } = preloadedData; invariant( superRepresentatives && superRepresentatives.length, "there are no super representatives to vote for, or the list has not been loaded yet", ); const count = 1 + Math.floor(5 * Math.random()); const candidates = sampleSize(superRepresentatives.slice(0, 40), count); let remaining = TP; const votes = candidates .map(c => { if (!remaining.gt(0)) return null; const voteCount = remaining.eq(1) ? remaining.integerValue().toNumber() : remaining.times(Math.random()).integerValue().toNumber(); if (voteCount === 0) return null; remaining = remaining.minus(voteCount); return { address: c.address, voteCount, }; }) .filter(Boolean); return { transaction: bridge.createTransaction(account) as Transaction, updates: [ { mode: "vote", }, { votes, }, ] as Array<Partial<Transaction>>, }; }, test: ({ account, transaction }) => { const votes = sortBy(transaction.votes, ["address"]); const currentVotes = sortBy(get(account, "tronResources.votes", []), ["address"]); botTest("current votes", () => expect(currentVotes).toEqual(votes)); }, },*/ /** * FIXME * * Our bad implementation of TRC10/TRC20 operations and sync make those tests impossible to * work properly. * * To make them work we need to rework the link between Operation and SubOperations which is wrong as of now and * rework our fee estimation (TRC20 do have fees which are ignored today until the next sync creates an OUT tx to represent it). */ // { // name: "move some TRC10", // maxRun: 1, // transaction: ({ account, siblings, bridge }) => { // const trc10Account = sample( // (account.subAccounts || []).filter( // (a) => a.type === "TokenAccount" && a.token.tokenType === "trc10" // ) // ); // invariant(trc10Account, "no trc10 account"); // if (!trc10Account) throw new Error("no trc10 account"); // invariant(trc10Account?.balance.gt(0), "trc10 account has no balance"); // const sibling = pickSiblings(siblings, maxAccount); // const recipient = sibling.freshAddress; // return { // transaction: bridge.createTransaction(account), // updates: [ // { // recipient, // subAccountId: trc10Account.id, // }, // Math.random() < 0.5 // ? { // useAllAmount: true, // } // : { // amount: trc10Account.balance // .times(Math.random()) // .integerValue(), // }, // ], // }; // }, // test: ({ accountBeforeTransaction, account, transaction }) => { // invariant(accountBeforeTransaction.subAccounts, "sub accounts before"); // const trc10accountBefore = ( // accountBeforeTransaction.subAccounts as SubAccount[] // ).find((s) => s.id === transaction.subAccountId); // invariant(trc10accountBefore, "trc10 acc was here before"); // if (!trc10accountBefore) throw new Error("no trc10before account"); // invariant(account.subAccounts, "sub accounts"); // const trc10account = (account.subAccounts as SubAccount[]).find( // (s) => s.id === transaction.subAccountId // ); // invariant(trc10account, "trc10 acc is still here"); // if (!trc10account) throw new Error("no trc10 account"); // if (transaction.useAllAmount) { // botTest("trc10 balance became zero", () => // expect(trc10account.balance.toString()).toBe("0") // ); // } else { // botTest("trc10 balance decreased with operation", () => // expect(trc10account.balance.toString()).toBe( // trc10accountBefore.balance.minus(transaction.amount).toString() // ) // ); // } // }, // }, // { // name: "move some TRC20", // maxRun: 1, // transaction: ({ account, siblings, bridge }) => { // const balance = account.spendableBalance; // const energy = get(account, "tronResources.energy", new BigNumber(0)); // invariant( // energy.gt(0) || balance.gt(minimalAmount), // "trx and energy too low" // ); // const trc20Account = sample( // (account.subAccounts || []).filter( // (a) => a.type === "TokenAccount" && a.token.tokenType === "trc20" // ) // ); // invariant(trc20Account, "no trc20 account"); // invariant(trc20Account?.balance.gt(0), "trc20 account has no balance"); // if (!trc20Account) throw new Error("no trc20 account"); // const sibling = pickSiblings(siblings, maxAccount); // invariant( // sibling.balance.gt(0), // "recipient cannot receive trc20 because it has no balance" // ); // const recipient = sibling.freshAddress; // return { // transaction: bridge.createTransaction(account), // updates: [ // { // recipient, // subAccountId: trc20Account.id, // }, // Math.random() < 0.5 // ? { // useAllAmount: true, // } // : { // amount: trc20Account.balance // .times(Math.random()) // .integerValue(), // }, // ], // }; // }, // test: ({ accountBeforeTransaction, account, transaction }) => { // invariant(accountBeforeTransaction.subAccounts, "sub accounts before"); // const trc20accountBefore = ( // accountBeforeTransaction.subAccounts as SubAccount[] // ).find((s) => s.id === transaction.subAccountId); // invariant(trc20accountBefore, "trc20 acc was here before"); // if (!trc20accountBefore) throw new Error("no trc20 before account"); // invariant(account.subAccounts, "sub accounts"); // const trc20account = (account.subAccounts as SubAccount[]).find( // (s) => s.id === transaction.subAccountId // ); // invariant(trc20account, "trc20 acc is still here"); // if (!trc20account) throw new Error("no trc20 account"); // if (transaction.useAllAmount) { // botTest("trc10 balance became zero", () => // expect(trc20account.balance.toString()).toBe("0") // ); // } else { // botTest("trc10 balance decreased with operation", () => // expect(trc20account.balance.toString()).toBe( // trc20accountBefore.balance.minus(transaction.amount).toString() // ) // ); // } // if ( // get(trc20accountBefore, "tronResources.energy", new BigNumber(0)).eq( // 0 // ) // ) { // botTest("account balance decreased", () => // expect(account.balance.lt(accountBeforeTransaction.balance)).toBe( // true // ) // ); // } else { // botTest("energy decreased", () => // expect( // get(account, "tronResources.energy", new BigNumber(0)).lt( // get( // accountBeforeTransaction, // "tronResources.energy", // new BigNumber(0) // ) // ) // ).toBe(true) // ); // botTest("balance didn't change", () => // expect(account.balance.eq(accountBeforeTransaction.balance)).toBe( // true // ) // ); // } // }, // }, /* We do not manage staking anymore { name: "claim rewards", maxRun: 1, transaction: ({ account, bridge }) => { const nextRewardDate = getNextRewardDate(account as TronAccount); const today = Date.now(); const unwithdrawnReward = new BigNumber( get(account, "tronResources.unwithdrawnReward", "0"), ); invariant(unwithdrawnReward.gt(0), "no rewards to claim"); invariant( nextRewardDate && nextRewardDate <= today, "you can't claim twice in less than 24 hours", ); return { transaction: bridge.createTransaction(account), updates: [ { mode: "claimReward", }, ], }; }, test: ({ account }) => { const rewards = new BigNumber(get(account, "tronResources.unwithdrawnReward", "0")); const nextRewardDate = getNextRewardDate(account as TronAccount); botTest("rewards is zero", () => expect(rewards.eq(0)).toBe(true)); botTest("next reward date settled", () => expect(nextRewardDate && nextRewardDate > Date.now()).toBe(true), ); }, },*/ ], }; exports.default = { tron, }; //# sourceMappingURL=bot-specs.js.map