@toon-protocol/client 0.19.0 → 0.20.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/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { UI_RENDERER_KIND, UI_TAG, UiCoordinate, buildUiCoordinate, getUiCoordin
4
4
  import { NostrEvent, EventTemplate } from 'nostr-tools/pure';
5
5
  import { MinaSignerClientLike, SettlementBundle } from '@toon-protocol/sdk';
6
6
  import { AccumulatedClaim } from '@toon-protocol/sdk/swap';
7
- import { PrivateKeyAccount, Hex } from 'viem';
7
+ import { PrivateKeyAccount, Hex, Address } from 'viem';
8
8
  import { PrivateKeyAccount as PrivateKeyAccount$1 } from 'viem/accounts';
9
9
  export { A2uiDecision, ConsentDecision, ConsentRequest, DispatchGuardInfo, DispatchInput, GenerateContext, GeneratedRenderer, GenerativeDecision, GenerativeFallbackOptions, GenerativeFallbackRenderer, GenerativeFallbackResult, GuardedDispatchInput, IntentClassification, KindRegistry, MIME_A2UI, MIME_MCP_APP, McpUiDecision, NativeDecision, PublishBackOptions, RenderBranch, RenderDecision, RenderTrust, RendererGenerator, RendererPin, RendererPinStore, RendererPublisher, RendererSigner, ResolvedCoordinate, SwapApproval, SwapDecision, SwapRejection, SwapRejectionReason, UiResource, VerifyRendererInput, WidgetIntent, buildConsentRequest, buildRendererEventTemplate, classifyIntent, deterministicGenerator, extractUiResource, guardedRenderDispatch, isTrustDowngrade, publishBackCoordinate, renderDeterministicHtml, renderDispatch, resolveRendererMime, resolveUiCoordinate, resolveUiRenderer, verifyRendererTrust } from './render/index.js';
10
10
 
@@ -1262,6 +1262,21 @@ interface BuildSwapSettlementsParams {
1262
1262
  tokenNetworks?: Record<string, string>;
1263
1263
  /** Pre-loaded `mina-signer` client for `mina:*` re-verification. */
1264
1264
  minaSignerClient?: MinaSignerClientLike;
1265
+ /**
1266
+ * Re-verify the stored claim's signature at settle time (defense in depth
1267
+ * over the store file). Default `true` — the published **v2** sdk
1268
+ * (`@toon-protocol/sdk@^3`, connector#324 finding #1) verifies EVM claims
1269
+ * against the **v2** EIP-712 domain-separated balance-proof digest, the SAME
1270
+ * digest the receive-side used (`ingestReceivedClaims`). Reconstructing that
1271
+ * EIP-712 domain needs `chainId` + `verifyingContract`, which this builder
1272
+ * threads into the sdk signer config from `tokenNetworks` (for EVM claims) —
1273
+ * so a claim verified at receipt re-verifies correctly here.
1274
+ *
1275
+ * Set `false` only to skip the settle-time re-verify entirely (e.g. when the
1276
+ * receive-side verify is treated as the sole authoritative gate); the sdk is
1277
+ * then used only to BUILD the settlement calldata.
1278
+ */
1279
+ verifySignatures?: boolean;
1265
1280
  }
1266
1281
  /** Rebuild the sdk `AccumulatedClaim` shape from a persisted entry. */
1267
1282
  declare function entryToAccumulatedClaim(entry: ReceivedClaimEntry): AccumulatedClaim;
@@ -3017,10 +3032,15 @@ declare function readMinaChannelState(graphqlUrl: string, zkAppAddress: string,
3017
3032
  * `swapSignerAddress` (kind:10032 discovery / operator config), the
3018
3033
  * claim's self-reported signer must match (`SWAP_SIGNER_MISMATCH`,
3019
3034
  * sdk 2.x vocabulary).
3020
- * 5. signature — `verifyAccumulatedClaim` (sdk) against the expected signer:
3021
- * the advertised address when given, else the claim's self-reported one
3022
- * (which then still proves the claim is internally consistent and pins
3023
- * the address the settlement tx will verify against on-chain).
3035
+ * 5. signature — against the expected signer (advertised address when given,
3036
+ * else the claim's self-reported one). EVM claims verify against the
3037
+ * **v2 EIP-712 domain-separated** balance-proof digest
3038
+ * (`verifyEvmClaimSignature`, connector#324 finding #1) the digest binds
3039
+ * `chainId` + `verifyingContract`, so `tokenNetworks[chain]` (the
3040
+ * RollingSwapChannel address) and a numeric chain id are REQUIRED (an EVM
3041
+ * claim without them is rejected `MISSING_CHAIN_CONFIG`, fail-closed).
3042
+ * Solana/Mina keep the sdk `verifyAccumulatedClaim` path (their domain is
3043
+ * folded into the message).
3024
3044
  * 6. signer pinning — a claim may not silently rotate the signer of an
3025
3045
  * already-persisted watermark for the same channel.
3026
3046
  * 7. nonce/cumulative monotonicity vs the locally persisted watermark —
@@ -3030,7 +3050,7 @@ declare function readMinaChannelState(graphqlUrl: string, zkAppAddress: string,
3030
3050
  */
3031
3051
 
3032
3052
  /** Why a claim was rejected at receipt time. Codes are stable API. */
3033
- type ReceivedClaimRejectionCode = 'CHAIN_MISMATCH' | 'RECIPIENT_MISMATCH' | 'SWAP_SIGNER_MISMATCH' | 'SIGNER_MISMATCH' | 'SIGNATURE_INVALID' | 'MINA_VERIFICATION_UNSUPPORTED' | 'UNSUPPORTED_CHAIN' | 'NON_MONOTONIC_NONCE' | 'NON_MONOTONIC_CUMULATIVE' | 'CUMULATIVE_SHORTFALL' | 'MALFORMED_METADATA';
3053
+ type ReceivedClaimRejectionCode = 'CHAIN_MISMATCH' | 'RECIPIENT_MISMATCH' | 'SWAP_SIGNER_MISMATCH' | 'SIGNER_MISMATCH' | 'SIGNATURE_INVALID' | 'MINA_VERIFICATION_UNSUPPORTED' | 'UNSUPPORTED_CHAIN' | 'NON_MONOTONIC_NONCE' | 'NON_MONOTONIC_CUMULATIVE' | 'CUMULATIVE_SHORTFALL' | 'MISSING_CHAIN_CONFIG' | 'MALFORMED_METADATA';
3034
3054
  interface ReceivedClaimRejection {
3035
3055
  claim: AccumulatedClaim;
3036
3056
  code: ReceivedClaimRejectionCode;
@@ -3056,6 +3076,21 @@ interface IngestReceivedClaimsParams {
3056
3076
  * never the claim's own.
3057
3077
  */
3058
3078
  expectedSignerAddress?: string;
3079
+ /**
3080
+ * Per-chain settlement contract addresses (the deployed `RollingSwapChannel`
3081
+ * / `verifyingContract`), keyed by the FULL chain key (e.g. `evm:base:8453`).
3082
+ * Matches the daemon config's `tokenNetworks` map and what
3083
+ * `buildSwapSettlements` already threads on the submit side.
3084
+ *
3085
+ * REQUIRED for EVM claims under the v2 EIP-712 digest (connector#324 finding
3086
+ * #1): the claim signature is domain-separated over `(chainId,
3087
+ * verifyingContract)`, so an EVM claim whose chain key lacks a `tokenNetworks`
3088
+ * entry (or a numeric chain id) is rejected `MISSING_CHAIN_CONFIG` — it cannot
3089
+ * be verified fail-closed without the domain. Supplied by the connector/swap
3090
+ * session context (the RollingSwapChannel the client settles against). Unused
3091
+ * for Solana/Mina claims, which fold their domain into the message itself.
3092
+ */
3093
+ tokenNetworks?: Record<string, string>;
3059
3094
  /** Durable watermark store; verified claims are persisted here. */
3060
3095
  store: ReceivedClaimStore;
3061
3096
  /** Pre-loaded `mina-signer` client — required to verify `mina:*` claims. */
@@ -3089,6 +3124,134 @@ declare function hasSettlementMetadata(claim: AccumulatedClaim): claim is Accumu
3089
3124
  */
3090
3125
  declare function ingestReceivedClaims(params: IngestReceivedClaimsParams): IngestReceivedClaimsResult;
3091
3126
 
3127
+ /**
3128
+ * EVM rolling-swap **v2** balance-proof claim digest (EIP-712 domain-separated).
3129
+ *
3130
+ * Refs connector#324 **finding #1** / connector#325 (canonical spec:
3131
+ * `docs/rolling-swap-v2-digest-spec.md`). This is the client's byte-for-byte
3132
+ * conformance anchor for the v2 migration.
3133
+ *
3134
+ * ## Why this exists in the client
3135
+ *
3136
+ * The v1 digest that shipped in `@toon-protocol/core` `balanceProofHashEvm`
3137
+ * (imported transitively by the sdk's `verifyAccumulatedClaim` /
3138
+ * `buildSettlementTx`, which is what this repo used to lean on) was
3139
+ *
3140
+ * keccak256( channelId(32) || cumulativeAmount(32BE)
3141
+ * || nonce(32BE) || recipient(20) )
3142
+ *
3143
+ * — it bound **neither** `chainId` **nor** the settling contract address, so a
3144
+ * signer-signed claim redeemed on one (chain, deployment) could be replayed
3145
+ * verbatim on another for the same tuple (cross-chain / cross-deployment
3146
+ * replay). v2 folds `chainId` **and** `verifyingContract` into the signed
3147
+ * preimage via a standard **EIP-712** typed-data domain, so a signature is
3148
+ * valid on **exactly one `(chainId, contract)` pair**, and the `version="2"`
3149
+ * string makes the cutover fail-closed (a v1 raw-keccak sig can never validate
3150
+ * as v2 and vice-versa).
3151
+ *
3152
+ * ## Lockstep dependency
3153
+ *
3154
+ * The canonical v2 digest util lives in `@toon-protocol/core`
3155
+ * (`balanceProofHashEvm` → EIP-712) + `@toon-protocol/sdk`
3156
+ * (`verifyAccumulatedClaim` / `buildSettlementTx`). Those packages migrate in
3157
+ * lockstep (connector#324 release order: core/sdk → swap → client). Until a v2
3158
+ * core/sdk is published, this module is the client's own v2 digest so the
3159
+ * receive-side verify (see {@link file://./received-claims.ts}) is already
3160
+ * domain-separated and the golden-vector conformance test
3161
+ * (`evm-claim-digest.test.ts`) pins the exact literals from the spec. When the
3162
+ * v2 core/sdk ships, the recompute here should delegate to it (or be asserted
3163
+ * byte-identical to it) — the golden-vector test stays as the shared fixture.
3164
+ *
3165
+ * The signer leg (`swap` `EvmPaymentChannelSigner.signBalanceProof`) MUST also
3166
+ * move to this same EIP-712 preimage and now REQUIRES `chainId` +
3167
+ * `verifyingContract` inputs it did not take before.
3168
+ */
3169
+
3170
+ /** EIP-712 domain `name` for the RollingSwapChannel (spec §2.1). */
3171
+ declare const ROLLING_SWAP_DOMAIN_NAME: "RollingSwapChannel";
3172
+ /** EIP-712 domain `version` — `"2"` is the domain-separated migration. */
3173
+ declare const ROLLING_SWAP_DOMAIN_VERSION: "2";
3174
+ /**
3175
+ * `keccak256("ClaimBalanceProof(bytes32 channelId,uint256 cumulativeAmount,uint256 nonce,address recipient)")`.
3176
+ * Pinned from the spec; asserted in `evm-claim-digest.test.ts`.
3177
+ */
3178
+ declare const CLAIM_TYPEHASH: "0xa0c8262c1a8615f7674d3af796b14d19672d3634f89c6093502ab35c0afe2d91";
3179
+ /**
3180
+ * `keccak256("CooperativeClose(bytes32 channelId,uint256 cumulativeAmount,uint256 nonce)")`.
3181
+ * Pinned from the spec; asserted in `evm-claim-digest.test.ts`.
3182
+ */
3183
+ declare const COOP_CLOSE_TYPEHASH: "0xa5753389755fea51cd5016d7b02b508ac03f2e822d9a7ee345ec45b36574ff9f";
3184
+ /** The two chain-context inputs v2 adds over v1 (the domain binding). */
3185
+ interface EvmClaimDomainContext {
3186
+ /** Settlement chain id (`block.chainid` on-chain). e.g. `8453` for Base. */
3187
+ chainId: number;
3188
+ /** Deployed `RollingSwapChannel` address (`address(this)` / EIP-712 `verifyingContract`). */
3189
+ verifyingContract: string;
3190
+ }
3191
+ /** Fields of the `ClaimBalanceProof` message (the balance-proof claim leg). */
3192
+ interface EvmClaimMessage {
3193
+ /** Channel id, 0x-prefixed 32-byte hex. */
3194
+ channelId: string;
3195
+ /** Cumulative transferred amount (target micro-units). */
3196
+ cumulativeAmount: bigint;
3197
+ /** Monotonic balance-proof nonce. */
3198
+ nonce: bigint;
3199
+ /** Recipient (the claim payout address), 0x-prefixed 20-byte hex. */
3200
+ recipient: string;
3201
+ }
3202
+ /** Fields of the `CooperativeClose` message (recipient close-ack leg). */
3203
+ interface EvmCooperativeCloseMessage {
3204
+ channelId: string;
3205
+ cumulativeAmount: bigint;
3206
+ nonce: bigint;
3207
+ }
3208
+ /**
3209
+ * The v2 EIP-712 claim digest — `keccak256(0x1901 || domainSeparator ||
3210
+ * hashStruct(ClaimBalanceProof))` — that the on-chain `updateBalance` verifier
3211
+ * (and the swap signer) produce. This is what a received claim's signature must
3212
+ * recover against.
3213
+ */
3214
+ declare function evmClaimDigest(ctx: EvmClaimDomainContext, message: EvmClaimMessage): Hex;
3215
+ /**
3216
+ * The v2 EIP-712 cooperative-close digest — same domain as the claim, distinct
3217
+ * type hash — that a recipient's close-ack signature must recover against.
3218
+ */
3219
+ declare function evmCooperativeCloseDigest(ctx: EvmClaimDomainContext, message: EvmCooperativeCloseMessage): Hex;
3220
+ /** A 65-byte `r || s || v` signature, as 0x-hex or raw bytes. */
3221
+ type ClaimSignature = Hex | Uint8Array;
3222
+ /**
3223
+ * Recover the EVM address that signed a v2 claim digest. Uses the same
3224
+ * `@noble/curves` secp256k1 recovery + keccak address derivation as the sdk's
3225
+ * on-chain-matching `recoverEvmSignerAddress`, over the **v2** EIP-712 digest.
3226
+ * Enforces the same 65-byte `r||s||v`, `v ∈ {27,28}` envelope the on-chain OZ
3227
+ * `ECDSA.recover` accepts (fail-closed on a malformed signature).
3228
+ *
3229
+ * @returns the recovered address (lowercase 0x-hex).
3230
+ * @throws if the signature is not 65 bytes or `v` is not 27/28.
3231
+ */
3232
+ declare function recoverEvmClaimSigner(ctx: EvmClaimDomainContext, message: EvmClaimMessage, signature: ClaimSignature): Address;
3233
+ /** Result of {@link verifyEvmClaimSignature}. Mirrors the sdk verify shape. */
3234
+ type EvmClaimVerifyResult = {
3235
+ valid: true;
3236
+ recovered: Address;
3237
+ } | {
3238
+ valid: false;
3239
+ reason: string;
3240
+ };
3241
+ /**
3242
+ * Verify a received EVM balance-proof claim: recover the v2-digest signer and
3243
+ * check it equals `expectedSigner` (case-insensitive, per EVM address rules).
3244
+ *
3245
+ * Result-shaped — never throws; a malformed signature or a mismatch is a
3246
+ * `{ valid: false, reason }` so the receive ladder can bucket it fail-closed.
3247
+ */
3248
+ declare function verifyEvmClaimSignature(params: {
3249
+ ctx: EvmClaimDomainContext;
3250
+ message: EvmClaimMessage;
3251
+ signature: ClaimSignature;
3252
+ expectedSigner: string;
3253
+ }): EvmClaimVerifyResult;
3254
+
3092
3255
  /**
3093
3256
  * Per-packet preimage retention (toon-client#360, rolling-swap epic
3094
3257
  * toon-meta#145 — spec `docs/rolling-swap.md` §3 R1 / §3.2 leg-B reveal).
@@ -4175,4 +4338,4 @@ declare function loadKeystore(path: string, password: string): string;
4175
4338
  */
4176
4339
  declare function writeKeystoreFile(path: string, keystore: EncryptedKeystore): void;
4177
4340
 
4178
- export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type BuildSwapSettlementsParams, CONDITION_LENGTH, type ChainMetadata, type ChainSigner, ChannelFundingError, ChannelManager, type ClaimMessage, type ClaimResolver, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, EvmSigner, type ExecutionConditionPair, FULFILLMENT_MISMATCH_CODE, FULFILLMENT_MISMATCH_MESSAGE, type FaucetChain, type FundWalletOptions, type FundWalletResult, type H402FetchOptions, Http402Client, type Http402ClientConfig, HttpConnectorAdmin, type HttpConnectorAdminConfig, HttpIlpClient, type HttpIlpClientConfig, type HttpIlpClientFactory, HttpRuntimeClient, type HttpRuntimeClientConfig, ILP_CLAIM_HEADER, ILP_CLAIM_WRAPPED_HEADER, ILP_PEER_ID_HEADER, type IlpSendParams, type IlpSendResultWithFulfillment, type IlpTransportChoice, InMemoryPreimageRetentionStore, InMemoryReceivedClaimStore, type IngestAndRevealParams, type IngestAndRevealResult, type IngestReceivedClaimsParams, type IngestReceivedClaimsResult, JsonFileReceivedClaimStore, KeyManager, type KeyManagerConfig, MINA_CHANNEL_STATE, type MinaChannelStateReader, type MinaClaimMessage, type MinaClaimSubmitArgs, type MinaClaimSubmitter, type MinaCoSignInputs, type MinaCoSignedClaim, type MinaDepositReader, type MinaOnChainChannelState, type MinaSettlementContext, MinaSettlementError, type MinaSettlementErrorCode, type MinaSettlementResult, type MinaSignaturePair, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedFulfillHttp, type ParsedX402Challenge, type PasskeyInfo, type PreimageRetentionStore, type PublishEventResult, type ReceivedClaimEntry, type ReceivedClaimRejection, type ReceivedClaimRejectionCode, type ReceivedClaimStore, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetainedPreimage, type RetryOptions, type RevealDecision, type RevealFn, type RevealResult, type RevealedClaim, type RolledBackClaim, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type SubmitEvmSettlementParams, type SubmitEvmSettlementResult, type SwapSettlementBuild, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, ValidationError, type VaultData, type VerifiedReceivedClaim, type WalletBalance, type WalletBalanceSources, type WalletChainBalances, type WalletTokenAmount, applyDefaults, applyNetworkPresets, assertValidCondition, buildBackupEvent, buildBackupFilter, buildMinaCoSignedClaim, buildSettlementInfo, buildStoreWriteEnvelope, buildSwapSettlements, createO1jsMinaClaimSubmitter, decodeEvmSettlementTx, decryptMnemonic, defaultFaucetTimeout, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, entryToAccumulatedClaim, extractArweaveTxId, fulfillmentMatchesCondition, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, hasSettlementMetadata, httpEndpointToBtpUrl, importKeystore, ingestAndReveal, ingestReceivedClaims, isInsufficientGasError, isPrfSupported, isZeroCondition, loadKeystore, mintExecutionCondition, parseBackupPayload, parseEvmChainId, parseFulfillHttp, parseFulfillHttpBytes, parseHttpResponse, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readEvmNativeBalance, readEvmTokenBalance, readMinaBalance, readMinaChannelState, readMinaDepositTotal, readSolanaNativeBalance, readSolanaTokenBalance, readWalletBalances, requestBlobStorage, selectIlpTransport, serializeHttpRequest, submitEvmSettlement, submitMinaSettlement, validateConfig, validateMnemonic, withRetry, writeKeystoreFile };
4341
+ export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type BuildSwapSettlementsParams, CLAIM_TYPEHASH, CONDITION_LENGTH, COOP_CLOSE_TYPEHASH, type ChainMetadata, type ChainSigner, ChannelFundingError, ChannelManager, type ClaimMessage, type ClaimResolver, type ClaimSignature, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, type EvmClaimDomainContext, type EvmClaimMessage, type EvmClaimVerifyResult, type EvmCooperativeCloseMessage, EvmSigner, type ExecutionConditionPair, FULFILLMENT_MISMATCH_CODE, FULFILLMENT_MISMATCH_MESSAGE, type FaucetChain, type FundWalletOptions, type FundWalletResult, type H402FetchOptions, Http402Client, type Http402ClientConfig, HttpConnectorAdmin, type HttpConnectorAdminConfig, HttpIlpClient, type HttpIlpClientConfig, type HttpIlpClientFactory, HttpRuntimeClient, type HttpRuntimeClientConfig, ILP_CLAIM_HEADER, ILP_CLAIM_WRAPPED_HEADER, ILP_PEER_ID_HEADER, type IlpSendParams, type IlpSendResultWithFulfillment, type IlpTransportChoice, InMemoryPreimageRetentionStore, InMemoryReceivedClaimStore, type IngestAndRevealParams, type IngestAndRevealResult, type IngestReceivedClaimsParams, type IngestReceivedClaimsResult, JsonFileReceivedClaimStore, KeyManager, type KeyManagerConfig, MINA_CHANNEL_STATE, type MinaChannelStateReader, type MinaClaimMessage, type MinaClaimSubmitArgs, type MinaClaimSubmitter, type MinaCoSignInputs, type MinaCoSignedClaim, type MinaDepositReader, type MinaOnChainChannelState, type MinaSettlementContext, MinaSettlementError, type MinaSettlementErrorCode, type MinaSettlementResult, type MinaSignaturePair, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedFulfillHttp, type ParsedX402Challenge, type PasskeyInfo, type PreimageRetentionStore, type PublishEventResult, ROLLING_SWAP_DOMAIN_NAME, ROLLING_SWAP_DOMAIN_VERSION, type ReceivedClaimEntry, type ReceivedClaimRejection, type ReceivedClaimRejectionCode, type ReceivedClaimStore, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetainedPreimage, type RetryOptions, type RevealDecision, type RevealFn, type RevealResult, type RevealedClaim, type RolledBackClaim, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type SubmitEvmSettlementParams, type SubmitEvmSettlementResult, type SwapSettlementBuild, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, ValidationError, type VaultData, type VerifiedReceivedClaim, type WalletBalance, type WalletBalanceSources, type WalletChainBalances, type WalletTokenAmount, applyDefaults, applyNetworkPresets, assertValidCondition, buildBackupEvent, buildBackupFilter, buildMinaCoSignedClaim, buildSettlementInfo, buildStoreWriteEnvelope, buildSwapSettlements, createO1jsMinaClaimSubmitter, decodeEvmSettlementTx, decryptMnemonic, defaultFaucetTimeout, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, entryToAccumulatedClaim, evmClaimDigest, evmCooperativeCloseDigest, extractArweaveTxId, fulfillmentMatchesCondition, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, hasSettlementMetadata, httpEndpointToBtpUrl, importKeystore, ingestAndReveal, ingestReceivedClaims, isInsufficientGasError, isPrfSupported, isZeroCondition, loadKeystore, mintExecutionCondition, parseBackupPayload, parseEvmChainId, parseFulfillHttp, parseFulfillHttpBytes, parseHttpResponse, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readEvmNativeBalance, readEvmTokenBalance, readMinaBalance, readMinaChannelState, readMinaDepositTotal, readSolanaNativeBalance, readSolanaTokenBalance, readWalletBalances, recoverEvmClaimSigner, requestBlobStorage, selectIlpTransport, serializeHttpRequest, submitEvmSettlement, submitMinaSettlement, validateConfig, validateMnemonic, verifyEvmClaimSignature, withRetry, writeKeystoreFile };
package/dist/index.js CHANGED
@@ -4677,6 +4677,7 @@ function buildSwapSettlements(params) {
4677
4677
  claims: [entryToAccumulatedClaim(entry)],
4678
4678
  signers: { [entry.chain]: signer },
4679
4679
  recipients: { [entry.chain]: entry.recipient },
4680
+ ...params.verifySignatures !== void 0 ? { verifySignatures: params.verifySignatures } : {},
4680
4681
  ...params.minaSignerClient ? { minaSignerClient: params.minaSignerClient } : {}
4681
4682
  });
4682
4683
  const firstRejected = result.rejected[0];
@@ -4724,7 +4725,9 @@ function decodeEvmSettlementTx(bundle) {
4724
4725
  const chainIdHex = fields[6];
4725
4726
  const chainId = Number.parseInt(chainIdHex === "0x" ? "0x0" : chainIdHex, 16);
4726
4727
  if (typeof to !== "string" || to.length !== 42) {
4727
- throw new Error(`settlement tx "to" is not a 20-byte address: ${String(to)}`);
4728
+ throw new Error(
4729
+ `settlement tx "to" is not a 20-byte address: ${String(to)}`
4730
+ );
4728
4731
  }
4729
4732
  return { to, data: data === "0x" ? "0x" : data, chainId };
4730
4733
  }
@@ -4741,7 +4744,10 @@ async function submitEvmSettlement(bundle, params) {
4741
4744
  nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
4742
4745
  rpcUrls: { default: { http: [params.rpcUrl] } }
4743
4746
  });
4744
- const publicClient = createPublicClient3({ chain, transport: http3(params.rpcUrl) });
4747
+ const publicClient = createPublicClient3({
4748
+ chain,
4749
+ transport: http3(params.rpcUrl)
4750
+ });
4745
4751
  const [nonce, gasPrice, gas] = await Promise.all([
4746
4752
  publicClient.getTransactionCount({
4747
4753
  address: params.account.address,
@@ -6463,6 +6469,116 @@ function fromJson(entry) {
6463
6469
  import {
6464
6470
  verifyAccumulatedClaim
6465
6471
  } from "@toon-protocol/sdk";
6472
+
6473
+ // src/swap/evm-claim-digest.ts
6474
+ import {
6475
+ hashTypedData
6476
+ } from "viem";
6477
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
6478
+ import { keccak_256 } from "@noble/hashes/sha3.js";
6479
+ import { hexToBytes, bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
6480
+ var ROLLING_SWAP_DOMAIN_NAME = "RollingSwapChannel";
6481
+ var ROLLING_SWAP_DOMAIN_VERSION = "2";
6482
+ var CLAIM_TYPEHASH = "0xa0c8262c1a8615f7674d3af796b14d19672d3634f89c6093502ab35c0afe2d91";
6483
+ var COOP_CLOSE_TYPEHASH = "0xa5753389755fea51cd5016d7b02b508ac03f2e822d9a7ee345ec45b36574ff9f";
6484
+ var CLAIM_TYPES = {
6485
+ ClaimBalanceProof: [
6486
+ { name: "channelId", type: "bytes32" },
6487
+ { name: "cumulativeAmount", type: "uint256" },
6488
+ { name: "nonce", type: "uint256" },
6489
+ { name: "recipient", type: "address" }
6490
+ ]
6491
+ };
6492
+ var COOP_CLOSE_TYPES = {
6493
+ CooperativeClose: [
6494
+ { name: "channelId", type: "bytes32" },
6495
+ { name: "cumulativeAmount", type: "uint256" },
6496
+ { name: "nonce", type: "uint256" }
6497
+ ]
6498
+ };
6499
+ function normalizeAddress(addr) {
6500
+ return addr.toLowerCase();
6501
+ }
6502
+ function domainOf(ctx) {
6503
+ return {
6504
+ name: ROLLING_SWAP_DOMAIN_NAME,
6505
+ version: ROLLING_SWAP_DOMAIN_VERSION,
6506
+ chainId: ctx.chainId,
6507
+ verifyingContract: normalizeAddress(ctx.verifyingContract)
6508
+ };
6509
+ }
6510
+ function evmClaimDigest(ctx, message) {
6511
+ return hashTypedData({
6512
+ domain: domainOf(ctx),
6513
+ types: CLAIM_TYPES,
6514
+ primaryType: "ClaimBalanceProof",
6515
+ message: {
6516
+ channelId: message.channelId,
6517
+ cumulativeAmount: message.cumulativeAmount,
6518
+ nonce: message.nonce,
6519
+ recipient: normalizeAddress(message.recipient)
6520
+ }
6521
+ });
6522
+ }
6523
+ function evmCooperativeCloseDigest(ctx, message) {
6524
+ return hashTypedData({
6525
+ domain: domainOf(ctx),
6526
+ types: COOP_CLOSE_TYPES,
6527
+ primaryType: "CooperativeClose",
6528
+ message: {
6529
+ channelId: message.channelId,
6530
+ cumulativeAmount: message.cumulativeAmount,
6531
+ nonce: message.nonce
6532
+ }
6533
+ });
6534
+ }
6535
+ var SIG_BYTES = 65;
6536
+ function signatureToBytes(sig) {
6537
+ if (typeof sig !== "string") return sig;
6538
+ const hex = sig.startsWith("0x") ? sig.slice(2) : sig;
6539
+ return hexToBytes(hex);
6540
+ }
6541
+ function recoverEvmClaimSigner(ctx, message, signature) {
6542
+ const bytes = signatureToBytes(signature);
6543
+ if (bytes.length !== SIG_BYTES) {
6544
+ throw new Error(
6545
+ `EVM signature must be 65 bytes (r||s||v), got ${bytes.length}`
6546
+ );
6547
+ }
6548
+ const v = bytes[64];
6549
+ if (v !== 27 && v !== 28) {
6550
+ throw new Error(`EVM signature v must be 27 or 28, got ${v}`);
6551
+ }
6552
+ const digest = evmClaimDigest(ctx, message).slice(2);
6553
+ const recovered = secp256k1.Signature.fromBytes(bytes.slice(0, 64), "compact").addRecoveryBit(v - 27).recoverPublicKey(hexToBytes(digest));
6554
+ const uncompressed = recovered.toBytes(false);
6555
+ const addrHash = keccak_256(uncompressed.slice(1));
6556
+ return `0x${bytesToHex2(addrHash.slice(-20))}`;
6557
+ }
6558
+ function verifyEvmClaimSignature(params) {
6559
+ let recovered;
6560
+ try {
6561
+ recovered = recoverEvmClaimSigner(
6562
+ params.ctx,
6563
+ params.message,
6564
+ params.signature
6565
+ );
6566
+ } catch (err) {
6567
+ return {
6568
+ valid: false,
6569
+ reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`
6570
+ };
6571
+ }
6572
+ if (recovered.toLowerCase() !== params.expectedSigner.toLowerCase()) {
6573
+ return {
6574
+ valid: false,
6575
+ reason: `SIGNER_MISMATCH: recovered ${recovered}, expected ${params.expectedSigner}`
6576
+ };
6577
+ }
6578
+ return { valid: true, recovered };
6579
+ }
6580
+
6581
+ // src/swap/received-claims.ts
6466
6582
  function hasSettlementMetadata(claim) {
6467
6583
  return claim.channelId !== void 0 && claim.nonce !== void 0 && claim.cumulativeAmount !== void 0 && claim.recipient !== void 0 && claim.swapSignerAddress !== void 0;
6468
6584
  }
@@ -6517,11 +6633,49 @@ function ingestReceivedClaims(params) {
6517
6633
  continue;
6518
6634
  }
6519
6635
  const expectedSigner = params.expectedSignerAddress ?? claim.swapSignerAddress;
6520
- const sig = verifyAccumulatedClaim(
6521
- claim,
6522
- { address: expectedSigner },
6523
- params.minaSignerClient
6524
- );
6636
+ let sig;
6637
+ if (chain.startsWith("evm")) {
6638
+ const chainId = parseEvmChainId2(chain);
6639
+ const verifyingContract = params.tokenNetworks?.[chain];
6640
+ if (chainId === void 0 || !verifyingContract) {
6641
+ reject(
6642
+ claim,
6643
+ "MISSING_CHAIN_CONFIG",
6644
+ `EVM v2 balance-proof verification for ${chain} needs a numeric chain id in the chain key AND tokenNetworks["${chain}"] (the RollingSwapChannel / verifyingContract) to reconstruct the EIP-712 domain; supply it from the connector/swap session context.`
6645
+ );
6646
+ continue;
6647
+ }
6648
+ let msgNonce;
6649
+ let msgCumulative;
6650
+ try {
6651
+ msgNonce = BigInt(claim.nonce);
6652
+ msgCumulative = BigInt(claim.cumulativeAmount);
6653
+ } catch {
6654
+ reject(
6655
+ claim,
6656
+ "MALFORMED_METADATA",
6657
+ `nonce "${claim.nonce}" / cumulativeAmount "${claim.cumulativeAmount}" are not decimal integers`
6658
+ );
6659
+ continue;
6660
+ }
6661
+ sig = verifyEvmClaimSignature({
6662
+ ctx: { chainId, verifyingContract },
6663
+ message: {
6664
+ channelId: claim.channelId,
6665
+ cumulativeAmount: msgCumulative,
6666
+ nonce: msgNonce,
6667
+ recipient: claim.recipient
6668
+ },
6669
+ signature: claim.claimBytes,
6670
+ expectedSigner
6671
+ });
6672
+ } else {
6673
+ sig = verifyAccumulatedClaim(
6674
+ claim,
6675
+ { address: expectedSigner },
6676
+ params.minaSignerClient
6677
+ );
6678
+ }
6525
6679
  if (!sig.valid) {
6526
6680
  reject(claim, signatureRejectionCode(sig.reason), sig.reason);
6527
6681
  continue;
@@ -6889,14 +7043,14 @@ function fromBase642(b64) {
6889
7043
  }
6890
7044
  return bytes;
6891
7045
  }
6892
- function hexToBytes(hex) {
7046
+ function hexToBytes2(hex) {
6893
7047
  const bytes = new Uint8Array(hex.length / 2);
6894
7048
  for (let i = 0; i < hex.length; i += 2) {
6895
7049
  bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
6896
7050
  }
6897
7051
  return bytes;
6898
7052
  }
6899
- function bytesToHex2(bytes) {
7053
+ function bytesToHex3(bytes) {
6900
7054
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
6901
7055
  }
6902
7056
 
@@ -7228,7 +7382,7 @@ var KeyManager = class {
7228
7382
  const mnemonic = generateMnemonic();
7229
7383
  const identity = await deriveFullIdentity(mnemonic);
7230
7384
  const prfSalt = crypto.getRandomValues(new Uint8Array(32));
7231
- const userIdBytes = hexToBytes(identity.nostr.pubkey);
7385
+ const userIdBytes = hexToBytes2(identity.nostr.pubkey);
7232
7386
  const registration = await registerPasskey({
7233
7387
  rpId: this.config.rpId,
7234
7388
  rpName: this.config.rpName,
@@ -7270,7 +7424,7 @@ var KeyManager = class {
7270
7424
  "Passkey did not return a userHandle. Cannot determine Nostr pubkey for recovery."
7271
7425
  );
7272
7426
  }
7273
- const pubkey = bytesToHex2(discovery.userHandle);
7427
+ const pubkey = bytesToHex3(discovery.userHandle);
7274
7428
  const vault = await fetchBackupFromRelays(pubkey, this.config.relayUrls);
7275
7429
  if (!vault) {
7276
7430
  throw new Error(
@@ -7308,7 +7462,7 @@ var KeyManager = class {
7308
7462
  }
7309
7463
  const identity = await deriveFullIdentity(mnemonic);
7310
7464
  const prfSalt = crypto.getRandomValues(new Uint8Array(32));
7311
- const userIdBytes = hexToBytes(identity.nostr.pubkey);
7465
+ const userIdBytes = hexToBytes2(identity.nostr.pubkey);
7312
7466
  const registration = await registerPasskey({
7313
7467
  rpId: this.config.rpId,
7314
7468
  rpName: this.config.rpName,
@@ -7339,7 +7493,7 @@ var KeyManager = class {
7339
7493
  const identity = deriveFromNsec(secretKey);
7340
7494
  if (isPrfSupported()) {
7341
7495
  const prfSalt = crypto.getRandomValues(new Uint8Array(32));
7342
- const userIdBytes = hexToBytes(identity.nostr.pubkey);
7496
+ const userIdBytes = hexToBytes2(identity.nostr.pubkey);
7343
7497
  try {
7344
7498
  const registration = await registerPasskey({
7345
7499
  rpId: this.config.rpId,
@@ -7350,7 +7504,7 @@ var KeyManager = class {
7350
7504
  });
7351
7505
  const kek = await deriveKek(registration.prfOutput);
7352
7506
  const credIdHash = await hashCredentialId(registration.credentialId);
7353
- const hexKey = bytesToHex2(secretKey);
7507
+ const hexKey = bytesToHex3(secretKey);
7354
7508
  this.vault = await createVault(hexKey, kek, credIdHash, prfSalt);
7355
7509
  this.activeCredentialIdHash = credIdHash;
7356
7510
  await this.saveToLocalStorage();
@@ -7369,7 +7523,7 @@ var KeyManager = class {
7369
7523
  throw new Error("No active identity \u2014 call create() or recover() first");
7370
7524
  }
7371
7525
  const prfSalt = crypto.getRandomValues(new Uint8Array(32));
7372
- const userIdBytes = hexToBytes(this.identity.nostr.pubkey);
7526
+ const userIdBytes = hexToBytes2(this.identity.nostr.pubkey);
7373
7527
  const registration = await registerPasskey({
7374
7528
  rpId: this.config.rpId,
7375
7529
  rpName: this.config.rpName,
@@ -7799,7 +7953,9 @@ function writeKeystoreFile(path, keystore) {
7799
7953
  }
7800
7954
  export {
7801
7955
  BtpRuntimeClient,
7956
+ CLAIM_TYPEHASH,
7802
7957
  CONDITION_LENGTH,
7958
+ COOP_CLOSE_TYPEHASH,
7803
7959
  ChannelFundingError,
7804
7960
  ChannelManager,
7805
7961
  ConnectorError,
@@ -7826,6 +7982,8 @@ export {
7826
7982
  MinaSigner,
7827
7983
  NetworkError,
7828
7984
  OnChainChannelClient,
7985
+ ROLLING_SWAP_DOMAIN_NAME,
7986
+ ROLLING_SWAP_DOMAIN_VERSION,
7829
7987
  RendererPinStore,
7830
7988
  SolanaSigner,
7831
7989
  ToonClient,
@@ -7856,6 +8014,8 @@ export {
7856
8014
  deterministicGenerator,
7857
8015
  encryptMnemonic2 as encryptMnemonic,
7858
8016
  entryToAccumulatedClaim,
8017
+ evmClaimDigest,
8018
+ evmCooperativeCloseDigest,
7859
8019
  extractArweaveTxId,
7860
8020
  extractUiResource,
7861
8021
  fulfillmentMatchesCondition,
@@ -7896,6 +8056,7 @@ export {
7896
8056
  readSolanaNativeBalance,
7897
8057
  readSolanaTokenBalance,
7898
8058
  readWalletBalances,
8059
+ recoverEvmClaimSigner,
7899
8060
  renderDeterministicHtml,
7900
8061
  renderDispatch,
7901
8062
  requestBlobStorage,
@@ -7909,6 +8070,7 @@ export {
7909
8070
  submitMinaSettlement,
7910
8071
  validateConfig,
7911
8072
  validateMnemonic,
8073
+ verifyEvmClaimSignature,
7912
8074
  verifyRendererTrust,
7913
8075
  withRetry,
7914
8076
  writeKeystoreFile