UNPKG

@ledgerhq/coin-stacks

Version:
189 lines 9.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTxToBroadcast = exports.applySignatureToTransaction = exports.createTransaction = exports.createStxTransferTransaction = exports.createTokenTransferTransaction = exports.createTokenTransferPostConditions = exports.createTokenTransferFunctionArgs = exports.getTokenContractDetails = void 0; const transactions_1 = require("@stacks/transactions"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const api_1 = require("../../network/api"); const memoUtils_1 = require("./memoUtils"); const specialConditionCode = new Set([ "SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.auto-alex-v3::auto-alex-v3", "SM26NBC8SFHNW4P1Y4DFH27974P56WN86C92HPEHH.token-lqstx::lqstx", "SP673Z4BPB4R73359K9HE55F2X91V5BJTN5SXZ5T.token-liabtc::liabtc", ]); // Type guard: validates rawData has all required token transfer fields function isTokenTransferRawData(data) { return (typeof data.contractAddress === "string" && typeof data.contractName === "string" && typeof data.assetName === "string" && typeof data.anchorMode === "number" && typeof data.network === "string" && typeof data.xpub === "string"); } // Extracts and validates base fields from rawData (throws if invalid) function extractBaseRawData(data) { const anchorMode = data.anchorMode; const network = data.network; const xpub = data.xpub; if (typeof anchorMode !== "number" || typeof network !== "string" || typeof xpub !== "string") { throw new Error("Invalid raw data: missing or invalid base fields"); } return { anchorMode, network, xpub }; } // Extracts contract address, name, and asset name from TokenAccount // CARE: usage of asset name is case-sensitive // Returns null if subAccount is undefined const getTokenContractDetails = (subAccount) => { if (!subAccount) return null; // Contract address format: "ADDRESS.CONTRACT-NAME::ASSET-NAME" const tokenId = subAccount.token.contractAddress; const contractAddress = tokenId.split(".").shift()?.toUpperCase() ?? ""; const contractName = tokenId.split(".").pop()?.split("::")[0] ?? ""; const assetName = tokenId.split(".").pop()?.split("::")[1] ?? ""; return { contractAddress, contractName, assetName }; }; exports.getTokenContractDetails = getTokenContractDetails; // Creates SIP-010 function arguments: [amount, sender, recipient, memo] const createTokenTransferFunctionArgs = (amount, senderAddress, recipientAddress, memo) => { return [ (0, transactions_1.uintCV)(amount.toFixed()), // Amount (0, transactions_1.standardPrincipalCV)(senderAddress), // Sender (0, transactions_1.standardPrincipalCV)(recipientAddress), // Recipient (0, memoUtils_1.memoToBufferCV)(memo), // Memo (optional) ]; }; exports.createTokenTransferFunctionArgs = createTokenTransferFunctionArgs; // Creates post conditions for token transfer // Uses LessEqual for special tokens (auto-alex-v3, lqstx, liabtc), Equal for others const createTokenTransferPostConditions = (senderAddress, amount, contractAddress, contractName, assetName) => { let conditionCode = transactions_1.FungibleConditionCode.Equal; if (specialConditionCode.has(`${contractAddress}.${contractName}::${assetName}`)) { conditionCode = transactions_1.FungibleConditionCode.LessEqual; } return [ { type: transactions_1.StacksMessageType.PostCondition, conditionType: transactions_1.PostConditionType.Fungible, principal: (0, transactions_1.createStandardPrincipal)(senderAddress), conditionCode, amount: BigInt(amount.toFixed()), assetInfo: (0, transactions_1.createAssetInfo)(contractAddress, contractName, assetName), }, ]; }; exports.createTokenTransferPostConditions = createTokenTransferPostConditions; // Creates unsigned SIP-010 token transfer transaction const createTokenTransferTransaction = async (options) => { const { contractAddress, contractName, assetName, amount, senderAddress, recipientAddress, anchorMode, network, publicKey, fee, nonce, memo, } = options; const functionArgs = (0, exports.createTokenTransferFunctionArgs)(amount, senderAddress, recipientAddress, memo); const postConditions = (0, exports.createTokenTransferPostConditions)(senderAddress, amount, contractAddress, contractName, assetName); const contractCallOptions = { contractAddress, contractName, functionName: "transfer", functionArgs, anchorMode, network: api_1.StacksNetwork[network], publicKey, postConditions, }; if (fee) { contractCallOptions.fee = fee.toFixed(); } if (nonce) { contractCallOptions.nonce = nonce.toFixed(); } return await (0, transactions_1.makeUnsignedContractCall)(contractCallOptions); }; exports.createTokenTransferTransaction = createTokenTransferTransaction; // Creates unsigned STX transfer transaction const createStxTransferTransaction = async (amount, recipientAddress, anchorMode, network, publicKey, fee, nonce, memo) => { const options = { amount: amount.toFixed(), recipient: recipientAddress, anchorMode, network: api_1.StacksNetwork[network], memo, publicKey, }; if (fee) { options.fee = fee.toFixed(); } if (nonce) { options.nonce = nonce.toFixed(); } return (0, transactions_1.makeUnsignedSTXTokenTransfer)(options); }; exports.createStxTransferTransaction = createStxTransferTransaction; // Creates either token or STX transfer transaction based on subAccount presence const createTransaction = async (transaction, senderAddress, publicKey, subAccount, fee, nonce) => { const { recipient, anchorMode, network, memo, amount } = transaction; const tokenDetails = (0, exports.getTokenContractDetails)(subAccount); if (tokenDetails) { // Token transfer transaction const { contractAddress, contractName, assetName } = tokenDetails; return (0, exports.createTokenTransferTransaction)({ contractAddress, contractName, assetName, amount, senderAddress, recipientAddress: recipient, anchorMode, network, publicKey, fee, nonce, memo, }); } else { // Regular STX transfer return (0, exports.createStxTransferTransaction)(amount, recipient, anchorMode, network, publicKey, fee, nonce, memo); } }; exports.createTransaction = createTransaction; // Applies signature to transaction and returns serialized buffer const applySignatureToTransaction = (tx, signature) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - TS doesn't recognize spendingCondition.signature property tx.auth.spendingCondition.signature = (0, transactions_1.createMessageSignature)(signature); return Buffer.from(tx.serialize()); }; exports.applySignatureToTransaction = applySignatureToTransaction; // Recreates transaction from operation and applies signature for broadcast // Used by the broadcast function - rawData type comes from internal ledger-live types const getTxToBroadcast = async (operation, signature, rawData) => { const { value, recipients, senders, fee, extra: { memo }, } = operation; if (isTokenTransferRawData(rawData)) { // TypeScript now knows rawData has all token transfer fields const { anchorMode, network, xpub, contractAddress, contractName, assetName } = rawData; // Create the function arguments for the SIP-010 transfer function const functionArgs = (0, exports.createTokenTransferFunctionArgs)(new bignumber_js_1.default(value), senders[0], recipients[0], memo !== undefined && memo !== null ? String(memo) : undefined); const postConditions = (0, exports.createTokenTransferPostConditions)(senders[0], new bignumber_js_1.default(value), contractAddress, contractName, assetName); const tx = await (0, transactions_1.makeUnsignedContractCall)({ contractAddress, contractName, functionName: "transfer", functionArgs, anchorMode, network: api_1.StacksNetwork[network], publicKey: xpub, fee: fee.toFixed(), nonce: operation.transactionSequenceNumber?.toString() ?? "0", postConditions, }); return (0, exports.applySignatureToTransaction)(tx, signature); } else { // STX transfer - extract base fields const { anchorMode, network, xpub } = extractBaseRawData(rawData); const tx = await (0, exports.createStxTransferTransaction)(new bignumber_js_1.default(value).minus(fee), recipients[0], anchorMode, network, xpub, new bignumber_js_1.default(fee), new bignumber_js_1.default(operation.transactionSequenceNumber ?? 0), memo !== undefined && memo !== null ? String(memo) : undefined); return (0, exports.applySignatureToTransaction)(tx, signature); } }; exports.getTxToBroadcast = getTxToBroadcast; //# sourceMappingURL=transactions.js.map