Skip to content

Commit 8995e9c

Browse files
committed
docs: rewrite playground examples — use EVM_TESTNET, add @wallet and @contracts examples
- Playground.tsx: remove network-config.ts injection, hard-code testnet label, bump @cfxdevkit/core to ^1.0.16 - _meta.js: group labels by package (@core, @wallet, @contracts) - espace-block-number.mdx → @core · EspaceClient: full EspaceClient tour (connection, block, gas, block details) - read-balance.mdx → @core · Balances: native CFX + ERC-20 balance reads via client.publicClient.readContract - erc20-info.mdx → @core · ERC-20 Info: batch metadata + holder balance/allowance; fix client.publicClient.readContract refs - send-cfx.mdx → @wallet · Mnemonic: generateMnemonic/validateMnemonic/deriveAccounts/deriveAccount - swap-quote.mdx → @contracts · ABIs: inspect bundled ABIs + live WCFX read; add @cfxdevkit/contracts extraDeps - deploy-token.mdx → @contracts · Deploy: erc20BaseAbi + erc20BaseBytecode deploy example - index.mdx: updated overview with new structure
1 parent 28a828e commit 8995e9c

File tree

9 files changed

+295
-428
lines changed

9 files changed

+295
-428
lines changed

docs-site/components/Playground.tsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,6 @@ const T = {
2424
runDisabledText: '#666',
2525
} as const
2626

27-
// ── Conflux network configs ──────────────────────────────────────────────────
28-
const NETWORKS = {
29-
testnet: {
30-
label: 'Testnet',
31-
chainId: 71,
32-
rpcUrl: 'https://evmtestnet.confluxrpc.com',
33-
blockExplorer: 'https://evmtestnet.confluxscan.io',
34-
},
35-
} as const
36-
3727
// ── Run button — must be inside SandpackProvider ─────────────────────────────
3828
function RunButton() {
3929
const { sandpack } = useSandpack()
@@ -69,7 +59,6 @@ function RunButton() {
6959

7060
// ── Network label ─────────────────────────────────────────────────────────────
7161
function NetworkLabel() {
72-
const cfg = NETWORKS.testnet
7362
return (
7463
<div
7564
style={{
@@ -92,10 +81,10 @@ function NetworkLabel() {
9281
whiteSpace: 'nowrap',
9382
}}
9483
>
95-
{cfg.label}
84+
Testnet
9685
</span>
9786
<span style={{ fontSize: '10px', whiteSpace: 'nowrap' }}>
98-
· chain {cfg.chainId}
87+
· chain 71
9988
</span>
10089
</div>
10190
)
@@ -112,7 +101,7 @@ interface PlaygroundProps {
112101

113102
const DEFAULT_DEPS: Record<string, string> = {
114103
viem: '^2.0.0',
115-
'@cfxdevkit/core': '^1.0.15',
104+
'@cfxdevkit/core': '^1.0.16',
116105
}
117106

118107
// ── Main component ────────────────────────────────────────────────────────────
@@ -123,12 +112,7 @@ export function Playground({
123112
extraDeps = {},
124113
file = 'index.ts',
125114
}: PlaygroundProps) {
126-
const networkConfig = NETWORKS.testnet
127-
128-
const networkFile = `// Auto-generated network config\nexport const NETWORK = {\n chainId: ${networkConfig.chainId},\n rpcUrl: '${networkConfig.rpcUrl}',\n blockExplorer: '${networkConfig.blockExplorer}',\n} as const\n`
129-
130115
const mergedFiles = {
131-
'/network-config.ts': { code: networkFile, readOnly: true },
132116
...Object.fromEntries(
133117
Object.entries(files).map(([name, code]) => [
134118
name.startsWith('/') ? name : `/${name}`,
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
export default {
22
index: 'Overview',
3-
'espace-block-number': 'Block Number',
4-
'read-balance': 'Read Balance',
5-
'erc20-info': 'ERC-20 Token Info',
6-
'swap-quote': 'Swap Quote',
7-
'send-cfx': 'Send CFX',
8-
'deploy-token': 'Deploy ERC-20',
3+
'espace-block-number': '@core · EspaceClient',
4+
'read-balance': '@core · Balances',
5+
'erc20-info': '@core · ERC-20 Info',
6+
'send-cfx': '@wallet · Mnemonic',
7+
'swap-quote': '@contracts · ABIs',
8+
'deploy-token': '@contracts · Deploy',
99
}
Lines changed: 63 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,91 @@
11
---
22
title: Deploy ERC-20
3-
description: Deploy a full-featured ERC-20 token from the cfxdevkit bootstrap library.
3+
description: Use @cfxdevkit/contracts bootstrap bytecode and ABI to deploy a production-ready ERC-20 token.
44
---
55

66
import { Callout } from 'nextra/components'
77

8-
# Deploy ERC-20
8+
# Deploy ERC-20 (Bootstrap)
99

10-
Deploy the `ERC20Base` bootstrap contract — a capped, burnable, pausable ERC-20 with ERC-2612 permit and role-based access control.
11-
12-
<Callout type="warning">
13-
This example **simulates** the deploy calldata and gas estimate. To actually send the transaction you need a private key — paste yours into the `PRIVATE_KEY` constant (use a testnet wallet only!).
14-
</Callout>
10+
Use the `erc20BaseAbi` and `erc20BaseBytecode` from `@cfxdevkit/contracts` to deploy a production-ready ERC-20 with one function call.
1511

1612
<Playground
13+
extraDeps={{ "@cfxdevkit/contracts": "^1.0.16" }}
1714
file="/index.ts"
1815
files={{
19-
"/index.ts": `import { EspaceWalletClient, ERC20_ABI, parseUnits, formatUnits } from '@cfxdevkit/core'
20-
import { NETWORK } from './network-config'
21-
22-
// ── Config ─────────────────────────────────────────────────
23-
// ⚠️ TESTNET key only — replace before sending real transactions
24-
const PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
25-
const TOKEN_NAME = 'Demo Token'
26-
const TOKEN_SYMBOL = 'DEMO'
27-
const INITIAL_SUPPLY = parseUnits('1000000', 18) // 1 000 000 DEMO
28-
29-
// ── Create cfxdevkit wallet client ─────────────────────────
30-
const wallet = new EspaceWalletClient({
31-
chainId: NETWORK.chainId,
32-
rpcUrl: NETWORK.rpcUrl,
33-
privateKey: PRIVATE_KEY,
34-
})
35-
36-
async function main() {
37-
console.log('Network: ', NETWORK.rpcUrl)
38-
console.log('Deployer:', wallet.getAddress())
39-
console.log()
40-
41-
console.log('Token name: ', TOKEN_NAME)
42-
console.log('Token symbol: ', TOKEN_SYMBOL)
43-
console.log('Initial supply:', formatUnits(INITIAL_SUPPLY, 18), TOKEN_SYMBOL)
44-
console.log()
45-
46-
// ERC20_ABI is exported directly from @cfxdevkit/core
47-
const fns = (ERC20_ABI as any[]).filter(e => e.type === 'function')
48-
console.log('ERC20_ABI has', fns.length, 'functions:')
49-
fns.slice(0, 8).forEach((f: any) => {
50-
console.log(' •', f.name, '(' + f.stateMutability + ')')
51-
})
52-
console.log(' ...')
53-
console.log()
54-
55-
// Constructor args for an ERC-20 (name, symbol, initialSupply, owner)
56-
const ctorArgs = [TOKEN_NAME, TOKEN_SYMBOL, INITIAL_SUPPLY, wallet.getAddress()]
57-
console.log('Constructor args:')
58-
ctorArgs.forEach((a, i) =>
59-
console.log(' [' + i + ']', typeof a === 'bigint' ? a.toString() : a)
60-
)
61-
console.log()
62-
63-
// wallet.deployContract(abi, bytecode, args) handles signing + receipt
64-
console.log('✓ To deploy:')
65-
console.log(' import { erc20BaseBytecode } from \'@cfxdevkit/contracts\'')
66-
console.log(' const addr = await wallet.deployContract(ERC20_ABI, erc20BaseBytecode, ctorArgs)')
67-
console.log(' console.log(\'Deployed at:\', addr)')
16+
"/index.ts": `import { erc20BaseAbi, erc20BaseBytecode } from '@cfxdevkit/contracts'
17+
import { EspaceWalletClient, EVM_TESTNET, parseUnits, formatUnits } from '@cfxdevkit/core'
18+
19+
// Show the bootstrap ERC-20 contract structure
20+
console.log('-- ERC20Base Bootstrap Contract --')
21+
console.log('ABI entries : ' + erc20BaseAbi.length)
22+
console.log('Bytecode : ' + erc20BaseBytecode.slice(0, 28) + '... (' + Math.floor(erc20BaseBytecode.length / 2) + ' bytes)')
23+
24+
// List the ABI functions
25+
const fns = erc20BaseAbi.filter(e => e.type === 'function').map(e => e.name + ' (' + e.stateMutability + ')')
26+
console.log('-- Functions (' + fns.length + ') --')
27+
for (const f of fns.slice(0, 10)) {
28+
console.log(' ' + f)
6829
}
69-
70-
main().catch(console.error)
30+
if (fns.length > 10) console.log(' ... and ' + (fns.length - 10) + ' more')
31+
32+
// Constructor arguments for ERC20Base
33+
// (name, symbol, initialSupply, owner)
34+
const TOKEN_NAME = 'Demo Token'
35+
const TOKEN_SYMBOL = 'DEMO'
36+
const TOTAL_SUPPLY = parseUnits('1000000', 18)
37+
const OWNER_ADDR = '0x85d80245Dc02f5a89589e1f19C5c718E405B56Aa'
38+
39+
console.log('-- Deploy Parameters --')
40+
console.log('Name : ' + TOKEN_NAME)
41+
console.log('Symbol : ' + TOKEN_SYMBOL)
42+
console.log('Supply : ' + formatUnits(TOTAL_SUPPLY, 18) + ' ' + TOKEN_SYMBOL)
43+
console.log('Owner : ' + OWNER_ADDR)
44+
45+
// Uncomment to deploy (requires a funded testnet wallet)
46+
//
47+
// const wallet = new EspaceWalletClient({
48+
// chainId: EVM_TESTNET.id,
49+
// rpcUrl: EVM_TESTNET.rpcUrls.default.http[0],
50+
// privateKey: '0xYOUR_TESTNET_PRIVATE_KEY',
51+
// })
52+
//
53+
// const contractAddress = await wallet.deployContract(
54+
// erc20BaseAbi,
55+
// erc20BaseBytecode,
56+
// [TOKEN_NAME, TOKEN_SYMBOL, TOTAL_SUPPLY, OWNER_ADDR],
57+
// )
58+
// console.log('Deployed at: ' + contractAddress)
59+
60+
console.log('-- Deploy call commented out (no testnet funds in playground) --')
61+
console.log('Paste your funded private key and uncomment wallet.deployContract(...) to deploy.')
7162
`
7263
}}
73-
showConsole
7464
/>
7565

7666
---
7767

78-
## Using the real bytecode
68+
## How it works
69+
70+
1. `erc20BaseAbi` + `erc20BaseBytecode` are exported from `@cfxdevkit/contracts` — a capped, burnable, pausable ERC-20 with ERC-2612 permit.
71+
2. `wallet.deployContract(abi, bytecode, args)` handles signing, broadcasting, and polling for the receipt — returns the deployed address directly.
72+
3. Constructor arguments: `(name, symbol, initialSupply, owner)` — the owner receives the full initial supply.
7973

8074
```typescript
81-
import { EspaceWalletClient, ERC20_ABI, parseUnits } from '@cfxdevkit/core'
82-
import { erc20BaseBytecode } from '@cfxdevkit/contracts'
75+
import { erc20BaseAbi, erc20BaseBytecode } from '@cfxdevkit/contracts'
76+
import { EspaceWalletClient, EVM_TESTNET, parseUnits } from '@cfxdevkit/core'
8377

84-
const wallet = new EspaceWalletClient({ chainId: 71, rpcUrl: '...', privateKey: '0x...' })
78+
const wallet = new EspaceWalletClient({
79+
chainId: EVM_TESTNET.id,
80+
rpcUrl: EVM_TESTNET.rpcUrls.default.http[0],
81+
privateKey: '0x...',
82+
})
8583

86-
// deployContract returns the deployed contract address directly
8784
const contractAddress = await wallet.deployContract(
88-
ERC20_ABI,
85+
erc20BaseAbi,
8986
erc20BaseBytecode,
90-
[
91-
'My Token', // name
92-
'MTK', // symbol
93-
parseUnits('1000000', 18), // initialSupply
94-
wallet.getAddress(), // owner
95-
]
87+
['My Token', 'MTK', parseUnits('1000000', 18), wallet.getAddress()],
9688
)
97-
9889
console.log('Deployed at:', contractAddress)
9990
```
10091

101-
`wallet.deployContract()` internally calls `walletClient.deployContract` then `waitForTransactionReceipt` and returns the contract address — no manual receipt polling needed.

docs-site/content/examples/erc20-info.mdx

Lines changed: 37 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Callout } from 'nextra/components'
77

88
# ERC-20 Token Info
99

10-
Read token metadata (name, symbol, decimals, total supply) and holder balances using `EspaceClient.readContract()` with the built-in `ERC20_ABI`.
10+
Read token metadata (name, symbol, decimals, total supply) and holder balances using `client.publicClient.readContract()` with the built-in `ERC20_ABI`.
1111

1212
<Callout type="tip">
1313
Change `TOKEN` to any ERC-20 address on testnet. The default is **WCFX** (Wrapped CFX on testnet), a contract that always has liquidity.
@@ -16,60 +16,43 @@ Change `TOKEN` to any ERC-20 address on testnet. The default is **WCFX** (Wrappe
1616
<Playground
1717
file="/index.ts"
1818
files={{
19-
"/index.ts": `import { EspaceClient, ERC20_ABI, formatUnits } from '@cfxdevkit/core'
20-
import { NETWORK } from './network-config'
19+
"/index.ts": `import { EspaceClient, EVM_TESTNET, ERC20_ABI, formatUnits } from '@cfxdevkit/core'
2120
22-
// ── Config ──────────────────────────────────────────────────
23-
// WCFX on testnet — always-live ERC-20, safe to query
21+
// WCFX (Wrapped CFX) — always-live ERC-20 on Conflux eSpace testnet
2422
const TOKEN = '0x2ed3dddae5b2f321af0806181fbfa6d049be47d8'
25-
const HOLDER = '0x0000000000000000000000000000000000000001'
23+
const HOLDER = '0x85d80245Dc02f5a89589e1f19C5c718E405B56Aa'
2624
27-
// ── Client ──────────────────────────────────────────────────
2825
const client = new EspaceClient({
29-
chainId: NETWORK.chainId,
30-
rpcUrl: NETWORK.rpcUrl,
26+
chainId: EVM_TESTNET.id,
27+
rpcUrl: EVM_TESTNET.rpcUrls.default.http[0],
3128
})
3229
33-
async function main() {
34-
console.log('Token: ', TOKEN)
35-
console.log('Network:', NETWORK.rpcUrl)
36-
console.log()
37-
38-
// Batch-read all ERC-20 metadata in parallel
39-
// ERC20_ABI is exported from @cfxdevkit/core and covers all standard methods
40-
const [name, symbol, decimals, totalSupply] = await Promise.all([
41-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'name' }),
42-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'symbol' }),
43-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'decimals' }),
44-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'totalSupply' }),
45-
])
46-
47-
console.log('Name: ', name)
48-
console.log('Symbol: ', symbol)
49-
console.log('Decimals: ', decimals)
50-
console.log('Total supply: ', formatUnits(totalSupply, decimals), symbol)
51-
console.log()
52-
53-
// Single holder balance
54-
const balance = await client.readContract({
55-
address: TOKEN,
56-
abi: ERC20_ABI,
57-
functionName: 'balanceOf',
58-
args: [HOLDER],
59-
})
60-
console.log('Balance of', HOLDER)
61-
console.log(' ', formatUnits(balance, decimals), symbol)
62-
console.log()
63-
64-
// getTokenBalance() is a convenience shortcut for balanceOf
65-
const shortcut = await client.getTokenBalance(HOLDER, TOKEN)
66-
console.log('getTokenBalance() raw:', shortcut, 'wei')
67-
console.log(' formatted:', formatUnits(BigInt(shortcut), decimals), symbol)
68-
console.log()
69-
console.log('\\u2713 Done')
70-
}
71-
72-
main().catch(console.error)
30+
console.log('-- Token: ' + TOKEN + ' --')
31+
32+
// Batch-read all standard ERC-20 metadata in one round-trip
33+
const [name, symbol, decimals, totalSupply] = await Promise.all([
34+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'name' }),
35+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'symbol' }),
36+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'decimals' }),
37+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'totalSupply' }),
38+
])
39+
40+
console.log('-- Metadata --')
41+
console.log('Name : ' + name)
42+
console.log('Symbol : ' + symbol)
43+
console.log('Decimals : ' + decimals)
44+
console.log('Total Supply : ' + formatUnits(totalSupply, decimals) + ' ' + symbol)
45+
46+
// Read holder balance and allowance
47+
const SPENDER = '0x33e5E5B262e5d8eBC443E1c6c9F14215b020554d' // AutomationManager testnet
48+
const [balance, allowance] = await Promise.all([
49+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'balanceOf', args: [HOLDER] }),
50+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'allowance', args: [HOLDER, SPENDER] }),
51+
])
52+
53+
console.log('-- Holder ' + HOLDER.slice(0,10) + '... --')
54+
console.log('Balance : ' + formatUnits(balance, decimals) + ' ' + symbol)
55+
console.log('Allowance : ' + formatUnits(allowance, decimals) + ' ' + symbol + ' (for AutomationManager)')
7356
`
7457
}}
7558
showConsole
@@ -80,18 +63,18 @@ main().catch(console.error)
8063
## How it works
8164

8265
1. `ERC20_ABI` is exported from `@cfxdevkit/core` and covers all standard ERC-20 methods (name, symbol, decimals, totalSupply, balanceOf, allowance, transfer, approve, transferFrom).
83-
2. `client.readContract({ address, abi, functionName, args })` is a thin wrapper around viem's `publicClient.readContract`.
66+
2. `client.publicClient.readContract({ address, abi, functionName, args })` is viem's `publicClient.readContract` — accessible via the `publicClient` property on `EspaceClient`.
8467
3. `Promise.all([...])` runs all four metadata reads in a single burst for efficiency.
85-
4. `client.getTokenBalance(holder, token)` is a convenience shortcut that returns the raw BigInt balance as a string; use `formatUnits` to convert.
68+
4. Wrap calls in `Promise.all([...])` to batch multiple reads in one round-trip — essential for reading all metadata at once.
8669

8770
```typescript
8871
// Production import
8972
import { EspaceClient, ERC20_ABI, formatUnits } from '@cfxdevkit/core'
9073

9174
const client = new EspaceClient({ chainId: 71, rpcUrl: 'https://evmtestnet.confluxrpc.com' })
9275
const [name, symbol, decimals] = await Promise.all([
93-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'name' }),
94-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'symbol' }),
95-
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'decimals' }),
76+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'name' }),
77+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'symbol' }),
78+
client.publicClient.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'decimals' }),
9679
])
9780
```

0 commit comments

Comments
 (0)