|
| 1 | +import { |
| 2 | + buildRgbppLockArgs, |
| 3 | + genRgbppLockScript, |
| 4 | + appendIssuerCellToBtcBatchTransferToSign, |
| 5 | + signCkbTransaction, |
| 6 | + addressToScriptHash, |
| 7 | + appendCkbTxWitnesses, |
| 8 | + updateCkbTxWithRealBtcTxId, |
| 9 | + sendCkbTx, |
| 10 | +} from 'rgbpp/ckb'; |
| 11 | +import { addressToScript, serializeScript } from '@nervosnetwork/ckb-sdk-utils'; |
| 12 | +import { genBtcJumpCkbVirtualTx, sendRgbppUtxos, BtcAssetsApiError } from 'rgbpp'; |
| 13 | +import { |
| 14 | + isMainnet, |
| 15 | + collector, |
| 16 | + btcService, |
| 17 | + btcAccount, |
| 18 | + BTC_TESTNET_TYPE, |
| 19 | + CKB_PRIVATE_KEY, |
| 20 | + ckbAddress, |
| 21 | + initOfflineCkbCollector, |
| 22 | + vendorCellDeps, |
| 23 | + initOfflineBtcDataSource, |
| 24 | +} from '../../../env'; |
| 25 | +import { saveCkbVirtualTxResult } from '../../../shared/utils'; |
| 26 | +import { signAndSendPsbt } from '../../../shared/btc-account'; |
| 27 | + |
| 28 | +interface LeapToCkbParams { |
| 29 | + rgbppLockArgsList: string[]; |
| 30 | + toCkbAddress: string; |
| 31 | + transferAmount: bigint; |
| 32 | + compatibleXudtTypeScript: CKBComponents.Script; |
| 33 | +} |
| 34 | + |
| 35 | +const leapRusdFromBtcToCKB = async ({ |
| 36 | + rgbppLockArgsList, |
| 37 | + toCkbAddress, |
| 38 | + compatibleXudtTypeScript, |
| 39 | + transferAmount, |
| 40 | +}: LeapToCkbParams) => { |
| 41 | + const rgbppLocks = rgbppLockArgsList.map((args) => genRgbppLockScript(args, isMainnet, BTC_TESTNET_TYPE)); |
| 42 | + const { collector: offlineCollector } = await initOfflineCkbCollector([ |
| 43 | + ...rgbppLocks.map((lock) => ({ lock, type: compatibleXudtTypeScript })), |
| 44 | + { lock: addressToScript(ckbAddress) }, |
| 45 | + ]); |
| 46 | + |
| 47 | + const ckbVirtualTxResult = await genBtcJumpCkbVirtualTx({ |
| 48 | + collector: offlineCollector, |
| 49 | + rgbppLockArgsList, |
| 50 | + xudtTypeBytes: serializeScript(compatibleXudtTypeScript), |
| 51 | + transferAmount, |
| 52 | + toCkbAddress, |
| 53 | + isMainnet, |
| 54 | + btcTestnetType: BTC_TESTNET_TYPE, |
| 55 | + vendorCellDeps, |
| 56 | + }); |
| 57 | + |
| 58 | + // Save ckbVirtualTxResult |
| 59 | + saveCkbVirtualTxResult(ckbVirtualTxResult, '3-compatible-xudt-btc-leap-ckb-offline'); |
| 60 | + |
| 61 | + const { commitment, ckbRawTx, sumInputsCapacity } = ckbVirtualTxResult; |
| 62 | + |
| 63 | + const btcOfflineDataSource = await initOfflineBtcDataSource(rgbppLockArgsList, btcAccount.from); |
| 64 | + |
| 65 | + // Send BTC tx |
| 66 | + const psbt = await sendRgbppUtxos({ |
| 67 | + ckbVirtualTx: ckbRawTx, |
| 68 | + commitment, |
| 69 | + tos: [btcAccount.from], |
| 70 | + ckbCollector: offlineCollector, |
| 71 | + from: btcAccount.from, |
| 72 | + fromPubkey: btcAccount.fromPubkey, |
| 73 | + source: btcOfflineDataSource, |
| 74 | + needPaymaster: false, |
| 75 | + feeRate: 128, |
| 76 | + }); |
| 77 | + |
| 78 | + const { txId: btcTxId, rawTxHex: btcTxBytes } = await signAndSendPsbt(psbt, btcAccount, btcService); |
| 79 | + console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); |
| 80 | + console.log('BTC tx bytes: ', btcTxBytes); |
| 81 | + |
| 82 | + const interval = setInterval(async () => { |
| 83 | + try { |
| 84 | + console.log('Waiting for BTC tx and proof to be ready'); |
| 85 | + const rgbppApiSpvProof = await btcService.getRgbppSpvProof(btcTxId, 0); |
| 86 | + clearInterval(interval); |
| 87 | + // Update CKB transaction with the real BTC txId |
| 88 | + const newCkbRawTx = updateCkbTxWithRealBtcTxId({ ckbRawTx, btcTxId, isMainnet }); |
| 89 | + const ckbTx = await appendCkbTxWitnesses({ |
| 90 | + ckbRawTx: newCkbRawTx, |
| 91 | + btcTxBytes, |
| 92 | + rgbppApiSpvProof, |
| 93 | + }); |
| 94 | + |
| 95 | + const { ckbRawTx: unsignedTx, inputCells } = await appendIssuerCellToBtcBatchTransferToSign({ |
| 96 | + issuerAddress: ckbAddress, |
| 97 | + ckbRawTx: ckbTx, |
| 98 | + collector: offlineCollector, |
| 99 | + sumInputsCapacity, |
| 100 | + isMainnet, |
| 101 | + }); |
| 102 | + |
| 103 | + const keyMap = new Map<string, string>(); |
| 104 | + keyMap.set(addressToScriptHash(ckbAddress), CKB_PRIVATE_KEY); |
| 105 | + const signedTx = signCkbTransaction(keyMap, unsignedTx, inputCells, true); |
| 106 | + |
| 107 | + const txHash = await sendCkbTx({ collector, signedTx }); |
| 108 | + console.info( |
| 109 | + `Rgbpp compatible xUDT asset has been leaped from BTC to CKB and the related CKB tx hash is ${txHash}`, |
| 110 | + ); |
| 111 | + } catch (error) { |
| 112 | + if (!(error instanceof BtcAssetsApiError)) { |
| 113 | + console.error(error); |
| 114 | + } |
| 115 | + } |
| 116 | + }, 20 * 1000); |
| 117 | +}; |
| 118 | + |
| 119 | +// Please use your real BTC UTXO information on the BTC Testnet |
| 120 | +// BTC Testnet3: https://mempool.space/testnet |
| 121 | +// BTC Signet: https://mempool.space/signet |
| 122 | + |
| 123 | +// rgbppLockArgs: outIndexU32 + btcTxId |
| 124 | +leapRusdFromBtcToCKB({ |
| 125 | + rgbppLockArgsList: [buildRgbppLockArgs(2, 'daec93a97c8b7f6fdd33696f814f0292be966dc4ea4853400d3cada816c70f5d')], |
| 126 | + toCkbAddress: 'ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqfpu7pwavwf3yang8khrsklumayj6nyxhqpmh7fq', |
| 127 | + // Please use your own RGB++ compatible xudt asset's type script |
| 128 | + compatibleXudtTypeScript: { |
| 129 | + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', |
| 130 | + hashType: 'type', |
| 131 | + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', |
| 132 | + }, |
| 133 | + transferAmount: BigInt(10_0000), |
| 134 | +}); |
| 135 | + |
| 136 | +/* |
| 137 | +npx tsx examples/rgbpp/xudt/offline/compatible-xudt/3-btc-leap-ckb.ts |
| 138 | +*/ |
1 commit comments
github-actions[bot] commentedon Feb 17, 2025
New snapshot version of the rgbpp-sdk packages have been released:
0.0.0-snap-20250217084517
0.0.0-snap-20250217084517
0.0.0-snap-20250217084517
0.0.0-snap-20250217084517