UNPKG

@ledgerhq/coin-ton

Version:
110 lines 4.06 kB
import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/index"; import { Address, beginCell, external, storeMessage, toNano } from "@ton/core"; import { WalletContractV4 } from "@ton/ton"; import BigNumber from "bignumber.js"; import { Observable } from "rxjs"; import { fetchAccountInfo } from "./bridge/bridgeHelpers/api"; import { TOKEN_TRANSFER_MAX_FEE } from "./constants"; import { buildTonTransaction, getLedgerTonPath } from "./utils"; const packTransaction = (account, needsInit, signature) => { const { address } = Address.parseFriendly(account.freshAddress); let init = null; if (needsInit) { if (account.xpub?.length !== 64) throw Error("[ton] xpub can't be found"); const wallet = WalletContractV4.create({ workchain: 0, publicKey: Buffer.from(account.xpub, "hex"), }); init = wallet.init; } const ext = external({ to: address, init, body: signature }); return beginCell().store(storeMessage(ext)).endCell().toBoc().toString("base64"); }; /** * Sign Transaction with Ledger hardware */ export const buildSignOperation = (signerContext) => ({ account, transaction, deviceId, }) => new Observable(o => { let cancelled = false; async function main() { const address = account.freshAddress; const accountInfo = await fetchAccountInfo(address); const tonTx = buildTonTransaction(transaction, accountInfo.seqno, account); const ledgerPath = getLedgerTonPath(account.freshAddressPath); o.next({ type: "device-signature-requested" }); const sig = await signerContext(deviceId, signer => signer.signTransaction(ledgerPath, tonTx)); if (cancelled) return; o.next({ type: "device-signature-granted" }); if (!sig) { throw new Error("No signature"); } const signature = packTransaction(account, accountInfo.status === "uninit", sig); const operation = buildOptimisticOperation(account, transaction); o.next({ type: "signed", signedOperation: { operation, signature, }, }); } main().then(() => o.complete(), e => o.error(e)); return () => { cancelled = true; }; }); export const buildOptimisticOperation = (account, transaction) => { const { recipient, amount, fees, comment, useAllAmount, subAccountId } = transaction; const { id: accountId } = account; const subAccount = findSubAccountById(account, subAccountId ?? ""); const tokenTransfer = Boolean(subAccount && isTokenAccount(subAccount)); const value = tokenTransfer ? BigNumber(toNano(TOKEN_TRANSFER_MAX_FEE).toString()) : amount.plus(fees); const op = { id: "", hash: "", type: "OUT", senders: [account.freshAddress], recipients: [recipient], accountId, value, fee: fees, blockHash: null, blockHeight: null, date: new Date(), extra: { // we don't know yet, will be patched in final operation lt: "", explorerHash: "", comment, }, }; if (tokenTransfer && subAccount) { op.subOperations = [ { id: "", hash: "", type: "OUT", value: useAllAmount ? subAccount.balance : amount, fee: fees, blockHash: null, blockHeight: null, senders: [account.freshAddress], recipients: [recipient], accountId: subAccount.id, date: new Date(), extra: { lt: "", explorerHash: "", comment, }, contract: subAccount.token.contractAddress, }, ]; } return op; }; export default buildSignOperation; //# sourceMappingURL=signOperation.js.map