UNPKG

@ledgerhq/coin-canton

Version:
242 lines (215 loc) 7.6 kB
import { getCryptoCurrencyById } from "@ledgerhq/ledger-wallet-framework/currencies"; import { AccountShapeInfo } from "@ledgerhq/ledger-wallet-framework/bridge/jsHelpers"; import { getDerivationModesForCurrency, getDerivationScheme, runDerivationScheme, } from "@ledgerhq/ledger-wallet-framework/derivation"; import { Operation } from "@ledgerhq/types-live"; import BigNumber from "bignumber.js"; import coinConfig from "../config"; import * as gateway from "../network/gateway"; import { generateMockKeyPair, createMockSigner } from "../test/cantonTestUtils"; import { CantonAccount } from "../types"; import { makeGetAccountShape } from "./sync"; const TEST_ADDRESS = "b6400f93ea1c74aea86be39b0ccc846fc5de01f12b2ad0d7c31848d6fb6eb6d9::1220c81315e2bf2524a9141bcc6cbf19b61c151e0dcaa95343c0ccf53aed7415c4ec"; const currency = getCryptoCurrencyById("canton_network"); const derivationMode = getDerivationModesForCurrency(currency)[0]; const derivationPath = runDerivationScheme( getDerivationScheme({ derivationMode, currency }), currency, { account: 0, }, ); const xpub = TEST_ADDRESS; const ACCOUNT_SHAPE_INFO: AccountShapeInfo<CantonAccount> = { address: TEST_ADDRESS, currency, derivationMode, derivationPath, index: 0, initialAccount: { xpub, } as CantonAccount, }; const TIMEOUT = 30000; // Mock signer context for testing const keyPair = generateMockKeyPair(); const mockSigner = createMockSigner(keyPair); const mockSignerContext = jest.fn().mockImplementation((_deviceId, callback) => { return callback(mockSigner); }); describe.skip("sync (devnet)", () => { beforeAll(async () => { coinConfig.setCoinConfig(() => ({ gatewayUrl: "https://canton-gateway-devnet.api.live.ledger-test.com", useGateway: true, networkType: "devnet", nativeInstrumentId: "Amulet", status: { type: "active", }, })); }); describe("makeGetAccountShape", () => { it( "should fetch account shape for a valid address", async () => { const getAccountShape = makeGetAccountShape(mockSignerContext); const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} }); expect(result.id).toEqual(expect.any(String)); expect(result.xpub).toBe(TEST_ADDRESS); expect(result.blockHeight).toBeGreaterThan(0); expect(result.balance?.toNumber()).toBeGreaterThanOrEqual(0); expect(result.spendableBalance?.toNumber()).toBeGreaterThanOrEqual(0); expect(result.operations).toBeInstanceOf(Array); expect(result.operationsCount).toBeGreaterThanOrEqual(0); expect(result.spendableBalance?.toNumber()).toBeLessThanOrEqual( result.balance?.toNumber() || 0, ); }, TIMEOUT, ); it( "should handle address with colons correctly", async () => { const getAccountShape = makeGetAccountShape(mockSignerContext); const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} }); expect(result.xpub).toContain("::"); }, TIMEOUT, ); it( "should merge operations correctly with initial account", async () => { const getAccountShape = makeGetAccountShape(mockSignerContext); const operations: Operation[] = [ { id: "test-op-1", hash: "test-hash-1", accountId: "test-account", type: "OUT" as const, value: new BigNumber(1000000), fee: new BigNumber(100000), blockHash: "block-hash-1", blockHeight: 100, senders: [TEST_ADDRESS], recipients: ["recipient-1"], date: new Date("2023-01-01"), transactionSequenceNumber: BigNumber(100), extra: { uid: "uid-1" }, }, ]; const result = await getAccountShape( { ...ACCOUNT_SHAPE_INFO, initialAccount: { xpub, operations, cantonResources: { isOnboarded: true, instrumentUtxoCounts: {}, pendingTransferProposals: [], }, } as unknown as CantonAccount, }, { paginationConfig: {} }, ); expect(result.operationsCount).toBeGreaterThanOrEqual(1); expect(result.operations?.find(op => op.id === "test-op-1")).toMatchObject({ id: "test-op-1", }); }, TIMEOUT, ); it( "should take locked balance into account when calculating spendable balance", async () => { const mockGetBalance = jest.spyOn(gateway, "getBalance"); mockGetBalance.mockResolvedValue([ { instrument_id: "Amulet", admin_id: "native-admin", amount: "500", locked: false, utxo_count: 1, }, { instrument_id: "LockedAmulet", admin_id: "native-admin", amount: "1000000", locked: true, utxo_count: 1, }, ]); const getAccountShape = makeGetAccountShape(mockSignerContext); const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} }); expect(result.balance?.toNumber()).toBe(1000500); expect(result.spendableBalance?.toNumber()).toBe(500); mockGetBalance.mockRestore(); }, TIMEOUT, ); it( "should call getOperations with correct cursor based on initial account", async () => { const mockGetOperations = jest.spyOn(gateway, "getOperations"); const operation: Operation = { id: "test-op-1", hash: "test-hash-1", accountId: "test-account", type: "OUT" as const, value: new BigNumber(1000000), fee: new BigNumber(100000), blockHash: "block-hash-1", blockHeight: 100, senders: [TEST_ADDRESS], recipients: ["recipient-1"], date: new Date("2023-01-01"), transactionSequenceNumber: BigNumber(100), extra: { uid: "uid-1" }, }; const initialAccount = { xpub, operations: [operation], cantonResources: { isOnboarded: true, instrumentUtxoCounts: {}, pendingTransferProposals: [], }, } as unknown as CantonAccount; const getAccountShape = makeGetAccountShape(mockSignerContext); const result = await getAccountShape( { ...ACCOUNT_SHAPE_INFO, initialAccount, }, { paginationConfig: {} }, ); expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, { cursor: (operation.blockHeight || 0) + 1, limit: 100, }); expect(result.operationsCount).toBeGreaterThan(1); }, TIMEOUT, ); it( "should call getOperations with cursor 0 when no initial account", async () => { const mockGetOperations = jest.spyOn(gateway, "getOperations"); const getAccountShape = makeGetAccountShape(mockSignerContext); const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} }); expect(result.operationsCount).toBeGreaterThanOrEqual(1); expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, { cursor: 0, limit: 100, }); mockGetOperations.mockRestore(); }, TIMEOUT, ); }); });