diff --git a/libs/ledger-live-common/src/families/aptos/buildTransaction.test.ts b/libs/ledger-live-common/src/families/aptos/buildTransaction.test.ts new file mode 100644 index 000000000000..c65ab1af1b41 --- /dev/null +++ b/libs/ledger-live-common/src/families/aptos/buildTransaction.test.ts @@ -0,0 +1,62 @@ +import { createFixtureAccount } from "../../mock/fixtures/cryptoCurrencies"; +import createTransaction from "./createTransaction"; +import buildTransaction from "./buildTransaction"; +import { AptosAPI } from "./api"; +import { normalizeTransactionOptions } from "./logic"; +import { InputEntryFunctionData } from "@aptos-labs/ts-sdk"; +import { TransactionOptions } from "./types"; + +const generateTransaction = jest.fn(() => "tx"); + +jest.mock("./logic", () => ({ + normalizeTransactionOptions: jest.fn(() => ({ + maxGasAmount: "100", + gasUnitPrice: "200", + })), + DEFAULT_GAS: 100, + DEFAULT_GAS_PRICE: 200, +})); + +jest.mock("./api", () => { + return { + AptosAPI: function () { + return { + generateTransaction, + }; + }, + }; +}); + +describe("buildTransaction Test", () => { + it("should return tx", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + const aptosClient = new AptosAPI(account.currency.id); + const result = await buildTransaction(account, transaction, aptosClient); + + const expected = "tx"; + + expect(result).toBe(expected); + + const mockedNormalizeTransactionOptions = jest.mocked(normalizeTransactionOptions); + + expect(mockedNormalizeTransactionOptions).toHaveBeenCalledTimes(1); + expect(generateTransaction).toHaveBeenCalledTimes(1); + + const generateTransactionArgs: [string, InputEntryFunctionData, TransactionOptions][] = + generateTransaction.mock.calls[0]; + + expect(mockedNormalizeTransactionOptions.mock.calls[0][0]).toEqual({ + maxGasAmount: "100", + gasUnitPrice: "200", + }); + + expect(generateTransactionArgs[0]).toBe("0x01"); + expect(generateTransactionArgs[1]).toEqual({ + function: "0x1::aptos_account::transfer_coins", + typeArguments: ["0x1::aptos_coin::AptosCoin"], + functionArguments: ["", "0"], + }); + expect(generateTransactionArgs[2]).toEqual({ maxGasAmount: "100", gasUnitPrice: "200" }); + }); +}); diff --git a/libs/ledger-live-common/src/families/aptos/createTransaction.test.ts b/libs/ledger-live-common/src/families/aptos/createTransaction.test.ts new file mode 100644 index 000000000000..fbff2ea83902 --- /dev/null +++ b/libs/ledger-live-common/src/families/aptos/createTransaction.test.ts @@ -0,0 +1,32 @@ +import BigNumber from "bignumber.js"; +import createTransaction from "./createTransaction"; + +jest.mock("./logic", () => ({ + DEFAULT_GAS: 100, + DEFAULT_GAS_PRICE: 200, +})); + +describe("createTransaction Test", () => { + it("should return a transaction object", async () => { + const result = createTransaction(); + + const expected = { + family: "aptos", + mode: "send", + amount: BigNumber(0), + recipient: "", + useAllAmount: false, + firstEmulation: true, + options: { + maxGasAmount: "100", + gasUnitPrice: "200", + }, + estimate: { + maxGasAmount: "100", + gasUnitPrice: "200", + }, + }; + + expect(result).toEqual(expected); + }); +}); diff --git a/libs/ledger-live-common/src/families/aptos/estimateMaxSpendable.test.ts b/libs/ledger-live-common/src/families/aptos/estimateMaxSpendable.test.ts new file mode 100644 index 000000000000..4c3840d018aa --- /dev/null +++ b/libs/ledger-live-common/src/families/aptos/estimateMaxSpendable.test.ts @@ -0,0 +1,97 @@ +import { createFixtureAccount } from "../../mock/fixtures/cryptoCurrencies"; +import createTransaction from "./createTransaction"; +import estimateMaxSpendable from "./estimateMaxSpendable"; +import BigNumber from "bignumber.js"; + +jest.mock("./getFeesForTransaction", () => ({ + getEstimatedGas: jest.fn(() => ({ + fees: new BigNumber(0), + estimate: { + maxGasAmount: 1, + gasUnitPrice: 2, + sequenceNumber: "", + expirationTimestampSecs: "", + }, + errors: {}, + })), +})); + +describe("estimateMaxSpendable Test", () => { + describe("spendable balance is lower than the total gas", () => { + it("should return 0", async () => { + const account = createFixtureAccount(); + + const spendableBalance = new BigNumber(0); + + account.spendableBalance = spendableBalance; + + const result = await estimateMaxSpendable({ + account, + }); + + const expected = spendableBalance; + + expect(result.isEqualTo(expected)).toBe(true); + }); + }); + + describe("spendable balance is higher than the total gas", () => { + it("should return spendable amount minus total gas", async () => { + const account = createFixtureAccount(); + + const spendableBalance = new BigNumber(100000); + + account.spendableBalance = spendableBalance; + + const result = await estimateMaxSpendable({ + account, + }); + + const expected = new BigNumber(80000); + + expect(result.isEqualTo(expected)).toBe(true); + }); + }); + + describe("transaction spendable balance is higher than the total gas", () => { + it("should return transaction spendable amount minus total gas", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + const spendableBalance = new BigNumber(1); + + account.spendableBalance = spendableBalance; + + const result = await estimateMaxSpendable({ + account, + parentAccount: account, + transaction, + }); + + const expected = new BigNumber(0); + + expect(result.isEqualTo(expected)).toBe(true); + }); + }); + + describe("transaction spendable balance is higher than the total gas", () => { + it("should return transaction spendable amount minus total gas", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + const spendableBalance = new BigNumber(100000); + + account.spendableBalance = spendableBalance; + + const result = await estimateMaxSpendable({ + account, + parentAccount: account, + transaction, + }); + + const expected = new BigNumber(99998); + + expect(result.isEqualTo(expected)).toBe(true); + }); + }); +}); diff --git a/libs/ledger-live-common/src/families/aptos/getFeesForTransaction.ts b/libs/ledger-live-common/src/families/aptos/getFeesForTransaction.ts index 17049072ed11..dc1cfc42717c 100644 --- a/libs/ledger-live-common/src/families/aptos/getFeesForTransaction.ts +++ b/libs/ledger-live-common/src/families/aptos/getFeesForTransaction.ts @@ -122,11 +122,9 @@ export const getEstimatedGas = async ( ): Promise => { const key = getCacheKey(transaction); - if (CACHE.has(key)) { - return CACHE.get(key); + if (!CACHE.has(key)) { + CACHE.set(key, await getFee(account, transaction, aptosClient)); } - CACHE.set(key, await getFee(account, transaction, aptosClient)); - return CACHE.get(key); }; diff --git a/libs/ledger-live-common/src/families/aptos/transaction.test.ts b/libs/ledger-live-common/src/families/aptos/transaction.test.ts new file mode 100644 index 000000000000..6408d66fc4d2 --- /dev/null +++ b/libs/ledger-live-common/src/families/aptos/transaction.test.ts @@ -0,0 +1,214 @@ +import BigNumber from "bignumber.js"; +import { createFixtureAccount } from "../../mock/fixtures/cryptoCurrencies"; +import createTransaction from "./createTransaction"; +import { formatTransaction, fromTransactionRaw, toTransactionRaw } from "./transaction"; +import { Transaction, TransactionRaw } from "./types"; + +jest.mock("./logic", () => ({ + DEFAULT_GAS: 100, + DEFAULT_GAS_PRICE: 200, +})); + +describe("transaction Test", () => { + describe("when formatTransaction", () => { + describe("when amount is 0 and fee is 0", () => { + it("should return a transaction SEND to 0xff00 with fees=?", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.recipient = "0xff00"; + const result = formatTransaction(transaction, account); + + const expected = ` +SEND +TO 0xff00 +with fees=?`; + + expect(result).toBe(expected); + }); + }); + + describe("when amount is 0 and fee is 0.0001", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("0.0001"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + + describe("when amount is 0 and fee is 0.1", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("0.1"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + + describe("when amount is 1 and fee is 0.1", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.amount = new BigNumber("1"); + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("0.1"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND 0 +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + + describe("when amount is 10 and fee is 1", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.amount = new BigNumber("10"); + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("1"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND 0 +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + + describe("when amount is 1000 and fee is 1", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.amount = new BigNumber("1000"); + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("1"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND 0 +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + + describe("when using MAX with amount is 1000 and fee is 1", () => { + it("should return a transaction SEND to 0xff00 with fees=0", async () => { + const account = createFixtureAccount(); + const transaction = createTransaction(); + + transaction.amount = new BigNumber("1000"); + transaction.useAllAmount = true; + transaction.recipient = "0xff00"; + transaction.fees = new BigNumber("1"); + const result = formatTransaction(transaction, account); + + const expected = ` +SEND MAX +TO 0xff00 +with fees=0`; + + expect(result).toBe(expected); + }); + }); + }); + + describe("when fromTransactionRaw", () => { + it("should return the transaction object", () => { + const txRaw = { + family: "aptos", + mode: "send", + fees: null, + options: "{}", + estimate: "{}", + firstEmulation: "{}", + amount: "0.5", + recipient: "0xff00", + useAllAmount: false, + subAccountId: "0xff01", + recipientDomain: {}, + } as TransactionRaw; + + const result = fromTransactionRaw(txRaw); + + const expected = { + family: "aptos", + amount: new BigNumber("0.5"), + estimate: {}, + firstEmulation: {}, + mode: "send", + options: {}, + recipient: "0xff00", + recipientDomain: {}, + subAccountId: "0xff01", + useAllAmount: false, + }; + + expect(result).toEqual(expected); + }); + }); + + describe("when toTransactionRaw", () => { + it("should return the raw transaction object", () => { + const tx = { + family: "aptos", + amount: new BigNumber("0.5"), + estimate: {}, + firstEmulation: {}, + mode: "send", + options: {}, + recipient: "0xff00", + recipientDomain: {}, + subAccountId: "0xff01", + useAllAmount: false, + } as Transaction; + + const result = toTransactionRaw(tx); + + const expected = { + family: "aptos", + mode: "send", + fees: null, + options: "{}", + estimate: "{}", + firstEmulation: "{}", + amount: "0.5", + recipient: "0xff00", + useAllAmount: false, + subAccountId: "0xff01", + recipientDomain: {}, + } as TransactionRaw; + + expect(result).toEqual(expected); + }); + }); +}); diff --git a/libs/ledger-live-common/src/families/aptos/transaction.ts b/libs/ledger-live-common/src/families/aptos/transaction.ts index 86b51f85e939..b9ddbadf948a 100644 --- a/libs/ledger-live-common/src/families/aptos/transaction.ts +++ b/libs/ledger-live-common/src/families/aptos/transaction.ts @@ -14,16 +14,18 @@ import { formatCurrencyUnit } from "../../currencies"; export const formatTransaction = ( { mode, amount, fees, recipient, useAllAmount }: Transaction, account: Account, -): string => ` +): string => { + return ` ${mode.toUpperCase()} ${ - useAllAmount - ? "MAX" - : amount.isZero() - ? "" - : " " + formatCurrencyUnit(account.currency.units[0], amount) -} + useAllAmount + ? "MAX" + : amount.isZero() + ? "" + : " " + formatCurrencyUnit(account.currency.units[0], amount) + } TO ${recipient} with fees=${fees ? formatCurrencyUnit(account.currency.units[0], fees) : "?"}`; +}; export const fromTransactionRaw = (t: TransactionRaw): Transaction => { const common = fromTransactionCommonRaw(t);