@toon-protocol/client 0.14.0 → 0.14.2

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
@@ -404,13 +404,21 @@ interface SignedBalanceProof extends BalanceProofParams {
404
404
  *
405
405
  * ## FULFILL data contract
406
406
  *
407
- * For a successful single-packet (non-chunked) blob upload, the DVM provider
408
- * returns the Arweave transaction ID as a **UTF-8 string, base64-encoded** in
409
- * the ILP FULFILL `data` field (the connector validates that FULFILL data is
410
- * base64). An Arweave tx ID is a 43-character base64url string (32 raw bytes).
407
+ * The deployed connector is a payment-proxy (HTTP-in-ILP): a successful blob
408
+ * upload returns the DVM's verbatim **HTTP/1.1 response message** in the ILP
409
+ * FULFILL `data` field. For a single-packet (non-chunked) upload the body is a
410
+ * JSON object:
411
411
  *
412
- * So the decode is:
413
- * `txId = utf8(base64decode(result.data))`
412
+ * HTTP/1.1 200 OK\r\n
413
+ * content-length: 189\r\n
414
+ * \r\n
415
+ * {"accept":true,"txId":"<43-char base64url>","data":"<base64 of txId>",...}
416
+ *
417
+ * We parse the HTTP envelope, fail on a non-2xx status (or `accept:false`), and
418
+ * read the Arweave tx ID from `txId` (falling back to base64-decoding `data`).
419
+ * An Arweave tx ID is a 43-character base64url string (32 raw bytes). A legacy
420
+ * fallback still accepts a bare `base64(utf8(txId))` FULFILL (no HTTP envelope)
421
+ * so non-proxy providers do not regress. See {@link extractArweaveTxId}.
414
422
  *
415
423
  * See `packages/sdk/src/arweave/arweave-dvm-handler.ts` for the server side and
416
424
  * `packages/client/tests/e2e/docker-arweave-dvm-e2e.test.ts` for the reference
@@ -1059,6 +1067,9 @@ declare class ToonClient {
1059
1067
  destination?: string;
1060
1068
  claim?: SignedBalanceProof;
1061
1069
  ilpAmount?: bigint;
1070
+ /** HTTP request-target the payment-proxy replays (default '/write', the
1071
+ * relay; use '/store' for the Arweave store/DVM backend). */
1072
+ proxyPath?: string;
1062
1073
  }): Promise<PublishEventResult>;
1063
1074
  /**
1064
1075
  * Payment-aware HTTP fetch over TOON (issue #50). A `fetch()`-like method that
@@ -2359,14 +2370,74 @@ declare function withRetry<T>(operation: () => Promise<T>, options: RetryOptions
2359
2370
  */
2360
2371
 
2361
2372
  /**
2362
- * Wrap a signed Nostr event in the `POST /write` HTTP envelope the deployed
2363
- * payment-proxy reverse-proxies to the relay store.
2373
+ * Wrap a signed Nostr event in the HTTP envelope the deployed payment-proxy
2374
+ * reverse-proxies to its backend. Defaults to the relay's `POST /write`; pass
2375
+ * `requestTarget: '/store'` for the Arweave store/DVM backend.
2364
2376
  *
2365
2377
  * @param event - A finalized (signed) Nostr event — passed through to the store
2366
2378
  * as the JSON `event` field verbatim (the store re-verifies the signature).
2379
+ * @param requestTarget - HTTP request-target the proxy replays (default `/write`).
2367
2380
  * @returns The envelope bytes to use as the ILP PREPARE `data`.
2368
2381
  */
2369
- declare function buildStoreWriteEnvelope(event: NostrEvent): Uint8Array;
2382
+ declare function buildStoreWriteEnvelope(event: NostrEvent, requestTarget?: string): Uint8Array;
2383
+
2384
+ /**
2385
+ * Shared parser for the HTTP-over-ILP response carried in an ILP **FULFILL**
2386
+ * packet's `data` field.
2387
+ *
2388
+ * The deployed connector is a payment-proxy (HTTP-in-ILP): a paid write/upload
2389
+ * is reverse-proxied to the relay/DVM origin and the origin's reply is returned
2390
+ * **verbatim as a full HTTP/1.1 response message** inside the FULFILL `data`:
2391
+ *
2392
+ * HTTP/1.1 200 OK\r\n
2393
+ * content-length: 189\r\n
2394
+ * \r\n
2395
+ * {"accept":true,"txId":"4QcRav...","data":"<base64-txid>",...}
2396
+ *
2397
+ * Callers (`ToonClient.publishEvent`, `requestBlobStorage`) previously treated
2398
+ * this `data` as opaque success bytes — `publishEvent` reported success on ANY
2399
+ * FULFILL even when the embedded HTTP status was `404 Not Found`, and
2400
+ * `requestBlobStorage` base64-decoded the WHOLE response as if it were the bare
2401
+ * Arweave tx id. This module makes the HTTP envelope first-class so both paths
2402
+ * can read the real status and body.
2403
+ *
2404
+ * The full Web-`Response` reconstruction used by the h402 fetch path lives in
2405
+ * `adapters/Http402Client.ts` (`parseHttpResponse`); this is a smaller,
2406
+ * dependency-free helper that returns the status code + raw body string, which
2407
+ * is all the publish/upload paths need.
2408
+ *
2409
+ * DEFENSIVE: not every FULFILL is HTTP-enveloped (e.g. Mill-swap raw-TOON
2410
+ * FULFILLs go through `sendSwapPacket`, not these paths). If the decoded data
2411
+ * does not begin with an `HTTP/<v>` status line, `isHttp` is `false` and the
2412
+ * caller should fall back to its prior (non-HTTP) interpretation rather than
2413
+ * fail. This keeps non-HTTP FULFILLs from regressing.
2414
+ */
2415
+ /** Result of parsing FULFILL `data` as an HTTP/1.1 response. */
2416
+ interface ParsedFulfillHttp {
2417
+ /** Whether the data looked like an HTTP/1.1 response (status line present). */
2418
+ isHttp: boolean;
2419
+ /** HTTP status code (e.g. 200, 404). Only meaningful when `isHttp` is true. */
2420
+ status: number;
2421
+ /** Reason phrase from the status line (may be empty). */
2422
+ statusText: string;
2423
+ /** Decoded response body as a UTF-8 string (empty when none). */
2424
+ body: string;
2425
+ }
2426
+ /**
2427
+ * Parse FULFILL `data` bytes as an HTTP/1.1 response.
2428
+ *
2429
+ * Returns `{ isHttp: false, ... }` (without throwing) when the payload does not
2430
+ * start with an `HTTP/<v>` status line, so callers can fall back to their
2431
+ * legacy non-HTTP interpretation. When it IS an HTTP response, the status code
2432
+ * and body are extracted; a present-but-malformed status line yields
2433
+ * `isHttp: false` as well (treated as non-HTTP rather than throwing).
2434
+ */
2435
+ declare function parseFulfillHttpBytes(bytes: Uint8Array): ParsedFulfillHttp;
2436
+ /**
2437
+ * Convenience wrapper: decode a base64 FULFILL `data` string (the shape carried
2438
+ * on `IlpSendResult.data`) and parse it as an HTTP/1.1 response.
2439
+ */
2440
+ declare function parseFulfillHttp(base64Data: string): ParsedFulfillHttp;
2370
2441
 
2371
2442
  /**
2372
2443
  * Settlement info produced by buildSettlementInfo().
@@ -3270,4 +3341,4 @@ declare function loadKeystore(path: string, password: string): string;
3270
3341
  */
3271
3342
  declare function writeKeystoreFile(path: string, keystore: EncryptedKeystore): void;
3272
3343
 
3273
- export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type ChainMetadata, type ChainSigner, ChannelManager, type ClaimMessage, type ClaimResolver, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, EvmSigner, 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 IlpTransportChoice, type InteractionResultContent, KeyManager, type KeyManagerConfig, type MinaClaimMessage, type MinaDepositReader, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedX402Challenge, type PasskeyInfo, type PetDvmProvider, type PetInteractionEventData, type PetInteractionRequestParams, type PetInteractionResultData, type PetListing, type PetListingFilterOptions, type PetListingParams, type PetPurchaseRequestParams, type ProofStatus, type PublishEventResult, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetryOptions, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type StatValues, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, type UnsignedNostrEvent, ValidationError, type VaultData, applyDefaults, applyNetworkPresets, buildBackupEvent, buildBackupFilter, buildPetInteractionRequest, buildPetListingEvent, buildPetPurchaseRequest, buildSettlementInfo, buildStoreWriteEnvelope, decryptMnemonic, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, filterPetDvmProviders, filterPetListings, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, httpEndpointToBtpUrl, importKeystore, isPrfSupported, loadKeystore, parseBackupPayload, parseHttpResponse, parsePetInteractionEvent, parsePetInteractionResult, parsePetListing, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readMinaDepositTotal, requestBlobStorage, selectIlpTransport, serializeHttpRequest, validateConfig, validateMnemonic, withRetry, writeKeystoreFile };
3344
+ export { type BackupPayload, type BalanceProofParams, BtpRuntimeClient, type BtpRuntimeClientConfig, type ChainMetadata, type ChainSigner, ChannelManager, type ClaimMessage, type ClaimResolver, ConnectorError, type DiscoveredIlpPeer, type EVMClaimMessage, type EncryptedKeystore, EvmSigner, 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 IlpTransportChoice, type InteractionResultContent, KeyManager, type KeyManagerConfig, type MinaClaimMessage, type MinaDepositReader, MinaSigner, type MinaSignerOptions, NetworkError, OnChainChannelClient, type OnChainChannelClientConfig, type ParsedFulfillHttp, type ParsedX402Challenge, type PasskeyInfo, type PetDvmProvider, type PetInteractionEventData, type PetInteractionRequestParams, type PetInteractionResultData, type PetListing, type PetListingFilterOptions, type PetListingParams, type PetPurchaseRequestParams, type ProofStatus, type PublishEventResult, type RequestBlobStorageParams, type RequestBlobStorageResult, type RetryOptions, type SelectIlpTransportOptions, type SignedBalanceProof, type SolanaChannelClientOptions, type SolanaClaimMessage, SolanaSigner, type StatValues, type ToonChannelAccept, ToonClient, type ToonClientConfig, ToonClientError, type ToonIdentity, type ToonSigners, type ToonStartResult, type UnsignedNostrEvent, ValidationError, type VaultData, applyDefaults, applyNetworkPresets, buildBackupEvent, buildBackupFilter, buildPetInteractionRequest, buildPetListingEvent, buildPetPurchaseRequest, buildSettlementInfo, buildStoreWriteEnvelope, decryptMnemonic, deriveFromNsec, deriveFullIdentity, deriveNostrKeyFromMnemonic, encryptMnemonic, filterPetDvmProviders, filterPetListings, fundWallet, generateKeystore, generateMnemonic, generateRandomIdentity, getNetworkStatus, httpEndpointToBtpUrl, importKeystore, isPrfSupported, loadKeystore, parseBackupPayload, parseFulfillHttp, parseFulfillHttpBytes, parseHttpResponse, parsePetInteractionEvent, parsePetInteractionResult, parsePetListing, parseX402Body, parseX402Challenge, proxyIlpEndpoint, readDiscoveredIlpPeer, readMinaDepositTotal, requestBlobStorage, selectIlpTransport, serializeHttpRequest, validateConfig, validateMnemonic, withRetry, writeKeystoreFile };
package/dist/index.js CHANGED
@@ -510,14 +510,52 @@ function isBase64(str) {
510
510
  }
511
511
 
512
512
  // src/utils/store-envelope.ts
513
- var REQUEST_LINE = "POST /write HTTP/1.1";
513
+ var DEFAULT_REQUEST_TARGET = "/write";
514
514
  var HEADERS = ["Host: relay", "Content-Type: application/json"];
515
- function buildStoreWriteEnvelope(event) {
515
+ function buildStoreWriteEnvelope(event, requestTarget = DEFAULT_REQUEST_TARGET) {
516
516
  const body = JSON.stringify({ event });
517
- const head = [REQUEST_LINE, ...HEADERS].join("\r\n");
517
+ const head = [`POST ${requestTarget} HTTP/1.1`, ...HEADERS].join("\r\n");
518
518
  return encodeUtf8(head + "\r\n\r\n" + body);
519
519
  }
520
520
 
521
+ // src/utils/fulfill-http.ts
522
+ var CRLF = "\r\n";
523
+ function findHeaderEnd(bytes) {
524
+ for (let i = 0; i + 3 < bytes.length; i++) {
525
+ if (bytes[i] === 13 && bytes[i + 1] === 10 && bytes[i + 2] === 13 && bytes[i + 3] === 10) {
526
+ return i + 4;
527
+ }
528
+ }
529
+ return -1;
530
+ }
531
+ function parseFulfillHttpBytes(bytes) {
532
+ const notHttp = {
533
+ isHttp: false,
534
+ status: 0,
535
+ statusText: "",
536
+ body: ""
537
+ };
538
+ const headerEnd = findHeaderEnd(bytes);
539
+ const headBytes = headerEnd === -1 ? bytes : bytes.subarray(0, headerEnd - 2);
540
+ const bodyBytes = headerEnd === -1 ? new Uint8Array(0) : bytes.subarray(headerEnd);
541
+ const headText = decodeUtf8(headBytes);
542
+ const lines = headText.split(CRLF).filter((l) => l.length > 0);
543
+ const statusLine = lines.shift();
544
+ if (!statusLine) return notHttp;
545
+ if (!statusLine.trimStart().startsWith("HTTP/")) return notHttp;
546
+ const match = /^HTTP\/\d\.\d\s+(\d{3})(?:\s+(.*))?$/.exec(statusLine.trim());
547
+ if (!match) return notHttp;
548
+ return {
549
+ isHttp: true,
550
+ status: parseInt(match[1], 10),
551
+ statusText: match[2] ?? "",
552
+ body: decodeUtf8(bodyBytes)
553
+ };
554
+ }
555
+ function parseFulfillHttp(base64Data) {
556
+ return parseFulfillHttpBytes(fromBase64(base64Data));
557
+ }
558
+
521
559
  // src/modes/http.ts
522
560
  import { BootstrapService, createDiscoveryTracker } from "@toon-protocol/core";
523
561
 
@@ -2313,12 +2351,12 @@ var OnChainChannelClient = class {
2313
2351
  */
2314
2352
  parseChainId(chain) {
2315
2353
  const parts = chain.split(":");
2316
- if (parts.length < 3) {
2354
+ if (parts.length < 2) {
2317
2355
  throw new Error(
2318
- `Invalid chain format: "${chain}". Expected "evm:{network}:{chainId}".`
2356
+ `Invalid chain format: "${chain}". Expected "evm:{network}:{chainId}" or "evm:{chainId}".`
2319
2357
  );
2320
2358
  }
2321
- const chainIdStr = parts[2];
2359
+ const chainIdStr = parts.length >= 3 ? parts[2] : parts[1];
2322
2360
  if (!chainIdStr) {
2323
2361
  throw new Error(
2324
2362
  `Invalid chain format: "${chain}". Expected "evm:{network}:{chainId}".`
@@ -3544,7 +3582,9 @@ async function requestBlobStorage(client, secretKey, params) {
3544
3582
  const result = await client.publishEvent(event, {
3545
3583
  destination: params.destination,
3546
3584
  claim: params.claim,
3547
- ilpAmount: params.ilpAmount
3585
+ ilpAmount: params.ilpAmount,
3586
+ // The store/DVM backend serves POST /store (not the relay's /write).
3587
+ proxyPath: "/store"
3548
3588
  });
3549
3589
  if (!result.success) {
3550
3590
  return {
@@ -3557,15 +3597,17 @@ async function requestBlobStorage(client, secretKey, params) {
3557
3597
  return {
3558
3598
  success: false,
3559
3599
  eventId: event.id,
3560
- error: "FULFILL contained no data; expected base64-encoded Arweave tx ID"
3600
+ error: "FULFILL contained no data; expected an HTTP response with the Arweave tx ID"
3561
3601
  };
3562
3602
  }
3563
- const txId = decodeUtf8(fromBase64(result.data));
3564
- if (!ARWEAVE_TX_ID_REGEX.test(txId)) {
3603
+ let txId;
3604
+ try {
3605
+ txId = extractArweaveTxId(result.data);
3606
+ } catch (error) {
3565
3607
  return {
3566
3608
  success: false,
3567
3609
  eventId: event.id,
3568
- error: `Decoded FULFILL data is not a valid Arweave tx ID: "${txId}"`
3610
+ error: error instanceof Error ? error.message : String(error)
3569
3611
  };
3570
3612
  }
3571
3613
  return {
@@ -3574,6 +3616,49 @@ async function requestBlobStorage(client, secretKey, params) {
3574
3616
  eventId: event.id
3575
3617
  };
3576
3618
  }
3619
+ function extractArweaveTxId(base64Data) {
3620
+ const http2 = parseFulfillHttp(base64Data);
3621
+ if (!http2.isHttp) {
3622
+ const legacy = decodeUtf8(fromBase64(base64Data));
3623
+ if (!ARWEAVE_TX_ID_REGEX.test(legacy)) {
3624
+ throw new Error(
3625
+ `Decoded FULFILL data is not a valid Arweave tx ID: "${legacy}"`
3626
+ );
3627
+ }
3628
+ return legacy;
3629
+ }
3630
+ if (http2.status < 200 || http2.status >= 300) {
3631
+ const detail = http2.body ? ` - ${http2.body}` : "";
3632
+ throw new Error(
3633
+ `Blob upload failed: DVM returned HTTP ${http2.status} ${http2.statusText}`.trimEnd() + detail
3634
+ );
3635
+ }
3636
+ let parsed;
3637
+ try {
3638
+ parsed = JSON.parse(http2.body);
3639
+ } catch {
3640
+ throw new Error(
3641
+ `Blob upload response body was not valid JSON: "${http2.body}"`
3642
+ );
3643
+ }
3644
+ const body = parsed;
3645
+ if (body.accept === false) {
3646
+ const reason = typeof body.error === "string" ? `: ${body.error}` : "";
3647
+ throw new Error(`Blob upload rejected by DVM (accept:false)${reason}`);
3648
+ }
3649
+ if (typeof body.txId === "string" && ARWEAVE_TX_ID_REGEX.test(body.txId)) {
3650
+ return body.txId;
3651
+ }
3652
+ if (typeof body.data === "string" && body.data.length > 0) {
3653
+ const decoded = decodeUtf8(fromBase64(body.data));
3654
+ if (ARWEAVE_TX_ID_REGEX.test(decoded)) {
3655
+ return decoded;
3656
+ }
3657
+ }
3658
+ throw new Error(
3659
+ `Blob upload response did not contain a valid Arweave tx ID: "${http2.body}"`
3660
+ );
3661
+ }
3577
3662
 
3578
3663
  // src/adapters/Http402Client.ts
3579
3664
  var Http402Client = class {
@@ -3748,7 +3833,7 @@ function parseX402Body(body) {
3748
3833
  }
3749
3834
  return version !== void 0 ? { x402Version: version } : {};
3750
3835
  }
3751
- var CRLF = "\r\n";
3836
+ var CRLF2 = "\r\n";
3752
3837
  function concatHeadAndBody(head, body) {
3753
3838
  const headBytes = encodeUtf8(head);
3754
3839
  const out = new Uint8Array(headBytes.length + body.length);
@@ -3778,10 +3863,10 @@ function serializeHttpRequest(req) {
3778
3863
  `${req.method.toUpperCase()} ${target} HTTP/1.1`,
3779
3864
  ...headers.values()
3780
3865
  ];
3781
- const head = lines.join(CRLF) + CRLF + CRLF;
3866
+ const head = lines.join(CRLF2) + CRLF2 + CRLF2;
3782
3867
  return concatHeadAndBody(head, bodyBytes);
3783
3868
  }
3784
- function findHeaderEnd(bytes) {
3869
+ function findHeaderEnd2(bytes) {
3785
3870
  for (let i = 0; i + 3 < bytes.length; i++) {
3786
3871
  if (bytes[i] === 13 && bytes[i + 1] === 10 && bytes[i + 2] === 13 && bytes[i + 3] === 10) {
3787
3872
  return i + 4;
@@ -3790,11 +3875,11 @@ function findHeaderEnd(bytes) {
3790
3875
  return -1;
3791
3876
  }
3792
3877
  function parseHttpResponse(bytes) {
3793
- const headerEnd = findHeaderEnd(bytes);
3878
+ const headerEnd = findHeaderEnd2(bytes);
3794
3879
  const headBytes = headerEnd === -1 ? bytes : bytes.subarray(0, headerEnd - 2);
3795
3880
  const body = headerEnd === -1 ? new Uint8Array(0) : bytes.subarray(headerEnd);
3796
3881
  const headText = decodeUtf8(headBytes);
3797
- const lines = headText.split(CRLF).filter((l) => l.length > 0);
3882
+ const lines = headText.split(CRLF2).filter((l) => l.length > 0);
3798
3883
  const statusLine = lines.shift();
3799
3884
  if (!statusLine) {
3800
3885
  throw new ConnectorError(
@@ -4003,7 +4088,7 @@ var ToonClient = class {
4003
4088
  if (result.negotiatedChain && result.settlementAddress) {
4004
4089
  const chainType = result.negotiatedChain.split(":")[0] ?? "evm";
4005
4090
  const parts = result.negotiatedChain.split(":");
4006
- const chainId = parts.length >= 3 ? parseInt(parts[2] ?? "0", 10) : 0;
4091
+ const chainId = parts.length >= 3 ? parseInt(parts[2] ?? "0", 10) : parts.length >= 2 ? parseInt(parts[1] ?? "0", 10) : 0;
4007
4092
  const r = result;
4008
4093
  this.peerNegotiations.set(result.registeredPeerId, {
4009
4094
  chain: result.negotiatedChain,
@@ -4021,7 +4106,7 @@ var ToonClient = class {
4021
4106
  if (matchedChain) {
4022
4107
  const peerAddr = peerInfo.settlementAddresses?.[matchedChain];
4023
4108
  const parts = matchedChain.split(":");
4024
- const chainId = parts.length >= 3 ? parseInt(parts[2] ?? "0", 10) : 0;
4109
+ const chainId = parts.length >= 3 ? parseInt(parts[2] ?? "0", 10) : parts.length >= 2 ? parseInt(parts[1] ?? "0", 10) : 0;
4025
4110
  if (peerAddr) {
4026
4111
  this.peerNegotiations.set(result.registeredPeerId, {
4027
4112
  chain: matchedChain,
@@ -4106,7 +4191,7 @@ var ToonClient = class {
4106
4191
  const toonData = this.config.toonEncoder(event);
4107
4192
  const basePricePerByte = 10n;
4108
4193
  const amount = options?.ilpAmount !== void 0 ? String(options.ilpAmount) : String(BigInt(toonData.length) * basePricePerByte);
4109
- const writeData = buildStoreWriteEnvelope(event);
4194
+ const writeData = buildStoreWriteEnvelope(event, options?.proxyPath);
4110
4195
  const destination = options?.destination ?? this.config.destinationAddress;
4111
4196
  const transport = this.getClaimTransport();
4112
4197
  let claimMessage;
@@ -4151,6 +4236,16 @@ var ToonClient = class {
4151
4236
  error: `Event rejected: ${response.code} - ${response.message}`
4152
4237
  };
4153
4238
  }
4239
+ if (response.data) {
4240
+ const httpResult = parseFulfillHttp(response.data);
4241
+ if (httpResult.isHttp && (httpResult.status < 200 || httpResult.status >= 300)) {
4242
+ const detail = httpResult.body ? ` - ${httpResult.body}` : "";
4243
+ return {
4244
+ success: false,
4245
+ error: `Write failed: relay returned HTTP ${httpResult.status} ${httpResult.statusText}`.trimEnd() + detail
4246
+ };
4247
+ }
4248
+ }
4154
4249
  return {
4155
4250
  success: true,
4156
4251
  eventId: event.id,
@@ -4468,7 +4563,7 @@ var ToonClient = class {
4468
4563
  getChainContext(negotiatedChain) {
4469
4564
  if (!negotiatedChain) return void 0;
4470
4565
  const parts = negotiatedChain.split(":");
4471
- const chainIdPart = parts.length >= 3 ? parts[2] : void 0;
4566
+ const chainIdPart = parts.length >= 3 ? parts[2] : parts.length >= 2 ? parts[1] : void 0;
4472
4567
  const numericChainId = chainIdPart !== void 0 ? parseInt(chainIdPart, 10) : NaN;
4473
4568
  if (isNaN(numericChainId)) return void 0;
4474
4569
  const tokenNetworkAddress = this.config.tokenNetworks?.[negotiatedChain];
@@ -6483,6 +6578,8 @@ export {
6483
6578
  isTrustDowngrade,
6484
6579
  loadKeystore,
6485
6580
  parseBackupPayload,
6581
+ parseFulfillHttp,
6582
+ parseFulfillHttpBytes,
6486
6583
  parseHttpResponse,
6487
6584
  parsePetInteractionEvent,
6488
6585
  parsePetInteractionResult,