@ledgerhq/coin-aptos
Version:
Ledger Aptos Coin integration
997 lines • 44.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const constants_1 = require("../../constants");
const logic_1 = require("../../bridge/logic");
const bridge_fixture_1 = require("../../bridge/bridge.fixture");
const cryptoassets_1 = require("@ledgerhq/cryptoassets");
const index_1 = require("@ledgerhq/coin-framework/account/index");
const normalizeTransactionOptions_1 = require("../../logic/normalizeTransactionOptions");
jest.mock("@ledgerhq/cryptoassets");
jest.mock("@ledgerhq/coin-framework/account/index");
describe("Aptos logic ", () => {
describe("getMaxSendBalance", () => {
it("should return the correct max send balance when amount is greater than total gas", () => {
const amount = new bignumber_js_1.default(1000000);
const account = (0, bridge_fixture_1.createFixtureAccount)({ balance: amount, spendableBalance: amount });
const transaction = (0, bridge_fixture_1.createFixtureTransaction)();
const gas = new bignumber_js_1.default(200);
const gasPrice = new bignumber_js_1.default(100);
const result = (0, logic_1.getMaxSendBalance)(account, transaction, gas, gasPrice);
expect(result.isEqualTo(amount.minus(gas.multipliedBy(gasPrice)))).toBe(true);
});
it("should return zero when amount is less than total gas", () => {
const account = (0, bridge_fixture_1.createFixtureAccount)();
const transaction = (0, bridge_fixture_1.createFixtureTransaction)();
const gas = new bignumber_js_1.default(200);
const gasPrice = new bignumber_js_1.default(100);
const result = (0, logic_1.getMaxSendBalance)(account, transaction, gas, gasPrice);
expect(result.isEqualTo(new bignumber_js_1.default(0))).toBe(true);
});
it("should return zero when amount is equal to total gas", () => {
const account = (0, bridge_fixture_1.createFixtureAccount)();
const transaction = (0, bridge_fixture_1.createFixtureTransaction)();
const gas = new bignumber_js_1.default(200);
const gasPrice = new bignumber_js_1.default(100);
const result = (0, logic_1.getMaxSendBalance)(account, transaction, gas, gasPrice);
expect(result.isEqualTo(new bignumber_js_1.default(0))).toBe(true);
});
it("should handle zero amount", () => {
const account = (0, bridge_fixture_1.createFixtureAccount)();
const transaction = (0, bridge_fixture_1.createFixtureTransaction)();
const gas = new bignumber_js_1.default(200);
const gasPrice = new bignumber_js_1.default(100);
const result = (0, logic_1.getMaxSendBalance)(account, transaction, gas, gasPrice);
expect(result.isEqualTo(new bignumber_js_1.default(0))).toBe(true);
});
it("should handle zero gas and gas price", () => {
const amount = new bignumber_js_1.default(1000000);
const account = (0, bridge_fixture_1.createFixtureAccount)({ balance: amount, spendableBalance: amount });
const transaction = (0, bridge_fixture_1.createFixtureTransaction)();
const gas = new bignumber_js_1.default(0);
const gasPrice = new bignumber_js_1.default(0);
const result = (0, logic_1.getMaxSendBalance)(account, transaction, gas, gasPrice);
expect(result.isEqualTo(amount)).toBe(true);
});
});
describe("normalizeTransactionOptions", () => {
it("should normalize transaction options", () => {
const options = {
maxGasAmount: "1000",
gasUnitPrice: "10",
};
const result = (0, normalizeTransactionOptions_1.normalizeTransactionOptions)(options);
expect(result).toEqual(options);
});
it("should return undefined for empty values", () => {
const options = {
maxGasAmount: "",
gasUnitPrice: "",
};
const result = (0, normalizeTransactionOptions_1.normalizeTransactionOptions)(options);
expect(result).toEqual({
maxGasAmount: undefined,
gasUnitPrice: undefined,
});
});
});
describe("getBlankOperation", () => {
it("should return a blank operation", () => {
const tx = {
hash: "0x123",
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
version: "1",
};
const id = "test_id";
const result = (0, logic_1.getBlankOperation)(tx, id);
expect(result).toEqual({
id: "",
hash: "0x123",
type: "",
value: new bignumber_js_1.default(0),
fee: new bignumber_js_1.default(0),
blockHash: "0xabc",
blockHeight: 1,
senders: [],
recipients: [],
accountId: id,
date: new Date(1000),
extra: { version: "1" },
transactionSequenceNumber: 1,
hasFailed: false,
});
});
it("should return a blank operation even when some transaction fields are missing", () => {
const tx = {
hash: "0x123",
timestamp: "1000000",
sequence_number: "1",
};
const id = "test_id";
const result = (0, logic_1.getBlankOperation)(tx, id);
expect(result).toEqual({
id: "",
hash: "0x123",
type: "",
value: new bignumber_js_1.default(0),
fee: new bignumber_js_1.default(0),
blockHash: undefined,
blockHeight: undefined,
senders: [],
recipients: [],
accountId: id,
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: false,
});
});
});
});
describe("Aptos sync logic ", () => {
describe("txsToOps", () => {
it("should convert Aptos transactions to operations correctly", () => {
const address = "0x11";
const id = "test_id";
const txs = [
{
hash: "0x123",
sender: "0x11",
gas_used: "200",
gas_unit_price: "100",
success: true,
payload: {
type: "entry_function_payload",
function: "0x1::coin::transfer",
type_arguments: [],
arguments: ["0x12", 100],
},
events: [
{
type: "0x1::coin::WithdrawEvent",
guid: {
account_address: "0x11",
creation_number: "1",
},
data: {
amount: "100",
},
},
{
type: "0x1::coin::DepositEvent",
guid: {
account_address: "0x12",
creation_number: "2",
},
data: {
amount: "100",
},
},
],
changes: [
{
type: "write_resource",
data: {
type: constants_1.APTOS_COIN_CHANGE,
data: {
withdraw_events: {
guid: {
id: {
addr: "0x11",
creation_num: "1",
},
},
},
deposit_events: {
guid: {
id: {
addr: "0x12",
creation_num: "2",
},
},
},
},
},
},
],
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
},
];
const [result] = (0, logic_1.txsToOps)({ address }, id, txs);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
id: expect.any(String),
hash: "0x123",
type: constants_1.OP_TYPE.OUT,
value: new bignumber_js_1.default(100),
fee: new bignumber_js_1.default(20000),
blockHash: "0xabc",
blockHeight: 1,
senders: ["0x11"],
recipients: ["0x0000000000000000000000000000000000000000000000000000000000000012"],
accountId: id,
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: false,
});
});
it("should skip transactions without functions in payload", () => {
const address = "0x11";
const id = "test_id";
const txs = [
{
hash: "0x123",
sender: "0x11",
gas_used: "200",
gas_unit_price: "100",
success: true,
payload: {},
events: [],
changes: [],
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
},
];
const [result] = (0, logic_1.txsToOps)({ address }, id, txs);
expect(result).toHaveLength(0);
});
it("should skip transactions that result in no Aptos change", () => {
const address = "0x11";
const id = "test_id";
const txs = [
{
hash: "0x123",
sender: "0x12",
gas_used: "200",
gas_unit_price: "100",
success: true,
payload: {
type: "entry_function_payload",
function: "0x1::coin::transfer",
type_arguments: [],
arguments: ["0x11", 100],
},
events: [],
changes: [],
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
},
];
const [result] = (0, logic_1.txsToOps)({ address }, id, txs);
expect(result).toHaveLength(0);
});
it("should handle failed transactions", () => {
const address = "0xa0d8";
const id = "test_id";
const txs = [
{
hash: "0x0189",
sender: "0xa0d8",
gas_used: "200",
gas_unit_price: "100",
success: false,
payload: {
function: "0x1::coin::transfer",
type_arguments: ["0xd111::staked_coin::StakedAptos"],
arguments: ["0x4e5e", "50000000"],
type: "entry_function_payload",
},
events: [
{
guid: {
creation_number: "0",
account_address: "0x0",
},
sequence_number: "0",
type: "0x1::transaction_fee::FeeStatement",
data: {
execution_gas_units: "5",
io_gas_units: "4",
storage_fee_octas: "0",
storage_fee_refund_octas: "0",
total_charge_gas_units: "8",
},
},
],
changes: [
{
address: "0xa0d8",
state_key_hash: "0x1709",
data: {
type: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
data: {
coin: {
value: "573163341",
},
deposit_events: {
counter: "45",
guid: {
id: {
addr: "0xa0d8",
creation_num: "2",
},
},
},
frozen: false,
withdraw_events: {
counter: "82",
guid: {
id: {
addr: "0xa0d8",
creation_num: "3",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d8",
state_key_hash: "0x6f1e",
data: {
type: "0x1::account::Account",
data: {
authentication_key: "0xa0d8",
coin_register_events: {
counter: "5",
guid: {
id: {
addr: "0xa0d8",
creation_num: "0",
},
},
},
guid_creation_num: "12",
key_rotation_events: {
counter: "0",
guid: {
id: {
addr: "0xa0d8",
creation_num: "1",
},
},
},
rotation_capability_offer: {
for: {
vec: [],
},
},
sequence_number: "83",
signer_capability_offer: {
for: {
vec: [],
},
},
},
},
type: "write_resource",
},
{
state_key_hash: "0x6e4b",
handle: "0x1b85",
key: "0x0619",
value: "0x72c5e483c25c96010000000000000000",
data: null,
type: "write_table_item",
},
],
block: {
hash: "0xc496",
height: 1,
},
timestamp: "1000000",
sequence_number: "1",
},
];
const [result] = (0, logic_1.txsToOps)({ address }, id, txs);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
id: expect.any(String),
hash: "0x0189",
type: constants_1.OP_TYPE.OUT,
value: new bignumber_js_1.default(20000),
fee: new bignumber_js_1.default(20000),
blockHash: "0xc496",
blockHeight: 1,
senders: ["0xa0d8"],
recipients: ["0x0000000000000000000000000000000000000000000000000000000000004e5e"],
accountId: id,
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: true,
});
});
it("should convert Aptos token transactions to operations correctly", () => {
cryptoassets_1.findTokenByAddressInCurrency.mockReturnValue({
type: "TokenCurrency",
id: "aptos/coin/dstapt::staked_coin::stakedaptos",
contractAddress: "0xd111::staked_coin::StakedAptos",
parentCurrency: {
type: "CryptoCurrency",
id: "aptos",
coinType: 637,
name: "Aptos",
managerAppName: "Aptos",
ticker: "APT",
scheme: "aptos",
color: "#231F20",
family: "aptos",
units: [
{
name: "APT",
code: "APT",
magnitude: 8,
},
],
explorerViews: [
{
address: "https://explorer.aptoslabs.com/account/$address?network=mainnet",
tx: "https://explorer.aptoslabs.com/txn/$hash?network=mainnet",
},
],
},
name: "dstAPT",
tokenType: "coin",
ticker: "dstAPT",
disableCountervalue: false,
delisted: false,
units: [
{
name: "dstAPT",
code: "dstAPT",
magnitude: 8,
},
],
});
jest.mock("../../bridge/logic", () => ({
...jest.requireActual("../../bridge/logic"),
getResourceAddress: jest.fn().mockReturnValue("0xd111::staked_coin::StakedAptos"),
}));
index_1.decodeTokenAccountId.mockReturnValue({
accountId: "token_account_id",
});
const address = "0xa0d";
const id = "test_id";
const txs = [
{
hash: "0x123",
sender: address,
gas_used: "200",
gas_unit_price: "100",
success: true,
payload: {
function: "0x1::aptos_account::transfer_coins",
type_arguments: ["0xd111::staked_coin::StakedAptos"],
arguments: ["0x4e5", "1500000"],
type: "entry_function_payload",
},
events: [
{
guid: {
creation_number: "11",
account_address: "0xa0d",
},
sequence_number: "12",
type: "0x1::coin::WithdrawEvent",
data: {
amount: "1500000",
},
},
{
guid: {
creation_number: "4",
account_address: "0x4e5",
},
sequence_number: "8",
type: "0x1::coin::DepositEvent",
data: {
amount: "1500000",
},
},
{
guid: {
creation_number: "0",
account_address: "0x0",
},
sequence_number: "0",
type: "0x1::transaction_fee::FeeStatement",
data: {
execution_gas_units: "6",
io_gas_units: "6",
storage_fee_octas: "0",
storage_fee_refund_octas: "0",
total_charge_gas_units: "12",
},
},
],
changes: [
{
address: "0x4e5",
state_key_hash: "0x3c0",
data: {
type: "0x1::coin::CoinStore<0xd111::staked_coin::StakedAptos>",
data: {
coin: {
value: "4000000",
},
deposit_events: {
counter: "9",
guid: {
id: {
addr: "0x4e5",
creation_num: "4",
},
},
},
frozen: false,
withdraw_events: {
counter: "6",
guid: {
id: {
addr: "0x4e5",
creation_num: "5",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d",
state_key_hash: "0x1709",
data: {
type: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
data: {
coin: {
value: "68254118",
},
deposit_events: {
counter: "46",
guid: {
id: {
addr: "0xa0d",
creation_num: "2",
},
},
},
frozen: false,
withdraw_events: {
counter: "89",
guid: {
id: {
addr: "0xa0d",
creation_num: "3",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d",
state_key_hash: "0x5520",
data: {
type: "0x1::coin::CoinStore<0xd111::staked_coin::StakedAptos>",
data: {
coin: {
value: "1000000",
},
deposit_events: {
counter: "7",
guid: {
id: {
addr: "0xa0d",
creation_num: "10",
},
},
},
frozen: false,
withdraw_events: {
counter: "13",
guid: {
id: {
addr: "0xa0d",
creation_num: "11",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d",
state_key_hash: "0x6f1e",
data: {
type: "0x1::account::Account",
data: {
authentication_key: "0xa0d",
coin_register_events: {
counter: "5",
guid: {
id: {
addr: "0xa0d",
creation_num: "0",
},
},
},
guid_creation_num: "12",
key_rotation_events: {
counter: "0",
guid: {
id: {
addr: "0xa0d",
creation_num: "1",
},
},
},
rotation_capability_offer: {
for: {
vec: [],
},
},
sequence_number: "122",
signer_capability_offer: {
for: {
vec: [],
},
},
},
},
type: "write_resource",
},
{
state_key_hash: "0x6e4b",
handle: "0x1b85",
key: "0x0619",
value: "0x1ddaf8da3b1497010000000000000000",
data: null,
type: "write_table_item",
},
],
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
},
];
const [ops, tokenOps] = (0, logic_1.txsToOps)({ address }, id, txs);
expect(ops).toHaveLength(1);
expect(ops[0]).toEqual({
id: expect.any(String),
hash: "0x123",
type: "FEES",
value: new bignumber_js_1.default(20000),
fee: new bignumber_js_1.default(20000),
blockHash: "0xabc",
blockHeight: 1,
senders: ["0xa0d"],
recipients: ["0x00000000000000000000000000000000000000000000000000000000000004e5"],
accountId: "token_account_id",
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: false,
});
expect(tokenOps).toHaveLength(1);
expect(tokenOps[0]).toEqual({
id: expect.any(String),
hash: "0x123",
type: constants_1.OP_TYPE.OUT,
value: new bignumber_js_1.default(1500000),
fee: new bignumber_js_1.default(20000),
blockHash: "0xabc",
blockHeight: 1,
senders: ["0xa0d"],
recipients: ["0x00000000000000000000000000000000000000000000000000000000000004e5"],
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: false,
});
});
it("should convert Aptos token transactions to operations correctly", () => {
cryptoassets_1.findTokenByAddressInCurrency.mockReturnValue({
type: "TokenCurrency",
id: "aptos/fungible_asset/cellana_0x2ebb2ccac5e027a87fa0e2e5f656a3a4238d6a48d93ec9b610d570fc0aa0df12",
contractAddress: "0x2ebb",
parentCurrency: {
type: "CryptoCurrency",
id: "aptos",
coinType: 637,
name: "Aptos",
managerAppName: "Aptos",
ticker: "APT",
scheme: "aptos",
color: "#231F20",
family: "aptos",
units: [
{
name: "APT",
code: "APT",
magnitude: 8,
},
],
explorerViews: [
{
address: "https://explorer.aptoslabs.com/account/$address?network=mainnet",
tx: "https://explorer.aptoslabs.com/txn/$hash?network=mainnet",
},
],
},
name: "CELLANA",
tokenType: "fungible_asset",
ticker: "CELL",
disableCountervalue: false,
delisted: false,
units: [
{
name: "CELLANA",
code: "CELL",
magnitude: 8,
},
],
});
jest.mock("../../bridge/logic", () => ({
...jest.requireActual("../../bridge/logic"),
getResourceAddress: jest.fn().mockReturnValue("0x2ebb"),
}));
index_1.encodeTokenAccountId.mockReturnValue("token_account_id");
const txs = [
{
hash: "0x10c9",
sender: "0xa0d8",
gas_used: "200",
gas_unit_price: "100",
success: true,
payload: {
function: "0x1::primary_fungible_store::transfer",
type_arguments: ["0x1::fungible_asset::Metadata"],
arguments: [
{
inner: "0x2ebb",
},
"0x6b8c",
"193",
],
type: "entry_function_payload",
},
events: [
{
guid: {
creation_number: "0",
account_address: "0x0",
},
sequence_number: "0",
type: "0x1::fungible_asset::Withdraw",
data: {
amount: "193",
store: "0xd475",
},
},
{
guid: {
creation_number: "0",
account_address: "0x0",
},
sequence_number: "0",
type: "0x1::fungible_asset::Deposit",
data: {
amount: "193",
store: "0xaaa9",
},
},
{
guid: {
creation_number: "0",
account_address: "0x0",
},
sequence_number: "0",
type: "0x1::transaction_fee::FeeStatement",
data: {
execution_gas_units: "4",
io_gas_units: "6",
storage_fee_octas: "0",
storage_fee_refund_octas: "0",
total_charge_gas_units: "10",
},
},
],
changes: [
{
address: "0xaaa9",
state_key_hash: "0x9a17",
data: {
type: "0x1::fungible_asset::FungibleStore",
data: {
balance: "10044959",
frozen: false,
metadata: {
inner: "0x2ebb",
},
},
},
type: "write_resource",
},
{
address: "0xaaa9",
state_key_hash: "0x9a17",
data: {
type: "0x1::object::ObjectCore",
data: {
allow_ungated_transfer: false,
guid_creation_num: "1125899906842625",
owner: "0x6b8c",
transfer_events: {
counter: "0",
guid: {
id: {
addr: "0xaaa9",
creation_num: "1125899906842624",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d8",
state_key_hash: "0x1709",
data: {
type: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
data: {
coin: {
value: "98423118",
},
deposit_events: {
counter: "46",
guid: {
id: {
addr: "0xa0d8",
creation_num: "2",
},
},
},
frozen: false,
withdraw_events: {
counter: "88",
guid: {
id: {
addr: "0xa0d8",
creation_num: "3",
},
},
},
},
},
type: "write_resource",
},
{
address: "0xa0d8",
state_key_hash: "0x6f1e",
data: {
type: "0x1::account::Account",
data: {
authentication_key: "0xa0d8",
coin_register_events: {
counter: "5",
guid: {
id: {
addr: "0xa0d8",
creation_num: "0",
},
},
},
guid_creation_num: "12",
key_rotation_events: {
counter: "0",
guid: {
id: {
addr: "0xa0d8",
creation_num: "1",
},
},
},
rotation_capability_offer: {
for: {
vec: [],
},
},
sequence_number: "108",
signer_capability_offer: {
for: {
vec: [],
},
},
},
},
type: "write_resource",
},
{
address: "0xd475",
state_key_hash: "0x7567",
data: {
type: "0x1::fungible_asset::FungibleStore",
data: {
balance: "14000",
frozen: false,
metadata: {
inner: "0x2ebb",
},
},
},
type: "write_resource",
},
{
address: "0xd475",
state_key_hash: "0x7567",
data: {
type: "0x1::object::ObjectCore",
data: {
allow_ungated_transfer: false,
guid_creation_num: "1125899906842625",
owner: "0xa0d8",
transfer_events: {
counter: "0",
guid: {
id: {
addr: "0xd475",
creation_num: "1125899906842624",
},
},
},
},
},
type: "write_resource",
},
{
state_key_hash: "0x6e4b",
handle: "0x1b85",
key: "0x0619",
value: "0xad4388dc7daf96010000000000000000",
data: null,
type: "write_table_item",
},
],
block: { hash: "0xabc", height: 1 },
timestamp: "1000000",
sequence_number: "1",
},
];
const [ops, tokenOps] = (0, logic_1.txsToOps)({ address: "0x6b8c" }, "test_id", txs);
expect(ops).toHaveLength(0);
expect(tokenOps).toHaveLength(1);
expect(tokenOps[0]).toEqual({
id: expect.any(String),
accountId: "token_account_id",
hash: "0x10c9",
type: constants_1.OP_TYPE.IN,
value: new bignumber_js_1.default(193),
fee: new bignumber_js_1.default(20000),
blockHash: "0xabc",
blockHeight: 1,
senders: ["0xa0d8"],
recipients: ["0x0000000000000000000000000000000000000000000000000000000000006b8c"],
date: new Date(1000),
extra: { version: undefined },
transactionSequenceNumber: 1,
hasFailed: false,
});
});
});
});
//# sourceMappingURL=logic.test.js.map