Skip to content

Commit b2d0f21

Browse files
author
chunteng-web3
committed
feat: udt:transfer, udt:extended:mint, udt:pausable initial implementation (WIP)
1 parent 855e561 commit b2d0f21

File tree

14 files changed

+318
-67
lines changed

14 files changed

+318
-67
lines changed

.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MAIN_WALLET_PRIVATE_KEY=c0f0166fbbabca125cd8f9e12ddde5bc204fcbdbb090d6c1eb3a29bc028029e6
2+
PAUSED_WALLET_PRIVATE_KEY=38ef35ae58d854676f7822623f5e2b3ec4284766323be6750e32e2f0ec44eed4
3+
CKB_RPC_URL=wss://testnet.ckb.dev/ws

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"@oclif/core": "^4",
1313
"@oclif/plugin-help": "^6",
1414
"@oclif/plugin-plugins": "^5",
15-
"axios": "^1.7.7"
15+
"axios": "^1.7.7",
16+
"dotenv": "^16.4.5"
1617
},
1718
"devDependencies": {
1819
"@oclif/prettier-config": "^0.2.1",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/hello/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Args, Command, Flags} from '@oclif/core'
2+
import { encodeU832Array } from '../../libs/utils'
23

34
export default class Hello extends Command {
45
static args = {

src/commands/udt/extended/mint.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { ccc, Cell, CellDep, CellDepLike } from '@ckb-ccc/core'
2+
import {Args, Command, Flags} from '@oclif/core'
3+
import { getUDTConfig } from '../../../libs/config.js'
4+
import 'dotenv/config'
5+
6+
export default class UdtExtendedMint extends Command {
7+
static override args = {
8+
}
9+
10+
static override description = 'describe the command here'
11+
12+
static override examples = [
13+
'<%= config.bin %> <%= command.id %>',
14+
]
15+
16+
static override flags = {
17+
// flag with no value (-f, --force)
18+
force: Flags.boolean({char: 'f'}),
19+
// flag with a value (-n, --name=VALUE)
20+
name: Flags.string({char: 'n', description: 'name to print'}),
21+
}
22+
23+
public async run(): Promise<void> {
24+
const {args, flags} = await this.parse(UdtExtendedMint)
25+
26+
const client = new ccc.ClientPublicTestnet({ url: process.env.CKB_RPC_URL });
27+
const signer = new ccc.SignerCkbPrivateKey(
28+
client,
29+
process.env.MAIN_WALLET_PRIVATE_KEY!,
30+
);
31+
32+
const recommendedActorAddress = await signer.getRecommendedAddress();
33+
const { script: ownerLock } = await signer.getRecommendedAddressObj();
34+
35+
const toAddress = "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqgtlcnzzna2tqst7jw78egjpujn7hdxpackjmmdp";
36+
const toLock = (await ccc.Address.fromString(toAddress, signer.client)).script;
37+
38+
const udtConfig = getUDTConfig();
39+
const udtTypeScript = new ccc.Script(
40+
udtConfig.code_hash,
41+
'type',
42+
udtConfig.args
43+
);
44+
45+
const mintTx = ccc.Transaction.from({
46+
outputs: [
47+
{
48+
lock: toLock,
49+
type: udtTypeScript,
50+
},
51+
],
52+
outputsData: [
53+
ccc.numLeToBytes(100000000, 16)
54+
]
55+
});
56+
57+
await mintTx.completeInputsByCapacity(signer);
58+
await mintTx.completeFeeBy(signer);
59+
60+
console.log(mintTx.inputs[0]);
61+
62+
const findCellDepResult = await client.findCells(udtConfig.cellDepSearchKey).next();
63+
const cellDepCell: Cell = findCellDepResult.value;
64+
const cellDepLike: CellDepLike = {
65+
outPoint: {
66+
txHash: cellDepCell.outPoint.txHash,
67+
index: cellDepCell.outPoint.index,
68+
},
69+
depType: 'code',
70+
};
71+
72+
mintTx.addCellDeps(cellDepLike);
73+
const mintTxHash = await signer.sendTransaction(mintTx);
74+
75+
console.log(`Minted UDT with transaction hash: ${mintTxHash}`);
76+
}
77+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {HasherCkb, numToBytes, numToHex} from '@ckb-ccc/core'
2+
import {Args, Command, Flags} from '@oclif/core'
3+
import {cccA} from '@ckb-ccc/core/advanced'
4+
import { encodeHex, encodeU832Array } from '../../../libs/utils.js'
5+
import axios from 'axios'
6+
7+
export default class UdtPausableIsPaused extends Command {
8+
static override args = {
9+
}
10+
11+
static strict = false;
12+
13+
static override description = 'describe the command here'
14+
15+
static override examples = ['<%= config.bin %> <%= command.id %>']
16+
17+
static override flags = {
18+
target: Flags.string({description: 'Target cell'}),
19+
index: Flags.integer({description: 'Index of the target cell'}),
20+
level: Flags.string({
21+
char: 'l',
22+
description: 'name to print',
23+
options: ['code', 'cell', 'transaction'],
24+
default: 'code',
25+
}),
26+
}
27+
28+
public async run(): Promise<void> {
29+
const {argv, flags} = await this.parse(UdtPausableIsPaused)
30+
// Method path hex function
31+
const hasher = new HasherCkb();
32+
const enumeratePausedPathHex = hasher.update(Buffer.from('UDT.enumerate_paused')).digest().slice(0, 18);
33+
console.debug('hashed method path hex:', enumeratePausedPathHex);
34+
35+
// Define URL
36+
const url = 'http://localhost:9090';
37+
38+
// Define the JSON payload
39+
const payload = {
40+
id: 2,
41+
jsonrpc: '2.0',
42+
method: 'run_script_level_code',
43+
params: [
44+
"0x24e477bdae84955713ce9075cc176e87f1c882fa3cedcde4ea3dd6c1ee7b0d5c",
45+
// args.target,
46+
0,
47+
// args.index,
48+
[enumeratePausedPathHex,]],
49+
};
50+
51+
// Send POST request
52+
axios
53+
.post(url, payload, {
54+
headers: {'Content-Type': 'application/json'},
55+
})
56+
.then((response) => {
57+
console.log('Response JSON:', response.data)
58+
})
59+
.catch((error) => {
60+
console.error('Request failed', error)
61+
})
62+
}
63+
}

src/commands/udt/pausable/is-paused.ts

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import {HasherCkb, numToBytes, numToHex} from '@ckb-ccc/core'
22
import {Args, Command, Flags} from '@oclif/core'
33
import {cccA} from '@ckb-ccc/core/advanced'
4+
import { encodeHex, encodeU832Array } from '../../../libs/utils.js'
45
import axios from 'axios'
56

67
export default class UdtPausableIsPaused extends Command {
78
static override args = {
8-
target: Args.string({description: 'Target cell'}),
9-
index: Args.integer({description: 'Index of the target cell'}),
109
lock_hash: Args.string({description: 'Lock hash in hex. Variable length'}),
1110
}
1211

@@ -17,6 +16,8 @@ export default class UdtPausableIsPaused extends Command {
1716
static override examples = ['<%= config.bin %> <%= command.id %>']
1817

1918
static override flags = {
19+
target: Flags.string({description: 'Target cell'}),
20+
index: Flags.integer({description: 'Index of the target cell'}),
2021
level: Flags.string({
2122
char: 'l',
2223
description: 'name to print',
@@ -25,55 +26,16 @@ export default class UdtPausableIsPaused extends Command {
2526
}),
2627
}
2728

28-
public encodeU832Array(val: Array<Uint8Array>): Uint8Array {
29-
if (val.some((arr) => arr.length !== 32)) {
30-
throw new Error('Each inner array must be exactly 32 bytes.')
31-
}
32-
33-
// Convert the length to a 4-byte little-endian array
34-
const lengthBytes = new Uint8Array(new Uint32Array([val.length]).buffer)
35-
console.log('lengthBytes:', lengthBytes);
36-
37-
// Flatten the 2D array of 32-byte elements into a single array
38-
const flattenedBytes = val.reduce((acc, curr) => {
39-
acc.push(...curr)
40-
return acc
41-
}, [] as number[])
42-
43-
// Combine the length bytes with the flattened byte array
44-
return new Uint8Array([...lengthBytes, ...flattenedBytes])
45-
}
46-
47-
public encodeHex(data: Uint8Array): string {
48-
// Convert each byte to a two-character hex string
49-
return Array.from(data, byte => byte.toString(16).padStart(2, '0')).join('');
50-
}
51-
52-
public decodeHex(data: string): Uint8Array {
53-
if (data.length % 2 !== 0) {
54-
throw new Error("Invalid hex string: must have an even length.");
55-
}
56-
57-
// Convert the hex string into a Uint8Array
58-
const result = new Uint8Array(data.length / 2);
59-
for (let i = 0; i < data.length; i += 2) {
60-
result[i / 2] = parseInt(data.slice(i, i + 2), 16);
61-
}
62-
63-
return result;
64-
}
65-
6629
public async run(): Promise<void> {
6730
const {argv, flags} = await this.parse(UdtPausableIsPaused)
6831
let lockHashU832Array = [];
69-
console.log('argv:', argv);
7032
for (const lock_hash of argv) {
7133
lockHashU832Array.push(numToBytes(String(lock_hash), 32).reverse());
7234
}
73-
console.log('lockHashU832Array:', lockHashU832Array);
74-
const lockHashU832ArrayEncoded = this.encodeU832Array(lockHashU832Array);
75-
console.log('lockHashArrayEncoded:', lockHashU832ArrayEncoded);
76-
const lockHashU832ArrayEncodedHex = this.encodeHex(lockHashU832ArrayEncoded);
35+
console.debug('lockHashU832Array:', lockHashU832Array);
36+
const lockHashU832ArrayEncoded = encodeU832Array(lockHashU832Array);
37+
console.debug('lockHashArrayEncoded:', lockHashU832ArrayEncoded);
38+
const lockHashU832ArrayEncodedHex = encodeHex(lockHashU832ArrayEncoded);
7739

7840
// Method path hex function
7941
const hasher = new HasherCkb();
@@ -89,7 +51,7 @@ export default class UdtPausableIsPaused extends Command {
8951
jsonrpc: '2.0',
9052
method: 'run_script_level_code',
9153
params: [
92-
"0xe06146dc630f96b3fb0a6cbaf81350b87aa8f195745bdf21d6d4f6de2f53d0cc",
54+
"0x24e477bdae84955713ce9075cc176e87f1c882fa3cedcde4ea3dd6c1ee7b0d5c",
9355
// args.target,
9456
0,
9557
// args.index,

src/commands/udt/transfer.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { ccc, Cell, CellDepLike } from '@ckb-ccc/core'
12
import {Args, Command, Flags} from '@oclif/core'
3+
import { getUDTConfig } from '../../libs/config.js'
24

35
export default class UdtTransfer extends Command {
46
static override args = {
@@ -20,7 +22,55 @@ export default class UdtTransfer extends Command {
2022
// TODO: Get signer
2123
public async run(): Promise<void> {
2224
const {args, flags} = await this.parse(UdtTransfer);
23-
// const newToAddress = ccc.Address.fromString(args.toAddress!, signer.client);
25+
26+
const client = new ccc.ClientPublicTestnet({ url: process.env.CKB_RPC_URL });
27+
const signer = new ccc.SignerCkbPrivateKey(
28+
client,
29+
process.env.PAUSED_WALLET_PRIVATE_KEY!,
30+
);
31+
const toAddress = "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqtxe0gs9yvwrsc40znvdc6sg4fehd2mttsngg4t4";
32+
const toLock = (await ccc.Address.fromString(toAddress, signer.client)).script;
2433

34+
const udtConfig = getUDTConfig();
35+
const udtTypeScript = new ccc.Script(
36+
udtConfig.code_hash,
37+
'type',
38+
udtConfig.args
39+
);
40+
41+
const transferTx = ccc.Transaction.from({
42+
outputs: [
43+
{
44+
lock: toLock,
45+
type: udtTypeScript,
46+
},
47+
],
48+
outputsData: [
49+
ccc.numLeToBytes(50000000, 16)
50+
]
51+
});
52+
await transferTx.completeInputsByUdt(signer, udtTypeScript);
53+
await transferTx.completeInputsByCapacity(signer);
54+
await transferTx.completeFeeBy(signer);
55+
56+
const findCellDepResult = await client.findCells(udtConfig.cellDepSearchKey).next();
57+
const cellDepCell: Cell = findCellDepResult.value;
58+
const cellDepLike: CellDepLike = {
59+
outPoint: {
60+
txHash: cellDepCell.outPoint.txHash,
61+
index: cellDepCell.outPoint.index,
62+
},
63+
depType: 'code',
64+
};
65+
66+
transferTx.addCellDeps(cellDepLike);
67+
const transferTxTxHash = await signer.sendTransaction(transferTx);
68+
69+
console.log(`Minted UDT with transaction hash: ${transferTxTxHash}`);
70+
2571
}
72+
73+
// TODO: Traditional transferring for pausable-udt
74+
75+
// TODO: SSRI transferring for pausable-udt
2676
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export {run} from '@oclif/core'
1+
2+
export {run} from '@oclif/core';

src/lib/vector.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)