@toon-protocol/core 3.3.0 → 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(
@@ -538,6 +557,8 @@ function parseIlpPeerInfo(event) {
538
557
  }
539
558
  prefixPricing = { basePrice };
540
559
  }
560
+ const { notice: rawNotice } = parsed;
561
+ const notice = parseNotice(rawNotice);
541
562
  const { swapPairs: rawSwapPairs } = parsed;
542
563
  let swapPairs;
543
564
  if (rawSwapPairs !== void 0) {
@@ -590,7 +611,8 @@ function parseIlpPeerInfo(event) {
590
611
  ilpAddresses,
591
612
  feePerByte,
592
613
  ...prefixPricing !== void 0 && { prefixPricing },
593
- ...swapPairs !== void 0 && { swapPairs }
614
+ ...swapPairs !== void 0 && { swapPairs },
615
+ ...notice !== void 0 && { notice }
594
616
  };
595
617
  }
596
618
 
@@ -3068,7 +3090,9 @@ var BootstrapService = class {
3068
3090
  ownIlpAddress;
3069
3091
  toonEncoder;
3070
3092
  toonDecoder;
3093
+ /** @deprecated see {@link BootstrapServiceConfig.basePricePerByte}. */
3071
3094
  basePricePerByte;
3095
+ connectorEdgeLookup;
3072
3096
  // Event emitter
3073
3097
  listeners = [];
3074
3098
  phase = "discovering";
@@ -3095,7 +3119,8 @@ var BootstrapService = class {
3095
3119
  this.ownIlpAddress = config.ownIlpAddress;
3096
3120
  this.toonEncoder = config.toonEncoder;
3097
3121
  this.toonDecoder = config.toonDecoder;
3098
- this.basePricePerByte = config.basePricePerByte ?? 10n;
3122
+ this.basePricePerByte = config.basePricePerByte;
3123
+ this.connectorEdgeLookup = config.connectorEdgeLookup;
3099
3124
  }
3100
3125
  /**
3101
3126
  * Set the ILP client for sending packets via the connector.
@@ -3396,32 +3421,86 @@ var BootstrapService = class {
3396
3421
  }
3397
3422
  return result;
3398
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
+ }
3399
3446
  /**
3400
3447
  * Announce own kind:10032 as paid ILP PREPARE (Phase 2).
3401
3448
  */
3402
3449
  async announceViaIlp(result) {
3403
- if (!this.ilpClient || !this.toonEncoder) {
3450
+ if (!this.ilpClient || !this.toonEncoder || !this.connectorEdgeLookup) {
3404
3451
  return;
3405
3452
  }
3406
3453
  const ilpInfoEvent = buildIlpPeerInfoEvent(this.ownIlpInfo, this.secretKey);
3407
3454
  const toonBytes = this.toonEncoder(ilpInfoEvent);
3408
- const base64Toon = Buffer.from(toonBytes).toString("base64");
3409
- 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
+ );
3410
3473
  let ilpResult;
3411
3474
  if (this.claimSigner && result.channelId && this.ilpClient.sendIlpPacketWithClaim) {
3412
3475
  const claim = await this.claimSigner(result.channelId, BigInt(amount));
3413
3476
  ilpResult = await this.ilpClient.sendIlpPacketWithClaim(
3414
- { destination: result.peerInfo.ilpAddress, amount, data: base64Toon },
3477
+ {
3478
+ destination: result.peerInfo.ilpAddress,
3479
+ amount,
3480
+ data,
3481
+ executionCondition
3482
+ },
3415
3483
  claim
3416
3484
  );
3417
3485
  } else {
3418
3486
  ilpResult = await this.ilpClient.sendIlpPacket({
3419
3487
  destination: result.peerInfo.ilpAddress,
3420
3488
  amount,
3421
- data: base64Toon
3489
+ data,
3490
+ executionCondition
3422
3491
  });
3423
3492
  }
3424
- 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") {
3425
3504
  console.log(
3426
3505
  `[Bootstrap] Announced to ${result.registeredPeerId} via ILP (eventId: ${ilpInfoEvent.id})`
3427
3506
  );
@@ -3431,17 +3510,18 @@ var BootstrapService = class {
3431
3510
  eventId: ilpInfoEvent.id,
3432
3511
  amount
3433
3512
  });
3434
- } else {
3435
- const reason = `${ilpResult.code} ${ilpResult.message}`;
3436
- this.emit({
3437
- type: "bootstrap:announce-failed",
3438
- peerId: result.registeredPeerId,
3439
- reason
3440
- });
3441
- console.warn(
3442
- `[Bootstrap] Announce rejected by ${result.registeredPeerId}: ${reason}`
3443
- );
3513
+ return;
3444
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
+ );
3445
3525
  }
3446
3526
  /**
3447
3527
  * Query a peer's relay for their kind:10032 ILP Peer Info event.
@@ -3996,6 +4076,9 @@ function createHttpIlpClient(baseUrl) {
3996
4076
  ...params.timeout !== void 0 && { timeout: params.timeout },
3997
4077
  ...params.expiresAt !== void 0 && {
3998
4078
  expiresAt: params.expiresAt
4079
+ },
4080
+ ...params.executionCondition !== void 0 && {
4081
+ executionCondition: params.executionCondition
3999
4082
  }
4000
4083
  })
4001
4084
  });
@@ -4041,7 +4124,12 @@ function createDirectIlpClient(connector, _config) {
4041
4124
  destination: params.destination,
4042
4125
  amount,
4043
4126
  data,
4044
- expiresAt
4127
+ expiresAt,
4128
+ ...params.executionCondition !== void 0 && {
4129
+ executionCondition: Uint8Array.from(
4130
+ Buffer.from(params.executionCondition, "base64")
4131
+ )
4132
+ }
4045
4133
  });
4046
4134
  if (result.type === "fulfill" || result.type === 13) {
4047
4135
  return {
@@ -4211,6 +4299,9 @@ function createHttpIlpClient2(connectorUrl) {
4211
4299
  data: params.data,
4212
4300
  ...params.expiresAt !== void 0 && {
4213
4301
  expiresAt: params.expiresAt
4302
+ },
4303
+ ...params.executionCondition !== void 0 && {
4304
+ executionCondition: params.executionCondition
4214
4305
  }
4215
4306
  }),
4216
4307
  signal: params.timeout ? AbortSignal.timeout(params.timeout) : void 0
@@ -4464,7 +4555,8 @@ function createToonNode(config) {
4464
4555
  ownIlpAddress: config.ilpInfo.ilpAddress,
4465
4556
  toonEncoder: config.toonEncoder,
4466
4557
  toonDecoder: config.toonDecoder,
4467
- basePricePerByte: config.basePricePerByte ?? 10n
4558
+ basePricePerByte: config.basePricePerByte,
4559
+ connectorEdgeLookup: config.connectorEdgeLookup
4468
4560
  },
4469
4561
  config.secretKey,
4470
4562
  config.ilpInfo
@@ -4648,8 +4740,49 @@ var SOLANA_CHAIN_PRESETS = {
4648
4740
  name: "solana-devnet",
4649
4741
  chainType: "solana",
4650
4742
  rpcUrl: "http://localhost:19899",
4651
- 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: "",
4652
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"
4653
4786
  }
4654
4787
  };
4655
4788
  var MINA_CHAIN_PRESETS = {