xflows 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/index.js +205 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@ A command-line interface for the [Wanchain XFlows](https://docs.wanchain.org/dev
|
|
|
18
18
|
- [Query Commands](#query-commands)
|
|
19
19
|
- [Quote](#quote)
|
|
20
20
|
- [Send Transaction](#send-transaction)
|
|
21
|
+
- [Transfer (Native Token)](#transfer-native-token)
|
|
22
|
+
- [Transfer Token (ERC20)](#transfer-token-erc20)
|
|
21
23
|
- [Transaction Status](#transaction-status)
|
|
22
24
|
- [RPC List](#rpc-list)
|
|
23
25
|
- [Complete Workflow Example](#complete-workflow-example)
|
|
@@ -511,6 +513,76 @@ xflows send \
|
|
|
511
513
|
7. Waits for on-chain confirmation
|
|
512
514
|
8. Prints the transaction hash and a ready-to-use `xflows status` command for tracking
|
|
513
515
|
|
|
516
|
+
### Transfer (Native Token)
|
|
517
|
+
|
|
518
|
+
Send native tokens (ETH, BNB, WAN, etc.) on the same chain. This is a simple transfer, not a cross-chain bridge operation.
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
# Send 0.1 ETH on Ethereum
|
|
522
|
+
xflows transfer --wallet alice --chain-id 1 --to 0xRecipient --amount 0.1
|
|
523
|
+
|
|
524
|
+
# Send 1.5 BNB on BSC with encrypted wallet
|
|
525
|
+
xflows transfer --wallet alice --password mysecret --chain-id 56 --to 0xRecipient --amount 1.5
|
|
526
|
+
|
|
527
|
+
# Dry run (preview without sending)
|
|
528
|
+
xflows transfer --wallet alice --chain-id 1 --to 0xRecipient --amount 0.1 --dry-run
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
| Flag | Required | Description |
|
|
532
|
+
|------|----------|-------------|
|
|
533
|
+
| `--wallet <name>` | Yes | Wallet name to use for signing |
|
|
534
|
+
| `--chain-id <id>` | Yes | Chain ID to send on |
|
|
535
|
+
| `--to <address>` | Yes | Recipient address |
|
|
536
|
+
| `--amount <amount>` | Yes | Amount to send (human-readable, e.g., `0.1`) |
|
|
537
|
+
| `--password <pw>` | No | Password for encrypted wallet |
|
|
538
|
+
| `--rpc <url>` | No | Override default RPC endpoint |
|
|
539
|
+
| `--gas-limit <limit>` | No | Custom gas limit |
|
|
540
|
+
| `--dry-run` | No | Build but do not send the transaction |
|
|
541
|
+
|
|
542
|
+
### Transfer Token (ERC20)
|
|
543
|
+
|
|
544
|
+
Send ERC20 tokens on the same chain. Token decimals are auto-detected from the contract, or can be specified manually.
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
# Send 100 USDC on Ethereum (auto-detect decimals)
|
|
548
|
+
xflows transfer-token --wallet alice --chain-id 1 \
|
|
549
|
+
--token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
|
|
550
|
+
--to 0xRecipient --amount 100
|
|
551
|
+
|
|
552
|
+
# Send with explicit decimals
|
|
553
|
+
xflows transfer-token --wallet alice --chain-id 1 \
|
|
554
|
+
--token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
|
|
555
|
+
--to 0xRecipient --amount 100 --decimals 6
|
|
556
|
+
|
|
557
|
+
# Send 50 USDT on BSC with encrypted wallet
|
|
558
|
+
xflows transfer-token --wallet alice --password mysecret --chain-id 56 \
|
|
559
|
+
--token 0x55d398326f99059fF775485246999027B3197955 \
|
|
560
|
+
--to 0xRecipient --amount 50
|
|
561
|
+
|
|
562
|
+
# Dry run (preview without sending)
|
|
563
|
+
xflows transfer-token --wallet alice --chain-id 1 \
|
|
564
|
+
--token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
|
|
565
|
+
--to 0xRecipient --amount 100 --dry-run
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
| Flag | Required | Description |
|
|
569
|
+
|------|----------|-------------|
|
|
570
|
+
| `--wallet <name>` | Yes | Wallet name to use for signing |
|
|
571
|
+
| `--chain-id <id>` | Yes | Chain ID to send on |
|
|
572
|
+
| `--token <address>` | Yes | ERC20 token contract address |
|
|
573
|
+
| `--to <address>` | Yes | Recipient address |
|
|
574
|
+
| `--amount <amount>` | Yes | Amount to send (human-readable, e.g., `100`) |
|
|
575
|
+
| `--decimals <n>` | No | Token decimals (auto-detected if omitted) |
|
|
576
|
+
| `--password <pw>` | No | Password for encrypted wallet |
|
|
577
|
+
| `--rpc <url>` | No | Override default RPC endpoint |
|
|
578
|
+
| `--gas-limit <limit>` | No | Custom gas limit |
|
|
579
|
+
| `--dry-run` | No | Build but do not send the transaction |
|
|
580
|
+
|
|
581
|
+
**Features:**
|
|
582
|
+
- Auto-detects token decimals and symbol from the contract
|
|
583
|
+
- Checks token balance before sending to provide a clear error message
|
|
584
|
+
- Supports Wanchain gas price enforcement
|
|
585
|
+
|
|
514
586
|
### Transaction Status
|
|
515
587
|
|
|
516
588
|
Check the current state of a cross-chain transaction.
|
package/dist/index.js
CHANGED
|
@@ -3955,6 +3955,9 @@ function parseUnits(value, unit) {
|
|
|
3955
3955
|
}
|
|
3956
3956
|
return FixedNumber.fromString(value, { decimals, width: 512 }).value;
|
|
3957
3957
|
}
|
|
3958
|
+
function parseEther(ether) {
|
|
3959
|
+
return parseUnits(ether, 18);
|
|
3960
|
+
}
|
|
3958
3961
|
// node_modules/ethers/lib.esm/utils/uuid.js
|
|
3959
3962
|
function uuidV4(randomBytes) {
|
|
3960
3963
|
const bytes = getBytes(randomBytes, "randomBytes");
|
|
@@ -18654,8 +18657,66 @@ import * as crypto2 from "crypto";
|
|
|
18654
18657
|
import * as fs from "fs";
|
|
18655
18658
|
import * as path from "path";
|
|
18656
18659
|
import * as readline from "readline";
|
|
18660
|
+
// package.json
|
|
18661
|
+
var package_default = {
|
|
18662
|
+
name: "xflows",
|
|
18663
|
+
version: "1.1.0",
|
|
18664
|
+
description: "CLI tool for Wanchain XFlows cross-chain bridge - wallet management, quote queries, and cross-chain transactions",
|
|
18665
|
+
type: "module",
|
|
18666
|
+
bin: {
|
|
18667
|
+
xflows: "dist/index.js"
|
|
18668
|
+
},
|
|
18669
|
+
files: [
|
|
18670
|
+
"dist",
|
|
18671
|
+
"README.md",
|
|
18672
|
+
"LICENSE"
|
|
18673
|
+
],
|
|
18674
|
+
scripts: {
|
|
18675
|
+
build: "bun build src/index.ts --outfile dist/index.js --target node --format esm && chmod +x dist/index.js",
|
|
18676
|
+
prepublishOnly: "bun run build",
|
|
18677
|
+
start: "bun src/index.ts",
|
|
18678
|
+
test: "bun test"
|
|
18679
|
+
},
|
|
18680
|
+
keywords: [
|
|
18681
|
+
"wanchain",
|
|
18682
|
+
"xflows",
|
|
18683
|
+
"cross-chain",
|
|
18684
|
+
"bridge",
|
|
18685
|
+
"cli",
|
|
18686
|
+
"defi",
|
|
18687
|
+
"web3",
|
|
18688
|
+
"evm",
|
|
18689
|
+
"swap"
|
|
18690
|
+
],
|
|
18691
|
+
author: "lolieatapple@gmail.com",
|
|
18692
|
+
license: "MIT",
|
|
18693
|
+
repository: {
|
|
18694
|
+
type: "git",
|
|
18695
|
+
url: "git+https://github.com/wandevs/xflows-cli.git"
|
|
18696
|
+
},
|
|
18697
|
+
bugs: {
|
|
18698
|
+
url: "https://github.com/wandevs/xflows-cli/issues"
|
|
18699
|
+
},
|
|
18700
|
+
homepage: "https://github.com/wandevs/xflows-cli#readme",
|
|
18701
|
+
engines: {
|
|
18702
|
+
node: ">=18"
|
|
18703
|
+
},
|
|
18704
|
+
devDependencies: {
|
|
18705
|
+
"@types/bun": "latest"
|
|
18706
|
+
},
|
|
18707
|
+
peerDependencies: {
|
|
18708
|
+
typescript: "^5"
|
|
18709
|
+
},
|
|
18710
|
+
dependencies: {
|
|
18711
|
+
chalk: "^5.6.2",
|
|
18712
|
+
commander: "^14.0.3",
|
|
18713
|
+
ethers: "^6.16.0"
|
|
18714
|
+
}
|
|
18715
|
+
};
|
|
18716
|
+
|
|
18717
|
+
// src/index.ts
|
|
18657
18718
|
var API_BASE = "https://xflows.wanchain.org/api/v3";
|
|
18658
|
-
var VERSION =
|
|
18719
|
+
var VERSION = package_default.version;
|
|
18659
18720
|
var RPC_MAP = {
|
|
18660
18721
|
"1": "https://ethereum-rpc.publicnode.com",
|
|
18661
18722
|
"56": "https://bsc-rpc.publicnode.com",
|
|
@@ -18768,14 +18829,18 @@ function printJson(data) {
|
|
|
18768
18829
|
}
|
|
18769
18830
|
var ERC20_ABI = [
|
|
18770
18831
|
"function approve(address spender, uint256 amount) returns (bool)",
|
|
18771
|
-
"function allowance(address owner, address spender) view returns (uint256)"
|
|
18832
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
18833
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
18834
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
18835
|
+
"function decimals() view returns (uint8)",
|
|
18836
|
+
"function symbol() view returns (string)"
|
|
18772
18837
|
];
|
|
18773
18838
|
var program2 = new Command;
|
|
18774
18839
|
program2.name("xflows").version(VERSION).description(`XFlows Cross-Chain Bridge CLI Tool
|
|
18775
18840
|
|
|
18776
18841
|
` + `A command-line interface for Wanchain XFlows cross-chain bridge.
|
|
18777
18842
|
` + `Supports wallet management, quote queries, cross-chain transactions,
|
|
18778
|
-
` + `and all XFlows API query endpoints.
|
|
18843
|
+
` + `same-chain native/ERC20 transfers, and all XFlows API query endpoints.
|
|
18779
18844
|
|
|
18780
18845
|
` + `Wallet files are stored in ~/.xflows/wallets/
|
|
18781
18846
|
|
|
@@ -18784,6 +18849,8 @@ program2.name("xflows").version(VERSION).description(`XFlows Cross-Chain Bridge
|
|
|
18784
18849
|
` + ` xflows wallet create --name secureWallet --encrypt
|
|
18785
18850
|
` + ` xflows chains
|
|
18786
18851
|
` + ` xflows tokens --chain-id 1
|
|
18852
|
+
` + ` xflows transfer --wallet myWallet --chain-id 1 --to 0x... --amount 0.1
|
|
18853
|
+
` + ` xflows transfer-token --wallet myWallet --chain-id 1 --token 0x... --to 0x... --amount 100
|
|
18787
18854
|
` + ` xflows quote --from-chain 1 --to-chain 56 --from-token 0x0...0 --to-token 0x0...0 --from-address 0x... --to-address 0x... --amount 1.0
|
|
18788
18855
|
` + ` xflows send --wallet myWallet --from-chain 1 --to-chain 56 --from-token 0x0...0 --to-token 0x0...0 --to-address 0x... --amount 1.0
|
|
18789
18856
|
` + " xflows status --hash 0x... --from-chain 1 --to-chain 56 --from-token 0x0...0 --to-token 0x0...0 --from-address 0x... --to-address 0x... --amount 1.0");
|
|
@@ -19123,6 +19190,141 @@ You can track the cross-chain status with:`);
|
|
|
19123
19190
|
process.exit(1);
|
|
19124
19191
|
}
|
|
19125
19192
|
});
|
|
19193
|
+
program2.command("transfer").description(`Send native tokens (ETH/BNB/WAN/etc.) on the same chain
|
|
19194
|
+
|
|
19195
|
+
` + `This is a simple same-chain transfer, NOT a cross-chain bridge operation.
|
|
19196
|
+
|
|
19197
|
+
` + `Examples:
|
|
19198
|
+
` + ` # Send 0.1 ETH on Ethereum
|
|
19199
|
+
` + ` xflows transfer --wallet alice --chain-id 1 --to 0xRecipient --amount 0.1
|
|
19200
|
+
|
|
19201
|
+
` + ` # Send 1.5 BNB on BSC with encrypted wallet
|
|
19202
|
+
` + " xflows transfer --wallet alice --password mysecret --chain-id 56 --to 0xRecipient --amount 1.5").requiredOption("--wallet <name>", "Wallet name to use for signing").requiredOption("--chain-id <chainId>", "Chain ID to send on").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Amount to send (human-readable, e.g., 0.1)").option("--password <password>", "Password to decrypt encrypted wallet").option("--rpc <url>", "Custom RPC URL (overrides default)").option("--gas-limit <limit>", "Custom gas limit").option("--dry-run", "Only build the transaction, don't send it", false).action(async (opts) => {
|
|
19203
|
+
try {
|
|
19204
|
+
const wallet = loadWallet(opts.wallet, opts.password);
|
|
19205
|
+
const provider = opts.rpc ? new JsonRpcProvider(opts.rpc) : getProvider2(opts.chainId);
|
|
19206
|
+
const signer = wallet.connect(provider);
|
|
19207
|
+
const txRequest = {
|
|
19208
|
+
to: opts.to,
|
|
19209
|
+
value: parseEther(opts.amount)
|
|
19210
|
+
};
|
|
19211
|
+
if (opts.gasLimit) {
|
|
19212
|
+
txRequest.gasLimit = BigInt(opts.gasLimit);
|
|
19213
|
+
}
|
|
19214
|
+
const isWanchain = opts.chainId === "888";
|
|
19215
|
+
if (isWanchain) {
|
|
19216
|
+
const feeData = await provider.getFeeData();
|
|
19217
|
+
const minBaseFee = parseUnits("1", "gwei");
|
|
19218
|
+
if (feeData.gasPrice && feeData.gasPrice < minBaseFee) {
|
|
19219
|
+
txRequest.gasPrice = minBaseFee;
|
|
19220
|
+
console.log("Wanchain: enforcing minimum gasPrice of 1 gwei");
|
|
19221
|
+
} else if (feeData.maxFeePerGas) {
|
|
19222
|
+
const maxFee = feeData.maxFeePerGas < minBaseFee ? minBaseFee : feeData.maxFeePerGas;
|
|
19223
|
+
txRequest.gasPrice = maxFee;
|
|
19224
|
+
console.log(`Wanchain: using gasPrice ${formatUnits(maxFee, "gwei")} gwei`);
|
|
19225
|
+
}
|
|
19226
|
+
}
|
|
19227
|
+
if (opts.dryRun) {
|
|
19228
|
+
console.log("[Dry Run] Transaction details:");
|
|
19229
|
+
printJson({
|
|
19230
|
+
from: wallet.address,
|
|
19231
|
+
to: opts.to,
|
|
19232
|
+
value: txRequest.value.toString(),
|
|
19233
|
+
chainId: opts.chainId
|
|
19234
|
+
});
|
|
19235
|
+
return;
|
|
19236
|
+
}
|
|
19237
|
+
console.log(`Sending ${opts.amount} native token to ${opts.to} on chain ${opts.chainId}...`);
|
|
19238
|
+
const sentTx = await signer.sendTransaction(txRequest);
|
|
19239
|
+
console.log(`Transaction hash: ${sentTx.hash}`);
|
|
19240
|
+
console.log("Waiting for confirmation...");
|
|
19241
|
+
const receipt = await sentTx.wait();
|
|
19242
|
+
console.log(`Transaction confirmed in block ${receipt?.blockNumber}`);
|
|
19243
|
+
console.log(`Gas used: ${receipt?.gasUsed.toString()}`);
|
|
19244
|
+
} catch (e) {
|
|
19245
|
+
console.error(`Error: ${e.message}`);
|
|
19246
|
+
process.exit(1);
|
|
19247
|
+
}
|
|
19248
|
+
});
|
|
19249
|
+
program2.command("transfer-token").description(`Send ERC20 tokens on the same chain
|
|
19250
|
+
|
|
19251
|
+
` + `This is a simple same-chain ERC20 transfer, NOT a cross-chain bridge operation.
|
|
19252
|
+
|
|
19253
|
+
` + `Examples:
|
|
19254
|
+
` + ` # Send 100 USDC on Ethereum (6 decimals)
|
|
19255
|
+
` + " xflows transfer-token --wallet alice --chain-id 1 \\\n" + " --token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \\\n" + ` --to 0xRecipient --amount 100 --decimals 6
|
|
19256
|
+
|
|
19257
|
+
` + ` # Send 50 USDT on BSC (auto-detect decimals)
|
|
19258
|
+
` + " xflows transfer-token --wallet alice --chain-id 56 \\\n" + " --token 0x55d398326f99059fF775485246999027B3197955 \\\n" + " --to 0xRecipient --amount 50").requiredOption("--wallet <name>", "Wallet name to use for signing").requiredOption("--chain-id <chainId>", "Chain ID to send on").requiredOption("--token <address>", "ERC20 token contract address").requiredOption("--to <address>", "Recipient address").requiredOption("--amount <amount>", "Amount to send (human-readable, e.g., 100)").option("--decimals <decimals>", "Token decimals (auto-detected from contract if omitted)").option("--password <password>", "Password to decrypt encrypted wallet").option("--rpc <url>", "Custom RPC URL (overrides default)").option("--gas-limit <limit>", "Custom gas limit").option("--dry-run", "Only build the transaction, don't send it", false).action(async (opts) => {
|
|
19259
|
+
try {
|
|
19260
|
+
const wallet = loadWallet(opts.wallet, opts.password);
|
|
19261
|
+
const provider = opts.rpc ? new JsonRpcProvider(opts.rpc) : getProvider2(opts.chainId);
|
|
19262
|
+
const signer = wallet.connect(provider);
|
|
19263
|
+
const tokenContract = new Contract(opts.token, ERC20_ABI, signer);
|
|
19264
|
+
let decimals;
|
|
19265
|
+
if (opts.decimals !== undefined) {
|
|
19266
|
+
decimals = Number(opts.decimals);
|
|
19267
|
+
} else {
|
|
19268
|
+
try {
|
|
19269
|
+
decimals = Number(await tokenContract.decimals());
|
|
19270
|
+
console.log(`Token decimals: ${decimals}`);
|
|
19271
|
+
} catch {
|
|
19272
|
+
throw new Error("Could not auto-detect token decimals. Please provide --decimals manually.");
|
|
19273
|
+
}
|
|
19274
|
+
}
|
|
19275
|
+
let symbol = "TOKEN";
|
|
19276
|
+
try {
|
|
19277
|
+
symbol = await tokenContract.symbol();
|
|
19278
|
+
} catch {}
|
|
19279
|
+
const amount = parseUnits(opts.amount, decimals);
|
|
19280
|
+
const balance = await tokenContract.balanceOf(wallet.address);
|
|
19281
|
+
if (balance < amount) {
|
|
19282
|
+
console.error(`Insufficient ${symbol} balance: ${formatUnits(balance, decimals)} < ${opts.amount}`);
|
|
19283
|
+
process.exit(1);
|
|
19284
|
+
}
|
|
19285
|
+
if (opts.dryRun) {
|
|
19286
|
+
console.log("[Dry Run] Transaction details:");
|
|
19287
|
+
printJson({
|
|
19288
|
+
from: wallet.address,
|
|
19289
|
+
to: opts.to,
|
|
19290
|
+
token: opts.token,
|
|
19291
|
+
symbol,
|
|
19292
|
+
amount: opts.amount,
|
|
19293
|
+
amountWei: amount.toString(),
|
|
19294
|
+
decimals,
|
|
19295
|
+
chainId: opts.chainId
|
|
19296
|
+
});
|
|
19297
|
+
return;
|
|
19298
|
+
}
|
|
19299
|
+
console.log(`Sending ${opts.amount} ${symbol} to ${opts.to} on chain ${opts.chainId}...`);
|
|
19300
|
+
const txOverrides = {};
|
|
19301
|
+
if (opts.gasLimit) {
|
|
19302
|
+
txOverrides.gasLimit = BigInt(opts.gasLimit);
|
|
19303
|
+
}
|
|
19304
|
+
const isWanchain = opts.chainId === "888";
|
|
19305
|
+
if (isWanchain) {
|
|
19306
|
+
const feeData = await provider.getFeeData();
|
|
19307
|
+
const minBaseFee = parseUnits("1", "gwei");
|
|
19308
|
+
if (feeData.gasPrice && feeData.gasPrice < minBaseFee) {
|
|
19309
|
+
txOverrides.gasPrice = minBaseFee;
|
|
19310
|
+
console.log("Wanchain: enforcing minimum gasPrice of 1 gwei");
|
|
19311
|
+
} else if (feeData.maxFeePerGas) {
|
|
19312
|
+
const maxFee = feeData.maxFeePerGas < minBaseFee ? minBaseFee : feeData.maxFeePerGas;
|
|
19313
|
+
txOverrides.gasPrice = maxFee;
|
|
19314
|
+
console.log(`Wanchain: using gasPrice ${formatUnits(maxFee, "gwei")} gwei`);
|
|
19315
|
+
}
|
|
19316
|
+
}
|
|
19317
|
+
const sentTx = await tokenContract.transfer(opts.to, amount, txOverrides);
|
|
19318
|
+
console.log(`Transaction hash: ${sentTx.hash}`);
|
|
19319
|
+
console.log("Waiting for confirmation...");
|
|
19320
|
+
const receipt = await sentTx.wait();
|
|
19321
|
+
console.log(`Transaction confirmed in block ${receipt?.blockNumber}`);
|
|
19322
|
+
console.log(`Gas used: ${receipt?.gasUsed.toString()}`);
|
|
19323
|
+
} catch (e) {
|
|
19324
|
+
console.error(`Error: ${e.message}`);
|
|
19325
|
+
process.exit(1);
|
|
19326
|
+
}
|
|
19327
|
+
});
|
|
19126
19328
|
program2.command("status").description(`Check cross-chain transaction status
|
|
19127
19329
|
|
|
19128
19330
|
` + `Status codes:
|