// protocols/wdk.js - Tether WDK Protocol Integration
//
// Implements the standard protocol interface for Tether WDK
// Supports: Bitcoin, Ethereum, and EVM-compatible chains
//
// WDK Documentation: https://docs.wallet.tether.io
// NOTE: This file is legacy - Phase 6 uses lib/wdk-integration.cjs instead
// Kept for reference or future ESM-based implementation
import 'bare-node-runtime/global'
import WDK from '@tetherto/wdk'
import WalletManagerEvm from '@tetherto/wdk-wallet-evm'
import WalletManagerBtc from '@tetherto/wdk-wallet-btc'
export default {
name: 'wdk',
// Protocol metadata
info: {
displayName: 'Tether WDK',
description: 'Multi-chain wallet with Bitcoin and Ethereum support',
chains: ['bitcoin', 'ethereum'],
stablecoin: true,
privacy: 'public',
speed: 'varies', // Depends on chain
fees: 'low' // Varies by chain
},
// Internal state
context: null,
wdk: null,
accounts: {},
/**
* Initialize WDK protocol
* @param {Object} context - Wallet context { identity, corestore, db, config }
* @returns {Object} - This protocol instance
*/
async init(context) {
this.context = context
// Get mnemonic from identity
// keet-identity-key stores the mnemonic, we can access it
const mnemonic = context.identity.mnemonic
// Initialize WDK manager
this.wdk = new WDK(mnemonic)
// Register enabled blockchains
const chains = context.config.wdk.chains || ['ethereum', 'bitcoin']
const networks = context.config.wdk.networks
for (const chain of chains) {
if (chain === 'ethereum' && networks.ethereum) {
this.wdk.registerWallet('ethereum', WalletManagerEvm, {
provider: networks.ethereum.provider
})
} else if (chain === 'bitcoin' && networks.bitcoin) {
this.wdk.registerWallet('bitcoin', WalletManagerBtc, {
network: networks.bitcoin.network,
host: networks.bitcoin.host,
port: networks.bitcoin.port
})
}
}
// Get accounts (index 0 for each chain)
for (const chain of chains) {
try {
this.accounts[chain] = await this.wdk.getAccount(chain, 0)
} catch (err) {
console.error(` ⚠️ Failed to get ${chain} account: ${err.message}`)
}
}
// Store wallet addresses in Hyperbee for multi-device sync
await this.storeWalletInfo()
console.log(' Chains:', chains.join(', '))
return this
},
/**
* Store wallet information in Hyperbee for multi-device sync
*/
async storeWalletInfo() {
for (const [chain, account] of Object.entries(this.accounts)) {
try {
const address = await account.getAddress()
await this.context.db.put(`wallets/${chain}`, {
chain,
address,
createdAt: Date.now()
})
} catch (err) {
console.error(` ⚠️ Failed to store ${chain} wallet info: ${err.message}`)
}
}
},
/**
* Get balances across all enabled chains
* @returns {Array<Object>} - Array of { chain, currency, amount }
*/
async getBalance() {
const balances = []
for (const [chain, account] of Object.entries(this.accounts)) {
try {
const balance = await account.getBalance()
const address = await account.getAddress()
balances.push({
chain,
address,
currency: this.getCurrency(chain),
amount: balance.toString(), // bigint to string
wei: balance // Keep raw bigint for calculations
})
} catch (err) {
balances.push({
chain,
currency: this.getCurrency(chain),
error: err.message
})
}
}
return balances
},
/**
* Send payment
* @param {Object} params - { to, amount, chain, memo }
* @returns {Object} - Transaction details
*/
async send({ to, amount, chain, memo }) {
// Select chain if not specified
if (!chain) {
chain = this.selectChain(to)
}
const account = this.accounts[chain]
if (!account) {
throw new Error(`Chain "${chain}" not enabled`)
}
// Send transaction
const result = await account.sendTransaction({
to,
value: BigInt(amount)
})
// Store transaction in Hyperbee
await this.context.db.put(`tx/${chain}/${result.hash}`, {
timestamp: Date.now(),
chain,
type: 'send',
to,
amount: amount.toString(),
hash: result.hash,
fee: result.fee.toString(),
memo
})
// Append to Autobase for multi-device sync
await this.context.txHistory.append(JSON.stringify({
timestamp: Date.now(),
chain,
type: 'send',
to,
amount: amount.toString(),
hash: result.hash
}))
return result
},
/**
* Create address for receiving payment
* @param {Object} params - { chain, amount, memo }
* @returns {Object} - Address details
*/
async receive({ chain, amount, memo }) {
// Default to ethereum if not specified
if (!chain) {
chain = 'ethereum'
}
const account = this.accounts[chain]
if (!account) {
throw new Error(`Chain "${chain}" not enabled`)
}
// Get address
const address = await account.getAddress()
return {
address,
chain,
currency: this.getCurrency(chain),
amount,
memo
}
},
/**
* Get transaction history
* @param {Object} params - { limit, chain }
* @returns {Array} - Transaction history
*/
async history({ limit = 50, chain = null }) {
const txs = []
const prefix = chain ? `tx/${chain}/` : 'tx/'
for await (const { key, value } of this.context.db.createReadStream()) {
if (key.startsWith(prefix)) {
txs.push(value)
}
if (txs.length >= limit) break
}
// Sort by timestamp (most recent first)
return txs.sort((a, b) => b.timestamp - a.timestamp)
},
/**
* Select appropriate chain based on address format
* @param {String} to - Recipient address
* @returns {String} - Chain name
*/
selectChain(to) {
// Auto-detect from address format
if (to.startsWith('0x')) return 'ethereum'
if (to.startsWith('bc1') || to.startsWith('tb1')) return 'bitcoin' // Native SegWit
if (to.startsWith('1') || to.startsWith('3')) return 'bitcoin' // Legacy (though WDK only supports SegWit)
// Default to ethereum
return 'ethereum'
},
/**
* Get currency symbol for chain
* @param {String} chain - Chain name
* @returns {String} - Currency symbol
*/
getCurrency(chain) {
const currencyMap = {
bitcoin: 'BTC',
ethereum: 'ETH'
}
return currencyMap[chain] || chain.toUpperCase()
},
/**
* Format balance for display
* @param {bigint} balance - Balance in wei or satoshis
* @param {String} chain - Chain name
* @returns {String} - Formatted balance
*/
formatBalance(balance, chain) {
if (chain === 'bitcoin') {
// Bitcoin: satoshis to BTC (1 BTC = 100,000,000 satoshis)
return (Number(balance) / 1e8).toFixed(8)
} else if (chain === 'ethereum') {
// Ethereum: wei to ETH (1 ETH = 10^18 wei)
return (Number(balance) / 1e18).toFixed(18)
}
return balance.toString()
}
}