Skip to content

Commit f50c3a3

Browse files
committed
fix:
- 修改 Script 接口中 args 的类型从 string[] 改为 string - 修改 shutdownChannel 函数中的 close_script.args 参数格式 - 确保所有参数都符合 API 期望的十六进制字符串格式
1 parent cc6ddfe commit f50c3a3

8 files changed

Lines changed: 693 additions & 117 deletions

File tree

packages/demo/src/app/fiber/channel/page.tsx

Lines changed: 78 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"use client";
2-
"use client";
32

4-
import { useEffect, useState, useCallback } from "react";
3+
import { useEffect, useState, useCallback, useRef } from "react";
54
import { Button } from "@/src/components/Button";
65
import { TextInput } from "@/src/components/Input";
76
import { ButtonsPanel } from "@/src/components/ButtonsPanel";
87
import { useFiber } from "../context/FiberContext";
8+
import { hexToDecimal, decimalToHex } from '@/src/utils/hex';
9+
import { shannonToCKB } from "../utils/numbers"
910

1011
interface ChannelState {
1112
fundingAmount: string;
@@ -26,33 +27,41 @@ interface OpenChannelForm {
2627

2728
export default function Channel() {
2829
const { fiber } = useFiber();
29-
const [endpoint, setEndpoint] = useState("");
3030
const [nodeInfo, setNodeInfo] = useState<any>(null);
3131
const [channels, setChannels] = useState<any[]>([]);
3232
const [peers, setPeers] = useState<any[]>([]);
3333
const [peerAddress, setPeerAddress] = useState("");
34-
const [channelStates, setChannelStates] = useState<
35-
Record<string, ChannelState>
36-
>({});
34+
const [channelStates, setChannelStates] = useState<Record<string, ChannelState>>({});
3735
const [isLoading, setIsLoading] = useState(false);
36+
const channelStatesRef = useRef(channelStates);
37+
const initialized = useRef(false);
3838
const [openChannelForm, setOpenChannelForm] = useState<OpenChannelForm>({
3939
peerAddress:
4040
"/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo",
4141
fundingAmount: "50000000000",
4242
isPublic: true,
4343
});
4444

45+
// 更新 ref 当 channelStates 改变时
46+
useEffect(() => {
47+
channelStatesRef.current = channelStates;
48+
}, [channelStates]);
49+
4550
const listChannels = useCallback(async () => {
4651
if (!fiber) return;
4752
setIsLoading(true);
4853
try {
4954
const channelList = await fiber.listChannels();
50-
console.log(channelList);
5155
setChannels(channelList);
52-
// 初始化每个通道的状态
56+
57+
// 只在需要时更新 channelStates
5358
const newChannelStates: Record<string, ChannelState> = {};
59+
let hasChanges = false;
60+
5461
channelList.forEach((channel: any) => {
55-
if (!channelStates[channel.channel_id]) {
62+
const existingState = channelStatesRef.current[channel.channel_id];
63+
if (!existingState) {
64+
hasChanges = true;
5665
newChannelStates[channel.channel_id] = {
5766
fundingAmount: channel.funding_amount || "0xba43b7400",
5867
feeRate: channel.fee_rate || "0x3FC",
@@ -64,17 +73,19 @@ export default function Channel() {
6473
forceClose: false,
6574
};
6675
} else {
67-
newChannelStates[channel.channel_id] =
68-
channelStates[channel.channel_id];
76+
newChannelStates[channel.channel_id] = existingState;
6977
}
7078
});
71-
setChannelStates(newChannelStates);
79+
80+
if (hasChanges) {
81+
setChannelStates(newChannelStates);
82+
}
7283
} catch (error) {
7384
console.error("Failed to list channels:", error);
7485
} finally {
7586
setIsLoading(false);
7687
}
77-
}, [fiber, channelStates]);
88+
}, [fiber]); // 只依赖 fiber
7889

7990
const updateChannel = async (channelId: string) => {
8091
if (!fiber || !channelId) return;
@@ -138,57 +149,42 @@ export default function Channel() {
138149
const state = channelStates[channelId];
139150
if (!state) return;
140151
try {
141-
if (state.forceClose) {
142-
await fiber.channel.shutdownChannel({
143-
channel_id: channelId,
144-
close_script: {
145-
code_hash:
146-
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
147-
hash_type: "type",
148-
args: ["0xcc015401df73a3287d8b2b19f0cc23572ac8b14d"],
149-
},
150-
force: true,
151-
fee_rate: BigInt(state.feeRate),
152-
});
153-
} else {
154-
await fiber.channel.shutdownChannel({
155-
channel_id: channelId,
156-
close_script: {
157-
code_hash:
158-
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
159-
hash_type: "type",
160-
args: ["0xcc015401df73a3287d8b2b19f0cc23572ac8b14d"],
161-
},
162-
force: false,
163-
fee_rate: BigInt(state.feeRate),
164-
});
152+
// 确保channelId是有效的十六进制字符串
153+
if (!channelId.startsWith('0x')) {
154+
channelId = '0x' + channelId;
165155
}
156+
157+
const params = {
158+
channel_id: channelId,
159+
close_script: {
160+
code_hash:
161+
"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
162+
hash_type: "type",
163+
args: "0xcc015401df73a3287d8b2b19f0cc23572ac8b14d"
164+
},
165+
force: state.forceClose,
166+
fee_rate: state.feeRate,
167+
};
168+
169+
console.log("Shutdown channel params:", JSON.stringify(params, null, 2));
170+
console.log("Fee rate type:", typeof state.feeRate);
171+
console.log("Fee rate value:", state.feeRate);
172+
173+
await fiber.channel.shutdownChannel(params);
166174
console.log("Channel shutdown initiated successfully");
167175
// Refresh channel list
168176
await listChannels();
169177
} catch (error) {
170178
console.error("Failed to shutdown channel:", error);
171-
}
172-
};
173-
const connectPeer = async () => {
174-
if (!fiber) return;
175-
try {
176-
await fiber.connectPeer(openChannelForm.peerAddress);
177-
console.log("Peer connected successfully");
178-
// 连接成功后刷新 peers 列表
179-
const peerList = await fiber.listPeers();
180-
console.log("Current peers:", peerList);
181-
setPeers(peerList);
182-
} catch (error) {
183-
console.error("Failed to connect peer:", error);
184179
if (error instanceof Error) {
185-
alert(`连接 peer 失败: ${error.message}`);
180+
alert(`关闭通道失败: ${error.message}`);
186181
} else {
187-
alert("连接 peer 失败,请检查网络连接");
182+
alert("关闭通道失败,请检查通道状态");
188183
}
189184
}
190185
};
191186

187+
192188
const handleOpenChannel = async () => {
193189
if (!fiber) return;
194190
try {
@@ -200,7 +196,7 @@ export default function Channel() {
200196
const peerId = openChannelForm.peerAddress.split("/p2p/")[1];
201197
await fiber.channel.openChannel({
202198
peer_id: peerId,
203-
funding_amount: BigInt(openChannelForm.fundingAmount),
199+
funding_amount: openChannelForm.fundingAmount,
204200
public: openChannelForm.isPublic,
205201
});
206202
console.log("Channel opened successfully");
@@ -217,7 +213,8 @@ export default function Channel() {
217213
};
218214

219215
useEffect(() => {
220-
if (fiber) {
216+
if (fiber && !initialized.current) {
217+
initialized.current = true;
221218
listChannels();
222219
}
223220
}, [fiber, listChannels]);
@@ -246,7 +243,6 @@ export default function Channel() {
246243
]}
247244
placeholder="输入 peer 地址"
248245
/>
249-
<Button onClick={connectPeer}>连接 peer</Button>
250246
<TextInput
251247
label="Funding Amount (CKB)"
252248
state={[
@@ -301,110 +297,112 @@ export default function Channel() {
301297
</p>
302298
<p>
303299
<span className="font-semibold">Local Balance:</span>{" "}
304-
{channel.local_balance}
300+
{hexToDecimal(channel.local_balance).toString()}
305301
</p>
306302
<p>
307303
<span className="font-semibold">Remote Balance:</span>{" "}
308-
{channel.remote_balance}
304+
{hexToDecimal(channel.remote_balance).toString()}
309305
</p>
310306
</div>
311307
<div>
312308
<div className="mt-1">
313309
<TextInput
314-
label="Funding Amount"
310+
label="Funding Amount (Decimal)"
315311
state={[
316-
channelStates[channel.channel_id]?.fundingAmount ||
317-
"0xba43b7400",
312+
hexToDecimal(channelStates[channel.channel_id]?.fundingAmount || "0xba43b7400").toString(),
318313
(value) => {
319314
const newState = {
320315
...channelStates[channel.channel_id],
321-
fundingAmount: value,
316+
fundingAmount: decimalToHex(parseInt(value) || 0),
322317
};
323318
setChannelStates((prev) => ({
324319
...prev,
325320
[channel.channel_id]: newState,
326321
}));
327322
},
328323
]}
329-
placeholder="0xba43b7400"
324+
placeholder="50000000000"
325+
type="number"
330326
/>
331327
</div>
332328
<div className="mt-1">
333329
<TextInput
334-
label="Fee Rate"
330+
label="Fee Rate (Decimal)"
335331
state={[
336-
channelStates[channel.channel_id]?.feeRate || "0x3FC",
332+
hexToDecimal(channelStates[channel.channel_id]?.feeRate || "0x3FC").toString(),
337333
(value) => {
338334
const newState = {
339335
...channelStates[channel.channel_id],
340-
feeRate: value,
336+
feeRate: decimalToHex(parseInt(value) || 0),
341337
};
342338
setChannelStates((prev) => ({
343339
...prev,
344340
[channel.channel_id]: newState,
345341
}));
346342
},
347343
]}
348-
placeholder="0x3FC"
344+
placeholder="1020"
345+
type="number"
349346
/>
350347
</div>
351348
<div className="mt-1">
352349
<TextInput
353-
label="TLC Expiry Delta"
350+
label="TLC Expiry Delta (Decimal)"
354351
state={[
355-
channelStates[channel.channel_id]?.tlcExpiryDelta ||
356-
"0x100",
352+
hexToDecimal(channelStates[channel.channel_id]?.tlcExpiryDelta || "0x100").toString(),
357353
(value) => {
358354
const newState = {
359355
...channelStates[channel.channel_id],
360-
tlcExpiryDelta: value,
356+
tlcExpiryDelta: decimalToHex(parseInt(value) || 0),
361357
};
362358
setChannelStates((prev) => ({
363359
...prev,
364360
[channel.channel_id]: newState,
365361
}));
366362
},
367363
]}
368-
placeholder="0x100"
364+
placeholder="256"
365+
type="number"
369366
/>
370367
</div>
371368
<div className="mt-1">
372369
<TextInput
373-
label="TLC Minimum Value"
370+
label="TLC Minimum Value (Decimal)"
374371
state={[
375-
channelStates[channel.channel_id]?.tlcMinValue || "0x0",
372+
hexToDecimal(channelStates[channel.channel_id]?.tlcMinValue || "0x0").toString(),
376373
(value) => {
377374
const newState = {
378375
...channelStates[channel.channel_id],
379-
tlcMinValue: value,
376+
tlcMinValue: decimalToHex(parseInt(value) || 0),
380377
};
381378
setChannelStates((prev) => ({
382379
...prev,
383380
[channel.channel_id]: newState,
384381
}));
385382
},
386383
]}
387-
placeholder="0x0"
384+
placeholder="0"
385+
type="number"
388386
/>
389387
</div>
390388
<div className="mt-1">
391389
<TextInput
392-
label="TLC Fee Proportional Millionths"
390+
label="TLC Fee Proportional Millionths (Decimal)"
393391
state={[
394-
channelStates[channel.channel_id]
395-
?.tlcFeeProportionalMillionths || "0x0",
392+
hexToDecimal(channelStates[channel.channel_id]?.tlcFeeProportionalMillionths || "0x0").toString(),
396393
(value) => {
397394
const newState = {
398395
...channelStates[channel.channel_id],
399-
tlcFeeProportionalMillionths: value,
396+
tlcFeeProportionalMillionths: decimalToHex(parseInt(value) || 0),
400397
};
401398
setChannelStates((prev) => ({
402399
...prev,
403400
[channel.channel_id]: newState,
404401
}));
405402
},
406403
]}
407-
placeholder="0x0"
404+
placeholder="0"
405+
type="number"
408406
/>
409407
</div>
410408
<div className="mt-1 flex items-center">

packages/demo/src/app/fiber/context/FiberContext.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { createContext, useContext, ReactNode, useState } from "react";
3+
import { createContext, useContext, ReactNode, useState, useEffect } from "react";
44
import { FiberSDK } from "@ckb-ccc/fiber";
55

66
interface FiberContextType {
@@ -11,7 +11,36 @@ interface FiberContextType {
1111
const FiberContext = createContext<FiberContextType | null>(null);
1212

1313
export function FiberProvider({ children }: { children: ReactNode }) {
14-
const [fiber, setFiber] = useState<FiberSDK | null>(null);
14+
const [fiber, setFiber] = useState<FiberSDK | null>(() => {
15+
// 从 localStorage 中获取存储的 fiber 配置
16+
if (typeof window !== 'undefined') {
17+
const savedConfig = localStorage.getItem('fiberConfig');
18+
if (savedConfig) {
19+
try {
20+
const config = JSON.parse(savedConfig);
21+
return new FiberSDK(config);
22+
} catch (error) {
23+
console.error('Failed to restore fiber from localStorage:', error);
24+
return null;
25+
}
26+
}
27+
}
28+
return null;
29+
});
30+
31+
// 当 fiber 更新时,保存到 localStorage
32+
useEffect(() => {
33+
if (fiber) {
34+
// 保存配置到 localStorage
35+
const config = {
36+
endpoint: '/api/fiber', // 使用默认的 endpoint
37+
timeout: 5000 // 使用默认的 timeout
38+
};
39+
localStorage.setItem('fiberConfig', JSON.stringify(config));
40+
} else {
41+
localStorage.removeItem('fiberConfig');
42+
}
43+
}, [fiber]);
1544

1645
return (
1746
<FiberContext.Provider value={{ fiber, setFiber }}>

0 commit comments

Comments
 (0)