/** * tron-sim-transfer.mjs — simulate USDT.transfer with caller = the HTLC contract * (vs an EOA) to surface the real revert reason behind B2's "transfer failed". */ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { TronWeb } from 'tronweb'; const ROOT = join(import.meta.dirname, '..'); const NILE = 'https://nile.trongrid.io'; const sw = JSON.parse(readFileSync(join(ROOT, '.tron-b2-lastswap.json'), 'utf8')); const k = JSON.parse(readFileSync(join(ROOT, '.tron-nile-keys.json'), 'utf8')); const tronWeb = new TronWeb({ fullHost: NILE, privateKey: k.privateKey }); const USDT_HEX = tronWeb.address.toHex(sw.usdt); const decodeRevert = (hexStr) => { if (!hexStr) return '(no data)'; if (hexStr.startsWith('08c379a0')) { const len = parseInt(hexStr.slice(72, 136), 16); return Buffer.from(hexStr.slice(136, 136 + len * 2), 'hex').toString('utf8'); } return hexStr.slice(0, 80); }; async function sim(caller, to, value, label) { const res = await tronWeb.transactionBuilder.triggerConstantContract( USDT_HEX, 'transfer(address,uint256)', {}, [{ type: 'address', value: tronWeb.address.toHex(to) }, { type: 'uint256', value }], tronWeb.address.toHex(caller)); const okFlag = res?.result?.result === true; const cres = res?.constant_result?.[0]; const msgHex = res?.result?.message ? Buffer.from(res.result.message, 'hex').toString('utf8') : ''; console.log(`\n${label} (caller ${caller}):`); console.log(' result.result:', okFlag, '| code:', res?.result?.code || '-'); console.log(' constant_result:', cres ? cres.slice(0, 80) : '(none)', '->', decodeRevert(cres)); if (msgHex) console.log(' message:', msgHex); } console.log('USDT', sw.usdt, '| HTLC', sw.htlc, '| receiver', sw.receiver, '| amount', sw.amount); await sim(sw.htlc, sw.receiver, sw.amount, '1) HTLC -> receiver (the failing call)'); await sim(k.address, sw.receiver, 100000, '2) Alice(EOA) -> receiver (control)');