UNPKG

@ledgerhq/coin-multiversx

Version:
286 lines 14.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const invariant_1 = __importDefault(require("invariant")); const currencies_1 = require("@ledgerhq/coin-framework/currencies"); const currencies_2 = require("@ledgerhq/cryptoassets/currencies"); const specs_1 = require("@ledgerhq/coin-framework/bot/specs"); const serialization_1 = require("@ledgerhq/coin-framework/serialization"); const devices_1 = require("@ledgerhq/devices"); const expect_1 = __importDefault(require("expect")); const speculos_deviceActions_1 = require("./speculos-deviceActions"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const constants_1 = require("./constants"); const sample_1 = __importDefault(require("lodash/sample")); const currency = (0, currencies_2.getCryptoCurrencyById)("elrond"); const minimalAmount = (0, currencies_1.parseCurrencyUnit)(currency.units[0], "0.001"); const maxAccounts = 6; const MULTIVERSX_MIN_ACTIVATION_SAFE = new bignumber_js_1.default(10000); const UNCAPPED_PROVIDER = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlhllllsr0pd0j"; function expectCorrectBalanceChange(input) { const { account, operation, accountBeforeTransaction } = input; (0, specs_1.botTest)("EGLD balance change is correct", () => (0, expect_1.default)(account.balance.toFixed()).toStrictEqual(accountBeforeTransaction.balance.minus(operation.value).toFixed())); } function expectCorrectEsdtBalanceChange(input) { const { account, operation, accountBeforeTransaction, transaction } = input; const { subAccountId } = transaction; const subAccounts = account.subAccounts ?? []; const subAccountsBefore = accountBeforeTransaction.subAccounts ?? []; const tokenAccount = subAccounts.find(ta => ta.id === subAccountId); const tokenAccountBefore = subAccountsBefore.find(ta => ta.id === subAccountId); const subOperation = operation.subOperations?.find(sa => sa.id === operation.id); if (tokenAccount && tokenAccountBefore && subOperation) { (0, specs_1.botTest)("ESDT balance change is correct", () => (0, expect_1.default)(tokenAccount.balance.toFixed()).toStrictEqual(tokenAccountBefore.balance.minus(subOperation.value).toFixed())); } } function expectCorrectOptimisticOperation(input) { const { operation, optimisticOperation, transaction } = input; const opExpected = (0, serialization_1.toOperationRaw)({ ...optimisticOperation, }); delete opExpected.value; delete opExpected.fee; delete opExpected.date; delete opExpected.blockHash; delete opExpected.blockHeight; if (operation.type !== "OUT") { delete opExpected.senders; delete opExpected.recipients; delete opExpected.contract; } (0, specs_1.botTest)("optimistic operation matches id", () => (0, expect_1.default)(operation.id).toStrictEqual(optimisticOperation.id)); (0, specs_1.botTest)("optimistic operation matches hash", () => (0, expect_1.default)(operation.hash).toStrictEqual(optimisticOperation.hash)); (0, specs_1.botTest)("optimistic operation matches accountId", () => (0, expect_1.default)(operation.accountId).toStrictEqual(optimisticOperation.accountId)); // On ESDT transactions the fee can decrease when the transaction is executed if (!transaction.subAccountId) { (0, specs_1.botTest)("optimistic operation matches fee", () => (0, expect_1.default)(operation.fee.toFixed()).toStrictEqual(optimisticOperation.fee.toFixed())); } (0, specs_1.botTest)("optimistic operation matches type", () => (0, expect_1.default)(operation.type).toStrictEqual(optimisticOperation.type)); if (operation.type === "OUT") { (0, specs_1.botTest)("optimistic operation matches contract", () => (0, expect_1.default)(operation.contract).toStrictEqual(optimisticOperation.contract)); (0, specs_1.botTest)("optimistic operation matches senders", () => (0, expect_1.default)(operation.senders).toStrictEqual(optimisticOperation.senders)); (0, specs_1.botTest)("optimistic operation matches recipients", () => (0, expect_1.default)(operation.recipients).toStrictEqual(optimisticOperation.recipients)); if (!transaction.subAccountId) { (0, specs_1.botTest)("optimistic operation matches value", () => (0, expect_1.default)(operation.value.toFixed()).toStrictEqual(optimisticOperation.value.toFixed())); } } (0, specs_1.botTest)("optimistic operation matches transactionSequenceNumber", () => (0, expect_1.default)(operation.transactionSequenceNumber).toStrictEqual(optimisticOperation.transactionSequenceNumber)); (0, specs_1.botTest)("raw optimistic operation matches", () => (0, expect_1.default)((0, serialization_1.toOperationRaw)(operation)).toMatchObject(opExpected)); } function expectCorrectSpendableBalanceChange(input) { const { account, accountBeforeTransaction } = input; const operation = input.operation; let value = operation.value; if (operation.extra.amount) { if (operation.type === "DELEGATE") { value = value.plus(operation.extra.amount); } else if (operation.type === "WITHDRAW_UNBONDED") { value = value.minus(operation.extra.amount); } } (0, specs_1.botTest)("EGLD spendable balance change is correct", () => (0, expect_1.default)(account.spendableBalance.toFixed()).toStrictEqual(accountBeforeTransaction.spendableBalance.minus(value).toFixed())); } function expectCorrectBalanceFeeChange(input) { const { account, operation, accountBeforeTransaction } = input; (0, specs_1.botTest)("Only change on balance is fees", () => (0, expect_1.default)(account.balance.toFixed()).toStrictEqual(accountBeforeTransaction.balance.minus(operation.fee).toFixed())); } const multiversx = { name: "MultiversX", currency: (0, currencies_2.getCryptoCurrencyById)("elrond"), appQuery: { model: devices_1.DeviceModelId.nanoS, appName: "MultiversX", }, genericDeviceAction: speculos_deviceActions_1.acceptMoveBalanceTransaction, genericDeviceActionForSubAccountTransfers: speculos_deviceActions_1.acceptEsdtTransferTransaction, testTimeout: 2 * 60 * 1000, minViableAmount: minimalAmount, transactionCheck: ({ maxSpendable }) => { (0, invariant_1.default)(maxSpendable.gt(minimalAmount), "balance is too low"); }, test: input => { expectCorrectOptimisticOperation(input); }, mutations: [ { name: "send 50%~", feature: "send", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptMoveBalanceTransaction, transaction: ({ account, siblings, bridge }) => { (0, invariant_1.default)(account.spendableBalance.gt(0), "balance is 0"); const sibling = (0, specs_1.pickSiblings)(siblings, maxAccounts); let amount = account.spendableBalance.div(1.9 + 0.2 * Math.random()).integerValue(); if (!sibling.used && amount.lt(MULTIVERSX_MIN_ACTIVATION_SAFE)) { (0, invariant_1.default)(account.spendableBalance.gt(MULTIVERSX_MIN_ACTIVATION_SAFE), "send is too low to activate account"); amount = MULTIVERSX_MIN_ACTIVATION_SAFE; } return { transaction: bridge.createTransaction(account), updates: [ { recipient: sibling.freshAddress, }, { amount, }, ], }; }, testDestination: specs_1.genericTestDestination, test: input => { expectCorrectBalanceChange(input); }, }, { name: "send max", feature: "sendMax", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptMoveBalanceTransaction, transaction: ({ account, siblings, bridge }) => { (0, invariant_1.default)(account.spendableBalance.gt(0), "balance is 0"); const sibling = (0, specs_1.pickSiblings)(siblings, maxAccounts); if (!sibling.used) { (0, invariant_1.default)(account.spendableBalance.gt(MULTIVERSX_MIN_ACTIVATION_SAFE), "send is too low to activate account"); } return { transaction: bridge.createTransaction(account), updates: [ { recipient: sibling.freshAddress, }, { useAllAmount: true, }, ], }; }, testDestination: specs_1.genericTestDestination, test: input => { expectCorrectBalanceChange(input); }, }, { name: "move some ESDT", feature: "tokens", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptEsdtTransferTransaction, transaction: ({ account, siblings, bridge }) => { const esdtAccount = (0, sample_1.default)((account.subAccounts || []).filter(a => a.balance.gt(0))); (0, invariant_1.default)(esdtAccount, "no esdt account"); (0, invariant_1.default)(esdtAccount?.balance.gt(0), "esdt balance is 0"); const sibling = (0, specs_1.pickSiblings)(siblings, 2); const recipient = sibling.freshAddress; const amount = esdtAccount?.balance.times(Math.random()).integerValue(); return { transaction: bridge.createTransaction(account), updates: [ { subAccountId: esdtAccount?.id, }, { recipient, }, { amount, }, ], }; }, test: input => { expectCorrectEsdtBalanceChange(input); expectCorrectBalanceFeeChange(input); }, }, { name: "delegate 1 EGLD", feature: "staking", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptDelegateTransaction, transaction: ({ account, bridge }) => { (0, invariant_1.default)(account.spendableBalance.gt(constants_1.MIN_DELEGATION_AMOUNT), `spendable balance is less than minimum delegation amount`); const amount = constants_1.MIN_DELEGATION_AMOUNT; return { transaction: bridge.createTransaction(account), updates: [ { recipient: UNCAPPED_PROVIDER, mode: "delegate", amount, }, ], }; }, test: input => { expectCorrectSpendableBalanceChange(input); expectCorrectBalanceFeeChange(input); }, }, { name: "unDelegate 1 EGLD", feature: "staking", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptUndelegateTransaction, transaction: ({ account, bridge }) => { const delegations = account?.multiversxResources?.delegations; (0, invariant_1.default)(delegations?.length, "account doesn't have any delegations"); (0, invariant_1.default)(delegations.some(d => new bignumber_js_1.default(d.userActiveStake).gt(0)), "no active stake for account"); const amount = constants_1.MIN_DELEGATION_AMOUNT; return { transaction: bridge.createTransaction(account), updates: [ { recipient: UNCAPPED_PROVIDER, mode: "unDelegate", amount, }, ], }; }, test: input => { expectCorrectSpendableBalanceChange(input); expectCorrectBalanceFeeChange(input); }, }, { name: "withdraw all EGLD", feature: "staking", maxRun: 1, deviceAction: speculos_deviceActions_1.acceptWithdrawTransaction, transaction: ({ account, bridge }) => { const delegations = account?.multiversxResources?.delegations; (0, invariant_1.default)(delegations?.length, "account doesn't have any delegations"); (0, invariant_1.default)( // among all delegations delegations.some(d => // among all undelegating amounts d.userUndelegatedList?.some(u => new bignumber_js_1.default(u.amount).gt(0) && // the undelegation has a positive amount new bignumber_js_1.default(u.seconds).eq(0))), "no withdrawable stake for account"); return { transaction: bridge.createTransaction(account), updates: [ { recipient: UNCAPPED_PROVIDER, mode: "withdraw", amount: new bignumber_js_1.default(0), }, ], }; }, test: input => { expectCorrectSpendableBalanceChange(input); expectCorrectBalanceFeeChange(input); }, }, // TODO // "reDelegateRewards" // "claimRewards" ], }; exports.default = { multiversx, }; //# sourceMappingURL=specs.js.map