UNPKG

@ledgerhq/coin-hedera

Version:
276 lines (232 loc) 9.35 kB
import { setupCalClientStore } from "@ledgerhq/cryptoassets/cal-client/test-helpers"; import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state"; import { encodeTokenAccountId } from "@ledgerhq/ledger-wallet-framework/account"; import BigNumber from "bignumber.js"; import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_MODES } from "../constants"; import { estimateFees } from "../logic/estimateFees"; import { apiClient } from "../network/api"; import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture"; import { getMockedHTSTokenCurrency, getTokenCurrencyFromCAL, getTokenCurrencyFromCALByType, } from "../test/fixtures/currency.fixture"; import { getMockedMirrorToken } from "../test/fixtures/mirror.fixture"; import { getMockedOperation } from "../test/fixtures/operation.fixture"; import { getMockedTransaction } from "../test/fixtures/transaction.fixture"; import type { EstimateFeesResult } from "../types"; import { calculateAmount, getSubAccounts } from "./utils"; describe("utils", () => { const address = "0.0.12345"; const evmAddress = "0x0000000000000000000000000000000000003039"; const mockedAccount = getMockedAccount(); beforeAll(() => { // Setup CAL client store (automatically set as global store) setupCalClientStore(); }); beforeEach(() => { jest.clearAllMocks(); jest.spyOn(apiClient, "getAccount").mockResolvedValue({ account: address, evm_address: evmAddress, max_automatic_token_associations: 0, balance: { balance: 1000000000, timestamp: "0", tokens: [] }, pending_reward: 0, staked_node_id: null, }); }); afterEach(() => { jest.restoreAllMocks(); }); describe("calculateAmount", () => { let estimatedFees: Record<"crypto" | "associate", EstimateFeesResult>; beforeAll(async () => { const [crypto, associate] = await Promise.all([ estimateFees({ currencyId: mockedAccount.currency.id, operationType: HEDERA_OPERATION_TYPES.CryptoTransfer, }), estimateFees({ currencyId: mockedAccount.currency.id, operationType: HEDERA_OPERATION_TYPES.TokenAssociate, }), ]); estimatedFees = { crypto, associate }; }); it("HBAR transfer, useAllAmount = true", async () => { const mockedTransaction = getMockedTransaction({ useAllAmount: true }); const amount = mockedAccount.balance.minus(estimatedFees.crypto.tinybars); const totalSpent = amount.plus(estimatedFees.crypto.tinybars); const result = await calculateAmount({ account: mockedAccount, transaction: mockedTransaction, }); expect(result).toEqual({ amount, totalSpent }); }); it("HBAR transfer, useAllAmount = false", async () => { const mockedTransaction = getMockedTransaction({ useAllAmount: false, amount: new BigNumber(1000000), }); const amount = mockedTransaction.amount; const totalSpent = amount.plus(estimatedFees.crypto.tinybars); const result = await calculateAmount({ account: mockedAccount, transaction: mockedTransaction, }); expect(result).toEqual({ amount, totalSpent }); }); it("token transfer, useAllAmount = true", async () => { const mockedTokenCurrency = getMockedHTSTokenCurrency(); const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency); const mockedTransaction = getMockedTransaction({ useAllAmount: true, subAccountId: mockedTokenAccount.id, }); const amount = mockedTokenAccount.balance; const totalSpent = amount; const result = await calculateAmount({ account: { ...mockedAccount, subAccounts: [mockedTokenAccount] }, transaction: mockedTransaction, }); expect(result).toEqual({ amount, totalSpent }); }); it("token transfer, useAllAmount = false", async () => { const mockedTokenCurrency = getMockedHTSTokenCurrency(); const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency); const mockedTransaction = getMockedTransaction({ useAllAmount: false, amount: new BigNumber(1), subAccountId: mockedTokenAccount.id, }); const amount = mockedTransaction.amount; const totalSpent = amount; const result = await calculateAmount({ account: { ...mockedAccount, subAccounts: [mockedTokenAccount] }, transaction: mockedTransaction, }); expect(result).toEqual({ amount, totalSpent }); }); it("token associate operation uses TokenAssociate fee", async () => { const mockedTokenCurrency = getMockedHTSTokenCurrency(); const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency); const mockedTransaction = getMockedTransaction({ useAllAmount: false, amount: new BigNumber(1), mode: HEDERA_TRANSACTION_MODES.TokenAssociate, properties: { token: mockedTokenCurrency, }, }); const amount = mockedTransaction.amount; const totalSpent = amount.plus(estimatedFees.associate.tinybars); const result = await calculateAmount({ account: { ...mockedAccount, subAccounts: [mockedTokenAccount] }, transaction: mockedTransaction, }); expect(result).toEqual({ amount, totalSpent }); }); }); describe("getSubAccounts", () => { it("returns sub account based on operations and mirror tokens", async () => { const firstTokenCurrencyFromCAL = getTokenCurrencyFromCAL(0); const secondTokenCurrencyFromCAL = getTokenCurrencyFromCAL(1); const mockedMirrorToken1 = getMockedMirrorToken({ token_id: firstTokenCurrencyFromCAL.contractAddress, balance: 10, }); const mockedMirrorToken2 = getMockedMirrorToken({ token_id: secondTokenCurrencyFromCAL.contractAddress, balance: 0, }); // Fetch actual tokens from CAL to get the real format const firstTokenFromCAL = await getCryptoAssetsStore().findTokenByAddressInCurrency( firstTokenCurrencyFromCAL.contractAddress, "hedera", ); const secondTokenFromCAL = await getCryptoAssetsStore().findTokenByAddressInCurrency( secondTokenCurrencyFromCAL.contractAddress, "hedera", ); if (!firstTokenFromCAL || !secondTokenFromCAL) { throw new Error("Tokens not found in CAL"); } const mockedOperation1 = getMockedOperation({ accountId: encodeTokenAccountId(mockedAccount.id, firstTokenFromCAL), }); const mockedOperation2 = getMockedOperation({ accountId: encodeTokenAccountId(mockedAccount.id, secondTokenFromCAL), }); const result = await getSubAccounts({ ledgerAccountId: mockedAccount.id, latestTokenOperations: [mockedOperation1, mockedOperation2], mirrorTokens: [mockedMirrorToken1, mockedMirrorToken2], erc20Tokens: [], }); const uniqueSubAccountIds = new Set(result.map(sa => sa.id)); expect(uniqueSubAccountIds.size).toBe(result.length); expect(result).toHaveLength(2); expect(result).toMatchObject([ { token: firstTokenCurrencyFromCAL, balance: new BigNumber(10), operations: [mockedOperation1], }, { token: secondTokenCurrencyFromCAL, balance: new BigNumber(0), operations: [mockedOperation2], }, ]); }); it("ignores operation if token is not listed in CAL", async () => { const mockedTokenCurrency = getMockedHTSTokenCurrency(); const mockedOperation = getMockedOperation({ accountId: encodeTokenAccountId(mockedAccount.id, mockedTokenCurrency), }); const result = await getSubAccounts({ ledgerAccountId: mockedAccount.id, latestTokenOperations: [mockedOperation], mirrorTokens: [], erc20Tokens: [], }); expect(result).toEqual([]); }); it("returns sub account for mirror token with no operations yet (e.g. right after association)", async () => { const tokenCurrencyFromCAL = getTokenCurrencyFromCAL(0); const mockedTokenHTS = getMockedMirrorToken({ token_id: tokenCurrencyFromCAL.contractAddress, balance: 42, }); const result = await getSubAccounts({ ledgerAccountId: mockedAccount.id, latestTokenOperations: [], mirrorTokens: [mockedTokenHTS], erc20Tokens: [], }); expect(result).toMatchObject([ { token: tokenCurrencyFromCAL, operations: [], balance: new BigNumber(42), }, ]); }); it("returns sub account for erc20 token with no operations yet", async () => { const tokenCurrencyFromCAL = getTokenCurrencyFromCALByType("erc20"); const result = await getSubAccounts({ ledgerAccountId: mockedAccount.id, latestTokenOperations: [], mirrorTokens: [], erc20Tokens: [{ balance: new BigNumber(42), token: tokenCurrencyFromCAL }], }); expect(result).toMatchObject([ { token: tokenCurrencyFromCAL, operations: [], balance: new BigNumber(42), }, ]); }); }); });