Transaction Signing Examples
Core transaction signing patterns based on real implementations from nodejs-playground/src.
Simple Signing
typescript
import { AppWallet, NETWORK_ID, deserializeTx } from '@hydra-sdk/core'
const wallet = new AppWallet({
key: {
type: 'mnemonic',
words: 'your mnemonic words here'.split(' ')
},
networkId: NETWORK_ID.PREPROD
})
async function signTransaction(cborHex: string, partialSign: boolean = false) {
try {
const signedTx = await wallet.signTx(cborHex, partialSign)
const txHash = deserializeTx(signedTx).transaction_hash().to_hex()
console.log('✅ Transaction signed:', txHash)
return { signedTx, txHash }
} catch (error) {
console.error('❌ Signing error:', error)
throw error
}
}
// Usage
const result = await signTransaction(transactionCborHex)
Multi-Signature
typescript
import { AppWallet, NETWORK_ID, deserializeTx } from '@hydra-sdk/core'
class MultiSigWallet {
private wallets: AppWallet[] = []
addSigner(mnemonic: string[]) {
const wallet = new AppWallet({
key: { type: 'mnemonic', words: mnemonic },
networkId: NETWORK_ID.PREPROD
})
this.wallets.push(wallet)
}
async signWithAllWallets(cborHex: string) {
let currentCbor = cborHex
for (let i = 0; i < this.wallets.length; i++) {
const isLastSigner = i === this.wallets.length - 1
currentCbor = await this.wallets[i].signTx(currentCbor, !isLastSigner)
console.log(`✅ Wallet ${i + 1}/${this.wallets.length} signed`)
}
return {
finalTx: currentCbor,
txHash: deserializeTx(currentCbor).transaction_hash().to_hex()
}
}
}
// Usage
const multiSig = new MultiSigWallet()
multiSig.addSigner('first signer mnemonic'.split(' '))
multiSig.addSigner('second signer mnemonic'.split(' '))
const result = await multiSig.signWithAllWallets(unsignedTxCbor)
Error Handling Pattern
typescript
import { AppWallet, NETWORK_ID, deserializeTx } from '@hydra-sdk/core'
enum SigningErrorType {
INVALID_CBOR = 'invalid_cbor',
USER_REJECTED = 'user_rejected',
NETWORK_ERROR = 'network_error'
}
class RobustSigner {
private wallet: AppWallet
private retryAttempts = 3
private retryDelay = 1000
constructor(mnemonic: string[]) {
this.wallet = new AppWallet({
key: { type: 'mnemonic', words: mnemonic },
networkId: NETWORK_ID.PREPROD
})
}
async signWithRetry(cborHex: string) {
let lastError: Error
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
try {
const signedTx = await this.wallet.signTx(cborHex)
const txHash = deserializeTx(signedTx).transaction_hash().to_hex()
console.log(`✅ Signing successful on attempt ${attempt}`)
return { signedTx, txHash, attempt }
} catch (error) {
lastError = error
const errorType = this.classifyError(error)
// Don't retry certain errors
if (errorType === SigningErrorType.INVALID_CBOR ||
errorType === SigningErrorType.USER_REJECTED) {
throw error
}
if (attempt < this.retryAttempts) {
await this.delay(this.retryDelay * attempt)
}
}
}
throw new Error(`Signing failed after ${this.retryAttempts} attempts: ${lastError.message}`)
}
private classifyError(error: Error): SigningErrorType {
const message = error.message.toLowerCase()
if (message.includes('invalid') && message.includes('cbor')) {
return SigningErrorType.INVALID_CBOR
}
if (message.includes('rejected') || message.includes('cancelled')) {
return SigningErrorType.USER_REJECTED
}
return SigningErrorType.NETWORK_ERROR
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
}
// Usage
const robustSigner = new RobustSigner('your mnemonic words here'.split(' '))
const result = await robustSigner.signWithRetry(transactionCbor)
Performance Pattern
typescript
import { AppWallet, NETWORK_ID, deserializeTx } from '@hydra-sdk/core'
class PerformanceSigner {
private wallet: AppWallet
private metrics: Array<{
timestamp: number
duration: number
txHash: string
inputCount: number
outputCount: number
}> = []
constructor(mnemonic: string[]) {
this.wallet = new AppWallet({
key: { type: 'mnemonic', words: mnemonic },
networkId: NETWORK_ID.PREPROD
})
}
async signWithMetrics(cborHex: string) {
const startTime = Date.now()
try {
const signedTx = await this.wallet.signTx(cborHex)
const duration = Date.now() - startTime
const tx = deserializeTx(signedTx)
const txHash = tx.transaction_hash().to_hex()
const body = tx.transaction_body()
this.metrics.push({
timestamp: startTime,
duration,
txHash,
inputCount: body.inputs().len(),
outputCount: body.outputs().len()
})
console.log(`⏱️ Signing took ${duration}ms: ${txHash}`)
return { signedTx, txHash, duration }
} catch (error) {
const duration = Date.now() - startTime
console.error(`❌ Signing failed after ${duration}ms:`, error)
throw error
}
}
getPerformanceStats() {
if (this.metrics.length === 0) return null
const durations = this.metrics.map(m => m.duration)
const avg = durations.reduce((a, b) => a + b, 0) / durations.length
return {
totalTransactions: this.metrics.length,
averageDuration: Math.round(avg),
minDuration: Math.min(...durations),
maxDuration: Math.max(...durations)
}
}
}
// Usage
const performanceSigner = new PerformanceSigner('your mnemonic words here'.split(' '))
await performanceSigner.signWithMetrics(tx1)
await performanceSigner.signWithMetrics(tx2)
const stats = performanceSigner.getPerformanceStats()
console.log('Stats:', stats)
