@ledgerhq/coin-algorand
Version:
Ledger Algorand Coin integration
229 lines (193 loc) • 6.83 kB
text/typescript
import type { CryptoCurrency, Unit } from "@ledgerhq/types-cryptoassets";
import type { Account, SubAccount } from "@ledgerhq/types-live";
import { BigNumber } from "bignumber.js";
import transactionModule, { formatTransaction } from "./transaction";
import type { AlgorandTransaction, AlgorandTransactionRaw } from "./types";
// Mock dependencies
jest.mock("@ledgerhq/ledger-wallet-framework/account/index", () => ({
getAccountCurrency: jest.fn().mockImplementation((account: Account) => account.currency),
}));
jest.mock("@ledgerhq/coin-module-framework/currencies/index", () => ({
formatCurrencyUnit: jest.fn().mockImplementation((unit: Unit, amount: BigNumber) => {
return `${amount.toString()} ${unit.code}`;
}),
}));
describe("transaction", () => {
const mockUnit: Unit = {
name: "Algo",
code: "ALGO",
magnitude: 6,
};
const mockCurrency: CryptoCurrency = {
type: "CryptoCurrency",
id: "algorand",
name: "Algorand",
ticker: "ALGO",
scheme: "algorand",
color: "#000000",
family: "algorand",
units: [mockUnit],
explorerViews: [],
managerAppName: "Algorand",
};
const mockMainAccount = {
id: "algorand-account-1",
currency: mockCurrency,
subAccounts: [] as SubAccount[],
} as Account;
describe("formatTransaction", () => {
it("should format a SEND transaction", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "send",
subAccountId: undefined,
amount: new BigNumber("1000000"),
recipient: "RECIPIENT_ADDRESS",
fees: new BigNumber("1000"),
useAllAmount: false,
};
const result = formatTransaction(transaction, mockMainAccount);
expect(result).toContain("SEND");
expect(result).toContain("1000000 ALGO");
expect(result).toContain("TO RECIPIENT_ADDRESS");
expect(result).toContain("fees=1000 ALGO");
});
it("should format a claimReward transaction", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "claimReward",
subAccountId: undefined,
amount: new BigNumber("0"),
recipient: "RECIPIENT_ADDRESS",
fees: new BigNumber("1000"),
useAllAmount: false,
};
const result = formatTransaction(transaction, mockMainAccount);
expect(result).toContain("CLAIM REWARD");
});
it("should format an optIn transaction", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "optIn",
subAccountId: undefined,
amount: new BigNumber("0"),
recipient: "RECIPIENT_ADDRESS",
fees: new BigNumber("1000"),
useAllAmount: false,
};
const result = formatTransaction(transaction, mockMainAccount);
expect(result).toContain("OPT_IN");
});
it("should format MAX amount when useAllAmount is true", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "send",
subAccountId: undefined,
amount: new BigNumber("1000000"),
recipient: "RECIPIENT_ADDRESS",
fees: new BigNumber("1000"),
useAllAmount: true,
};
const result = formatTransaction(transaction, mockMainAccount);
expect(result).toContain("MAX");
});
it("should show ? for undefined fees", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "send",
subAccountId: undefined,
amount: new BigNumber("1000000"),
recipient: "RECIPIENT_ADDRESS",
fees: null,
useAllAmount: false,
};
const result = formatTransaction(transaction, mockMainAccount);
expect(result).toContain("fees=?");
});
});
describe("fromTransactionRaw", () => {
it("should convert raw transaction to transaction", () => {
const raw: AlgorandTransactionRaw = {
family: "algorand",
mode: "send",
amount: "1000000",
recipient: "RECIPIENT_ADDRESS",
fees: "1000",
memo: "test memo",
assetId: "12345",
};
const result = transactionModule.fromTransactionRaw(raw);
expect(result.family).toBe("algorand");
expect(result.mode).toBe("send");
expect(result.fees).toBeInstanceOf(BigNumber);
expect(result.fees?.toString()).toBe("1000");
expect(result.memo).toBe("test memo");
expect(result.assetId).toBe("12345");
});
it("should handle null fees", () => {
const raw: AlgorandTransactionRaw = {
family: "algorand",
mode: "send",
amount: "1000000",
recipient: "RECIPIENT_ADDRESS",
fees: null,
memo: undefined,
assetId: undefined,
};
const result = transactionModule.fromTransactionRaw(raw);
expect(result.fees).toBeNull();
});
});
describe("toTransactionRaw", () => {
it("should convert transaction to raw transaction", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "send",
amount: new BigNumber("1000000"),
recipient: "RECIPIENT_ADDRESS",
fees: new BigNumber("1000"),
memo: "test memo",
assetId: "12345",
};
const result = transactionModule.toTransactionRaw(transaction);
expect(result.family).toBe("algorand");
expect(result.mode).toBe("send");
expect(result.fees).toBe("1000");
expect(result.memo).toBe("test memo");
expect(result.assetId).toBe("12345");
});
it("should handle null fees", () => {
const transaction: AlgorandTransaction = {
family: "algorand",
mode: "send",
amount: new BigNumber("1000000"),
recipient: "RECIPIENT_ADDRESS",
fees: null,
memo: undefined,
assetId: undefined,
};
const result = transactionModule.toTransactionRaw(transaction);
expect(result.fees).toBeNull();
});
});
describe("round-trip conversion", () => {
it("should preserve data through toRaw and fromRaw", () => {
const original: AlgorandTransaction = {
family: "algorand",
mode: "optIn",
amount: new BigNumber("500000"),
recipient: "SOME_ADDRESS",
fees: new BigNumber("2000"),
memo: "round trip test",
assetId: "99999",
};
const raw = transactionModule.toTransactionRaw(original);
const restored = transactionModule.fromTransactionRaw(raw);
expect(restored.family).toBe(original.family);
expect(restored.mode).toBe(original.mode);
expect(restored.fees?.toString()).toBe(original.fees?.toString());
expect(restored.memo).toBe(original.memo);
expect(restored.assetId).toBe(original.assetId);
});
});
});