@toon-protocol/core 3.2.1 → 3.4.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.js CHANGED
@@ -9,6 +9,10 @@ import {
9
9
  encodeEventToToonString,
10
10
  shallowParseToon
11
11
  } from "./chunk-5WT7ISKC.js";
12
+ import {
13
+ readExchangeOutcome,
14
+ sealExchange
15
+ } from "./chunk-K72H36Y4.js";
12
16
 
13
17
  // src/constants.ts
14
18
  var ILP_PEER_INFO_KIND = 10032;
@@ -395,6 +399,21 @@ function assertSwapPairForParse(pair, index) {
395
399
  function isObject2(value) {
396
400
  return typeof value === "object" && value !== null && !Array.isArray(value);
397
401
  }
402
+ function parseNotice(raw) {
403
+ if (!isObject2(raw)) {
404
+ return void 0;
405
+ }
406
+ const { id, severity, summary, url } = raw;
407
+ if (typeof id !== "string" || id.length === 0 || typeof summary !== "string" || summary.length === 0 || typeof url !== "string" || url.length === 0) {
408
+ return void 0;
409
+ }
410
+ return {
411
+ id,
412
+ severity: severity === "action-required" ? "action-required" : "info",
413
+ summary,
414
+ url
415
+ };
416
+ }
398
417
  function parseIlpPeerInfo(event) {
399
418
  if (event.kind !== ILP_PEER_INFO_KIND) {
400
419
  throw new InvalidEventError(
@@ -436,7 +455,9 @@ function parseIlpPeerInfo(event) {
436
455
  throw new InvalidEventError("Invalid field: httpEndpoint must be a string");
437
456
  }
438
457
  if (supportsUpgrade !== void 0 && typeof supportsUpgrade !== "boolean") {
439
- throw new InvalidEventError("Invalid field: supportsUpgrade must be a boolean");
458
+ throw new InvalidEventError(
459
+ "Invalid field: supportsUpgrade must be a boolean"
460
+ );
440
461
  }
441
462
  if (typeof assetCode !== "string" || assetCode.length === 0) {
442
463
  throw new InvalidEventError("Missing or invalid required field: assetCode");
@@ -536,6 +557,8 @@ function parseIlpPeerInfo(event) {
536
557
  }
537
558
  prefixPricing = { basePrice };
538
559
  }
560
+ const { notice: rawNotice } = parsed;
561
+ const notice = parseNotice(rawNotice);
539
562
  const { swapPairs: rawSwapPairs } = parsed;
540
563
  let swapPairs;
541
564
  if (rawSwapPairs !== void 0) {
@@ -588,7 +611,8 @@ function parseIlpPeerInfo(event) {
588
611
  ilpAddresses,
589
612
  feePerByte,
590
613
  ...prefixPricing !== void 0 && { prefixPricing },
591
- ...swapPairs !== void 0 && { swapPairs }
614
+ ...swapPairs !== void 0 && { swapPairs },
615
+ ...notice !== void 0 && { notice }
592
616
  };
593
617
  }
594
618
 
@@ -2238,8 +2262,14 @@ var genesis_peers_default = [
2238
2262
  {
2239
2263
  pubkey: "30fdd01d55c3efeb4c19c2cbeda8247cbc40ae9b15c026e9a301a263001fa7a9",
2240
2264
  relayUrl: "wss://relay-ws.devnet.toonprotocol.dev",
2241
- ilpAddress: "g.toon",
2242
- btpEndpoint: "wss://proxy.devnet.toonprotocol.dev/ilp/btp"
2265
+ ilpAddress: "g.toon.relay",
2266
+ btpEndpoint: "wss://proxy.relay.devnet.toonprotocol.dev/ilp/btp"
2267
+ },
2268
+ {
2269
+ pubkey: "499cdd71c7c3eab8d9b35f88ec9cde29018461e4bef86389004abcd7cfa1108a",
2270
+ relayUrl: "wss://relay-ws.devnet.toonprotocol.dev",
2271
+ ilpAddress: "g.toon.ario",
2272
+ btpEndpoint: "wss://proxy.ario.devnet.toonprotocol.dev/ilp/btp"
2243
2273
  }
2244
2274
  ];
2245
2275
 
@@ -3060,7 +3090,9 @@ var BootstrapService = class {
3060
3090
  ownIlpAddress;
3061
3091
  toonEncoder;
3062
3092
  toonDecoder;
3093
+ /** @deprecated see {@link BootstrapServiceConfig.basePricePerByte}. */
3063
3094
  basePricePerByte;
3095
+ connectorEdgeLookup;
3064
3096
  // Event emitter
3065
3097
  listeners = [];
3066
3098
  phase = "discovering";
@@ -3087,7 +3119,8 @@ var BootstrapService = class {
3087
3119
  this.ownIlpAddress = config.ownIlpAddress;
3088
3120
  this.toonEncoder = config.toonEncoder;
3089
3121
  this.toonDecoder = config.toonDecoder;
3090
- this.basePricePerByte = config.basePricePerByte ?? 10n;
3122
+ this.basePricePerByte = config.basePricePerByte;
3123
+ this.connectorEdgeLookup = config.connectorEdgeLookup;
3091
3124
  }
3092
3125
  /**
3093
3126
  * Set the ILP client for sending packets via the connector.
@@ -3388,32 +3421,86 @@ var BootstrapService = class {
3388
3421
  }
3389
3422
  return result;
3390
3423
  }
3424
+ /**
3425
+ * What the announce PREPARE should carry as its amount.
3426
+ *
3427
+ * ASKED for (ADR 0020, flat per handler), never computed as bytes × rate.
3428
+ * `basePricePerByte`, when a caller still sets it, is a deprecated explicit
3429
+ * override rather than a per-byte rate — see its doc comment on
3430
+ * {@link BootstrapServiceConfig}.
3431
+ *
3432
+ * @throws {BootstrapError} when nothing terminated here prices `destination`.
3433
+ */
3434
+ async resolveAnnounceAmount(lookup, destination) {
3435
+ if (this.basePricePerByte !== void 0) {
3436
+ return String(this.basePricePerByte);
3437
+ }
3438
+ const routePrice = await lookup.getRoutePrice(destination);
3439
+ if (routePrice === null) {
3440
+ throw new BootstrapError(
3441
+ `No locally-terminated route price for ${destination}`
3442
+ );
3443
+ }
3444
+ return String(routePrice.price);
3445
+ }
3391
3446
  /**
3392
3447
  * Announce own kind:10032 as paid ILP PREPARE (Phase 2).
3393
3448
  */
3394
3449
  async announceViaIlp(result) {
3395
- if (!this.ilpClient || !this.toonEncoder) {
3450
+ if (!this.ilpClient || !this.toonEncoder || !this.connectorEdgeLookup) {
3396
3451
  return;
3397
3452
  }
3398
3453
  const ilpInfoEvent = buildIlpPeerInfoEvent(this.ownIlpInfo, this.secretKey);
3399
3454
  const toonBytes = this.toonEncoder(ilpInfoEvent);
3400
- const base64Toon = Buffer.from(toonBytes).toString("base64");
3401
- const amount = String(BigInt(toonBytes.length) * this.basePricePerByte);
3455
+ const identity = await this.connectorEdgeLookup.getIdentity(
3456
+ result.peerInfo.ilpAddress
3457
+ );
3458
+ const amount = await this.resolveAnnounceAmount(
3459
+ this.connectorEdgeLookup,
3460
+ result.peerInfo.ilpAddress
3461
+ );
3462
+ const request = {
3463
+ method: "POST",
3464
+ target: "",
3465
+ headers: [["content-type", "application/json"]],
3466
+ body: toonBytes
3467
+ };
3468
+ const exchange = sealExchange(request, identity.publicKey);
3469
+ const data = Buffer.from(exchange.data).toString("base64");
3470
+ const executionCondition = Buffer.from(exchange.condition).toString(
3471
+ "base64"
3472
+ );
3402
3473
  let ilpResult;
3403
3474
  if (this.claimSigner && result.channelId && this.ilpClient.sendIlpPacketWithClaim) {
3404
3475
  const claim = await this.claimSigner(result.channelId, BigInt(amount));
3405
3476
  ilpResult = await this.ilpClient.sendIlpPacketWithClaim(
3406
- { destination: result.peerInfo.ilpAddress, amount, data: base64Toon },
3477
+ {
3478
+ destination: result.peerInfo.ilpAddress,
3479
+ amount,
3480
+ data,
3481
+ executionCondition
3482
+ },
3407
3483
  claim
3408
3484
  );
3409
3485
  } else {
3410
3486
  ilpResult = await this.ilpClient.sendIlpPacket({
3411
3487
  destination: result.peerInfo.ilpAddress,
3412
3488
  amount,
3413
- data: base64Toon
3489
+ data,
3490
+ executionCondition
3414
3491
  });
3415
3492
  }
3416
- if (ilpResult.accepted) {
3493
+ const responseData = ilpResult.data !== void 0 ? Uint8Array.from(Buffer.from(ilpResult.data, "base64")) : void 0;
3494
+ const outcome = readExchangeOutcome(
3495
+ {
3496
+ accepted: ilpResult.accepted,
3497
+ code: ilpResult.code,
3498
+ message: ilpResult.message
3499
+ },
3500
+ responseData,
3501
+ exchange.sharedSecret
3502
+ );
3503
+ if (outcome.kind === "answered") {
3417
3504
  console.log(
3418
3505
  `[Bootstrap] Announced to ${result.registeredPeerId} via ILP (eventId: ${ilpInfoEvent.id})`
3419
3506
  );
@@ -3423,17 +3510,18 @@ var BootstrapService = class {
3423
3510
  eventId: ilpInfoEvent.id,
3424
3511
  amount
3425
3512
  });
3426
- } else {
3427
- const reason = `${ilpResult.code} ${ilpResult.message}`;
3428
- this.emit({
3429
- type: "bootstrap:announce-failed",
3430
- peerId: result.registeredPeerId,
3431
- reason
3432
- });
3433
- console.warn(
3434
- `[Bootstrap] Announce rejected by ${result.registeredPeerId}: ${reason}`
3435
- );
3513
+ return;
3436
3514
  }
3515
+ const refusedBy = outcome.kind === "destination-refused" ? "destination" : "path";
3516
+ const reason = `${outcome.code} ${outcome.message} (${refusedBy} refused)`;
3517
+ this.emit({
3518
+ type: "bootstrap:announce-failed",
3519
+ peerId: result.registeredPeerId,
3520
+ reason
3521
+ });
3522
+ console.warn(
3523
+ `[Bootstrap] Announce rejected by ${result.registeredPeerId}: ${reason}`
3524
+ );
3437
3525
  }
3438
3526
  /**
3439
3527
  * Query a peer's relay for their kind:10032 ILP Peer Info event.
@@ -3988,6 +4076,9 @@ function createHttpIlpClient(baseUrl) {
3988
4076
  ...params.timeout !== void 0 && { timeout: params.timeout },
3989
4077
  ...params.expiresAt !== void 0 && {
3990
4078
  expiresAt: params.expiresAt
4079
+ },
4080
+ ...params.executionCondition !== void 0 && {
4081
+ executionCondition: params.executionCondition
3991
4082
  }
3992
4083
  })
3993
4084
  });
@@ -4033,7 +4124,12 @@ function createDirectIlpClient(connector, _config) {
4033
4124
  destination: params.destination,
4034
4125
  amount,
4035
4126
  data,
4036
- expiresAt
4127
+ expiresAt,
4128
+ ...params.executionCondition !== void 0 && {
4129
+ executionCondition: Uint8Array.from(
4130
+ Buffer.from(params.executionCondition, "base64")
4131
+ )
4132
+ }
4037
4133
  });
4038
4134
  if (result.type === "fulfill" || result.type === 13) {
4039
4135
  return {
@@ -4203,6 +4299,9 @@ function createHttpIlpClient2(connectorUrl) {
4203
4299
  data: params.data,
4204
4300
  ...params.expiresAt !== void 0 && {
4205
4301
  expiresAt: params.expiresAt
4302
+ },
4303
+ ...params.executionCondition !== void 0 && {
4304
+ executionCondition: params.executionCondition
4206
4305
  }
4207
4306
  }),
4208
4307
  signal: params.timeout ? AbortSignal.timeout(params.timeout) : void 0
@@ -4456,7 +4555,8 @@ function createToonNode(config) {
4456
4555
  ownIlpAddress: config.ilpInfo.ilpAddress,
4457
4556
  toonEncoder: config.toonEncoder,
4458
4557
  toonDecoder: config.toonDecoder,
4459
- basePricePerByte: config.basePricePerByte ?? 10n
4558
+ basePricePerByte: config.basePricePerByte,
4559
+ connectorEdgeLookup: config.connectorEdgeLookup
4460
4560
  },
4461
4561
  config.secretKey,
4462
4562
  config.ilpInfo
@@ -4640,8 +4740,49 @@ var SOLANA_CHAIN_PRESETS = {
4640
4740
  name: "solana-devnet",
4641
4741
  chainType: "solana",
4642
4742
  rpcUrl: "http://localhost:19899",
4643
- programId: "EdJxYPDxGvaJuu57DSUptf4soLv8enpdyQJJhHDLiydG",
4743
+ // Deliberately empty: this preset targets a local `solana-test-validator`
4744
+ // (see rpcUrl), not public devnet -- the public-devnet program id
4745
+ // (2aEVJ8ko…, per connector's packages/solana-program/deployments/
4746
+ // devnet-public.md) does not exist there. A local validator's program id
4747
+ // is a fresh keypair generated per `cargo build-sbf` deploy, so no fixed
4748
+ // default is meaningful; callers must supply the id of whatever they
4749
+ // deployed locally via the `SOLANA_PROGRAM_ID` env override below.
4750
+ programId: "",
4644
4751
  cluster: "devnet"
4752
+ },
4753
+ // Solana mainnet-beta (public). NOT wired into network-profile.ts's
4754
+ // public tiers, which already resolve their own mainnet Solana endpoint
4755
+ // (SOLANA_TIER.mainnet) independently of this low-level,
4756
+ // e2e-stack-facing preset table.
4757
+ "solana-mainnet": {
4758
+ name: "solana-mainnet",
4759
+ chainType: "solana",
4760
+ rpcUrl: "https://api.mainnet-beta.solana.com",
4761
+ // VERIFIED 2026-08-14, per toon#198: owner
4762
+ // BPFLoaderUpgradeab1e11111111111111111111111 (executable, upgradeable),
4763
+ // ProgramData 29MT16eh1GCdL4JWrJHjyTWu5ZMn217h25ojCvFpx2wc, 136,770
4764
+ // bytes, deployed slot 439316400. Bytecode pulled back with
4765
+ // `solana program dump` hashes identical to a local build from
4766
+ // connector@main with the pinned `cargo build-sbf --tools-version
4767
+ // v1.52`, so the on-chain program is provably the audited source. Full
4768
+ // deployment record: toon-protocol/connector#971.
4769
+ programId: "8e7BhzydH1EqL486tw6Lp99BXviH3i5JN8qNpMSNmHj3",
4770
+ cluster: "mainnet-beta",
4771
+ // Circle's native USDC mint on Solana mainnet-beta, 6 decimals.
4772
+ // VERIFIED 2026-08-13 by Drew Pierson, per toon#166's "Needs a human
4773
+ // before merge" criterion, on two independent sources:
4774
+ // - Circle's published list gives this address for Solana mainnet:
4775
+ // https://developers.circle.com/stablecoins/usdc-contract-addresses
4776
+ // That page names bridged representations separately where they
4777
+ // exist (X Layer's USDC.e); none is listed for Solana.
4778
+ // - mainnet-beta at slot 439080550: initialized mint, decimals 6,
4779
+ // supply ~7.73e9, live mint and freeze authorities, owned by the
4780
+ // SPL Token program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA),
4781
+ // not Token-2022.
4782
+ // Native, not bridged. Two things not to confuse it with: this is the
4783
+ // token's MINT ACCOUNT, never a transfer destination; and Circle runs
4784
+ // separate CCTP/Gateway pre-mint addresses that are not this one.
4785
+ tokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
4645
4786
  }
4646
4787
  };
4647
4788
  var MINA_CHAIN_PRESETS = {