Skip to content

Commit

Permalink
Merge pull request #25 from Zondax/feat/ton-restructure-files
Browse files Browse the repository at this point in the history
refac(ton): move ton to coin-framework
  • Loading branch information
ayelenmurano committed Apr 26, 2024
2 parents fbf59b5 + 524bc1f commit dff0968
Show file tree
Hide file tree
Showing 46 changed files with 1,094 additions and 733 deletions.
20 changes: 20 additions & 0 deletions libs/coin-modules/coin-ton/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
env: {
browser: true,
es6: true,
},
overrides: [
{
files: ["src/**/*.test.{ts,tsx}"],
env: {
"jest/globals": true,
},
plugins: ["jest"],
},
],
rules: {
"no-console": ["error", { allow: ["warn", "error"] }],
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "warn",
},
};
12 changes: 12 additions & 0 deletions libs/coin-modules/coin-ton/.unimportedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"entry": [
"src/bridge/js.ts",
"src/errors.ts",
"src/hw-getAddress.ts",
"src/types.ts",
"src/cli-transaction.ts"
],
"ignoreUnimported": [
"src/transaction.ts"
]
}
6 changes: 6 additions & 0 deletions libs/coin-modules/coin-ton/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["lib/", "lib-es/"],
};
84 changes: 84 additions & 0 deletions libs/coin-modules/coin-ton/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"name": "@ledgerhq/coin-ton",
"version": "0.3.11",
"description": "Ton Coin integration",
"keywords": [
"Ledger",
"LedgerWallet",
"ton",
"Ton",
"Hardware Wallet"
],
"repository": {
"type": "git",
"url": "https://github.com/LedgerHQ/ledger-live.git"
},
"bugs": {
"url": "https://github.com/LedgerHQ/ledger-live/issues"
},
"homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/coin-modules/coin-ton",
"publishConfig": {
"access": "public"
},
"typesVersions": {
"*": {
"lib/*": [
"lib/*"
],
"lib-es/*": [
"lib-es/*"
],
"*": [
"lib/*"
]
}
},
"exports": {
"./lib/*": "./lib/*.js",
"./lib-es/*": "./lib-es/*.js",
"./*": {
"require": "./lib/*.js",
"default": "./lib-es/*.js"
},
"./package.json": "./package.json"
},
"license": "Apache-2.0",
"dependencies": {
"@ledgerhq/coin-framework": "workspace:^",
"@ledgerhq/devices": "workspace:^",
"@ledgerhq/errors": "workspace:^",
"@ledgerhq/live-env": "workspace:^",
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
"@ledgerhq/hw-transport": "workspace:^",
"@ton-community/ton-ledger": "^7.0.1",
"@ton/core": "^0.56.1",
"@ton/ton": "^13.11.1",
"@ton/crypto": "^3.2.0",
"bignumber.js": "^9.1.2",
"expect": "^27.4.6",
"invariant": "^2.2.2",
"lodash": "^4.17.21",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/invariant": "^2.2.2",
"@types/jest": "^29.5.10",
"@types/lodash": "^4.14.191",
"jest": "^29.7.0",
"ts-jest": "^29.1.1"
},
"scripts": {
"clean": "rimraf lib lib-es",
"build": "tsc && tsc -m ES6 --outDir lib-es",
"prewatch": "pnpm build",
"watch": "tsc --watch",
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
"lint:fix": "pnpm lint --fix",
"test": "jest",
"unimported": "unimported"
}
}
146 changes: 146 additions & 0 deletions libs/coin-modules/coin-ton/src/bridge.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { InvalidAddress, NotEnoughBalance } from "@ledgerhq/errors";
import { CurrenciesData, DatasetTest } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
import { TonCommentInvalid } from "./errors";
import { fromTransactionRaw } from "./transaction";
import { Transaction } from "./types";

const PUBKEY = "86196cb40cd25e9e696bc808e3f2c074ce0b39f2a2a9d482a68eafef86e4a060";
const ADDRESS = "UQCOvQLYvTcbi5tL9MaDNzuVl3-J3vATimNm9yO5XPafLfV4";
const ADDRESS_2 = "UQAui6M4jOYOezUGfmeONA22Ars9yjd34YIGdAR1Pcpp4sgR";
const PATH = "44'/607'/0'/0'/0'/0'";

const ton: CurrenciesData<Transaction> = {
scanAccounts: [
{
name: "ton seed 1",
apdus: `
=> e005000019068000002c8000025f80000000800000008000000080000000
<= 86196cb40cd25e9e696bc808e3f2c074ce0b39f2a2a9d482a68eafef86e4a0609000
=> e005000019068000002c8000025f80000000800000008000000180000000
<= b5177c2b32f9d72fa8c673cc3d61acec6a9f68eb5e4945445fdbb48a45eb48879000
`,
test: (expect, accounts) => {
for (const account of accounts) {
expect(account.derivationMode).toEqual("ton");
}
},
},
],
accounts: [
{
raw: {
id: `js:2:ton:${PUBKEY}:ton`,
currencyId: "ton",
seedIdentifier: PUBKEY,
name: "TON 1",
derivationMode: "ton",
index: 0,
freshAddress: ADDRESS,
freshAddressPath: PATH,
freshAddresses: [
{
address: ADDRESS,
derivationPath: PATH,
},
],
xpub: PUBKEY,
blockHeight: 0,
operations: [],
pendingOperations: [],
unitMagnitude: 9,
lastSyncDate: "",
balance: "5000000000",
},
transactions: [
{
name: "Not a valid address",
transaction: fromTransactionRaw({
family: "ton",
recipient: "novalidaddress",
fees: "10000000",
amount: "1000",
comment: { isEncrypted: false, text: "" },
}),
expectedStatus: {
errors: {
recipient: new InvalidAddress(),
},
warnings: {},
},
},
{
name: "Not enough balance",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: (300 * 1e9).toString(),
comment: { isEncrypted: false, text: "" },
}),
expectedStatus: {
errors: {
amount: new NotEnoughBalance(),
},
warnings: {},
},
},
{
name: "Invalid transferID/Memo",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: (1 * 1e9).toString(),
comment: { isEncrypted: false, text: "😀" },
}),
expectedStatus: {
errors: {
comment: new TonCommentInvalid(),
},
warnings: {},
},
},
{
name: "New account and sufficient amount",
transaction: fromTransactionRaw({
family: "ton",
recipient: ADDRESS_2,
fees: "10000000",
amount: "10000000",
comment: { isEncrypted: false, text: "Valid" },
}),
expectedStatus: {
amount: new BigNumber("10000000"),
errors: {},
warnings: {},
},
},
],
},
],
};

export const dataset: DatasetTest<Transaction> = {
implementations: ["js"],
currencies: {
ton,
},
};

describe("Ton bridge", () => {
test.todo(
"This is an empty test to make jest command pass. Remove it once there is a real test.",
);
});

/**
* NOTE: if tests are added to this file,
* like done in libs/coin-polkadot/src/bridge.integration.test.ts for example,
* this file fill need to be imported in ledger-live-common
* libs/ledger-live-common/src/families/ton/bridge.integration.test.ts
* like done for polkadot.
* cf.
* - libs/coin-polkadot/src/bridge.integration.test.ts
* - libs/ledger-live-common/src/families/polkadot/bridge.integration.test.ts
*/
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function getTransactions(
// we found the last transaction
if (tmpTxs.transactions.length === 1) break;
// it should always match
if (hash !== tmpTxs[0].hash) throw Error("[ton] transaction hash does not match");
if (hash !== tmpTxs.transactions[0].hash) throw Error("[ton] transaction hash does not match");
tmpTxs.transactions.shift(); // first element is repeated
txs.transactions.push(...tmpTxs.transactions);
txs.address_book = { ...txs.address_book, ...tmpTxs.address_book };
Expand Down Expand Up @@ -67,7 +67,6 @@ export function mapTxToOps(

const date = new Date(tx.now * 1000); // now is defined in seconds
const hash = tx.in_msg?.hash ?? tx.hash; // this is the hash we know in signature time

if (isReceiving) {
if (tx.total_fees !== "0") {
// these are small amount of fees payed when receiving
Expand Down
64 changes: 64 additions & 0 deletions libs/coin-modules/coin-ton/src/bridge/js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
import {
defaultUpdateTransaction,
makeAccountBridgeReceive,
makeScanAccounts,
} from "@ledgerhq/coin-framework/bridge/jsHelpers";
import { SignerContext } from "@ledgerhq/coin-framework/signer";

import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live";
import resolver from "../hw-getAddress";
import broadcast from "../js-broadcast";
import createTransaction from "../js-createTransaction";
import estimateMaxSpendable from "../js-estimateMaxSpendable";
import getTransactionStatus from "../js-getTransactionStatus";
import prepareTransaction from "../js-prepareTransaction";
import { buildSignOperation } from "../js-signOperation";
import { getAccountShape, sync } from "../js-synchronisation";
import { TonAddress, TonSignature, TonSigner } from "../signer";
import type { Transaction } from "../types";

export function buildCurrencyBridge(
signerContext: SignerContext<TonSigner, TonAddress | TonSignature>,
): CurrencyBridge {
const getAddress = resolver(signerContext);

const scanAccounts = makeScanAccounts({
getAccountShape,
getAddressFn: getAddress,
});

return {
preload: async () => Promise.resolve({}),
hydrate: () => {},
scanAccounts,
};
}

export function buildAccountBridge(
signerContext: SignerContext<TonSigner, TonAddress | TonSignature>,
): AccountBridge<Transaction> {
const getAddress = resolver(signerContext);

const receive = makeAccountBridgeReceive(getAddressWrapper(getAddress));
const signOperation = buildSignOperation(signerContext);

return {
estimateMaxSpendable,
createTransaction,
updateTransaction: defaultUpdateTransaction,
getTransactionStatus,
prepareTransaction,
sync,
receive,
signOperation,
broadcast,
};
}

export function createBridges(signerContext: SignerContext<TonSigner, TonAddress | TonSignature>) {
return {
currencyBridge: buildCurrencyBridge(signerContext),
accountBridge: buildAccountBridge(signerContext),
};
}
32 changes: 32 additions & 0 deletions libs/coin-modules/coin-ton/src/cli-transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import invariant from "invariant";
import flatMap from "lodash/flatMap";

import type { AccountLike } from "@ledgerhq/types-live";
import { Transaction } from "./types";

const options: any = [];

function inferTransactions(
transactions: Array<{
account: AccountLike;
transaction: Transaction;
}>,
opts: Record<string, string>,
): Transaction[] {
return flatMap(transactions, ({ transaction }) => {
invariant(transaction.family === "ton", "ton family");

return {
...transaction,
family: "ton",
mode: opts.mode || "send",
} as Transaction;
});
}

export default function makeCliTools() {
return {
options,
inferTransactions,
};
}
6 changes: 6 additions & 0 deletions libs/coin-modules/coin-ton/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createCustomErrorClass } from "@ledgerhq/errors";

/*
* When the recipient is a new named account, and needs to be created first.
*/
export const TonCommentInvalid: any = createCustomErrorClass("TonCommentInvalid");

0 comments on commit dff0968

Please sign in to comment.