From 50bc660074eeac49a6861ca5f01169667278a02a Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 28 Nov 2025 15:24:29 +0800 Subject: [PATCH 01/15] feat: parse tx body --- package.json | 2 +- src/utils/onekey/dapp.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index efb29cf..a588bb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.5", + "version": "1.1.6", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index 9d2517f..c9ab413 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -99,8 +99,14 @@ const convertCborTxToEncodeTx = async ( addresses: string[], changeAddress: IChangeAddress, ): Promise => { - const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); - const body = tx.body(); + let body: CardanoWasm.TransactionBody; + + try { + const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); + body = tx.body(); + } catch { + body = CardanoWasm.TransactionBody.from_bytes(Buffer.from(txHex, 'hex')); + } // Fee const fee = body.fee().to_str(); From 952f690a003a831f2a8ac82c8eaaf63dffa344f7 Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 28 Nov 2025 17:13:05 +0800 Subject: [PATCH 02/15] feat: update Cardano transaction handling with staking support --- package.json | 2 +- src/utils/onekey/dapp.ts | 97 +++++++++++++++++++++++++++++++++++++-- src/utils/onekey/types.ts | 22 +++++++++ 3 files changed, 115 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a588bb3..474ad8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.6", + "version": "1.1.7", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index c9ab413..b90c483 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -11,6 +11,9 @@ import { IEncodedTxADA, IEncodeInput, IEncodeOutput, + IStakingInfo, + ICardanoCertificate, + CardanoCertificateType, } from './types'; const getBalance = async (balances: IAdaAmount[]) => { @@ -101,6 +104,7 @@ const convertCborTxToEncodeTx = async ( ): Promise => { let body: CardanoWasm.TransactionBody; + console.log('CARDANO LOCAL_VERSION : 1'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); @@ -127,9 +131,79 @@ const convertCborTxToEncodeTx = async ( const utxo = utxos.find( utxo => utxo.tx_hash === txHash && +utxo.tx_index === +index, ); - encodeInputs.push(utxo as unknown as IEncodeInput); + // Only push matched UTXOs, skip external inputs (e.g., from DeFi protocols) + if (utxo) { + encodeInputs.push(utxo as unknown as IEncodeInput); + } + } + + // Parse certificates (for staking transactions) + const certificates: ICardanoCertificate[] = []; + let poolId: string | undefined; + const certs = body.certs(); + if (certs) { + const certsLen = certs.len(); + for (let i = 0; i < certsLen; i++) { + const cert = certs.get(i); + const certKind = cert.kind(); + + // Stake Registration (kind = 0) + if (certKind === CardanoWasm.CertificateKind.StakeRegistration) { + const stakeReg = cert.as_stake_registration(); + if (stakeReg) { + const stakeCredential = stakeReg.stake_credential(); + const keyHash = stakeCredential.to_keyhash(); + certificates.push({ + type: CardanoCertificateType.STAKE_REGISTRATION, + stakeCredential: keyHash + ? Buffer.from(keyHash.to_bytes() as any, 'hex').toString('hex') + : undefined, + }); + } + } + + // Stake Deregistration (kind = 1) + if (certKind === CardanoWasm.CertificateKind.StakeDeregistration) { + const stakeDeReg = cert.as_stake_deregistration(); + if (stakeDeReg) { + const stakeCredential = stakeDeReg.stake_credential(); + const keyHash = stakeCredential.to_keyhash(); + certificates.push({ + type: CardanoCertificateType.STAKE_DEREGISTRATION, + stakeCredential: keyHash + ? Buffer.from(keyHash.to_bytes() as any, 'hex').toString('hex') + : undefined, + }); + } + } + + // Stake Delegation (kind = 2) + if (certKind === CardanoWasm.CertificateKind.StakeDelegation) { + const stakeDelegation = cert.as_stake_delegation(); + if (stakeDelegation) { + const stakeCredential = stakeDelegation.stake_credential(); + const keyHash = stakeCredential.to_keyhash(); + const poolKeyHash = stakeDelegation.pool_keyhash(); + poolId = Buffer.from(poolKeyHash.to_bytes() as any, 'hex').toString( + 'hex', + ); + certificates.push({ + type: CardanoCertificateType.STAKE_DELEGATION, + stakeCredential: keyHash + ? Buffer.from(keyHash.to_bytes() as any, 'hex').toString('hex') + : undefined, + poolKeyHash: poolId, + }); + } + } + } } + const isStakingTx = certificates.length > 0; + const stakingInfo: IStakingInfo | undefined = isStakingTx + ? { isStakingTx: true, certificates, poolId } + : undefined; + // outputs txs const outputs: IEncodeOutput[] = []; const outputsLen = body.outputs().len(); @@ -182,13 +256,25 @@ const convertCborTxToEncodeTx = async ( }); } - const totalSpent = BigNumber.sum(...outputs.map(o => o.amount)).toFixed(); + const totalSpent = + outputs.length > 0 + ? BigNumber.sum(...outputs.map(o => o.amount)).toFixed() + : '0'; const token = outputs .filter(o => !addresses.includes(o.address)) .find(o => o.assets.length > 0)?.assets?.[0].unit; - const encodedTx = { + // Determine 'from' address: prefer matched input, fallback to changeAddress + const fromAddress = + encodeInputs[0]?.address || changeAddress.address || ''; + + // For staking transactions, 'to' can be the pool id or empty + const toAddress = isStakingTx + ? poolId || '' + : outputs[0]?.address || ''; + + const encodedTx: IEncodedTxADA = { inputs: encodeInputs.map(input => ({ ...input, txHash: input.tx_hash, @@ -199,8 +285,8 @@ const convertCborTxToEncodeTx = async ( totalSpent, totalFeeInNative, transferInfo: { - from: encodeInputs[0].address, - to: outputs[0].address, + from: fromAddress, + to: toAddress, amount: totalSpent, token, }, @@ -213,6 +299,7 @@ const convertCborTxToEncodeTx = async ( rawTxHex: txHex, }, signOnly: true, + staking: stakingInfo, }; console.log('Cardano DApp EncodedTx: ', encodedTx); diff --git a/src/utils/onekey/types.ts b/src/utils/onekey/types.ts index 91ac02a..121d910 100644 --- a/src/utils/onekey/types.ts +++ b/src/utils/onekey/types.ts @@ -50,6 +50,7 @@ type ITxInfo = { body: string; hash: string; size: number; + rawTxHex?: string; }; export type IEncodedTxADA = { @@ -61,6 +62,7 @@ export type IEncodedTxADA = { transferInfo: ITransferInfo; tx: ITxInfo; signOnly?: boolean; + staking?: IStakingInfo; }; enum CardanoAddressType { @@ -85,3 +87,23 @@ export type IChangeAddress = { stakingPath: string; }; }; + +// Staking certificate types +export enum CardanoCertificateType { + STAKE_REGISTRATION = 0, + STAKE_DEREGISTRATION = 1, + STAKE_DELEGATION = 2, + STAKE_POOL_REGISTRATION = 3, +} + +export type IStakingInfo = { + isStakingTx: boolean; + certificates: ICardanoCertificate[]; + poolId?: string; // For delegation +}; + +export type ICardanoCertificate = { + type: CardanoCertificateType; + stakeCredential?: string; // stake key hash + poolKeyHash?: string; // for delegation +}; From 88f854d029c6cffc3338f857e8ead07da2e4e288 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 00:12:08 +0800 Subject: [PATCH 03/15] fix: update version to 1.1.8 and add output validation in convertCborTxToEncodeTx --- package.json | 2 +- src/utils/onekey/dapp.ts | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 474ad8c..a5c8a1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.7", + "version": "1.1.8", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index b90c483..886503f 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -4,6 +4,8 @@ import * as CardanoMessage from '@emurgo/cardano-message-signing-asmjs/cardano_m import BigNumber from 'bignumber.js'; import { getUtxos as getRawUtxos, requestAccountKey } from './signTx'; import { DataSignError } from './error'; +import { CoinSelectionError } from '../errors'; +import { ERROR } from '../../constants'; import { IAdaAmount, IAdaUTXO, @@ -207,6 +209,12 @@ const convertCborTxToEncodeTx = async ( // outputs txs const outputs: IEncodeOutput[] = []; const outputsLen = body.outputs().len(); + + // All valid transactions must have at least one output + // Empty outputs means insufficient funds to cover the transaction + if (outputsLen === 0) { + throw new CoinSelectionError(ERROR.UTXO_BALANCE_INSUFFICIENT); + } for (let i = 0; i < outputsLen; i++) { const output = body.outputs().get(i); const address = output.address().to_bech32(); @@ -256,10 +264,7 @@ const convertCborTxToEncodeTx = async ( }); } - const totalSpent = - outputs.length > 0 - ? BigNumber.sum(...outputs.map(o => o.amount)).toFixed() - : '0'; + const totalSpent = BigNumber.sum(...outputs.map(o => o.amount)).toFixed(); const token = outputs .filter(o => !addresses.includes(o.address)) From 008f87232f2dfb576ef6f221c1efdfa16a3e0950 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 00:33:27 +0800 Subject: [PATCH 04/15] fix: update version to 1.1.9 and enhance transaction handling in convertCborTxToEncodeTx --- package.json | 2 +- src/utils/onekey/dapp.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a5c8a1d..c58cdf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.8", + "version": "1.1.9", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index 886503f..2d34711 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -105,13 +105,20 @@ const convertCborTxToEncodeTx = async ( changeAddress: IChangeAddress, ): Promise => { let body: CardanoWasm.TransactionBody; + let rawTxHex: string; console.log('CARDANO LOCAL_VERSION : 1'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); + rawTxHex = txHex; } catch { + // Input is TransactionBody only (e.g., from staking providers like stakefish) + // Build a complete Transaction with empty witness set body = CardanoWasm.TransactionBody.from_bytes(Buffer.from(txHex, 'hex')); + const emptyWitnessSet = CardanoWasm.TransactionWitnessSet.new(); + const tx = CardanoWasm.Transaction.new(body, emptyWitnessSet); + rawTxHex = Buffer.from(tx.to_bytes() as any, 'hex').toString('hex'); } // Fee @@ -301,7 +308,7 @@ const convertCborTxToEncodeTx = async ( .transaction_hash() .to_hex(), size: 0, - rawTxHex: txHex, + rawTxHex, }, signOnly: true, staking: stakingInfo, From d9bd80f329299f1e89b9b0fdd9b130e36f01ce56 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 00:50:23 +0800 Subject: [PATCH 05/15] fix: update version to 1.1.10-alpha.0 and improve certificate handling in txToOneKey --- .gitignore | 1 + package.json | 2 +- src/utils/onekey/txToOneKey.ts | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 8689e49..8e1be32 100644 --- a/.gitignore +++ b/.gitignore @@ -149,3 +149,4 @@ dist !.yarn/sdks !.yarn/versions +.vscode/ \ No newline at end of file diff --git a/package.json b/package.json index c58cdf3..22090b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.9", + "version": "1.1.10-alpha.0", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index e3f56c2..f9cfd52 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -218,7 +218,9 @@ export const txToOneKey = async ( for (let i = 0; i < certificates.len(); i++) { const cert = certificates.get(i); const certificate: any = {}; - if (cert.kind() === 0) { + const certKind = cert.kind(); + + if (certKind === 0) { const credential = cert.as_stake_registration()?.stake_credential(); certificate.type = CardanoCertificateType.STAKE_REGISTRATION; if (credential?.kind() === 0) { @@ -229,7 +231,7 @@ export const txToOneKey = async ( ).toString('hex'); certificate.scriptHash = scriptHash; } - } else if (cert.kind() === 1) { + } else if (certKind === 1) { const credential = cert.as_stake_deregistration().stake_credential(); certificate.type = CardanoCertificateType.STAKE_DEREGISTRATION; if (credential.kind() === 0) { @@ -240,7 +242,7 @@ export const txToOneKey = async ( ).toString('hex'); certificate.scriptHash = scriptHash; } - } else if (cert.kind() === 2) { + } else if (certKind === 2) { const delegation = cert.as_stake_delegation(); const credential = delegation.stake_credential(); const poolKeyHashHex = Buffer.from( @@ -256,7 +258,7 @@ export const txToOneKey = async ( certificate.scriptHash = scriptHash; } certificate.pool = poolKeyHashHex; - } else if (cert.kind() === 3) { + } else if (certKind === 3) { const params = cert.as_pool_registration().pool_params(); certificate.type = CardanoCertificateType.STAKE_POOL_REGISTRATION; const owners = params.pool_owners(); @@ -342,6 +344,14 @@ export const txToOneKey = async ( relays: onekeyRelays, metadata, }; + } else { + // Skip unsupported certificate types (kind 4, 5, 6, etc.) + // Don't push empty certificate objects + console.warn( + `[txToOneKey] Unsupported certificate kind: ${certKind}, cert:`, + cert.to_hex(), + ); + continue; } onekeyCertificates.push(certificate); } From 720a1af438215abb3ef2e350a27082bba597f6f3 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 01:02:50 +0800 Subject: [PATCH 06/15] fix: update version to 1.1.10-alpha.1 and enhance certificate handling in txToOneKey --- package.json | 2 +- src/utils/onekey/txToOneKey.ts | 57 ++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 22090b8..3dcd941 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.0", + "version": "1.1.10-alpha.1", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index f9cfd52..2c81f67 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -28,6 +28,14 @@ export enum CardanoCertificateType { STAKE_DEREGISTRATION = 1, STAKE_DELEGATION = 2, STAKE_POOL_REGISTRATION = 3, + // Conway era + STAKE_REGISTRATION_CONWAY = 7, + STAKE_DEREGISTRATION_CONWAY = 8, + VOTE_DELEGATION = 9, + STAKE_AND_VOTE_DELEGATION = 10, + STAKE_REGISTRATION_AND_DELEGATION = 11, + VOTE_REGISTRATION_AND_DELEGATION = 12, + STAKE_VOTE_REGISTRATION_AND_DELEGATION = 13, } export enum CardanoPoolRelayType { @@ -344,10 +352,55 @@ export const txToOneKey = async ( relays: onekeyRelays, metadata, }; + } else if (certKind === 9 || certKind === 15) { + // Conway era: Vote Delegation (kind 9 in CDDL, may appear as 15 in some lib versions) + // For hardware wallet signing, we need the stake key path + const voteDelegation = cert.as_vote_delegation?.(); + if (voteDelegation) { + const credential = voteDelegation.stake_credential(); + certificate.type = CardanoCertificateType.VOTE_DELEGATION; + if (credential.kind() === 0) { + certificate.path = keys.stake.path; + } else { + const scriptHash = Buffer.from( + credential.to_scripthash().to_bytes(), + ).toString('hex'); + certificate.scriptHash = scriptHash; + } + // drep can be key hash, script hash, or abstain/no_confidence + const drep = voteDelegation.drep(); + if (drep.to_key_hash?.()) { + certificate.dRep = { + type: 0, // key hash + keyHash: Buffer.from(drep.to_key_hash().to_bytes()).toString( + 'hex', + ), + }; + } else if (drep.to_script_hash?.()) { + certificate.dRep = { + type: 1, // script hash + scriptHash: Buffer.from(drep.to_script_hash().to_bytes()).toString( + 'hex', + ), + }; + } else { + // abstain or no_confidence + certificate.dRep = { + type: drep.kind(), // 2 = abstain, 3 = no_confidence + }; + } + } else { + // Fallback: just include stake path for signing + console.log( + `[txToOneKey] Vote delegation cert kind ${certKind}, using stake path for signing`, + ); + certificate.type = CardanoCertificateType.VOTE_DELEGATION; + certificate.path = keys.stake.path; + } } else { - // Skip unsupported certificate types (kind 4, 5, 6, etc.) + // Skip unsupported certificate types // Don't push empty certificate objects - console.warn( + console.log( `[txToOneKey] Unsupported certificate kind: ${certKind}, cert:`, cert.to_hex(), ); From 35f8be9d2f071f3e9cc377eca26fc895146424c8 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 01:18:45 +0800 Subject: [PATCH 07/15] Revert "fix: update version to 1.1.10-alpha.1 and enhance certificate handling in txToOneKey" This reverts commit 720a1af438215abb3ef2e350a27082bba597f6f3. --- package.json | 2 +- src/utils/onekey/txToOneKey.ts | 57 ++-------------------------------- 2 files changed, 3 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 3dcd941..22090b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.1", + "version": "1.1.10-alpha.0", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index 2c81f67..f9cfd52 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -28,14 +28,6 @@ export enum CardanoCertificateType { STAKE_DEREGISTRATION = 1, STAKE_DELEGATION = 2, STAKE_POOL_REGISTRATION = 3, - // Conway era - STAKE_REGISTRATION_CONWAY = 7, - STAKE_DEREGISTRATION_CONWAY = 8, - VOTE_DELEGATION = 9, - STAKE_AND_VOTE_DELEGATION = 10, - STAKE_REGISTRATION_AND_DELEGATION = 11, - VOTE_REGISTRATION_AND_DELEGATION = 12, - STAKE_VOTE_REGISTRATION_AND_DELEGATION = 13, } export enum CardanoPoolRelayType { @@ -352,55 +344,10 @@ export const txToOneKey = async ( relays: onekeyRelays, metadata, }; - } else if (certKind === 9 || certKind === 15) { - // Conway era: Vote Delegation (kind 9 in CDDL, may appear as 15 in some lib versions) - // For hardware wallet signing, we need the stake key path - const voteDelegation = cert.as_vote_delegation?.(); - if (voteDelegation) { - const credential = voteDelegation.stake_credential(); - certificate.type = CardanoCertificateType.VOTE_DELEGATION; - if (credential.kind() === 0) { - certificate.path = keys.stake.path; - } else { - const scriptHash = Buffer.from( - credential.to_scripthash().to_bytes(), - ).toString('hex'); - certificate.scriptHash = scriptHash; - } - // drep can be key hash, script hash, or abstain/no_confidence - const drep = voteDelegation.drep(); - if (drep.to_key_hash?.()) { - certificate.dRep = { - type: 0, // key hash - keyHash: Buffer.from(drep.to_key_hash().to_bytes()).toString( - 'hex', - ), - }; - } else if (drep.to_script_hash?.()) { - certificate.dRep = { - type: 1, // script hash - scriptHash: Buffer.from(drep.to_script_hash().to_bytes()).toString( - 'hex', - ), - }; - } else { - // abstain or no_confidence - certificate.dRep = { - type: drep.kind(), // 2 = abstain, 3 = no_confidence - }; - } - } else { - // Fallback: just include stake path for signing - console.log( - `[txToOneKey] Vote delegation cert kind ${certKind}, using stake path for signing`, - ); - certificate.type = CardanoCertificateType.VOTE_DELEGATION; - certificate.path = keys.stake.path; - } } else { - // Skip unsupported certificate types + // Skip unsupported certificate types (kind 4, 5, 6, etc.) // Don't push empty certificate objects - console.log( + console.warn( `[txToOneKey] Unsupported certificate kind: ${certKind}, cert:`, cert.to_hex(), ); From 4983cf5a2d086e21a1d86da122168467985d1622 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 01:19:17 +0800 Subject: [PATCH 08/15] fix: update version to 1.1.10-alpha.2 and enhance transaction handling in txToOneKey and convertCborTxToEncodeTx --- package.json | 2 +- src/utils/onekey/dapp.ts | 16 ++++++++++++++-- src/utils/onekey/txToOneKey.ts | 18 ++++++++++++++++-- src/utils/onekey/types.ts | 3 +++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 22090b8..d73b43e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.0", + "version": "1.1.10-alpha.2", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index 2d34711..b44f56d 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -106,20 +106,29 @@ const convertCborTxToEncodeTx = async ( ): Promise => { let body: CardanoWasm.TransactionBody; let rawTxHex: string; + let originalBodyHex: string | undefined; + let isBodyOnly = false; console.log('CARDANO LOCAL_VERSION : 1'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); rawTxHex = txHex; + // No originalBodyHex needed for full Transaction } catch { // Input is TransactionBody only (e.g., from staking providers like stakefish) - // Build a complete Transaction with empty witness set - body = CardanoWasm.TransactionBody.from_bytes(Buffer.from(txHex, 'hex')); + // IMPORTANT: Preserve the original body hex for signing to maintain CBOR encoding + isBodyOnly = true; + originalBodyHex = txHex; // Save original body hex + body = CardanoWasm.TransactionBody.from_bytes( + Buffer.from(txHex, 'hex') as unknown as Uint8Array, + ); + // For rawTxHex, we need to build a Transaction for compatibility const emptyWitnessSet = CardanoWasm.TransactionWitnessSet.new(); const tx = CardanoWasm.Transaction.new(body, emptyWitnessSet); rawTxHex = Buffer.from(tx.to_bytes() as any, 'hex').toString('hex'); } + console.log('[convertCborTxToEncodeTx] isBodyOnly:', isBodyOnly, 'originalBodyHex:', !!originalBodyHex); // Fee const fee = body.fee().to_str(); @@ -309,6 +318,9 @@ const convertCborTxToEncodeTx = async ( .to_hex(), size: 0, rawTxHex, + // Original body hex from external provider, use this for signing + // to preserve CBOR encoding (important for Conway era transactions) + originalBodyHex, }, signOnly: true, staking: stakingInfo, diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index f9cfd52..97c4d3a 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -194,7 +194,7 @@ const outputsToOneKey = ( /** * - * @param {Transaction} tx + * @param {Transaction | TransactionBody} rawTx - can be full Transaction or just TransactionBody hex */ export const txToOneKey = async ( rawTx: string, @@ -204,7 +204,20 @@ export const txToOneKey = async ( changeAddress: IChangeAddress, ) => { const keys = generateKeys(initKeys, xpub); - const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(rawTx, 'hex')); + + // Try to parse as full Transaction first, fallback to TransactionBody + let tx: CardanoWasm.Transaction; + try { + tx = CardanoWasm.Transaction.from_bytes(Buffer.from(rawTx, 'hex')); + } catch { + // Input is TransactionBody only, wrap it with empty witness set + const body = CardanoWasm.TransactionBody.from_bytes( + Buffer.from(rawTx, 'hex'), + ); + const emptyWitnessSet = CardanoWasm.TransactionWitnessSet.new(); + tx = CardanoWasm.Transaction.new(body, emptyWitnessSet); + console.log('[txToOneKey] Input is TransactionBody, wrapped with empty witness set'); + } let signingMode = CardanoTxSigningMode.ORDINARY_TRANSACTION; @@ -540,6 +553,7 @@ export const txToOneKey = async ( key => !onekeyTx[key] && onekeyTx[key] != 0 && delete onekeyTx[key], ); console.log('format onekey cardano hardware Tx =====>>> : ', onekeyTx); + console.log('[txToOneKey] certificates detail:', JSON.stringify(onekeyCertificates, null, 2)); return Promise.resolve(onekeyTx); }; diff --git a/src/utils/onekey/types.ts b/src/utils/onekey/types.ts index 121d910..3d0affd 100644 --- a/src/utils/onekey/types.ts +++ b/src/utils/onekey/types.ts @@ -51,6 +51,9 @@ type ITxInfo = { hash: string; size: number; rawTxHex?: string; + // Original body hex from external provider (e.g., stakefish) + // Use this for signing to preserve CBOR encoding + originalBodyHex?: string; }; export type IEncodedTxADA = { From a6bab2fcc84e566d9413ccfcbf96cf99ab5fe0df Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 01:31:02 +0800 Subject: [PATCH 09/15] Revert "fix: update version to 1.1.10-alpha.2 and enhance transaction handling in txToOneKey and convertCborTxToEncodeTx" This reverts commit 4983cf5a2d086e21a1d86da122168467985d1622. --- package.json | 2 +- src/utils/onekey/dapp.ts | 16 ++-------------- src/utils/onekey/txToOneKey.ts | 18 ++---------------- src/utils/onekey/types.ts | 3 --- 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index d73b43e..22090b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.2", + "version": "1.1.10-alpha.0", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index b44f56d..2d34711 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -106,29 +106,20 @@ const convertCborTxToEncodeTx = async ( ): Promise => { let body: CardanoWasm.TransactionBody; let rawTxHex: string; - let originalBodyHex: string | undefined; - let isBodyOnly = false; console.log('CARDANO LOCAL_VERSION : 1'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); rawTxHex = txHex; - // No originalBodyHex needed for full Transaction } catch { // Input is TransactionBody only (e.g., from staking providers like stakefish) - // IMPORTANT: Preserve the original body hex for signing to maintain CBOR encoding - isBodyOnly = true; - originalBodyHex = txHex; // Save original body hex - body = CardanoWasm.TransactionBody.from_bytes( - Buffer.from(txHex, 'hex') as unknown as Uint8Array, - ); - // For rawTxHex, we need to build a Transaction for compatibility + // Build a complete Transaction with empty witness set + body = CardanoWasm.TransactionBody.from_bytes(Buffer.from(txHex, 'hex')); const emptyWitnessSet = CardanoWasm.TransactionWitnessSet.new(); const tx = CardanoWasm.Transaction.new(body, emptyWitnessSet); rawTxHex = Buffer.from(tx.to_bytes() as any, 'hex').toString('hex'); } - console.log('[convertCborTxToEncodeTx] isBodyOnly:', isBodyOnly, 'originalBodyHex:', !!originalBodyHex); // Fee const fee = body.fee().to_str(); @@ -318,9 +309,6 @@ const convertCborTxToEncodeTx = async ( .to_hex(), size: 0, rawTxHex, - // Original body hex from external provider, use this for signing - // to preserve CBOR encoding (important for Conway era transactions) - originalBodyHex, }, signOnly: true, staking: stakingInfo, diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index 97c4d3a..f9cfd52 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -194,7 +194,7 @@ const outputsToOneKey = ( /** * - * @param {Transaction | TransactionBody} rawTx - can be full Transaction or just TransactionBody hex + * @param {Transaction} tx */ export const txToOneKey = async ( rawTx: string, @@ -204,20 +204,7 @@ export const txToOneKey = async ( changeAddress: IChangeAddress, ) => { const keys = generateKeys(initKeys, xpub); - - // Try to parse as full Transaction first, fallback to TransactionBody - let tx: CardanoWasm.Transaction; - try { - tx = CardanoWasm.Transaction.from_bytes(Buffer.from(rawTx, 'hex')); - } catch { - // Input is TransactionBody only, wrap it with empty witness set - const body = CardanoWasm.TransactionBody.from_bytes( - Buffer.from(rawTx, 'hex'), - ); - const emptyWitnessSet = CardanoWasm.TransactionWitnessSet.new(); - tx = CardanoWasm.Transaction.new(body, emptyWitnessSet); - console.log('[txToOneKey] Input is TransactionBody, wrapped with empty witness set'); - } + const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(rawTx, 'hex')); let signingMode = CardanoTxSigningMode.ORDINARY_TRANSACTION; @@ -553,7 +540,6 @@ export const txToOneKey = async ( key => !onekeyTx[key] && onekeyTx[key] != 0 && delete onekeyTx[key], ); console.log('format onekey cardano hardware Tx =====>>> : ', onekeyTx); - console.log('[txToOneKey] certificates detail:', JSON.stringify(onekeyCertificates, null, 2)); return Promise.resolve(onekeyTx); }; diff --git a/src/utils/onekey/types.ts b/src/utils/onekey/types.ts index 3d0affd..121d910 100644 --- a/src/utils/onekey/types.ts +++ b/src/utils/onekey/types.ts @@ -51,9 +51,6 @@ type ITxInfo = { hash: string; size: number; rawTxHex?: string; - // Original body hex from external provider (e.g., stakefish) - // Use this for signing to preserve CBOR encoding - originalBodyHex?: string; }; export type IEncodedTxADA = { From 322a78a1b510e0b0dcba46b4cdf1286f320b7616 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 09:29:35 +0800 Subject: [PATCH 10/15] fix: vote delegate --- package.json | 2 +- src/utils/onekey/txToOneKey.ts | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 22090b8..5d5873a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.0", + "version": "1.1.10-alpha.5", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index f9cfd52..2a041b6 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -28,6 +28,14 @@ export enum CardanoCertificateType { STAKE_DEREGISTRATION = 1, STAKE_DELEGATION = 2, STAKE_POOL_REGISTRATION = 3, + VOTE_DELEGATION = 9, +} + +export enum CardanoDRepType { + KEY_HASH = 0, + SCRIPT_HASH = 1, + ABSTAIN = 2, + NO_CONFIDENCE = 3, } export enum CardanoPoolRelayType { @@ -344,6 +352,37 @@ export const txToOneKey = async ( relays: onekeyRelays, metadata, }; + } else if (certKind === 15) { + // VoteDelegation (CertificateKind.VoteDelegation = 15 in WASM lib) + const voteDelegation = cert.as_vote_delegation(); + const credential = voteDelegation.stake_credential(); + certificate.type = CardanoCertificateType.VOTE_DELEGATION; + + if (credential.kind() === 0) { + certificate.path = keys.stake.path; + } else { + const scriptHash = Buffer.from( + credential.to_scripthash().to_bytes(), + ).toString('hex'); + certificate.scriptHash = scriptHash; + } + + // Parse DRep + const drep = voteDelegation.drep(); + const drepKind = drep.kind(); + // DRepKind: KeyHash = 0, ScriptHash = 1, AlwaysAbstain = 2, AlwaysNoConfidence = 3 + certificate.dRep = { + type: drepKind as CardanoDRepType, + }; + + if (drepKind === 0) { + // KEY_HASH + certificate.dRep.keyHash = drep.to_key_hash()?.to_hex(); + } else if (drepKind === 1) { + // SCRIPT_HASH + certificate.dRep.scriptHash = drep.to_script_hash()?.to_hex(); + } + // ABSTAIN (2) and NO_CONFIDENCE (3) don't need additional fields } else { // Skip unsupported certificate types (kind 4, 5, 6, etc.) // Don't push empty certificate objects From 11c82d9e7aeb1f1df1acee3df0753b7223a1b369 Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 11:06:06 +0800 Subject: [PATCH 11/15] fix: update version to 1.1.10-alpha.6 and enhance transaction handling in convertCborTxToEncodeTx --- package.json | 2 +- src/utils/onekey/dapp.ts | 17 +++++++++-------- src/utils/onekey/types.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 5d5873a..861cd83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.5", + "version": "1.1.10-alpha.6", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index 2d34711..2c90efc 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -9,13 +9,13 @@ import { ERROR } from '../../constants'; import { IAdaAmount, IAdaUTXO, - IChangeAddress, IEncodedTxADA, IEncodeInput, IEncodeOutput, IStakingInfo, ICardanoCertificate, CardanoCertificateType, + IConvertCborTxParams, } from './types'; const getBalance = async (balances: IAdaAmount[]) => { @@ -98,12 +98,13 @@ const getUtxos = async ( ); }; -const convertCborTxToEncodeTx = async ( - txHex: string, - utxos: IAdaUTXO[], - addresses: string[], - changeAddress: IChangeAddress, -): Promise => { +const convertCborTxToEncodeTx = async ({ + txHex, + utxos, + addresses, + changeAddress, + isSignOnly, +}: IConvertCborTxParams): Promise => { let body: CardanoWasm.TransactionBody; let rawTxHex: string; @@ -310,7 +311,7 @@ const convertCborTxToEncodeTx = async ( size: 0, rawTxHex, }, - signOnly: true, + signOnly: isSignOnly, staking: stakingInfo, }; diff --git a/src/utils/onekey/types.ts b/src/utils/onekey/types.ts index 121d910..8b31fcb 100644 --- a/src/utils/onekey/types.ts +++ b/src/utils/onekey/types.ts @@ -107,3 +107,11 @@ export type ICardanoCertificate = { stakeCredential?: string; // stake key hash poolKeyHash?: string; // for delegation }; + +export type IConvertCborTxParams = { + txHex: string; + utxos: IAdaUTXO[]; + addresses: string[]; + changeAddress: IChangeAddress; + isSignOnly: boolean; +}; From 6787ef6da00af503b6ef8572c082a18c8e5384ba Mon Sep 17 00:00:00 2001 From: Leon Date: Sat, 29 Nov 2025 17:07:56 +0800 Subject: [PATCH 12/15] fix: update version to 1.1.10-alpha.7 and enhance logging in convertCborTxToEncodeTx for better debugging --- package.json | 2 +- src/utils/onekey/dapp.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 861cd83..e41db64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.6", + "version": "1.1.10-alpha.7", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index 2c90efc..dfe6c23 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -221,6 +221,20 @@ const convertCborTxToEncodeTx = async ({ // All valid transactions must have at least one output // Empty outputs means insufficient funds to cover the transaction if (outputsLen === 0) { + console.log('[convertCborTxToEncodeTx] Empty outputs detected, transaction data:', { + fee, + totalFeeInNative, + inputsCount: inputsLen, + inputs: inputs, + matchedUtxos: encodeInputs.map(u => ({ + tx_hash: u.tx_hash, + tx_index: u.tx_index, + amount: u.amount, + })), + isStakingTx, + certificates, + poolId, + }); throw new CoinSelectionError(ERROR.UTXO_BALANCE_INSUFFICIENT); } for (let i = 0; i < outputsLen; i++) { From 4cdb6490b97ba0d2678c7a67166fdab36ddc4b8e Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 4 Dec 2025 11:39:15 +0800 Subject: [PATCH 13/15] feat: add sync-to-monorepo script and update environment configuration --- .env.example | 2 + .vscode/settings.json | 9 -- package.json | 6 +- scripts/sync-to-monorepo.js | 158 ++++++++++++++++++++++++++++++++++++ yarn.lock | 64 +++++++++++++++ 5 files changed, 229 insertions(+), 10 deletions(-) create mode 100644 .env.example delete mode 100644 .vscode/settings.json create mode 100644 scripts/sync-to-monorepo.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2364b40 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# Path to app-monorepo for local sync +APP_MONOREPO_LOCAL_PATH=/path/to/app-monorepo diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ef727d1..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "search.exclude": { - "**/.yarn": true, - "**/.pnp.*": true - }, - "typescript.tsdk": ".yarn/sdks/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, - "cSpell.words": ["DEREGISTRATION", "drep", "preprod"] -} diff --git a/package.json b/package.json index e41db64..9b92a33 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "type-check": "yarn tsc -p tsconfig.types.json", "test": "yarn run-s 'test:*'", "test:unit": "jest -c ./jest.config.js", - "test:badges": "make-coverage-badge --output-path ./docs/badge-coverage.svg" + "test:badges": "make-coverage-badge --output-path ./docs/badge-coverage.svg", + "debug:watcher": "node scripts/sync-to-monorepo.js" }, "devDependencies": { "@emurgo/cardano-serialization-lib-nodejs": "13.2.0", @@ -42,10 +43,13 @@ "@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/parser": "8.8.0", "bignumber.js": "^9.0.1", + "chokidar": "^5.0.0", + "dotenv": "^17.2.3", "eslint": "^9.11.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.30.0", "eslint-plugin-prettier": "^5.2.1", + "fs-extra": "^11.3.2", "jest": "^29.7.0", "make-coverage-badge": "^1.2.0", "npm-run-all": "^4.1.5", diff --git a/scripts/sync-to-monorepo.js b/scripts/sync-to-monorepo.js new file mode 100644 index 0000000..daa7041 --- /dev/null +++ b/scripts/sync-to-monorepo.js @@ -0,0 +1,158 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +require('dotenv').config(); +const chokidar = require('chokidar'); +const fs = require('fs-extra'); +const path = require('path'); +const { exec } = require('child_process'); + +// Configuration +const config = { + // Path to app-monorepo, read from environment variable + targetDir: process.env.APP_MONOREPO_LOCAL_PATH, + // Current package name + packageName: '@onekeyfe/cardano-coin-selection-asmjs', + // Source directory to watch + watchDir: 'src', + // Build output directory + buildDir: 'lib', + // Debounce time in milliseconds + debounceTime: 300, + // Apps whose caches need to be cleared + appCacheDirs: ['desktop', 'ext', 'web', 'mobile'], +}; + +if (!config.targetDir) { + console.error( + 'โŒ APP_MONOREPO_LOCAL_PATH is not set. Please specify it in the .env file.\n' + + 'Example: APP_MONOREPO_LOCAL_PATH=/path/to/app-monorepo' + ); + process.exit(1); +} + +const projectRoot = __dirname; +const destNodeModulesPath = path.join( + config.targetDir, + 'node_modules', + config.packageName +); + +console.log('๐Ÿ“ฆ Package:', config.packageName); +console.log('๐Ÿ“‚ Source:', path.join(projectRoot, config.watchDir)); +console.log('๐Ÿ“ Target:', destNodeModulesPath); +console.log(''); + +// Debounced build state +let buildTimeout = null; +let isBuilding = false; + +function triggerBuild() { + if (buildTimeout) { + clearTimeout(buildTimeout); + } + + buildTimeout = setTimeout(() => { + if (isBuilding) { + console.log('โณ Build already in progress, queuing...'); + triggerBuild(); + return; + } + + isBuilding = true; + console.log('\n๐Ÿ”จ Building...'); + + exec('yarn build', { cwd: projectRoot }, (error, stdout, stderr) => { + isBuilding = false; + + if (error) { + console.error('โŒ Build failed:', error.message); + if (stderr) console.error(stderr); + return; + } + + console.log('โœ… Build complete'); + syncBuildOutput(); + }); + }, config.debounceTime); +} + +async function clearAppCaches() { + const clearedApps = []; + + for (const app of config.appCacheDirs) { + const cachePath = path.join(config.targetDir, 'apps', app, 'node_modules', '.cache'); + if (await fs.pathExists(cachePath)) { + await fs.remove(cachePath); + clearedApps.push(app); + } + } + + if (clearedApps.length > 0) { + console.log(`๐Ÿงน Cleared cache for: ${clearedApps.join(', ')}`); + } +} + +async function syncBuildOutput() { + const srcLibPath = path.join(projectRoot, config.buildDir); + const destLibPath = path.join(destNodeModulesPath, config.buildDir); + + try { + // Sync the entire lib directory + await fs.copy(srcLibPath, destLibPath, { overwrite: true }); + console.log(`๐Ÿ“ค Synced ${config.buildDir}/ -> ${destLibPath}`); + + // Sync package.json (in case version or other fields changed) + const srcPackageJson = path.join(projectRoot, 'package.json'); + const destPackageJson = path.join(destNodeModulesPath, 'package.json'); + await fs.copy(srcPackageJson, destPackageJson, { overwrite: true }); + console.log('๐Ÿ“ค Synced package.json'); + + // Clear webpack caches for all apps + await clearAppCaches(); + + console.log('โœจ Sync complete!\n'); + } catch (err) { + console.error('โŒ Sync error:', err); + } +} + +// Watch for source file changes +const watcher = chokidar.watch(path.join(projectRoot, config.watchDir), { + ignored: [ + /(^|[\/\\])\./, // Ignore dotfiles + /node_modules/, + /\.test\./, // Ignore test files + /\.spec\./, + ], + persistent: true, + ignoreInitial: true, +}); + +watcher + .on('add', filePath => { + console.log(`โž• Added: ${path.relative(projectRoot, filePath)}`); + triggerBuild(); + }) + .on('change', filePath => { + console.log(`โœ๏ธ Changed: ${path.relative(projectRoot, filePath)}`); + triggerBuild(); + }) + .on('unlink', filePath => { + console.log(`โž– Removed: ${path.relative(projectRoot, filePath)}`); + triggerBuild(); + }) + .on('error', error => console.error(`Watcher error: ${error}`)) + .on('ready', () => { + console.log('๐Ÿ‘€ Watching for changes in src/...'); + console.log('๐Ÿ’ก Tip: Press Ctrl+C to stop\n'); + + // Run initial build and sync on startup + console.log('๐Ÿš€ Initial build and sync...'); + triggerBuild(); + }); + +process.on('SIGINT', () => { + console.log('\n๐Ÿ‘‹ Closing watcher...'); + watcher.close(); + console.log('Bye!'); + process.exit(0); +}); diff --git a/yarn.lock b/yarn.lock index 616dca5..5d4e3aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1624,10 +1624,13 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^8.8.0" "@typescript-eslint/parser": "npm:8.8.0" bignumber.js: "npm:^9.0.1" + chokidar: "npm:^5.0.0" + dotenv: "npm:^17.2.3" eslint: "npm:^9.11.1" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-import: "npm:^2.30.0" eslint-plugin-prettier: "npm:^5.2.1" + fs-extra: "npm:^11.3.2" jest: "npm:^29.7.0" make-coverage-badge: "npm:^1.2.0" npm-run-all: "npm:^4.1.5" @@ -2788,6 +2791,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^5.0.0": + version: 5.0.0 + resolution: "chokidar@npm:5.0.0" + dependencies: + readdirp: "npm:^5.0.0" + checksum: 10/a1c2a4ee6ee81ba6409712c295a47be055fb9de1186dfbab33c1e82f28619de962ba02fc5f9d433daaedc96c35747460d8b2079ac2907de2c95e3f7cce913113 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -3149,6 +3161,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^17.2.3": + version: 17.2.3 + resolution: "dotenv@npm:17.2.3" + checksum: 10/f8b78626ebfff6e44420f634773375c9651808b3e1a33df6d4cc19120968eea53e100f59f04ec35f2a20b2beb334b6aba4f24040b2f8ad61773f158ac042a636 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -3791,6 +3810,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^11.3.2": + version: 11.3.2 + resolution: "fs-extra@npm:11.3.2" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10/d559545c73fda69c75aa786f345c2f738b623b42aea850200b1582e006a35278f63787179e3194ba19413c26a280441758952b0c7e88dd96762d497e365a6c3e + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -4036,6 +4066,13 @@ __metadata: languageName: node linkType: hard +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" @@ -5239,6 +5276,19 @@ __metadata: languageName: node linkType: hard +"jsonfile@npm:^6.0.1": + version: 6.2.0 + resolution: "jsonfile@npm:6.2.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10/513aac94a6eff070767cafc8eb4424b35d523eec0fcd8019fe5b975f4de5b10a54640c8d5961491ddd8e6f562588cf62435c5ddaf83aaf0986cd2ee789e0d7b9 + languageName: node + linkType: hard + "keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -6224,6 +6274,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^5.0.0": + version: 5.0.0 + resolution: "readdirp@npm:5.0.0" + checksum: 10/a17a591b51d8b912083660df159e8bd17305dc1a9ef27c869c818bd95ff59e3a6496f97e91e724ef433e789d559d24e39496ea1698822eb5719606dc9c1a923d + languageName: node + linkType: hard + "regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" @@ -7184,6 +7241,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10/ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.5": version: 1.0.5 resolution: "update-browserslist-db@npm:1.0.5" From 0fe3b72586be47503ff351ad9d31bf8147d8fbdb Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 4 Dec 2025 16:15:42 +0800 Subject: [PATCH 14/15] fix: update project root path and enhance logging in convertCborTxToEncodeTx --- scripts/sync-to-monorepo.js | 2 +- src/utils/onekey/dapp.ts | 61 ++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/scripts/sync-to-monorepo.js b/scripts/sync-to-monorepo.js index daa7041..b5ee6be 100644 --- a/scripts/sync-to-monorepo.js +++ b/scripts/sync-to-monorepo.js @@ -29,7 +29,7 @@ if (!config.targetDir) { process.exit(1); } -const projectRoot = __dirname; +const projectRoot = path.join(__dirname, '..'); const destNodeModulesPath = path.join( config.targetDir, 'node_modules', diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index dfe6c23..a661163 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -108,7 +108,7 @@ const convertCborTxToEncodeTx = async ({ let body: CardanoWasm.TransactionBody; let rawTxHex: string; - console.log('CARDANO LOCAL_VERSION : 1'); + console.log('CARDANO LOCAL_VERSION : 222====>>>'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); @@ -126,28 +126,7 @@ const convertCborTxToEncodeTx = async ({ const fee = body.fee().to_str(); const totalFeeInNative = new BigNumber(fee).shiftedBy(-1 * 6).toFixed(); - // inputs txs - const encodeInputs: IEncodedTxADA['inputs'] = []; - const inputs: { tx_hash: string; tx_index: number }[] = []; - const inputsLen = body.inputs().len(); - for (let i = 0; i < inputsLen; i++) { - const input = body.inputs().get(i); - const txHash = Buffer.from( - input.transaction_id().to_bytes() as any, - 'utf8', - ).toString('hex'); - const index = input.index(); - inputs.push({ tx_hash: txHash, tx_index: index }); - const utxo = utxos.find( - utxo => utxo.tx_hash === txHash && +utxo.tx_index === +index, - ); - // Only push matched UTXOs, skip external inputs (e.g., from DeFi protocols) - if (utxo) { - encodeInputs.push(utxo as unknown as IEncodeInput); - } - } - - // Parse certificates (for staking transactions) + // Parse certificates first (needed to determine if this is a staking transaction) const certificates: ICardanoCertificate[] = []; let poolId: string | undefined; const certs = body.certs(); @@ -214,13 +193,34 @@ const convertCborTxToEncodeTx = async ({ ? { isStakingTx: true, certificates, poolId } : undefined; + // inputs txs + const encodeInputs: IEncodedTxADA['inputs'] = []; + const inputs: { tx_hash: string; tx_index: number }[] = []; + const inputsLen = body.inputs().len(); + for (let i = 0; i < inputsLen; i++) { + const input = body.inputs().get(i); + const txHash = Buffer.from( + input.transaction_id().to_bytes() as any, + 'utf8', + ).toString('hex'); + const index = input.index(); + inputs.push({ tx_hash: txHash, tx_index: index }); + const utxo = utxos.find( + utxo => utxo.tx_hash === txHash && +utxo.tx_index === +index, + ); + // Only push matched UTXOs, skip external inputs (e.g., from DeFi protocols) + if (utxo) { + encodeInputs.push(utxo as unknown as IEncodeInput); + } + } + // outputs txs const outputs: IEncodeOutput[] = []; const outputsLen = body.outputs().len(); - // All valid transactions must have at least one output - // Empty outputs means insufficient funds to cover the transaction - if (outputsLen === 0) { + // Staking transactions (delegation, registration, deregistration) can have empty outputs + // Regular transactions must have at least one output + if (outputsLen === 0 && !isStakingTx) { console.log('[convertCborTxToEncodeTx] Empty outputs detected, transaction data:', { fee, totalFeeInNative, @@ -231,9 +231,6 @@ const convertCborTxToEncodeTx = async ({ tx_index: u.tx_index, amount: u.amount, })), - isStakingTx, - certificates, - poolId, }); throw new CoinSelectionError(ERROR.UTXO_BALANCE_INSUFFICIENT); } @@ -286,7 +283,9 @@ const convertCborTxToEncodeTx = async ({ }); } - const totalSpent = BigNumber.sum(...outputs.map(o => o.amount)).toFixed(); + const totalSpent = outputs.length > 0 + ? BigNumber.sum(...outputs.map(o => o.amount)).toFixed() + : '0'; const token = outputs .filter(o => !addresses.includes(o.address)) @@ -298,7 +297,7 @@ const convertCborTxToEncodeTx = async ({ // For staking transactions, 'to' can be the pool id or empty const toAddress = isStakingTx - ? poolId || '' + ? fromAddress || '' : outputs[0]?.address || ''; const encodedTx: IEncodedTxADA = { From 472e3e5b35678660bccaaefdc5d390e334a7fcd2 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 4 Dec 2025 17:01:04 +0800 Subject: [PATCH 15/15] fix: update version to 1.1.10-alpha.8 and enhance stake registration handling in txToOneKey --- package.json | 2 +- src/utils/onekey/dapp.ts | 2 +- src/utils/onekey/txToOneKey.ts | 32 ++++++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9b92a33..44f9261 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@onekeyfe/cardano-coin-selection-asmjs", - "version": "1.1.10-alpha.7", + "version": "1.1.10-alpha.8", "description": "", "keywords": [ "coin selection" diff --git a/src/utils/onekey/dapp.ts b/src/utils/onekey/dapp.ts index a661163..abc224a 100644 --- a/src/utils/onekey/dapp.ts +++ b/src/utils/onekey/dapp.ts @@ -108,7 +108,7 @@ const convertCborTxToEncodeTx = async ({ let body: CardanoWasm.TransactionBody; let rawTxHex: string; - console.log('CARDANO LOCAL_VERSION : 222====>>>'); + // console.log('CARDANO LOCAL_VERSION : 333====>>>'); try { const tx = CardanoWasm.Transaction.from_bytes(Buffer.from(txHex, 'hex')); body = tx.body(); diff --git a/src/utils/onekey/txToOneKey.ts b/src/utils/onekey/txToOneKey.ts index 2a041b6..2283914 100644 --- a/src/utils/onekey/txToOneKey.ts +++ b/src/utils/onekey/txToOneKey.ts @@ -28,6 +28,8 @@ export enum CardanoCertificateType { STAKE_DEREGISTRATION = 1, STAKE_DELEGATION = 2, STAKE_POOL_REGISTRATION = 3, + STAKE_REGISTRATION_CONWAY = 7, + STAKE_DEREGISTRATION_CONWAY = 8, VOTE_DELEGATION = 9, } @@ -229,8 +231,19 @@ export const txToOneKey = async ( const certKind = cert.kind(); if (certKind === 0) { - const credential = cert.as_stake_registration()?.stake_credential(); - certificate.type = CardanoCertificateType.STAKE_REGISTRATION; + const stakeRegistration = cert.as_stake_registration(); + const credential = stakeRegistration?.stake_credential(); + const deposit = stakeRegistration?.coin?.(); + + if (deposit) { + // Conway era - reg_cert with deposit + certificate.type = CardanoCertificateType.STAKE_REGISTRATION_CONWAY; + certificate.deposit = deposit.to_str(); + } else { + // Pre-Conway - stake_registration without deposit + certificate.type = CardanoCertificateType.STAKE_REGISTRATION; + } + if (credential?.kind() === 0) { certificate.path = keys.stake.path; } else { @@ -240,8 +253,19 @@ export const txToOneKey = async ( certificate.scriptHash = scriptHash; } } else if (certKind === 1) { - const credential = cert.as_stake_deregistration().stake_credential(); - certificate.type = CardanoCertificateType.STAKE_DEREGISTRATION; + const stakeDeregistration = cert.as_stake_deregistration(); + const credential = stakeDeregistration.stake_credential(); + const deposit = stakeDeregistration.coin?.(); + + if (deposit) { + // Conway era - unreg_cert with deposit + certificate.type = CardanoCertificateType.STAKE_DEREGISTRATION_CONWAY; + certificate.deposit = deposit.to_str(); + } else { + // Pre-Conway - stake_deregistration without deposit + certificate.type = CardanoCertificateType.STAKE_DEREGISTRATION; + } + if (credential.kind() === 0) { certificate.path = keys.stake.path; } else {