@toon-protocol/sdk 2.2.0 → 3.0.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
@@ -60,7 +60,7 @@ const result = await node.start();
60
60
  console.log(`Connected to ${result.peerCount} peers`);
61
61
  ```
62
62
 
63
- > **Easier path:** If you just want to run a relay, use [`@toon-protocol/town`](../town) — it wraps the SDK with sensible defaults and runs out of the box. The SDK is for building custom services where you control the full pipeline.
63
+ > **Easier path:** If you just want to run a relay, use [`@toon-protocol/relay`](https://github.com/toon-protocol/relay) — it wraps the SDK with sensible defaults and runs out of the box. The SDK is for building custom services where you control the full pipeline.
64
64
 
65
65
  ## Where It Sits
66
66
 
@@ -100,17 +100,17 @@ The SDK is the framework layer. You bring handlers; it handles the protocol.
100
100
  | **Self-write bypass** | Events from your own pubkey skip pricing |
101
101
  | **Dev mode** | Skip verification and pricing for testing |
102
102
  | **[TOON](https://github.com/toon-format/toon) encoding** | Events encoded in compact text format, not JSON |
103
- | **Address assignment** | ILP addresses are derived from peering topology — your upstream peer assigns your address automatically based on your pubkey. A node with multiple upstream peers has multiple addresses. See [Protocol — ILP Address Hierarchy](../../docs/protocol.md#ilp-address-hierarchy) |
103
+ | **Address assignment** | ILP addresses are derived from peering topology — your upstream peer assigns your address automatically based on your pubkey. A node with multiple upstream peers has multiple addresses. See [Protocol — ILP Address Hierarchy](https://github.com/toon-protocol/toon-meta/blob/main/docs/protocol.md#ilp-address-hierarchy) |
104
104
  | **Invisible fee calculation** | The SDK computes multi-hop fees internally. Each intermediary advertises a fee-per-byte; the SDK sums them along the route so `publishEvent()` callers pay one total amount |
105
105
 
106
106
  ## Full Documentation
107
107
 
108
- See the [SDK Guide](../../docs/sdk-guide.md) for the complete API reference, handler patterns, verification pipeline details, publishing events, and error handling.
108
+ See the [SDK Guide](https://github.com/toon-protocol/toon-meta/blob/main/docs/sdk-guide.md) for the complete API reference, handler patterns, verification pipeline details, publishing events, and error handling.
109
109
 
110
110
  ## Requirements
111
111
 
112
- - Node.js >= 20
113
- - `@toon-protocol/connector` >= 1.6.0 (peer dependency, optional)
112
+ - Node.js >= 22
113
+ - `@toon-protocol/connector` >= 3.3.3 (peer dependency, optional)
114
114
 
115
115
  ## License
116
116
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ToonError, HandlePacketAcceptResponse, HandlePacketRejectResponse, ReputationScore, SkillDescriptor, EmbeddableConnectorLike, KnownPeer, SettlementConfig, ChainProviderConfigEntry, ConnectorChannelClient, BootstrapEventListener, BootstrapResult, DvmJobStatus, IlpSendResult, ParsedWorkflowDefinition, SwapPair } from '@toon-protocol/core';
2
- export { BootstrapEvent, BootstrapEventListener, HandlePacketAcceptResponse, HandlePacketRejectResponse, SkillDescriptor, balanceProofFieldsMina, balanceProofHashEvm, balanceProofHashSolana, bigintToBytes32BE, concatBytes, hexToBytes, minaHashToField } from '@toon-protocol/core';
2
+ export { BootstrapEvent, BootstrapEventListener, HandlePacketAcceptResponse, HandlePacketRejectResponse, SkillDescriptor, balanceProofFieldsMina, balanceProofHashEvm, balanceProofHashSolana, bigintToBytes32BE, concatBytes, coopCloseHashEvm, eip712DomainSeparatorEvm, hexToBytes, minaHashToField } from '@toon-protocol/core';
3
3
  import { NostrEvent, UnsignedEvent } from 'nostr-tools/pure';
4
4
  import { ToonRoutingMeta } from '@toon-protocol/core/toon';
5
5
  import { TransportConfig } from '@toon-protocol/connector';
@@ -1788,7 +1788,10 @@ interface SwapSignerConfig {
1788
1788
  */
1789
1789
  address: string;
1790
1790
  /**
1791
- * On-chain payment-channel contract address (EVM-only).
1791
+ * On-chain payment-channel contract address (EVM-only). Required for EVM
1792
+ * claims: it is the RLP `to` field AND the EIP-712 `verifyingContract` of the
1793
+ * v2 balance-proof digest (connector#324 finding #1), so it now binds the
1794
+ * signature to this exact deployment.
1792
1795
  */
1793
1796
  contractAddress?: string;
1794
1797
  /**
@@ -1796,7 +1799,9 @@ interface SwapSignerConfig {
1796
1799
  */
1797
1800
  programId?: string;
1798
1801
  /**
1799
- * EVM chain-id (decimal). Required for EVM claims — baked into RLP per EIP-155.
1802
+ * EVM chain-id (decimal). Required for EVM claims — baked into RLP per EIP-155
1803
+ * AND into the EIP-712 domain `chainId` of the v2 balance-proof digest
1804
+ * (connector#324 finding #1), so the signature is valid on this chain only.
1800
1805
  */
1801
1806
  chainId?: number;
1802
1807
  }
package/dist/index.js CHANGED
@@ -2374,6 +2374,8 @@ import {
2374
2374
  bigintToBytes32BE,
2375
2375
  concatBytes,
2376
2376
  balanceProofHashEvm,
2377
+ coopCloseHashEvm,
2378
+ eip712DomainSeparatorEvm,
2377
2379
  balanceProofHashSolana,
2378
2380
  minaHashToField,
2379
2381
  balanceProofFieldsMina
@@ -2388,7 +2390,19 @@ var EVM_SETTLEMENT_FUNCTION_SELECTOR = keccak_256(
2388
2390
  var EVM_SETTLEMENT_EVENT_TOPIC = "0x" + bytesToHex(
2389
2391
  keccak_256(new TextEncoder().encode(EVM_SETTLEMENT_EVENT_SIGNATURE))
2390
2392
  );
2391
- function recoverEvmSignerAddress(claim) {
2393
+ function recoverEvmSignerAddress(claim, chainId, verifyingContract) {
2394
+ if (typeof chainId !== "number" || !Number.isInteger(chainId) || chainId <= 0) {
2395
+ throw new SettlementTxError(
2396
+ "INVALID_INPUT",
2397
+ `EVM v2 digest requires a positive integer chainId, got ${chainId}`
2398
+ );
2399
+ }
2400
+ if (!verifyingContract) {
2401
+ throw new SettlementTxError(
2402
+ "INVALID_INPUT",
2403
+ "EVM v2 digest requires verifyingContract (RollingSwapChannel address)"
2404
+ );
2405
+ }
2392
2406
  if (claim.channelId === void 0 || claim.cumulativeAmount === void 0 || claim.nonce === void 0 || claim.recipient === void 0) {
2393
2407
  throw new SettlementTxError(
2394
2408
  "MISSING_SETTLEMENT_METADATA",
@@ -2415,6 +2429,7 @@ function recoverEvmSignerAddress(claim) {
2415
2429
  try {
2416
2430
  const channelIdBytes = hexToBytes2(claim.channelId);
2417
2431
  const recipientBytes = hexToBytes2(claim.recipient);
2432
+ const verifyingContractBytes = hexToBytes2(verifyingContract);
2418
2433
  if (channelIdBytes.length !== 32) {
2419
2434
  throw new Error(
2420
2435
  `channelId must be 32 bytes (got ${channelIdBytes.length})`
@@ -2425,11 +2440,18 @@ function recoverEvmSignerAddress(claim) {
2425
2440
  `recipient must be 20 bytes (got ${recipientBytes.length})`
2426
2441
  );
2427
2442
  }
2443
+ if (verifyingContractBytes.length !== 20) {
2444
+ throw new Error(
2445
+ `verifyingContract must be 20 bytes (got ${verifyingContractBytes.length})`
2446
+ );
2447
+ }
2428
2448
  msgHash = balanceProofHashEvm(
2429
2449
  channelIdBytes,
2430
2450
  BigInt(claim.cumulativeAmount),
2431
2451
  BigInt(claim.nonce),
2432
- recipientBytes
2452
+ recipientBytes,
2453
+ BigInt(chainId),
2454
+ verifyingContractBytes
2433
2455
  );
2434
2456
  const sig = secp256k1.Signature.fromBytes(
2435
2457
  compactRS,
@@ -2731,8 +2753,8 @@ function rlpDecodeList(buf) {
2731
2753
  }
2732
2754
  return items;
2733
2755
  }
2734
- function verifyEvmClaimSignature(claim, expectedAddress) {
2735
- const recovered = recoverEvmSignerAddress(claim);
2756
+ function verifyEvmClaimSignature(claim, expectedAddress, chainId, verifyingContract) {
2757
+ const recovered = recoverEvmSignerAddress(claim, chainId, verifyingContract);
2736
2758
  const expected = expectedAddress.toLowerCase();
2737
2759
  return {
2738
2760
  valid: recovered.toLowerCase() === expected,
@@ -3098,7 +3120,9 @@ function buildSettlementTx(params) {
3098
3120
  try {
3099
3121
  const { valid, recovered } = verifyEvmClaimSignature(
3100
3122
  claim,
3101
- signer.address
3123
+ signer.address,
3124
+ signer.chainId,
3125
+ signer.contractAddress
3102
3126
  );
3103
3127
  if (!valid) {
3104
3128
  rejected.push({
@@ -3348,10 +3372,18 @@ function buildSettlementTx(params) {
3348
3372
  function verifyAccumulatedClaim(claim, signer, minaSignerClient) {
3349
3373
  const kind = chainKindOf(claim.pair.to.chain);
3350
3374
  if (kind === "evm") {
3375
+ if (typeof signer.chainId !== "number" || !Number.isInteger(signer.chainId) || signer.chainId <= 0 || !signer.contractAddress) {
3376
+ return {
3377
+ valid: false,
3378
+ reason: "INVALID_INPUT: EVM signer requires chainId (positive integer) + contractAddress for the v2 EIP-712 digest (connector#324 finding #1)"
3379
+ };
3380
+ }
3351
3381
  try {
3352
3382
  const { valid, recovered } = verifyEvmClaimSignature(
3353
3383
  claim,
3354
- signer.address
3384
+ signer.address,
3385
+ signer.chainId,
3386
+ signer.contractAddress
3355
3387
  );
3356
3388
  if (valid) return { valid: true };
3357
3389
  return {
@@ -3441,6 +3473,7 @@ export {
3441
3473
  buildSettlementTx,
3442
3474
  buildSkillDescriptor,
3443
3475
  concatBytes,
3476
+ coopCloseHashEvm,
3444
3477
  createArweaveDvmHandler,
3445
3478
  createEventStorageHandler,
3446
3479
  createHandlerContext,
@@ -3451,6 +3484,7 @@ export {
3451
3484
  createSwapHandler,
3452
3485
  createVerificationPipeline,
3453
3486
  decryptFulfillClaim,
3487
+ eip712DomainSeparatorEvm,
3454
3488
  encodeReceiptSigningPayload,
3455
3489
  encryptFulfillClaim,
3456
3490
  fillEvmSettlementTxGas,