-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support for batch transferring of RGBPP XUDT assets
- Loading branch information
1 parent
25c8459
commit 3c0eb05
Showing
17 changed files
with
1,094 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export enum RgbppErrorCodes { | ||
UNKNOWN, | ||
|
||
CANNOT_DECODE_UTXO_ID = 20, | ||
UNEXPECTED_CKB_VTX_OUTPUTS_LENGTH, | ||
} | ||
|
||
export const RgbppErrorMessages = { | ||
[RgbppErrorCodes.UNKNOWN]: 'Unknown error', | ||
|
||
[RgbppErrorCodes.CANNOT_DECODE_UTXO_ID]: 'Cannot decode UtxoId', | ||
[RgbppErrorCodes.UNEXPECTED_CKB_VTX_OUTPUTS_LENGTH]: 'Unexpected length of the CkbVirtualTx outputs', | ||
}; | ||
|
||
export class RgbppError extends Error { | ||
public code = RgbppErrorCodes.UNKNOWN; | ||
constructor(code: RgbppErrorCodes, message = RgbppErrorMessages[code] || 'Unknown error') { | ||
super(message); | ||
this.code = code; | ||
Object.setPrototypeOf(this, RgbppError.prototype); | ||
} | ||
|
||
static withComment(code: RgbppErrorCodes, comment?: string): RgbppError { | ||
const message: string | undefined = RgbppErrorMessages[code]; | ||
return new RgbppError(code, comment ? `${message}: ${comment}` : message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { Cell } from '@ckb-lumos/base'; | ||
import { Utxo } from '@rgbpp-sdk/btc'; | ||
import { leToU128 } from '@rgbpp-sdk/ckb'; | ||
import { encodeCellId } from '../utils/ckb'; | ||
import { encodeUtxoId } from '../utils/btc'; | ||
|
||
export interface AssetSummary { | ||
amount: bigint; | ||
utxos: number; | ||
cells: number; | ||
} | ||
|
||
export interface AssetGroupSummary { | ||
utxoId: string; | ||
cellIds: string[]; | ||
assets: Record<string, AssetSummary>; | ||
} | ||
|
||
export interface TransactionGroupSummary { | ||
utxos: number; | ||
cells: number; | ||
utxoIds: string[]; | ||
cellIds: string[]; | ||
assets: Record<string, AssetSummary>; | ||
} | ||
|
||
export class AssetSummarizer { | ||
groups: AssetGroupSummary[] = []; | ||
|
||
constructor() {} | ||
|
||
addGroup(utxo: Utxo, cells: Cell[]): AssetGroupSummary { | ||
const utxoId = encodeUtxoId(utxo.txid, utxo.vout); | ||
const assets: Record<string, Omit<AssetSummary, 'xudtTypeArgs'>> = {}; | ||
const cellIds: string[] = []; | ||
|
||
for (const cell of cells) { | ||
cellIds.push(encodeCellId(cell.outPoint!.txHash, cell.outPoint!.index)); | ||
const xudtTypeArgs = cell.cellOutput.type?.args ?? 'empty'; | ||
const amount = leToU128(cell.data.substring(0, 34)); | ||
if (assets[xudtTypeArgs] === undefined) { | ||
assets[xudtTypeArgs] = { | ||
utxos: 1, | ||
cells: 0, | ||
amount: 0n, | ||
}; | ||
} | ||
|
||
assets[xudtTypeArgs]!.cells += 1; | ||
assets[xudtTypeArgs]!.amount += amount; | ||
} | ||
|
||
const result: AssetGroupSummary = { | ||
utxoId, | ||
cellIds, | ||
assets, | ||
}; | ||
|
||
this.groups.push(result); | ||
return result; | ||
} | ||
|
||
addGroups(groups: { utxo: Utxo; cells: Cell[] }[]): TransactionGroupSummary { | ||
const groupResults = groups.map((group) => this.addGroup(group.utxo, group.cells)); | ||
return this.summarizeGroups(groupResults); | ||
} | ||
|
||
summarizeGroups(groups?: AssetGroupSummary[]): TransactionGroupSummary { | ||
const targetGroups = groups ?? this.groups; | ||
const utxoIds = targetGroups.map((summary) => summary.utxoId); | ||
const cellIds = targetGroups.flatMap((summary) => summary.cellIds); | ||
const assets = targetGroups.reduce( | ||
(result, summary) => { | ||
for (const xudtTypeArgs in summary.assets) { | ||
if (result[xudtTypeArgs] === undefined) { | ||
result[xudtTypeArgs] = { | ||
utxos: 0, | ||
cells: 0, | ||
amount: 0n, | ||
}; | ||
} | ||
|
||
result[xudtTypeArgs]!.utxos += summary.assets[xudtTypeArgs]!.utxos; | ||
result[xudtTypeArgs]!.cells += summary.assets[xudtTypeArgs]!.cells; | ||
result[xudtTypeArgs]!.amount += summary.assets[xudtTypeArgs]!.amount; | ||
} | ||
return result; | ||
}, | ||
{} as Record<string, Omit<AssetSummary, 'xudtTypeArgs'>>, | ||
); | ||
|
||
return { | ||
utxos: utxoIds.length, | ||
cells: cellIds.length, | ||
utxoIds, | ||
cellIds, | ||
assets, | ||
}; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { BaseCkbVirtualTxResult, BTCTestnetType, BtcTransferVirtualTxResult, Collector, Hex } from '@rgbpp-sdk/ckb'; | ||
import { AddressToPubkeyMap, DataSource } from '@rgbpp-sdk/btc'; | ||
import { TransactionGroupSummary } from '../summary/asset-summarizer'; | ||
|
||
export interface RgbppTransferCkbParams { | ||
// The collector that collects CKB live cells and transactions | ||
collector: Collector; | ||
// The transferred RGB++ xUDT type script args | ||
xudtTypeArgs: Hex; | ||
// The rgbpp assets cell lock script args array whose data structure is: out_index | bitcoin_tx_id | ||
rgbppLockArgsList: Hex[]; | ||
// The XUDT amount to be transferred, if the noMergeOutputCells is true, the transferAmount will be ignored | ||
transferAmount: bigint; | ||
// The CKB transaction fee rate, default value is 1100 | ||
feeRate?: bigint; | ||
} | ||
|
||
export interface RgbppTransferBtcParams { | ||
// The sender BTC address | ||
fromAddress: string; | ||
// The receiver BTC address | ||
toAddress: string; | ||
dataSource: DataSource; | ||
// The public key of sender BTC address | ||
fromPubkey?: Hex; | ||
// The fee rate of the BTC transaction | ||
feeRate?: number; | ||
// The Bitcoin Testnet type including Testnet3 and Signet, default value is Testnet3 | ||
testnetType?: BTCTestnetType; | ||
} | ||
|
||
export interface RgbppTransferTxParams { | ||
ckb: RgbppTransferCkbParams; | ||
btc: RgbppTransferBtcParams; | ||
// True is for BTC and CKB Mainnet, false is for BTC and CKB Testnet | ||
isMainnet: boolean; | ||
} | ||
|
||
export interface RgbppTransferTxResult { | ||
ckbVirtualTxResult: BtcTransferVirtualTxResult; | ||
// The BTC PSBT hex string which can be used to construct Bitcoin PSBT | ||
btcPsbtHex: Hex; | ||
} | ||
|
||
export interface RgbppTransferAllTxsParams { | ||
ckb: { | ||
// The collector that collects CKB live cells and transactions | ||
collector: Collector; | ||
// The transferred RGB++ xUDT type script args | ||
xudtTypeArgs: Hex; | ||
// The CKB transaction fee rate, default value is 1100 | ||
feeRate?: bigint; | ||
}; | ||
btc: { | ||
// The BTC addresses to transfer all the RGB++ assets from | ||
assetAddresses: string[]; | ||
// The BTC address for paying all the transaction fees | ||
fromAddress: string; | ||
// The BTC address for receiving all the RGB++ assets | ||
toAddress: string; | ||
// The data source for collecting Bitcoin-related info | ||
dataSource: DataSource; | ||
// The map helps find the corresponding public key of a BTC address, | ||
// note that you must specify a pubkey for each P2TR address in assetAddresses/fromAddress | ||
pubkeyMap?: AddressToPubkeyMap; | ||
// The BTC address to return change satoshi, default value is fromAddress | ||
changeAddress?: string; | ||
// The fee rate of the BTC transactions | ||
feeRate?: number; | ||
// The BTC Testnet to use, supports "Testnet3" and "Signet", default value is "Testnet3", | ||
// the param helps find the targeting version of rgbpp-lock script on CKB Testnet | ||
testnetType?: BTCTestnetType; | ||
}; | ||
// True is for BTC and CKB Mainnet, false is for BTC Testnet3/Signet and CKB Testnet | ||
isMainnet: boolean; | ||
} | ||
|
||
export interface RgbppTransferAllTxsResult { | ||
transactions: RgbppTransferAllTxGroup[]; | ||
summary: { | ||
included: TransactionGroupSummary; | ||
excluded: TransactionGroupSummary; | ||
}; | ||
} | ||
|
||
export interface RgbppTransferAllTxGroup { | ||
ckb: { | ||
virtualTxResult: BaseCkbVirtualTxResult; | ||
}; | ||
btc: { | ||
psbtHex: string; | ||
feeRate: number; | ||
fee: number; | ||
}; | ||
summary: TransactionGroupSummary; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { BaseOutput, remove0x } from '@rgbpp-sdk/btc'; | ||
import { RgbppError, RgbppErrorCodes } from '../error'; | ||
|
||
export function encodeUtxoId(txid: string, vout: number): string { | ||
return `${remove0x(txid)}:${vout}`; | ||
} | ||
|
||
export function decodeUtxoId(utxoId: string): BaseOutput { | ||
const [txid, vout] = utxoId.split(':'); | ||
if (!txid || txid.length !== 64 || !vout || isNaN(parseInt(vout))) { | ||
throw RgbppError.withComment(RgbppErrorCodes.CANNOT_DECODE_UTXO_ID, utxoId); | ||
} | ||
|
||
return { | ||
txid, | ||
vout: parseInt(vout), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { bytes, BytesLike, UnpackResult } from '@ckb-lumos/codec'; | ||
import { getXudtTypeScript, RGBPPLock } from '@rgbpp-sdk/ckb'; | ||
import { blockchain } from '@ckb-lumos/base'; | ||
|
||
export function unpackRgbppLockArgs(source: BytesLike): UnpackResult<typeof RGBPPLock> { | ||
const unpacked = RGBPPLock.unpack(source); | ||
const reversedTxId = bytes.bytify(unpacked.btcTxid).reverse(); | ||
return { | ||
btcTxid: bytes.hexify(reversedTxId), | ||
outIndex: unpacked.outIndex, | ||
}; | ||
} | ||
|
||
export function buildXudtTypeScriptHex(xudtTypeArgs: string, isMainnet: boolean): string { | ||
return bytes.hexify( | ||
blockchain.Script.pack({ | ||
...getXudtTypeScript(isMainnet), | ||
args: xudtTypeArgs, | ||
}), | ||
); | ||
} | ||
|
||
export function encodeCellId(txHash: string, index: string): string { | ||
return `${txHash}:${index}`; | ||
} |
Oops, something went wrong.