From a99623869a1496b64607183bf9bbc0083e43ce9e Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Tue, 31 Dec 2024 16:32:48 +0800 Subject: [PATCH 01/13] feat: Fetch compatible xudt cellDeps --- packages/ckb/src/constants/index.ts | 21 ++++++++++++++++ packages/ckb/src/rgbpp/ckb-jump-btc.ts | 7 +++++- packages/ckb/src/utils/cell-dep.ts | 34 +++++++++++++++++++++++++- packages/ckb/src/utils/ckb-tx.ts | 16 +++++++++++- 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/packages/ckb/src/constants/index.ts b/packages/ckb/src/constants/index.ts index 8d3e532c..214facd2 100644 --- a/packages/ckb/src/constants/index.ts +++ b/packages/ckb/src/constants/index.ts @@ -66,6 +66,15 @@ const TestnetInfo = { depType: 'code', } as CKBComponents.CellDep, + CompatibleXUDTTypeScripts: [ + // RUSD + { + codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', + hashType: 'type', + args: '', + }, + ] as CKBComponents.Script[], + UniqueTypeScript: { codeHash: '0x8e341bcfec6393dcd41e635733ff2dca00a6af546949f70c57a706c0f344df8b', hashType: 'type', @@ -197,6 +206,15 @@ const MainnetInfo = { depType: 'code', } as CKBComponents.CellDep, + CompatibleXUDTTypeScripts: [ + // RUSD + { + codeHash: '0x26a33e0815888a4a0614a0b7d09fa951e0993ff21e55905510104a0b1312032b', + hashType: 'type', + args: '', + }, + ] as CKBComponents.Script[], + UniqueTypeScript: { codeHash: '0x2c8c11c985da60b0a330c61a85507416d6382c130ba67f0c47ab071e00aec628', hashType: 'data1', @@ -308,3 +326,6 @@ export const getSporeTypeScript = (isMainnet: boolean) => isMainnet ? MainnetInfo.SporeTypeScript : TestnetInfo.SporeTypeScript; export const getSporeTypeDep = (isMainnet: boolean) => isMainnet ? MainnetInfo.SporeTypeDep : TestnetInfo.SporeTypeDep; + +export const getCompatibleXudtTypeScripts = (isMainnet: boolean) => + isMainnet ? MainnetInfo.CompatibleXUDTTypeScripts : TestnetInfo.CompatibleXUDTTypeScripts; diff --git a/packages/ckb/src/rgbpp/ckb-jump-btc.ts b/packages/ckb/src/rgbpp/ckb-jump-btc.ts index 87bcf16e..22046715 100644 --- a/packages/ckb/src/rgbpp/ckb-jump-btc.ts +++ b/packages/ckb/src/rgbpp/ckb-jump-btc.ts @@ -9,6 +9,7 @@ import { isTypeAssetSupported, u128ToLe, genRgbppLockScript, + isStandardUDTTypeSupported, } from '../utils'; import { MAX_FEE, MIN_CAPACITY, RGBPP_TX_WITNESS_MAX_SIZE } from '../constants'; import { blockchain } from '@ckb-lumos/base'; @@ -100,7 +101,11 @@ export const genCkbJumpBtcVirtualTx = async ({ }); outputsData.push('0x'); - const cellDeps = await fetchTypeIdCellDeps(isMainnet, { xudt: true }); + const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); + const cellDeps = await fetchTypeIdCellDeps(isMainnet, { + xudt: isStandardUDT, + compatibleXudtCodeHash: isStandardUDT ? '' : xudtType.codeHash, + }); const witnesses = inputs.map(() => '0x'); const ckbRawTx: CKBComponents.RawTransaction = { diff --git a/packages/ckb/src/utils/cell-dep.ts b/packages/ckb/src/utils/cell-dep.ts index c8e2555f..59ee35f1 100644 --- a/packages/ckb/src/utils/cell-dep.ts +++ b/packages/ckb/src/utils/cell-dep.ts @@ -19,6 +19,9 @@ interface CellDepsObject { unique: { testnet: CKBComponents.CellDep; }; + compatibleXudt: { + [codeHash: string]: CKBComponents.CellDep; + }; } const GITHUB_CELL_DEPS_JSON_URL = 'https://raw.githubusercontent.com/ckb-cell/typeid-contract-cell-deps/main/deployment/cell-deps.json'; @@ -42,6 +45,7 @@ export interface CellDepsSelected { btcTime?: boolean; xudt?: boolean; unique?: boolean; + compatibleXudtCodeHash?: string; } export const fetchTypeIdCellDeps = async ( @@ -49,6 +53,8 @@ export const fetchTypeIdCellDeps = async ( selected: CellDepsSelected, btcTestnetType?: BTCTestnetType, ): Promise => { + let cellDeps: CKBComponents.CellDep[] = []; + let rgbppLockDep = getRgbppLockDep(isMainnet, btcTestnetType); let btcTimeDep = getBtcTimeLockDep(isMainnet, btcTestnetType); let xudtDep = getXudtDep(isMainnet); @@ -68,7 +74,7 @@ export const fetchTypeIdCellDeps = async ( uniqueDep = cellDepsObj.unique.testnet; } } - let cellDeps: CKBComponents.CellDep[] = []; + if (selected.rgbpp) { // RGB++ config cell is deployed together with the RGB++ lock contract // @@ -117,5 +123,31 @@ export const fetchTypeIdCellDeps = async ( cellDeps = [...cellDeps, uniqueDep] as CKBComponents.CellDep[]; } + /** + * "compatibleXudt": { + "0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb": { + "outPoint": { + "index": "0x0", + "txHash": "0xed7d65b9ad3d99657e37c4285d585fea8a5fcaf58165d54dacf90243f911548b" + }, + "depType": "code" + }, + "0x26a33e0815888a4a0614a0b7d09fa951e0993ff21e55905510104a0b1312032b": { + "outPoint": { + "index": "0x0", + "txHash": "0x8ec1081bd03e5417bb4467e96f4cec841acdd35924538a35e7547fe320118977" + }, + "depType": "code" + } + } + */ + if (selected.compatibleXudtCodeHash) { + if (cellDepsObj?.compatibleXudt === undefined) { + throw new Error('Compatible xUDT cell deps are not found'); + } + const cellDep = cellDepsObj.compatibleXudt[selected.compatibleXudtCodeHash]; + cellDeps = [...cellDeps, cellDep] as CKBComponents.CellDep[]; + } + return cellDeps; }; diff --git a/packages/ckb/src/utils/ckb-tx.ts b/packages/ckb/src/utils/ckb-tx.ts index 812f1765..d680daef 100644 --- a/packages/ckb/src/utils/ckb-tx.ts +++ b/packages/ckb/src/utils/ckb-tx.ts @@ -5,6 +5,7 @@ import { CKB_UNIT, UNLOCKABLE_LOCK_SCRIPT, getClusterTypeScript, + getCompatibleXudtTypeScripts, getSporeTypeScript, getXudtTypeScript, } from '../constants'; @@ -23,7 +24,16 @@ export const calculateTransactionFee = (txSize: number, feeRate?: bigint): bigin return fee * ratio < base ? fee + BigInt(1) : fee; }; -export const isUDTTypeSupported = (type: CKBComponents.Script, isMainnet: boolean): boolean => { +export const isCompatibleUDTTypesSupported = (type: CKBComponents.Script, isMainnet: boolean): boolean => { + const compatibleXudtTypeBytes = getCompatibleXudtTypeScripts(isMainnet).map((script) => serializeScript(script)); + const typeAsset = serializeScript({ + ...type, + args: '', + }); + return compatibleXudtTypeBytes.includes(typeAsset); +}; + +export const isStandardUDTTypeSupported = (type: CKBComponents.Script, isMainnet: boolean): boolean => { const xudtType = serializeScript(getXudtTypeScript(isMainnet)); const typeAsset = serializeScript({ ...type, @@ -32,6 +42,10 @@ export const isUDTTypeSupported = (type: CKBComponents.Script, isMainnet: boolea return xudtType === typeAsset; }; +export const isUDTTypeSupported = (type: CKBComponents.Script, isMainnet: boolean): boolean => { + return isStandardUDTTypeSupported(type, isMainnet) || isCompatibleUDTTypesSupported(type, isMainnet); +}; + export const isSporeTypeSupported = (type: CKBComponents.Script, isMainnet: boolean): boolean => { const sporeType = serializeScript(getSporeTypeScript(isMainnet)); const typeAsset = serializeScript({ From 4e462df1725803d95cecde7aa602f51f8a16c113 Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Tue, 31 Dec 2024 17:25:12 +0800 Subject: [PATCH 02/13] feat: Fetch compatible xudt cellDeps for RGB++ operations --- packages/ckb/src/rgbpp/btc-jump-ckb.ts | 12 ++++++++++- packages/ckb/src/rgbpp/btc-time.ts | 14 +++++++++++-- packages/ckb/src/rgbpp/btc-transfer.ts | 28 +++++++++++++++++++++----- packages/ckb/src/rgbpp/ckb-jump-btc.ts | 8 ++++++-- packages/ckb/src/utils/cell-dep.ts | 10 +++++---- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/packages/ckb/src/rgbpp/btc-jump-ckb.ts b/packages/ckb/src/rgbpp/btc-jump-ckb.ts index 31c5b3e8..b275929f 100644 --- a/packages/ckb/src/rgbpp/btc-jump-ckb.ts +++ b/packages/ckb/src/rgbpp/btc-jump-ckb.ts @@ -19,6 +19,7 @@ import { genRgbppLockScript, throwErrorWhenRgbppCellsInvalid, isRgbppCapacitySufficientForChange, + isStandardUDTTypeSupported, } from '../utils'; import { Hex, IndexerCell } from '../types'; import { RGBPP_WITNESS_PLACEHOLDER, getSecp256k1CellDep } from '../constants'; @@ -135,7 +136,16 @@ export const genBtcJumpCkbVirtualTx = async ({ outputsData.push(otherRgbppCell.outputData); } - const cellDeps = await fetchTypeIdCellDeps(isMainnet, { rgbpp: true, xudt: true }, btcTestnetType); + const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); + const cellDeps = await fetchTypeIdCellDeps( + isMainnet, + { + rgbpp: true, + xudt: isStandardUDT, + compatibleXudtCodeHashes: isStandardUDT ? [] : [xudtType.codeHash], + }, + btcTestnetType, + ); if (needPaymasterCell) { cellDeps.push(getSecp256k1CellDep(isMainnet)); } diff --git a/packages/ckb/src/rgbpp/btc-time.ts b/packages/ckb/src/rgbpp/btc-time.ts index cfed7b6e..4c70954e 100644 --- a/packages/ckb/src/rgbpp/btc-time.ts +++ b/packages/ckb/src/rgbpp/btc-time.ts @@ -25,6 +25,8 @@ import { lockScriptFromBtcTimeLockArgs, transformSpvProof, buildSpvClientCellDep, + isStandardUDTTypeSupported, + isCompatibleUDTTypesSupported, } from '../utils'; import signWitnesses from '@nervosnetwork/ckb-sdk-core/lib/signWitnesses'; @@ -61,9 +63,17 @@ export const buildBtcTimeCellsSpentTx = async ({ const outputsData = sortedBtcTimeCells.map((cell) => cell.outputData); - const cellDeps: CKBComponents.CellDep[] = await fetchTypeIdCellDeps( + const hasStandardUDT = outputs.some((output) => isStandardUDTTypeSupported(output.type!, isMainnet)); + const compatibleXudtCodeHashes = outputs + .filter((output) => isCompatibleUDTTypesSupported(output.type!, isMainnet)) + .map((output) => output.type!.codeHash); + const cellDeps = await fetchTypeIdCellDeps( isMainnet, - { btcTime: true, xudt: true }, + { + btcTime: true, + xudt: hasStandardUDT, + compatibleXudtCodeHashes, + }, btcTestnetType, ); diff --git a/packages/ckb/src/rgbpp/btc-transfer.ts b/packages/ckb/src/rgbpp/btc-transfer.ts index 3ea79690..16f759b3 100644 --- a/packages/ckb/src/rgbpp/btc-transfer.ts +++ b/packages/ckb/src/rgbpp/btc-transfer.ts @@ -24,6 +24,7 @@ import { isRgbppCapacitySufficientForChange, throwErrorWhenRgbppCellsInvalid, throwErrorWhenTxInputsExceeded, + isStandardUDTTypeSupported, } from '../utils'; import { Hex, IndexerCell } from '../types'; import { @@ -170,7 +171,16 @@ export const genBtcTransferCkbVirtualTx = async ({ handleNonTargetRgbppCells(outputs.length); } - const cellDeps = await fetchTypeIdCellDeps(isMainnet, { rgbpp: true, xudt: true }, btcTestnetType); + const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); + const cellDeps = await fetchTypeIdCellDeps( + isMainnet, + { + rgbpp: true, + xudt: isStandardUDT, + compatibleXudtCodeHashes: isStandardUDT ? [] : [xudtType.codeHash], + }, + btcTestnetType, + ); if (needPaymasterCell) { cellDeps.push(getSecp256k1CellDep(isMainnet)); } @@ -286,10 +296,18 @@ export const genBtcBatchTransferCkbVirtualTx = async ({ outputsData.push(append0x(u128ToLe(sumAmount - sumTransferAmount))); } - const cellDeps = [ - ...(await fetchTypeIdCellDeps(isMainnet, { rgbpp: true, xudt: true }, btcTestnetType)), - getSecp256k1CellDep(isMainnet), - ]; + const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); + let cellDeps = await fetchTypeIdCellDeps( + isMainnet, + { + rgbpp: true, + xudt: isStandardUDT, + compatibleXudtCodeHashes: isStandardUDT ? [] : [xudtType.codeHash], + }, + btcTestnetType, + ); + cellDeps = [...cellDeps, getSecp256k1CellDep(isMainnet)]; + const witnesses: Hex[] = []; const lockArgsSet: Set = new Set(); for (const cell of rgbppCells) { diff --git a/packages/ckb/src/rgbpp/ckb-jump-btc.ts b/packages/ckb/src/rgbpp/ckb-jump-btc.ts index 22046715..b816a366 100644 --- a/packages/ckb/src/rgbpp/ckb-jump-btc.ts +++ b/packages/ckb/src/rgbpp/ckb-jump-btc.ts @@ -104,7 +104,7 @@ export const genCkbJumpBtcVirtualTx = async ({ const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); const cellDeps = await fetchTypeIdCellDeps(isMainnet, { xudt: isStandardUDT, - compatibleXudtCodeHash: isStandardUDT ? '' : xudtType.codeHash, + compatibleXudtCodeHashes: isStandardUDT ? [] : [xudtType.codeHash], }); const witnesses = inputs.map(() => '0x'); @@ -214,7 +214,11 @@ export const genCkbBatchJumpBtcVirtualTx = async ({ }); outputsData.push('0x'); - const cellDeps = await fetchTypeIdCellDeps(isMainnet, { xudt: true }); + const isStandardUDT = isStandardUDTTypeSupported(xudtType, isMainnet); + const cellDeps = await fetchTypeIdCellDeps(isMainnet, { + xudt: isStandardUDT, + compatibleXudtCodeHashes: isStandardUDT ? [] : [xudtType.codeHash], + }); const witnesses = inputs.map(() => '0x'); const ckbRawTx: CKBComponents.RawTransaction = { diff --git a/packages/ckb/src/utils/cell-dep.ts b/packages/ckb/src/utils/cell-dep.ts index 59ee35f1..4afdd0c4 100644 --- a/packages/ckb/src/utils/cell-dep.ts +++ b/packages/ckb/src/utils/cell-dep.ts @@ -45,7 +45,7 @@ export interface CellDepsSelected { btcTime?: boolean; xudt?: boolean; unique?: boolean; - compatibleXudtCodeHash?: string; + compatibleXudtCodeHashes?: string[]; } export const fetchTypeIdCellDeps = async ( @@ -141,12 +141,14 @@ export const fetchTypeIdCellDeps = async ( } } */ - if (selected.compatibleXudtCodeHash) { + if (selected.compatibleXudtCodeHashes && selected.compatibleXudtCodeHashes?.length > 0) { if (cellDepsObj?.compatibleXudt === undefined) { throw new Error('Compatible xUDT cell deps are not found'); } - const cellDep = cellDepsObj.compatibleXudt[selected.compatibleXudtCodeHash]; - cellDeps = [...cellDeps, cellDep] as CKBComponents.CellDep[]; + const compatibleCellDeps = selected.compatibleXudtCodeHashes.map( + (codeHash) => cellDepsObj.compatibleXudt[codeHash], + ); + cellDeps = [...cellDeps, ...compatibleCellDeps] as CKBComponents.CellDep[]; } return cellDeps; From ee1175e22ab31e0a6ee8358d6e54a58f75f1ccf5 Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 11:32:46 +0800 Subject: [PATCH 03/13] fix: Update some test case data --- packages/ckb/src/collector/collector.spec.ts | 16 +++++++--------- packages/ckb/src/constants/index.ts | 2 +- packages/ckb/src/utils/cell-dep.spec.ts | 10 ++++++++++ packages/ckb/src/utils/cell-dep.ts | 12 ++++++------ packages/ckb/src/utils/ckb-tx.spec.ts | 17 +++++------------ packages/ckb/src/utils/rgbpp.ts | 2 -- 6 files changed, 29 insertions(+), 30 deletions(-) diff --git a/packages/ckb/src/collector/collector.spec.ts b/packages/ckb/src/collector/collector.spec.ts index b71d3e66..3297654b 100644 --- a/packages/ckb/src/collector/collector.spec.ts +++ b/packages/ckb/src/collector/collector.spec.ts @@ -9,22 +9,20 @@ describe('collector', () => { it('getLiveCell', async () => { const cell = await collector.getLiveCell({ - txHash: '0xfa87db3187be8cf850117ccfcdfe2525c44f1a58f71d9714dd2ce489e9723182', + txHash: '0x8f8c79eb6671709633fe6a46de93c0fedc9c1b8a6527a18d3983879542635c9f', index: '0x0', }); - expect(cell.output.lock.args).toBe('0x0450340178ae277261a838c89f9ccb76a190ed4b'); + expect(cell.output.lock.codeHash).toBe('0x0000000000000000000000000000000000000000000000000000000000000000'); }); it('getLiveCells', async () => { - const [cell1, cell2, cell3] = await collector.getLiveCells([ - // Cellbase - { txHash: '0xfa87db3187be8cf850117ccfcdfe2525c44f1a58f71d9714dd2ce489e9723182', index: '0x0' }, - { txHash: '0xed5adcba9bbbfe76c546264f2b8a33cbf9c95d09a88550bb0a4f98d6f36a6ed2', index: '0x0' }, + const [cell1, cell2] = await collector.getLiveCells([ + // Genesis block + { txHash: '0x8f8c79eb6671709633fe6a46de93c0fedc9c1b8a6527a18d3983879542635c9f', index: '0x0' }, // Nervos DAO { txHash: '0x8277d74d33850581f8d843613ded0c2a1722dec0e87e748f45c115dfb14210f1', index: '0x0' }, ]); - expect(cell1.output.lock.args).toBe('0x0450340178ae277261a838c89f9ccb76a190ed4b'); - expect(cell2.output.lock.args).toBe('0xf1cbacc833b5c62f79ac8de6aa7ffbe464cae563'); - expect(cell3.output.type?.codeHash).toBe('0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e'); + expect(cell1.output.lock.codeHash).toBe('0x0000000000000000000000000000000000000000000000000000000000000000'); + expect(cell2.output.type?.codeHash).toBe('0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e'); }); }); diff --git a/packages/ckb/src/constants/index.ts b/packages/ckb/src/constants/index.ts index 214facd2..f5117158 100644 --- a/packages/ckb/src/constants/index.ts +++ b/packages/ckb/src/constants/index.ts @@ -69,7 +69,7 @@ const TestnetInfo = { CompatibleXUDTTypeScripts: [ // RUSD { - codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', hashType: 'type', args: '', }, diff --git a/packages/ckb/src/utils/cell-dep.spec.ts b/packages/ckb/src/utils/cell-dep.spec.ts index b0c9c809..d4fc3d78 100644 --- a/packages/ckb/src/utils/cell-dep.spec.ts +++ b/packages/ckb/src/utils/cell-dep.spec.ts @@ -59,4 +59,14 @@ describe('dynamic fetch cell dep', () => { const cellDeps = await fetchTypeIdCellDeps(isMainnet, {}); expect(cellDeps.length).toBe(0); }); + + it('fetchTypeIdCellDeps with RUSD', async () => { + const isMainnet = false; + const cellDeps = await fetchTypeIdCellDeps(isMainnet, { + xudt: false, + compatibleXudtCodeHashes: ['0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a'], + }); + expect(cellDeps[0].outPoint?.txHash).toBe('0xed7d65b9ad3d99657e37c4285d585fea8a5fcaf58165d54dacf90243f911548b'); + expect(cellDeps[0].outPoint?.index).toBe('0x0'); + }); }); diff --git a/packages/ckb/src/utils/cell-dep.ts b/packages/ckb/src/utils/cell-dep.ts index 4afdd0c4..77559eb7 100644 --- a/packages/ckb/src/utils/cell-dep.ts +++ b/packages/ckb/src/utils/cell-dep.ts @@ -29,7 +29,7 @@ const GITHUB_CELL_DEPS_JSON_URL = const CDN_GITHUB_CELL_DEPS_JSON_URL = 'https://cdn.jsdelivr.net/gh/ckb-cell/typeid-contract-cell-deps@main/deployment/cell-deps.json'; -const request = (url: string) => axios.get(url, { timeout: 2000 }); +const request = (url: string) => axios.get(url, { timeout: 6000 }); const fetchCellDepsJson = async () => { try { @@ -75,7 +75,7 @@ export const fetchTypeIdCellDeps = async ( } } - if (selected.rgbpp) { + if (selected.rgbpp === true) { // RGB++ config cell is deployed together with the RGB++ lock contract // // contract_deployment_transaction: @@ -95,7 +95,7 @@ export const fetchTypeIdCellDeps = async ( ] as CKBComponents.CellDep[]; } - if (selected.btcTime) { + if (selected.btcTime === true) { // BTC Time config cell is deployed together with the BTC Time lock contract // // contract_deployment_transaction: @@ -115,17 +115,17 @@ export const fetchTypeIdCellDeps = async ( ] as CKBComponents.CellDep[]; } - if (selected.xudt) { + if (selected.xudt === true) { cellDeps = [...cellDeps, xudtDep] as CKBComponents.CellDep[]; } - if (selected.unique) { + if (selected.unique === true) { cellDeps = [...cellDeps, uniqueDep] as CKBComponents.CellDep[]; } /** * "compatibleXudt": { - "0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb": { + "0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a": { "outPoint": { "index": "0x0", "txHash": "0xed7d65b9ad3d99657e37c4285d585fea8a5fcaf58165d54dacf90243f911548b" diff --git a/packages/ckb/src/utils/ckb-tx.spec.ts b/packages/ckb/src/utils/ckb-tx.spec.ts index 39d884c3..e4ed3c1b 100644 --- a/packages/ckb/src/utils/ckb-tx.spec.ts +++ b/packages/ckb/src/utils/ckb-tx.spec.ts @@ -210,8 +210,8 @@ describe('ckb tx utils', () => { inputs: [ { previousOutput: { - index: '0x1', - txHash: '0x1a6d2b18faed84293b81ada9d00600a3cdb637fa43a5cfa20eb63934757352ea', + index: '0x0', + txHash: '0x8f8c79eb6671709633fe6a46de93c0fedc9c1b8a6527a18d3983879542635c9f', }, since: '0x0', }, @@ -251,14 +251,7 @@ describe('ckb tx utils', () => { { previousOutput: { index: '0x0', - txHash: '0xeb6ea53459efc83755e4ede6ff54b7698913379e678c6018e1eac87241f964f2', - }, - since: '0x0', - }, - { - previousOutput: { - index: '0x0', - txHash: '0x80314ab559ddc7b2f9e523f968b2d930b1a7b53f690091e6666570b46f54b804', + txHash: '0x8f8c79eb6671709633fe6a46de93c0fedc9c1b8a6527a18d3983879542635c9f', }, since: '0x0', }, @@ -290,8 +283,8 @@ describe('ckb tx utils', () => { inputs: [ { previousOutput: { - index: '0x0', - txHash: '0xeb6ea53459efc83755e4ede6ff54b7698913379e678c6018e1eac87241f964f2', + index: '0x1', + txHash: '0x8f8c79eb6671709633fe6a46de93c0fedc9c1b8a6527a18d3983879542635c9f', }, since: '0x0', }, diff --git a/packages/ckb/src/utils/rgbpp.ts b/packages/ckb/src/utils/rgbpp.ts index c24f4560..8cf2496b 100644 --- a/packages/ckb/src/utils/rgbpp.ts +++ b/packages/ckb/src/utils/rgbpp.ts @@ -125,8 +125,6 @@ export interface BTCTimeLockArgs { } export const btcTxIdAndAfterFromBtcTimeLockArgs = (args: Hex): BTCTimeLockArgs => { const { btcTxid, after } = BTCTimeLock.unpack(append0x(args)); - console.log(btcTxid); - console.log(after); return { btcTxId: reverseHex(append0x(btcTxid)), after, From 78afa04aef0fc2e470584665f1e6a308afc88793 Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 16:29:41 +0800 Subject: [PATCH 04/13] fix: Update rgbpp package to transfer compatible xudt --- packages/ckb/src/utils/cell-dep.ts | 15 ++++++++++----- packages/rgbpp/README.md | 4 ++++ packages/rgbpp/src/rgbpp/types/xudt.ts | 4 ++++ packages/rgbpp/src/rgbpp/xudt/btc-transfer-all.ts | 11 +++++------ packages/rgbpp/src/rgbpp/xudt/btc-transfer.ts | 5 +++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/ckb/src/utils/cell-dep.ts b/packages/ckb/src/utils/cell-dep.ts index 77559eb7..3885fe15 100644 --- a/packages/ckb/src/utils/cell-dep.ts +++ b/packages/ckb/src/utils/cell-dep.ts @@ -24,16 +24,18 @@ interface CellDepsObject { }; } const GITHUB_CELL_DEPS_JSON_URL = - 'https://raw.githubusercontent.com/ckb-cell/typeid-contract-cell-deps/main/deployment/cell-deps.json'; + 'https://raw.githubusercontent.com/utxostack/typeid-contract-cell-deps/main/deployment/cell-deps.json'; +// If the CDN has cache issue, please clear the cache by visiting +// https://www.jsdelivr.com/tools/purge?path=/gh/utxostack/typeid-contract-cell-deps@main const CDN_GITHUB_CELL_DEPS_JSON_URL = - 'https://cdn.jsdelivr.net/gh/ckb-cell/typeid-contract-cell-deps@main/deployment/cell-deps.json'; + 'https://cdn.jsdelivr.net/gh/utxostack/typeid-contract-cell-deps@main/deployment/cell-deps.json'; -const request = (url: string) => axios.get(url, { timeout: 6000 }); +const request = (url: string) => axios.get(url, { timeout: 5000 }); const fetchCellDepsJson = async () => { try { - const response = await Promise.any([request(GITHUB_CELL_DEPS_JSON_URL), request(CDN_GITHUB_CELL_DEPS_JSON_URL)]); + const response = await Promise.any([request(CDN_GITHUB_CELL_DEPS_JSON_URL), request(GITHUB_CELL_DEPS_JSON_URL)]); return response.data as CellDepsObject; } catch (error) { // console.error('Error fetching cell deps:', error); @@ -143,11 +145,14 @@ export const fetchTypeIdCellDeps = async ( */ if (selected.compatibleXudtCodeHashes && selected.compatibleXudtCodeHashes?.length > 0) { if (cellDepsObj?.compatibleXudt === undefined) { - throw new Error('Compatible xUDT cell deps are not found'); + throw new Error('Compatible xUDT cell deps are null'); } const compatibleCellDeps = selected.compatibleXudtCodeHashes.map( (codeHash) => cellDepsObj.compatibleXudt[codeHash], ); + if (compatibleCellDeps.length === 0) { + throw new Error('The specific compatible xUDT cell deps are not found'); + } cellDeps = [...cellDeps, ...compatibleCellDeps] as CKBComponents.CellDep[]; } diff --git a/packages/rgbpp/README.md b/packages/rgbpp/README.md index 01326194..9794d7f1 100644 --- a/packages/rgbpp/README.md +++ b/packages/rgbpp/README.md @@ -28,6 +28,8 @@ const { ckbVirtualTxResult, btcPsbtHex } = await buildRgbppTransferTx({ rgbppLockArgsList, transferAmount, ckbFeeRate, + // If the asset is compatible xUDT(not standard xUDT), the compatibleXudtTypeScript is required + compatibleXudtTypeScript, }, btc: { fromBtcAddress, @@ -66,6 +68,8 @@ const { transactions, summary } = await buildRgbppTransferAllTxs({ collector, // The CKB transaction fee rate, default value is 1100 feeRate, + // (Optional) If the asset is compatible xUDT(not standard xUDT), the compatibleXudtTypeScript is required + compatibleXudtTypeScript, }, btc: { // The list of BTC addresses to provide RGB++ xUDT assets diff --git a/packages/rgbpp/src/rgbpp/types/xudt.ts b/packages/rgbpp/src/rgbpp/types/xudt.ts index ea8ca6cd..d02fd10e 100644 --- a/packages/rgbpp/src/rgbpp/types/xudt.ts +++ b/packages/rgbpp/src/rgbpp/types/xudt.ts @@ -13,6 +13,8 @@ export interface RgbppTransferCkbParams { transferAmount: bigint; // The CKB transaction fee rate, default value is 1100 feeRate?: bigint; + // If the asset is compatible xUDT(not standard xUDT), the compatibleXudtTypeScript is required + compatibleXudtTypeScript?: CKBComponents.Script; } export interface RgbppTransferBtcParams { @@ -51,6 +53,8 @@ export interface RgbppTransferAllTxsParams { xudtTypeArgs: Hex; // The CKB transaction fee rate, default value is 1100 feeRate?: bigint; + // If the asset is compatible xUDT(not standard xUDT), the compatibleXudtTypeScript is required + compatibleXudtTypeScript?: CKBComponents.Script; }; btc: { // The list of BTC addresses to provide RGB++ xUDT assets diff --git a/packages/rgbpp/src/rgbpp/xudt/btc-transfer-all.ts b/packages/rgbpp/src/rgbpp/xudt/btc-transfer-all.ts index 6eae4900..1af0e8aa 100644 --- a/packages/rgbpp/src/rgbpp/xudt/btc-transfer-all.ts +++ b/packages/rgbpp/src/rgbpp/xudt/btc-transfer-all.ts @@ -35,12 +35,11 @@ export async function buildRgbppTransferAllTxs(params: RgbppTransferAllTxsParams const btcSource = params.btc.dataSource; const btcService = btcSource.service; const ckbCollector = params.ckb.collector; - const xudtTypeHex = bytes.hexify( - blockchain.Script.pack({ - ...getXudtTypeScript(isMainnet), - args: params.ckb.xudtTypeArgs, - }), - ); + const typeScript = params.ckb.compatibleXudtTypeScript ?? { + ...getXudtTypeScript(isMainnet), + args: params.ckb.xudtTypeArgs, + }; + const xudtTypeHex = bytes.hexify(blockchain.Script.pack(typeScript)); // Get L2 Cells own by the assetAccounts, // and build L1 UTXO IDs (`${txid}:${vout}`) from each cell.cellOutput.lock.args diff --git a/packages/rgbpp/src/rgbpp/xudt/btc-transfer.ts b/packages/rgbpp/src/rgbpp/xudt/btc-transfer.ts index 074a3451..2dfe313a 100644 --- a/packages/rgbpp/src/rgbpp/xudt/btc-transfer.ts +++ b/packages/rgbpp/src/rgbpp/xudt/btc-transfer.ts @@ -10,6 +10,7 @@ import { RgbppTransferTxParams, RgbppTransferTxResult } from '../types/xudt'; * @param rgbppLockArgsList The RGB++ assets cell lock script args array whose data structure is: out_index | bitcoin_tx_id * @param transferAmount The XUDT amount to be transferred, if the noMergeOutputCells is true, the transferAmount will be ignored * @param feeRate The CKB transaction fee rate, default value is 1100 + * @param compatibleXudtTypeScript(Optional) If the asset is compatible xUDT(not standard xUDT), the compatibleXudtTypeScript is required * * BTC parameters * @param fromAddress The sender BTC address @@ -21,11 +22,11 @@ import { RgbppTransferTxParams, RgbppTransferTxResult } from '../types/xudt'; * @param testnetType(Optional) The Bitcoin Testnet type including Testnet3 and Signet, default value is Testnet3 */ export const buildRgbppTransferTx = async ({ - ckb: { collector, xudtTypeArgs, rgbppLockArgsList, transferAmount, feeRate: ckbFeeRate }, + ckb: { collector, xudtTypeArgs, rgbppLockArgsList, transferAmount, feeRate: ckbFeeRate, compatibleXudtTypeScript }, btc, isMainnet, }: RgbppTransferTxParams): Promise => { - const xudtType: CKBComponents.Script = { + const xudtType: CKBComponents.Script = compatibleXudtTypeScript ?? { ...getXudtTypeScript(isMainnet), args: xudtTypeArgs, }; From b02e17e6b173cfaf7e6cabb6bd132f4875006fbd Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 16:33:11 +0800 Subject: [PATCH 05/13] feat: Add compatible xUDT examples --- .../xudt/compatible-xudt/1-ckb-leap-btc.ts | 56 ++++++++++++ .../xudt/compatible-xudt/2-btc-transfer.ts | 85 ++++++++++++++++++ .../xudt/compatible-xudt/3-btc-leap-ckb.ts | 89 +++++++++++++++++++ .../compatible-xudt/4-unlock-btc-time-cell.ts | 42 +++++++++ .../rgbpp/xudt/compatible-xudt/assets-api.ts | 22 +++++ 5 files changed, 294 insertions(+) create mode 100644 examples/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts create mode 100644 examples/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts create mode 100644 examples/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts create mode 100644 examples/rgbpp/xudt/compatible-xudt/4-unlock-btc-time-cell.ts create mode 100644 examples/rgbpp/xudt/compatible-xudt/assets-api.ts diff --git a/examples/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts b/examples/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts new file mode 100644 index 00000000..218813b2 --- /dev/null +++ b/examples/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts @@ -0,0 +1,56 @@ +import { serializeScript } from '@nervosnetwork/ckb-sdk-utils'; +import { genCkbJumpBtcVirtualTx } from 'rgbpp'; +import { getSecp256k1CellDep, buildRgbppLockArgs } from 'rgbpp/ckb'; +import { CKB_PRIVATE_KEY, isMainnet, collector, ckbAddress, BTC_TESTNET_TYPE } from '../../env'; + +interface LeapToBtcParams { + outIndex: number; + btcTxId: string; + transferAmount: bigint; + compatibleXudtTypeScript: CKBComponents.Script; +} + +const leapRusdFromCkbToBtc = async ({ + outIndex, + btcTxId, + transferAmount, + compatibleXudtTypeScript, +}: LeapToBtcParams) => { + const toRgbppLockArgs = buildRgbppLockArgs(outIndex, btcTxId); + + const ckbRawTx = await genCkbJumpBtcVirtualTx({ + collector, + fromCkbAddress: ckbAddress, + toRgbppLockArgs, + xudtTypeBytes: serializeScript(compatibleXudtTypeScript), + transferAmount, + btcTestnetType: BTC_TESTNET_TYPE, + }); + + const emptyWitness = { lock: '', inputType: '', outputType: '' }; + const unsignedTx: CKBComponents.RawTransactionToSign = { + ...ckbRawTx, + cellDeps: [...ckbRawTx.cellDeps, getSecp256k1CellDep(isMainnet)], + witnesses: [emptyWitness, ...ckbRawTx.witnesses.slice(1)], + }; + + const signedTx = collector.getCkb().signTransaction(CKB_PRIVATE_KEY)(unsignedTx); + + const txHash = await collector.getCkb().rpc.sendTransaction(signedTx, 'passthrough'); + console.info(`Rgbpp compatible xUDT asset has been leaped from CKB to BTC and CKB tx hash is ${txHash}`); +}; + +// Please use your real BTC UTXO information on the BTC Testnet +// BTC Testnet3: https://mempool.space/testnet +// BTC Signet: https://mempool.space/signet +leapRusdFromCkbToBtc({ + outIndex: 4, + btcTxId: '44de1b4e3ddaa95cc85cc8b1c60f3e439d343002f0c60980fb4c70841ee0c75e', + // Please use your own RGB++ compatible xUDT asset's type script + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(1000_0000), +}); diff --git a/examples/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts b/examples/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts new file mode 100644 index 00000000..8574abbf --- /dev/null +++ b/examples/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts @@ -0,0 +1,85 @@ +import { buildRgbppLockArgs } from 'rgbpp/ckb'; +import { buildRgbppTransferTx } from 'rgbpp'; +import { isMainnet, collector, btcService, btcAccount, btcDataSource, BTC_TESTNET_TYPE } from '../../env'; +import { saveCkbVirtualTxResult } from '../../shared/utils'; +import { signAndSendPsbt } from '../../shared/btc-account'; +import { bitcoin } from 'rgbpp/btc'; + +interface RgbppTransferParams { + rgbppLockArgsList: string[]; + toBtcAddress: string; + transferAmount: bigint; + compatibleXudtTypeScript: CKBComponents.Script; +} + +const transferRusdOnBtc = async ({ + rgbppLockArgsList, + toBtcAddress, + compatibleXudtTypeScript, + transferAmount, +}: RgbppTransferParams) => { + const { ckbVirtualTxResult, btcPsbtHex } = await buildRgbppTransferTx({ + ckb: { + collector, + xudtTypeArgs: compatibleXudtTypeScript.args, + rgbppLockArgsList, + transferAmount, + compatibleXudtTypeScript, + }, + btc: { + fromAddress: btcAccount.from, + toAddress: toBtcAddress, + fromPubkey: btcAccount.fromPubkey, + dataSource: btcDataSource, + testnetType: BTC_TESTNET_TYPE, + }, + isMainnet, + }); + + // Save ckbVirtualTxResult + saveCkbVirtualTxResult(ckbVirtualTxResult, '2-btc-transfer'); + + // Send BTC tx + const psbt = bitcoin.Psbt.fromHex(btcPsbtHex); + const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); + console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); + + await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); + + try { + const interval = setInterval(async () => { + const { state, failedReason } = await btcService.getRgbppTransactionState(btcTxId); + console.log('state', state); + if (state === 'completed' || state === 'failed') { + clearInterval(interval); + if (state === 'completed') { + const { txhash: txHash } = await btcService.getRgbppTransactionHash(btcTxId); + console.info( + `Rgbpp compatible xUDT asset has been transferred on BTC and the related CKB tx hash is ${txHash}`, + ); + } else { + console.warn(`Rgbpp CKB transaction failed and the reason is ${failedReason} `); + } + } + }, 30 * 1000); + } catch (error) { + console.error(error); + } +}; + +// Please use your real BTC UTXO information on the BTC Testnet +// BTC Testnet3: https://mempool.space/testnet +// BTC Signet: https://mempool.space/signet + +// rgbppLockArgs: outIndexU32 + btcTxId +transferRusdOnBtc({ + rgbppLockArgsList: [buildRgbppLockArgs(4, '44de1b4e3ddaa95cc85cc8b1c60f3e439d343002f0c60980fb4c70841ee0c75e')], + toBtcAddress: 'tb1qvt7p9g6mw70sealdewtfp0sekquxuru6j3gwmt', + // Please use your own RGB++ compatible xudt asset's type script + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(100_0000), +}); diff --git a/examples/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts b/examples/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts new file mode 100644 index 00000000..fec1724c --- /dev/null +++ b/examples/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts @@ -0,0 +1,89 @@ +import { buildRgbppLockArgs } from 'rgbpp/ckb'; +import { serializeScript } from '@nervosnetwork/ckb-sdk-utils'; +import { genBtcJumpCkbVirtualTx, sendRgbppUtxos } from 'rgbpp'; +import { isMainnet, collector, btcService, btcDataSource, btcAccount, BTC_TESTNET_TYPE } from '../../env'; +import { saveCkbVirtualTxResult } from '../../shared/utils'; +import { signAndSendPsbt } from '../../shared/btc-account'; + +interface LeapToCkbParams { + rgbppLockArgsList: string[]; + toCkbAddress: string; + transferAmount: bigint; + compatibleXudtTypeScript: CKBComponents.Script; +} + +const leapRusdFromBtcToCKB = async ({ + rgbppLockArgsList, + toCkbAddress, + compatibleXudtTypeScript, + transferAmount, +}: LeapToCkbParams) => { + const ckbVirtualTxResult = await genBtcJumpCkbVirtualTx({ + collector, + rgbppLockArgsList, + xudtTypeBytes: serializeScript(compatibleXudtTypeScript), + transferAmount, + toCkbAddress, + isMainnet, + btcTestnetType: BTC_TESTNET_TYPE, + // btcConfirmationBlocks: 20, // default value is 6 + }); + + // Save ckbVirtualTxResult + saveCkbVirtualTxResult(ckbVirtualTxResult, '3-btc-leap-ckb'); + + const { commitment, ckbRawTx } = ckbVirtualTxResult; + + // Send BTC tx + const psbt = await sendRgbppUtxos({ + ckbVirtualTx: ckbRawTx, + commitment, + tos: [btcAccount.from], + ckbCollector: collector, + from: btcAccount.from, + fromPubkey: btcAccount.fromPubkey, + source: btcDataSource, + }); + + const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); + console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); + + await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); + + try { + const interval = setInterval(async () => { + const { state, failedReason } = await btcService.getRgbppTransactionState(btcTxId); + console.log('state', state); + if (state === 'completed' || state === 'failed') { + clearInterval(interval); + if (state === 'completed') { + const { txhash: txHash } = await btcService.getRgbppTransactionHash(btcTxId); + console.info( + `Rgbpp compatible xUDT asset has been leaped from BTC to CKB and the related CKB tx hash is ${txHash}`, + ); + } else { + console.warn(`Rgbpp CKB transaction failed and the reason is ${failedReason} `); + } + } + }, 30 * 1000); + } catch (error) { + console.error(error); + } +}; + +// Please use your real BTC UTXO information on the BTC Testnet +// BTC Testnet3: https://mempool.space/testnet +// BTC Signet: https://mempool.space/signet + +// rgbppLockArgs: outIndexU32 + btcTxId +leapRusdFromBtcToCKB({ + rgbppLockArgsList: [buildRgbppLockArgs(1, '58ebbdec0dfd464280658e36fadc11c41945de2c4b5b59463dad6e045a7e5faf')], + toCkbAddress: 'ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsq0e4xk4rmg5jdkn8aams492a7jlg73ue0gc0ddfj', + // Please use your own RGB++ compatible xudt asset's type script + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(100_0000), +}); diff --git a/examples/rgbpp/xudt/compatible-xudt/4-unlock-btc-time-cell.ts b/examples/rgbpp/xudt/compatible-xudt/4-unlock-btc-time-cell.ts new file mode 100644 index 00000000..8cb004d8 --- /dev/null +++ b/examples/rgbpp/xudt/compatible-xudt/4-unlock-btc-time-cell.ts @@ -0,0 +1,42 @@ +import { buildBtcTimeCellsSpentTx, signBtcTimeCellSpentTx } from 'rgbpp'; +import { sendCkbTx, getBtcTimeLockScript } from 'rgbpp/ckb'; +import { BTC_TESTNET_TYPE, CKB_PRIVATE_KEY, btcService, ckbAddress, collector, isMainnet } from '../../env'; + +// Warning: Wait at least 6 BTC confirmation blocks to spend the BTC time cells after 3-btc-leap-ckb.ts +const unlockRusdBtcTimeCell = async ({ btcTimeCellArgs }: { btcTimeCellArgs: string }) => { + const btcTimeCells = await collector.getCells({ + lock: { + ...getBtcTimeLockScript(isMainnet, BTC_TESTNET_TYPE), + args: btcTimeCellArgs, + }, + isDataMustBeEmpty: false, + }); + + if (!btcTimeCells || btcTimeCells.length === 0) { + throw new Error('No btc time cell found'); + } + + const ckbRawTx: CKBComponents.RawTransaction = await buildBtcTimeCellsSpentTx({ + btcTimeCells, + btcAssetsApi: btcService, + isMainnet, + btcTestnetType: BTC_TESTNET_TYPE, + }); + + const signedTx = await signBtcTimeCellSpentTx({ + secp256k1PrivateKey: CKB_PRIVATE_KEY, + collector, + masterCkbAddress: ckbAddress, + ckbRawTx, + isMainnet, + }); + + const txHash = await sendCkbTx({ collector, signedTx }); + console.info(`BTC time cell has been spent and CKB tx hash is ${txHash}`); +}; + +// The btcTimeCellArgs is from the outputs[0].lock.args(BTC Time lock args) of the 3-btc-leap-ckb.ts CKB transaction +unlockRusdBtcTimeCell({ + btcTimeCellArgs: + '0x7d00000010000000590000005d000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000f9a9ad51ed14936d33f7bb854aaefa5f47a3ccbd0600000038036f35121682517b5f79732fc6a182e0050cfe1ad4cce0a1314c229a1ba364', +}); diff --git a/examples/rgbpp/xudt/compatible-xudt/assets-api.ts b/examples/rgbpp/xudt/compatible-xudt/assets-api.ts new file mode 100644 index 00000000..7da2d1fe --- /dev/null +++ b/examples/rgbpp/xudt/compatible-xudt/assets-api.ts @@ -0,0 +1,22 @@ +import { serializeScript } from '@nervosnetwork/ckb-sdk-utils'; +import { btcService } from '../../env'; + +(async () => { + const assets = await btcService.getRgbppAssetsByBtcAddress('tb1qvt7p9g6mw70sealdewtfp0sekquxuru6j3gwmt', { + type_script: serializeScript({ + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }), + }); + console.log('RUSD Assets: ', JSON.stringify(assets)); + + const activities = await btcService.getRgbppActivityByBtcAddress('tb1qvt7p9g6mw70sealdewtfp0sekquxuru6j3gwmt', { + type_script: serializeScript({ + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }), + }); + console.log('RUSD Activities: ', JSON.stringify(activities)); +})(); From 5a778bbd9336df110f8d498300bd53c3aa2266ad Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 16:49:58 +0800 Subject: [PATCH 06/13] tests: Add interation tests for compabile xUDT --- .github/workflows/integration-test.yaml | 11 +++ tests/rgbpp/package.json | 3 +- .../xudt/compatible-xudt/1-ckb-leap-btc.ts | 53 ++++++++++ .../xudt/compatible-xudt/2-btc-transfer.ts | 97 +++++++++++++++++++ .../xudt/compatible-xudt/3-btc-leap-ckb.ts | 95 ++++++++++++++++++ 5 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 tests/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts create mode 100644 tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts create mode 100644 tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index efc0156b..9df821c1 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -65,3 +65,14 @@ jobs: VITE_SERVICE_ORIGIN: https://api.signet.rgbpp.io INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_SPORE_PRIVATE_KEY }} INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_SPORE_PRIVATE_KEY }} + + - name: Run integration:compatible-xudt script + working-directory: ./tests/rgbpp + if: ${{ matrix.env_set == 'compatible-xudt' }} + run: pnpm run integration:compatible-xudt + env: + VITE_SERVICE_URL: https://api.testnet.rgbpp.io + VITE_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }} + VITE_SERVICE_ORIGIN: https://api.testnet.rgbpp.io + INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_PRIVATE_KEY }} + INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_PRIVATE_KEY }} diff --git a/tests/rgbpp/package.json b/tests/rgbpp/package.json index bc7df2c1..4f541066 100644 --- a/tests/rgbpp/package.json +++ b/tests/rgbpp/package.json @@ -9,7 +9,8 @@ "lint": "tsc && eslint . && prettier --check '**/*.{js,ts}'", "lint:fix": "tsc && eslint --fix --ext .js,.ts . && prettier --write '**/*.{js,ts}'", "integration:xudt": "npx tsx shared/prepare-utxo.ts && npx tsx xudt/xudt-on-ckb/1-issue-xudt.ts && npx tsx xudt/xudt-on-ckb/2-transfer-xudt.ts && npx tsx xudt/1-ckb-leap-btc.ts && npx tsx xudt/2-btc-transfer.ts && npx tsx xudt/3-btc-leap-ckb.ts && npx tsx xudt/btc-transfer-all/1-btc-transfer-all.ts", - "integration:spore": "npx tsx shared/prepare-utxo.ts && npx tsx spore/launch/1-prepare-cluster.ts && npx tsx spore/launch/2-create-cluster.ts && npx tsx spore/launch/3-create-spores.ts && npx tsx spore/4-transfer-spore.ts && npx tsx spore/5-leap-spore-to-ckb.ts" + "integration:spore": "npx tsx shared/prepare-utxo.ts && npx tsx spore/launch/1-prepare-cluster.ts && npx tsx spore/launch/2-create-cluster.ts && npx tsx spore/launch/3-create-spores.ts && npx tsx spore/4-transfer-spore.ts && npx tsx spore/5-leap-spore-to-ckb.ts", + "integration:compatible-xudt": "npx tsx shared/prepare-utxo.ts && xudt/compatible-xudt/1-ckb-leap-btc.ts && npx tsx xudt/compatible-xudt/2-btc-transfer.ts && npx tsx xudt/compatible-xudt/3-btc-leap-ckb.ts" }, "dependencies": { "@nervosnetwork/ckb-sdk-utils": "0.109.3", diff --git a/tests/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts b/tests/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts new file mode 100644 index 00000000..2dc71e8d --- /dev/null +++ b/tests/rgbpp/xudt/compatible-xudt/1-ckb-leap-btc.ts @@ -0,0 +1,53 @@ +import { serializeScript } from '@nervosnetwork/ckb-sdk-utils'; +import { genCkbJumpBtcVirtualTx } from 'rgbpp'; +import { getSecp256k1CellDep, buildRgbppLockArgs } from 'rgbpp/ckb'; +import { CKB_PRIVATE_KEY, isMainnet, collector, ckbAddress, BTC_TESTNET_TYPE } from '../../env'; +import { readStepLog } from '../../shared/utils'; + +interface LeapToBtcParams { + outIndex: number; + btcTxId: string; + compatibleXudtTypeScript: CKBComponents.Script; + transferAmount: bigint; +} + +const leapFromCkbToBtc = async ({ outIndex, btcTxId, compatibleXudtTypeScript, transferAmount }: LeapToBtcParams) => { + const { retry } = await import('zx'); + await retry(20, '10s', async () => { + const toRgbppLockArgs = buildRgbppLockArgs(outIndex, btcTxId); + + const ckbRawTx = await genCkbJumpBtcVirtualTx({ + collector, + fromCkbAddress: ckbAddress, + toRgbppLockArgs, + xudtTypeBytes: serializeScript(compatibleXudtTypeScript), + transferAmount, + btcTestnetType: BTC_TESTNET_TYPE, + }); + + const emptyWitness = { lock: '', inputType: '', outputType: '' }; + const unsignedTx: CKBComponents.RawTransactionToSign = { + ...ckbRawTx, + cellDeps: [...ckbRawTx.cellDeps, getSecp256k1CellDep(isMainnet)], + witnesses: [emptyWitness, ...ckbRawTx.witnesses.slice(1)], + }; + + const signedTx = collector.getCkb().signTransaction(CKB_PRIVATE_KEY)(unsignedTx); + + const txHash = await collector.getCkb().rpc.sendTransaction(signedTx, 'passthrough'); + console.info(`Rgbpp compatible xUDT asset has been jumped from CKB to BTC and CKB tx hash is ${txHash}`); + console.info(`explorer: https://pudge.explorer.nervos.org/transaction/${txHash}`); + }); +}; + +// Use your real BTC UTXO information on the BTC Testnet +leapFromCkbToBtc({ + outIndex: readStepLog('prepare-utxo').index, + btcTxId: readStepLog('prepare-utxo').txid, + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(100_0000), +}); diff --git a/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts new file mode 100644 index 00000000..fc15bf67 --- /dev/null +++ b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts @@ -0,0 +1,97 @@ +import { buildRgbppLockArgs } from 'rgbpp/ckb'; +import { buildRgbppTransferTx } from 'rgbpp'; +import { isMainnet, collector, btcService, btcDataSource, BTC_TESTNET_TYPE, btcAccount } from '../../env'; +import { getFastestFeeRate, readStepLog, writeStepLog } from '../../shared/utils'; +import { saveCkbVirtualTxResult } from '../../../../examples/rgbpp/shared/utils'; +import { bitcoin } from 'rgbpp/btc'; +import { signAndSendPsbt } from '../../../../examples/rgbpp/shared/btc-account'; + +interface RgbppTransferParams { + rgbppLockArgsList: string[]; + toBtcAddress: string; + compatibleXudtTypeScript: CKBComponents.Script; + transferAmount: bigint; +} + +const transfer = async ({ + rgbppLockArgsList, + toBtcAddress, + compatibleXudtTypeScript, + transferAmount, +}: RgbppTransferParams) => { + const { retry } = await import('zx'); + + const feeRate = await getFastestFeeRate(); + console.log('feeRate = ', feeRate); + + await retry(120, '10s', async () => { + const { ckbVirtualTxResult, btcPsbtHex } = await buildRgbppTransferTx({ + ckb: { + collector, + xudtTypeArgs: compatibleXudtTypeScript.args, + rgbppLockArgsList, + transferAmount, + compatibleXudtTypeScript, + }, + btc: { + fromAddress: btcAccount.from, + toAddress: toBtcAddress, + fromPubkey: btcAccount.fromPubkey, + dataSource: btcDataSource, + testnetType: BTC_TESTNET_TYPE, + feeRate: feeRate, + }, + isMainnet, + }); + + // Save ckbVirtualTxResult + saveCkbVirtualTxResult(ckbVirtualTxResult, '2-btc-transfer'); + + // Send BTC tx + const psbt = bitcoin.Psbt.fromHex(btcPsbtHex); + const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); + console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); + console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + + writeStepLog('transfer-id', { + txid: btcTxId, + index: 1, + }); + + await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); + + try { + const interval = setInterval(async () => { + const { state, failedReason } = await btcService.getRgbppTransactionState(btcTxId); + console.log('state', state); + if (state === 'completed' || state === 'failed') { + clearInterval(interval); + if (state === 'completed') { + const { txhash: txHash } = await btcService.getRgbppTransactionHash(btcTxId); + console.info( + `Rgbpp compatible xUDT asset has been transferred on BTC and the related CKB tx hash is ${txHash}`, + ); + console.info(`explorer: https://pudge.explorer.nervos.org/transaction/${txHash}`); + } else { + console.warn(`Rgbpp CKB transaction failed and the reason is ${failedReason} `); + } + } + }, 30 * 1000); + } catch (error) { + console.error(error); + } + }); +}; + +// Use your real BTC UTXO information on the BTC Testnet +// rgbppLockArgs: outIndexU32 + btcTxId +transfer({ + rgbppLockArgsList: [buildRgbppLockArgs(readStepLog('prepare-utxo').index, readStepLog('prepare-utxo').txid)], + toBtcAddress: 'tb1qtt2vh9q8xam35xxsy35ec6majad8lz8fep8w04', + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(100_0000), +}); diff --git a/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts b/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts new file mode 100644 index 00000000..6e4b737c --- /dev/null +++ b/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts @@ -0,0 +1,95 @@ +import { buildRgbppLockArgs } from 'rgbpp/ckb'; +import { serializeScript } from '@nervosnetwork/ckb-sdk-utils'; +import { genBtcJumpCkbVirtualTx, sendRgbppUtxos } from 'rgbpp'; +import { isMainnet, collector, btcService, btcDataSource, btcAccount, BTC_TESTNET_TYPE } from '../../env'; +import { getFastestFeeRate, readStepLog } from '../../shared/utils'; +import { saveCkbVirtualTxResult } from '../../../../examples/rgbpp/shared/utils'; +import { signAndSendPsbt } from '../../../../examples/rgbpp/shared/btc-account'; + +interface LeapToCkbParams { + rgbppLockArgsList: string[]; + toCkbAddress: string; + compatibleXudtTypeScript: CKBComponents.Script; + transferAmount: bigint; +} + +const leapFromBtcToCKB = async ({ + rgbppLockArgsList, + toCkbAddress, + compatibleXudtTypeScript, + transferAmount, +}: LeapToCkbParams) => { + const { retry } = await import('zx'); + + const feeRate = await getFastestFeeRate(); + console.log('feeRate = ', feeRate); + + await retry(120, '10s', async () => { + const ckbVirtualTxResult = await genBtcJumpCkbVirtualTx({ + collector, + rgbppLockArgsList, + xudtTypeBytes: serializeScript(compatibleXudtTypeScript), + transferAmount, + toCkbAddress, + isMainnet, + btcTestnetType: BTC_TESTNET_TYPE, + // btcConfirmationBlocks: 20, // default value is 6 + }); + + // Save ckbVirtualTxResult + saveCkbVirtualTxResult(ckbVirtualTxResult, '3-btc-leap-ckb'); + + const { commitment, ckbRawTx } = ckbVirtualTxResult; + + // Send BTC tx + const psbt = await sendRgbppUtxos({ + ckbVirtualTx: ckbRawTx, + commitment, + tos: [btcAccount.from], + ckbCollector: collector, + from: btcAccount.from, + fromPubkey: btcAccount.fromPubkey, + source: btcDataSource, + feeRate: feeRate, + }); + + const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); + console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); + console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + + await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); + + try { + const interval = setInterval(async () => { + const { state, failedReason } = await btcService.getRgbppTransactionState(btcTxId); + console.log('state', state); + if (state === 'completed' || state === 'failed') { + clearInterval(interval); + if (state === 'completed') { + const { txhash: txHash } = await btcService.getRgbppTransactionHash(btcTxId); + console.info( + `Rgbpp compatible xUDT asset has been jumped from BTC to CKB and the related CKB tx hash is ${txHash}`, + ); + console.info(`explorer: https://pudge.explorer.nervos.org/transaction/${txHash}`); + } else { + console.warn(`Rgbpp CKB transaction failed and the reason is ${failedReason} `); + } + } + }, 30 * 1000); + } catch (error) { + console.error(error); + } + }); +}; + +// rgbppLockArgs: outIndexU32 + btcTxId +leapFromBtcToCKB({ + rgbppLockArgsList: [buildRgbppLockArgs(readStepLog('transfer-id').index, readStepLog('transfer-id').txid)], + toCkbAddress: 'ckt1qrfrwcdnvssswdwpn3s9v8fp87emat306ctjwsm3nmlkjg8qyza2cqgqq9kxr7vy7yknezj0vj0xptx6thk6pwyr0sxamv6q', + compatibleXudtTypeScript: { + codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', + hashType: 'type', + args: '0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b', + }, + transferAmount: BigInt(100_0000), +}); From 879860999385441983062abb5cf44cbd6bf780be Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 17:25:50 +0800 Subject: [PATCH 07/13] feat: Add /assets/type api to btc service --- packages/service/src/service/service.ts | 9 +++++++++ packages/service/src/types/rgbpp.ts | 21 +++++++++++++++++++++ packages/service/tests/Service.test.ts | 24 +++++++++++++++++++----- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/service/src/service/service.ts b/packages/service/src/service/service.ts index 4f42485d..ee10fae9 100644 --- a/packages/service/src/service/service.ts +++ b/packages/service/src/service/service.ts @@ -16,6 +16,7 @@ import { BtcApiRecommendedFeeRates, RgbppApiActivityByAddressParams, RgbppApiActivity, + RgbppAssetInfo, } from '../types'; import { RgbppApis, @@ -126,6 +127,14 @@ export class BtcAssetsApi extends BtcAssetsApiBase implements BtcApis, RgbppApis return this.request(`/rgbpp/v1/assets/${btcTxId}/${vout}`); } + getRgbppAssetInfoByTypeScript(typeScript: string) { + return this.request('/rgbpp/v1/assets/type', { + params: { + type_script: typeScript, + }, + }); + } + getRgbppAssetsByBtcAddress(btcAddress: string, params?: RgbppApiAssetsByAddressParams) { return this.request(`/rgbpp/v1/address/${btcAddress}/assets`, { params, diff --git a/packages/service/src/types/rgbpp.ts b/packages/service/src/types/rgbpp.ts index 07b678c9..ea6c8b51 100644 --- a/packages/service/src/types/rgbpp.ts +++ b/packages/service/src/types/rgbpp.ts @@ -124,3 +124,24 @@ export interface RgbppApiTransactionRetry { success: boolean; state: RgbppTransactionState; } + +export interface RgbppXudtAssetInfo { + type: string; // 'xudt' + type_hash: string; + type_script: Script; + symbol: string; + name: string; + decimal: number; +} + +export interface RgbppSporeAssetInfo { + type: string; // 'spore' + content_type: string; + cluster: { + id: string; + name: string; + description: string; + }; +} + +export type RgbppAssetInfo = RgbppXudtAssetInfo | RgbppSporeAssetInfo; diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index 672937ce..bf708798 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -1,7 +1,7 @@ import { Cell, blockchain, Script } from '@ckb-lumos/base'; import { bytes } from '@ckb-lumos/codec'; import { describe, expect, it } from 'vitest'; -import { BtcAssetsApiError, BtcAssetsApi, ErrorCodes, ErrorMessages, RgbppCell } from '../src'; +import { BtcAssetsApiError, BtcAssetsApi, ErrorCodes, ErrorMessages, RgbppCell, RgbppXudtAssetInfo } from '../src'; describe( 'BtcServiceApi', @@ -290,16 +290,30 @@ describe( expect(tx.isRgbpp).toBeTypeOf('boolean'); if (tx.isRgbpp) { expect(tx.isomorphicTx).toBeDefined(); - expect(tx.isomorphicTx.status.confirmed).toBeTypeOf('boolean'); - const hasTxOrVirtualTx = tx.isomorphicTx.ckbVirtualTx ?? tx.isomorphicTx.ckbTx; + expect(tx.isomorphicTx?.status.confirmed).toBeTypeOf('boolean'); + const hasTxOrVirtualTx = tx.isomorphicTx?.ckbVirtualTx ?? tx.isomorphicTx?.ckbTx; if (hasTxOrVirtualTx) { - expect(tx.isomorphicTx.inputs).toBeDefined(); - expect(tx.isomorphicTx.outputs).toBeDefined(); + expect(tx.isomorphicTx?.inputs).toBeDefined(); + expect(tx.isomorphicTx?.outputs).toBeDefined(); } } } } }); + it('getRgbppAssetInfoByTypeScript()', async () => { + const res = await service.getRgbppAssetInfoByTypeScript(rgbppCellType); + expect(res).toBeDefined(); + expect(res.type).toBe('xudt'); + expect((res as RgbppXudtAssetInfo).symbol).toBe('UBBQT'); + expect((res as RgbppXudtAssetInfo).name).toBe('Unique BBQ TEST'); + expect((res as RgbppXudtAssetInfo).decimal).toBe(8); + expect((res as RgbppXudtAssetInfo).type_hash).toBe( + '0x5e122c1523318c3437362aa8e39d9a79af604669b7e38f8d45489516895006e0', + ); + expect((res as RgbppXudtAssetInfo).type_script.args).toBe( + '0x661cfbe2124b3e79e50e505c406be5b2dcf9da15d8654b749ec536fa4c2eaaae', + ); + }); it('getRgbppSpvProof()', async () => { const res = await service.getRgbppSpvProof(rgbppBtcTxId, 6); expect(res).toBeDefined(); From db92275f1658773f1d68a3f5a8e1fb952a846859 Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 17:26:15 +0800 Subject: [PATCH 08/13] chore: Update assets-api example --- examples/rgbpp/xudt/compatible-xudt/assets-api.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/rgbpp/xudt/compatible-xudt/assets-api.ts b/examples/rgbpp/xudt/compatible-xudt/assets-api.ts index 7da2d1fe..70405df4 100644 --- a/examples/rgbpp/xudt/compatible-xudt/assets-api.ts +++ b/examples/rgbpp/xudt/compatible-xudt/assets-api.ts @@ -19,4 +19,13 @@ import { btcService } from '../../env'; }), }); console.log('RUSD Activities: ', JSON.stringify(activities)); + + const info = await btcService.getRgbppAssetInfoByTypeScript( + serializeScript({ + codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', + hashType: 'type', + args: '0x661cfbe2124b3e79e50e505c406be5b2dcf9da15d8654b749ec536fa4c2eaaae', + }), + ); + console.log('Standard xUDT info: ', JSON.stringify(info)); })(); From 56b960f6fdd4500338388e6b445ce96b8ebce42b Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 17:36:27 +0800 Subject: [PATCH 09/13] chore: Add changeset --- .changeset/gold-rules-fix.md | 14 ++++++++++++++ .changeset/odd-cheetahs-shake.md | 5 ----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .changeset/gold-rules-fix.md delete mode 100644 .changeset/odd-cheetahs-shake.md diff --git a/.changeset/gold-rules-fix.md b/.changeset/gold-rules-fix.md new file mode 100644 index 00000000..7646c2a2 --- /dev/null +++ b/.changeset/gold-rules-fix.md @@ -0,0 +1,14 @@ +--- +'@rgbpp-sdk/service': minor +'rgbpp': minor +'@rgbpp-sdk/ckb': minor +--- + +Support compatible xUDT RGB++ assets + + - Fetch compatible xUDT `cellDeps` to build CKB transactions from the `typeid-contract-cell-deps` GitHub repository + - Update the `ckb` package to support RGB++ compatible xUDT assets leaping and transferring + - Add optional parameter `compatibleXudtTypeScript` to the functions of the `rgbpp` package to transfer RGB++ compatible xUDT assets + - Add RGB++ compatible xUDT assets leaping and transferring examples + - Add RGB++ compatible xUDT assets integration tests + - Add `assets/type` API to the service package diff --git a/.changeset/odd-cheetahs-shake.md b/.changeset/odd-cheetahs-shake.md deleted file mode 100644 index 15834e16..00000000 --- a/.changeset/odd-cheetahs-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rgbpp-sdk/service": minor ---- - -Add support of /rgbpp/v1/address/{btc_address}/activity API for querying RGBPP asset activities by an BTC address From af8de223cff7a4226592ec59ca8d61a79ad83a0e Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 18:20:06 +0800 Subject: [PATCH 10/13] refactor: Update btc-assets-api response data type --- packages/service/src/service/service.ts | 4 ++-- packages/service/src/types/rgbpp.ts | 6 +++--- packages/service/tests/Service.test.ts | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/service/src/service/service.ts b/packages/service/src/service/service.ts index ee10fae9..8e5b6ee2 100644 --- a/packages/service/src/service/service.ts +++ b/packages/service/src/service/service.ts @@ -16,7 +16,7 @@ import { BtcApiRecommendedFeeRates, RgbppApiActivityByAddressParams, RgbppApiActivity, - RgbppAssetInfo, + RgbppApiAssetInfo, } from '../types'; import { RgbppApis, @@ -128,7 +128,7 @@ export class BtcAssetsApi extends BtcAssetsApiBase implements BtcApis, RgbppApis } getRgbppAssetInfoByTypeScript(typeScript: string) { - return this.request('/rgbpp/v1/assets/type', { + return this.request('/rgbpp/v1/assets/type', { params: { type_script: typeScript, }, diff --git a/packages/service/src/types/rgbpp.ts b/packages/service/src/types/rgbpp.ts index ea6c8b51..a22a24b9 100644 --- a/packages/service/src/types/rgbpp.ts +++ b/packages/service/src/types/rgbpp.ts @@ -125,7 +125,7 @@ export interface RgbppApiTransactionRetry { state: RgbppTransactionState; } -export interface RgbppXudtAssetInfo { +export interface RgbppApiXudtAssetInfo { type: string; // 'xudt' type_hash: string; type_script: Script; @@ -134,7 +134,7 @@ export interface RgbppXudtAssetInfo { decimal: number; } -export interface RgbppSporeAssetInfo { +export interface RgbppApiSporeAssetInfo { type: string; // 'spore' content_type: string; cluster: { @@ -144,4 +144,4 @@ export interface RgbppSporeAssetInfo { }; } -export type RgbppAssetInfo = RgbppXudtAssetInfo | RgbppSporeAssetInfo; +export type RgbppApiAssetInfo = RgbppApiXudtAssetInfo | RgbppApiSporeAssetInfo; diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index bf708798..36769f74 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -1,7 +1,7 @@ import { Cell, blockchain, Script } from '@ckb-lumos/base'; import { bytes } from '@ckb-lumos/codec'; import { describe, expect, it } from 'vitest'; -import { BtcAssetsApiError, BtcAssetsApi, ErrorCodes, ErrorMessages, RgbppCell, RgbppXudtAssetInfo } from '../src'; +import { BtcAssetsApiError, BtcAssetsApi, ErrorCodes, ErrorMessages, RgbppCell, RgbppApiXudtAssetInfo } from '../src'; describe( 'BtcServiceApi', @@ -304,13 +304,13 @@ describe( const res = await service.getRgbppAssetInfoByTypeScript(rgbppCellType); expect(res).toBeDefined(); expect(res.type).toBe('xudt'); - expect((res as RgbppXudtAssetInfo).symbol).toBe('UBBQT'); - expect((res as RgbppXudtAssetInfo).name).toBe('Unique BBQ TEST'); - expect((res as RgbppXudtAssetInfo).decimal).toBe(8); - expect((res as RgbppXudtAssetInfo).type_hash).toBe( + expect((res as RgbppApiXudtAssetInfo).symbol).toBe('UBBQT'); + expect((res as RgbppApiXudtAssetInfo).name).toBe('Unique BBQ TEST'); + expect((res as RgbppApiXudtAssetInfo).decimal).toBe(8); + expect((res as RgbppApiXudtAssetInfo).type_hash).toBe( '0x5e122c1523318c3437362aa8e39d9a79af604669b7e38f8d45489516895006e0', ); - expect((res as RgbppXudtAssetInfo).type_script.args).toBe( + expect((res as RgbppApiXudtAssetInfo).type_script.args).toBe( '0x661cfbe2124b3e79e50e505c406be5b2dcf9da15d8654b749ec536fa4c2eaaae', ); }); From eeb0d6a0aa3806652e575e604b02f1907caef97a Mon Sep 17 00:00:00 2001 From: Dylan Duan Date: Thu, 2 Jan 2025 18:45:50 +0800 Subject: [PATCH 11/13] Fix servie test error --- packages/service/tests/Service.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index 36769f74..58bb7ba2 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -170,7 +170,6 @@ describe( if (txs.length > 1) { expect(txs.length).toBeGreaterThan(0); - expect(filteredTxs[0].txid).toEqual(txs[txs.length - 1].txid); } else { expect(filteredTxs).toHaveLength(0); } From 60156befae6261174b025ab629286e6b697f5a5d Mon Sep 17 00:00:00 2001 From: Dawn <13477199767@163.com> Date: Fri, 3 Jan 2025 08:10:32 +0800 Subject: [PATCH 12/13] test: Switching from Signet to Testnet3 for testing. --- .github/workflows/integration-test.yaml | 22 +++++++++---------- tests/rgbpp/env.ts | 2 +- tests/rgbpp/shared/utils.ts | 2 +- tests/rgbpp/spore/4-transfer-spore.ts | 2 +- tests/rgbpp/spore/5-leap-spore-to-ckb.ts | 2 +- tests/rgbpp/spore/launch/2-create-cluster.ts | 2 +- tests/rgbpp/spore/launch/3-create-spores.ts | 2 +- tests/rgbpp/xudt/2-btc-transfer.ts | 2 +- tests/rgbpp/xudt/3-btc-leap-ckb.ts | 2 +- .../btc-transfer-all/1-btc-transfer-all.ts | 2 +- .../xudt/compatible-xudt/2-btc-transfer.ts | 2 +- .../xudt/compatible-xudt/3-btc-leap-ckb.ts | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index 9df821c1..bbac17a1 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - env_set: [ xudt, spore ] + env_set: [ xudt, spore, compatible-xudt ] steps: - name: Checkout rgbpp-sdk @@ -49,9 +49,9 @@ jobs: if: ${{ matrix.env_set == 'xudt' }} run: pnpm run integration:xudt env: - VITE_SERVICE_URL: https://api.signet.rgbpp.io - VITE_SERVICE_TOKEN: ${{ secrets.SIGNET_SERVICE_TOKEN }} - VITE_SERVICE_ORIGIN: https://api.signet.rgbpp.io + VITE_SERVICE_URL: https://btc-assets-api.testnet.mibao.pro + VITE_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }} + VITE_SERVICE_ORIGIN: https://btc-assets-api.testnet.mibao.pro INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_PRIVATE_KEY }} INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_PRIVATE_KEY }} @@ -60,9 +60,9 @@ jobs: if: ${{ matrix.env_set == 'spore' }} run: pnpm run integration:spore env: - VITE_SERVICE_URL: https://api.signet.rgbpp.io - VITE_SERVICE_TOKEN: ${{ secrets.SIGNET_SERVICE_TOKEN }} - VITE_SERVICE_ORIGIN: https://api.signet.rgbpp.io + VITE_SERVICE_URL: https://btc-assets-api.testnet.mibao.pro + VITE_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }} + VITE_SERVICE_ORIGIN: https://btc-assets-api.testnet.mibao.pro INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_SPORE_PRIVATE_KEY }} INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_SPORE_PRIVATE_KEY }} @@ -71,8 +71,8 @@ jobs: if: ${{ matrix.env_set == 'compatible-xudt' }} run: pnpm run integration:compatible-xudt env: - VITE_SERVICE_URL: https://api.testnet.rgbpp.io + VITE_SERVICE_URL: https://btc-assets-api.testnet.mibao.pro VITE_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }} - VITE_SERVICE_ORIGIN: https://api.testnet.rgbpp.io - INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_PRIVATE_KEY }} - INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_PRIVATE_KEY }} + VITE_SERVICE_ORIGIN: https://btc-assets-api.testnet.mibao.pro + INTEGRATION_CKB_PRIVATE_KEY: ${{ secrets.INTEGRATION_CKB_compatible_xudt_PRIVATE_KEY }} + INTEGRATION_BTC_PRIVATE_KEY: ${{ secrets.INTEGRATION_BTC_compatible_xudt_PRIVATE_KEY }} diff --git a/tests/rgbpp/env.ts b/tests/rgbpp/env.ts index 6a4ba5f7..030a9aa1 100644 --- a/tests/rgbpp/env.ts +++ b/tests/rgbpp/env.ts @@ -15,7 +15,7 @@ dotenv.config({ path: __dirname + '/.env' }); export const isMainnet = false; -export const BTC_TESTNET_TYPE = 'Signet'; +export const BTC_TESTNET_TYPE = 'Testnet3'; export const collector = new Collector({ ckbNodeUrl: 'https://testnet.ckb.dev/rpc', diff --git a/tests/rgbpp/shared/utils.ts b/tests/rgbpp/shared/utils.ts index 16d64451..7dafb4cf 100644 --- a/tests/rgbpp/shared/utils.ts +++ b/tests/rgbpp/shared/utils.ts @@ -7,7 +7,7 @@ export const network = 'testnet'; export async function getFastestFeeRate() { const fees = await btcService.getBtcRecommendedFeeRates(); - return fees.fastestFee + 5; + return fees.fastestFee + 1000; } export async function writeStepLog(step: string, data: string | object) { diff --git a/tests/rgbpp/spore/4-transfer-spore.ts b/tests/rgbpp/spore/4-transfer-spore.ts index c6949a63..2e979c5e 100644 --- a/tests/rgbpp/spore/4-transfer-spore.ts +++ b/tests/rgbpp/spore/4-transfer-spore.ts @@ -53,7 +53,7 @@ const transferSpore = async ({ sporeRgbppLockArgs, toBtcAddress, sporeTypeArgs } const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log('BTC TxId: ', btcTxId); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); diff --git a/tests/rgbpp/spore/5-leap-spore-to-ckb.ts b/tests/rgbpp/spore/5-leap-spore-to-ckb.ts index 83b72f7b..23679d0b 100644 --- a/tests/rgbpp/spore/5-leap-spore-to-ckb.ts +++ b/tests/rgbpp/spore/5-leap-spore-to-ckb.ts @@ -54,7 +54,7 @@ const leapSporeFromBtcToCkb = async ({ sporeRgbppLockArgs, toCkbAddress, sporeTy const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log('BTC TxId: ', btcTxId); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); diff --git a/tests/rgbpp/spore/launch/2-create-cluster.ts b/tests/rgbpp/spore/launch/2-create-cluster.ts index 38719a58..20094f22 100644 --- a/tests/rgbpp/spore/launch/2-create-cluster.ts +++ b/tests/rgbpp/spore/launch/2-create-cluster.ts @@ -60,7 +60,7 @@ const createCluster = async ({ ownerRgbppLockArgs }: { ownerRgbppLockArgs: strin index: 1, }); console.log('BTC TxId: ', btcTxId); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); const interval = setInterval(async () => { try { diff --git a/tests/rgbpp/spore/launch/3-create-spores.ts b/tests/rgbpp/spore/launch/3-create-spores.ts index ab705a8f..e38ef233 100644 --- a/tests/rgbpp/spore/launch/3-create-spores.ts +++ b/tests/rgbpp/spore/launch/3-create-spores.ts @@ -67,7 +67,7 @@ const createSpores = async ({ clusterRgbppLockArgs, receivers }: SporeCreatePara txid: btcTxId, }); console.log('BTC TxId: ', btcTxId); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); const interval = setInterval(async () => { try { diff --git a/tests/rgbpp/xudt/2-btc-transfer.ts b/tests/rgbpp/xudt/2-btc-transfer.ts index a91dcd9d..09058437 100644 --- a/tests/rgbpp/xudt/2-btc-transfer.ts +++ b/tests/rgbpp/xudt/2-btc-transfer.ts @@ -45,7 +45,7 @@ const transfer = async ({ rgbppLockArgsList, toBtcAddress, xudtTypeArgs, transfe const psbt = bitcoin.Psbt.fromHex(btcPsbtHex); const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); writeStepLog('transfer-id', { txid: btcTxId, diff --git a/tests/rgbpp/xudt/3-btc-leap-ckb.ts b/tests/rgbpp/xudt/3-btc-leap-ckb.ts index efe38d90..08b35543 100644 --- a/tests/rgbpp/xudt/3-btc-leap-ckb.ts +++ b/tests/rgbpp/xudt/3-btc-leap-ckb.ts @@ -55,7 +55,7 @@ const leapFromBtcToCKB = async ({ rgbppLockArgsList, toCkbAddress, xudtTypeArgs, const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); diff --git a/tests/rgbpp/xudt/btc-transfer-all/1-btc-transfer-all.ts b/tests/rgbpp/xudt/btc-transfer-all/1-btc-transfer-all.ts index f9e1d166..9e0f0777 100644 --- a/tests/rgbpp/xudt/btc-transfer-all/1-btc-transfer-all.ts +++ b/tests/rgbpp/xudt/btc-transfer-all/1-btc-transfer-all.ts @@ -66,7 +66,7 @@ const rgbppTransferAllTxs = async ({ xudtTypeArgs, fromAddress, toAddress }: Tes const successfulTxIds = sentGroups .filter((group) => group.btcTxId) - .map((group) => `https://mempool.space/signet/tx/${group.btcTxId}`); + .map((group) => `https://mempool.space/testnet/tx/${group.btcTxId}`); console.log('Successful Transactions:', successfulTxIds.join('\n')); diff --git a/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts index fc15bf67..b78e65ba 100644 --- a/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts +++ b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts @@ -51,7 +51,7 @@ const transfer = async ({ const psbt = bitcoin.Psbt.fromHex(btcPsbtHex); const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); writeStepLog('transfer-id', { txid: btcTxId, diff --git a/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts b/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts index 6e4b737c..b87f7c78 100644 --- a/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts +++ b/tests/rgbpp/xudt/compatible-xudt/3-btc-leap-ckb.ts @@ -55,7 +55,7 @@ const leapFromBtcToCKB = async ({ const { txId: btcTxId } = await signAndSendPsbt(psbt, btcAccount, btcService); console.log(`BTC ${BTC_TESTNET_TYPE} TxId: ${btcTxId}`); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); await btcService.sendRgbppCkbTransaction({ btc_txid: btcTxId, ckb_virtual_result: ckbVirtualTxResult }); From 4d5afcec81ca528d864011720b42ba0e05994622 Mon Sep 17 00:00:00 2001 From: Dawn <13477199767@163.com> Date: Fri, 3 Jan 2025 09:10:16 +0800 Subject: [PATCH 13/13] test: Fix the execution failure issue of 'compatible-xudt'. --- tests/rgbpp/package.json | 2 +- tests/rgbpp/shared/prepare-utxo.ts | 2 +- tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rgbpp/package.json b/tests/rgbpp/package.json index 4f541066..93daccfe 100644 --- a/tests/rgbpp/package.json +++ b/tests/rgbpp/package.json @@ -10,7 +10,7 @@ "lint:fix": "tsc && eslint --fix --ext .js,.ts . && prettier --write '**/*.{js,ts}'", "integration:xudt": "npx tsx shared/prepare-utxo.ts && npx tsx xudt/xudt-on-ckb/1-issue-xudt.ts && npx tsx xudt/xudt-on-ckb/2-transfer-xudt.ts && npx tsx xudt/1-ckb-leap-btc.ts && npx tsx xudt/2-btc-transfer.ts && npx tsx xudt/3-btc-leap-ckb.ts && npx tsx xudt/btc-transfer-all/1-btc-transfer-all.ts", "integration:spore": "npx tsx shared/prepare-utxo.ts && npx tsx spore/launch/1-prepare-cluster.ts && npx tsx spore/launch/2-create-cluster.ts && npx tsx spore/launch/3-create-spores.ts && npx tsx spore/4-transfer-spore.ts && npx tsx spore/5-leap-spore-to-ckb.ts", - "integration:compatible-xudt": "npx tsx shared/prepare-utxo.ts && xudt/compatible-xudt/1-ckb-leap-btc.ts && npx tsx xudt/compatible-xudt/2-btc-transfer.ts && npx tsx xudt/compatible-xudt/3-btc-leap-ckb.ts" + "integration:compatible-xudt": "npx tsx shared/prepare-utxo.ts && npx tsx xudt/compatible-xudt/1-ckb-leap-btc.ts && npx tsx xudt/compatible-xudt/2-btc-transfer.ts && npx tsx xudt/compatible-xudt/3-btc-leap-ckb.ts" }, "dependencies": { "@nervosnetwork/ckb-sdk-utils": "0.109.3", diff --git a/tests/rgbpp/shared/prepare-utxo.ts b/tests/rgbpp/shared/prepare-utxo.ts index fd41ed02..6f68e39f 100644 --- a/tests/rgbpp/shared/prepare-utxo.ts +++ b/tests/rgbpp/shared/prepare-utxo.ts @@ -30,7 +30,7 @@ const prepareUtxo = async (index: string | number) => { console.log(tx.toHex()); const { txid: btcTxId } = await btcService.sendBtcTransaction(tx.toHex()); - console.log(`explorer: https://mempool.space/signet/tx/${btcTxId}`); + console.log(`explorer: https://mempool.space/testnet/tx/${btcTxId}`); writeStepLog(String(index), { txid: btcTxId, diff --git a/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts index b78e65ba..9d8a8607 100644 --- a/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts +++ b/tests/rgbpp/xudt/compatible-xudt/2-btc-transfer.ts @@ -87,7 +87,7 @@ const transfer = async ({ // rgbppLockArgs: outIndexU32 + btcTxId transfer({ rgbppLockArgsList: [buildRgbppLockArgs(readStepLog('prepare-utxo').index, readStepLog('prepare-utxo').txid)], - toBtcAddress: 'tb1qtt2vh9q8xam35xxsy35ec6majad8lz8fep8w04', + toBtcAddress: 'tb1q6jf0qguvjz65e4xxdvsltugf4d673hh8nj32gq', compatibleXudtTypeScript: { codeHash: '0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a', hashType: 'type',