@yellow-org/sdk 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Yellow TypeScript SDK
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@yellow-org/sdk.svg)](https://www.npmjs.com/package/@yellow-org/sdk)
4
- [![License](https://img.shields.io/npm/l/@yellow-org/sdk.svg)](https://github.com/erc7824/nitrolite/blob/main/LICENSE)
4
+ [![License](https://img.shields.io/npm/l/@yellow-org/sdk.svg)](https://github.com/layer-3/nitrolite/blob/main/LICENSE)
5
5
 
6
6
  TypeScript SDK for Clearnode payment channels providing both high-level and low-level operations in a unified client:
7
7
  - **State Operations**: `deposit()`, `withdraw()`, `transfer()`, `closeHomeChannel()`, `acknowledge()` - build and co-sign states off-chain
@@ -9,7 +9,7 @@ TypeScript SDK for Clearnode payment channels providing both high-level and low-
9
9
  - **Low-Level Operations**: Direct RPC access for custom flows and advanced use cases
10
10
  - **Full Feature Parity**: 100% compatibility with Go SDK functionality
11
11
 
12
- > If you are migrating from `@erc7824/nitrolite@v0.5.3`, please consider using the [@yellow-org/sdk-compat](https://www.npmjs.com/package/@yellow-org/sdk-compat) package. It is a translation layer that uses this SDK underneath and maps the familiar v0.5.3 API surfaces to this SDK.
12
+ > If you are migrating from `@layer-3/nitrolite@v0.5.3`, please consider using the [@yellow-org/sdk-compat](https://www.npmjs.com/package/@yellow-org/sdk-compat) package. It is a translation layer that uses this SDK underneath and maps the familiar v0.5.3 API surfaces to this SDK.
13
13
 
14
14
  ## Method Cheat Sheet
15
15
 
@@ -52,11 +52,18 @@ client.getEscrowChannel(escrowChannelId) // Escrow channel info
52
52
  client.getLatestState(wallet, asset, onlySigned) // Latest state
53
53
  ```
54
54
 
55
+ ### App Registry
56
+ ```typescript
57
+ client.getApps(opts) // List registered apps
58
+ client.registerApp(appID, metadata, approvalNotRequired) // Register new app
59
+ ```
60
+
55
61
  ### App Sessions
56
62
  ```typescript
57
63
  client.getAppSessions(opts) // List sessions
58
64
  client.getAppDefinition(appSessionId) // Session definition
59
65
  client.createAppSession(definition, sessionData, sigs) // Create session
66
+ client.createAppSession(def, data, sigs, { ownerSig }) // Create with owner approval
60
67
  client.submitAppSessionDeposit(update, sigs, asset, amount) // Deposit to session
61
68
  client.submitAppState(update, sigs) // Update session
62
69
  client.rebalanceAppSessions(signedUpdates) // Atomic rebalance
@@ -145,7 +152,7 @@ main().catch(console.error);
145
152
  ```
146
153
  sdk/ts/src/
147
154
  ├── client.ts # Core client, constructors, high-level operations
148
- ├── signers.ts # EthereumMsgSigner and EthereumRawSigner
155
+ ├── signers.ts # Signers (EthereumMsg/Raw, Channel, AppSession)
149
156
  ├── config.ts # Configuration options
150
157
  ├── asset_store.ts # Asset metadata caching
151
158
  ├── utils.ts # Type transformations
@@ -391,6 +398,21 @@ const state = await client.getLatestState(wallet, asset, onlySigned);
391
398
 
392
399
  **Note:** State submission and channel creation are handled internally by state operations (`deposit()`, `withdraw()`, `transfer()`). On-chain settlement is handled by `checkpoint()`.
393
400
 
401
+ ### App Registry
402
+
403
+ ```typescript
404
+ // List registered applications with optional filtering
405
+ const { apps, metadata } = await client.getApps({
406
+ appId: 'my-app',
407
+ ownerWallet: '0x1234...',
408
+ page: 1,
409
+ pageSize: 10,
410
+ });
411
+
412
+ // Register a new application
413
+ await client.registerApp('my-app', '{"name": "My App"}', false);
414
+ ```
415
+
394
416
  ### App Sessions (Low-Level)
395
417
 
396
418
  ```typescript
@@ -417,6 +439,50 @@ await client.submitAppState(appUpdate, quorumSigs);
417
439
  const batchId = await client.rebalanceAppSessions(signedUpdates);
418
440
  ```
419
441
 
442
+ #### Owner Approval for App Session Creation
443
+
444
+ When an app is registered with `creationApprovalNotRequired: false`, the app owner must sign the session creation request. Pass the owner's signature via the options parameter:
445
+
446
+ ```typescript
447
+ import { AppSessionWalletSignerV1, EthereumMsgSigner } from '@yellow-org/sdk';
448
+
449
+ // Owner signs the create request using their app session signer
450
+ const ownerMsgSigner = new EthereumMsgSigner(ownerPrivateKey);
451
+ const ownerAppSessionSigner = new AppSessionWalletSignerV1(ownerMsgSigner);
452
+ const ownerSig = await ownerAppSessionSigner.signMessage(createRequest);
453
+
454
+ const { appSessionId } = await client.createAppSession(
455
+ definition,
456
+ sessionData,
457
+ quorumSigs,
458
+ { ownerSig }
459
+ );
460
+ ```
461
+
462
+ ### App Session Signers
463
+
464
+ App session operations require signatures with a type byte prefix, similar to channel signers:
465
+
466
+ | Type | Byte | Class | Usage |
467
+ |------|------|-------|-------|
468
+ | Wallet | `0xA1` | `AppSessionWalletSignerV1` | Main wallet signs app session operations |
469
+ | Session Key | `0xA2` | `AppSessionKeySignerV1` | Delegated session key signs on behalf of wallet |
470
+
471
+ ```typescript
472
+ import { EthereumMsgSigner, AppSessionWalletSignerV1, AppSessionKeySignerV1 } from '@yellow-org/sdk';
473
+
474
+ // Create app session wallet signer
475
+ const msgSigner = new EthereumMsgSigner(privateKey);
476
+ const appSessionSigner = new AppSessionWalletSignerV1(msgSigner);
477
+
478
+ // Sign app session operations (create, deposit, state updates, etc.)
479
+ const sig = await appSessionSigner.signMessage(packedRequest);
480
+
481
+ // For session key delegation
482
+ const sessionKeyMsgSigner = new EthereumMsgSigner(sessionKeyPrivateKey);
483
+ const sessionKeySigner = new AppSessionKeySignerV1(sessionKeyMsgSigner);
484
+ ```
485
+
420
486
  ### App Session Keys
421
487
 
422
488
  ```typescript
@@ -734,8 +800,17 @@ queryTransactions().catch(console.error);
734
800
 
735
801
  ### Example 4: App Session Workflow
736
802
 
803
+ For a comprehensive multi-party app session example including app registration, owner approval,
804
+ session key delegation, deposits, redistribution, and more, see
805
+ [examples/app_sessions/lifecycle.ts](examples/app_sessions/lifecycle.ts).
806
+
737
807
  ```typescript
738
- import { Client, createSigners, withBlockchainRPC } from '@yellow-org/sdk';
808
+ import {
809
+ Client, createSigners, EthereumMsgSigner,
810
+ AppSessionWalletSignerV1, withBlockchainRPC
811
+ } from '@yellow-org/sdk';
812
+ import { AppStateUpdateIntent } from '@yellow-org/sdk/app/types';
813
+ import { packCreateAppSessionRequestV1, packAppStateUpdateV1 } from '@yellow-org/sdk/app/packing';
739
814
  import Decimal from 'decimal.js';
740
815
 
741
816
  async function appSessionExample() {
@@ -748,42 +823,55 @@ async function appSessionExample() {
748
823
  );
749
824
 
750
825
  try {
826
+ // Register app (required before creating sessions)
827
+ await client.registerApp('chess-v1', '{}', true);
828
+
829
+ // Create app session signer
830
+ const msgSigner = new EthereumMsgSigner(process.env.PRIVATE_KEY!);
831
+ const appSessionSigner = new AppSessionWalletSignerV1(msgSigner);
832
+
751
833
  // Create app session
752
834
  const definition = {
753
- application: 'chess-v1',
835
+ applicationId: 'chess-v1',
754
836
  participants: [
755
- { walletAddress: client.getUserAddress(), signatureWeight: 1 },
756
- { walletAddress: '0xOpponent...', signatureWeight: 1 },
837
+ { walletAddress: client.getUserAddress(), signatureWeight: 100 },
757
838
  ],
758
- quorum: 2,
759
- nonce: 1n,
839
+ quorum: 100,
840
+ nonce: BigInt(Date.now() * 1000000),
760
841
  };
761
842
 
843
+ const createRequest = packCreateAppSessionRequestV1(definition, '{}');
844
+ const createSig = await appSessionSigner.signMessage(createRequest);
845
+
762
846
  const { appSessionId } = await client.createAppSession(
763
847
  definition,
764
848
  '{}',
765
- ['sig1', 'sig2']
849
+ [createSig]
766
850
  );
767
851
  console.log('Session created:', appSessionId);
768
852
 
769
853
  // Deposit to app session
854
+ const depositAmount = new Decimal(50);
770
855
  const appUpdate = {
771
856
  appSessionId,
772
- intent: 1, // Deposit
773
- version: 1n,
857
+ intent: AppStateUpdateIntent.Deposit,
858
+ version: 2n,
774
859
  allocations: [{
775
860
  participant: client.getUserAddress(),
776
861
  asset: 'usdc',
777
- amount: new Decimal(50),
862
+ amount: depositAmount,
778
863
  }],
779
864
  sessionData: '{}',
780
865
  };
781
866
 
867
+ const depositRequest = packAppStateUpdateV1(appUpdate);
868
+ const depositSig = await appSessionSigner.signMessage(depositRequest);
869
+
782
870
  const nodeSig = await client.submitAppSessionDeposit(
783
871
  appUpdate,
784
- ['sig1'],
872
+ [depositSig],
785
873
  'usdc',
786
- new Decimal(50)
874
+ depositAmount
787
875
  );
788
876
  console.log('Deposit signature:', nodeSig);
789
877
 
@@ -854,6 +942,20 @@ import type {
854
942
  ChannelSessionKeyStateV1,
855
943
  PaginationMetadata,
856
944
  } from '@yellow-org/sdk';
945
+
946
+ // Signer types
947
+ import {
948
+ EthereumMsgSigner,
949
+ EthereumRawSigner,
950
+ ChannelDefaultSigner,
951
+ ChannelSessionKeyStateSigner,
952
+ AppSessionWalletSignerV1,
953
+ AppSessionKeySignerV1,
954
+ createSigners,
955
+ } from '@yellow-org/sdk';
956
+
957
+ // App Registry types (from rpc/types)
958
+ import type { AppV1, AppInfoV1 } from '@yellow-org/sdk';
857
959
  ```
858
960
 
859
961
  ### BigInt for Chain IDs
@@ -976,8 +1078,8 @@ Part of the Nitrolite project. See [LICENSE](../../LICENSE) for details.
976
1078
  ## Related Projects
977
1079
 
978
1080
  - [Nitrolite TS Compat](https://www.npmjs.com/package/@yellow-org/sdk-compat) - Compatibility layer for older TypeScript versions
979
- - [Nitrolite Go SDK](https://github.com/erc7824/nitrolite/tree/stable/sdk/go) - Go implementation with same API
980
- - [Nitrolite Smart Contracts](https://github.com/erc7824/nitrolite/tree/stable/contracts) - On-chain contracts
1081
+ - [Nitrolite Go SDK](https://github.com/layer-3/nitrolite/tree/stable/sdk/go) - Go implementation with same API
1082
+ - [Nitrolite Smart Contracts](https://github.com/layer-3/nitrolite/tree/stable/contracts) - On-chain contracts
981
1083
 
982
1084
  ---
983
1085
 
@@ -1,7 +1,9 @@
1
1
  import { AppDefinitionV1, AppStateUpdateV1, AppSessionKeyStateV1, AppSessionVersionV1 } from './types';
2
+ import { AppV1 } from '../rpc/types';
2
3
  export declare function packCreateAppSessionRequestV1(definition: AppDefinitionV1, sessionData: string): `0x${string}`;
3
4
  export declare function packAppStateUpdateV1(stateUpdate: AppStateUpdateV1): `0x${string}`;
4
5
  export declare function generateAppSessionIDV1(definition: AppDefinitionV1): `0x${string}`;
5
6
  export declare function generateRebalanceBatchIDV1(sessionVersions: AppSessionVersionV1[]): `0x${string}`;
6
7
  export declare function generateRebalanceTransactionIDV1(batchId: string, sessionId: string, asset: string): `0x${string}`;
8
+ export declare function packAppV1(app: AppV1): `0x${string}`;
7
9
  export declare function packAppSessionKeyStateV1(state: AppSessionKeyStateV1): `0x${string}`;
@@ -1,4 +1,4 @@
1
- import { encodeAbiParameters, keccak256 } from 'viem';
1
+ import { encodeAbiParameters, keccak256, pad, toHex } from 'viem';
2
2
  export function packCreateAppSessionRequestV1(definition, sessionData) {
3
3
  const participantComponents = [
4
4
  { name: 'walletAddress', type: 'address' },
@@ -15,7 +15,7 @@ export function packCreateAppSessionRequestV1(definition, sessionData) {
15
15
  { type: 'uint64' },
16
16
  { type: 'string' },
17
17
  ], [
18
- definition.application,
18
+ definition.applicationId,
19
19
  participants,
20
20
  definition.quorum,
21
21
  definition.nonce,
@@ -64,7 +64,7 @@ export function generateAppSessionIDV1(definition) {
64
64
  { type: 'tuple[]', components: participantComponents },
65
65
  { type: 'uint8' },
66
66
  { type: 'uint64' },
67
- ], [definition.application, participants, definition.quorum, definition.nonce]);
67
+ ], [definition.applicationId, participants, definition.quorum, definition.nonce]);
68
68
  return keccak256(packed);
69
69
  }
70
70
  export function generateRebalanceBatchIDV1(sessionVersions) {
@@ -89,9 +89,43 @@ export function generateRebalanceTransactionIDV1(batchId, sessionId, asset) {
89
89
  ], [batchId, sessionId, asset]);
90
90
  return keccak256(packed);
91
91
  }
92
+ export function packAppV1(app) {
93
+ const metadataHash = keccak256(toHex(new TextEncoder().encode(app.metadata)));
94
+ const packed = encodeAbiParameters([
95
+ { type: 'string' },
96
+ { type: 'address' },
97
+ { type: 'bytes32' },
98
+ { type: 'uint64' },
99
+ { type: 'bool' },
100
+ ], [
101
+ app.id,
102
+ app.owner_wallet,
103
+ metadataHash,
104
+ BigInt(app.version),
105
+ app.creation_approval_not_required,
106
+ ]);
107
+ return keccak256(packed);
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
+ }
92
126
  export function packAppSessionKeyStateV1(state) {
93
- const applicationIDHashes = state.application_ids.map((id) => id);
94
- const appSessionIDHashes = state.app_session_ids.map((id) => id);
127
+ const applicationIDHashes = state.application_ids.map(hexToBytes32);
128
+ const appSessionIDHashes = state.app_session_ids.map(hexToBytes32);
95
129
  const packed = encodeAbiParameters([
96
130
  { type: 'address' },
97
131
  { type: 'address' },
@@ -31,7 +31,7 @@ export interface AppParticipantV1 {
31
31
  signatureWeight: number;
32
32
  }
33
33
  export interface AppDefinitionV1 {
34
- application: string;
34
+ applicationId: string;
35
35
  participants: AppParticipantV1[];
36
36
  quorum: number;
37
37
  nonce: bigint;
@@ -58,12 +58,10 @@ export interface SignedAppStateUpdateV1 {
58
58
  }
59
59
  export interface AppSessionInfoV1 {
60
60
  appSessionId: string;
61
+ appDefinition: AppDefinitionV1;
61
62
  isClosed: boolean;
62
- participants: AppParticipantV1[];
63
63
  sessionData: string;
64
- quorum: number;
65
64
  version: bigint;
66
- nonce: bigint;
67
65
  allocations: AppAllocationV1[];
68
66
  }
69
67
  export interface SessionKeyV1 {
@@ -0,0 +1,88 @@
1
+ export declare const AppRegistryAbi: readonly [{
2
+ readonly type: "function";
3
+ readonly name: "asset";
4
+ readonly inputs: readonly [];
5
+ readonly outputs: readonly [{
6
+ readonly name: "";
7
+ readonly type: "address";
8
+ }];
9
+ readonly stateMutability: "view";
10
+ }, {
11
+ readonly type: "function";
12
+ readonly name: "UNLOCK_PERIOD";
13
+ readonly inputs: readonly [];
14
+ readonly outputs: readonly [{
15
+ readonly name: "";
16
+ readonly type: "uint256";
17
+ }];
18
+ readonly stateMutability: "view";
19
+ }, {
20
+ readonly type: "function";
21
+ readonly name: "balanceOf";
22
+ readonly inputs: readonly [{
23
+ readonly name: "user";
24
+ readonly type: "address";
25
+ }];
26
+ readonly outputs: readonly [{
27
+ readonly name: "";
28
+ readonly type: "uint256";
29
+ }];
30
+ readonly stateMutability: "view";
31
+ }, {
32
+ readonly type: "function";
33
+ readonly name: "lockStateOf";
34
+ readonly inputs: readonly [{
35
+ readonly name: "user";
36
+ readonly type: "address";
37
+ }];
38
+ readonly outputs: readonly [{
39
+ readonly name: "";
40
+ readonly type: "uint8";
41
+ }];
42
+ readonly stateMutability: "view";
43
+ }, {
44
+ readonly type: "function";
45
+ readonly name: "unlockTimestampOf";
46
+ readonly inputs: readonly [{
47
+ readonly name: "user";
48
+ readonly type: "address";
49
+ }];
50
+ readonly outputs: readonly [{
51
+ readonly name: "";
52
+ readonly type: "uint256";
53
+ }];
54
+ readonly stateMutability: "view";
55
+ }, {
56
+ readonly type: "function";
57
+ readonly name: "lock";
58
+ readonly inputs: readonly [{
59
+ readonly name: "target";
60
+ readonly type: "address";
61
+ }, {
62
+ readonly name: "amount";
63
+ readonly type: "uint256";
64
+ }];
65
+ readonly outputs: readonly [];
66
+ readonly stateMutability: "nonpayable";
67
+ }, {
68
+ readonly type: "function";
69
+ readonly name: "relock";
70
+ readonly inputs: readonly [];
71
+ readonly outputs: readonly [];
72
+ readonly stateMutability: "nonpayable";
73
+ }, {
74
+ readonly type: "function";
75
+ readonly name: "unlock";
76
+ readonly inputs: readonly [];
77
+ readonly outputs: readonly [];
78
+ readonly stateMutability: "nonpayable";
79
+ }, {
80
+ readonly type: "function";
81
+ readonly name: "withdraw";
82
+ readonly inputs: readonly [{
83
+ readonly name: "destination";
84
+ readonly type: "address";
85
+ }];
86
+ readonly outputs: readonly [];
87
+ readonly stateMutability: "nonpayable";
88
+ }];
@@ -0,0 +1,68 @@
1
+ export const AppRegistryAbi = [
2
+ {
3
+ type: 'function',
4
+ name: 'asset',
5
+ inputs: [],
6
+ outputs: [{ name: '', type: 'address' }],
7
+ stateMutability: 'view',
8
+ },
9
+ {
10
+ type: 'function',
11
+ name: 'UNLOCK_PERIOD',
12
+ inputs: [],
13
+ outputs: [{ name: '', type: 'uint256' }],
14
+ stateMutability: 'view',
15
+ },
16
+ {
17
+ type: 'function',
18
+ name: 'balanceOf',
19
+ inputs: [{ name: 'user', type: 'address' }],
20
+ outputs: [{ name: '', type: 'uint256' }],
21
+ stateMutability: 'view',
22
+ },
23
+ {
24
+ type: 'function',
25
+ name: 'lockStateOf',
26
+ inputs: [{ name: 'user', type: 'address' }],
27
+ outputs: [{ name: '', type: 'uint8' }],
28
+ stateMutability: 'view',
29
+ },
30
+ {
31
+ type: 'function',
32
+ name: 'unlockTimestampOf',
33
+ inputs: [{ name: 'user', type: 'address' }],
34
+ outputs: [{ name: '', type: 'uint256' }],
35
+ stateMutability: 'view',
36
+ },
37
+ {
38
+ type: 'function',
39
+ name: 'lock',
40
+ inputs: [
41
+ { name: 'target', type: 'address' },
42
+ { name: 'amount', type: 'uint256' },
43
+ ],
44
+ outputs: [],
45
+ stateMutability: 'nonpayable',
46
+ },
47
+ {
48
+ type: 'function',
49
+ name: 'relock',
50
+ inputs: [],
51
+ outputs: [],
52
+ stateMutability: 'nonpayable',
53
+ },
54
+ {
55
+ type: 'function',
56
+ name: 'unlock',
57
+ inputs: [],
58
+ outputs: [],
59
+ stateMutability: 'nonpayable',
60
+ },
61
+ {
62
+ type: 'function',
63
+ name: 'withdraw',
64
+ inputs: [{ name: 'destination', type: 'address' }],
65
+ outputs: [],
66
+ stateMutability: 'nonpayable',
67
+ },
68
+ ];
@@ -4,3 +4,5 @@ export * from './utils';
4
4
  export * from './erc20';
5
5
  export * from './channel_hub_abi';
6
6
  export * from './client';
7
+ export * from './app_registry_abi';
8
+ export * from './locking_client';
@@ -4,3 +4,5 @@ export * from './utils';
4
4
  export * from './erc20';
5
5
  export * from './channel_hub_abi';
6
6
  export * from './client';
7
+ export * from './app_registry_abi';
8
+ export * from './locking_client';
@@ -0,0 +1,21 @@
1
+ import { Address } from 'viem';
2
+ import Decimal from 'decimal.js';
3
+ import { EVMClient, WalletSigner } from './interface';
4
+ export declare class LockingClient {
5
+ private contractAddress;
6
+ private evmClient;
7
+ private walletSigner?;
8
+ private tokenAddress?;
9
+ private tokenDecimals?;
10
+ constructor(contractAddress: Address, evmClient: EVMClient, walletSigner?: WalletSigner);
11
+ private requireWalletSigner;
12
+ private ensureTokenInfo;
13
+ lock(target: Address, amount: Decimal): Promise<string>;
14
+ relock(): Promise<string>;
15
+ unlock(): Promise<string>;
16
+ withdraw(destination: Address): Promise<string>;
17
+ approveToken(amount: Decimal): Promise<string>;
18
+ getBalance(user: Address): Promise<Decimal>;
19
+ getLockState(user: Address): Promise<number>;
20
+ getTokenDecimals(): Promise<number>;
21
+ }
@@ -0,0 +1,124 @@
1
+ import Decimal from 'decimal.js';
2
+ import { decimalToBigInt } from '../../core/utils';
3
+ import { AppRegistryAbi } from './app_registry_abi';
4
+ import { Erc20Abi } from './erc20_abi';
5
+ export class LockingClient {
6
+ constructor(contractAddress, evmClient, walletSigner) {
7
+ this.contractAddress = contractAddress;
8
+ this.evmClient = evmClient;
9
+ this.walletSigner = walletSigner;
10
+ }
11
+ requireWalletSigner() {
12
+ if (!this.walletSigner) {
13
+ throw new Error('Write operations require a wallet signer. In Node.js, use a TransactionSigner that implements getAccount() (e.g., EthereumRawSigner)');
14
+ }
15
+ return this.walletSigner;
16
+ }
17
+ async ensureTokenInfo() {
18
+ if (this.tokenAddress !== undefined && this.tokenDecimals !== undefined) {
19
+ return { address: this.tokenAddress, decimals: this.tokenDecimals };
20
+ }
21
+ const tokenAddress = await this.evmClient.readContract({
22
+ address: this.contractAddress,
23
+ abi: AppRegistryAbi,
24
+ functionName: 'asset',
25
+ });
26
+ const decimals = await this.evmClient.readContract({
27
+ address: tokenAddress,
28
+ abi: Erc20Abi,
29
+ functionName: 'decimals',
30
+ });
31
+ this.tokenAddress = tokenAddress;
32
+ this.tokenDecimals = decimals;
33
+ return { address: tokenAddress, decimals };
34
+ }
35
+ async lock(target, amount) {
36
+ const walletSigner = this.requireWalletSigner();
37
+ const { decimals } = await this.ensureTokenInfo();
38
+ const amountBig = decimalToBigInt(amount, decimals);
39
+ const { request } = await this.evmClient.simulateContract({
40
+ address: this.contractAddress,
41
+ abi: AppRegistryAbi,
42
+ functionName: 'lock',
43
+ args: [target, amountBig],
44
+ account: walletSigner.account.address,
45
+ });
46
+ const hash = await walletSigner.writeContract(request);
47
+ await this.evmClient.waitForTransactionReceipt({ hash });
48
+ return hash;
49
+ }
50
+ async relock() {
51
+ const walletSigner = this.requireWalletSigner();
52
+ const { request } = await this.evmClient.simulateContract({
53
+ address: this.contractAddress,
54
+ abi: AppRegistryAbi,
55
+ functionName: 'relock',
56
+ account: walletSigner.account.address,
57
+ });
58
+ const hash = await walletSigner.writeContract(request);
59
+ await this.evmClient.waitForTransactionReceipt({ hash });
60
+ return hash;
61
+ }
62
+ async unlock() {
63
+ const walletSigner = this.requireWalletSigner();
64
+ const { request } = await this.evmClient.simulateContract({
65
+ address: this.contractAddress,
66
+ abi: AppRegistryAbi,
67
+ functionName: 'unlock',
68
+ account: walletSigner.account.address,
69
+ });
70
+ const hash = await walletSigner.writeContract(request);
71
+ await this.evmClient.waitForTransactionReceipt({ hash });
72
+ return hash;
73
+ }
74
+ async withdraw(destination) {
75
+ const walletSigner = this.requireWalletSigner();
76
+ const { request } = await this.evmClient.simulateContract({
77
+ address: this.contractAddress,
78
+ abi: AppRegistryAbi,
79
+ functionName: 'withdraw',
80
+ args: [destination],
81
+ account: walletSigner.account.address,
82
+ });
83
+ const hash = await walletSigner.writeContract(request);
84
+ await this.evmClient.waitForTransactionReceipt({ hash });
85
+ return hash;
86
+ }
87
+ async approveToken(amount) {
88
+ const walletSigner = this.requireWalletSigner();
89
+ const { address: tokenAddress, decimals } = await this.ensureTokenInfo();
90
+ const amountBig = decimalToBigInt(amount, decimals);
91
+ const { request } = await this.evmClient.simulateContract({
92
+ address: tokenAddress,
93
+ abi: Erc20Abi,
94
+ functionName: 'approve',
95
+ args: [this.contractAddress, amountBig],
96
+ account: walletSigner.account.address,
97
+ });
98
+ const hash = await walletSigner.writeContract(request);
99
+ await this.evmClient.waitForTransactionReceipt({ hash });
100
+ return hash;
101
+ }
102
+ async getBalance(user) {
103
+ const { decimals } = await this.ensureTokenInfo();
104
+ const balance = await this.evmClient.readContract({
105
+ address: this.contractAddress,
106
+ abi: AppRegistryAbi,
107
+ functionName: 'balanceOf',
108
+ args: [user],
109
+ });
110
+ return new Decimal(balance.toString()).div(Decimal.pow(10, decimals));
111
+ }
112
+ async getLockState(user) {
113
+ return await this.evmClient.readContract({
114
+ address: this.contractAddress,
115
+ abi: AppRegistryAbi,
116
+ functionName: 'lockStateOf',
117
+ args: [user],
118
+ });
119
+ }
120
+ async getTokenDecimals() {
121
+ const { decimals } = await this.ensureTokenInfo();
122
+ return decimals;
123
+ }
124
+ }
package/dist/client.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Address, Hex } from 'viem';
2
2
  import Decimal from 'decimal.js';
3
3
  import * as core from './core';
4
4
  import * as app from './app';
5
- import { ChannelSessionKeyStateV1 } from './rpc/types';
5
+ import { ChannelSessionKeyStateV1, AppInfoV1 } from './rpc/types';
6
6
  import { Option } from './config';
7
7
  import { StateSigner, TransactionSigner } from './signers';
8
8
  export declare const DEFAULT_CHALLENGE_PERIOD = 86400;
@@ -13,6 +13,7 @@ export declare class Client {
13
13
  private exitPromise;
14
14
  private exitResolve?;
15
15
  private blockchainClients;
16
+ private blockchainLockingClients;
16
17
  private homeBlockchains;
17
18
  private stateSigner;
18
19
  private txSigner;
@@ -34,6 +35,12 @@ export declare class Client {
34
35
  challenge(state: core.State): Promise<string>;
35
36
  approveToken(chainId: bigint, asset: string, amount: Decimal): Promise<string>;
36
37
  checkTokenAllowance(chainId: bigint, tokenAddress: string, owner: string): Promise<bigint>;
38
+ escrowSecurityTokens(targetWalletAddress: string, blockchainId: bigint, amount: Decimal): Promise<string>;
39
+ initiateSecurityTokensWithdrawal(blockchainId: bigint): Promise<string>;
40
+ cancelSecurityTokensWithdrawal(blockchainId: bigint): Promise<string>;
41
+ withdrawSecurityTokens(blockchainId: bigint, destinationWalletAddress: string): Promise<string>;
42
+ approveSecurityToken(chainId: bigint, amount: Decimal): Promise<string>;
43
+ getLockedBalance(chainId: bigint, wallet: string): Promise<Decimal>;
37
44
  ping(): Promise<void>;
38
45
  getConfig(): Promise<core.NodeConfig>;
39
46
  getBlockchains(): Promise<core.Blockchain[]>;
@@ -50,6 +57,7 @@ export declare class Client {
50
57
  transactions: core.Transaction[];
51
58
  metadata: core.PaginationMetadata;
52
59
  }>;
60
+ getActionAllowances(wallet: Address): Promise<core.ActionAllowance[]>;
53
61
  getChannels(wallet: Address, options?: {
54
62
  status?: string;
55
63
  asset?: string;
@@ -73,7 +81,9 @@ export declare class Client {
73
81
  metadata: core.PaginationMetadata;
74
82
  }>;
75
83
  getAppDefinition(appSessionId: string): Promise<app.AppDefinitionV1>;
76
- createAppSession(definition: app.AppDefinitionV1, sessionData: string, quorumSigs: string[]): Promise<{
84
+ createAppSession(definition: app.AppDefinitionV1, sessionData: string, quorumSigs: string[], opts?: {
85
+ ownerSig?: string;
86
+ }): Promise<{
77
87
  appSessionId: string;
78
88
  version: string;
79
89
  status: string;
@@ -81,13 +91,26 @@ export declare class Client {
81
91
  submitAppSessionDeposit(appStateUpdate: app.AppStateUpdateV1, quorumSigs: string[], asset: string, depositAmount: Decimal): Promise<string>;
82
92
  submitAppState(appStateUpdate: app.AppStateUpdateV1, quorumSigs: string[]): Promise<void>;
83
93
  rebalanceAppSessions(signedUpdates: app.SignedAppStateUpdateV1[]): Promise<string>;
94
+ getApps(options?: {
95
+ appId?: string;
96
+ ownerWallet?: string;
97
+ page?: number;
98
+ pageSize?: number;
99
+ }): Promise<{
100
+ apps: AppInfoV1[];
101
+ metadata: core.PaginationMetadata;
102
+ }>;
103
+ registerApp(appID: string, metadata: string, creationApprovalNotRequired: boolean): Promise<void>;
84
104
  signChannelSessionKeyState(state: ChannelSessionKeyStateV1): Promise<Hex>;
85
105
  submitChannelSessionKeyState(state: ChannelSessionKeyStateV1): Promise<void>;
86
106
  getLastChannelKeyStates(userAddress: string, sessionKey?: string): Promise<ChannelSessionKeyStateV1[]>;
87
107
  signSessionKeyState(state: app.AppSessionKeyStateV1): Promise<Hex>;
88
108
  submitSessionKeyState(state: app.AppSessionKeyStateV1): Promise<void>;
89
109
  getLastKeyStates(userAddress: string, sessionKey?: string): Promise<app.AppSessionKeyStateV1[]>;
110
+ private getBlockchainRPCInfo;
111
+ private createEVMClients;
90
112
  private initializeBlockchainClient;
113
+ private initializeLockingClient;
91
114
  private buildSigValidatorsBitmap;
92
115
  private getSupportedSigValidatorsBitmap;
93
116
  private getNodeAddress;
package/dist/client.js CHANGED
@@ -5,7 +5,7 @@ import { RPCClient } from './rpc/client';
5
5
  import { WebsocketDialer } from './rpc/dialer';
6
6
  import { ClientAssetStore } from './asset_store';
7
7
  import { DefaultConfig } from './config';
8
- import { generateNonce, transformNodeConfig, transformAssets, transformBalances, transformChannel, transformState, transformTransaction, transformPaginationMetadata, transformAppDefinitionToRPC, transformAppStateUpdateToRPC, transformSignedAppStateUpdateToRPC, transformAppSessionInfo, transformAppDefinitionFromRPC, } from './utils';
8
+ import { generateNonce, transformNodeConfig, transformAssets, transformBalances, transformChannel, transformState, transformTransaction, transformPaginationMetadata, transformAppDefinitionToRPC, transformAppStateUpdateToRPC, transformSignedAppStateUpdateToRPC, transformAppSessionInfo, transformAppDefinitionFromRPC, transformActionAllowance, } from './utils';
9
9
  import * as blockchain from './blockchain';
10
10
  import { nextState, applyChannelCreation, applyAcknowledgementTransition, applyHomeDepositTransition, applyHomeWithdrawalTransition, applyTransferSendTransition, applyFinalizeTransition, applyCommitTransition } from './core/state';
11
11
  import { newVoidState } from './core/types';
@@ -30,6 +30,7 @@ export class Client {
30
30
  this.txSigner = txSigner;
31
31
  this.assetStore = assetStore;
32
32
  this.blockchainClients = new Map();
33
+ this.blockchainLockingClients = new Map();
33
34
  this.homeBlockchains = new Map();
34
35
  this.exitPromise = new Promise((resolve) => {
35
36
  this.exitResolve = resolve;
@@ -376,6 +377,30 @@ export class Client {
376
377
  const blockchainClient = this.blockchainClients.get(chainId);
377
378
  return await blockchainClient.checkAllowanceByAddress(tokenAddress, owner);
378
379
  }
380
+ async escrowSecurityTokens(targetWalletAddress, blockchainId, amount) {
381
+ await this.initializeLockingClient(blockchainId);
382
+ return this.blockchainLockingClients.get(blockchainId).lock(targetWalletAddress, amount);
383
+ }
384
+ async initiateSecurityTokensWithdrawal(blockchainId) {
385
+ await this.initializeLockingClient(blockchainId);
386
+ return this.blockchainLockingClients.get(blockchainId).unlock();
387
+ }
388
+ async cancelSecurityTokensWithdrawal(blockchainId) {
389
+ await this.initializeLockingClient(blockchainId);
390
+ return this.blockchainLockingClients.get(blockchainId).relock();
391
+ }
392
+ async withdrawSecurityTokens(blockchainId, destinationWalletAddress) {
393
+ await this.initializeLockingClient(blockchainId);
394
+ return this.blockchainLockingClients.get(blockchainId).withdraw(destinationWalletAddress);
395
+ }
396
+ async approveSecurityToken(chainId, amount) {
397
+ await this.initializeLockingClient(chainId);
398
+ return this.blockchainLockingClients.get(chainId).approveToken(amount);
399
+ }
400
+ async getLockedBalance(chainId, wallet) {
401
+ await this.initializeLockingClient(chainId);
402
+ return this.blockchainLockingClients.get(chainId).getBalance(wallet);
403
+ }
379
404
  async ping() {
380
405
  await this.rpcClient.nodeV1Ping();
381
406
  }
@@ -420,6 +445,11 @@ export class Client {
420
445
  metadata: transformPaginationMetadata(resp.metadata),
421
446
  };
422
447
  }
448
+ async getActionAllowances(wallet) {
449
+ const req = { wallet };
450
+ const resp = await this.rpcClient.userV1GetActionAllowances(req);
451
+ return resp.allowances.map(transformActionAllowance);
452
+ }
423
453
  async getChannels(wallet, options) {
424
454
  const req = {
425
455
  wallet,
@@ -486,12 +516,15 @@ export class Client {
486
516
  const resp = await this.rpcClient.appSessionsV1GetAppDefinition(req);
487
517
  return transformAppDefinitionFromRPC(resp.definition);
488
518
  }
489
- async createAppSession(definition, sessionData, quorumSigs) {
519
+ async createAppSession(definition, sessionData, quorumSigs, opts) {
490
520
  const req = {
491
521
  definition: transformAppDefinitionToRPC(definition),
492
522
  session_data: sessionData,
493
523
  quorum_sigs: quorumSigs,
494
524
  };
525
+ if (opts?.ownerSig) {
526
+ req.owner_sig = opts.ownerSig;
527
+ }
495
528
  const resp = await this.rpcClient.appSessionsV1CreateAppSession(req);
496
529
  return {
497
530
  appSessionId: resp.app_session_id,
@@ -530,6 +563,40 @@ export class Client {
530
563
  const resp = await this.rpcClient.appSessionsV1RebalanceAppSessions(req);
531
564
  return resp.batch_id;
532
565
  }
566
+ async getApps(options) {
567
+ const req = {
568
+ app_id: options?.appId,
569
+ owner_wallet: options?.ownerWallet,
570
+ pagination: options?.page && options?.pageSize ? {
571
+ offset: (options.page - 1) * options.pageSize,
572
+ limit: options.pageSize,
573
+ } : undefined,
574
+ };
575
+ const resp = await this.rpcClient.appsV1GetApps(req);
576
+ return {
577
+ apps: resp.apps,
578
+ metadata: transformPaginationMetadata(resp.metadata),
579
+ };
580
+ }
581
+ async registerApp(appID, metadata, creationApprovalNotRequired) {
582
+ const appDef = {
583
+ id: appID,
584
+ owner_wallet: this.txSigner.getAddress(),
585
+ metadata,
586
+ version: '1',
587
+ creation_approval_not_required: creationApprovalNotRequired,
588
+ };
589
+ const packed = app.packAppV1(appDef);
590
+ if (!this.txSigner.signPersonalMessage) {
591
+ throw new Error('TransactionSigner must implement signPersonalMessage for app registration');
592
+ }
593
+ const ownerSig = await this.txSigner.signPersonalMessage(packed);
594
+ const req = {
595
+ app: appDef,
596
+ owner_sig: ownerSig,
597
+ };
598
+ await this.rpcClient.appsV1SubmitAppVersion(req);
599
+ }
533
600
  async signChannelSessionKeyState(state) {
534
601
  const metadataHash = core.getChannelSessionKeyAuthMetadataHashV1(BigInt(state.version), state.assets, BigInt(state.expires_at));
535
602
  const packed = core.packChannelKeyStateV1(state.session_key, metadataHash);
@@ -569,10 +636,7 @@ export class Client {
569
636
  const resp = await this.rpcClient.appSessionsV1GetLastKeyStates(req);
570
637
  return resp.states;
571
638
  }
572
- async initializeBlockchainClient(chainId) {
573
- if (this.blockchainClients.has(chainId)) {
574
- return;
575
- }
639
+ async getBlockchainRPCInfo(chainId) {
576
640
  const rpcUrl = this.config.blockchainRPCs?.get(chainId);
577
641
  if (!rpcUrl) {
578
642
  throw new Error(`blockchain RPC not configured for chain ${chainId} (use withBlockchainRPC)`);
@@ -580,10 +644,11 @@ export class Client {
580
644
  const config = await this.getConfig();
581
645
  const blockchainInfo = config.blockchains.find((b) => b.id === chainId);
582
646
  if (!blockchainInfo) {
583
- throw new Error(`blockchain ${chainId} not supported by node`);
647
+ throw new Error(`blockchain ${chainId} not found in node config`);
584
648
  }
585
- const contractAddress = blockchainInfo.channelHubAddress;
586
- const nodeAddress = config.nodeAddress;
649
+ return { rpcUrl, blockchainInfo, config };
650
+ }
651
+ createEVMClients(chainId, rpcUrl) {
587
652
  const publicClient = createPublicClient({
588
653
  transport: http(rpcUrl),
589
654
  });
@@ -597,7 +662,7 @@ export class Client {
597
662
  },
598
663
  };
599
664
  const isBrowser = typeof window !== 'undefined' && typeof window.ethereum !== 'undefined';
600
- let walletClient;
665
+ let walletClient = null;
601
666
  if (isBrowser) {
602
667
  walletClient = createWalletClient({
603
668
  chain,
@@ -606,15 +671,44 @@ export class Client {
606
671
  });
607
672
  }
608
673
  else {
609
- walletClient = createWalletClient({
610
- chain,
611
- transport: http(rpcUrl),
612
- account: this.txSigner.getAddress(),
613
- });
674
+ const account = this.txSigner.getAccount ? this.txSigner.getAccount() : undefined;
675
+ if (account) {
676
+ walletClient = createWalletClient({
677
+ chain,
678
+ transport: http(rpcUrl),
679
+ account,
680
+ });
681
+ }
614
682
  }
615
- const blockchainClient = new blockchain.evm.Client(contractAddress, publicClient, walletClient, chainId, nodeAddress, this.assetStore);
683
+ return { publicClient, walletClient };
684
+ }
685
+ async initializeBlockchainClient(chainId) {
686
+ if (this.blockchainClients.has(chainId)) {
687
+ return;
688
+ }
689
+ const { rpcUrl, blockchainInfo, config } = await this.getBlockchainRPCInfo(chainId);
690
+ if (!blockchainInfo.channelHubAddress) {
691
+ throw new Error(`channel hub address not configured for blockchain ${chainId}`);
692
+ }
693
+ const { publicClient, walletClient } = this.createEVMClients(chainId, rpcUrl);
694
+ if (!walletClient) {
695
+ throw new Error('Node.js environment requires a TransactionSigner that implements getAccount() (e.g., EthereumRawSigner)');
696
+ }
697
+ const blockchainClient = new blockchain.evm.Client(blockchainInfo.channelHubAddress, publicClient, walletClient, chainId, config.nodeAddress, this.assetStore);
616
698
  this.blockchainClients.set(chainId, blockchainClient);
617
699
  }
700
+ async initializeLockingClient(chainId) {
701
+ if (this.blockchainLockingClients.has(chainId)) {
702
+ return;
703
+ }
704
+ const { rpcUrl, blockchainInfo } = await this.getBlockchainRPCInfo(chainId);
705
+ if (!blockchainInfo.lockingContractAddress) {
706
+ throw new Error(`locking contract address not configured for blockchain ${chainId}`);
707
+ }
708
+ const { publicClient, walletClient } = this.createEVMClients(chainId, rpcUrl);
709
+ const lockingClient = new blockchain.evm.LockingClient(blockchainInfo.lockingContractAddress, publicClient, walletClient || undefined);
710
+ this.blockchainLockingClients.set(chainId, lockingClient);
711
+ }
618
712
  buildSigValidatorsBitmap(signerTypes) {
619
713
  const bitmap = new Uint8Array(32);
620
714
  for (const t of signerTypes) {
@@ -120,6 +120,7 @@ export interface Blockchain {
120
120
  name: string;
121
121
  id: bigint;
122
122
  channelHubAddress: Address;
123
+ lockingContractAddress?: Address;
123
124
  blockStep: bigint;
124
125
  }
125
126
  export interface Token {
@@ -154,6 +155,12 @@ export interface BalanceEntry {
154
155
  asset: string;
155
156
  balance: Decimal;
156
157
  }
158
+ export interface ActionAllowance {
159
+ gatedAction: string;
160
+ timeWindow: string;
161
+ allowance: bigint;
162
+ used: bigint;
163
+ }
157
164
  export interface PaginationParams {
158
165
  offset?: number;
159
166
  limit?: number;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Client, DEFAULT_CHALLENGE_PERIOD, type StateSigner, type TransactionSigner } from './client';
2
- export { EthereumMsgSigner, EthereumRawSigner, ChannelDefaultSigner, ChannelSessionKeyStateSigner, createSigners, } from './signers';
2
+ export { EthereumMsgSigner, EthereumRawSigner, ChannelDefaultSigner, ChannelSessionKeyStateSigner, AppSessionWalletSignerV1, AppSessionKeySignerV1, createSigners, } from './signers';
3
3
  export { type Config, DefaultConfig, type Option, withHandshakeTimeout, withErrorHandler, withBlockchainRPC } from './config';
4
4
  export { ClientAssetStore } from './asset_store';
5
5
  export * from './utils';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Client, DEFAULT_CHALLENGE_PERIOD } from './client';
2
- export { EthereumMsgSigner, EthereumRawSigner, ChannelDefaultSigner, ChannelSessionKeyStateSigner, createSigners, } from './signers';
2
+ export { EthereumMsgSigner, EthereumRawSigner, ChannelDefaultSigner, ChannelSessionKeyStateSigner, AppSessionWalletSignerV1, AppSessionKeySignerV1, createSigners, } from './signers';
3
3
  export { DefaultConfig, withHandshakeTimeout, withErrorHandler, withBlockchainRPC } from './config';
4
4
  export { ClientAssetStore } from './asset_store';
5
5
  export * from './utils';
package/dist/rpc/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Address } from 'viem';
2
- import { ChannelV1, ChannelDefinitionV1, ChannelSessionKeyStateV1, StateV1, BalanceEntryV1, TransactionV1, PaginationParamsV1, PaginationMetadataV1, AssetV1, BlockchainInfoV1 } from './types';
2
+ import { ChannelV1, ChannelDefinitionV1, ChannelSessionKeyStateV1, StateV1, BalanceEntryV1, TransactionV1, PaginationParamsV1, PaginationMetadataV1, AssetV1, BlockchainInfoV1, AppV1, AppInfoV1, ActionAllowanceV1 } from './types';
3
3
  import { AppDefinitionV1, AppStateUpdateV1, AppSessionInfoV1, AppAllocationV1, AppSessionKeyStateV1, SignedAppStateUpdateV1 } from '../app/types';
4
4
  import { TransactionType } from '../core/types';
5
5
  export interface ChannelsV1GetHomeChannelRequest {
@@ -115,6 +115,7 @@ export interface AppSessionsV1CreateAppSessionRequest {
115
115
  definition: AppDefinitionV1;
116
116
  session_data: string;
117
117
  quorum_sigs?: string[];
118
+ owner_sig?: string;
118
119
  }
119
120
  export interface AppSessionsV1CreateAppSessionResponse {
120
121
  app_session_id: string;
@@ -143,6 +144,21 @@ export interface AppSessionsV1GetLastKeyStatesRequest {
143
144
  export interface AppSessionsV1GetLastKeyStatesResponse {
144
145
  states: AppSessionKeyStateV1[];
145
146
  }
147
+ export interface AppsV1GetAppsRequest {
148
+ app_id?: string;
149
+ owner_wallet?: string;
150
+ pagination?: PaginationParamsV1;
151
+ }
152
+ export interface AppsV1GetAppsResponse {
153
+ apps: AppInfoV1[];
154
+ metadata: PaginationMetadataV1;
155
+ }
156
+ export interface AppsV1SubmitAppVersionRequest {
157
+ app: AppV1;
158
+ owner_sig: string;
159
+ }
160
+ export interface AppsV1SubmitAppVersionResponse {
161
+ }
146
162
  export interface UserV1GetBalancesRequest {
147
163
  wallet: Address;
148
164
  }
@@ -161,6 +177,12 @@ export interface UserV1GetTransactionsResponse {
161
177
  transactions: TransactionV1[];
162
178
  metadata: PaginationMetadataV1;
163
179
  }
180
+ export interface UserV1GetActionAllowancesRequest {
181
+ wallet: Address;
182
+ }
183
+ export interface UserV1GetActionAllowancesResponse {
184
+ allowances: ActionAllowanceV1[];
185
+ }
164
186
  export interface NodeV1PingRequest {
165
187
  }
166
188
  export interface NodeV1PingResponse {
@@ -24,8 +24,11 @@ export declare class RPCClient {
24
24
  appSessionsV1CloseAppSession(req: API.AppSessionsV1CloseAppSessionRequest, signal?: AbortSignal): Promise<API.AppSessionsV1CloseAppSessionResponse>;
25
25
  appSessionsV1SubmitSessionKeyState(req: API.AppSessionsV1SubmitSessionKeyStateRequest, signal?: AbortSignal): Promise<API.AppSessionsV1SubmitSessionKeyStateResponse>;
26
26
  appSessionsV1GetLastKeyStates(req: API.AppSessionsV1GetLastKeyStatesRequest, signal?: AbortSignal): Promise<API.AppSessionsV1GetLastKeyStatesResponse>;
27
+ appsV1GetApps(req: API.AppsV1GetAppsRequest, signal?: AbortSignal): Promise<API.AppsV1GetAppsResponse>;
28
+ appsV1SubmitAppVersion(req: API.AppsV1SubmitAppVersionRequest, signal?: AbortSignal): Promise<API.AppsV1SubmitAppVersionResponse>;
27
29
  userV1GetBalances(req: API.UserV1GetBalancesRequest, signal?: AbortSignal): Promise<API.UserV1GetBalancesResponse>;
28
30
  userV1GetTransactions(req: API.UserV1GetTransactionsRequest, signal?: AbortSignal): Promise<API.UserV1GetTransactionsResponse>;
31
+ userV1GetActionAllowances(req: API.UserV1GetActionAllowancesRequest, signal?: AbortSignal): Promise<API.UserV1GetActionAllowancesResponse>;
29
32
  nodeV1Ping(signal?: AbortSignal): Promise<void>;
30
33
  nodeV1GetConfig(signal?: AbortSignal): Promise<API.NodeV1GetConfigResponse>;
31
34
  nodeV1GetAssets(req: API.NodeV1GetAssetsRequest, signal?: AbortSignal): Promise<API.NodeV1GetAssetsResponse>;
@@ -72,12 +72,21 @@ export class RPCClient {
72
72
  async appSessionsV1GetLastKeyStates(req, signal) {
73
73
  return this.call(Methods.AppSessionsV1GetLastKeyStatesMethod, req, signal);
74
74
  }
75
+ async appsV1GetApps(req, signal) {
76
+ return this.call(Methods.AppsV1GetAppsMethod, req, signal);
77
+ }
78
+ async appsV1SubmitAppVersion(req, signal) {
79
+ return this.call(Methods.AppsV1SubmitAppVersionMethod, req, signal);
80
+ }
75
81
  async userV1GetBalances(req, signal) {
76
82
  return this.call(Methods.UserV1GetBalancesMethod, req, signal);
77
83
  }
78
84
  async userV1GetTransactions(req, signal) {
79
85
  return this.call(Methods.UserV1GetTransactionsMethod, req, signal);
80
86
  }
87
+ async userV1GetActionAllowances(req, signal) {
88
+ return this.call(Methods.UserV1GetActionAllowancesMethod, req, signal);
89
+ }
81
90
  async nodeV1Ping(signal) {
82
91
  await this.call(Methods.NodeV1PingMethod, {}, signal);
83
92
  }
@@ -20,9 +20,13 @@ export declare const AppSessionsV1CreateAppSessionMethod: Method;
20
20
  export declare const AppSessionsV1CloseAppSessionMethod: Method;
21
21
  export declare const AppSessionsV1SubmitSessionKeyStateMethod: Method;
22
22
  export declare const AppSessionsV1GetLastKeyStatesMethod: Method;
23
+ export declare const AppsV1Group: Group;
24
+ export declare const AppsV1GetAppsMethod: Method;
25
+ export declare const AppsV1SubmitAppVersionMethod: Method;
23
26
  export declare const UserV1Group: Group;
24
27
  export declare const UserV1GetBalancesMethod: Method;
25
28
  export declare const UserV1GetTransactionsMethod: Method;
29
+ export declare const UserV1GetActionAllowancesMethod: Method;
26
30
  export declare const NodeV1Group: Group;
27
31
  export declare const NodeV1PingMethod: Method;
28
32
  export declare const NodeV1GetConfigMethod: Method;
@@ -18,9 +18,13 @@ export const AppSessionsV1CreateAppSessionMethod = 'app_sessions.v1.create_app_s
18
18
  export const AppSessionsV1CloseAppSessionMethod = 'app_sessions.v1.close_app_session';
19
19
  export const AppSessionsV1SubmitSessionKeyStateMethod = 'app_sessions.v1.submit_session_key_state';
20
20
  export const AppSessionsV1GetLastKeyStatesMethod = 'app_sessions.v1.get_last_key_states';
21
+ export const AppsV1Group = 'apps.v1';
22
+ export const AppsV1GetAppsMethod = 'apps.v1.get_apps';
23
+ export const AppsV1SubmitAppVersionMethod = 'apps.v1.submit_app_version';
21
24
  export const UserV1Group = 'user.v1';
22
25
  export const UserV1GetBalancesMethod = 'user.v1.get_balances';
23
26
  export const UserV1GetTransactionsMethod = 'user.v1.get_transactions';
27
+ export const UserV1GetActionAllowancesMethod = 'user.v1.get_action_allowances';
24
28
  export const NodeV1Group = 'node.v1';
25
29
  export const NodeV1PingMethod = 'node.v1.ping';
26
30
  export const NodeV1GetConfigMethod = 'node.v1.get_config';
@@ -55,6 +55,17 @@ export interface ChannelSessionKeyStateV1 {
55
55
  expires_at: string;
56
56
  user_sig: string;
57
57
  }
58
+ export interface AppV1 {
59
+ id: string;
60
+ owner_wallet: string;
61
+ metadata: string;
62
+ version: string;
63
+ creation_approval_not_required: boolean;
64
+ }
65
+ export interface AppInfoV1 extends AppV1 {
66
+ created_at: string;
67
+ updated_at: string;
68
+ }
58
69
  export interface AssetV1 {
59
70
  name: string;
60
71
  symbol: string;
@@ -73,6 +84,7 @@ export interface BlockchainInfoV1 {
73
84
  name: string;
74
85
  blockchain_id: string;
75
86
  channel_hub_address: Address;
87
+ locking_contract_address?: Address;
76
88
  }
77
89
  export interface BalanceEntryV1 {
78
90
  asset: string;
@@ -93,6 +105,12 @@ export interface PaginationParamsV1 {
93
105
  offset?: number;
94
106
  limit?: number;
95
107
  }
108
+ export interface ActionAllowanceV1 {
109
+ gated_action: string;
110
+ time_window: string;
111
+ allowance: string;
112
+ used: string;
113
+ }
96
114
  export interface PaginationMetadataV1 {
97
115
  page: number;
98
116
  per_page: number;
package/dist/signers.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface TransactionSigner {
10
10
  signMessage(message: {
11
11
  raw: Hex;
12
12
  }): Promise<Hex>;
13
+ signPersonalMessage?(hash: Hex): Promise<Hex>;
14
+ getAccount?(): ReturnType<typeof privateKeyToAccount>;
13
15
  }
14
16
  export declare class EthereumMsgSigner implements StateSigner {
15
17
  private account;
@@ -25,6 +27,8 @@ export declare class EthereumRawSigner implements TransactionSigner {
25
27
  signMessage(message: {
26
28
  raw: Hex;
27
29
  }): Promise<Hex>;
30
+ signPersonalMessage(hash: Hex): Promise<Hex>;
31
+ getAccount(): ReturnType<typeof privateKeyToAccount>;
28
32
  }
29
33
  export declare class ChannelDefaultSigner implements StateSigner {
30
34
  private inner;
@@ -42,6 +46,18 @@ export declare class ChannelSessionKeyStateSigner implements StateSigner {
42
46
  getSessionKeyAddress(): Address;
43
47
  signMessage(hash: Hex): Promise<Hex>;
44
48
  }
49
+ export declare class AppSessionWalletSignerV1 implements StateSigner {
50
+ private inner;
51
+ constructor(inner: StateSigner);
52
+ getAddress(): Address;
53
+ signMessage(hash: Hex): Promise<Hex>;
54
+ }
55
+ export declare class AppSessionKeySignerV1 implements StateSigner {
56
+ private inner;
57
+ constructor(inner: StateSigner);
58
+ getAddress(): Address;
59
+ signMessage(hash: Hex): Promise<Hex>;
60
+ }
45
61
  export declare function createSigners(privateKey: Hex): {
46
62
  stateSigner: StateSigner;
47
63
  txSigner: TransactionSigner;
package/dist/signers.js CHANGED
@@ -36,6 +36,12 @@ export class EthereumRawSigner {
36
36
  async signMessage(message) {
37
37
  return await this.account.sign({ hash: message.raw });
38
38
  }
39
+ async signPersonalMessage(hash) {
40
+ return await this.account.signMessage({ message: { raw: hash } });
41
+ }
42
+ getAccount() {
43
+ return this.account;
44
+ }
39
45
  }
40
46
  export class ChannelDefaultSigner {
41
47
  constructor(inner) {
@@ -87,6 +93,30 @@ export class ChannelSessionKeyStateSigner {
87
93
  return `0x01${encoded.slice(2)}`;
88
94
  }
89
95
  }
96
+ export class AppSessionWalletSignerV1 {
97
+ constructor(inner) {
98
+ this.inner = inner;
99
+ }
100
+ getAddress() {
101
+ return this.inner.getAddress();
102
+ }
103
+ async signMessage(hash) {
104
+ const sig = await this.inner.signMessage(hash);
105
+ return `0xa1${sig.slice(2)}`;
106
+ }
107
+ }
108
+ export class AppSessionKeySignerV1 {
109
+ constructor(inner) {
110
+ this.inner = inner;
111
+ }
112
+ getAddress() {
113
+ return this.inner.getAddress();
114
+ }
115
+ async signMessage(hash) {
116
+ const sig = await this.inner.signMessage(hash);
117
+ return `0xa2${sig.slice(2)}`;
118
+ }
119
+ }
90
120
  export function createSigners(privateKey) {
91
121
  const account = privateKeyToAccount(privateKey);
92
122
  return {
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as core from './core/types';
2
2
  import * as API from './rpc/api';
3
- import { AssetV1, BalanceEntryV1, ChannelV1, LedgerV1, TransitionV1, StateV1, TransactionV1, PaginationMetadataV1 } from './rpc/types';
3
+ import { AssetV1, BalanceEntryV1, ChannelV1, LedgerV1, TransitionV1, StateV1, TransactionV1, PaginationMetadataV1, ActionAllowanceV1 } from './rpc/types';
4
4
  export declare function generateNonce(): bigint;
5
5
  export declare function transformNodeConfig(resp: API.NodeV1GetConfigResponse): core.NodeConfig;
6
6
  export declare function transformAssets(assets: AssetV1[]): core.Asset[];
@@ -11,6 +11,7 @@ export declare function transformTransition(transition: TransitionV1): core.Tran
11
11
  export declare function transformState(state: StateV1): core.State;
12
12
  export declare function transformTransaction(tx: TransactionV1): core.Transaction;
13
13
  export declare function transformPaginationMetadata(metadata: PaginationMetadataV1): core.PaginationMetadata;
14
+ export declare function transformActionAllowance(a: ActionAllowanceV1): core.ActionAllowance;
14
15
  import { AppDefinitionV1, AppStateUpdateV1, SignedAppStateUpdateV1, AppSessionInfoV1 } from './app/types';
15
16
  export declare function transformAppDefinitionToRPC(def: AppDefinitionV1): any;
16
17
  export declare function transformAppStateUpdateToRPC(update: AppStateUpdateV1): {
package/dist/utils.js CHANGED
@@ -17,6 +17,7 @@ export function transformNodeConfig(resp) {
17
17
  name: info.name,
18
18
  id: BigInt(info.blockchain_id),
19
19
  channelHubAddress: info.channel_hub_address,
20
+ lockingContractAddress: info.locking_contract_address,
20
21
  blockStep: 0n,
21
22
  }));
22
23
  return {
@@ -164,9 +165,17 @@ export function transformPaginationMetadata(metadata) {
164
165
  pageCount: metadata.page_count,
165
166
  };
166
167
  }
168
+ export function transformActionAllowance(a) {
169
+ return {
170
+ gatedAction: a.gated_action,
171
+ timeWindow: a.time_window,
172
+ allowance: BigInt(a.allowance),
173
+ used: BigInt(a.used),
174
+ };
175
+ }
167
176
  export function transformAppDefinitionToRPC(def) {
168
177
  return {
169
- application: def.application,
178
+ application_id: def.applicationId,
170
179
  participants: def.participants.map(p => ({
171
180
  wallet_address: p.walletAddress,
172
181
  signature_weight: p.signatureWeight,
@@ -197,15 +206,10 @@ export function transformSignedAppStateUpdateToRPC(signed) {
197
206
  export function transformAppSessionInfo(raw) {
198
207
  return {
199
208
  appSessionId: raw.app_session_id,
209
+ appDefinition: transformAppDefinitionFromRPC(raw.app_definition),
200
210
  isClosed: raw.status === 'closed',
201
- participants: (raw.participants || []).map((p) => ({
202
- walletAddress: p.wallet_address,
203
- signatureWeight: p.signature_weight,
204
- })),
205
211
  sessionData: raw.session_data || '',
206
- quorum: raw.quorum,
207
212
  version: BigInt(raw.version),
208
- nonce: BigInt(raw.nonce),
209
213
  allocations: (raw.allocations || []).map((a) => ({
210
214
  participant: a.participant,
211
215
  asset: a.asset,
@@ -214,8 +218,11 @@ export function transformAppSessionInfo(raw) {
214
218
  };
215
219
  }
216
220
  export function transformAppDefinitionFromRPC(raw) {
221
+ if (!raw.application_id || raw.nonce === undefined || raw.nonce === null) {
222
+ throw new Error('Invalid app definition: missing required fields (application_id, nonce)');
223
+ }
217
224
  return {
218
- application: raw.application,
225
+ applicationId: raw.application_id,
219
226
  participants: (raw.participants || []).map((p) => ({
220
227
  walletAddress: p.wallet_address,
221
228
  signatureWeight: p.signature_weight,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yellow-org/sdk",
3
- "version": "1.1.1",
4
- "description": "The Nitrolite SDK empowers developers to build high-performance, scalable web3 applications using state channels. It's designed to provide near-instant transactions and significantly improved user experiences by minimizing direct blockchain interactions.",
3
+ "version": "1.2.0",
4
+ "description": "The Yellow SDK empowers developers to build high-performance, scalable web3 applications using state channels. It's designed to provide near-instant transactions and significantly improved user experiences by minimizing direct blockchain interactions.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -34,7 +34,6 @@
34
34
  "prepublishOnly": "npm run clean && npm run build:prod"
35
35
  },
36
36
  "keywords": [
37
- "erc7824",
38
37
  "yellow",
39
38
  "yellow-org",
40
39
  "statechannels",
@@ -53,7 +52,7 @@
53
52
  "license": "MIT",
54
53
  "repository": {
55
54
  "type": "git",
56
- "url": "https://github.com/erc7824/nitrolite.git"
55
+ "url": "https://github.com/layer-3/nitrolite.git"
57
56
  },
58
57
  "engines": {
59
58
  "node": ">=20.0.0"