@toon-protocol/core 3.3.0 → 3.5.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
 
@@ -2965,6 +2987,8 @@ import {
2965
2987
  eip712DomainSeparatorEvm,
2966
2988
  balanceProofHashEvm,
2967
2989
  coopCloseHashEvm,
2990
+ balanceProofMessageSolana,
2991
+ SOLANA_BALANCE_PROOF_MESSAGE_SIZE,
2968
2992
  balanceProofHashSolana,
2969
2993
  minaHashToField,
2970
2994
  balanceProofFieldsMina
@@ -3068,7 +3092,9 @@ var BootstrapService = class {
3068
3092
  ownIlpAddress;
3069
3093
  toonEncoder;
3070
3094
  toonDecoder;
3095
+ /** @deprecated see {@link BootstrapServiceConfig.basePricePerByte}. */
3071
3096
  basePricePerByte;
3097
+ connectorEdgeLookup;
3072
3098
  // Event emitter
3073
3099
  listeners = [];
3074
3100
  phase = "discovering";
@@ -3095,7 +3121,8 @@ var BootstrapService = class {
3095
3121
  this.ownIlpAddress = config.ownIlpAddress;
3096
3122
  this.toonEncoder = config.toonEncoder;
3097
3123
  this.toonDecoder = config.toonDecoder;
3098
- this.basePricePerByte = config.basePricePerByte ?? 10n;
3124
+ this.basePricePerByte = config.basePricePerByte;
3125
+ this.connectorEdgeLookup = config.connectorEdgeLookup;
3099
3126
  }
3100
3127
  /**
3101
3128
  * Set the ILP client for sending packets via the connector.
@@ -3396,32 +3423,86 @@ var BootstrapService = class {
3396
3423
  }
3397
3424
  return result;
3398
3425
  }
3426
+ /**
3427
+ * What the announce PREPARE should carry as its amount.
3428
+ *
3429
+ * ASKED for (ADR 0020, flat per handler), never computed as bytes × rate.
3430
+ * `basePricePerByte`, when a caller still sets it, is a deprecated explicit
3431
+ * override rather than a per-byte rate — see its doc comment on
3432
+ * {@link BootstrapServiceConfig}.
3433
+ *
3434
+ * @throws {BootstrapError} when nothing terminated here prices `destination`.
3435
+ */
3436
+ async resolveAnnounceAmount(lookup, destination) {
3437
+ if (this.basePricePerByte !== void 0) {
3438
+ return String(this.basePricePerByte);
3439
+ }
3440
+ const routePrice = await lookup.getRoutePrice(destination);
3441
+ if (routePrice === null) {
3442
+ throw new BootstrapError(
3443
+ `No locally-terminated route price for ${destination}`
3444
+ );
3445
+ }
3446
+ return String(routePrice.price);
3447
+ }
3399
3448
  /**
3400
3449
  * Announce own kind:10032 as paid ILP PREPARE (Phase 2).
3401
3450
  */
3402
3451
  async announceViaIlp(result) {
3403
- if (!this.ilpClient || !this.toonEncoder) {
3452
+ if (!this.ilpClient || !this.toonEncoder || !this.connectorEdgeLookup) {
3404
3453
  return;
3405
3454
  }
3406
3455
  const ilpInfoEvent = buildIlpPeerInfoEvent(this.ownIlpInfo, this.secretKey);
3407
3456
  const toonBytes = this.toonEncoder(ilpInfoEvent);
3408
- const base64Toon = Buffer.from(toonBytes).toString("base64");
3409
- const amount = String(BigInt(toonBytes.length) * this.basePricePerByte);
3457
+ const identity = await this.connectorEdgeLookup.getIdentity(
3458
+ result.peerInfo.ilpAddress
3459
+ );
3460
+ const amount = await this.resolveAnnounceAmount(
3461
+ this.connectorEdgeLookup,
3462
+ result.peerInfo.ilpAddress
3463
+ );
3464
+ const request = {
3465
+ method: "POST",
3466
+ target: "",
3467
+ headers: [["content-type", "application/json"]],
3468
+ body: toonBytes
3469
+ };
3470
+ const exchange = sealExchange(request, identity.publicKey);
3471
+ const data = Buffer.from(exchange.data).toString("base64");
3472
+ const executionCondition = Buffer.from(exchange.condition).toString(
3473
+ "base64"
3474
+ );
3410
3475
  let ilpResult;
3411
3476
  if (this.claimSigner && result.channelId && this.ilpClient.sendIlpPacketWithClaim) {
3412
3477
  const claim = await this.claimSigner(result.channelId, BigInt(amount));
3413
3478
  ilpResult = await this.ilpClient.sendIlpPacketWithClaim(
3414
- { destination: result.peerInfo.ilpAddress, amount, data: base64Toon },
3479
+ {
3480
+ destination: result.peerInfo.ilpAddress,
3481
+ amount,
3482
+ data,
3483
+ executionCondition
3484
+ },
3415
3485
  claim
3416
3486
  );
3417
3487
  } else {
3418
3488
  ilpResult = await this.ilpClient.sendIlpPacket({
3419
3489
  destination: result.peerInfo.ilpAddress,
3420
3490
  amount,
3421
- data: base64Toon
3491
+ data,
3492
+ executionCondition
3422
3493
  });
3423
3494
  }
3424
- if (ilpResult.accepted) {
3495
+ const responseData = ilpResult.data !== void 0 ? Uint8Array.from(Buffer.from(ilpResult.data, "base64")) : void 0;
3496
+ const outcome = readExchangeOutcome(
3497
+ {
3498
+ accepted: ilpResult.accepted,
3499
+ code: ilpResult.code,
3500
+ message: ilpResult.message
3501
+ },
3502
+ responseData,
3503
+ exchange.sharedSecret
3504
+ );
3505
+ if (outcome.kind === "answered") {
3425
3506
  console.log(
3426
3507
  `[Bootstrap] Announced to ${result.registeredPeerId} via ILP (eventId: ${ilpInfoEvent.id})`
3427
3508
  );
@@ -3431,17 +3512,18 @@ var BootstrapService = class {
3431
3512
  eventId: ilpInfoEvent.id,
3432
3513
  amount
3433
3514
  });
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
- );
3515
+ return;
3444
3516
  }
3517
+ const refusedBy = outcome.kind === "destination-refused" ? "destination" : "path";
3518
+ const reason = `${outcome.code} ${outcome.message} (${refusedBy} refused)`;
3519
+ this.emit({
3520
+ type: "bootstrap:announce-failed",
3521
+ peerId: result.registeredPeerId,
3522
+ reason
3523
+ });
3524
+ console.warn(
3525
+ `[Bootstrap] Announce rejected by ${result.registeredPeerId}: ${reason}`
3526
+ );
3445
3527
  }
3446
3528
  /**
3447
3529
  * Query a peer's relay for their kind:10032 ILP Peer Info event.
@@ -3996,6 +4078,9 @@ function createHttpIlpClient(baseUrl) {
3996
4078
  ...params.timeout !== void 0 && { timeout: params.timeout },
3997
4079
  ...params.expiresAt !== void 0 && {
3998
4080
  expiresAt: params.expiresAt
4081
+ },
4082
+ ...params.executionCondition !== void 0 && {
4083
+ executionCondition: params.executionCondition
3999
4084
  }
4000
4085
  })
4001
4086
  });
@@ -4041,7 +4126,12 @@ function createDirectIlpClient(connector, _config) {
4041
4126
  destination: params.destination,
4042
4127
  amount,
4043
4128
  data,
4044
- expiresAt
4129
+ expiresAt,
4130
+ ...params.executionCondition !== void 0 && {
4131
+ executionCondition: Uint8Array.from(
4132
+ Buffer.from(params.executionCondition, "base64")
4133
+ )
4134
+ }
4045
4135
  });
4046
4136
  if (result.type === "fulfill" || result.type === 13) {
4047
4137
  return {
@@ -4211,6 +4301,9 @@ function createHttpIlpClient2(connectorUrl) {
4211
4301
  data: params.data,
4212
4302
  ...params.expiresAt !== void 0 && {
4213
4303
  expiresAt: params.expiresAt
4304
+ },
4305
+ ...params.executionCondition !== void 0 && {
4306
+ executionCondition: params.executionCondition
4214
4307
  }
4215
4308
  }),
4216
4309
  signal: params.timeout ? AbortSignal.timeout(params.timeout) : void 0
@@ -4464,7 +4557,8 @@ function createToonNode(config) {
4464
4557
  ownIlpAddress: config.ilpInfo.ilpAddress,
4465
4558
  toonEncoder: config.toonEncoder,
4466
4559
  toonDecoder: config.toonDecoder,
4467
- basePricePerByte: config.basePricePerByte ?? 10n
4560
+ basePricePerByte: config.basePricePerByte,
4561
+ connectorEdgeLookup: config.connectorEdgeLookup
4468
4562
  },
4469
4563
  config.secretKey,
4470
4564
  config.ilpInfo
@@ -4648,8 +4742,49 @@ var SOLANA_CHAIN_PRESETS = {
4648
4742
  name: "solana-devnet",
4649
4743
  chainType: "solana",
4650
4744
  rpcUrl: "http://localhost:19899",
4651
- programId: "EdJxYPDxGvaJuu57DSUptf4soLv8enpdyQJJhHDLiydG",
4745
+ // Deliberately empty: this preset targets a local `solana-test-validator`
4746
+ // (see rpcUrl), not public devnet -- the public-devnet program id
4747
+ // (2aEVJ8ko…, per connector's packages/solana-program/deployments/
4748
+ // devnet-public.md) does not exist there. A local validator's program id
4749
+ // is a fresh keypair generated per `cargo build-sbf` deploy, so no fixed
4750
+ // default is meaningful; callers must supply the id of whatever they
4751
+ // deployed locally via the `SOLANA_PROGRAM_ID` env override below.
4752
+ programId: "",
4652
4753
  cluster: "devnet"
4754
+ },
4755
+ // Solana mainnet-beta (public). NOT wired into network-profile.ts's
4756
+ // public tiers, which already resolve their own mainnet Solana endpoint
4757
+ // (SOLANA_TIER.mainnet) independently of this low-level,
4758
+ // e2e-stack-facing preset table.
4759
+ "solana-mainnet": {
4760
+ name: "solana-mainnet",
4761
+ chainType: "solana",
4762
+ rpcUrl: "https://api.mainnet-beta.solana.com",
4763
+ // VERIFIED 2026-08-14, per toon#198: owner
4764
+ // BPFLoaderUpgradeab1e11111111111111111111111 (executable, upgradeable),
4765
+ // ProgramData 29MT16eh1GCdL4JWrJHjyTWu5ZMn217h25ojCvFpx2wc, 136,770
4766
+ // bytes, deployed slot 439316400. Bytecode pulled back with
4767
+ // `solana program dump` hashes identical to a local build from
4768
+ // connector@main with the pinned `cargo build-sbf --tools-version
4769
+ // v1.52`, so the on-chain program is provably the audited source. Full
4770
+ // deployment record: toon-protocol/connector#971.
4771
+ programId: "8e7BhzydH1EqL486tw6Lp99BXviH3i5JN8qNpMSNmHj3",
4772
+ cluster: "mainnet-beta",
4773
+ // Circle's native USDC mint on Solana mainnet-beta, 6 decimals.
4774
+ // VERIFIED 2026-08-13 by Drew Pierson, per toon#166's "Needs a human
4775
+ // before merge" criterion, on two independent sources:
4776
+ // - Circle's published list gives this address for Solana mainnet:
4777
+ // https://developers.circle.com/stablecoins/usdc-contract-addresses
4778
+ // That page names bridged representations separately where they
4779
+ // exist (X Layer's USDC.e); none is listed for Solana.
4780
+ // - mainnet-beta at slot 439080550: initialized mint, decimals 6,
4781
+ // supply ~7.73e9, live mint and freeze authorities, owned by the
4782
+ // SPL Token program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA),
4783
+ // not Token-2022.
4784
+ // Native, not bridged. Two things not to confuse it with: this is the
4785
+ // token's MINT ACCOUNT, never a transfer destination; and Circle runs
4786
+ // separate CCTP/Gateway pre-mint addresses that are not this one.
4787
+ tokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
4653
4788
  }
4654
4789
  };
4655
4790
  var MINA_CHAIN_PRESETS = {
@@ -5444,6 +5579,7 @@ export {
5444
5579
  ReputationScoreCalculator,
5445
5580
  SEED_RELAY_LIST_KIND,
5446
5581
  SERVICE_DISCOVERY_KIND,
5582
+ SOLANA_BALANCE_PROOF_MESSAGE_SIZE,
5447
5583
  SOLANA_CHAIN_PRESETS,
5448
5584
  SeedRelayDiscovery,
5449
5585
  SocialPeerDiscovery,
@@ -5467,6 +5603,7 @@ export {
5467
5603
  balanceProofFieldsMina,
5468
5604
  balanceProofHashEvm,
5469
5605
  balanceProofHashSolana,
5606
+ balanceProofMessageSolana,
5470
5607
  base58Decode,
5471
5608
  base58Encode,
5472
5609
  bigintToBytes32BE,