@ledgerhq/coin-ton 0.3.12-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +20 -0
- package/.turbo/turbo-build.log +4 -0
- package/.unimportedrc.json +19 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE.txt +21 -0
- package/jest.config.js +12 -0
- package/lib/__tests__/fixtures/api.fixtures.d.ts +4 -0
- package/lib/__tests__/fixtures/api.fixtures.d.ts.map +1 -0
- package/lib/__tests__/fixtures/api.fixtures.js +33 -0
- package/lib/__tests__/fixtures/api.fixtures.js.map +1 -0
- package/lib/__tests__/fixtures/common.fixtures.d.ts +29 -0
- package/lib/__tests__/fixtures/common.fixtures.d.ts.map +1 -0
- package/lib/__tests__/fixtures/common.fixtures.js +124 -0
- package/lib/__tests__/fixtures/common.fixtures.js.map +1 -0
- package/lib/__tests__/integration/bridge.integration.test.d.ts +14 -0
- package/lib/__tests__/integration/bridge.integration.test.d.ts.map +1 -0
- package/lib/__tests__/integration/bridge.integration.test.js +136 -0
- package/lib/__tests__/integration/bridge.integration.test.js.map +1 -0
- package/lib/__tests__/unit/api.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/api.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/api.unit.test.js +77 -0
- package/lib/__tests__/unit/api.unit.test.js.map +1 -0
- package/lib/__tests__/unit/broadcast.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/broadcast.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/broadcast.unit.test.js +40 -0
- package/lib/__tests__/unit/broadcast.unit.test.js.map +1 -0
- package/lib/__tests__/unit/createTransaction.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/createTransaction.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/createTransaction.unit.test.js +34 -0
- package/lib/__tests__/unit/createTransaction.unit.test.js.map +1 -0
- package/lib/__tests__/unit/deviceTransactionConfig.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/deviceTransactionConfig.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/deviceTransactionConfig.unit.test.js +74 -0
- package/lib/__tests__/unit/deviceTransactionConfig.unit.test.js.map +1 -0
- package/lib/__tests__/unit/estimateMaxSpendable.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/estimateMaxSpendable.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/estimateMaxSpendable.unit.test.js +31 -0
- package/lib/__tests__/unit/estimateMaxSpendable.unit.test.js.map +1 -0
- package/lib/__tests__/unit/getTransactionStatus.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/getTransactionStatus.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/getTransactionStatus.unit.test.js +96 -0
- package/lib/__tests__/unit/getTransactionStatus.unit.test.js.map +1 -0
- package/lib/__tests__/unit/hw-getAddress.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/hw-getAddress.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/hw-getAddress.unit.test.js +67 -0
- package/lib/__tests__/unit/hw-getAddress.unit.test.js.map +1 -0
- package/lib/__tests__/unit/prepareTransaction.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/prepareTransaction.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/prepareTransaction.unit.test.js +42 -0
- package/lib/__tests__/unit/prepareTransaction.unit.test.js.map +1 -0
- package/lib/__tests__/unit/signOperation.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/signOperation.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/signOperation.unit.test.js +83 -0
- package/lib/__tests__/unit/signOperation.unit.test.js.map +1 -0
- package/lib/__tests__/unit/txn.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/txn.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/txn.unit.test.js +116 -0
- package/lib/__tests__/unit/txn.unit.test.js.map +1 -0
- package/lib/__tests__/unit/utils.unit.test.d.ts +2 -0
- package/lib/__tests__/unit/utils.unit.test.d.ts.map +1 -0
- package/lib/__tests__/unit/utils.unit.test.js +97 -0
- package/lib/__tests__/unit/utils.unit.test.js.map +1 -0
- package/lib/bridge/bridgeHelpers/api.d.ts +10 -0
- package/lib/bridge/bridgeHelpers/api.d.ts.map +1 -0
- package/lib/bridge/bridgeHelpers/api.js +104 -0
- package/lib/bridge/bridgeHelpers/api.js.map +1 -0
- package/lib/bridge/bridgeHelpers/api.types.d.ts +164 -0
- package/lib/bridge/bridgeHelpers/api.types.d.ts.map +1 -0
- package/lib/bridge/bridgeHelpers/api.types.js +3 -0
- package/lib/bridge/bridgeHelpers/api.types.js.map +1 -0
- package/lib/bridge/bridgeHelpers/txn.d.ts +5 -0
- package/lib/bridge/bridgeHelpers/txn.d.ts.map +1 -0
- package/lib/bridge/bridgeHelpers/txn.js +166 -0
- package/lib/bridge/bridgeHelpers/txn.js.map +1 -0
- package/lib/bridge/js.d.ts +12 -0
- package/lib/bridge/js.d.ts.map +1 -0
- package/lib/bridge/js.js +65 -0
- package/lib/bridge/js.js.map +1 -0
- package/lib/broadcast.d.ts +5 -0
- package/lib/broadcast.d.ts.map +1 -0
- package/lib/broadcast.js +19 -0
- package/lib/broadcast.js.map +1 -0
- package/lib/cli-transaction.d.ts +12 -0
- package/lib/cli-transaction.d.ts.map +1 -0
- package/lib/cli-transaction.js +21 -0
- package/lib/cli-transaction.js.map +1 -0
- package/lib/config.d.ts +9 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +16 -0
- package/lib/config.js.map +1 -0
- package/lib/createTransaction.d.ts +5 -0
- package/lib/createTransaction.d.ts.map +1 -0
- package/lib/createTransaction.js +16 -0
- package/lib/createTransaction.js.map +1 -0
- package/lib/deviceTransactionConfig.d.ts +11 -0
- package/lib/deviceTransactionConfig.d.ts.map +1 -0
- package/lib/deviceTransactionConfig.js +37 -0
- package/lib/deviceTransactionConfig.js.map +1 -0
- package/lib/errors.d.ts +4 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +9 -0
- package/lib/errors.js.map +1 -0
- package/lib/estimateMaxSpendable.d.ts +5 -0
- package/lib/estimateMaxSpendable.d.ts.map +1 -0
- package/lib/estimateMaxSpendable.js +32 -0
- package/lib/estimateMaxSpendable.js.map +1 -0
- package/lib/getTransactionStatus.d.ts +5 -0
- package/lib/getTransactionStatus.d.ts.map +1 -0
- package/lib/getTransactionStatus.js +97 -0
- package/lib/getTransactionStatus.js.map +1 -0
- package/lib/hw-getAddress.d.ts +6 -0
- package/lib/hw-getAddress.d.ts.map +1 -0
- package/lib/hw-getAddress.js +31 -0
- package/lib/hw-getAddress.js.map +1 -0
- package/lib/hw-signMessage.d.ts +12 -0
- package/lib/hw-signMessage.d.ts.map +1 -0
- package/lib/hw-signMessage.js +33 -0
- package/lib/hw-signMessage.js.map +1 -0
- package/lib/prepareTransaction.d.ts +5 -0
- package/lib/prepareTransaction.d.ts.map +1 -0
- package/lib/prepareTransaction.js +29 -0
- package/lib/prepareTransaction.js.map +1 -0
- package/lib/signOperation.d.ts +11 -0
- package/lib/signOperation.d.ts.map +1 -0
- package/lib/signOperation.js +96 -0
- package/lib/signOperation.js.map +1 -0
- package/lib/signer.d.ts +27 -0
- package/lib/signer.d.ts.map +1 -0
- package/lib/signer.js +3 -0
- package/lib/signer.js.map +1 -0
- package/lib/specs.d.ts +8 -0
- package/lib/specs.d.ts.map +1 -0
- package/lib/specs.js +106 -0
- package/lib/specs.js.map +1 -0
- package/lib/speculos-deviceActions.d.ts +4 -0
- package/lib/speculos-deviceActions.d.ts.map +1 -0
- package/lib/speculos-deviceActions.js +34 -0
- package/lib/speculos-deviceActions.js.map +1 -0
- package/lib/synchronisation.d.ts +5 -0
- package/lib/synchronisation.d.ts.map +1 -0
- package/lib/synchronisation.js +95 -0
- package/lib/synchronisation.js.map +1 -0
- package/lib/transaction.d.ts +14 -0
- package/lib/transaction.d.ts.map +1 -0
- package/lib/transaction.js +41 -0
- package/lib/transaction.js.map +1 -0
- package/lib/types.d.ts +65 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib/utils.d.ts +11 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +125 -0
- package/lib/utils.js.map +1 -0
- package/lib-es/__tests__/fixtures/api.fixtures.d.ts +4 -0
- package/lib-es/__tests__/fixtures/api.fixtures.d.ts.map +1 -0
- package/lib-es/__tests__/fixtures/api.fixtures.js +30 -0
- package/lib-es/__tests__/fixtures/api.fixtures.js.map +1 -0
- package/lib-es/__tests__/fixtures/common.fixtures.d.ts +29 -0
- package/lib-es/__tests__/fixtures/common.fixtures.d.ts.map +1 -0
- package/lib-es/__tests__/fixtures/common.fixtures.js +118 -0
- package/lib-es/__tests__/fixtures/common.fixtures.js.map +1 -0
- package/lib-es/__tests__/integration/bridge.integration.test.d.ts +14 -0
- package/lib-es/__tests__/integration/bridge.integration.test.d.ts.map +1 -0
- package/lib-es/__tests__/integration/bridge.integration.test.js +130 -0
- package/lib-es/__tests__/integration/bridge.integration.test.js.map +1 -0
- package/lib-es/__tests__/unit/api.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/api.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/api.unit.test.js +52 -0
- package/lib-es/__tests__/unit/api.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/broadcast.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/broadcast.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/broadcast.unit.test.js +35 -0
- package/lib-es/__tests__/unit/broadcast.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/createTransaction.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/createTransaction.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/createTransaction.unit.test.js +29 -0
- package/lib-es/__tests__/unit/createTransaction.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.js +69 -0
- package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/estimateMaxSpendable.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/estimateMaxSpendable.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/estimateMaxSpendable.unit.test.js +26 -0
- package/lib-es/__tests__/unit/estimateMaxSpendable.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/getTransactionStatus.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/getTransactionStatus.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/getTransactionStatus.unit.test.js +91 -0
- package/lib-es/__tests__/unit/getTransactionStatus.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/hw-getAddress.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/hw-getAddress.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/hw-getAddress.unit.test.js +62 -0
- package/lib-es/__tests__/unit/hw-getAddress.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/prepareTransaction.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/prepareTransaction.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/prepareTransaction.unit.test.js +37 -0
- package/lib-es/__tests__/unit/prepareTransaction.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/signOperation.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/signOperation.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/signOperation.unit.test.js +78 -0
- package/lib-es/__tests__/unit/signOperation.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/txn.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/txn.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/txn.unit.test.js +111 -0
- package/lib-es/__tests__/unit/txn.unit.test.js.map +1 -0
- package/lib-es/__tests__/unit/utils.unit.test.d.ts +2 -0
- package/lib-es/__tests__/unit/utils.unit.test.d.ts.map +1 -0
- package/lib-es/__tests__/unit/utils.unit.test.js +95 -0
- package/lib-es/__tests__/unit/utils.unit.test.js.map +1 -0
- package/lib-es/bridge/bridgeHelpers/api.d.ts +10 -0
- package/lib-es/bridge/bridgeHelpers/api.d.ts.map +1 -0
- package/lib-es/bridge/bridgeHelpers/api.js +93 -0
- package/lib-es/bridge/bridgeHelpers/api.js.map +1 -0
- package/lib-es/bridge/bridgeHelpers/api.types.d.ts +164 -0
- package/lib-es/bridge/bridgeHelpers/api.types.d.ts.map +1 -0
- package/lib-es/bridge/bridgeHelpers/api.types.js +2 -0
- package/lib-es/bridge/bridgeHelpers/api.types.js.map +1 -0
- package/lib-es/bridge/bridgeHelpers/txn.d.ts +5 -0
- package/lib-es/bridge/bridgeHelpers/txn.d.ts.map +1 -0
- package/lib-es/bridge/bridgeHelpers/txn.js +158 -0
- package/lib-es/bridge/bridgeHelpers/txn.js.map +1 -0
- package/lib-es/bridge/js.d.ts +12 -0
- package/lib-es/bridge/js.d.ts.map +1 -0
- package/lib-es/bridge/js.js +56 -0
- package/lib-es/bridge/js.js.map +1 -0
- package/lib-es/broadcast.d.ts +5 -0
- package/lib-es/broadcast.d.ts.map +1 -0
- package/lib-es/broadcast.js +17 -0
- package/lib-es/broadcast.js.map +1 -0
- package/lib-es/cli-transaction.d.ts +12 -0
- package/lib-es/cli-transaction.d.ts.map +1 -0
- package/lib-es/cli-transaction.js +15 -0
- package/lib-es/cli-transaction.js.map +1 -0
- package/lib-es/config.d.ts +9 -0
- package/lib-es/config.d.ts.map +1 -0
- package/lib-es/config.js +11 -0
- package/lib-es/config.js.map +1 -0
- package/lib-es/createTransaction.d.ts +5 -0
- package/lib-es/createTransaction.d.ts.map +1 -0
- package/lib-es/createTransaction.js +14 -0
- package/lib-es/createTransaction.js.map +1 -0
- package/lib-es/deviceTransactionConfig.d.ts +11 -0
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -0
- package/lib-es/deviceTransactionConfig.js +35 -0
- package/lib-es/deviceTransactionConfig.js.map +1 -0
- package/lib-es/errors.d.ts +4 -0
- package/lib-es/errors.d.ts.map +1 -0
- package/lib-es/errors.js +6 -0
- package/lib-es/errors.js.map +1 -0
- package/lib-es/estimateMaxSpendable.d.ts +5 -0
- package/lib-es/estimateMaxSpendable.d.ts.map +1 -0
- package/lib-es/estimateMaxSpendable.js +30 -0
- package/lib-es/estimateMaxSpendable.js.map +1 -0
- package/lib-es/getTransactionStatus.d.ts +5 -0
- package/lib-es/getTransactionStatus.d.ts.map +1 -0
- package/lib-es/getTransactionStatus.js +93 -0
- package/lib-es/getTransactionStatus.js.map +1 -0
- package/lib-es/hw-getAddress.d.ts +6 -0
- package/lib-es/hw-getAddress.d.ts.map +1 -0
- package/lib-es/hw-getAddress.js +29 -0
- package/lib-es/hw-getAddress.js.map +1 -0
- package/lib-es/hw-signMessage.d.ts +12 -0
- package/lib-es/hw-signMessage.d.ts.map +1 -0
- package/lib-es/hw-signMessage.js +29 -0
- package/lib-es/hw-signMessage.js.map +1 -0
- package/lib-es/prepareTransaction.d.ts +5 -0
- package/lib-es/prepareTransaction.d.ts.map +1 -0
- package/lib-es/prepareTransaction.js +27 -0
- package/lib-es/prepareTransaction.js.map +1 -0
- package/lib-es/signOperation.d.ts +11 -0
- package/lib-es/signOperation.d.ts.map +1 -0
- package/lib-es/signOperation.js +91 -0
- package/lib-es/signOperation.js.map +1 -0
- package/lib-es/signer.d.ts +27 -0
- package/lib-es/signer.d.ts.map +1 -0
- package/lib-es/signer.js +2 -0
- package/lib-es/signer.js.map +1 -0
- package/lib-es/specs.d.ts +8 -0
- package/lib-es/specs.d.ts.map +1 -0
- package/lib-es/specs.js +99 -0
- package/lib-es/specs.js.map +1 -0
- package/lib-es/speculos-deviceActions.d.ts +4 -0
- package/lib-es/speculos-deviceActions.d.ts.map +1 -0
- package/lib-es/speculos-deviceActions.js +31 -0
- package/lib-es/speculos-deviceActions.js.map +1 -0
- package/lib-es/synchronisation.d.ts +5 -0
- package/lib-es/synchronisation.d.ts.map +1 -0
- package/lib-es/synchronisation.js +88 -0
- package/lib-es/synchronisation.js.map +1 -0
- package/lib-es/transaction.d.ts +14 -0
- package/lib-es/transaction.d.ts.map +1 -0
- package/lib-es/transaction.js +33 -0
- package/lib-es/transaction.js.map +1 -0
- package/lib-es/types.d.ts +65 -0
- package/lib-es/types.d.ts.map +1 -0
- package/lib-es/types.js +2 -0
- package/lib-es/types.js.map +1 -0
- package/lib-es/utils.d.ts +11 -0
- package/lib-es/utils.d.ts.map +1 -0
- package/lib-es/utils.js +112 -0
- package/lib-es/utils.js.map +1 -0
- package/package.json +86 -0
- package/src/__tests__/fixtures/api.fixtures.ts +39 -0
- package/src/__tests__/fixtures/common.fixtures.ts +139 -0
- package/src/__tests__/integration/bridge.integration.test.ts +135 -0
- package/src/__tests__/unit/api.unit.test.ts +60 -0
- package/src/__tests__/unit/broadcast.unit.test.ts +36 -0
- package/src/__tests__/unit/createTransaction.unit.test.ts +20 -0
- package/src/__tests__/unit/deviceTransactionConfig.unit.test.ts +69 -0
- package/src/__tests__/unit/estimateMaxSpendable.unit.test.ts +19 -0
- package/src/__tests__/unit/getTransactionStatus.unit.test.ts +119 -0
- package/src/__tests__/unit/hw-getAddress.unit.test.ts +58 -0
- package/src/__tests__/unit/prepareTransaction.unit.test.ts +52 -0
- package/src/__tests__/unit/signOperation.unit.test.ts +90 -0
- package/src/__tests__/unit/txn.unit.test.ts +121 -0
- package/src/__tests__/unit/utils.unit.test.ts +126 -0
- package/src/bridge/bridgeHelpers/api.ts +104 -0
- package/src/bridge/bridgeHelpers/api.types.ts +178 -0
- package/src/bridge/bridgeHelpers/txn.ts +166 -0
- package/src/bridge/js.ts +65 -0
- package/src/broadcast.ts +13 -0
- package/src/cli-transaction.ts +30 -0
- package/src/config.ts +21 -0
- package/src/createTransaction.ts +17 -0
- package/src/deviceTransactionConfig.ts +48 -0
- package/src/errors.ts +6 -0
- package/src/estimateMaxSpendable.ts +40 -0
- package/src/getTransactionStatus.ts +130 -0
- package/src/hw-getAddress.ts +28 -0
- package/src/hw-signMessage.ts +31 -0
- package/src/prepareTransaction.ts +27 -0
- package/src/signOperation.ts +115 -0
- package/src/signer.ts +32 -0
- package/src/specs.ts +122 -0
- package/src/speculos-deviceActions.ts +38 -0
- package/src/synchronisation.ts +91 -0
- package/src/transaction.ts +61 -0
- package/src/types.ts +82 -0
- package/src/utils.ts +120 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
2
|
+
import type { Account, AccountBridge, DeviceId, SignOperationEvent } from "@ledgerhq/types-live";
|
|
3
|
+
import { Address, beginCell, external, storeMessage } from "@ton/core";
|
|
4
|
+
import { WalletContractV4 } from "@ton/ton";
|
|
5
|
+
import { Observable } from "rxjs";
|
|
6
|
+
import { fetchAccountInfo } from "./bridge/bridgeHelpers/api";
|
|
7
|
+
import type { TonSigner } from "./signer";
|
|
8
|
+
import type { TonCell, TonOperation, Transaction } from "./types";
|
|
9
|
+
import { buildTonTransaction, getLedgerTonPath } from "./utils";
|
|
10
|
+
|
|
11
|
+
const packTransaction = (account: Account, needsInit: boolean, signature: TonCell): string => {
|
|
12
|
+
const { address } = Address.parseFriendly(account.freshAddress);
|
|
13
|
+
let init: { code: TonCell; data: TonCell } | null = null;
|
|
14
|
+
if (needsInit) {
|
|
15
|
+
if (account.xpub?.length !== 64) throw Error("[ton] xpub can't be found");
|
|
16
|
+
const wallet = WalletContractV4.create({
|
|
17
|
+
workchain: 0,
|
|
18
|
+
publicKey: Buffer.from(account.xpub, "hex"),
|
|
19
|
+
});
|
|
20
|
+
init = wallet.init;
|
|
21
|
+
}
|
|
22
|
+
const ext = external({ to: address, init, body: signature });
|
|
23
|
+
return beginCell().store(storeMessage(ext)).endCell().toBoc().toString("base64");
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sign Transaction with Ledger hardware
|
|
28
|
+
*/
|
|
29
|
+
export const buildSignOperation =
|
|
30
|
+
(signerContext: SignerContext<TonSigner>): AccountBridge<Transaction>["signOperation"] =>
|
|
31
|
+
({
|
|
32
|
+
account,
|
|
33
|
+
transaction,
|
|
34
|
+
deviceId,
|
|
35
|
+
}: {
|
|
36
|
+
account: Account;
|
|
37
|
+
transaction: Transaction;
|
|
38
|
+
deviceId: DeviceId;
|
|
39
|
+
}): Observable<SignOperationEvent> =>
|
|
40
|
+
new Observable(o => {
|
|
41
|
+
let cancelled = false;
|
|
42
|
+
async function main() {
|
|
43
|
+
const address = account.freshAddress;
|
|
44
|
+
const accountInfo = await fetchAccountInfo(address);
|
|
45
|
+
|
|
46
|
+
const tonTx = buildTonTransaction(transaction, accountInfo.seqno);
|
|
47
|
+
|
|
48
|
+
const ledgerPath = getLedgerTonPath(account.freshAddressPath);
|
|
49
|
+
|
|
50
|
+
o.next({ type: "device-signature-requested" });
|
|
51
|
+
const sig = await signerContext(deviceId, signer =>
|
|
52
|
+
signer.signTransaction(ledgerPath, tonTx),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (cancelled) return;
|
|
56
|
+
|
|
57
|
+
o.next({ type: "device-signature-granted" });
|
|
58
|
+
|
|
59
|
+
if (!sig) {
|
|
60
|
+
throw new Error("No signature");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const signature = packTransaction(account, accountInfo.status === "uninit", sig);
|
|
64
|
+
|
|
65
|
+
const operation = buildOptimisticOperation(account, transaction);
|
|
66
|
+
|
|
67
|
+
o.next({
|
|
68
|
+
type: "signed",
|
|
69
|
+
signedOperation: {
|
|
70
|
+
operation,
|
|
71
|
+
signature,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
main().then(
|
|
77
|
+
() => o.complete(),
|
|
78
|
+
e => o.error(e),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return () => {
|
|
82
|
+
cancelled = true;
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const buildOptimisticOperation = (
|
|
87
|
+
account: Account,
|
|
88
|
+
transaction: Transaction,
|
|
89
|
+
): TonOperation => {
|
|
90
|
+
const { recipient, amount, fees, comment } = transaction;
|
|
91
|
+
const { id: accountId } = account;
|
|
92
|
+
|
|
93
|
+
const op: TonOperation = {
|
|
94
|
+
id: "",
|
|
95
|
+
hash: "",
|
|
96
|
+
type: "OUT",
|
|
97
|
+
senders: [account.freshAddress],
|
|
98
|
+
recipients: [recipient],
|
|
99
|
+
accountId,
|
|
100
|
+
value: amount.plus(fees),
|
|
101
|
+
fee: fees,
|
|
102
|
+
blockHash: null,
|
|
103
|
+
blockHeight: null,
|
|
104
|
+
date: new Date(),
|
|
105
|
+
extra: {
|
|
106
|
+
// we don't know yet, will be patched in final operation
|
|
107
|
+
lt: "",
|
|
108
|
+
explorerHash: "",
|
|
109
|
+
comment: comment,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
return op;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default buildSignOperation;
|
package/src/signer.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { TonCell, TonTransaction } from "./types";
|
|
2
|
+
|
|
3
|
+
export type TonAddress = {
|
|
4
|
+
publicKey: Buffer;
|
|
5
|
+
address: string;
|
|
6
|
+
};
|
|
7
|
+
export type TonSignature = TonCell | undefined;
|
|
8
|
+
export interface TonSigner {
|
|
9
|
+
getAddress(
|
|
10
|
+
path: number[],
|
|
11
|
+
opts?: {
|
|
12
|
+
testOnly?: boolean;
|
|
13
|
+
bounceable?: boolean;
|
|
14
|
+
chain?: number;
|
|
15
|
+
},
|
|
16
|
+
): Promise<{
|
|
17
|
+
address: string;
|
|
18
|
+
publicKey: Buffer;
|
|
19
|
+
}>;
|
|
20
|
+
validateAddress(
|
|
21
|
+
path: number[],
|
|
22
|
+
opts?: {
|
|
23
|
+
testOnly?: boolean;
|
|
24
|
+
bounceable?: boolean;
|
|
25
|
+
chain?: number;
|
|
26
|
+
},
|
|
27
|
+
): Promise<{
|
|
28
|
+
address: string;
|
|
29
|
+
publicKey: Buffer;
|
|
30
|
+
}>;
|
|
31
|
+
signTransaction: (path: number[], transaction: TonTransaction) => Promise<TonCell>;
|
|
32
|
+
}
|
package/src/specs.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { botTest, pickSiblings } from "@ledgerhq/coin-framework/bot/specs";
|
|
2
|
+
import type { AppSpec, TransactionDestinationTestInput } from "@ledgerhq/coin-framework/bot/types";
|
|
3
|
+
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
4
|
+
import { DeviceModelId } from "@ledgerhq/devices";
|
|
5
|
+
import BigNumber from "bignumber.js";
|
|
6
|
+
import expect from "expect";
|
|
7
|
+
import invariant from "invariant";
|
|
8
|
+
import { acceptTransaction } from "./speculos-deviceActions";
|
|
9
|
+
import { Transaction } from "./types";
|
|
10
|
+
|
|
11
|
+
const MIN_SAFE = new BigNumber(1.5e7); // approx two txs' fees (0.015 TON)
|
|
12
|
+
|
|
13
|
+
export const testDestination = <T>({
|
|
14
|
+
destination,
|
|
15
|
+
destinationBeforeTransaction,
|
|
16
|
+
sendingOperation,
|
|
17
|
+
}: TransactionDestinationTestInput<T>): void => {
|
|
18
|
+
const amount = sendingOperation.value.minus(sendingOperation.fee);
|
|
19
|
+
const inOp = destination.operations.find(
|
|
20
|
+
op => op.hash === sendingOperation.hash && op.type === "IN",
|
|
21
|
+
);
|
|
22
|
+
const inFees = inOp?.fee ?? BigNumber(0);
|
|
23
|
+
botTest("account balance increased with transaction amount", () =>
|
|
24
|
+
expect(destination.balance.toString()).toBe(
|
|
25
|
+
destinationBeforeTransaction.balance.plus(amount).minus(inFees).toString(),
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
botTest("operation amount is consistent with sendingOperation", () =>
|
|
29
|
+
expect({
|
|
30
|
+
type: inOp?.type,
|
|
31
|
+
amount: inOp?.value?.toString(),
|
|
32
|
+
}).toMatchObject({
|
|
33
|
+
type: "IN",
|
|
34
|
+
amount: amount.toString(),
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const tonSpecs: AppSpec<Transaction> = {
|
|
40
|
+
name: "TON",
|
|
41
|
+
currency: getCryptoCurrencyById("ton"),
|
|
42
|
+
appQuery: {
|
|
43
|
+
model: DeviceModelId.nanoS,
|
|
44
|
+
appName: "TON",
|
|
45
|
+
},
|
|
46
|
+
genericDeviceAction: acceptTransaction,
|
|
47
|
+
testTimeout: 6 * 60 * 1000,
|
|
48
|
+
minViableAmount: MIN_SAFE,
|
|
49
|
+
transactionCheck: ({ maxSpendable }) => {
|
|
50
|
+
invariant(maxSpendable.gt(MIN_SAFE), "balance is too low");
|
|
51
|
+
},
|
|
52
|
+
mutations: [
|
|
53
|
+
{
|
|
54
|
+
name: "Send ~50%",
|
|
55
|
+
maxRun: 1,
|
|
56
|
+
testDestination,
|
|
57
|
+
transaction: ({ account, siblings, bridge, maxSpendable }) => {
|
|
58
|
+
invariant(maxSpendable.gt(MIN_SAFE), "balance is too low");
|
|
59
|
+
|
|
60
|
+
const updates: Array<Partial<Transaction>> = [
|
|
61
|
+
{ recipient: pickSiblings(siblings).freshAddress },
|
|
62
|
+
{ amount: maxSpendable.div(2).integerValue() },
|
|
63
|
+
];
|
|
64
|
+
if (Math.random() < 0.5) updates.push({ comment: { isEncrypted: false, text: "LL Bot" } });
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
transaction: bridge.createTransaction(account),
|
|
68
|
+
updates,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
test: ({ accountBeforeTransaction, operation, account, transaction }) => {
|
|
73
|
+
// we don't know the exact amount in fees, so we accept +- 20% of expected fees
|
|
74
|
+
const baseAmount = accountBeforeTransaction.balance.minus(transaction.amount);
|
|
75
|
+
const maxBalance = baseAmount.minus(transaction.fees.multipliedBy(0.8).integerValue());
|
|
76
|
+
const minBalance = baseAmount.minus(transaction.fees.multipliedBy(1.2).integerValue());
|
|
77
|
+
botTest("account spendable balance decreased with operation", () => {
|
|
78
|
+
expect(account.spendableBalance.lte(maxBalance)).toBe(true);
|
|
79
|
+
expect(account.spendableBalance.gte(minBalance)).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
botTest("operation comment", () =>
|
|
83
|
+
expect(operation.extra).toMatchObject({
|
|
84
|
+
comment: transaction.comment,
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "Transfer Max",
|
|
91
|
+
maxRun: 1,
|
|
92
|
+
transaction: ({ account, siblings, bridge }) => {
|
|
93
|
+
const updates: Array<Partial<Transaction>> = [
|
|
94
|
+
{ recipient: pickSiblings(siblings).freshAddress },
|
|
95
|
+
{ useAllAmount: true },
|
|
96
|
+
];
|
|
97
|
+
if (Math.random() < 0.5) updates.push({ comment: { isEncrypted: false, text: "LL Bot" } });
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
transaction: bridge.createTransaction(account),
|
|
101
|
+
updates,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
testDestination,
|
|
105
|
+
test: ({ account, transaction, operation }) => {
|
|
106
|
+
botTest("account spendable balance is zero", () =>
|
|
107
|
+
expect(account.spendableBalance.toFixed()).toBe("0"),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
botTest("operation comment", () =>
|
|
111
|
+
expect(operation.extra).toMatchObject({
|
|
112
|
+
comment: transaction.comment,
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export default {
|
|
121
|
+
tonSpecs,
|
|
122
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SpeculosButton,
|
|
3
|
+
deviceActionFlow,
|
|
4
|
+
formatDeviceAmount,
|
|
5
|
+
} from "@ledgerhq/coin-framework/bot/specs";
|
|
6
|
+
import type { DeviceAction, State } from "@ledgerhq/coin-framework/bot/types";
|
|
7
|
+
import type { Transaction } from "./types";
|
|
8
|
+
|
|
9
|
+
export const acceptTransaction: DeviceAction<Transaction, State<Transaction>> = deviceActionFlow({
|
|
10
|
+
steps: [
|
|
11
|
+
{
|
|
12
|
+
title: "Review",
|
|
13
|
+
button: SpeculosButton.RIGHT,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
title: "To",
|
|
17
|
+
button: SpeculosButton.RIGHT,
|
|
18
|
+
expectedValue: ({ transaction }) => transaction.recipient,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: "Amount",
|
|
22
|
+
button: SpeculosButton.RIGHT,
|
|
23
|
+
expectedValue: ({ account, transaction }) =>
|
|
24
|
+
transaction.useAllAmount
|
|
25
|
+
? "ALL YOUR TONs"
|
|
26
|
+
: formatDeviceAmount(account.currency, transaction.amount),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: "Comment",
|
|
30
|
+
button: SpeculosButton.RIGHT,
|
|
31
|
+
expectedValue: ({ transaction }) => transaction.comment.text,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: "Approve",
|
|
35
|
+
button: SpeculosButton.BOTH,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { decodeAccountId, encodeAccountId } from "@ledgerhq/coin-framework/account/index";
|
|
2
|
+
import { GetAccountShape, makeSync, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
|
|
3
|
+
import { log } from "@ledgerhq/logs";
|
|
4
|
+
import { Account } from "@ledgerhq/types-live";
|
|
5
|
+
import BigNumber from "bignumber.js";
|
|
6
|
+
import flatMap from "lodash/flatMap";
|
|
7
|
+
import { fetchAccountInfo, fetchLastBlockNumber } from "./bridge/bridgeHelpers/api";
|
|
8
|
+
import { TonTransactionsList } from "./bridge/bridgeHelpers/api.types";
|
|
9
|
+
import { getTransactions, mapTxToOps } from "./bridge/bridgeHelpers/txn";
|
|
10
|
+
import { TonOperation } from "./types";
|
|
11
|
+
|
|
12
|
+
export const getAccountShape: GetAccountShape<Account> = async info => {
|
|
13
|
+
const { address, rest, currency, derivationMode, initialAccount } = info;
|
|
14
|
+
|
|
15
|
+
const publicKey = reconciliatePubkey(rest?.publicKey, initialAccount);
|
|
16
|
+
|
|
17
|
+
const blockHeight = await fetchLastBlockNumber();
|
|
18
|
+
const accountId = encodeAccountId({
|
|
19
|
+
type: "js",
|
|
20
|
+
version: "2",
|
|
21
|
+
currencyId: currency.id,
|
|
22
|
+
xpubOrAddress: publicKey,
|
|
23
|
+
derivationMode,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
log("debug", `Generation account shape for ${address}`);
|
|
27
|
+
|
|
28
|
+
const newTxs: TonTransactionsList = { transactions: [], address_book: {} };
|
|
29
|
+
const oldOps = (initialAccount?.operations ?? []) as TonOperation[];
|
|
30
|
+
const { last_transaction_lt, balance } = await fetchAccountInfo(address);
|
|
31
|
+
// if last_transaction_lt is empty, then there are no transactions in account
|
|
32
|
+
if (last_transaction_lt != null) {
|
|
33
|
+
if (oldOps.length === 0) {
|
|
34
|
+
const [tmpTxs] = await Promise.all([getTransactions(address)]);
|
|
35
|
+
newTxs.transactions.push(...tmpTxs.transactions);
|
|
36
|
+
newTxs.address_book = { ...newTxs.address_book, ...tmpTxs.address_book };
|
|
37
|
+
} else {
|
|
38
|
+
// if they are the same, we have no new ops
|
|
39
|
+
if (oldOps[0].extra.lt !== last_transaction_lt) {
|
|
40
|
+
const [tmpTxs] = await Promise.all([getTransactions(address, oldOps[0].extra.lt)]);
|
|
41
|
+
newTxs.transactions.push(...tmpTxs.transactions);
|
|
42
|
+
newTxs.address_book = { ...newTxs.address_book, ...tmpTxs.address_book };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const newOps = flatMap(newTxs.transactions, mapTxToOps(accountId, address, newTxs.address_book));
|
|
48
|
+
const operations = mergeOps(oldOps, newOps);
|
|
49
|
+
|
|
50
|
+
const toReturn = {
|
|
51
|
+
id: accountId,
|
|
52
|
+
balance: new BigNumber(balance),
|
|
53
|
+
spendableBalance: new BigNumber(balance),
|
|
54
|
+
operations,
|
|
55
|
+
operationsCount: operations.length,
|
|
56
|
+
blockHeight,
|
|
57
|
+
xpub: publicKey,
|
|
58
|
+
lastSyncDate: new Date(),
|
|
59
|
+
} as Partial<Account>;
|
|
60
|
+
return toReturn;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const postSync = (initial: Account, synced: Account): Account => {
|
|
64
|
+
const initialPendingOperations = initial.pendingOperations || [];
|
|
65
|
+
const { operations } = synced;
|
|
66
|
+
const pendingOperations = initialPendingOperations.filter(
|
|
67
|
+
op => !operations.some(o => o.id === op.id),
|
|
68
|
+
);
|
|
69
|
+
// Set of hashes from the pending operations of the main account
|
|
70
|
+
const coinPendingOperationsHashes = new Set();
|
|
71
|
+
for (const op of pendingOperations) {
|
|
72
|
+
coinPendingOperationsHashes.add(op.hash);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...synced,
|
|
77
|
+
pendingOperations,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function reconciliatePubkey(publicKey?: string, initialAccount?: Account): string {
|
|
82
|
+
if (publicKey?.length === 64) return publicKey;
|
|
83
|
+
if (initialAccount) {
|
|
84
|
+
if (initialAccount.xpub?.length === 64) return initialAccount.xpub;
|
|
85
|
+
const { xpubOrAddress } = decodeAccountId(initialAccount.id);
|
|
86
|
+
if (xpubOrAddress.length === 64) return xpubOrAddress;
|
|
87
|
+
}
|
|
88
|
+
throw Error("[ton] pubkey was not properly restored");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const sync = makeSync({ getAccountShape, postSync });
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getAccountCurrency } from "@ledgerhq/coin-framework/account/index";
|
|
2
|
+
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
|
|
3
|
+
import { formatTransactionStatus } from "@ledgerhq/coin-framework/formatters";
|
|
4
|
+
import {
|
|
5
|
+
fromTransactionCommonRaw,
|
|
6
|
+
fromTransactionStatusRawCommon as fromTransactionStatusRaw,
|
|
7
|
+
toTransactionCommonRaw,
|
|
8
|
+
toTransactionStatusRawCommon as toTransactionStatusRaw,
|
|
9
|
+
} from "@ledgerhq/coin-framework/serialization";
|
|
10
|
+
import type { Account } from "@ledgerhq/types-live";
|
|
11
|
+
import BigNumber from "bignumber.js";
|
|
12
|
+
import type { Transaction, TransactionRaw } from "./types";
|
|
13
|
+
|
|
14
|
+
export const formatTransaction = (
|
|
15
|
+
{ recipient, useAllAmount, amount }: Transaction,
|
|
16
|
+
account: Account,
|
|
17
|
+
): string => `
|
|
18
|
+
SEND ${
|
|
19
|
+
useAllAmount
|
|
20
|
+
? "MAX"
|
|
21
|
+
: amount.isZero()
|
|
22
|
+
? ""
|
|
23
|
+
: " " +
|
|
24
|
+
formatCurrencyUnit(getAccountCurrency(account).units[0], amount, {
|
|
25
|
+
showCode: true,
|
|
26
|
+
disableRounding: true,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
TO ${recipient}`;
|
|
30
|
+
|
|
31
|
+
export const fromTransactionRaw = (tr: TransactionRaw): Transaction => {
|
|
32
|
+
const common = fromTransactionCommonRaw(tr);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...common,
|
|
36
|
+
family: tr.family,
|
|
37
|
+
fees: new BigNumber(tr.fees),
|
|
38
|
+
comment: tr.comment,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const toTransactionRaw = (transaction: Transaction): TransactionRaw => {
|
|
43
|
+
const common = toTransactionCommonRaw(transaction);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
...common,
|
|
47
|
+
family: transaction.family,
|
|
48
|
+
amount: transaction.amount.toFixed(),
|
|
49
|
+
fees: transaction.fees.toFixed(),
|
|
50
|
+
comment: transaction.comment,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default {
|
|
55
|
+
formatTransaction,
|
|
56
|
+
fromTransactionRaw,
|
|
57
|
+
toTransactionRaw,
|
|
58
|
+
fromTransactionStatusRaw,
|
|
59
|
+
toTransactionStatusRaw,
|
|
60
|
+
formatTransactionStatus,
|
|
61
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Operation,
|
|
3
|
+
TransactionCommon,
|
|
4
|
+
TransactionCommonRaw,
|
|
5
|
+
TransactionStatusCommon,
|
|
6
|
+
TransactionStatusCommonRaw,
|
|
7
|
+
} from "@ledgerhq/types-live";
|
|
8
|
+
import { Address, SendMode, StateInit } from "@ton/core";
|
|
9
|
+
import { Cell } from "@ton/ton";
|
|
10
|
+
import BigNumber from "bignumber.js";
|
|
11
|
+
|
|
12
|
+
type FamilyType = "ton";
|
|
13
|
+
|
|
14
|
+
// ledger app does not support encrypted comments yet
|
|
15
|
+
// leaving the arch for the future
|
|
16
|
+
export interface TonComment {
|
|
17
|
+
isEncrypted: boolean;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Transaction = TransactionCommon & {
|
|
22
|
+
family: FamilyType;
|
|
23
|
+
fees: BigNumber;
|
|
24
|
+
comment: TonComment;
|
|
25
|
+
};
|
|
26
|
+
export type TransactionRaw = TransactionCommonRaw & {
|
|
27
|
+
family: FamilyType;
|
|
28
|
+
fees: string;
|
|
29
|
+
comment: TonComment;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type TransactionStatus = TransactionStatusCommon;
|
|
33
|
+
export type TransactionStatusRaw = TransactionStatusCommonRaw;
|
|
34
|
+
|
|
35
|
+
export type TonOperation = Operation<TonOperationExtra>;
|
|
36
|
+
|
|
37
|
+
export type TonPayloadJettonTransfer = {
|
|
38
|
+
type: "jetton-transfer";
|
|
39
|
+
queryId: bigint | null;
|
|
40
|
+
amount: bigint;
|
|
41
|
+
destination: Address;
|
|
42
|
+
responseDestination: Address;
|
|
43
|
+
customPayload: TonCell | null;
|
|
44
|
+
forwardAmount: bigint;
|
|
45
|
+
forwardPayload: TonCell | null;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type TonPayloadNftTransfer = {
|
|
49
|
+
type: "nft-transfer";
|
|
50
|
+
queryId: bigint | null;
|
|
51
|
+
newOwner: Address;
|
|
52
|
+
responseDestination: Address;
|
|
53
|
+
customPayload: TonCell | null;
|
|
54
|
+
forwardAmount: bigint;
|
|
55
|
+
forwardPayload: TonCell | null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type TonPayloadComment = {
|
|
59
|
+
type: "comment";
|
|
60
|
+
text: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type TonPayloadFormat = TonPayloadComment | TonPayloadJettonTransfer | TonPayloadNftTransfer;
|
|
64
|
+
|
|
65
|
+
export interface TonTransaction {
|
|
66
|
+
to: Address;
|
|
67
|
+
sendMode: SendMode;
|
|
68
|
+
seqno: number;
|
|
69
|
+
timeout: number;
|
|
70
|
+
bounce: boolean;
|
|
71
|
+
amount: bigint;
|
|
72
|
+
stateInit?: StateInit;
|
|
73
|
+
payload?: TonPayloadFormat;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TonCell extends Cell {}
|
|
77
|
+
|
|
78
|
+
export type TonOperationExtra = {
|
|
79
|
+
comment: TonComment;
|
|
80
|
+
lt: string;
|
|
81
|
+
explorerHash: string;
|
|
82
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { decodeAccountId } from "@ledgerhq/coin-framework/account/index";
|
|
2
|
+
import { Account } from "@ledgerhq/types-live";
|
|
3
|
+
import { SendMode, Address as TonAddress, WalletContractV4, comment, internal } from "@ton/ton";
|
|
4
|
+
import BigNumber from "bignumber.js";
|
|
5
|
+
import { estimateFee } from "./bridge/bridgeHelpers/api";
|
|
6
|
+
import { TonComment, TonTransaction, Transaction } from "./types";
|
|
7
|
+
|
|
8
|
+
export const isAddressValid = (recipient: string) => {
|
|
9
|
+
try {
|
|
10
|
+
return Boolean(
|
|
11
|
+
(TonAddress.isRaw(recipient) || TonAddress.isFriendly(recipient)) &&
|
|
12
|
+
TonAddress.parse(recipient),
|
|
13
|
+
);
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const addressesAreEqual = (addr1: string, addr2: string) => {
|
|
20
|
+
try {
|
|
21
|
+
return (
|
|
22
|
+
isAddressValid(addr1) &&
|
|
23
|
+
isAddressValid(addr2) &&
|
|
24
|
+
TonAddress.parse(addr1).equals(TonAddress.parse(addr2))
|
|
25
|
+
);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const buildTonTransaction = (transaction: Transaction, seqno: number): TonTransaction => {
|
|
32
|
+
const { useAllAmount, amount, comment, recipient } = transaction;
|
|
33
|
+
let recipientParsed = recipient;
|
|
34
|
+
// if recipient is not valid calculate fees with empty address
|
|
35
|
+
// we handle invalid addresses in account bridge
|
|
36
|
+
try {
|
|
37
|
+
TonAddress.parse(recipientParsed);
|
|
38
|
+
} catch {
|
|
39
|
+
recipientParsed = new TonAddress(0, Buffer.alloc(32)).toRawString();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const finalAmount = useAllAmount ? BigInt(0) : BigInt(amount.toFixed());
|
|
43
|
+
|
|
44
|
+
const tonTransaction: TonTransaction = {
|
|
45
|
+
to: TonAddress.parse(recipientParsed),
|
|
46
|
+
seqno,
|
|
47
|
+
amount: finalAmount,
|
|
48
|
+
bounce: TonAddress.isFriendly(recipientParsed)
|
|
49
|
+
? TonAddress.parseFriendly(recipientParsed).isBounceable
|
|
50
|
+
: true,
|
|
51
|
+
timeout: getTransferExpirationTime(),
|
|
52
|
+
sendMode: useAllAmount
|
|
53
|
+
? SendMode.CARRY_ALL_REMAINING_BALANCE
|
|
54
|
+
: SendMode.IGNORE_ERRORS + SendMode.PAY_GAS_SEPARATELY,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (comment.text.length) {
|
|
58
|
+
tonTransaction.payload = { type: "comment", text: comment.text };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return tonTransaction;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// max length is 120 and only ascii allowed
|
|
65
|
+
export const commentIsValid = (msg: TonComment) =>
|
|
66
|
+
!msg.isEncrypted && msg.text.length <= 120 && /^[\x20-\x7F]*$/.test(msg.text);
|
|
67
|
+
|
|
68
|
+
// 1 minute
|
|
69
|
+
export const getTransferExpirationTime = () => Math.floor(Date.now() / 1000 + 60);
|
|
70
|
+
|
|
71
|
+
export const getTonEstimatedFees = async (
|
|
72
|
+
account: Account,
|
|
73
|
+
needsInit: boolean,
|
|
74
|
+
tx: TonTransaction,
|
|
75
|
+
) => {
|
|
76
|
+
const { xpubOrAddress: pubKey } = decodeAccountId(account.id);
|
|
77
|
+
if (pubKey.length !== 64) throw Error("[ton] pubKey can't be found");
|
|
78
|
+
if (tx.payload && tx.payload?.type !== "comment") {
|
|
79
|
+
throw Error("[ton] payload kind not expected");
|
|
80
|
+
}
|
|
81
|
+
const contract = WalletContractV4.create({ workchain: 0, publicKey: Buffer.from(pubKey, "hex") });
|
|
82
|
+
const transfer = contract.createTransfer({
|
|
83
|
+
seqno: tx.seqno,
|
|
84
|
+
secretKey: Buffer.alloc(64), // secretKey set to 0, signature is not verified
|
|
85
|
+
messages: [
|
|
86
|
+
internal({
|
|
87
|
+
bounce: tx.bounce,
|
|
88
|
+
to: tx.to,
|
|
89
|
+
value: tx.amount,
|
|
90
|
+
body: tx.payload?.text ? comment(tx.payload.text) : undefined,
|
|
91
|
+
}),
|
|
92
|
+
],
|
|
93
|
+
sendMode: tx.sendMode,
|
|
94
|
+
});
|
|
95
|
+
const initCode = needsInit ? contract.init.code.toBoc().toString("base64") : undefined;
|
|
96
|
+
const initData = needsInit ? contract.init.data.toBoc().toString("base64") : undefined;
|
|
97
|
+
const fee = await estimateFee(
|
|
98
|
+
account.freshAddress,
|
|
99
|
+
transfer.toBoc().toString("base64"),
|
|
100
|
+
initCode,
|
|
101
|
+
initData,
|
|
102
|
+
);
|
|
103
|
+
return BigNumber(fee.fwd_fee + fee.gas_fee + fee.in_fwd_fee + fee.storage_fee);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const getLedgerTonPath = (path: string): number[] => {
|
|
107
|
+
const numPath: number[] = [];
|
|
108
|
+
if (!path) throw Error("[ton] Path is empty");
|
|
109
|
+
if (path.startsWith("m/")) path = path.slice(2);
|
|
110
|
+
const pathEntries = path.split("/");
|
|
111
|
+
if (pathEntries.length !== 6) throw Error(`[ton] Path length is not right ${path}`);
|
|
112
|
+
for (const entry of pathEntries) {
|
|
113
|
+
if (!entry.endsWith("'")) throw Error(`[ton] Path entry is not hardened ${path}`);
|
|
114
|
+
const num = parseInt(entry.slice(0, entry.length - 1));
|
|
115
|
+
if (!Number.isInteger(num) || num < 0 || num >= 0x80000000)
|
|
116
|
+
throw Error(`[ton] Path entry is not right ${path}`);
|
|
117
|
+
numPath.push(num);
|
|
118
|
+
}
|
|
119
|
+
return numPath;
|
|
120
|
+
};
|
package/tsconfig.json
ADDED