UNPKG

@ledgerhq/coin-ton

Version:
298 lines 17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const operation_1 = require("@ledgerhq/coin-framework/lib/operation"); const bignumber_js_1 = __importDefault(require("bignumber.js")); // eslint-disable-next-line no-restricted-imports const core_1 = require("@ton/core"); const flatMap_1 = __importDefault(require("lodash/flatMap")); const txn_1 = require("../../bridge/bridgeHelpers/txn"); const common_fixtures_1 = require("../fixtures/common.fixtures"); describe("Transaction functions", () => { describe("mapTxToOps", () => { it("should map an IN failed ton transaction without total_fees to a ledger operation", async () => { const { now, lt, hash, in_msg, total_fees, mc_block_seqno } = common_fixtures_1.tonTransactionResponse.transactions[0]; const finalOperation = (0, flatMap_1.default)(common_fixtures_1.tonTransactionResponse.transactions, (0, txn_1.mapTxToOps)(common_fixtures_1.mockAccountId, common_fixtures_1.mockAddress, common_fixtures_1.tonTransactionResponse.address_book)); expect(finalOperation).toEqual([ { accountId: common_fixtures_1.mockAccountId, blockHash: null, blockHeight: mc_block_seqno, date: new Date(now * 1000), // now is defined in seconds extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt }, fee: (0, bignumber_js_1.default)(total_fees), hasFailed: true, hash: in_msg?.hash, id: (0, operation_1.encodeOperationId)(common_fixtures_1.mockAccountId, in_msg?.hash ?? "", "IN"), recipients: [in_msg?.destination], senders: ["EQCVnqqL0OOiZi2BQnjVGm-ZeUYgfUhHgAi-vn9F8-94HwrH"], type: "IN", value: (0, bignumber_js_1.default)(in_msg?.value ?? 0), subOperations: undefined, }, ]); }); it("should map an IN ton transaction with total_fees to a ledger operation", async () => { const transactions = [{ ...common_fixtures_1.tonTransactionResponse.transactions[0], total_fees: "15" }]; const { now, lt, hash, in_msg, total_fees, mc_block_seqno } = transactions[0]; const finalOperation = (0, flatMap_1.default)(transactions, (0, txn_1.mapTxToOps)(common_fixtures_1.mockAccountId, common_fixtures_1.mockAddress, common_fixtures_1.tonTransactionResponse.address_book)); expect(finalOperation).toEqual([ { accountId: common_fixtures_1.mockAccountId, blockHash: null, blockHeight: mc_block_seqno, date: new Date(now * 1000), // now is defined in seconds extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt }, fee: (0, bignumber_js_1.default)(total_fees), hasFailed: true, hash: in_msg?.hash, id: (0, operation_1.encodeOperationId)(common_fixtures_1.mockAccountId, in_msg?.hash ?? "", "IN"), recipients: [in_msg?.destination], senders: ["EQCVnqqL0OOiZi2BQnjVGm-ZeUYgfUhHgAi-vn9F8-94HwrH"], type: "IN", value: (0, bignumber_js_1.default)(in_msg?.value ?? 0), subOperations: [ { id: (0, operation_1.encodeOperationId)(common_fixtures_1.mockAccountId, in_msg?.hash ?? "", "NONE"), hash: in_msg?.hash, type: "NONE", value: (0, bignumber_js_1.default)(total_fees), fee: (0, bignumber_js_1.default)(0), blockHeight: mc_block_seqno, blockHash: null, hasFailed: true, accountId: common_fixtures_1.mockAccountId, senders: [common_fixtures_1.mockAddress], recipients: [], date: new Date(now * 1000), extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt }, }, ], }, ]); }); it("should map a failed OUT ton transaction to a ledger operation", async () => { // The IN transaction will be used as OUT transaction and it will be adjusted const transactions = [ { ...common_fixtures_1.tonTransactionResponse.transactions[0], in_msg: null, }, ]; if (common_fixtures_1.tonTransactionResponse.transactions[0].in_msg) { transactions[0].out_msgs = [ { ...common_fixtures_1.tonTransactionResponse.transactions[0].in_msg, source: transactions[0].account }, ]; } const { now, lt, hash, out_msgs, total_fees, mc_block_seqno } = transactions[0]; const finalOperation = (0, flatMap_1.default)(transactions, (0, txn_1.mapTxToOps)(common_fixtures_1.mockAccountId, common_fixtures_1.mockAddress, common_fixtures_1.tonTransactionResponse.address_book)); expect(finalOperation).toEqual([ { id: (0, operation_1.encodeOperationId)(common_fixtures_1.mockAccountId, hash, "OUT"), hash: out_msgs?.[0].hash, type: "OUT", value: (0, bignumber_js_1.default)(out_msgs?.[0].value ?? 0), fee: (0, bignumber_js_1.default)(total_fees), blockHeight: mc_block_seqno, blockHash: null, hasFailed: true, accountId: common_fixtures_1.mockAccountId, senders: [transactions[0].account], recipients: ["EQDzd8aeBOU-jqYw_ZSuZjceI5p-F4b7HMprAsUJAtRPbJfg"], date: new Date(now * 1000), // now is defined in seconds extra: { comment: { isEncrypted: false, text: "" }, explorerHash: hash, lt }, }, ]); }); }); describe("mapJettonToOps", () => { it("should map an IN ton transaction without total_fees to a ledger operation", async () => { const { transaction_hash, amount, transaction_now, transaction_lt } = common_fixtures_1.jettonTransferResponse.jetton_transfers[0]; const finalOperation = (0, flatMap_1.default)(common_fixtures_1.jettonTransferResponse.jetton_transfers, (0, txn_1.mapJettonTxToOps)(common_fixtures_1.mockAccountId, common_fixtures_1.mockAddress, common_fixtures_1.tonTransactionResponse.address_book)); const tokenByCurrencyAddress = `${common_fixtures_1.mockAccountId}+ton%2Fjetton%2Feqavlwfdxgf2lxm67y4yzc17wykd9a0guwpkms1gosm~!underscore!~~!underscore!~not`; expect(finalOperation).toEqual([ { id: (0, operation_1.encodeOperationId)(tokenByCurrencyAddress, transaction_hash, "IN"), hash: transaction_hash, type: "IN", value: (0, bignumber_js_1.default)(amount), fee: (0, bignumber_js_1.default)(0), blockHeight: 1, blockHash: null, hasFailed: false, accountId: tokenByCurrencyAddress, senders: ["EQDnqcVSV4S9m2Y9gLAQrDerQktKSx2I1uhs6r5o_H8VT9G-"], recipients: [common_fixtures_1.mockAddress], date: new Date(transaction_now * 1000), // now is defined in seconds extra: { comment: { isEncrypted: false, text: "" }, explorerHash: transaction_hash, lt: transaction_lt, }, }, ]); }); it("should map an OUT jetton transaction to a ledger operation", async () => { // The IN jetton transaction will be used as OUT transaction and it will be adjusted const jettonTransfers = [ { ...common_fixtures_1.jettonTransferResponse.jetton_transfers[0], }, ]; jettonTransfers[0].source = jettonTransfers[0].destination; jettonTransfers[0].destination = common_fixtures_1.jettonTransferResponse.jetton_transfers[0].source; const { transaction_hash, amount, transaction_now, transaction_lt } = jettonTransfers[0]; const finalOperation = (0, flatMap_1.default)(jettonTransfers, (0, txn_1.mapJettonTxToOps)(common_fixtures_1.mockAccountId, common_fixtures_1.mockAddress, common_fixtures_1.tonTransactionResponse.address_book)); const tokenByCurrencyAddress = `${common_fixtures_1.mockAccountId}+ton%2Fjetton%2Feqavlwfdxgf2lxm67y4yzc17wykd9a0guwpkms1gosm~!underscore!~~!underscore!~not`; expect(finalOperation).toEqual([ { id: (0, operation_1.encodeOperationId)(tokenByCurrencyAddress, transaction_hash, "OUT"), hash: transaction_hash, type: "OUT", value: (0, bignumber_js_1.default)(amount), fee: (0, bignumber_js_1.default)(0), blockHeight: 1, blockHash: null, hasFailed: false, accountId: tokenByCurrencyAddress, recipients: ["EQDnqcVSV4S9m2Y9gLAQrDerQktKSx2I1uhs6r5o_H8VT9G-"], senders: [common_fixtures_1.mockAddress], date: new Date(transaction_now * 1000), // now is defined in seconds extra: { comment: { isEncrypted: false, text: "" }, explorerHash: transaction_hash, lt: transaction_lt, }, }, ]); }); }); }); describe("TON Payload Processing Functions", () => { describe("dataToSlice", () => { it("should convert base64 string to Slice when it's a valid BOC", () => { // Create a Cell from a string and convert to BOC const cell = new core_1.Builder().storeUint(123, 32).endCell(); const bocBase64 = cell.toBoc().toString("base64"); const result = (0, txn_1.dataToSlice)(bocBase64); expect(result).toBeInstanceOf(core_1.Slice); expect(result?.loadUint(32)).toBe(123); }); it("should fallback to BitString when the data is not a valid BOC", () => { const invalidBocBase64 = "aW52YWxpZCB0b24gZGF0YQ=="; // "invalid ton data" const result = (0, txn_1.dataToSlice)(invalidBocBase64); expect(result).toBeInstanceOf(core_1.Slice); }); it("should return undefined for non-string input", () => { // @ts-expect-error - Testing invalid input const result = (0, txn_1.dataToSlice)(null); expect(result).toBeUndefined(); }); }); describe("loadSnakeBytes", () => { it("should load bytes from a simple slice without refs", () => { const cell = new core_1.Builder().storeBuffer(Buffer.from("Slice", "utf-8")).endCell(); const slice = cell.beginParse(); const result = (0, txn_1.loadSnakeBytes)(slice); expect(result.toString("utf-8")).toBe("Slice"); }); it("should load bytes from a slice with refs (snake structure)", () => { // Create a chain of cells (snake structure) const cell2 = new core_1.Builder().storeBuffer(Buffer.from(" Data", "utf-8")).endCell(); const cell1 = new core_1.Builder() .storeBuffer(Buffer.from("Slice", "utf-8")) .storeRef(cell2) .endCell(); const slice = cell1.beginParse(); const result = (0, txn_1.loadSnakeBytes)(slice); expect(result.toString("utf-8")).toBe("Slice Data"); }); it("should handle empty slice", () => { const cell = new core_1.Builder().endCell(); const slice = cell.beginParse(); const result = (0, txn_1.loadSnakeBytes)(slice); expect(result.length).toBe(0); }); it("should handle slice with multiple refs in chain", () => { // Create a longer chain of cells (snake structure) const cell3 = new core_1.Builder().storeBuffer(Buffer.from("Part3", "utf-8")).endCell(); const cell2 = new core_1.Builder() .storeBuffer(Buffer.from("Part2", "utf-8")) .storeRef(cell3) .endCell(); const cell1 = new core_1.Builder() .storeBuffer(Buffer.from("Part1", "utf-8")) .storeRef(cell2) .endCell(); const slice = cell1.beginParse(); const result = (0, txn_1.loadSnakeBytes)(slice); expect(result.toString("utf-8")).toBe("Part1Part2Part3"); }); }); describe("decodeForwardPayload", () => { it("should return empty string for null payload", () => { const result = (0, txn_1.decodeForwardPayload)(null); expect(result).toBe(""); }); it("should decode a valid payload with opcode 0 containing text", () => { // Create a cell with opcode 0 followed by a text string const cell = new core_1.Builder() .storeUint(0, 32) // opcode 0 .storeBuffer(Buffer.from("This is the comment", "utf-8")) .endCell(); const bocBase64 = cell.toBoc().toString("base64"); const result = (0, txn_1.decodeForwardPayload)(bocBase64); expect(result).toBe("This is the comment"); }); it("should return empty string for payloads with non-zero opcode", () => { // Create a cell with opcode 1 followed by some data const cell = new core_1.Builder() .storeUint(1, 32) // non-zero opcode .storeBuffer(Buffer.from("Should be ignored", "utf-8")) .endCell(); const bocBase64 = cell.toBoc().toString("base64"); const result = (0, txn_1.decodeForwardPayload)(bocBase64); expect(result).toBe(""); }); it("should handle payload with unicode characters", () => { // Create a cell with opcode 0 followed by a text with unicode const cell = new core_1.Builder() .storeUint(0, 32) // opcode 0 .storeBuffer(Buffer.from("Unicode: 你好, мир, 🚀", "utf-8")) .endCell(); const bocBase64 = cell.toBoc().toString("base64"); const result = (0, txn_1.decodeForwardPayload)(bocBase64); expect(result).toBe("Unicode: 你好, мир, 🚀"); }); it("should handle snake format payloads correctly", () => { // Create a chain of cells with opcode 0 followed by a long message const cell2 = new core_1.Builder() .storeBuffer(Buffer.from(" would need multiple cells to store.", "utf-8")) .endCell(); const cell1 = new core_1.Builder() .storeUint(0, 32) // opcode 0 .storeBuffer(Buffer.from("This is a very long message that", "utf-8")) .storeRef(cell2) .endCell(); const bocBase64 = cell1.toBoc().toString("base64"); const result = (0, txn_1.decodeForwardPayload)(bocBase64); expect(result).toBe("This is a very long message that would need multiple cells to store."); }); it("should handle invalid payloads gracefully by returning empty string", () => { // Create an invalid base64 string const invalidBase64 = "!@#$%^&*()"; const result = (0, txn_1.decodeForwardPayload)(invalidBase64); expect(result).toBe(""); }); it("should handle valid base64 but invalid BOC payloads", () => { // Valid base64 but not a valid BOC const validBase64NotBoc = "aW52YWxpZCB0b24gZGF0YQ=="; // "invalid ton data" in base64 const result = (0, txn_1.decodeForwardPayload)(validBase64NotBoc); // Should return empty string as it's not a valid Cell expect(result).toBe(""); }); }); }); //# sourceMappingURL=txn.unit.test.js.map