11"use client" ;
2- "use client" ;
32
4- import { useEffect , useState , useCallback } from "react" ;
3+ import { useEffect , useState , useCallback , useRef } from "react" ;
54import { Button } from "@/src/components/Button" ;
65import { TextInput } from "@/src/components/Input" ;
76import { ButtonsPanel } from "@/src/components/ButtonsPanel" ;
87import { useFiber } from "../context/FiberContext" ;
8+ import { hexToDecimal , decimalToHex } from '@/src/utils/hex' ;
9+ import { shannonToCKB } from "../utils/numbers"
910
1011interface ChannelState {
1112 fundingAmount : string ;
@@ -26,33 +27,41 @@ interface OpenChannelForm {
2627
2728export 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" >
0 commit comments