-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from ckb-cell/debug-examples
feat(rgbpp-sdk): Add examples and fix btc/ckb bugs
- Loading branch information
Showing
35 changed files
with
1,541 additions
and
459 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
## RGB++ Examples | ||
|
||
**All examples are just to demonstrate the use of RGB++ SDK. SPV proof is not ready yet, so these examples do not involve the verification of SPV proof.** | ||
|
||
### What you must know about BTC transaction id | ||
|
||
**The btc tx transaction id(hash) displayed on BTC explorer is different from the btc transaction id(hash) in RGB++ lock args. They are in reverse byte order.** | ||
|
||
We follow the following two rules: | ||
|
||
- Whenever you're working with transaction/block hashes **internally** (e.g. inside raw bitcoin data), you use the **natural** byte order. | ||
- Whenever you're **displaying or searching** for transaction/block hashes, you use the **reverse** byte order. | ||
|
||
For detailed rules, please refer to [Byte Order](https://learnmeabitcoin.com/technical/general/byte-order/) | ||
|
||
For example, the BTC transaction id(hash) of the RGB++ lock args like this: | ||
|
||
``` | ||
4abc778213bc4da692f93745c2b07410ef2bfaee70417784d4ee8969fb258001 | ||
``` | ||
|
||
But when you're searching for this transaction in [Bitcoin Core](https://bitcoin.org/en/bitcoin-core/) or on a block explorer, you'll see this byte order: | ||
|
||
``` | ||
018025fb6989eed484774170eefa2bef1074b0c24537f992a64dbc138277bc4a | ||
``` | ||
|
||
### Mint XUDT | ||
As a simple example, Omiga protocol is reused for a quick demonstration of XUDT asset insurance. | ||
Developers have the option to utilize an existing XUDT (User-Defined Token) on CKB. | ||
|
||
```shell | ||
npx ts-node examples/rgbpp/src/1-mint-xudt.ts | ||
``` | ||
|
||
### Jump XUDT from CKB to BTC | ||
|
||
```shell | ||
npx ts-node examples/rgbpp/src/2-ckb-jump-btc.ts | ||
``` | ||
|
||
### Transfer RGB++ asset on BTC | ||
|
||
```shell | ||
npx ts-node examples/rgbpp/src/3-btc-transfer.ts | ||
``` | ||
|
||
### Jump RGB++ asset from BTC to CKB | ||
|
||
```shell | ||
npx ts-node examples/rgbpp/src/4-btc-jump-ckb.ts | ||
``` | ||
|
||
### Unlock BTC time cells on CKB | ||
|
||
**Warning: Wait at least 6 BTC confirmation blocks to unlock the BTC time cells after 4-btc-jump-ckb.ts** | ||
|
||
```shell | ||
npx ts-node examples/rgbpp/src/5-spend-btc-time-cell.ts | ||
``` |
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 @@ | ||
{ | ||
"name": "rgbpp-examples", | ||
"version": "0.1.0", | ||
"description": "Examples used for RGBPP assets issuance, transfer, and jumping between BTC and CKB", | ||
"private": true, | ||
"dependencies": { | ||
"@ckb-lumos/base": "0.21.1", | ||
"@nervosnetwork/ckb-sdk-utils": "^0.109.1", | ||
"@rgbpp-sdk/ckb": "workspace:^", | ||
"@rgbpp-sdk/btc": "workspace:^", | ||
"axios": "^1.6.8", | ||
"ckb-omiga": "^0.0.12" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^5.4.2", | ||
"@types/node": "^20.11.28" | ||
} | ||
} |
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,44 @@ | ||
import { AddressPrefix } from '@nervosnetwork/ckb-sdk-utils'; | ||
import { blockchain } from '@ckb-lumos/base'; | ||
import { buildMintTx, Collector } from 'ckb-omiga'; | ||
|
||
// CKB SECP256K1 private key | ||
const CKB_TEST_PRIVATE_KEY = '0x0000000000000000000000000000000000000000000000000000000000000001'; | ||
|
||
// To simplify, we reuse the Omiga protocol to quickly issue XUDT assets on Testnet CKB | ||
const mintXudt = async () => { | ||
const collector = new Collector({ | ||
ckbNodeUrl: 'https://testnet.ckb.dev/rpc', | ||
ckbIndexerUrl: 'https://testnet.ckb.dev/indexer', | ||
}); | ||
|
||
const address = collector.getCkb().utils.privateKeyToAddress(CKB_TEST_PRIVATE_KEY, { prefix: AddressPrefix.Testnet }); | ||
// ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqt4z78ng4yutl5u6xsv27ht6q08mhujf8s2r0n40 | ||
console.log('ckb address: ', address); | ||
|
||
const mintLimit = BigInt(1000) * BigInt(10 ** 8); | ||
const inscriptionId = '0xd378891e711cf5c612321b7f51529215187403c61cbb27bc4413fded871b73d5'; | ||
|
||
const rawTx = await buildMintTx({ collector, address, inscriptionId, mintLimit }); | ||
|
||
const secp256k1Dep: CKBComponents.CellDep = { | ||
outPoint: { | ||
txHash: '0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37', | ||
index: '0x0', | ||
}, | ||
depType: 'depGroup', | ||
}; | ||
const witnessArgs = blockchain.WitnessArgs.unpack(rawTx.witnesses[0]) as CKBComponents.WitnessArgs; | ||
let unsignedTx: CKBComponents.RawTransactionToSign = { | ||
...rawTx, | ||
cellDeps: [...rawTx.cellDeps, secp256k1Dep], | ||
witnesses: [witnessArgs, ...rawTx.witnesses.slice(1)], | ||
}; | ||
const signedTx = collector.getCkb().signTransaction(CKB_TEST_PRIVATE_KEY)(unsignedTx); | ||
|
||
let txHash = await collector.getCkb().rpc.sendTransaction(signedTx, 'passthrough'); | ||
console.info(`Xudt has been minted with tx hash ${txHash}`); | ||
}; | ||
|
||
mintXudt(); | ||
|
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,49 @@ | ||
import { AddressPrefix, privateKeyToAddress, serializeScript } from '@nervosnetwork/ckb-sdk-utils'; | ||
import { genCkbJumpBtcVirtualTx, Collector, getSecp256k1CellDep, buildRgbppLockArgs } from '@rgbpp-sdk/ckb'; | ||
|
||
// CKB SECP256K1 private key | ||
const CKB_TEST_PRIVATE_KEY = '0x0000000000000000000000000000000000000000000000000000000000000001'; | ||
|
||
const jumpFromCkbToBtc = async ({ outIndex, btcTxId }: { outIndex: number; btcTxId: string }) => { | ||
const collector = new Collector({ | ||
ckbNodeUrl: 'https://testnet.ckb.dev/rpc', | ||
ckbIndexerUrl: 'https://testnet.ckb.dev/indexer', | ||
}); | ||
const address = privateKeyToAddress(CKB_TEST_PRIVATE_KEY, { prefix: AddressPrefix.Testnet }); | ||
console.log('ckb address: ', address); | ||
|
||
const toRgbppLockArgs = buildRgbppLockArgs(outIndex, btcTxId); | ||
|
||
const xudtType: CKBComponents.Script = { | ||
codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', | ||
hashType: 'type', | ||
args: '0x1ba116c119d1cfd98a53e9d1a615cf2af2bb87d95515c9d217d367054cfc696b', | ||
}; | ||
|
||
const ckbRawTx = await genCkbJumpBtcVirtualTx({ | ||
collector, | ||
fromCkbAddress: address, | ||
toRgbppLockArgs, | ||
xudtTypeBytes: serializeScript(xudtType), | ||
transferAmount: BigInt(800_0000_0000), | ||
}); | ||
|
||
const emptyWitness = { lock: '', inputType: '', outputType: '' }; | ||
let unsignedTx: CKBComponents.RawTransactionToSign = { | ||
...ckbRawTx, | ||
cellDeps: [...ckbRawTx.cellDeps, getSecp256k1CellDep(false)], | ||
witnesses: [emptyWitness, ...ckbRawTx.witnesses.slice(1)], | ||
}; | ||
|
||
const signedTx = collector.getCkb().signTransaction(CKB_TEST_PRIVATE_KEY)(unsignedTx); | ||
|
||
let txHash = await collector.getCkb().rpc.sendTransaction(signedTx, 'passthrough'); | ||
console.info(`Rgbpp asset has been jumped from CKB to BTC and tx hash is ${txHash}`); | ||
}; | ||
|
||
// Use your real BTC UTXO information on the BTC Testnet | ||
jumpFromCkbToBtc({ | ||
outIndex: 1, | ||
btcTxId: 'dad1814249f3bcbcb1e26436108c8f48b9d71456938cf62e122357caf7913013', | ||
}); | ||
|
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,117 @@ | ||
import { AddressPrefix, privateKeyToAddress, serializeScript } from '@nervosnetwork/ckb-sdk-utils'; | ||
import { | ||
Collector, | ||
SPVService, | ||
appendCkbTxWitnesses, | ||
buildRgbppLockArgs, | ||
genBtcTransferCkbVirtualTx, | ||
sendCkbTx, | ||
updateCkbTxWithRealBtcTxId, | ||
} from '@rgbpp-sdk/ckb'; | ||
import { transactionToHex, sendRgbppUtxos, BtcAssetsApi, DataSource, ECPair, bitcoin, NetworkType } from '@rgbpp-sdk/btc'; | ||
|
||
// CKB SECP256K1 private key | ||
const CKB_TEST_PRIVATE_KEY = '0x0000000000000000000000000000000000000000000000000000000000000001'; | ||
// BTC SECP256K1 private key | ||
const BTC_TEST_PRIVATE_KEY = '0x0000000000000000000000000000000000000000000000000000000000000001'; | ||
// https://btc-assets-api-develop.vercel.app/docs/static/index.html | ||
const BTC_ASSETS_API_URL = 'https://btc-assets-api-url'; | ||
// https://btc-assets-api-develop.vercel.app/docs/static/index.html#/Token/post_token_generate | ||
const BTC_ASSETS_TOKEN = ''; | ||
// See https://github.com/ckb-cell/ckb-bitcoin-spv-service#json-rpc-api-reference | ||
const SPV_SERVICE_URL = 'https://ckb-bitcoin-spv-service.testnet.mibao.pro'; | ||
|
||
interface Params { | ||
rgbppLockArgsList: string[]; | ||
toBtcAddress: string; | ||
transferAmount: bigint; | ||
} | ||
const transferRgbppOnBtc = async ({ rgbppLockArgsList, toBtcAddress, transferAmount }: Params) => { | ||
const collector = new Collector({ | ||
ckbNodeUrl: 'https://testnet.ckb.dev/rpc', | ||
ckbIndexerUrl: 'https://testnet.ckb.dev/indexer', | ||
}); | ||
const ckbAddress = privateKeyToAddress(CKB_TEST_PRIVATE_KEY, { prefix: AddressPrefix.Testnet }); | ||
console.log('ckb address: ', ckbAddress); | ||
// const fromLock = addressToScript(ckbAddress); | ||
|
||
const network = bitcoin.networks.testnet; | ||
const keyPair = ECPair.fromPrivateKey(Buffer.from(BTC_TEST_PRIVATE_KEY, 'hex'), { network }); | ||
const { address: btcAddress } = bitcoin.payments.p2wpkh({ | ||
pubkey: keyPair.publicKey, | ||
network, | ||
}); | ||
|
||
console.log('btc address: ', btcAddress); | ||
|
||
const networkType = NetworkType.TESTNET; | ||
const service = BtcAssetsApi.fromToken(BTC_ASSETS_API_URL, BTC_ASSETS_TOKEN, 'http://localhost'); | ||
const source = new DataSource(service, networkType); | ||
|
||
const xudtType: CKBComponents.Script = { | ||
codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', | ||
hashType: 'type', | ||
args: '0x1ba116c119d1cfd98a53e9d1a615cf2af2bb87d95515c9d217d367054cfc696b', | ||
}; | ||
|
||
const ckbVirtualTxResult = await genBtcTransferCkbVirtualTx({ | ||
collector, | ||
rgbppLockArgsList, | ||
xudtTypeBytes: serializeScript(xudtType), | ||
transferAmount, | ||
isMainnet: false, | ||
}); | ||
|
||
const { commitment, ckbRawTx } = ckbVirtualTxResult; | ||
|
||
// Send BTC tx | ||
const psbt = await sendRgbppUtxos({ | ||
ckbVirtualTx: ckbRawTx, | ||
commitment, | ||
tos: [toBtcAddress], | ||
ckbCollector: collector, | ||
from: btcAddress!, | ||
source, | ||
}); | ||
psbt.signAllInputs(keyPair); | ||
psbt.finalizeAllInputs(); | ||
|
||
const btcTx = psbt.extractTransaction(); | ||
// Remove the witness from BTC tx for RGBPP unlock | ||
const btcTxBytes = transactionToHex(btcTx, false); | ||
let { txid: btcTxId } = await service.sendTransaction(btcTx.toHex()); | ||
|
||
console.log('BTC Tx bytes: ', btcTxBytes); | ||
console.log('BTC TxId: ', btcTxId); | ||
|
||
// Update CKB transaction with the real BTC txId | ||
const newCkbRawTx = updateCkbTxWithRealBtcTxId({ ckbRawTx, btcTxId, isMainnet: false }); | ||
|
||
const spvService = new SPVService(SPV_SERVICE_URL); | ||
// Use an exist BTC transaction id to get the tx proof and the contract will not verify the tx proof now | ||
btcTxId = '018025fb6989eed484774170eefa2bef1074b0c24537f992a64dbc138277bc4a'; | ||
|
||
let ckbTx = await appendCkbTxWitnesses({ | ||
ckbRawTx: newCkbRawTx, | ||
btcTxBytes, | ||
spvService, | ||
btcTxIndexInBlock: 0, // ignore spv proof now | ||
btcTxId, | ||
}); | ||
|
||
console.log(JSON.stringify(ckbTx)); | ||
|
||
const txHash = await sendCkbTx({ collector, signedTx: ckbTx }); | ||
console.info(`Rgbpp asset has been transferred on BTC and tx hash is ${txHash}`); | ||
}; | ||
|
||
|
||
// Use your real BTC UTXO information on the BTC Testnet | ||
// rgbppLockArgs: outIndexU32 + btcTxId | ||
transferRgbppOnBtc({ | ||
rgbppLockArgsList: [buildRgbppLockArgs(1, '53e7c02eba522d1e3b0698b4bf5405c25c33b32e7df84a1a6c19e2cf165681f0')], | ||
toBtcAddress: 'tb1qvt7p9g6mw70sealdewtfp0sekquxuru6j3gwmt', | ||
// To simplify, keep the transferAmount the same as 2-ckb-jump-btc | ||
transferAmount: BigInt(800_0000_0000), | ||
}); | ||
|
Oops, something went wrong.
709c181
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.