Skip to content

Commit f557655

Browse files
authored
Merge pull request #12 from RGBPlusPlus/develop
Merge branch `develop` into `main`
2 parents 401a9cb + 467ba0a commit f557655

File tree

16 files changed

+249
-111
lines changed

16 files changed

+249
-111
lines changed

.github/workflows/test.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ jobs:
4848

4949
- name: Lint packages
5050
run: pnpm run lint
51-
51+
5252
- name: Run unit tests for the packages
5353
run: pnpm run test:packages
5454
env:
5555
VITE_CKB_NODE_URL: https://testnet.ckb.dev/rpc
5656
VITE_CKB_INDEXER_URL: https://testnet.ckb.dev/indexer
57-
VITE_BTC_SERVICE_URL: https://btc-assets-api.testnet.mibao.pro
57+
VITE_BTC_SERVICE_URL: https://api.testnet.rgbpp.io
5858
VITE_BTC_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }}
59-
VITE_BTC_SERVICE_ORIGIN: https://btc-assets-api.testnet.mibao.pro
59+
VITE_BTC_SERVICE_ORIGIN: https://api.testnet.rgbpp.io
6060

6161
- name: Run unit tests for the rgbpp-sdk-service
6262
run: pnpm run test:service

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ This repository offers utilities for Bitcoin and RGB++ asset integration.
6969

7070
## FAQ
7171

72-
### How to get an access token of Bitcoin/RGB++ Assets Service?
73-
See [Generate a JSON Web Token (JWT) for Bitcoin/RGB++ Assets Service](./packages/service/README.md#get-an-access-token)
72+
### How to access the Bitcoin/RGB++ Assets Service?
73+
See [Get a service URL](./packages/service/README.md#get-a-service-url)
7474

7575
### Where is the error code description for the RgbppLockScript?
7676
See [RGB++ Lock Script Error Codes](https://github.com/nervosnetwork/ckb-script-error-codes/blob/main/by-type-hash/bc6c568a1a0d0a09f6844dc9d74ddb4343c32143ff25f727c59edf4fb72d6936.md)

packages/ckb/src/constants/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ const TestnetInfo = {
2727
} as CKBComponents.Script,
2828

2929
RgbppLockDep: {
30-
outPoint: { txHash: '0xf1de59e973b85791ec32debbba08dff80c63197e895eb95d67fc1e9f6b413e00', index: '0x0' },
30+
outPoint: { txHash: '0x0d1567da0979f78b297d5311442669fbd1bd853c8be324c5ab6da41e7a1ed6e5', index: '0x0' },
3131
depType: 'code',
3232
} as CKBComponents.CellDep,
3333

3434
RgbppLockConfigDep: {
35-
outPoint: { txHash: '0xf1de59e973b85791ec32debbba08dff80c63197e895eb95d67fc1e9f6b413e00', index: '0x1' },
35+
outPoint: { txHash: '0x0d1567da0979f78b297d5311442669fbd1bd853c8be324c5ab6da41e7a1ed6e5', index: '0x1' },
3636
depType: 'code',
3737
} as CKBComponents.CellDep,
3838

@@ -43,12 +43,12 @@ const TestnetInfo = {
4343
} as CKBComponents.Script,
4444

4545
BtcTimeLockDep: {
46-
outPoint: { txHash: '0xde0f87878a97500f549418e5d46d2f7704c565a262aa17036c9c1c13ad638529', index: '0x0' },
46+
outPoint: { txHash: '0x8fb747ff0416a43e135c583b028f98c7b81d3770551b196eb7ba1062dd9acc94', index: '0x0' },
4747
depType: 'code',
4848
} as CKBComponents.CellDep,
4949

5050
BtcTimeLockConfigDep: {
51-
outPoint: { txHash: '0xde0f87878a97500f549418e5d46d2f7704c565a262aa17036c9c1c13ad638529', index: '0x1' },
51+
outPoint: { txHash: '0x8fb747ff0416a43e135c583b028f98c7b81d3770551b196eb7ba1062dd9acc94', index: '0x1' },
5252
depType: 'code',
5353
} as CKBComponents.CellDep,
5454

packages/ckb/src/rgbpp/btc-jump-ckb.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import { RgbppCkbVirtualTx, BtcJumpCkbVirtualTxParams, BtcJumpCkbVirtualTxResult
22
import { TypeAssetNotSupportedError } from '../error';
33
import {
44
append0x,
5-
calculateCellOccupiedCapacity,
65
calculateRgbppCellCapacity,
7-
calculateTransactionFee,
86
deduplicateList,
97
fetchTypeIdCellDeps,
108
isLockArgsSizeExceeded,
@@ -22,11 +20,12 @@ import {
2220
isRgbppCapacitySufficientForChange,
2321
isStandardUDTTypeSupported,
2422
isOfflineMode,
23+
adjustVirtualTxForTxFee,
2524
} from '../utils';
2625
import { Hex, IndexerCell } from '../types';
2726
import { RGBPP_WITNESS_PLACEHOLDER, getSecp256k1CellDep } from '../constants';
2827
import { blockchain } from '@ckb-lumos/base';
29-
import { addressToScript, getTransactionSize } from '@nervosnetwork/ckb-sdk-utils';
28+
import { addressToScript } from '@nervosnetwork/ckb-sdk-utils';
3029

3130
/**
3231
* Generate the virtual ckb transaction for the jumping tx from BTC to CKB
@@ -99,31 +98,28 @@ export const genBtcJumpCkbVirtualTx = async ({
9998

10099
let needPaymasterCell = false;
101100
const needRgbppChange = sumAmount > transferAmount;
102-
// To simplify, when the xUDT does not need change, all the capacity of the inputs will be given to the receiver
103-
const candidateCapacity = needRgbppChange ? BigInt(rgbppTargetCells[0].output.capacity) : sumInputsCapacity;
104-
105-
const receiverLock = genBtcTimeLockScript(toLock, isMainnet, btcTestnetType, btcConfirmationBlocks);
106-
const receiverData = append0x(u128ToLe(transferAmount));
107101

108-
const minRequiredCapacity = calculateCellOccupiedCapacity({
109-
output: { lock: receiverLock, type: xudtType, capacity: '0x0' },
110-
outputData: receiverData,
111-
} as IndexerCell);
102+
// To simplify, when the xUDT does not need change, all the capacity of the inputs will be given to the receiver
103+
const candidateReceiverOutputCapacity = needRgbppChange
104+
? BigInt(rgbppTargetCells[0].output.capacity)
105+
: sumInputsCapacity;
106+
const receiverOutputCapacity =
107+
candidateReceiverOutputCapacity >= rgbppCellCapacity ? candidateReceiverOutputCapacity : rgbppCellCapacity;
112108

113-
const receiverOutputCapacity = candidateCapacity > minRequiredCapacity ? candidateCapacity : minRequiredCapacity;
114109
// The BTC time cell does not need to be bound to the BTC UTXO
115110
const outputs: CKBComponents.CellOutput[] = [
116111
{
117-
lock: receiverLock,
112+
lock: genBtcTimeLockScript(toLock, isMainnet, btcTestnetType, btcConfirmationBlocks),
118113
type: xudtType,
119114
capacity: append0x(receiverOutputCapacity.toString(16)),
120115
},
121116
];
122-
const outputsData = [receiverData];
117+
const outputsData = [append0x(u128ToLe(transferAmount))];
118+
119+
const isCapacitySufficient = isRgbppCapacitySufficientForChange(sumInputsCapacity, receiverOutputCapacity);
120+
needPaymasterCell = !isCapacitySufficient;
123121

124122
if (needRgbppChange) {
125-
const isCapacitySufficient = isRgbppCapacitySufficientForChange(sumInputsCapacity, receiverOutputCapacity);
126-
needPaymasterCell = !isCapacitySufficient;
127123
// When the capacity of inputs is enough for the outputs, the sender needs to recover the excess capacity.
128124
const udtChangeCapacity = isCapacitySufficient ? sumInputsCapacity - receiverOutputCapacity : rgbppCellCapacity;
129125
outputs.push({
@@ -188,11 +184,15 @@ export const genBtcJumpCkbVirtualTx = async ({
188184
};
189185

190186
if (!needPaymasterCell) {
191-
const txSize =
192-
getTransactionSize(ckbRawTx) + (witnessLockPlaceholderSize ?? estimateWitnessSize(deduplicatedLockArgsList));
193-
const estimatedTxFee = calculateTransactionFee(txSize, ckbFeeRate);
194-
const changeCapacity = BigInt(outputs[outputs.length - 1].capacity) - estimatedTxFee;
195-
ckbRawTx.outputs[ckbRawTx.outputs.length - 1].capacity = append0x(changeCapacity.toString(16));
187+
const txFeeAdjustedResult = adjustVirtualTxForTxFee(
188+
ckbRawTx,
189+
isMainnet,
190+
witnessLockPlaceholderSize ?? estimateWitnessSize(deduplicatedLockArgsList),
191+
ckbFeeRate,
192+
);
193+
needPaymasterCell = txFeeAdjustedResult.needPaymasterCell;
194+
ckbRawTx.outputs = txFeeAdjustedResult.outputs;
195+
ckbRawTx.cellDeps = txFeeAdjustedResult.cellDeps;
196196
}
197197

198198
const virtualTx: RgbppCkbVirtualTx = {

packages/ckb/src/rgbpp/btc-transfer.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
signCkbTransaction,
2929
addressToScriptHash,
3030
isOfflineMode,
31+
adjustVirtualTxForTxFee,
3132
} from '../utils';
3233
import { Hex, IndexerCell } from '../types';
3334
import {
@@ -143,7 +144,12 @@ export const genBtcTransferCkbVirtualTx = async ({
143144

144145
const needRgbppChange = collectResult.sumAmount > transferAmount;
145146
// To simplify, when the xUDT does not need change, all the capacity of the inputs will be given to the receiver
146-
const receiverOutputCapacity = needRgbppChange ? BigInt(rgbppTargetCells[0].output.capacity) : sumInputsCapacity;
147+
const candidateReceiverOutputCapacity = needRgbppChange
148+
? BigInt(rgbppTargetCells[0].output.capacity)
149+
: sumInputsCapacity;
150+
const receiverOutputCapacity =
151+
candidateReceiverOutputCapacity >= rgbppCellCapacity ? candidateReceiverOutputCapacity : rgbppCellCapacity;
152+
147153
// The Vouts[0] for OP_RETURN and Vouts[1] for target transfer RGBPP assets
148154
outputs.push({
149155
lock: genRgbppLockScript(buildPreLockArgs(1), isMainnet, btcTestnetType),
@@ -152,9 +158,9 @@ export const genBtcTransferCkbVirtualTx = async ({
152158
});
153159
outputsData.push(append0x(u128ToLe(transferAmount)));
154160

161+
const isCapacitySufficient = isRgbppCapacitySufficientForChange(sumInputsCapacity, receiverOutputCapacity);
162+
needPaymasterCell = !isCapacitySufficient;
155163
if (needRgbppChange) {
156-
const isCapacitySufficient = isRgbppCapacitySufficientForChange(sumInputsCapacity, receiverOutputCapacity);
157-
needPaymasterCell = !isCapacitySufficient;
158164
// When the capacity of inputs is enough for the outputs, the sender needs to recover the excess capacity.
159165
const udtChangeCapacity = isCapacitySufficient ? sumInputsCapacity - receiverOutputCapacity : rgbppCellCapacity;
160166
// The Vouts[2] for target change RGBPP assets
@@ -206,12 +212,15 @@ export const genBtcTransferCkbVirtualTx = async ({
206212
};
207213

208214
if (!needPaymasterCell) {
209-
const txSize =
210-
getTransactionSize(ckbRawTx) + (witnessLockPlaceholderSize ?? estimateWitnessSize(deduplicatedLockArgsList));
211-
const estimatedTxFee = calculateTransactionFee(txSize, ckbFeeRate);
212-
213-
const changeCapacity = BigInt(outputs[outputs.length - 1].capacity) - estimatedTxFee;
214-
ckbRawTx.outputs[ckbRawTx.outputs.length - 1].capacity = append0x(changeCapacity.toString(16));
215+
const txFeeAdjustedResult = adjustVirtualTxForTxFee(
216+
ckbRawTx,
217+
isMainnet,
218+
witnessLockPlaceholderSize ?? estimateWitnessSize(deduplicatedLockArgsList),
219+
ckbFeeRate,
220+
);
221+
needPaymasterCell = txFeeAdjustedResult.needPaymasterCell;
222+
ckbRawTx.outputs = txFeeAdjustedResult.outputs;
223+
ckbRawTx.cellDeps = txFeeAdjustedResult.cellDeps;
215224
}
216225

217226
const virtualTx: RgbppCkbVirtualTx = {

packages/ckb/src/rgbpp/ckb-builder.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const appendPaymasterCellAndSignCkbTx = async ({
8787
ckbFeeRate,
8888
}: AppendPaymasterCellAndSignTxParams): Promise<CKBComponents.RawTransaction> => {
8989
const rawTx = ckbRawTx as CKBComponents.RawTransactionToSign;
90+
const numOriginalInputs = rawTx.inputs.length;
9091
const paymasterInput = { previousOutput: paymasterCell.outPoint, since: '0x0' };
9192
rawTx.inputs = [...rawTx.inputs, paymasterInput];
9293
const inputsCapacity = BigInt(sumInputsCapacity) + BigInt(paymasterCell.output.capacity);
@@ -123,7 +124,11 @@ export const appendPaymasterCellAndSignCkbTx = async ({
123124
}));
124125

125126
const emptyWitness = { lock: '', inputType: '', outputType: '' };
126-
rawTx.witnesses = [...rawTx.witnesses, emptyWitness];
127+
rawTx.witnesses = [
128+
...rawTx.witnesses.slice(0, numOriginalInputs),
129+
emptyWitness,
130+
...rawTx.witnesses.slice(numOriginalInputs),
131+
];
127132

128133
const transactionHash = rawTransactionToHash(rawTx);
129134
const signedWitnesses = signWitnesses(keyMap)({

packages/ckb/src/rgbpp/launch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { getTransactionSize, scriptToHash } from '@nervosnetwork/ckb-sdk-utils';
2929
* @param collector The collector that collects CKB live cells and transactions
3030
* @param ownerRgbppLockArgs The owner RGBPP lock args whose data structure is: out_index | bitcoin_tx_id
3131
* @param launchAmount The total amount of RGBPP assets issued
32-
* @param rgbppTokenInfo The RGBPP token info https://github.com/ckb-cell/unique-cell?tab=readme-ov-file#xudt-information
32+
* @param rgbppTokenInfo The RGBPP token info https://github.com/RGBPlusPlus/unique-cell?tab=readme-ov-file#xudt-information
3333
* @param isMainnet True is for BTC and CKB Mainnet, false is for BTC and CKB Testnet(see btcTestnetType for details about BTC Testnet)
3434
* @param witnessLockPlaceholderSize(Optional) The WitnessArgs.lock placeholder bytes array size and the default value is 5000
3535
* @param ckbFeeRate(Optional) The CKB transaction fee rate, default value is 1100

packages/ckb/src/spore/leap.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ import {
1212
append0x,
1313
fetchTypeIdCellDeps,
1414
calculateTransactionFee,
15+
adjustVirtualTxForTxFee,
16+
calculateRgbppSporeCellCapacity,
17+
calculateCellOccupiedCapacity,
1518
} from '../utils';
1619
import {
20+
IndexerCell,
1721
Hex,
1822
LeapSporeFromBtcToCkbVirtualTxParams,
1923
LeapSporeFromCkbToBtcVirtualTxParams,
@@ -23,6 +27,7 @@ import {
2327
RGBPP_TX_WITNESS_MAX_SIZE,
2428
RGBPP_WITNESS_PLACEHOLDER,
2529
getRgbppLockScript,
30+
getSecp256k1CellDep,
2631
getSporeTypeDep,
2732
} from '../constants';
2833
import { NoRgbppLiveCellError } from '../error';
@@ -34,6 +39,7 @@ import {
3439
serializeOutPoint,
3540
serializeWitnessArgs,
3641
} from '@nervosnetwork/ckb-sdk-utils';
42+
import { unpackToRawSporeData } from '@spore-sdk/core';
3743

3844
/**
3945
* Generate the virtual ckb transaction for leaping spore from BTC to CKB
@@ -75,16 +81,31 @@ export const genLeapSporeFromBtcToCkbVirtualTx = async ({
7581
];
7682

7783
const toLock = addressToScript(toCkbAddress);
78-
const outputs: CKBComponents.CellOutput[] = [
79-
{
80-
...sporeCell.output,
81-
lock: genBtcTimeLockScript(toLock, isMainnet, btcTestnetType),
82-
},
83-
];
84+
const sporeOutput = {
85+
...sporeCell.output,
86+
lock: genBtcTimeLockScript(toLock, isMainnet, btcTestnetType),
87+
};
88+
89+
let needPaymasterCell = false;
90+
let cellDeps: CKBComponents.CellDep[] = [];
91+
if (
92+
BigInt(sporeOutput.capacity) <=
93+
calculateCellOccupiedCapacity({ output: sporeOutput, outputData: sporeCell.outputData } as IndexerCell)
94+
) {
95+
needPaymasterCell = true;
96+
// restore the capacity to be consistent (with margin)
97+
sporeOutput.capacity = append0x(
98+
calculateRgbppSporeCellCapacity(unpackToRawSporeData(sporeCell.outputData)).toString(16),
99+
);
100+
cellDeps.push(getSecp256k1CellDep(isMainnet));
101+
}
102+
103+
const outputs: CKBComponents.CellOutput[] = [sporeOutput];
84104
const outputsData: Hex[] = [sporeCell.outputData];
85-
const cellDeps = [
105+
cellDeps = [
86106
...(await fetchTypeIdCellDeps(isMainnet, { rgbpp: true }, btcTestnetType, vendorCellDeps)),
87107
getSporeTypeDep(isMainnet),
108+
...cellDeps,
88109
];
89110
const sporeCoBuild = generateSporeTransferCoBuild([sporeCell], outputs);
90111
const witnesses = [RGBPP_WITNESS_PLACEHOLDER, sporeCoBuild];
@@ -99,12 +120,17 @@ export const genLeapSporeFromBtcToCkbVirtualTx = async ({
99120
witnesses,
100121
};
101122

102-
let changeCapacity = BigInt(sporeCell.output.capacity);
103-
const txSize = getTransactionSize(ckbRawTx) + (witnessLockPlaceholderSize ?? RGBPP_TX_WITNESS_MAX_SIZE);
104-
const estimatedTxFee = calculateTransactionFee(txSize, ckbFeeRate);
105-
changeCapacity -= estimatedTxFee;
106-
107-
ckbRawTx.outputs[ckbRawTx.outputs.length - 1].capacity = append0x(changeCapacity.toString(16));
123+
if (!needPaymasterCell) {
124+
const txFeeAdjustedResult = adjustVirtualTxForTxFee(
125+
ckbRawTx,
126+
isMainnet,
127+
witnessLockPlaceholderSize ?? RGBPP_TX_WITNESS_MAX_SIZE,
128+
ckbFeeRate,
129+
);
130+
ckbRawTx.outputs = txFeeAdjustedResult.outputs;
131+
ckbRawTx.cellDeps = txFeeAdjustedResult.cellDeps;
132+
needPaymasterCell = txFeeAdjustedResult.needPaymasterCell;
133+
}
108134

109135
const virtualTx: RgbppCkbVirtualTx = {
110136
...ckbRawTx,
@@ -115,7 +141,7 @@ export const genLeapSporeFromBtcToCkbVirtualTx = async ({
115141
ckbRawTx,
116142
commitment,
117143
sporeCell,
118-
needPaymasterCell: false,
144+
needPaymasterCell,
119145
sumInputsCapacity: sporeCell.output.capacity,
120146
};
121147
};

0 commit comments

Comments
 (0)