-
Notifications
You must be signed in to change notification settings - Fork 8
feat: add UDT balance and transfer CLI commands #447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
0cc645f
f646dba
25ec278
5ba6515
288a8b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@offckb/cli": minor | ||
| --- | ||
|
|
||
| Refactor UDT CLI support: reuse `balance` and `transfer` commands for CKB and UDT queries, and add `offckb udt issue` / `offckb udt destroy` subcommands. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| #!/usr/bin/env node | ||
| import { Command } from 'commander'; | ||
| import { Command, Option } from 'commander'; | ||
| import { startNode, stopNode } from './cmd/node'; | ||
| import { accounts } from './cmd/accounts'; | ||
| import { clean } from './cmd/clean'; | ||
|
|
@@ -8,6 +8,7 @@ import { DepositOptions, deposit } from './cmd/deposit'; | |
| import { DeployOptions, deploy } from './cmd/deploy'; | ||
| import { TransferOptions, transfer } from './cmd/transfer'; | ||
| import { BalanceOption, balanceOf } from './cmd/balance'; | ||
| import { udtIssue, udtDestroy, UdtIssueOption, UdtDestroyOption } from './cmd/udt'; | ||
| import { createScriptProject, CreateScriptProjectOptions } from './cmd/create'; | ||
| import { Config, ConfigItem } from './cmd/config'; | ||
| import { devnetConfig } from './cmd/devnet-config'; | ||
|
|
@@ -137,13 +138,15 @@ program | |
| }); | ||
|
|
||
| program | ||
| .command('transfer [toAddress] [amountInCKB]') | ||
| .description('Transfer CKB tokens to address, only devnet and testnet') | ||
| .command('transfer [toAddress] [amount]') | ||
| .description('Transfer CKB or UDT tokens to address, only devnet and testnet') | ||
| .option('--network <network>', 'Specify the network to transfer to', 'devnet') | ||
| .option('--privkey <privkey>', 'Specify the private key to transfer CKB') | ||
| .option('--privkey <privkey>', 'Specify the private key to transfer') | ||
| .addOption(new Option('--udt-kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt')) | ||
| .option('--udt-type-args <typeArgs>', 'Specify the UDT type script args to transfer UDT') | ||
| .option('-r, --proxy-rpc', 'Use Proxy RPC to connect to blockchain') | ||
| .action(async (toAddress: string, amountInCKB: string, options: TransferOptions) => { | ||
| return transfer(toAddress, amountInCKB, options); | ||
| .action(async (toAddress: string, amount: string, options: TransferOptions) => { | ||
| return transfer(toAddress, amount, options); | ||
| }); | ||
|
|
||
| program | ||
|
|
@@ -158,12 +161,39 @@ program | |
|
|
||
| program | ||
| .command('balance [toAddress]') | ||
| .description('Check account balance, only devnet and testnet') | ||
| .description('Check account balance (CKB + detected SUDT/xUDT), only devnet and testnet') | ||
| .option('--network <network>', 'Specify the network to check', 'devnet') | ||
| .addOption(new Option('--udt-kind <kind>', 'Filter by UDT kind').choices(['sudt', 'xudt'])) | ||
| .option('--udt-type-args <typeArgs>', 'Filter by UDT type script args') | ||
| .action(async (toAddress: string, options: BalanceOption) => { | ||
| return balanceOf(toAddress, options); | ||
| }); | ||
|
|
||
| const udtCommand = program.command('udt').description('UDT token commands'); | ||
|
|
||
| udtCommand | ||
| .command('issue <amount>') | ||
| .description('Issue new UDT tokens, only devnet and testnet') | ||
| .option('--network <network>', 'Specify the network', 'devnet') | ||
| .addOption(new Option('--kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt')) | ||
| .option('--type-args <typeArgs>', 'Specify the UDT type script args (xudt only; defaults to signer lock hash)') | ||
| .option('--to <toAddress>', 'Specify the receiver address (defaults to signer)') | ||
| .option('--privkey <privkey>', 'Specify the private key to issue UDT') | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛑 [CRITICAL / Security] Accepting the private key via |
||
| .action(async (amount: string, options: UdtIssueOption) => { | ||
| return udtIssue(amount, options); | ||
| }); | ||
|
|
||
| udtCommand | ||
| .command('destroy <amount>') | ||
| .description('Destroy UDT tokens, only devnet and testnet') | ||
| .option('--network <network>', 'Specify the network', 'devnet') | ||
| .addOption(new Option('--kind <kind>', 'Specify the UDT kind').choices(['sudt', 'xudt']).default('sudt')) | ||
| .requiredOption('--type-args <typeArgs>', 'Specify the UDT type script args') | ||
| .option('--privkey <privkey>', 'Specify the private key to destroy UDT') | ||
| .action(async (amount: string, options: UdtDestroyOption) => { | ||
| return udtDestroy(amount, options); | ||
| }); | ||
|
|
||
| program | ||
| .command('debugger') | ||
| .description('Port of the raw CKB Standalone Debugger') | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,12 @@ | ||
| import { CKB } from '../sdk/ckb'; | ||
| import { CKB, UdtBalanceInfo, UdtKind } from '../sdk/ckb'; | ||
| import { validateNetworkOpt } from '../util/validator'; | ||
| import { NetworkOption, Network } from '../type/base'; | ||
| import { logger } from '../util/logger'; | ||
|
|
||
| export interface BalanceOption extends NetworkOption {} | ||
| export interface BalanceOption extends NetworkOption { | ||
| udtKind?: UdtKind; | ||
| udtTypeArgs?: string; | ||
| } | ||
|
|
||
| export async function balanceOf(address: string, opt: BalanceOption = { network: Network.devnet }) { | ||
| const network = opt.network; | ||
|
|
@@ -12,6 +15,27 @@ export async function balanceOf(address: string, opt: BalanceOption = { network: | |
| const ckb = new CKB({ network }); | ||
|
|
||
| const balanceInCKB = await ckb.balance(address); | ||
| logger.info(`Balance: ${balanceInCKB} CKB`); | ||
| process.exit(0); | ||
| logger.info(`CKB: ${balanceInCKB}`); | ||
|
|
||
| const udtBalances = await ckb.detectUdtBalances(address); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| const filtered = filterUdtBalances(udtBalances, opt); | ||
|
|
||
| if (filtered.length > 0) { | ||
| logger.info('UDT:'); | ||
| for (const udt of filtered) { | ||
| logger.info(` ${udt.kind} (args=${udt.args}): ${udt.balance}`); | ||
| } | ||
| } | ||
| } | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| function filterUdtBalances(balances: UdtBalanceInfo[], opt: BalanceOption): UdtBalanceInfo[] { | ||
| if (!opt.udtKind && !opt.udtTypeArgs) { | ||
| return balances; | ||
| } | ||
|
|
||
| return balances.filter((udt) => { | ||
| const kindMatch = opt.udtKind ? udt.kind === opt.udtKind : true; | ||
| const argsMatch = opt.udtTypeArgs ? udt.args === opt.udtTypeArgs : true; | ||
| return kindMatch && argsMatch; | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import { CKB, UdtKind } from '../sdk/ckb'; | ||
| import { NetworkOption, Network } from '../type/base'; | ||
| import { buildTestnetTxLink } from '../util/link'; | ||
| import { validateNetworkOpt, validateUdtKind, validateUdtTypeArgs } from '../util/validator'; | ||
| import { logger } from '../util/logger'; | ||
|
|
||
| export interface UdtIssueOption extends NetworkOption { | ||
| kind: UdtKind; | ||
| typeArgs?: string; | ||
| to?: string; | ||
| privkey: string; | ||
| } | ||
|
|
||
| export interface UdtDestroyOption extends NetworkOption { | ||
| kind: UdtKind; | ||
| typeArgs: string; | ||
| privkey: string; | ||
| } | ||
|
|
||
| export async function udtIssue( | ||
| amount: string, | ||
| opt: UdtIssueOption = { network: Network.devnet, kind: 'sudt', privkey: '' }, | ||
| ) { | ||
| const network = opt.network; | ||
| validateNetworkOpt(network); | ||
| validateUdtKind(opt.kind); | ||
|
|
||
| if (!opt.privkey) { | ||
| throw new Error('--privkey is required!'); | ||
| } | ||
|
|
||
| const ckb = new CKB({ network }); | ||
| const txHash = await ckb.udtIssue({ | ||
| privateKey: opt.privkey, | ||
| kind: opt.kind, | ||
| amount, | ||
| typeArgs: opt.typeArgs ? validateUdtTypeArgs(opt.kind, opt.typeArgs) : undefined, | ||
| toAddress: opt.to, | ||
| }); | ||
|
|
||
| if (network === 'testnet') { | ||
| logger.info(`Successfully issued UDT, check ${buildTestnetTxLink(txHash)} for details.`); | ||
| return; | ||
| } | ||
| logger.info('Successfully issued UDT, txHash:', txHash); | ||
| } | ||
|
|
||
| export async function udtDestroy( | ||
| amount: string, | ||
| opt: UdtDestroyOption = { network: Network.devnet, kind: 'sudt', typeArgs: '', privkey: '' }, | ||
| ) { | ||
| const network = opt.network; | ||
| validateNetworkOpt(network); | ||
| validateUdtKind(opt.kind); | ||
|
|
||
| if (!opt.privkey) { | ||
| throw new Error('--privkey is required!'); | ||
| } | ||
|
|
||
| const ckb = new CKB({ network }); | ||
| const txHash = await ckb.udtDestroy({ | ||
| privateKey: opt.privkey, | ||
| kind: opt.kind, | ||
| amount, | ||
| typeArgs: validateUdtTypeArgs(opt.kind, opt.typeArgs), | ||
| }); | ||
|
|
||
| if (network === 'testnet') { | ||
| logger.info(`Successfully destroyed UDT, check ${buildTestnetTxLink(txHash)} for details.`); | ||
| return; | ||
| } | ||
| logger.info('Successfully destroyed UDT, txHash:', txHash); | ||
| } |
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.
--kindhere, buttransfer/balanceuse--udt-kind. Unifying the flag name would make the CLI more predictable for users.