@yellow-org/sdk 1.2.1 → 1.3.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 +54 -43
- package/dist/app/packing.js +3 -20
- package/dist/app/types.d.ts +1 -0
- package/dist/blockchain/evm/channel_hub_abi.d.ts +22 -3
- package/dist/blockchain/evm/channel_hub_abi.js +27 -3
- package/dist/blockchain/evm/client.js +3 -3
- package/dist/blockchain/evm/erc20.js +5 -6
- package/dist/blockchain/evm/index.d.ts +1 -0
- package/dist/blockchain/evm/index.js +1 -0
- package/dist/blockchain/evm/validator_watcher.d.ts +4 -0
- package/dist/blockchain/evm/validator_watcher.js +119 -0
- package/dist/client.d.ts +14 -7
- package/dist/client.js +77 -42
- package/dist/config.d.ts +4 -0
- package/dist/config.js +21 -0
- package/dist/core/event.d.ts +6 -0
- package/dist/core/types.d.ts +2 -1
- package/dist/core/types.js +2 -1
- package/dist/core/utils.d.ts +3 -2
- package/dist/core/utils.js +6 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/rpc/api.d.ts +10 -16
- package/dist/rpc/client.d.ts +0 -1
- package/dist/rpc/client.js +0 -3
- package/dist/rpc/methods.d.ts +0 -1
- package/dist/rpc/methods.js +0 -1
- package/dist/rpc/types.d.ts +1 -0
- package/dist/session_key_state_transforms.d.ts +4 -0
- package/dist/session_key_state_transforms.js +45 -0
- package/dist/utils.js +50 -9
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@yellow-org/sdk)
|
|
4
4
|
[](https://github.com/layer-3/nitrolite/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
> The off-chain broker was renamed from "Clearnode" to "Nitronode" in v1.3.0. The default WebSocket sandbox URL is now `wss://nitronode-sandbox.yellow.org/v1/ws`. See [`MIGRATION-NITRONODE.md`](../../MIGRATION-NITRONODE.md).
|
|
7
|
+
|
|
8
|
+
TypeScript SDK for Nitronode payment channels providing both high-level and low-level operations in a unified client:
|
|
7
9
|
- **State Operations**: `deposit()`, `withdraw()`, `transfer()`, `closeHomeChannel()`, `acknowledge()` - build and co-sign states off-chain
|
|
8
10
|
- **Blockchain Settlement**: `checkpoint()` - the single entry point for all on-chain transactions
|
|
9
11
|
- **Low-Level Operations**: Direct RPC access for custom flows and advanced use cases
|
|
@@ -76,16 +78,18 @@ client.rebalanceAppSessions(signedUpdates) // Atomic rebala
|
|
|
76
78
|
|
|
77
79
|
### App Session Keys
|
|
78
80
|
```typescript
|
|
79
|
-
client.signSessionKeyState(state)
|
|
80
|
-
client.
|
|
81
|
-
client.
|
|
81
|
+
client.signSessionKeyState(state) // Wallet user_sig over app session key state
|
|
82
|
+
client.signAppSessionKeyOwnership(state, sessionKeySigner) // Session-key holder's session_key_sig
|
|
83
|
+
client.submitSessionKeyState(state) // Register/update app session key (both sigs required)
|
|
84
|
+
client.getLastAppKeyStates(userAddress, sessionKey?, options?) // Get app session key states (active-only by default; pass { includeInactive: true } to include expired)
|
|
82
85
|
```
|
|
83
86
|
|
|
84
87
|
### Channel Session Keys
|
|
85
88
|
```typescript
|
|
86
|
-
client.signChannelSessionKeyState(state)
|
|
87
|
-
client.
|
|
88
|
-
client.
|
|
89
|
+
client.signChannelSessionKeyState(state) // Wallet user_sig over channel session key state
|
|
90
|
+
client.signChannelSessionKeyOwnership(state, sessionKeySigner) // Session-key holder's session_key_sig
|
|
91
|
+
client.submitChannelSessionKeyState(state) // Register/update channel session key (both sigs required)
|
|
92
|
+
client.getLastChannelKeyStates(userAddress, sessionKey?, options?) // Get channel session key states (active-only by default; pass { includeInactive: true } to include expired)
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
### Shared Utilities
|
|
@@ -123,7 +127,7 @@ async function main() {
|
|
|
123
127
|
|
|
124
128
|
// Create unified client
|
|
125
129
|
const client = await Client.create(
|
|
126
|
-
'wss://
|
|
130
|
+
'wss://nitronode.example.com/ws',
|
|
127
131
|
stateSigner,
|
|
128
132
|
txSigner,
|
|
129
133
|
withBlockchainRPC(80002n, 'https://polygon-amoy.alchemy.com/v2/KEY')
|
|
@@ -490,58 +494,65 @@ const sessionKeySigner = new AppSessionKeySignerV1(sessionKeyMsgSigner);
|
|
|
490
494
|
|
|
491
495
|
### App Session Keys
|
|
492
496
|
|
|
497
|
+
Registration requires two signatures: the wallet's `user_sig` (authorizing the
|
|
498
|
+
delegation) and the session-key holder's `session_key_sig` (proving possession of the key
|
|
499
|
+
being registered). The node rejects submits that lack a valid `session_key_sig`.
|
|
500
|
+
|
|
493
501
|
```typescript
|
|
494
|
-
//
|
|
495
|
-
|
|
502
|
+
// sessionKeyHolder is an EthereumMsgSigner whose address equals state.session_key.
|
|
503
|
+
// Use a raw message signer (not a wrapped StateSigner) — the node expects a
|
|
504
|
+
// raw 65-byte EIP-191 signature for session_key_sig.
|
|
505
|
+
const sessionKeyHolder = new EthereumMsgSigner(sessionKeyPrivateKey);
|
|
506
|
+
const state = {
|
|
496
507
|
user_address: '0x1234...',
|
|
497
508
|
session_key: '0xabcd...',
|
|
498
509
|
version: '1',
|
|
499
510
|
application_ids: ['app1'],
|
|
500
511
|
app_session_ids: [],
|
|
501
512
|
expires_at: String(Math.floor(Date.now() / 1000) + 86400),
|
|
502
|
-
user_sig: '
|
|
503
|
-
|
|
513
|
+
user_sig: '',
|
|
514
|
+
session_key_sig: '',
|
|
515
|
+
};
|
|
516
|
+
state.user_sig = await client.signSessionKeyState(state);
|
|
517
|
+
state.session_key_sig = await client.signAppSessionKeyOwnership(state, sessionKeyHolder);
|
|
504
518
|
|
|
505
|
-
await client.submitSessionKeyState(
|
|
506
|
-
user_address: '0x1234...',
|
|
507
|
-
session_key: '0xabcd...',
|
|
508
|
-
version: '1',
|
|
509
|
-
application_ids: ['app1'],
|
|
510
|
-
app_session_ids: [],
|
|
511
|
-
expires_at: String(Math.floor(Date.now() / 1000) + 86400),
|
|
512
|
-
user_sig: sig,
|
|
513
|
-
});
|
|
519
|
+
await client.submitSessionKeyState(state);
|
|
514
520
|
|
|
515
|
-
// Query
|
|
516
|
-
const states = await client.
|
|
517
|
-
const filtered = await client.
|
|
521
|
+
// Query app session key states (active-only by default)
|
|
522
|
+
const states = await client.getLastAppKeyStates('0x1234...');
|
|
523
|
+
const filtered = await client.getLastAppKeyStates('0x1234...', '0xSessionKey...');
|
|
524
|
+
|
|
525
|
+
// Include expired/revoked latest states (e.g. for rotation flows that need the prior version)
|
|
526
|
+
const all = await client.getLastAppKeyStates('0x1234...', '0xSessionKey...', { includeInactive: true });
|
|
518
527
|
```
|
|
519
528
|
|
|
520
529
|
### Channel Session Keys
|
|
521
530
|
|
|
522
531
|
```typescript
|
|
523
|
-
//
|
|
524
|
-
|
|
532
|
+
// sessionKeyHolder is an EthereumMsgSigner whose address equals state.session_key.
|
|
533
|
+
// Use a raw message signer (not a wrapped StateSigner) — the node expects a
|
|
534
|
+
// raw 65-byte EIP-191 signature for session_key_sig.
|
|
535
|
+
const sessionKeyHolder = new EthereumMsgSigner(sessionKeyPrivateKey);
|
|
536
|
+
const state = {
|
|
525
537
|
user_address: '0x1234...',
|
|
526
538
|
session_key: '0xabcd...',
|
|
527
539
|
version: '1',
|
|
528
540
|
assets: ['usdc'],
|
|
529
541
|
expires_at: String(Math.floor(Date.now() / 1000) + 86400),
|
|
530
|
-
user_sig: '
|
|
531
|
-
|
|
542
|
+
user_sig: '',
|
|
543
|
+
session_key_sig: '',
|
|
544
|
+
};
|
|
545
|
+
state.user_sig = await client.signChannelSessionKeyState(state);
|
|
546
|
+
state.session_key_sig = await client.signChannelSessionKeyOwnership(state, sessionKeyHolder);
|
|
532
547
|
|
|
533
|
-
await client.submitChannelSessionKeyState(
|
|
534
|
-
user_address: '0x1234...',
|
|
535
|
-
session_key: '0xabcd...',
|
|
536
|
-
version: '1',
|
|
537
|
-
assets: ['usdc'],
|
|
538
|
-
expires_at: String(Math.floor(Date.now() / 1000) + 86400),
|
|
539
|
-
user_sig: sig,
|
|
540
|
-
});
|
|
548
|
+
await client.submitChannelSessionKeyState(state);
|
|
541
549
|
|
|
542
|
-
// Query
|
|
550
|
+
// Query channel session key states (active-only by default)
|
|
543
551
|
const states = await client.getLastChannelKeyStates('0x1234...');
|
|
544
552
|
const filtered = await client.getLastChannelKeyStates('0x1234...', '0xSessionKey...');
|
|
553
|
+
|
|
554
|
+
// Include expired/revoked latest states (e.g. for rotation flows that need the prior version)
|
|
555
|
+
const all = await client.getLastChannelKeyStates('0x1234...', '0xSessionKey...', { includeInactive: true });
|
|
545
556
|
```
|
|
546
557
|
|
|
547
558
|
## Key Concepts
|
|
@@ -688,7 +699,7 @@ async function basicExample() {
|
|
|
688
699
|
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
|
|
689
700
|
|
|
690
701
|
const client = await Client.create(
|
|
691
|
-
'wss://
|
|
702
|
+
'wss://nitronode.example.com/ws',
|
|
692
703
|
stateSigner,
|
|
693
704
|
txSigner,
|
|
694
705
|
withBlockchainRPC(80002n, process.env.RPC_URL!)
|
|
@@ -737,7 +748,7 @@ async function multiChainExample() {
|
|
|
737
748
|
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
|
|
738
749
|
|
|
739
750
|
const client = await Client.create(
|
|
740
|
-
'wss://
|
|
751
|
+
'wss://nitronode.example.com/ws',
|
|
741
752
|
stateSigner,
|
|
742
753
|
txSigner,
|
|
743
754
|
withBlockchainRPC(80002n, process.env.POLYGON_RPC!), // Polygon Amoy
|
|
@@ -775,7 +786,7 @@ import { Client, createSigners } from '@yellow-org/sdk';
|
|
|
775
786
|
async function queryTransactions() {
|
|
776
787
|
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
|
|
777
788
|
const client = await Client.create(
|
|
778
|
-
'wss://
|
|
789
|
+
'wss://nitronode.example.com/ws',
|
|
779
790
|
stateSigner,
|
|
780
791
|
txSigner
|
|
781
792
|
);
|
|
@@ -821,7 +832,7 @@ import Decimal from 'decimal.js';
|
|
|
821
832
|
async function appSessionExample() {
|
|
822
833
|
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
|
|
823
834
|
const client = await Client.create(
|
|
824
|
-
'wss://
|
|
835
|
+
'wss://nitronode.example.com/ws',
|
|
825
836
|
stateSigner,
|
|
826
837
|
txSigner,
|
|
827
838
|
withBlockchainRPC(80002n, process.env.RPC_URL!)
|
|
@@ -902,7 +913,7 @@ async function monitorConnection() {
|
|
|
902
913
|
const { stateSigner, txSigner } = createSigners(process.env.PRIVATE_KEY!);
|
|
903
914
|
|
|
904
915
|
const client = await Client.create(
|
|
905
|
-
'wss://
|
|
916
|
+
'wss://nitronode.example.com/ws',
|
|
906
917
|
stateSigner,
|
|
907
918
|
txSigner,
|
|
908
919
|
withPingInterval(3000),
|
|
@@ -1073,7 +1084,7 @@ For understanding how operations work under the hood:
|
|
|
1073
1084
|
|
|
1074
1085
|
- **Node.js**: 20.0.0 or later
|
|
1075
1086
|
- **TypeScript**: 5.3.0 or later (for development)
|
|
1076
|
-
- **Running
|
|
1087
|
+
- **Running Nitronode instance** or access to public node
|
|
1077
1088
|
- **Blockchain RPC endpoint** (for on-chain operations via `checkpoint()`)
|
|
1078
1089
|
|
|
1079
1090
|
## License
|
package/dist/app/packing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeAbiParameters, keccak256,
|
|
1
|
+
import { encodeAbiParameters, keccak256, toHex } from 'viem';
|
|
2
2
|
export function packCreateAppSessionRequestV1(definition, sessionData) {
|
|
3
3
|
const participantComponents = [
|
|
4
4
|
{ name: 'walletAddress', type: 'address' },
|
|
@@ -106,26 +106,9 @@ export function packAppV1(app) {
|
|
|
106
106
|
]);
|
|
107
107
|
return keccak256(packed);
|
|
108
108
|
}
|
|
109
|
-
function hexToBytes32(s) {
|
|
110
|
-
let hex = s.startsWith('0x') || s.startsWith('0X') ? s.slice(2) : s;
|
|
111
|
-
if (hex.length % 2 === 1) {
|
|
112
|
-
hex = '0' + hex;
|
|
113
|
-
}
|
|
114
|
-
if (!/^[0-9a-fA-F]*$/.test(hex)) {
|
|
115
|
-
return pad('0x00', { size: 32 });
|
|
116
|
-
}
|
|
117
|
-
if (hex.length === 0) {
|
|
118
|
-
return pad('0x00', { size: 32 });
|
|
119
|
-
}
|
|
120
|
-
if (hex.length > 64) {
|
|
121
|
-
hex = hex.slice(hex.length - 64);
|
|
122
|
-
}
|
|
123
|
-
const padded = hex.padStart(64, '0');
|
|
124
|
-
return `0x${padded}`;
|
|
125
|
-
}
|
|
126
109
|
export function packAppSessionKeyStateV1(state) {
|
|
127
|
-
const applicationIDHashes = state.application_ids.map(
|
|
128
|
-
const appSessionIDHashes = state.app_session_ids.map(
|
|
110
|
+
const applicationIDHashes = state.application_ids.map((id) => keccak256(toHex(id)));
|
|
111
|
+
const appSessionIDHashes = state.app_session_ids.map((id) => keccak256(toHex(id)));
|
|
129
112
|
const packed = encodeAbiParameters([
|
|
130
113
|
{ type: 'address' },
|
|
131
114
|
{ type: 'address' },
|
package/dist/app/types.d.ts
CHANGED
|
@@ -30,6 +30,16 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
30
30
|
readonly internalType: "uint32";
|
|
31
31
|
}];
|
|
32
32
|
readonly stateMutability: "view";
|
|
33
|
+
}, {
|
|
34
|
+
readonly type: "function";
|
|
35
|
+
readonly name: "MAX_CHALLENGE_DURATION";
|
|
36
|
+
readonly inputs: readonly [];
|
|
37
|
+
readonly outputs: readonly [{
|
|
38
|
+
readonly name: "";
|
|
39
|
+
readonly type: "uint32";
|
|
40
|
+
readonly internalType: "uint32";
|
|
41
|
+
}];
|
|
42
|
+
readonly stateMutability: "view";
|
|
33
43
|
}, {
|
|
34
44
|
readonly type: "function";
|
|
35
45
|
readonly name: "MAX_DEPOSIT_ESCROW_STEPS";
|
|
@@ -335,7 +345,7 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
335
345
|
}];
|
|
336
346
|
}];
|
|
337
347
|
readonly outputs: readonly [];
|
|
338
|
-
readonly stateMutability: "
|
|
348
|
+
readonly stateMutability: "nonpayable";
|
|
339
349
|
}, {
|
|
340
350
|
readonly type: "function";
|
|
341
351
|
readonly name: "claimFunds";
|
|
@@ -450,7 +460,7 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
450
460
|
}];
|
|
451
461
|
}];
|
|
452
462
|
readonly outputs: readonly [];
|
|
453
|
-
readonly stateMutability: "
|
|
463
|
+
readonly stateMutability: "nonpayable";
|
|
454
464
|
}, {
|
|
455
465
|
readonly type: "function";
|
|
456
466
|
readonly name: "createChannel";
|
|
@@ -2009,7 +2019,7 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
2009
2019
|
}];
|
|
2010
2020
|
}];
|
|
2011
2021
|
readonly outputs: readonly [];
|
|
2012
|
-
readonly stateMutability: "
|
|
2022
|
+
readonly stateMutability: "nonpayable";
|
|
2013
2023
|
}, {
|
|
2014
2024
|
readonly type: "function";
|
|
2015
2025
|
readonly name: "withdrawFromNode";
|
|
@@ -3234,6 +3244,11 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
3234
3244
|
readonly type: "event";
|
|
3235
3245
|
readonly name: "EscrowDepositsPurged";
|
|
3236
3246
|
readonly inputs: readonly [{
|
|
3247
|
+
readonly name: "escrowIds";
|
|
3248
|
+
readonly type: "bytes32[]";
|
|
3249
|
+
readonly indexed: false;
|
|
3250
|
+
readonly internalType: "bytes32[]";
|
|
3251
|
+
}, {
|
|
3237
3252
|
readonly name: "purgedCount";
|
|
3238
3253
|
readonly type: "uint256";
|
|
3239
3254
|
readonly indexed: false;
|
|
@@ -4325,6 +4340,10 @@ export declare const ChannelHubAbi: readonly [{
|
|
|
4325
4340
|
readonly type: "error";
|
|
4326
4341
|
readonly name: "IncorrectChannelStatus";
|
|
4327
4342
|
readonly inputs: readonly [];
|
|
4343
|
+
}, {
|
|
4344
|
+
readonly type: "error";
|
|
4345
|
+
readonly name: "IncorrectMsgSender";
|
|
4346
|
+
readonly inputs: readonly [];
|
|
4328
4347
|
}, {
|
|
4329
4348
|
readonly type: "error";
|
|
4330
4349
|
readonly name: "IncorrectNode";
|
|
@@ -41,6 +41,19 @@ export const ChannelHubAbi = [
|
|
|
41
41
|
],
|
|
42
42
|
stateMutability: 'view'
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
type: 'function',
|
|
46
|
+
name: 'MAX_CHALLENGE_DURATION',
|
|
47
|
+
inputs: [],
|
|
48
|
+
outputs: [
|
|
49
|
+
{
|
|
50
|
+
name: '',
|
|
51
|
+
type: 'uint32',
|
|
52
|
+
internalType: 'uint32'
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
stateMutability: 'view'
|
|
56
|
+
},
|
|
44
57
|
{
|
|
45
58
|
type: 'function',
|
|
46
59
|
name: 'MAX_DEPOSIT_ESCROW_STEPS',
|
|
@@ -431,7 +444,7 @@ export const ChannelHubAbi = [
|
|
|
431
444
|
}
|
|
432
445
|
],
|
|
433
446
|
outputs: [],
|
|
434
|
-
stateMutability: '
|
|
447
|
+
stateMutability: 'nonpayable'
|
|
435
448
|
},
|
|
436
449
|
{
|
|
437
450
|
type: 'function',
|
|
@@ -578,7 +591,7 @@ export const ChannelHubAbi = [
|
|
|
578
591
|
}
|
|
579
592
|
],
|
|
580
593
|
outputs: [],
|
|
581
|
-
stateMutability: '
|
|
594
|
+
stateMutability: 'nonpayable'
|
|
582
595
|
},
|
|
583
596
|
{
|
|
584
597
|
type: 'function',
|
|
@@ -2578,7 +2591,7 @@ export const ChannelHubAbi = [
|
|
|
2578
2591
|
}
|
|
2579
2592
|
],
|
|
2580
2593
|
outputs: [],
|
|
2581
|
-
stateMutability: '
|
|
2594
|
+
stateMutability: 'nonpayable'
|
|
2582
2595
|
},
|
|
2583
2596
|
{
|
|
2584
2597
|
type: 'function',
|
|
@@ -4136,6 +4149,12 @@ export const ChannelHubAbi = [
|
|
|
4136
4149
|
type: 'event',
|
|
4137
4150
|
name: 'EscrowDepositsPurged',
|
|
4138
4151
|
inputs: [
|
|
4152
|
+
{
|
|
4153
|
+
name: 'escrowIds',
|
|
4154
|
+
type: 'bytes32[]',
|
|
4155
|
+
indexed: false,
|
|
4156
|
+
internalType: 'bytes32[]'
|
|
4157
|
+
},
|
|
4139
4158
|
{
|
|
4140
4159
|
name: 'purgedCount',
|
|
4141
4160
|
type: 'uint256',
|
|
@@ -5526,6 +5545,11 @@ export const ChannelHubAbi = [
|
|
|
5526
5545
|
name: 'IncorrectChannelStatus',
|
|
5527
5546
|
inputs: []
|
|
5528
5547
|
},
|
|
5548
|
+
{
|
|
5549
|
+
type: 'error',
|
|
5550
|
+
name: 'IncorrectMsgSender',
|
|
5551
|
+
inputs: []
|
|
5552
|
+
},
|
|
5529
5553
|
{
|
|
5530
5554
|
type: 'error',
|
|
5531
5555
|
name: 'IncorrectNode',
|
|
@@ -132,7 +132,7 @@ export class Client {
|
|
|
132
132
|
abi: ChannelHubAbi,
|
|
133
133
|
functionName: 'depositToNode',
|
|
134
134
|
args: [token, amountBig],
|
|
135
|
-
account: this.walletSigner.account
|
|
135
|
+
account: this.walletSigner.account,
|
|
136
136
|
...(token === zeroAddress ? { value: amountBig } : {}),
|
|
137
137
|
});
|
|
138
138
|
console.log('✅ Simulation successful - executing deposit...');
|
|
@@ -179,7 +179,7 @@ export class Client {
|
|
|
179
179
|
abi: ChannelHubAbi,
|
|
180
180
|
functionName: 'withdrawFromNode',
|
|
181
181
|
args: [to, token, amountBig],
|
|
182
|
-
account: this.walletSigner.account
|
|
182
|
+
account: this.walletSigner.account,
|
|
183
183
|
});
|
|
184
184
|
console.log('✅ Simulation successful - executing withdrawal...');
|
|
185
185
|
const hash = await this.walletSigner.writeContract(request);
|
|
@@ -237,7 +237,7 @@ export class Client {
|
|
|
237
237
|
abi: ChannelHubAbi,
|
|
238
238
|
functionName: 'createChannel',
|
|
239
239
|
args: [contractDef, contractState],
|
|
240
|
-
account: this.walletSigner.account
|
|
240
|
+
account: this.walletSigner.account,
|
|
241
241
|
...(nativeValue != null ? { value: nativeValue } : {}),
|
|
242
242
|
}));
|
|
243
243
|
console.log('✅ Simulation successful - executing transaction...');
|
|
@@ -26,19 +26,18 @@ export class ERC20 {
|
|
|
26
26
|
throw new Error('Wallet signer is required for approve operation');
|
|
27
27
|
}
|
|
28
28
|
try {
|
|
29
|
-
const
|
|
29
|
+
const hash = await this.walletSigner.writeContract({
|
|
30
30
|
address: this.tokenAddress,
|
|
31
31
|
abi: Erc20Abi,
|
|
32
32
|
functionName: 'approve',
|
|
33
33
|
args: [spender, amount],
|
|
34
|
-
account: this.walletSigner.account
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
await this.client.waitForTransactionReceipt({ hash });
|
|
34
|
+
account: this.walletSigner.account,
|
|
35
|
+
});
|
|
36
|
+
await this.client.waitForTransactionReceipt({ hash, confirmations: 3 });
|
|
38
37
|
return hash;
|
|
39
38
|
}
|
|
40
39
|
catch (error) {
|
|
41
|
-
console.error('❌ Approve
|
|
40
|
+
console.error('❌ Approve execution failed!');
|
|
42
41
|
if (error.message) {
|
|
43
42
|
console.error(' Reason:', error.message);
|
|
44
43
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import { EVMClient } from './interface.js';
|
|
3
|
+
import { ValidatorRegisteredEvent } from '../../core/event.js';
|
|
4
|
+
export declare function watchValidatorRegistered(contractAddress: Address, client: EVMClient, blockchainId: bigint, fromBlock: bigint, signal?: AbortSignal): AsyncGenerator<ValidatorRegisteredEvent>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
import { ChannelHubAbi } from './channel_hub_abi.js';
|
|
3
|
+
const VALIDATOR_REGISTERED_ABI = [
|
|
4
|
+
{
|
|
5
|
+
type: 'event',
|
|
6
|
+
name: 'ValidatorRegistered',
|
|
7
|
+
inputs: [
|
|
8
|
+
{ name: 'validatorId', type: 'uint8', indexed: true, internalType: 'uint8' },
|
|
9
|
+
{ name: 'validator', type: 'address', indexed: true, internalType: 'contract ISignatureValidator' },
|
|
10
|
+
],
|
|
11
|
+
anonymous: false,
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
export async function* watchValidatorRegistered(contractAddress, client, blockchainId, fromBlock, signal) {
|
|
15
|
+
let headBlock = 0n;
|
|
16
|
+
try {
|
|
17
|
+
headBlock = await client.getBlockNumber();
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
if (!signal?.aborted) {
|
|
21
|
+
console.warn('[nitrolite] watchValidatorRegistered: failed to fetch block number, historical replay and transition gap-fill skipped', err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (fromBlock > 0n && headBlock >= fromBlock) {
|
|
25
|
+
try {
|
|
26
|
+
const logs = await client.getLogs({
|
|
27
|
+
address: contractAddress,
|
|
28
|
+
event: VALIDATOR_REGISTERED_ABI[0],
|
|
29
|
+
fromBlock,
|
|
30
|
+
toBlock: headBlock,
|
|
31
|
+
strict: true,
|
|
32
|
+
});
|
|
33
|
+
for (const log of logs) {
|
|
34
|
+
if (log.removed)
|
|
35
|
+
continue;
|
|
36
|
+
if (signal?.aborted)
|
|
37
|
+
return;
|
|
38
|
+
yield {
|
|
39
|
+
blockchainId,
|
|
40
|
+
validatorId: log.args.validatorId,
|
|
41
|
+
validator: getAddress(log.args.validator),
|
|
42
|
+
blockNumber: log.blockNumber ?? headBlock,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (!signal?.aborted) {
|
|
48
|
+
console.warn('[nitrolite] watchValidatorRegistered: failed to fetch historical logs, gap fill incomplete', err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (signal?.aborted)
|
|
53
|
+
return;
|
|
54
|
+
const liveFromBlock = headBlock > 0n ? headBlock + 1n : undefined;
|
|
55
|
+
const queue = [];
|
|
56
|
+
let wakeUp = null;
|
|
57
|
+
let watchError = null;
|
|
58
|
+
let done = false;
|
|
59
|
+
const notify = () => {
|
|
60
|
+
const resolve = wakeUp;
|
|
61
|
+
wakeUp = null;
|
|
62
|
+
resolve?.();
|
|
63
|
+
};
|
|
64
|
+
const unwatch = client.watchContractEvent({
|
|
65
|
+
address: contractAddress,
|
|
66
|
+
abi: ChannelHubAbi,
|
|
67
|
+
eventName: 'ValidatorRegistered',
|
|
68
|
+
fromBlock: liveFromBlock,
|
|
69
|
+
onLogs(logs) {
|
|
70
|
+
for (const log of logs) {
|
|
71
|
+
if (log.removed)
|
|
72
|
+
continue;
|
|
73
|
+
if (log.blockNumber === null)
|
|
74
|
+
continue;
|
|
75
|
+
const { validatorId, validator } = log.args;
|
|
76
|
+
if (validatorId === undefined || !validator)
|
|
77
|
+
continue;
|
|
78
|
+
queue.push({
|
|
79
|
+
blockchainId,
|
|
80
|
+
validatorId,
|
|
81
|
+
validator: getAddress(validator),
|
|
82
|
+
blockNumber: log.blockNumber,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
notify();
|
|
86
|
+
},
|
|
87
|
+
onError(err) {
|
|
88
|
+
watchError = err;
|
|
89
|
+
done = true;
|
|
90
|
+
notify();
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
const onAbort = () => {
|
|
94
|
+
done = true;
|
|
95
|
+
notify();
|
|
96
|
+
};
|
|
97
|
+
signal?.addEventListener('abort', onAbort);
|
|
98
|
+
try {
|
|
99
|
+
while (!done || queue.length > 0) {
|
|
100
|
+
while (queue.length > 0) {
|
|
101
|
+
if (signal?.aborted)
|
|
102
|
+
return;
|
|
103
|
+
yield queue.shift();
|
|
104
|
+
}
|
|
105
|
+
if (!done) {
|
|
106
|
+
await new Promise((resolve) => {
|
|
107
|
+
wakeUp = resolve;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (watchError && !signal?.aborted) {
|
|
112
|
+
throw watchError;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
signal?.removeEventListener('abort', onAbort);
|
|
117
|
+
unwatch();
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as core from './core/index.js';
|
|
|
4
4
|
import * as app from './app/index.js';
|
|
5
5
|
import { ChannelSessionKeyStateV1, AppInfoV1 } from './rpc/types.js';
|
|
6
6
|
import { Option } from './config.js';
|
|
7
|
-
import { StateSigner, TransactionSigner } from './signers.js';
|
|
7
|
+
import { EthereumMsgSigner, StateSigner, TransactionSigner } from './signers.js';
|
|
8
8
|
export declare const DEFAULT_CHALLENGE_PERIOD = 86400;
|
|
9
9
|
export type { StateSigner, TransactionSigner };
|
|
10
10
|
export declare class Client {
|
|
@@ -70,9 +70,9 @@ export declare class Client {
|
|
|
70
70
|
channels: core.Channel[];
|
|
71
71
|
metadata: core.PaginationMetadata;
|
|
72
72
|
}>;
|
|
73
|
-
getHomeChannel(wallet: Address, asset: string): Promise<core.Channel>;
|
|
74
|
-
getEscrowChannel(escrowChannelId: string): Promise<core.Channel>;
|
|
75
|
-
getLatestState(wallet: Address, asset: string, onlySigned: boolean): Promise<core.State>;
|
|
73
|
+
getHomeChannel(wallet: Address, asset: string): Promise<core.Channel | null>;
|
|
74
|
+
getEscrowChannel(escrowChannelId: string): Promise<core.Channel | null>;
|
|
75
|
+
getLatestState(wallet: Address, asset: string, onlySigned: boolean): Promise<core.State | null>;
|
|
76
76
|
getAppSessions(options?: {
|
|
77
77
|
appSessionId?: string;
|
|
78
78
|
wallet?: Address;
|
|
@@ -83,7 +83,7 @@ export declare class Client {
|
|
|
83
83
|
sessions: app.AppSessionInfoV1[];
|
|
84
84
|
metadata: core.PaginationMetadata;
|
|
85
85
|
}>;
|
|
86
|
-
getAppDefinition(appSessionId: string): Promise<app.AppDefinitionV1>;
|
|
86
|
+
getAppDefinition(appSessionId: string): Promise<app.AppDefinitionV1 | null>;
|
|
87
87
|
createAppSession(definition: app.AppDefinitionV1, sessionData: string, quorumSigs: string[], opts?: {
|
|
88
88
|
ownerSig?: string;
|
|
89
89
|
}): Promise<{
|
|
@@ -105,11 +105,18 @@ export declare class Client {
|
|
|
105
105
|
}>;
|
|
106
106
|
registerApp(appID: string, metadata: string, creationApprovalNotRequired: boolean): Promise<void>;
|
|
107
107
|
signChannelSessionKeyState(state: ChannelSessionKeyStateV1): Promise<Hex>;
|
|
108
|
+
signChannelSessionKeyOwnership(state: ChannelSessionKeyStateV1, sessionKeySigner: EthereumMsgSigner): Promise<Hex>;
|
|
108
109
|
submitChannelSessionKeyState(state: ChannelSessionKeyStateV1): Promise<void>;
|
|
109
|
-
getLastChannelKeyStates(userAddress: string, sessionKey?: string
|
|
110
|
+
getLastChannelKeyStates(userAddress: string, sessionKey?: string, options?: {
|
|
111
|
+
includeInactive?: boolean;
|
|
112
|
+
}): Promise<ChannelSessionKeyStateV1[]>;
|
|
110
113
|
signSessionKeyState(state: app.AppSessionKeyStateV1): Promise<Hex>;
|
|
114
|
+
signAppSessionKeyOwnership(state: app.AppSessionKeyStateV1, sessionKeySigner: EthereumMsgSigner): Promise<Hex>;
|
|
111
115
|
submitSessionKeyState(state: app.AppSessionKeyStateV1): Promise<void>;
|
|
112
|
-
|
|
116
|
+
getLastAppKeyStates(userAddress: string, sessionKey?: string, options?: {
|
|
117
|
+
includeInactive?: boolean;
|
|
118
|
+
}): Promise<app.AppSessionKeyStateV1[]>;
|
|
119
|
+
watchValidatorRegistered(chainId: bigint, fromBlock?: bigint, signal?: AbortSignal): AsyncGenerator<core.ValidatorRegisteredEvent>;
|
|
113
120
|
private getBlockchainRPCInfo;
|
|
114
121
|
private createEVMClients;
|
|
115
122
|
private initializeBlockchainClient;
|