@toon-protocol/sdk 2.2.0 → 3.1.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 +5 -5
- package/dist/index.d.ts +8 -3
- package/dist/index.js +43 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
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/
|
|
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](
|
|
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](
|
|
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 >=
|
|
113
|
-
- `@toon-protocol/connector` >=
|
|
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
|
@@ -2364,9 +2364,9 @@ function validateConfig(config) {
|
|
|
2364
2364
|
}
|
|
2365
2365
|
|
|
2366
2366
|
// src/settlement/evm.ts
|
|
2367
|
-
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
2368
2367
|
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
2369
2368
|
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
2369
|
+
import { recoverEvmSigner } from "@toon-protocol/settlement-digest";
|
|
2370
2370
|
|
|
2371
2371
|
// src/settlement/hashes.ts
|
|
2372
2372
|
import {
|
|
@@ -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",
|
|
@@ -2408,13 +2422,10 @@ function recoverEvmSignerAddress(claim) {
|
|
|
2408
2422
|
`EVM signature v must be 27 or 28, got ${v}`
|
|
2409
2423
|
);
|
|
2410
2424
|
}
|
|
2411
|
-
const recovery = v - 27;
|
|
2412
|
-
const compactRS = claim.claimBytes.slice(0, 64);
|
|
2413
|
-
let msgHash;
|
|
2414
|
-
let uncompressedPubkey;
|
|
2415
2425
|
try {
|
|
2416
2426
|
const channelIdBytes = hexToBytes2(claim.channelId);
|
|
2417
2427
|
const recipientBytes = hexToBytes2(claim.recipient);
|
|
2428
|
+
const verifyingContractBytes = hexToBytes2(verifyingContract);
|
|
2418
2429
|
if (channelIdBytes.length !== 32) {
|
|
2419
2430
|
throw new Error(
|
|
2420
2431
|
`channelId must be 32 bytes (got ${channelIdBytes.length})`
|
|
@@ -2425,18 +2436,20 @@ function recoverEvmSignerAddress(claim) {
|
|
|
2425
2436
|
`recipient must be 20 bytes (got ${recipientBytes.length})`
|
|
2426
2437
|
);
|
|
2427
2438
|
}
|
|
2428
|
-
|
|
2439
|
+
if (verifyingContractBytes.length !== 20) {
|
|
2440
|
+
throw new Error(
|
|
2441
|
+
`verifyingContract must be 20 bytes (got ${verifyingContractBytes.length})`
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
const msgHash = balanceProofHashEvm(
|
|
2429
2445
|
channelIdBytes,
|
|
2430
2446
|
BigInt(claim.cumulativeAmount),
|
|
2431
2447
|
BigInt(claim.nonce),
|
|
2432
|
-
recipientBytes
|
|
2448
|
+
recipientBytes,
|
|
2449
|
+
BigInt(chainId),
|
|
2450
|
+
verifyingContractBytes
|
|
2433
2451
|
);
|
|
2434
|
-
|
|
2435
|
-
compactRS,
|
|
2436
|
-
"compact"
|
|
2437
|
-
).addRecoveryBit(recovery);
|
|
2438
|
-
const point = sig.recoverPublicKey(msgHash);
|
|
2439
|
-
uncompressedPubkey = point.toBytes(false);
|
|
2452
|
+
return recoverEvmSigner(msgHash, claim.claimBytes);
|
|
2440
2453
|
} catch (err) {
|
|
2441
2454
|
throw new SettlementTxError(
|
|
2442
2455
|
"ENCODING_FAILED",
|
|
@@ -2444,8 +2457,6 @@ function recoverEvmSignerAddress(claim) {
|
|
|
2444
2457
|
{ cause: err }
|
|
2445
2458
|
);
|
|
2446
2459
|
}
|
|
2447
|
-
const addrHash = keccak_256(uncompressedPubkey.slice(1));
|
|
2448
|
-
return "0x" + bytesToHex(addrHash.slice(-20)).toLowerCase();
|
|
2449
2460
|
}
|
|
2450
2461
|
function padLeft32(bytes) {
|
|
2451
2462
|
if (bytes.length > 32) {
|
|
@@ -2731,8 +2742,8 @@ function rlpDecodeList(buf) {
|
|
|
2731
2742
|
}
|
|
2732
2743
|
return items;
|
|
2733
2744
|
}
|
|
2734
|
-
function verifyEvmClaimSignature(claim, expectedAddress) {
|
|
2735
|
-
const recovered = recoverEvmSignerAddress(claim);
|
|
2745
|
+
function verifyEvmClaimSignature(claim, expectedAddress, chainId, verifyingContract) {
|
|
2746
|
+
const recovered = recoverEvmSignerAddress(claim, chainId, verifyingContract);
|
|
2736
2747
|
const expected = expectedAddress.toLowerCase();
|
|
2737
2748
|
return {
|
|
2738
2749
|
valid: recovered.toLowerCase() === expected,
|
|
@@ -3098,7 +3109,9 @@ function buildSettlementTx(params) {
|
|
|
3098
3109
|
try {
|
|
3099
3110
|
const { valid, recovered } = verifyEvmClaimSignature(
|
|
3100
3111
|
claim,
|
|
3101
|
-
signer.address
|
|
3112
|
+
signer.address,
|
|
3113
|
+
signer.chainId,
|
|
3114
|
+
signer.contractAddress
|
|
3102
3115
|
);
|
|
3103
3116
|
if (!valid) {
|
|
3104
3117
|
rejected.push({
|
|
@@ -3348,10 +3361,18 @@ function buildSettlementTx(params) {
|
|
|
3348
3361
|
function verifyAccumulatedClaim(claim, signer, minaSignerClient) {
|
|
3349
3362
|
const kind = chainKindOf(claim.pair.to.chain);
|
|
3350
3363
|
if (kind === "evm") {
|
|
3364
|
+
if (typeof signer.chainId !== "number" || !Number.isInteger(signer.chainId) || signer.chainId <= 0 || !signer.contractAddress) {
|
|
3365
|
+
return {
|
|
3366
|
+
valid: false,
|
|
3367
|
+
reason: "INVALID_INPUT: EVM signer requires chainId (positive integer) + contractAddress for the v2 EIP-712 digest (connector#324 finding #1)"
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3351
3370
|
try {
|
|
3352
3371
|
const { valid, recovered } = verifyEvmClaimSignature(
|
|
3353
3372
|
claim,
|
|
3354
|
-
signer.address
|
|
3373
|
+
signer.address,
|
|
3374
|
+
signer.chainId,
|
|
3375
|
+
signer.contractAddress
|
|
3355
3376
|
);
|
|
3356
3377
|
if (valid) return { valid: true };
|
|
3357
3378
|
return {
|
|
@@ -3441,6 +3462,7 @@ export {
|
|
|
3441
3462
|
buildSettlementTx,
|
|
3442
3463
|
buildSkillDescriptor,
|
|
3443
3464
|
concatBytes,
|
|
3465
|
+
coopCloseHashEvm,
|
|
3444
3466
|
createArweaveDvmHandler,
|
|
3445
3467
|
createEventStorageHandler,
|
|
3446
3468
|
createHandlerContext,
|
|
@@ -3451,6 +3473,7 @@ export {
|
|
|
3451
3473
|
createSwapHandler,
|
|
3452
3474
|
createVerificationPipeline,
|
|
3453
3475
|
decryptFulfillClaim,
|
|
3476
|
+
eip712DomainSeparatorEvm,
|
|
3454
3477
|
encodeReceiptSigningPayload,
|
|
3455
3478
|
encryptFulfillClaim,
|
|
3456
3479
|
fillEvmSettlementTxGas,
|