// test/mocks/wdk-mock.cjs - WDK Mock Factory for Testing
const crypto = require('hypercore-crypto')
const b4a = require('b4a')
/**
* Mock WDK class that simulates @tetherto/wdk behavior
*/
class MockWDK {
constructor(mnemonic, options = {}) {
this.mnemonic = mnemonic
this.wallets = {}
this.options = options
}
/**
* Register wallet manager (chainable)
* @param {string} chain - Chain name (bitcoin, ethereum)
* @param {Function} Manager - Wallet manager class
* @param {Object} config - Configuration
* @returns {MockWDK} - This instance for chaining
*/
registerWallet(chain, Manager, config) {
this.wallets[chain] = {
Manager,
config,
accounts: new Map()
}
return this
}
/**
* Get account for chain at index
* @param {string} chain - Chain name
* @param {number} accountIndex - BIP44 account index
* @returns {Promise<MockAccount>}
*/
async getAccount(chain, accountIndex = 0) {
const wallet = this.wallets[chain]
if (!wallet) {
throw new Error(`Wallet not registered for chain: ${chain}`)
}
const key = `${chain}:${accountIndex}`
if (!wallet.accounts.has(key)) {
wallet.accounts.set(key, new MockAccount(chain, accountIndex, this.mnemonic))
}
return wallet.accounts.get(key)
}
/**
* Generate random seed phrase (12 words)
* @returns {string} - BIP-39 mnemonic
*/
static getRandomSeedPhrase() {
// Generate deterministic test mnemonic from random bytes
const entropy = crypto.randomBytes(16) // 128 bits for 12 words
const checksum = entropy[0] % 16 // Simple checksum for testing
const words = [
'abandon', 'ability', 'able', 'about', 'above', 'absent',
'absorb', 'abstract', 'absurd', 'abuse', 'access', 'about'
]
return words.join(' ')
}
}
/**
* Mock Account class that simulates WDK account operations
*/
class MockAccount {
constructor(chain, accountIndex, mnemonic) {
this.chain = chain
this.accountIndex = accountIndex
this.mnemonic = mnemonic
// Mock balances
this.balance = BigInt(chain === 'bitcoin' ? 100000000 : 1000000000000000000) // 1 BTC or 1 ETH
this.tokenBalances = new Map()
// Default token balances
this.tokenBalances.set('0x7169D38820dfd117C3FA1f22a697dBA58d90BA06', BigInt(500000000)) // 500 USDT (6 decimals)
this.tokenBalances.set('0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', BigInt(250000000)) // 250 USDC (6 decimals)
}
/**
* Get account balance
* @returns {Promise<BigInt>}
*/
async getBalance() {
return this.balance
}
/**
* Get address for account
* @param {number} addressIndex - BIP44 address index
* @returns {Promise<string>}
*/
async getAddress(addressIndex = 0) {
if (this.chain === 'bitcoin') {
// Mock Bitcoin testnet address
return `tb1q${b4a.toString(crypto.randomBytes(32), 'hex').slice(0, 38)}`
} else if (this.chain === 'ethereum') {
// Mock Ethereum address
return `0x${b4a.toString(crypto.randomBytes(20), 'hex')}`
}
throw new Error(`Unsupported chain: ${this.chain}`)
}
/**
* Get token balance (ERC-20)
* @param {string} contractAddress - Token contract address
* @returns {Promise<BigInt>}
*/
async getTokenBalance(contractAddress) {
return this.tokenBalances.get(contractAddress) || BigInt(0)
}
/**
* Send transaction
* @param {Object} params - Transaction parameters
* @returns {Promise<Object>} - Transaction result with hash
*/
async sendTransaction(params) {
const { to, value } = params
// Validate balance
if (value > this.balance) {
throw new Error('Insufficient balance')
}
// Deduct from balance
this.balance -= value
// Return mock transaction
return {
hash: `0x${b4a.toString(crypto.randomBytes(32), 'hex')}`,
from: await this.getAddress(),
to,
value: value.toString(),
blockNumber: Math.floor(Math.random() * 1000000),
timestamp: Date.now()
}
}
/**
* Quote transaction fee
* @param {Object} params - Transaction parameters
* @returns {Promise<Object>} - Fee estimate
*/
async quoteSendTransaction(params) {
const { value } = params
// Mock fee calculation (0.1% of amount or minimum)
const calculatedFee = value / BigInt(1000)
const minFee = this.chain === 'bitcoin' ? BigInt(1000) : BigInt(21000) // 1000 sats or 21000 gwei
const fee = calculatedFee > minFee ? calculatedFee : minFee
return {
fee,
feeRate: this.chain === 'bitcoin' ? '10 sat/vB' : '50 gwei',
estimatedConfirmation: '10 minutes'
}
}
/**
* Transfer ERC-20 token
* @param {Object} params - Transfer parameters
* @returns {Promise<Object>} - Transfer result
*/
async transfer(params) {
const { token, recipient, amount } = params
// Validate token balance
const tokenBalance = await this.getTokenBalance(token)
if (amount > tokenBalance) {
throw new Error('Insufficient token balance')
}
// Deduct from token balance
this.tokenBalances.set(token, tokenBalance - amount)
return {
hash: `0x${b4a.toString(crypto.randomBytes(32), 'hex')}`,
from: await this.getAddress(),
to: recipient,
token,
amount: amount.toString(),
blockNumber: Math.floor(Math.random() * 1000000),
timestamp: Date.now()
}
}
/**
* Quote token transfer fee
* @param {Object} params - Transfer parameters
* @returns {Promise<Object>} - Fee estimate
*/
async quoteTransfer(params) {
// ERC-20 transfer costs more gas than ETH transfer
const fee = BigInt(65000) // ~65k gas units
return {
fee,
feeRate: '50 gwei',
estimatedConfirmation: '10 minutes'
}
}
}
/**
* Mock WalletManager classes (Bitcoin, EVM)
*/
class MockWalletManagerBtc {
static default = MockWalletManagerBtc
}
class MockWalletManagerEvm {
static default = MockWalletManagerEvm
}
module.exports = {
default: MockWDK,
MockWDK,
MockAccount,
WalletManagerBtc: MockWalletManagerBtc,
WalletManagerEvm: MockWalletManagerEvm
}