@ostraagent/mcp 0.1.0 → 0.1.1

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/ostra-mcp.js CHANGED
@@ -18,7 +18,7 @@ var __export = (target, all) => {
18
18
  var define_OSTRA_PACKAGED_default;
19
19
  var init_define_OSTRA_PACKAGED = __esm({
20
20
  "<define:OSTRA_PACKAGED>"() {
21
- define_OSTRA_PACKAGED_default = { version: "0.1.0", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xa455cbf5Aa10cE858f3b250dc791FCD900e950BC", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" } }, relayerUrl: "https://relayer.ostraagent.xyz", keeperUrl: "https://keeper.ostraagent.xyz", aggregatorUrl: "https://aggregator.ostraagent.xyz" };
21
+ define_OSTRA_PACKAGED_default = { version: "0.1.1", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xa455cbf5Aa10cE858f3b250dc791FCD900e950BC", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" } }, relayerUrl: "https://relayer.ostraagent.xyz", keeperUrl: "https://keeper.ostraagent.xyz", aggregatorUrl: "https://aggregator.ostraagent.xyz" };
22
22
  }
23
23
  });
24
24
 
@@ -13598,6 +13598,108 @@ var init_quote = __esm({
13598
13598
  }
13599
13599
  });
13600
13600
 
13601
+ // ../sdk/src/pairs.ts
13602
+ import { getAddress as getAddress9, parseAbi as parseAbi2, zeroAddress as zeroAddress6 } from "viem";
13603
+ function findLaunchPair(symbolOrAddress) {
13604
+ const s = symbolOrAddress.trim();
13605
+ if (/^0x[0-9a-fA-F]{40}$/.test(s)) {
13606
+ const a = getAddress9(s);
13607
+ return LAUNCH_PAIRS.find((q) => q.address === a) ?? null;
13608
+ }
13609
+ const u = s.toUpperCase();
13610
+ return LAUNCH_PAIRS.find((q) => q.symbol === u) ?? null;
13611
+ }
13612
+ function launchPair(symbolOrAddress) {
13613
+ const q = findLaunchPair(symbolOrAddress);
13614
+ if (q === null) throw new RangeError(`${symbolOrAddress} is not a pair Ostra offers; the pairs are ${LAUNCH_PAIRS.map((x) => x.symbol).join(", ")}`);
13615
+ return q;
13616
+ }
13617
+ async function readPairUsdPrice(client, quote) {
13618
+ const block = await client.getBlock();
13619
+ const now3 = Number(block.timestamp);
13620
+ if (quote.feed === null) return { quote, priceE8: 100000000n, updatedAt: now3 };
13621
+ const [decimals, round] = await Promise.all([
13622
+ client.readContract({ address: quote.feed, abi: chainlinkAggregatorAbi, functionName: "decimals" }),
13623
+ client.readContract({ address: quote.feed, abi: chainlinkAggregatorAbi, functionName: "latestRoundData" })
13624
+ ]);
13625
+ if (Number(decimals) !== 8) throw new Error(`the ${quote.symbol}/USD feed ${quote.feed} has ${decimals} decimals, not 8`);
13626
+ const answer = round[1];
13627
+ const updatedAt = Number(round[3]);
13628
+ if (answer <= 0n) throw new Error(`the ${quote.symbol}/USD feed ${quote.feed} answers ${answer}`);
13629
+ const maxAge = quote.kind === "stock" ? STOCK_PRICE_MAX_AGE : ETH_PRICE_MAX_AGE;
13630
+ if (now3 - updatedAt > maxAge) {
13631
+ throw new Error(`the ${quote.symbol}/USD price was last updated ${Math.round((now3 - updatedAt) / 3600)} hours ago, older than ${Math.round(maxAge / 3600)} hours; try again once the feed updates`);
13632
+ }
13633
+ let priceE8 = answer;
13634
+ if (quote.kind === "stock") {
13635
+ const multiplier = await client.readContract({ address: quote.address, abi: stockTokenAbi, functionName: "uiMultiplier" });
13636
+ if (multiplier <= 0n) throw new Error(`${quote.symbol} reports a uiMultiplier of ${multiplier}`);
13637
+ priceE8 = answer * multiplier / 10n ** 18n;
13638
+ }
13639
+ return { quote, priceE8, updatedAt };
13640
+ }
13641
+ function usdToQuoteUnits(usdE6, priceE8, decimals) {
13642
+ if (usdE6 <= 0n) throw new RangeError("the amount in USD must be positive");
13643
+ if (priceE8 <= 0n) throw new RangeError("the price must be positive");
13644
+ return usdE6 * 10n ** BigInt(decimals) * 100n / priceE8;
13645
+ }
13646
+ var STOCK_PRICE_MAX_AGE, ETH_PRICE_MAX_AGE, stock, LAUNCH_PAIRS, stockTokenAbi;
13647
+ var init_pairs = __esm({
13648
+ "../sdk/src/pairs.ts"() {
13649
+ "use strict";
13650
+ init_define_OSTRA_PACKAGED();
13651
+ init_constants();
13652
+ init_external_abi();
13653
+ STOCK_PRICE_MAX_AGE = 4 * 86400;
13654
+ ETH_PRICE_MAX_AGE = 86400 + 3600;
13655
+ stock = (symbol, name, address4, feed) => ({
13656
+ symbol,
13657
+ name,
13658
+ kind: "stock",
13659
+ address: getAddress9(address4),
13660
+ decimals: 18,
13661
+ feed: getAddress9(feed)
13662
+ });
13663
+ LAUNCH_PAIRS = [
13664
+ { symbol: "USDG", name: "Global Dollar", kind: "usdg", address: USDG, decimals: 6, feed: null },
13665
+ { symbol: "ETH", name: "Ether", kind: "eth", address: zeroAddress6, decimals: 18, feed: CHAINLINK_ETH_USD },
13666
+ stock("AAPL", "Apple", "0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9", "0x6B22A786bAa607d76728168703a39Ea9C99f2cD0"),
13667
+ stock("AMZN", "Amazon", "0x12f190a9F9d7D37a250758b26824B97CE941bF54", "0xD5a1508ceD74c084eBf3cBe853e2C968fB2a651C"),
13668
+ stock("ASML", "ASML Holding", "0x47F93d52cBeC7C6D2CfC080e154002370a60dAEA", "0xB4106147E8cce40b7d46124090d373A71b70f87D"),
13669
+ stock("BABA", "Alibaba Group", "0xad25Ac6C84D497db898fa1E8387bf6Af3532a1c4", "0x62Cc8F9b5f56a33c9C8A60c8B92779f523c4E984"),
13670
+ stock("CLSK", "CleanSpark", "0xcBB95BBF36099d34dA091dc6Fa6F49EfA257Cee3", "0x810c12D3a554Bc47fd39597Fe3b3AAC4941F50eF"),
13671
+ stock("COIN", "Coinbase", "0x6330D8C3178a418788dF01a47479c0ce7CCF450b", "0xA3a468A452940B7D6b69991207B508c609a98Ef2"),
13672
+ stock("CRCL", "Circle Internet Group", "0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5", "0x6652eDf64bA3731C4F2D3ce821A0Fb1f1f6b482a"),
13673
+ stock("DELL", "Dell Technologies", "0x941AE714EC6D8130c7B75d67160Ca08f1e7d11Dd", "0x1C6c8cADBe02E19129c39dDB92281cE4c0bf206b"),
13674
+ stock("EWY", "iShares MSCI South Korea ETF", "0x7f0aBeF0C07280F82c6a08ead09dEd6BAE2C13Fc", "0xEFdf54610B62A7753Ec30bDc380847c12D32e1D1"),
13675
+ stock("GOOGL", "Alphabet", "0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3", "0xF6f373a037c30F0e5010d854385cA89185AE638b"),
13676
+ stock("INTC", "Intel", "0xc72b96e0E48ecd4DC75E1e45396e26300BC39681", "0x3f390C5C24628Ac7C489515402235FeAD71D1913"),
13677
+ stock("IONQ", "IonQ", "0x558378E000D634A36593E338eBacdd6207640EfE", "0x22EfeC4919baf55F360E0EDee4AbEB26DE4971eb"),
13678
+ stock("META", "Meta", "0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35", "0x7C38C00C30BEe9378381E7B6135d7283356D71b1"),
13679
+ stock("MSFT", "Microsoft", "0xe93237C50D904957Cf27E7B1133b510C669c2e74", "0x45C3C877C15E6BA2EBB19eA114Ea508d14C1Af2E"),
13680
+ stock("MSTR", "MicroStrategy", "0xec262a75e413fAfD0dF80480274532C79D42da09", "0x396118bdFB181e6240E74D243F266B061c0edc3D"),
13681
+ stock("MU", "Micron Technology", "0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD", "0x425EEFdCf05ed6526C3cE61Af99429A228a6d596"),
13682
+ stock("NBIS", "Nebius Group", "0x9D9c6684F596F66a64C030B93A886D51Fd4D7931", "0xE1D87B116Ba0fe898998f1D140339D1fA1E09705"),
13683
+ stock("NVDA", "Nvidia", "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC", "0x379EC4f7C378F34a1B47E4F3cbeBCbAC3E8E9F15"),
13684
+ stock("ORCL", "Oracle", "0xb0992820E760d836549ba69BC7598b4af75dEE03", "0x0e6a64a2B58A6693a531E6c555f3A5d042eEA844"),
13685
+ stock("PLTR", "Palantir Technologies", "0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A", "0x820ABedFF239034956B7A9d2F0a331f9F075eB4c"),
13686
+ stock("QQQ", "Invesco QQQ ETF", "0xD5f3879160bc7c32ebb4dC785F8a4F505888de68", "0x80901d846d5D7B030F26B480776EE3b29374C2ae"),
13687
+ stock("RGTI", "Rigetti Computing", "0x284358abc07F9359f19f4b5b4aC91901Be2597Ba", "0x2A045cF1C49c61c166C036d2f06FA2D2d984f765"),
13688
+ stock("RKLB", "Rocket Lab", "0x3b14C39E89D60D627b42a1A4CA45b5bb45Fc12e2", "0x045477BF65Aef6f4F2386ad0164579e48381CC74"),
13689
+ stock("SGOV", "iShares 0-3 Month Treasury Bond ETF", "0x92FD66527192E3e61d4DDd13322Aa222DE86F9B5", "0xa0DF4ee0fFf975306345875E3548Fcc519577A11"),
13690
+ stock("SLV", "iShares Silver Trust ETF", "0x411eFb0E7f985935DAec3D4C3ebaEa0d0AD7D89f", "0x209b73908e92Ae021826eD79609845451Ecba2ce"),
13691
+ stock("SNDK", "SanDisk", "0xB90A19fF0Af67f7779afF50A882A9CfF42446400", "0xfb133Fa4B7b385802B693a293606682Df47109A3"),
13692
+ stock("SPCX", "Space Exploration Technologies", "0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa", "0xB265810950ba6c5C0Ff821c9963014a56fD8Bffb"),
13693
+ stock("SPY", "SPDR S&P 500 ETF", "0x117cc2133c37B721F49dE2A7a74833232B3B4C0C", "0x319724394D3A0e3669269846abE664Cd621f9f6A"),
13694
+ stock("TSLA", "Tesla", "0x322F0929c4625eD5bAd873c95208D54E1c003b2d", "0x4A1166a659A55625345e9515b32adECea5547C38"),
13695
+ stock("TSM", "Taiwan Semiconductor", "0x58FfE4a942d3885bAa22D7520691F611EF09e7AA", "0x874cF94aa8eC88Fd9560094dD065f2fB3E41Fc2F"),
13696
+ stock("USAR", "USA Rare Earth", "0xd917B029C761D264c6A312BBbcDA868658eF86a6", "0xA994d3684e8400A6c8078226925779FdeE682DD9"),
13697
+ stock("USO", "United States Oil Fund ETF", "0xa30FA36Db767ad9eD3f7a60fC79526fB4d56D344", "0x75a9c76Ef439e2C7c2E5a34Ab105EcFe3766431c")
13698
+ ];
13699
+ stockTokenAbi = parseAbi2(["function uiMultiplier() view returns (uint256)"]);
13700
+ }
13701
+ });
13702
+
13601
13703
  // ../sdk/src/browser.ts
13602
13704
  var init_browser = __esm({
13603
13705
  "../sdk/src/browser.ts"() {
@@ -13617,6 +13719,7 @@ var init_browser = __esm({
13617
13719
  init_lthash();
13618
13720
  init_messages();
13619
13721
  init_quote();
13722
+ init_pairs();
13620
13723
  init_reads();
13621
13724
  init_relayer();
13622
13725
  init_wots();
@@ -13624,7 +13727,7 @@ var init_browser = __esm({
13624
13727
  });
13625
13728
 
13626
13729
  // ../sdk/src/deploy.ts
13627
- import { concat as concat3, encodeDeployData, encodeFunctionData as encodeFunctionData5, getAddress as getAddress9, getContractAddress as getContractAddress2, keccak256 as keccak2569, numberToHex as numberToHex2 } from "viem";
13730
+ import { concat as concat3, encodeDeployData, encodeFunctionData as encodeFunctionData5, getAddress as getAddress10, getContractAddress as getContractAddress2, keccak256 as keccak2569, numberToHex as numberToHex2 } from "viem";
13628
13731
  var init_deploy = __esm({
13629
13732
  "../sdk/src/deploy.ts"() {
13630
13733
  "use strict";
@@ -13859,13 +13962,13 @@ var init_config = __esm({
13859
13962
  });
13860
13963
 
13861
13964
  // ../core/src/relay.ts
13862
- import { decodeFunctionData as decodeFunctionData3, getAddress as getAddress10, isAddress as isAddress5 } from "viem";
13965
+ import { decodeFunctionData as decodeFunctionData3, getAddress as getAddress11, isAddress as isAddress5 } from "viem";
13863
13966
  import { x402Client, x402HTTPClient } from "@x402/core/client";
13864
13967
  import { toClientEvmSigner } from "@x402/evm";
13865
13968
  import { BatchSettlementEvmScheme } from "@x402/evm/batch-settlement/client";
13866
13969
  function address(value, what) {
13867
13970
  if (typeof value !== "string" || !isAddress5(value, { strict: false })) throw new Error(`${what} is not an address`);
13868
- return getAddress10(value);
13971
+ return getAddress11(value);
13869
13972
  }
13870
13973
  function checkSplits(raw, what) {
13871
13974
  if (!Array.isArray(raw)) throw new Error(`${what} are not a list`);
@@ -13886,7 +13989,7 @@ function checkRelayerFacts(raw, want) {
13886
13989
  const f = raw;
13887
13990
  if (f === null || typeof f !== "object") throw new Error("the relayer's terms are not an object");
13888
13991
  if (f.chainId !== want.chainId) throw new Error(`the relayer serves chain ${String(f.chainId)}, not ${want.chainId}`);
13889
- if (address(f.token, "the relayer's token") !== getAddress10(want.token)) throw new Error(`the relayer serves token ${String(f.token)}, not ${want.token}`);
13992
+ if (address(f.token, "the relayer's token") !== getAddress11(want.token)) throw new Error(`the relayer serves token ${String(f.token)}, not ${want.token}`);
13890
13993
  if (f.decimals !== TOKEN_DECIMALS) throw new Error(`the relayer's token has ${String(f.decimals)} decimals, not ${TOKEN_DECIMALS}`);
13891
13994
  if (address(f.priceFeed, "the relayer's price feed") !== CHAINLINK_ETH_USD) throw new Error(`the relayer prices from ${String(f.priceFeed)}, not the Chainlink ETH/USD feed ${CHAINLINK_ETH_USD}`);
13892
13995
  if (f.maxCalls !== MAX_RELAY_CALLS) throw new Error(`the relayer takes ${String(f.maxCalls)} calls per request, not ${MAX_RELAY_CALLS}`);
@@ -13914,10 +14017,10 @@ function relayerChannelDeposit(needed, vaultBalance, keep = 0n) {
13914
14017
  function relayerOfferProblem(r, want) {
13915
14018
  if (r.scheme !== "batch-settlement") return `the offer is ${r.scheme}, not batch-settlement`;
13916
14019
  if (r.network !== want.network) return `the offer is on ${r.network}, not ${want.network}`;
13917
- if (!isAddress5(r.asset, { strict: false }) || getAddress10(r.asset) !== getAddress10(want.config.token)) return `the offer is in ${r.asset}, not ${want.config.token}`;
13918
- if (!isAddress5(r.payTo, { strict: false }) || getAddress10(r.payTo) !== getAddress10(want.config.receiver)) return `the offer pays ${r.payTo}, not the relayer's split ${want.config.receiver}`;
14020
+ if (!isAddress5(r.asset, { strict: false }) || getAddress11(r.asset) !== getAddress11(want.config.token)) return `the offer is in ${r.asset}, not ${want.config.token}`;
14021
+ if (!isAddress5(r.payTo, { strict: false }) || getAddress11(r.payTo) !== getAddress11(want.config.receiver)) return `the offer pays ${r.payTo}, not the relayer's split ${want.config.receiver}`;
13919
14022
  const extra = r.extra ?? {};
13920
- if (typeof extra.receiverAuthorizer !== "string" || !isAddress5(extra.receiverAuthorizer, { strict: false }) || getAddress10(extra.receiverAuthorizer) !== getAddress10(want.config.receiverAuthorizer)) {
14023
+ if (typeof extra.receiverAuthorizer !== "string" || !isAddress5(extra.receiverAuthorizer, { strict: false }) || getAddress11(extra.receiverAuthorizer) !== getAddress11(want.config.receiverAuthorizer)) {
13921
14024
  return `the offer's receiverAuthorizer is ${String(extra.receiverAuthorizer)}, not the relayer's vault ${want.config.receiverAuthorizer}`;
13922
14025
  }
13923
14026
  if (extra.withdrawDelay !== want.config.withdrawDelay) return `the offer's withdrawDelay is ${String(extra.withdrawDelay)}, not the relayer's ${want.config.withdrawDelay} s`;
@@ -13930,7 +14033,7 @@ function relayerOfferProblem(r, want) {
13930
14033
  }
13931
14034
  function openChannelCall(calls, vault, token) {
13932
14035
  for (const c of calls) {
13933
- if (getAddress10(c.to) !== getAddress10(vault)) continue;
14036
+ if (getAddress11(c.to) !== getAddress11(vault)) continue;
13934
14037
  let decoded;
13935
14038
  try {
13936
14039
  decoded = decodeFunctionData3({ abi: ostraVaultAbi, data: c.data });
@@ -13945,7 +14048,7 @@ function openChannelCall(calls, vault, token) {
13945
14048
  }
13946
14049
  return null;
13947
14050
  }
13948
- var RELAYER_DIR, VAULT_SPEND_GAS, REQUEST_TIMEOUT_MS, MAX_STEPS, MAX_RESENDS, LANDED_TIMEOUT_MS, BPS_DENOMINATOR, now, LocalSender, RelayerUnavailableError, PendingSpendUnpaidError, RelayerError, RelayerStore, RelayerClient;
14051
+ var RELAYER_DIR, VAULT_SPEND_GAS, REQUEST_TIMEOUT_MS, MAX_STEPS, MAX_RESENDS, LANDED_TIMEOUT_MS, OPEN_READ_TRIES, OPEN_READ_INTERVAL_MS, BPS_DENOMINATOR, now, LocalSender, RelayerUnavailableError, PendingSpendUnpaidError, RelayerError, RelayerStore, RelayerClient;
13949
14052
  var init_relay = __esm({
13950
14053
  "../core/src/relay.ts"() {
13951
14054
  "use strict";
@@ -13960,6 +14063,8 @@ var init_relay = __esm({
13960
14063
  MAX_STEPS = 8;
13961
14064
  MAX_RESENDS = 3;
13962
14065
  LANDED_TIMEOUT_MS = 6e4;
14066
+ OPEN_READ_TRIES = 20;
14067
+ OPEN_READ_INTERVAL_MS = 500;
13963
14068
  BPS_DENOMINATOR = 1e4;
13964
14069
  now = () => Math.floor(Date.now() / 1e3);
13965
14070
  LocalSender = class {
@@ -14014,7 +14119,7 @@ var init_relay = __esm({
14014
14119
  #name;
14015
14120
  constructor(agentFolder, chainId, token, relayerVault) {
14016
14121
  this.#folder = agentFolder.folder(RELAYER_DIR);
14017
- this.#name = `${chainId}-${getAddress10(token)}-${getAddress10(relayerVault)}.json`;
14122
+ this.#name = `${chainId}-${getAddress11(token)}-${getAddress11(relayerVault)}.json`;
14018
14123
  }
14019
14124
  async load() {
14020
14125
  const text2 = await this.#folder.read(this.#name);
@@ -14099,7 +14204,7 @@ var init_relay = __esm({
14099
14204
  functionName: "splitAddress",
14100
14205
  args: [d.token, facts.vault, facts.splits.map((s) => s.recipient), facts.splits.map((s) => s.bps)]
14101
14206
  });
14102
- if (getAddress10(split) !== facts.split) throw new Error(`the relayer's split ${facts.split} is not the OstraSplit of its vault and splits (${split})`);
14207
+ if (getAddress11(split) !== facts.split) throw new Error(`the relayer's split ${facts.split} is not the OstraSplit of its vault and splits (${split})`);
14103
14208
  this.#facts = facts;
14104
14209
  this.#store = new RelayerStore(this.#keys.folder, this.#config.chainId, d.token, facts.vault);
14105
14210
  return facts;
@@ -14121,7 +14226,7 @@ var init_relay = __esm({
14121
14226
  channel: String(open3.channel).toLowerCase(),
14122
14227
  amount: BigInt(String(open3.amount)),
14123
14228
  txHash: String(open3.txHash),
14124
- calls: Array.isArray(open3.calls) ? open3.calls.map((c) => ({ to: getAddress10(c.to), data: c.data })) : []
14229
+ calls: Array.isArray(open3.calls) ? open3.calls.map((c) => ({ to: getAddress11(c.to), data: c.data })) : []
14125
14230
  }
14126
14231
  };
14127
14232
  }
@@ -14175,7 +14280,7 @@ var init_relay = __esm({
14175
14280
  continue;
14176
14281
  }
14177
14282
  const ch = await readChannel(this.#client, this.#config.deployment.batchSettlement, c.channel);
14178
- const usable = payer !== null && getAddress10(c.config.payer) === payer && getAddress10(c.config.receiverAuthorizer) === facts.vault && getAddress10(c.config.receiver) === facts.split && ch.pendingWithdrawal === null && ch.balance - BigInt(c.cumulative) >= amount;
14283
+ const usable = payer !== null && getAddress11(c.config.payer) === payer && getAddress11(c.config.receiverAuthorizer) === facts.vault && getAddress11(c.config.receiver) === facts.split && ch.pendingWithdrawal === null && ch.balance - BigInt(c.cumulative) >= amount;
14179
14284
  if (!usable) {
14180
14285
  c.retired = true;
14181
14286
  await this.#save();
@@ -14243,7 +14348,7 @@ var init_relay = __esm({
14243
14348
  else this.#reserved.delete(reservation.channel);
14244
14349
  }
14245
14350
  async #post(calls, extra, headers = {}) {
14246
- const body = { calls: calls.map((c) => ({ to: getAddress10(c.to), data: c.data })) };
14351
+ const body = { calls: calls.map((c) => ({ to: getAddress11(c.to), data: c.data })) };
14247
14352
  if (extra.prepay === true) body.prepay = true;
14248
14353
  if (extra.payer !== null) body.payer = extra.payer;
14249
14354
  if (extra.allowFailedSwaps === true) body.allowFailedSwaps = true;
@@ -14252,10 +14357,10 @@ var init_relay = __esm({
14252
14357
  /** The x402 HTTP client of relayer channel `c`: the stock batch-settlement client, the vault as payer, the channel's voucher key signing, never depositing. */
14253
14358
  #x402(c) {
14254
14359
  const voucherKey = this.#keys.channelVoucherSigner(this.#config.chainId, this.#config.deployment.token, c.config.salt);
14255
- if (voucherKey.address !== getAddress10(c.config.payerAuthorizer)) throw new Error(`the voucher key of relayer channel ${c.channel} is not derived from this machine's master seed`);
14360
+ if (voucherKey.address !== getAddress11(c.config.payerAuthorizer)) throw new Error(`the voucher key of relayer channel ${c.channel} is not derived from this machine's master seed`);
14256
14361
  const client = this.#client;
14257
14362
  const payer = {
14258
- address: getAddress10(c.config.payer),
14363
+ address: getAddress11(c.config.payer),
14259
14364
  signTypedData: () => Promise.reject(new Error("a vault never signs typed data")),
14260
14365
  readContract: (args) => client.readContract(args)
14261
14366
  };
@@ -14338,7 +14443,7 @@ var init_relay = __esm({
14338
14443
  * records it, and pays the open and the vault's debt with its first
14339
14444
  * voucher (the same request sent again).
14340
14445
  */
14341
- async #payOpen(calls, channel, paymentDue, debt) {
14446
+ async #payOpen(calls, channel, openTx, paymentDue, debt) {
14342
14447
  const facts = await this.facts();
14343
14448
  const d = this.#config.deployment;
14344
14449
  const vault = await this.#buyerVault().vaultAddress();
@@ -14347,12 +14452,12 @@ var init_relay = __esm({
14347
14452
  const config = found.config;
14348
14453
  const id = channelId(config, this.#config.chainId, d.batchSettlement).toLowerCase();
14349
14454
  if (id !== channel.toLowerCase()) throw new Error(`the relayer reports channel ${channel}, but the open in the request is channel ${id}`);
14350
- if (getAddress10(config.receiver) !== facts.split || getAddress10(config.receiverAuthorizer) !== facts.vault || config.withdrawDelay !== facts.gracePeriodSeconds) {
14455
+ if (getAddress11(config.receiver) !== facts.split || getAddress11(config.receiverAuthorizer) !== facts.vault || config.withdrawDelay !== facts.gracePeriodSeconds) {
14351
14456
  throw new Error(`channel ${channel} is not on the relayer's terms`);
14352
14457
  }
14353
14458
  const voucherKey = this.#keys.channelVoucherSigner(this.#config.chainId, d.token, config.salt);
14354
- if (voucherKey.address !== getAddress10(config.payerAuthorizer)) throw new Error(`the voucher key of relayer channel ${channel} is not derived from this machine's master seed`);
14355
- const ch = await readChannel(this.#client, d.batchSettlement, id);
14459
+ if (voucherKey.address !== getAddress11(config.payerAuthorizer)) throw new Error(`the voucher key of relayer channel ${channel} is not derived from this machine's master seed`);
14460
+ const ch = await this.#openedChannel(id, openTx);
14356
14461
  if (ch.balance !== found.amount || ch.pendingWithdrawal !== null) throw new Error(`relayer channel ${channel} does not hold its deposit of ${found.amount}`);
14357
14462
  const most = debt + await this.#spendBound();
14358
14463
  if (paymentDue > most) throw new Error(`the relayer asks ${formatTokenAmount(paymentDue)} for the open of channel ${channel}, more than an open's price and the vault's debt (${formatTokenAmount(most)})`);
@@ -14379,7 +14484,22 @@ var init_relay = __esm({
14379
14484
  /** Pays an open the relayer sent before payment that this machine did not pay (it stopped, or was restored from the seed). */
14380
14485
  async #payUnpaidOpen(unpaid, debt) {
14381
14486
  if (unpaid.calls.length === 0) throw new Error(`the relayer holds an unpaid open of channel ${unpaid.channel} without its calls; it cannot be paid from here`);
14382
- await this.#landed(await this.#payOpen(unpaid.calls, unpaid.channel, unpaid.amount, debt));
14487
+ await this.#landed(await this.#payOpen(unpaid.calls, unpaid.channel, unpaid.txHash, unpaid.amount, debt));
14488
+ }
14489
+ /**
14490
+ * Channel `id` as its open `openTx` left it: the open's receipt on this RPC
14491
+ * first (it must have landed and succeeded), then reads until the channel
14492
+ * shows on the node that answers, for at most OPEN_READ_TRIES reads.
14493
+ */
14494
+ async #openedChannel(id, openTx) {
14495
+ await this.#landed(openTx);
14496
+ const settlement = this.#config.deployment.batchSettlement;
14497
+ let ch = await readChannel(this.#client, settlement, id);
14498
+ for (let i = 1; i < OPEN_READ_TRIES && ch.balance === 0n && ch.pendingWithdrawal === null; i++) {
14499
+ await new Promise((resolve) => setTimeout(resolve, OPEN_READ_INTERVAL_MS));
14500
+ ch = await readChannel(this.#client, settlement, id);
14501
+ }
14502
+ return ch;
14383
14503
  }
14384
14504
  /** Waits until `hash` has a successful receipt on this RPC. */
14385
14505
  async #landed(hash) {
@@ -14431,7 +14551,7 @@ var init_relay = __esm({
14431
14551
  const b = answer.body;
14432
14552
  const error = typeof b?.error === "string" ? b.error : "";
14433
14553
  if (answer.status === 200 && typeof b?.txHash === "string") {
14434
- const hash = typeof b.paymentDue === "string" && typeof b.channel === "string" ? await this.#payOpen(calls, b.channel, BigInt(b.paymentDue), (await this.vaultState(await this.#buyerVault().vaultAddress())).debt) : b.txHash;
14554
+ const hash = typeof b.paymentDue === "string" && typeof b.channel === "string" ? await this.#payOpen(calls, b.channel, b.txHash, BigInt(b.paymentDue), (await this.vaultState(await this.#buyerVault().vaultAddress())).debt) : b.txHash;
14435
14555
  await this.#landed(hash);
14436
14556
  return hash;
14437
14557
  }
@@ -14491,7 +14611,7 @@ var init_relay = __esm({
14491
14611
  });
14492
14612
 
14493
14613
  // ../core/src/channels.ts
14494
- import { bytesToHex as bytesToHex7, decodeFunctionData as decodeFunctionData4, encodeFunctionData as encodeFunctionData6, getAddress as getAddress11, isAddress as isAddress6, parseAbiItem } from "viem";
14614
+ import { bytesToHex as bytesToHex7, decodeFunctionData as decodeFunctionData4, encodeFunctionData as encodeFunctionData6, getAddress as getAddress12, isAddress as isAddress6, parseAbiItem } from "viem";
14495
14615
  async function channelOpenedLogs(client, vault, channel, fromBlock, toBlock) {
14496
14616
  const out = [];
14497
14617
  for (let from = fromBlock; from <= toBlock; from += LOG_WINDOW_BLOCKS) {
@@ -14507,7 +14627,7 @@ function parseRecipients(input) {
14507
14627
  const out = [];
14508
14628
  for (const r of input) {
14509
14629
  if (!isAddress6(r.recipient, { strict: false })) throw new RangeError(`${r.recipient} is not an address`);
14510
- const recipient = getAddress11(r.recipient);
14630
+ const recipient = getAddress12(r.recipient);
14511
14631
  if (BigInt(recipient) === 0n) throw new RangeError("a recipient must not be the zero address");
14512
14632
  if (!Number.isInteger(r.bps) || r.bps < 1 || r.bps > BPS_DENOMINATOR2) throw new RangeError(`the bps of ${recipient} must be an integer from 1 to ${BPS_DENOMINATOR2}`);
14513
14633
  if (seen.has(recipient)) throw new RangeError(`${recipient} is listed twice`);
@@ -14526,7 +14646,7 @@ function parseGracePeriod(seconds) {
14526
14646
  }
14527
14647
  async function splitAddress(client, config, vault, recipients) {
14528
14648
  const d = config.deployment;
14529
- return getAddress11(
14649
+ return getAddress12(
14530
14650
  await client.readContract({
14531
14651
  address: d.splitFactory,
14532
14652
  abi: ostraSplitFactoryAbi,
@@ -14560,12 +14680,12 @@ async function recoverChannelConfig(client, config, vault, channel) {
14560
14680
  if (log2.transactionHash === null) continue;
14561
14681
  const tx = await client.getTransaction({ hash: log2.transactionHash });
14562
14682
  const calls = [];
14563
- if (tx.to !== null && tx.to !== void 0 && getAddress11(tx.to) === getAddress11(vault)) calls.push({ to: vault, data: tx.input });
14564
- if (tx.to !== null && tx.to !== void 0 && getAddress11(tx.to) === MULTICALL3) {
14683
+ if (tx.to !== null && tx.to !== void 0 && getAddress12(tx.to) === getAddress12(vault)) calls.push({ to: vault, data: tx.input });
14684
+ if (tx.to !== null && tx.to !== void 0 && getAddress12(tx.to) === MULTICALL3) {
14565
14685
  try {
14566
14686
  const decoded = decodeFunctionData4({ abi: multicall3Abi, data: tx.input });
14567
14687
  const inner = decoded.args[0];
14568
- for (const c of inner) if (getAddress11(c.target) === getAddress11(vault)) calls.push({ to: vault, data: c.callData });
14688
+ for (const c of inner) if (getAddress12(c.target) === getAddress12(vault)) calls.push({ to: vault, data: c.callData });
14569
14689
  } catch {
14570
14690
  }
14571
14691
  }
@@ -14645,7 +14765,7 @@ var init_channels = __esm({
14645
14765
  const text2 = await this.#folder.read(name);
14646
14766
  if (text2 === void 0) continue;
14647
14767
  const record = this.#check(JSON.parse(text2), name);
14648
- if (record.chainId === chainId && getAddress11(record.token) === getAddress11(token)) out.push(record);
14768
+ if (record.chainId === chainId && getAddress12(record.token) === getAddress12(token)) out.push(record);
14649
14769
  }
14650
14770
  return out.sort((x, y) => x.createdAt - y.createdAt);
14651
14771
  }
@@ -14693,8 +14813,8 @@ var init_channels = __esm({
14693
14813
  vault,
14694
14814
  channel: opening.channel,
14695
14815
  config: opening.config,
14696
- payee: getAddress11(o.receiverAuthorizer),
14697
- voucherKey: getAddress11(o.payerAuthorizer),
14816
+ payee: getAddress12(o.receiverAuthorizer),
14817
+ voucherKey: getAddress12(o.payerAuthorizer),
14698
14818
  deposit: o.amount.toString(),
14699
14819
  gracePeriod: o.withdrawDelay,
14700
14820
  recipients,
@@ -14730,7 +14850,7 @@ var init_channels = __esm({
14730
14850
  open(input) {
14731
14851
  return this.#serial(async () => {
14732
14852
  if (!isAddress6(input.payee, { strict: false })) throw new RangeError(`${input.payee} is not an address`);
14733
- const payee = getAddress11(input.payee);
14853
+ const payee = getAddress12(input.payee);
14734
14854
  const deposit = parseTokenAmount(input.deposit);
14735
14855
  if (deposit === 0n) throw new RangeError("deposit must be greater than zero");
14736
14856
  const gracePeriod = parseGracePeriod(input.gracePeriod);
@@ -14739,8 +14859,8 @@ var init_channels = __esm({
14739
14859
  if (payeeVault === null) throw new Error(`${payee} is not an Ostra vault; open_channel pays Ostra vaults only (pay other sellers with pay)`);
14740
14860
  if (payeeVault.token !== this.#token()) throw new Error(`the payee vault holds ${payeeVault.token}, not ${this.#token()}`);
14741
14861
  if (input.receiver !== void 0 && recipients.length !== 0) throw new RangeError("a channel names its receiver or the recipients its split is derived from, not both");
14742
- const receiver = input.receiver !== void 0 ? getAddress11(input.receiver) : await splitAddress(this.#client, this.#config, payee, recipients);
14743
- if (receiver === getAddress11("0x0000000000000000000000000000000000000000")) throw new RangeError("the receiver must not be the zero address");
14862
+ const receiver = input.receiver !== void 0 ? getAddress12(input.receiver) : await splitAddress(this.#client, this.#config, payee, recipients);
14863
+ if (receiver === getAddress12("0x0000000000000000000000000000000000000000")) throw new RangeError("the receiver must not be the zero address");
14744
14864
  const salt = randomSalt();
14745
14865
  const voucher = this.#keys.channelVoucherSigner(this.#config.chainId, this.#token(), salt);
14746
14866
  const open3 = { payerAuthorizer: voucher.address, receiver, receiverAuthorizer: payee, withdrawDelay: gracePeriod, salt, amount: deposit };
@@ -14784,7 +14904,7 @@ var init_channels = __esm({
14784
14904
  const view2 = {
14785
14905
  channel: record.channel,
14786
14906
  payee: record.payee,
14787
- receiver: getAddress11(record.config.receiver),
14907
+ receiver: getAddress12(record.config.receiver),
14788
14908
  gracePeriod: record.gracePeriod,
14789
14909
  voucherKey: record.voucherKey,
14790
14910
  closeRequested: record.closeRequested,
@@ -14821,11 +14941,11 @@ var init_channels = __esm({
14821
14941
  const cfg = config ?? await recoverChannelConfig(this.#client, this.#config, vault, channel);
14822
14942
  if (cfg === null) throw new Error(`channel ${channel} was not opened by this agent's vault`);
14823
14943
  const d = this.#config.deployment;
14824
- if (channelId(cfg, this.#config.chainId, d.batchSettlement).toLowerCase() !== channel || getAddress11(cfg.payer) !== vault || getAddress11(cfg.token) !== d.token) {
14944
+ if (channelId(cfg, this.#config.chainId, d.batchSettlement).toLowerCase() !== channel || getAddress12(cfg.payer) !== vault || getAddress12(cfg.token) !== d.token) {
14825
14945
  throw new Error(`channel ${channel} was not opened by this agent's vault`);
14826
14946
  }
14827
14947
  const voucher = this.#keys.channelVoucherSigner(this.#config.chainId, d.token, cfg.salt);
14828
- if (voucher.address !== getAddress11(cfg.payerAuthorizer)) throw new Error(`channel ${channel} uses a voucher key this machine cannot derive`);
14948
+ if (voucher.address !== getAddress12(cfg.payerAuthorizer)) throw new Error(`channel ${channel} uses a voucher key this machine cannot derive`);
14829
14949
  const ch = await this.#read(channel);
14830
14950
  const open3 = {
14831
14951
  payerAuthorizer: cfg.payerAuthorizer,
@@ -14903,7 +15023,7 @@ var init_channels = __esm({
14903
15023
  sent,
14904
15024
  ...extra
14905
15025
  });
14906
- if (getAddress11(record.config.payer) !== getAddress11(record.vault)) throw new Error(`channel ${channel} does not belong to this vault's record`);
15026
+ if (getAddress12(record.config.payer) !== getAddress12(record.vault)) throw new Error(`channel ${channel} does not belong to this vault's record`);
14907
15027
  for (let i = 0; i < MAX_STEPS_PER_CALL; i++) {
14908
15028
  const ch = await this.#read(channel);
14909
15029
  const status = channelStatus(ch);
@@ -14918,7 +15038,7 @@ var init_channels = __esm({
14918
15038
  try {
14919
15039
  if (status === "open") {
14920
15040
  const voucher = this.voucherSigner(record);
14921
- if (voucher.address !== getAddress11(record.config.payerAuthorizer)) throw new Error(`the voucher key of channel ${channel} could not be derived`);
15041
+ if (voucher.address !== getAddress12(record.config.payerAuthorizer)) throw new Error(`the voucher key of channel ${channel} could not be derived`);
14922
15042
  const amount = ch.balance - ch.totalClaimed;
14923
15043
  const closeNonce = await this.#client.readContract({ address: record.vault, abi: ostraVaultAbi, functionName: "closeNonce", args: [channel] });
14924
15044
  const deadline = await chainTime(this.#client) + CLOSE_REQUEST_TTL_SECONDS;
@@ -15108,14 +15228,14 @@ var init_passkey = __esm({
15108
15228
  });
15109
15229
 
15110
15230
  // ../core/src/keys.ts
15111
- import { bytesToHex as bytesToHex9, getAddress as getAddress12, hexToBytes as hexToBytes7 } from "viem";
15231
+ import { bytesToHex as bytesToHex9, getAddress as getAddress13, hexToBytes as hexToBytes7 } from "viem";
15112
15232
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
15113
15233
  function chainBytes(chainId) {
15114
15234
  if (!Number.isSafeInteger(chainId) || chainId <= 0) throw new RangeError("chainId must be a positive integer");
15115
15235
  return hexToBytes7(`0x${BigInt(chainId).toString(16).padStart(64, "0")}`);
15116
15236
  }
15117
15237
  function tokenBytes(token) {
15118
- return hexToBytes7(getAddress12(token));
15238
+ return hexToBytes7(getAddress13(token));
15119
15239
  }
15120
15240
  function u64Be(value) {
15121
15241
  if (value < 0n || value > U64_MAX5) throw new RangeError("index out of range");
@@ -15207,7 +15327,7 @@ var init_keys = __esm({
15207
15327
  * payer and the WOTS store stay the agent's.
15208
15328
  */
15209
15329
  async forPrivateVault(chainId, token, index) {
15210
- const folder = this.#root.folder(PRIVATE_DIR).folder(`${chainId}-${getAddress12(token)}-${index}`);
15330
+ const folder = this.#root.folder(PRIVATE_DIR).folder(`${chainId}-${getAddress13(token)}-${index}`);
15211
15331
  await folder.ensure();
15212
15332
  return new _Keys({ master: this.#master, feePayer: this.feePayer, store: this.store, folder, root: this.#root, backupExported: this.#backupExported, escrow: this.escrow });
15213
15333
  }
@@ -15218,7 +15338,7 @@ var init_keys = __esm({
15218
15338
  * agent's folder (ShieldService takes the agent's keys).
15219
15339
  */
15220
15340
  async forCreatorVault(chainId, token) {
15221
- const folder = this.#root.folder(CREATOR_DIR).folder(`${chainId}-${getAddress12(token)}`);
15341
+ const folder = this.#root.folder(CREATOR_DIR).folder(`${chainId}-${getAddress13(token)}`);
15222
15342
  await folder.ensure();
15223
15343
  return new _Keys({ master: this.#master, feePayer: this.feePayer, store: this.store, folder, root: this.#root, backupExported: this.#backupExported, escrow: this.escrow });
15224
15344
  }
@@ -15299,13 +15419,13 @@ var init_keys = __esm({
15299
15419
  });
15300
15420
 
15301
15421
  // ../core/src/pools.ts
15302
- import { getAddress as getAddress13, isAddress as isAddress7, zeroAddress as zeroAddress6 } from "viem";
15422
+ import { getAddress as getAddress14, isAddress as isAddress7, zeroAddress as zeroAddress7 } from "viem";
15303
15423
  function iso(seconds) {
15304
15424
  return new Date(Number(seconds) * 1e3).toISOString();
15305
15425
  }
15306
15426
  function launchedTokenAddress(text2, what = "token") {
15307
15427
  if (!isAddress7(text2, { strict: false })) throw new RangeError(`${what} ${text2} is not an address`);
15308
- return getAddress13(text2);
15428
+ return getAddress14(text2);
15309
15429
  }
15310
15430
  async function readLaunchedPool(client, lp, tokenText) {
15311
15431
  const token = launchedTokenAddress(tokenText);
@@ -15318,13 +15438,13 @@ async function readLaunchedPool(client, lp, tokenText) {
15318
15438
  return { token, id, key, config };
15319
15439
  }
15320
15440
  function quoteOfPool(pool) {
15321
- return getAddress13(pool.config.tokenIs0 ? pool.key.currency1 : pool.key.currency0);
15441
+ return getAddress14(pool.config.tokenIs0 ? pool.key.currency1 : pool.key.currency0);
15322
15442
  }
15323
15443
  async function readPoolView(client, lp, tokenText) {
15324
15444
  const pool = await readLaunchedPool(client, lp, tokenText);
15325
15445
  const c = pool.config;
15326
15446
  const quote = quoteOfPool(pool);
15327
- const quoteDecimals = quote === zeroAddress6 ? 18 : await tokenDecimals(client, quote);
15447
+ const quoteDecimals = quote === zeroAddress7 ? 18 : await tokenDecimals(client, quote);
15328
15448
  const read = (address4, abi, functionName, args = []) => client.readContract({ address: address4, abi, functionName, args });
15329
15449
  const [state, rates, tick, name, symbol, round, available, nextAmount, shares, maxWallet, limitsReleased] = await Promise.all([
15330
15450
  read(lp.hook, ostraHookAbi, "poolState", [pool.id]),
@@ -15352,7 +15472,7 @@ async function readPoolView(client, lp, tokenText) {
15352
15472
  name,
15353
15473
  symbol,
15354
15474
  quote,
15355
- creatorVault: getAddress13(c.creatorVault),
15475
+ creatorVault: getAddress14(c.creatorVault),
15356
15476
  graduated: state.graduated,
15357
15477
  tokensSold: formatUnitsAmount(state.tokensSold < 0n ? 0n : state.tokensSold, 18),
15358
15478
  quoteRaised: fmtQuote(state.quoteRaised),
@@ -15364,7 +15484,7 @@ async function readPoolView(client, lp, tokenText) {
15364
15484
  sellTaxBps: Number(rates[1]),
15365
15485
  antiSnipeBps: Number(rates[2]),
15366
15486
  maxBuy: c.maxBuy === 0n ? null : formatUnitsAmount(c.maxBuy, 18),
15367
- dividendToken: getAddress13(c.dividendToken) === zeroAddress6 ? null : getAddress13(c.dividendToken),
15487
+ dividendToken: getAddress14(c.dividendToken) === zeroAddress7 ? null : getAddress14(c.dividendToken),
15368
15488
  launchedAt: iso(BigInt(c.launchedAt)),
15369
15489
  airdrop: {
15370
15490
  round: round.number,
@@ -15375,7 +15495,7 @@ async function readPoolView(client, lp, tokenText) {
15375
15495
  available: formatUnitsAmount(available, 18),
15376
15496
  nextRoundAmount: formatUnitsAmount(nextAmount, 18)
15377
15497
  },
15378
- creatorShares: shares[0].map((recipient, i) => ({ recipient: getAddress13(recipient), bps: Number(shares[1][i]) })),
15498
+ creatorShares: shares[0].map((recipient, i) => ({ recipient: getAddress14(recipient), bps: Number(shares[1][i]) })),
15379
15499
  rules: {
15380
15500
  quoteDecimals,
15381
15501
  supply: formatUnitsAmount(state.supply, 18),
@@ -15516,7 +15636,7 @@ var init_publish = __esm({
15516
15636
  });
15517
15637
 
15518
15638
  // ../core/src/pending.ts
15519
- import { zeroAddress as zeroAddress7 } from "viem";
15639
+ import { zeroAddress as zeroAddress8 } from "viem";
15520
15640
  function decodeVaultSpendHeader(message) {
15521
15641
  const s = decodeVaultSpendMessage(message);
15522
15642
  return { chainId: s.chainId, vault: s.vault, nonce: s.nonce, nextKeyRoot: s.nextKeyRoot, action: s.action };
@@ -15537,7 +15657,7 @@ function decodeVaultOpenChannelMessage(message) {
15537
15657
  function decodeVaultSetCrankKeyMessage(message) {
15538
15658
  const { header, params } = decodeAs(message, VAULT_ACTION.SET_CRANK_KEY);
15539
15659
  const crank = decodeSetCrankParams(params);
15540
- return { ...header, crank: crank === zeroAddress7 ? null : crank };
15660
+ return { ...header, crank: crank === zeroAddress8 ? null : crank };
15541
15661
  }
15542
15662
  var init_pending = __esm({
15543
15663
  "../core/src/pending.ts"() {
@@ -15548,7 +15668,7 @@ var init_pending = __esm({
15548
15668
  });
15549
15669
 
15550
15670
  // ../core/src/vault.ts
15551
- import { bytesToHex as bytesToHex10, encodeAbiParameters as encodeAbiParameters5, encodeFunctionData as encodeFunctionData7, getAddress as getAddress14, isAddress as isAddress8, keccak256 as keccak25610, zeroAddress as zeroAddress8 } from "viem";
15671
+ import { bytesToHex as bytesToHex10, encodeAbiParameters as encodeAbiParameters5, encodeFunctionData as encodeFunctionData7, getAddress as getAddress15, isAddress as isAddress8, keccak256 as keccak25610, zeroAddress as zeroAddress9 } from "viem";
15552
15672
  function spendGas(calls) {
15553
15673
  return calls.reduce((gas, c) => gas + reservedSwapGas(c), VAULT_SPEND_GAS);
15554
15674
  }
@@ -15591,10 +15711,11 @@ var init_vault = __esm({
15591
15711
  #onChannelOpening = null;
15592
15712
  constructor(client, transport, config, keys, sender, kind = "buyer", privateIndex = null, vaultToken = null) {
15593
15713
  if (kind === "private" !== (privateIndex !== null)) throw new Error("a private vault, and only a private vault, has an index");
15594
- if (kind === "token" !== (vaultToken !== null)) throw new Error("a token vault, and only a token vault, names its token");
15595
- if (vaultToken !== null && getAddress14(vaultToken) === config.deployment.token) throw new Error(`the vault in ${vaultToken} is the buyer vault, not a token vault`);
15714
+ if (kind === "token" && vaultToken === null) throw new Error("a token vault names its token");
15715
+ if (vaultToken !== null && kind !== "token" && kind !== "creator") throw new Error("only a token vault or a creator vault names its token");
15716
+ if (vaultToken !== null && getAddress15(vaultToken) === config.deployment.token) throw new Error(`the vault in ${vaultToken} is the ${kind === "creator" ? "USDG creator vault, named without a token" : "buyer vault, not a token vault"}`);
15596
15717
  this.kind = kind;
15597
- this.token = vaultToken === null ? config.deployment.token : getAddress14(vaultToken);
15718
+ this.token = vaultToken === null ? config.deployment.token : getAddress15(vaultToken);
15598
15719
  this.privateIndex = privateIndex;
15599
15720
  this.#client = client;
15600
15721
  this.#transport = transport;
@@ -15603,17 +15724,21 @@ var init_vault = __esm({
15603
15724
  this.#sender = sender;
15604
15725
  const token = this.token;
15605
15726
  this.#keychain = kind === "buyer" || kind === "token" ? keys.vaultKeychain(config.chainId, token) : kind === "seller" ? keys.sellerKeychain(config.chainId, token) : kind === "creator" ? keys.creatorKeychain(config.chainId, token) : keys.privateVaultKeychain(config.chainId, token, privateIndex);
15606
- this.#initCrank = kind === "seller" ? null : zeroAddress8;
15727
+ this.#initCrank = kind === "seller" ? null : zeroAddress9;
15607
15728
  }
15608
15729
  /** Seller vault: the crank it is (or will be) created with, which is part of its address. */
15609
15730
  setInitCrank(crank) {
15610
15731
  if (this.kind !== "seller") throw new Error(`the ${this.kind} vault is created without a crank`);
15611
15732
  if (!isAddress8(crank, { strict: false })) throw new RangeError("a crank is an address");
15612
- this.#initCrank = getAddress14(crank);
15733
+ this.#initCrank = getAddress15(crank);
15734
+ }
15735
+ /** A vault in the deployment's token (USDG); else a token vault or a pair's creator vault, paid for by the buyer vault. */
15736
+ #inUsdg() {
15737
+ return this.token === this.#config.deployment.token;
15613
15738
  }
15614
15739
  #missing() {
15615
15740
  if (this.kind === "private") return `private vault ${this.privateIndex} does not exist yet; unshield creates it`;
15616
- if (this.kind === "creator") return "this agent has no creator vault yet; open it before a launch names it";
15741
+ if (this.kind === "creator") return `this agent has no creator vault${this.#inUsdg() ? "" : ` in ${this.token}`} yet; open it before a launch names it`;
15617
15742
  if (this.kind === "token") return `this agent has no vault in ${this.token} yet; buying the token creates it`;
15618
15743
  return this.kind === "buyer" ? "this agent has no vault yet; call open_vault first" : "this agent has no seller vault yet; call accept_payments first";
15619
15744
  }
@@ -15645,10 +15770,10 @@ var init_vault = __esm({
15645
15770
  return null;
15646
15771
  }
15647
15772
  const r = JSON.parse(text2);
15648
- if (r.chainId !== this.#config.chainId || typeof r.token !== "string" || getAddress14(r.token) !== this.token || typeof r.vault !== "string" || !isAddress8(r.vault, { strict: false })) {
15773
+ if (r.chainId !== this.#config.chainId || typeof r.token !== "string" || getAddress15(r.token) !== this.token || typeof r.vault !== "string" || !isAddress8(r.vault, { strict: false })) {
15649
15774
  throw new Error(`${this.#adoptedName()} is not a recovered vault of chain ${this.#config.chainId} in ${this.token}`);
15650
15775
  }
15651
- this.#adopted = getAddress14(r.vault);
15776
+ this.#adopted = getAddress15(r.vault);
15652
15777
  return this.#adopted;
15653
15778
  }
15654
15779
  async vaultAddress() {
@@ -15679,6 +15804,7 @@ var init_vault = __esm({
15679
15804
  }
15680
15805
  async #checkToken() {
15681
15806
  if (this.kind === "token") throw new Error(`the vault in ${this.token} spends only through its token operations (sell, transfer, claim dividends)`);
15807
+ if (!this.#inUsdg()) throw new Error(`the creator vault in ${this.token} spends only through the launchpad (launch, pool settings, creator_withdraw)`);
15682
15808
  const decimals = await tokenDecimals(this.#client, this.#config.deployment.token);
15683
15809
  if (decimals !== TOKEN_DECIMALS) throw new Error(`token ${this.#config.deployment.token} has ${decimals} decimals; this package supports ${TOKEN_DECIMALS}`);
15684
15810
  }
@@ -15720,7 +15846,7 @@ var init_vault = __esm({
15720
15846
  }
15721
15847
  /** The vaults that open relayer channels and pay their own requests: the buyer, a private and the creator vault (not the seller vault nor a token vault). */
15722
15848
  #paysRelayer() {
15723
- return this.kind !== "seller" && this.kind !== "token";
15849
+ return this.kind !== "seller" && this.kind !== "token" && this.#inUsdg();
15724
15850
  }
15725
15851
  #options(extra = {}) {
15726
15852
  const openChannel = this.#opener();
@@ -15739,7 +15865,7 @@ var init_vault = __esm({
15739
15865
  open() {
15740
15866
  return this.#serial(async () => {
15741
15867
  if (this.kind === "private") throw new Error("a private vault is created by unshield, together with the leaf spent into it");
15742
- if (this.kind !== "token") await this.#checkToken();
15868
+ if (this.kind !== "token" && this.#inUsdg()) await this.#checkToken();
15743
15869
  const vault = await this.vaultAddress();
15744
15870
  const token = this.token;
15745
15871
  const softLimit = formatTokenAmount(VAULT_SOFT_LIMIT);
@@ -15750,9 +15876,9 @@ var init_vault = __esm({
15750
15876
  return found;
15751
15877
  }
15752
15878
  await this.requireEth(CREATE_VAULT_GAS, "open the vault");
15753
- const txHash = await this.#sender.send([await this.createCall()], this.#options({ prepay: this.kind === "seller" || this.kind === "token" }));
15879
+ const txHash = await this.#sender.send([await this.createCall()], this.#options({ prepay: this.kind === "seller" || !this.#paysRelayer() }));
15754
15880
  const result = { vault, token, nonce: "0", created: true, txHash, softLimit };
15755
- if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator")) {
15881
+ if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator") && this.#paysRelayer()) {
15756
15882
  result.note = "the Ostra relayer paid the vault's creation; the vault owes that price, paid with the first voucher of its channel to the relayer, which this server opens from the vault once it holds tokens (nothing else is relayed for the vault until then)";
15757
15883
  }
15758
15884
  if (!await this.#keys.backupExported()) result.backupReminder = BACKUP_REMINDER;
@@ -15803,6 +15929,7 @@ var init_vault = __esm({
15803
15929
  status() {
15804
15930
  return this.#serial(async () => {
15805
15931
  if (this.kind === "token") throw new Error(`the vault in ${this.token} is read with its token's tools (token_vault)`);
15932
+ if (!this.#inUsdg()) throw new Error(`the creator vault in ${this.token} is read with the launchpad's pair tools`);
15806
15933
  const vault = await this.vaultAddress();
15807
15934
  const token = this.#config.deployment.token;
15808
15935
  const v = await this.#read(vault);
@@ -15872,7 +15999,7 @@ var init_vault = __esm({
15872
15999
  throw new Error("the stored signature does not belong to this vault and nonce");
15873
16000
  }
15874
16001
  const token = this.#config.deployment.token;
15875
- if (header.action === VAULT_ACTION.TRANSFER && this.kind !== "token") {
16002
+ if (header.action === VAULT_ACTION.TRANSFER && this.#inUsdg()) {
15876
16003
  const t = decodeVaultTransferMessage(message);
15877
16004
  await this.#notFrozen(t.to, "the destination of a withdrawal signed earlier,");
15878
16005
  const params = transferParams({ to: t.to, amount: t.amount });
@@ -15890,7 +16017,7 @@ var init_vault = __esm({
15890
16017
  }
15891
16018
  if (header.action === VAULT_ACTION.SET_CRANK_KEY) {
15892
16019
  const { crank, nextKeyRoot } = decodeVaultSetCrankKeyMessage(message);
15893
- const params = setCrankParams(crank ?? zeroAddress8);
16020
+ const params = setCrankParams(crank ?? zeroAddress9);
15894
16021
  const txHash2 = await sender.send([spendCall(vault, { action: VAULT_ACTION.SET_CRANK_KEY, nextKeyRoot, params, signature })], { reservation });
15895
16022
  return { operation: "set_crank", txHash: txHash2, crank };
15896
16023
  }
@@ -15898,7 +16025,7 @@ var init_vault = __esm({
15898
16025
  const s2 = decodeVaultSpendMessage(message);
15899
16026
  const { devBuy, launchHash: hash } = decodeDevBuyParams(s2.params);
15900
16027
  const lp = this.#config.deployment.launchpad;
15901
- if (lp === void 0 || getAddress14(devBuy) !== lp.devBuy) throw new Error(`the vault signed a DevBuy for ${devBuy}, not this deployment's OstraDevBuy`);
16028
+ if (lp === void 0 || getAddress15(devBuy) !== lp.devBuy) throw new Error(`the vault signed a DevBuy for ${devBuy}, not this deployment's OstraDevBuy`);
15902
16029
  const l = await this.#signedLaunch(vault, v.nonce, hash);
15903
16030
  const call = { to: lp.devBuy, data: launchFromVaultData(l.launch, l.dev, { nextKeyRoot: s2.nextKeyRoot, signature }) };
15904
16031
  const txHash2 = await sender.send([call], { reservation, value: LAUNCHPAD.FEE_WEI });
@@ -16001,8 +16128,8 @@ the signed ${op.what} is kept; the next call on this vault sends it again before
16001
16128
  const amount = parseTokenAmount(amountText);
16002
16129
  if (amount === 0n) throw new RangeError("amount must be greater than zero");
16003
16130
  if (!isAddress8(destinationText, { strict: false })) throw new RangeError(`${destinationText} is not an address`);
16004
- const destination = getAddress14(destinationText);
16005
- if (destination === zeroAddress8) throw new RangeError("the destination must not be the zero address");
16131
+ const destination = getAddress15(destinationText);
16132
+ if (destination === zeroAddress9) throw new RangeError("the destination must not be the zero address");
16006
16133
  await this.#checkToken();
16007
16134
  const vault = await this.vaultAddress();
16008
16135
  if (await this.#read(vault) === null) throw new Error(this.#missing());
@@ -16052,7 +16179,7 @@ the signed withdrawal is kept; the next withdraw or open_channel call sends it a
16052
16179
  if (this.kind === "seller") throw new Error("channels are not opened from the seller vault");
16053
16180
  await this.#checkToken();
16054
16181
  const vault = await this.vaultAddress();
16055
- if (getAddress14(open3.receiverAuthorizer) === vault || getAddress14(open3.receiver) === vault) throw new Error("the channel would pay this agent's own vault");
16182
+ if (getAddress15(open3.receiverAuthorizer) === vault || getAddress15(open3.receiver) === vault) throw new Error("the channel would pay this agent's own vault");
16056
16183
  if (await this.#read(vault) === null) throw new Error(this.#missing());
16057
16184
  await this.requireEth(VAULT_SPEND_GAS, "send the vault operation");
16058
16185
  return this.#paidSpend(this.#sender, async (sender, reservation) => {
@@ -16126,8 +16253,8 @@ the signed channel open is kept; the next open_channel or withdraw call sends it
16126
16253
  setCrankKey(crankText, via) {
16127
16254
  return this.#serial(async () => {
16128
16255
  if (!isAddress8(crankText, { strict: false })) throw new RangeError("a crank is an address");
16129
- const crank = getAddress14(crankText);
16130
- const target = crank === zeroAddress8 ? null : crank;
16256
+ const crank = getAddress15(crankText);
16257
+ const target = crank === zeroAddress9 ? null : crank;
16131
16258
  const vault = await this.vaultAddress();
16132
16259
  if (await this.#read(vault) === null) throw new Error(this.#missing());
16133
16260
  const chosen = via ?? this.#sender;
@@ -16214,8 +16341,8 @@ the signed operation is kept; the next call on this vault sends it again before
16214
16341
  /** Adds (enabled) or removes a guardian of this vault: another Ostra vault keeps the recovery post-quantum. */
16215
16342
  async setGuardian(guardianText, enabled, via) {
16216
16343
  if (!isAddress8(guardianText, { strict: false })) throw new RangeError(`${guardianText} is not an address`);
16217
- const guardian = getAddress14(guardianText);
16218
- if (guardian === zeroAddress8) throw new RangeError("a guardian is not the zero address");
16344
+ const guardian = getAddress15(guardianText);
16345
+ if (guardian === zeroAddress9) throw new RangeError("a guardian is not the zero address");
16219
16346
  return this.#recoverySpend(
16220
16347
  (v) => signVaultSetGuardian(this.#keychain, v, { chainId: this.#config.chainId, guardian, enabled }),
16221
16348
  async (_v, vault) => {
@@ -16260,7 +16387,7 @@ the signed operation is kept; the next call on this vault sends it again before
16260
16387
  /** As a guardian (this vault), supports moving `vaultText` to `newKeyRootText` (the key root its owner's new device gave). */
16261
16388
  async supportRecovery(vaultText, newKeyRootText, via) {
16262
16389
  if (!isAddress8(vaultText, { strict: false })) throw new RangeError(`${vaultText} is not an address`);
16263
- const target = getAddress14(vaultText);
16390
+ const target = getAddress15(vaultText);
16264
16391
  if (!/^0x[0-9a-fA-F]{64}$/.test(newKeyRootText) || /^0x0{64}$/.test(newKeyRootText)) throw new RangeError("the new key root is 32 bytes of 0x hex, not zero");
16265
16392
  const newKeyRoot = newKeyRootText.toLowerCase();
16266
16393
  return this.#recoverySpend(
@@ -16289,7 +16416,7 @@ the signed operation is kept; the next call on this vault sends it again before
16289
16416
  return this.#serial(async () => {
16290
16417
  if (this.kind === "private") throw new Error("a private vault is recovered with the seed that unshielded it");
16291
16418
  if (!isAddress8(vaultText, { strict: false })) throw new RangeError(`${vaultText} is not an address`);
16292
- const vault = getAddress14(vaultText);
16419
+ const vault = getAddress15(vaultText);
16293
16420
  const v = await readVault(this.#client, this.#config.deployment, vault);
16294
16421
  if (v === null || v.token !== this.token) throw new Error(`${vault} is not an Ostra vault in ${this.token}`);
16295
16422
  const adopted = await this.#adoptedVault();
@@ -16347,7 +16474,7 @@ the signed operation is kept; the next call on this vault sends it again before
16347
16474
  });
16348
16475
 
16349
16476
  // ../core/src/launchpad.ts
16350
- import { bytesToHex as bytesToHex11, decodeEventLog as decodeEventLog3, encodeFunctionData as encodeFunctionData8, getAddress as getAddress15, isAddress as isAddress9, parseAbiItem as parseAbiItem2, zeroAddress as zeroAddress9 } from "viem";
16477
+ import { bytesToHex as bytesToHex11, decodeEventLog as decodeEventLog3, encodeFunctionData as encodeFunctionData8, getAddress as getAddress16, isAddress as isAddress9, parseAbiItem as parseAbiItem2, zeroAddress as zeroAddress10 } from "viem";
16351
16478
  async function readIpfsJson(uri2) {
16352
16479
  if (uri2 === null || !uri2.startsWith("ipfs://")) return null;
16353
16480
  const path = uri2.slice("ipfs://".length).replace(/^ipfs\//, "");
@@ -16384,7 +16511,7 @@ function nowIso(seconds) {
16384
16511
  }
16385
16512
  function address2(text2, what) {
16386
16513
  if (!isAddress9(text2, { strict: false })) throw new RangeError(`${what} ${text2} is not an address`);
16387
- return getAddress15(text2);
16514
+ return getAddress16(text2);
16388
16515
  }
16389
16516
  function slippage(bps) {
16390
16517
  const s = bps ?? QUOTE_DEFAULT_SLIPPAGE_BPS;
@@ -16398,7 +16525,7 @@ function simulatedGas(gasUsed) {
16398
16525
  const g = gasUsed * 5n / 4n + SPEND_GAS_HEADROOM;
16399
16526
  return g > VAULT_SPEND_GAS ? g : VAULT_SPEND_GAS;
16400
16527
  }
16401
- var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, SPEND_GAS_HEADROOM, DAY, TOKEN_LIST_MAX, IPFS_GATEWAY, METADATA_MAX_BYTES, METADATA_TIMEOUT_MS, launchedEvent, POOL_SETTING_NAMES, LaunchpadService;
16528
+ var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM, DAY, TOKEN_LIST_MAX, IPFS_GATEWAY, METADATA_MAX_BYTES, METADATA_TIMEOUT_MS, launchedEvent, POOL_SETTING_NAMES, LaunchpadService;
16402
16529
  var init_launchpad = __esm({
16403
16530
  "../core/src/launchpad.ts"() {
16404
16531
  "use strict";
@@ -16415,6 +16542,7 @@ var init_launchpad = __esm({
16415
16542
  LAUNCHPAD_DIR = "launchpad";
16416
16543
  CREATE_VAULT_GAS2 = 300000n;
16417
16544
  LAUNCH_GAS_HEADROOM = 300000n;
16545
+ PAIRS_READ_CONCURRENCY = 6;
16418
16546
  SPEND_GAS_HEADROOM = 100000n;
16419
16547
  DAY = 86400;
16420
16548
  TOKEN_LIST_MAX = 50;
@@ -16445,6 +16573,8 @@ var init_launchpad = __esm({
16445
16573
  #creatorSender;
16446
16574
  #folder;
16447
16575
  #tokenVaults = /* @__PURE__ */ new Map();
16576
+ /** A pair's creator vault (not USDG's), one per token: created once, so its operations share one queue. */
16577
+ #pairCreators = /* @__PURE__ */ new Map();
16448
16578
  constructor(p) {
16449
16579
  if (p.creator.kind !== "creator" || p.buyer.kind !== "buyer") throw new Error("the launchpad takes the agent's buyer and creator vaults");
16450
16580
  this.#client = p.client;
@@ -16476,6 +16606,87 @@ var init_launchpad = __esm({
16476
16606
  }
16477
16607
  return v;
16478
16608
  }
16609
+ /**
16610
+ * Refuses to pay `paid` to an Ostra vault (of this deployment) in another
16611
+ * token: a vault only ever moves its own token, so what it is paid in
16612
+ * another stays there for good. A vault not created yet cannot be told
16613
+ * from any other address.
16614
+ */
16615
+ async #notStranded(to, paid, what) {
16616
+ const v = await readVault(this.#client, this.#config.deployment, to);
16617
+ if (v !== null && v.token !== getAddress16(paid)) {
16618
+ throw new Error(`${to} is an Ostra vault in ${v.token}: a vault only moves its own token, so ${what} in ${getAddress16(paid)} would be stuck there for good; nothing was signed`);
16619
+ }
16620
+ }
16621
+ /** The token a pair's vaults hold: the pair's own; WETH for ETH. */
16622
+ #pairToken(pair3) {
16623
+ return pair3.kind === "eth" ? WETH : pair3.address;
16624
+ }
16625
+ /**
16626
+ * The agent's creator vault for `pairText` and the sender of its operations:
16627
+ * for USDG the USDG creator vault and its own relayer client; for a stock
16628
+ * token or ETH, the creator vault in that token (WETH for ETH), paid for by
16629
+ * the buyer vault.
16630
+ */
16631
+ async pairCreator(pairText) {
16632
+ const pair3 = launchPair(pairText);
16633
+ if (pair3.kind === "usdg") return { pair: pair3, vault: this.#creator, sender: this.#creatorSender };
16634
+ const token = this.#pairToken(pair3);
16635
+ let made = this.#pairCreators.get(token);
16636
+ if (made === void 0) {
16637
+ made = this.#keys.forCreatorVault(this.#config.chainId, token).then((keys) => new VaultService(this.#client, this.#transport, this.#config, keys, this.#buyerSender, "creator", null, token));
16638
+ this.#pairCreators.set(token, made);
16639
+ made.catch(() => {
16640
+ if (this.#pairCreators.get(token) === made) this.#pairCreators.delete(token);
16641
+ });
16642
+ }
16643
+ return { pair: pair3, vault: await made, sender: this.#buyerSender };
16644
+ }
16645
+ /**
16646
+ * The pairs a launch can take, with this agent's vaults for each: its
16647
+ * creator vault (what a launch's dev buy is paid from) and its vault of the
16648
+ * pair's token (what it buys that pair's pools with); with `prices`, each
16649
+ * pair's USD price now.
16650
+ */
16651
+ async pairs(a = {}) {
16652
+ const out = new Array(LAUNCH_PAIRS.length);
16653
+ let next = 0;
16654
+ const worker = async () => {
16655
+ while (next < LAUNCH_PAIRS.length) {
16656
+ const i = next++;
16657
+ out[i] = await this.#pairRow(LAUNCH_PAIRS[i], a.prices === true);
16658
+ }
16659
+ };
16660
+ await Promise.all(Array.from({ length: PAIRS_READ_CONCURRENCY }, worker));
16661
+ return out;
16662
+ }
16663
+ /** One row of pairs(). */
16664
+ async #pairRow(pair3, prices) {
16665
+ const { vault } = await this.pairCreator(pair3.symbol);
16666
+ const s = await vault.state();
16667
+ const token = this.#pairToken(pair3);
16668
+ const row = {
16669
+ symbol: pair3.symbol,
16670
+ name: pair3.name,
16671
+ kind: pair3.kind,
16672
+ token: pair3.address,
16673
+ decimals: pair3.decimals,
16674
+ creatorVault: s.vault,
16675
+ creatorBalance: formatUnitsAmount(s.balance, pair3.decimals),
16676
+ tradingVault: pair3.kind === "eth" ? null : await this.tokenVault(token).vaultAddress()
16677
+ };
16678
+ if (prices) {
16679
+ try {
16680
+ const price = await readPairUsdPrice(this.#client, pair3);
16681
+ row.priceUsd = formatUnitsAmount(price.priceE8, 8);
16682
+ row.priceAt = new Date(price.updatedAt * 1e3).toISOString();
16683
+ } catch {
16684
+ row.priceUsd = null;
16685
+ row.priceAt = null;
16686
+ }
16687
+ }
16688
+ return row;
16689
+ }
16479
16690
  // ---------------------------------------------------------------------------
16480
16691
  // Reads
16481
16692
  // ---------------------------------------------------------------------------
@@ -16496,9 +16707,9 @@ var init_launchpad = __esm({
16496
16707
  const pool = await this.#pool(tokenText);
16497
16708
  const s = await this.tokenVault(pool.token).state();
16498
16709
  let dividends = null;
16499
- if (getAddress15(pool.config.dividendToken) !== zeroAddress9) {
16710
+ if (getAddress16(pool.config.dividendToken) !== zeroAddress10) {
16500
16711
  const owed = await this.#client.readContract({ address: pool.token, abi: ostraTokenAbi, functionName: "dividendOf", args: [s.vault] });
16501
- const decimals = getAddress15(pool.config.dividendToken) === this.#usdg() ? TOKEN_DECIMALS : await tokenDecimals(this.#client, getAddress15(pool.config.dividendToken));
16712
+ const decimals = getAddress16(pool.config.dividendToken) === this.#usdg() ? TOKEN_DECIMALS : await tokenDecimals(this.#client, getAddress16(pool.config.dividendToken));
16502
16713
  dividends = formatUnitsAmount(owed, decimals);
16503
16714
  }
16504
16715
  return { token: pool.token, vault: s.vault, exists: s.exists, balance: formatUnitsAmount(s.balance, 18), dividends, pendingOperation: s.pendingAction };
@@ -16520,38 +16731,44 @@ var init_launchpad = __esm({
16520
16731
  async #swapQuote(a, create) {
16521
16732
  const pool = await this.#pool(a.token);
16522
16733
  const quote = this.#quoteOf(pool);
16523
- if (quote !== this.#usdg()) throw new Error(`the pool of ${pool.token} trades against ${quote === zeroAddress9 ? "ETH" : quote}; this agent trades USDG pools`);
16734
+ if (quote === zeroAddress10) {
16735
+ throw new Error(`the pool of ${pool.token} trades against ETH: an Ostra vault holds one ERC-20 and cannot pay or take native ETH, so this pool is traded from a wallet; nothing was signed`);
16736
+ }
16737
+ const pair3 = findLaunchPair(quote);
16738
+ if (pair3 === null) throw new Error(`the pool of ${pool.token} trades against ${quote}, not a pair Ostra offers (USDG, ETH or a listed stock token); nothing was signed`);
16524
16739
  const referrer = a.referrer === void 0 ? void 0 : address2(a.referrer, "referrer");
16525
- const usdgIs0 = getAddress15(pool.key.currency0) === this.#usdg();
16740
+ const quoteIs0 = getAddress16(pool.key.currency0) === quote;
16526
16741
  const buying = a.side === "buy";
16527
- const from = buying ? this.#buyer : this.tokenVault(pool.token);
16742
+ const quoteVault = this.tokenVault(quote);
16743
+ const from = buying ? quoteVault : this.tokenVault(pool.token);
16528
16744
  const fromVault = await from.vaultAddress();
16529
- const to = buying ? await this.tokenVault(pool.token).vaultAddress() : await this.#buyer.vaultAddress();
16530
- const amountIn = buying ? parseTokenAmount(a.amount) : parseUnitsAmount(a.amount, 18);
16745
+ const to = buying ? await this.tokenVault(pool.token).vaultAddress() : await quoteVault.vaultAddress();
16746
+ const amountIn = buying ? parseUnitsAmount(a.amount, pair3.decimals) : parseUnitsAmount(a.amount, 18);
16531
16747
  if (amountIn === 0n) throw new RangeError("amount must be greater than zero");
16532
16748
  const state = await from.state();
16533
- if (!state.exists && buying) throw new Error("this agent has no vault yet; call open_vault first");
16749
+ if (!state.exists && from === this.#buyer) throw new Error("this agent has no vault yet; call open_vault first");
16534
16750
  if (state.balance < amountIn) {
16535
- throw new Error(`the ${buying ? "vault" : `vault in ${pool.token}`} holds ${buying ? formatTokenAmount(state.balance) : formatUnitsAmount(state.balance, 18)}, less than ${a.amount}`);
16751
+ const what = from === this.#buyer ? "vault" : `vault in ${buying ? pair3.symbol : pool.token}`;
16752
+ throw new Error(`the ${what} holds ${formatUnitsAmount(state.balance, buying ? pair3.decimals : 18)}, less than ${a.amount}`);
16536
16753
  }
16537
16754
  const sender = this.#buyerSender;
16538
16755
  if (!state.exists) {
16539
- if (!create) throw new Error(`the vault in ${pool.token} holds tokens but is not created yet; sell creates it, then quotes`);
16756
+ if (!create) throw new Error(`the vault in ${buying ? pair3.symbol : pool.token} holds tokens but is not created yet; ${a.side} creates it, then quotes`);
16540
16757
  await from.open();
16541
16758
  }
16542
16759
  const q = await quoteVaultSwap(this.#client, this.#config.deployment, {
16543
16760
  vault: fromVault,
16544
16761
  relayer: await sender.payer(),
16545
- swap: { key: pool.key, zeroForOne: buying ? usdgIs0 : !usdgIs0, amountIn, recipient: to, ...referrer === void 0 ? {} : { referrer } },
16762
+ swap: { key: pool.key, zeroForOne: buying ? quoteIs0 : !quoteIs0, amountIn, recipient: to, ...referrer === void 0 ? {} : { referrer } },
16546
16763
  slippageBps: slippage(a.slippageBps)
16547
16764
  });
16548
- return { q, pool, from, fromVault, to, inDecimals: buying ? TOKEN_DECIMALS : 18, outDecimals: buying ? 18 : TOKEN_DECIMALS, sender };
16765
+ return { q, pool, from, fromVault, to, inDecimals: buying ? pair3.decimals : 18, outDecimals: buying ? 18 : pair3.decimals, sender };
16549
16766
  }
16550
16767
  /** What the vault's Swap in `txHash` did: Swapped (in, out) or SwapFailed. */
16551
16768
  async #swapOutcome(txHash, vault) {
16552
16769
  const receipt = await this.#client.getTransactionReceipt({ hash: txHash });
16553
16770
  for (const log2 of receipt.logs) {
16554
- if (getAddress15(log2.address) !== vault) continue;
16771
+ if (getAddress16(log2.address) !== vault) continue;
16555
16772
  try {
16556
16773
  const e = decodeEventLog3({ abi: ostraVaultAbi, topics: log2.topics, data: log2.data });
16557
16774
  if (e.eventName === "Swapped") {
@@ -16564,11 +16781,11 @@ var init_launchpad = __esm({
16564
16781
  }
16565
16782
  throw new Error(`transaction ${txHash} has no Swap of ${vault}`);
16566
16783
  }
16567
- /** Buys `token` with `amount` USDG of the buyer vault, into the agent's token vault. */
16784
+ /** Buys `token` with `amount` of its pool's pair (USDG from the buyer vault, a stock token from its token vault), into the agent's token vault. */
16568
16785
  buy(a) {
16569
16786
  return this.#swap({ ...a, side: "buy" });
16570
16787
  }
16571
- /** Sells `amount` of `token` from the agent's token vault for USDG into the buyer vault. */
16788
+ /** Sells `amount` of `token` from the agent's token vault for its pool's pair, into the agent's vault of the pair (the buyer vault for USDG). */
16572
16789
  sell(a) {
16573
16790
  return this.#swap({ ...a, side: "sell" });
16574
16791
  }
@@ -16577,7 +16794,7 @@ var init_launchpad = __esm({
16577
16794
  const buying = a.side === "buy";
16578
16795
  const tokenVault = this.tokenVault(pool.token);
16579
16796
  const createTokenVault = buying && !(await tokenVault.state()).exists;
16580
- const inToken = buying ? this.#usdg() : pool.token;
16797
+ const inToken = buying ? this.#quoteOf(pool) : pool.token;
16581
16798
  const result = await from.operate({
16582
16799
  gas: swapSpendGas(q.swap.swapGas) + (createTokenVault ? CREATE_VAULT_GAS2 : 0n),
16583
16800
  moves: q.amountIn,
@@ -16623,7 +16840,7 @@ var init_launchpad = __esm({
16623
16840
  async transferToken(a) {
16624
16841
  const token = address2(a.token, "token");
16625
16842
  const destination = address2(a.destination, "destination");
16626
- if (destination === zeroAddress9) throw new RangeError("the destination must not be the zero address");
16843
+ if (destination === zeroAddress10) throw new RangeError("the destination must not be the zero address");
16627
16844
  const v = this.tokenVault(token);
16628
16845
  if (v === this.#buyer) throw new Error("USDG is withdrawn with withdraw");
16629
16846
  const decimals = await tokenDecimals(this.#client, token);
@@ -16633,6 +16850,7 @@ var init_launchpad = __esm({
16633
16850
  if (!s.exists) throw new Error(`this agent has no vault in ${token}`);
16634
16851
  if (s.balance < amount) throw new Error(`the vault in ${token} holds ${formatUnitsAmount(s.balance, decimals)}, less than ${a.amount}`);
16635
16852
  if (destination === s.vault) throw new Error("the destination is the vault itself");
16853
+ await this.#notStranded(destination, token, "the transfer");
16636
16854
  return this.#simulatedSpend(v, this.#buyerSender, VAULT_ACTION.TRANSFER, transferParams({ to: destination, amount }), "token transfer");
16637
16855
  }
16638
16856
  /** The agent's vault that receives `dividendToken`: the buyer vault for USDG, else the token vault of that token. */
@@ -16642,8 +16860,8 @@ var init_launchpad = __esm({
16642
16860
  /** Claims the dividends the agent's token vault for `token` earned, into the agent's vault of the dividend token. */
16643
16861
  async claimDividends(tokenText) {
16644
16862
  const pool = await this.#pool(tokenText);
16645
- const dividendToken = getAddress15(pool.config.dividendToken);
16646
- if (dividendToken === zeroAddress9) throw new Error(`the pool of ${pool.token} pays no dividends`);
16863
+ const dividendToken = getAddress16(pool.config.dividendToken);
16864
+ if (dividendToken === zeroAddress10) throw new Error(`the pool of ${pool.token} pays no dividends`);
16647
16865
  const v = this.tokenVault(pool.token);
16648
16866
  const s = await v.state();
16649
16867
  if (!s.exists) throw new Error(`this agent has no vault in ${pool.token}`);
@@ -16670,11 +16888,51 @@ var init_launchpad = __esm({
16670
16888
  }
16671
16889
  return out.sort((x, y) => x.at - y.at);
16672
16890
  }
16673
- /** Moves `amount` USDG from the buyer vault into the creator vault (what a launch's dev buy and the creator's relayer requests are paid from). */
16674
- async fundCreatorVault(amount) {
16675
- const creator = await this.#creator.vaultAddress();
16676
- if ((await this.#creator.state()).exists === false) await this.#creator.open();
16677
- return this.#buyer.withdraw(creator, amount);
16891
+ /**
16892
+ * Moves `amount` into the creator vault of `pairText` (default USDG): USDG
16893
+ * from the buyer vault (a launch's dev buy and the USDG creator vault's
16894
+ * relayer requests are paid from it); a stock token or WETH from the
16895
+ * agent's token vault of it (what a dev buy in that pair is paid from). The
16896
+ * creator vault is created first when it does not exist yet.
16897
+ */
16898
+ async fundCreatorVault(amount, pairText = "USDG") {
16899
+ const { pair: pair3, vault: creator } = await this.pairCreator(pairText);
16900
+ const to = await creator.vaultAddress();
16901
+ if (pair3.kind === "usdg") {
16902
+ if ((await creator.state()).exists === false) await creator.open();
16903
+ return this.#buyer.withdraw(to, amount);
16904
+ }
16905
+ const units2 = parseUnitsAmount(amount, pair3.decimals);
16906
+ if (units2 === 0n) throw new RangeError("amount must be greater than zero");
16907
+ const token = this.#pairToken(pair3);
16908
+ const from = this.tokenVault(token);
16909
+ const s = await from.state();
16910
+ const tokenName = pair3.kind === "eth" ? "WETH" : pair3.symbol;
16911
+ if (s.balance < units2) {
16912
+ throw new Error(
16913
+ `this agent's vault in ${tokenName} (${s.vault}) holds ${formatUnitsAmount(s.balance, pair3.decimals)}, less than ${amount}; send ${tokenName} to it, or straight to the ${pair3.symbol} creator vault ${to}; nothing was signed`
16914
+ );
16915
+ }
16916
+ if (!s.exists) await from.open();
16917
+ if ((await creator.state()).exists === false) await creator.open();
16918
+ return this.#simulatedSpend(from, this.#buyerSender, VAULT_ACTION.TRANSFER, transferParams({ to, amount: units2 }), `${tokenName} transfer to the ${pair3.symbol} creator vault`);
16919
+ }
16920
+ /** Sends `amount` from the creator vault of `pair` (default USDG) to `destination` (default: the agent's vault of that token; for ETH, required). */
16921
+ async creatorWithdraw(a) {
16922
+ const { pair: pair3, vault: creator, sender } = await this.pairCreator(a.pair ?? "USDG");
16923
+ const token = this.#pairToken(pair3);
16924
+ if (pair3.kind !== "usdg" && pair3.kind !== "stock" && a.destination === void 0) throw new RangeError(`a ${pair3.symbol} creator vault holds WETH: name the destination`);
16925
+ const destination = a.destination === void 0 ? await this.tokenVault(token).vaultAddress() : address2(a.destination, "destination");
16926
+ await this.#notStranded(destination, token, `the ${pair3.symbol} creator vault's transfer`);
16927
+ if (pair3.kind === "usdg") return creator.withdraw(destination, a.amount);
16928
+ if (destination === zeroAddress10) throw new RangeError("the destination must not be the zero address");
16929
+ const units2 = parseUnitsAmount(a.amount, pair3.decimals);
16930
+ if (units2 === 0n) throw new RangeError("amount must be greater than zero");
16931
+ const s = await creator.state();
16932
+ if (!s.exists) throw new Error(`this agent has no ${pair3.symbol} creator vault yet`);
16933
+ if (destination === s.vault) throw new Error("the destination is the vault itself");
16934
+ if (s.balance < units2) throw new Error(`the ${pair3.symbol} creator vault holds ${formatUnitsAmount(s.balance, pair3.decimals)}, less than ${a.amount}`);
16935
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.TRANSFER, transferParams({ to: destination, amount: units2 }), `${pair3.symbol} creator vault transfer`);
16678
16936
  }
16679
16937
  /**
16680
16938
  * Launches a token from the creator vault (OstraDevBuy.launchFromVault),
@@ -16685,13 +16943,20 @@ var init_launchpad = __esm({
16685
16943
  const lp = this.#lp();
16686
16944
  if (typeof input.name !== "string" || input.name.length === 0 || input.name.length > 64) throw new RangeError("a name is 1..64 characters");
16687
16945
  if (typeof input.symbol !== "string" || !/^[A-Za-z0-9]{1,11}$/.test(input.symbol)) throw new RangeError("a symbol is 1..11 letters and digits");
16688
- if (input.marketCap === void 0 === (input.startTick === void 0)) throw new RangeError("give the starting marketCap (USDG) or the startTick, one of them");
16689
- const startTick = input.startTick ?? startTickForMarketCap(parseTokenAmount(input.marketCap));
16946
+ if (input.marketCap === void 0 === (input.startTick === void 0)) throw new RangeError("give the starting marketCap (US dollars) or the startTick, one of them");
16947
+ const { pair: pair3, vault: creator, sender } = await this.pairCreator(input.pair ?? "USDG");
16948
+ const marketCapUsd = input.marketCap === void 0 ? null : parseTokenAmount(input.marketCap);
16949
+ const price = marketCapUsd === null ? null : await readPairUsdPrice(this.#client, pair3);
16950
+ const marketCapUnits = marketCapUsd === null || price === null ? null : usdToQuoteUnits(marketCapUsd, price.priceE8, pair3.decimals);
16951
+ if (marketCapUnits === 0n) throw new RangeError(`a market cap of ${input.marketCap} USD is less than one base unit of ${pair3.symbol}`);
16952
+ const startTick = input.startTick ?? startTickForMarketCap(marketCapUnits);
16690
16953
  if (!Number.isInteger(startTick) || startTick % LAUNCHPAD.TICK_SPACING !== 0) throw new RangeError(`the start tick must be a multiple of ${LAUNCHPAD.TICK_SPACING}`);
16691
- let creatorState = await this.#creator.state();
16954
+ const vaultToken = this.#pairToken(pair3);
16955
+ const pairUnits = (x) => formatUnitsAmount(x, pair3.decimals);
16956
+ let creatorState = await creator.state();
16692
16957
  if (!creatorState.exists) {
16693
- await this.#creator.open();
16694
- creatorState = await this.#creator.state();
16958
+ await creator.open();
16959
+ creatorState = await creator.state();
16695
16960
  }
16696
16961
  const creatorVault = creatorState.vault;
16697
16962
  const p = launchParams({
@@ -16699,46 +16964,46 @@ var init_launchpad = __esm({
16699
16964
  name: input.name,
16700
16965
  symbol: input.symbol,
16701
16966
  salt: bytesToHex11(randomBytes3(32)),
16702
- quote: this.#usdg(),
16967
+ quote: pair3.address,
16703
16968
  creatorVault,
16704
16969
  startTick
16705
16970
  });
16706
- const token = getAddress15(await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "tokenAddressFromVault", args: [p] }));
16971
+ const token = getAddress16(await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "tokenAddressFromVault", args: [p] }));
16707
16972
  const tokenVault = await this.tokenVault(token).vaultAddress();
16708
16973
  let dev = NO_DEV_BUY;
16709
16974
  if (input.devBuy !== void 0) {
16710
- const quoteIn = parseTokenAmount(input.devBuy.amount);
16975
+ const quoteIn = parseUnitsAmount(input.devBuy.amount, pair3.decimals);
16711
16976
  if (quoteIn === 0n) throw new RangeError("the dev buy amount must be greater than zero (leave devBuy out for none)");
16712
16977
  const cliff = Math.round((input.devBuy.cliffDays ?? DEV_BUY.MIN_CLIFF / DAY) * DAY);
16713
16978
  const duration = Math.round((input.devBuy.vestingDays ?? 0) * DAY);
16714
- const dividendToken = getAddress15(p.dividendToken);
16979
+ const dividendToken = getAddress16(p.dividendToken);
16715
16980
  dev = {
16716
16981
  quoteIn,
16717
16982
  beneficiary: tokenVault,
16718
16983
  // The dividend token is the launched token itself, USDG or another token: the agent's vault of it.
16719
16984
  // (OstraLaunchpad takes "dividends in the token" as its own address: the token does not exist yet.)
16720
- dividendRecipient: dividendToken === zeroAddress9 ? zeroAddress9 : dividendToken === lp.launchpad || dividendToken === token ? tokenVault : await this.#vaultFor(dividendToken),
16985
+ dividendRecipient: dividendToken === zeroAddress10 ? zeroAddress10 : dividendToken === lp.launchpad || dividendToken === token ? tokenVault : await this.#vaultFor(dividendToken),
16721
16986
  cliff,
16722
16987
  duration
16723
16988
  };
16724
16989
  if (creatorState.balance < quoteIn) {
16725
16990
  throw new Error(
16726
- `the creator vault ${creatorVault} holds ${formatTokenAmount(creatorState.balance)}, less than the dev buy of ${input.devBuy.amount}; fund it first with fund_creator_vault (the dev buy plus about 1 USDG for its relayer channel)`
16991
+ `the ${pair3.symbol} creator vault ${creatorVault} holds ${pairUnits(creatorState.balance)}, less than the dev buy of ${input.devBuy.amount}; ` + (pair3.kind === "usdg" ? "fund it first with fund_creator_vault (the dev buy plus about 1 USDG for its relayer channel)" : `fund it first with fund_creator_vault (pair ${pair3.symbol}), or send ${pair3.kind === "eth" ? "WETH" : pair3.symbol} straight to it`)
16727
16992
  );
16728
16993
  }
16729
16994
  }
16730
- const q = await quoteVaultLaunch(this.#client, this.#config.deployment, { relayer: await this.#creatorSender.payer(), launch: p, dev });
16731
- const result = await this.#creator.operate({
16995
+ const q = await quoteVaultLaunch(this.#client, this.#config.deployment, { relayer: await sender.payer(), launch: p, dev });
16996
+ const result = await creator.operate({
16732
16997
  gas: q.gasUsed * 5n / 4n + LAUNCH_GAS_HEADROOM,
16733
16998
  value: LAUNCHPAD.FEE_WEI,
16734
16999
  moves: dev.quoteIn,
16735
17000
  what: "launch",
16736
17001
  build: async ({ v, keychain, chainId }) => {
16737
17002
  if (dev.quoteIn !== 0n) {
16738
- const balance = await tokenBalance(this.#client, this.#usdg(), creatorVault);
16739
- if (balance < dev.quoteIn) throw new Error(`the creator vault holds ${formatTokenAmount(balance)} now, less than the dev buy; nothing was signed`);
17003
+ const balance = await tokenBalance(this.#client, vaultToken, creatorVault);
17004
+ if (balance < dev.quoteIn) throw new Error(`the creator vault holds ${pairUnits(balance)} now, less than the dev buy; nothing was signed`);
16740
17005
  }
16741
- await this.#creator.keepLaunch(v.nonce, p, dev);
17006
+ await creator.keepLaunch(v.nonce, p, dev);
16742
17007
  const signed = await signVaultDevBuy(keychain, v, { chainId, devBuy: lp.devBuy, launch: p, dev });
16743
17008
  return [{ to: lp.devBuy, data: launchFromVaultData(p, dev, signed) }];
16744
17009
  }
@@ -16749,14 +17014,14 @@ var init_launchpad = __esm({
16749
17014
  let bought = null;
16750
17015
  for (const log2 of receipt.logs) {
16751
17016
  try {
16752
- if (getAddress15(log2.address) === lp.launchpad) {
17017
+ if (getAddress16(log2.address) === lp.launchpad) {
16753
17018
  const e = decodeEventLog3({ abi: ostraLaunchpadAbi, topics: log2.topics, data: log2.data });
16754
17019
  if (e.eventName === "Launched") poolId2 = e.args.poolId;
16755
- } else if (getAddress15(log2.address) === lp.devBuy) {
17020
+ } else if (getAddress16(log2.address) === lp.devBuy) {
16756
17021
  const e = decodeEventLog3({ abi: ostraDevBuyAbi, topics: log2.topics, data: log2.data });
16757
17022
  if (e.eventName === "DevBought") {
16758
17023
  const g = e.args;
16759
- bought = { ...g, vesting: getAddress15(g.vesting) };
17024
+ bought = { ...g, vesting: getAddress16(g.vesting) };
16760
17025
  }
16761
17026
  }
16762
17027
  } catch {
@@ -16764,7 +17029,7 @@ var init_launchpad = __esm({
16764
17029
  }
16765
17030
  if (poolId2 === null) throw new Error(`transaction ${result.txHash} landed without the launch`);
16766
17031
  await this.#folder.ensure();
16767
- await this.#folder.write(this.#launchName(token), `${JSON.stringify({ version: 1, token, poolId: poolId2, txHash: result.txHash, vesting: bought?.vesting ?? null, at: Math.floor(Date.now() / 1e3) })}
17032
+ await this.#folder.write(this.#launchName(token), `${JSON.stringify({ version: 1, token, poolId: poolId2, txHash: result.txHash, vesting: bought?.vesting ?? null, pair: pair3.symbol, creatorVault, at: Math.floor(Date.now() / 1e3) })}
16768
17033
  `);
16769
17034
  return {
16770
17035
  status: "launched",
@@ -16773,16 +17038,28 @@ var init_launchpad = __esm({
16773
17038
  poolId: poolId2,
16774
17039
  creatorVault,
16775
17040
  tokenVault,
16776
- devBuy: bought === null ? null : { paid: formatTokenAmount(bought.quotePaid), tokens: formatUnitsAmount(bought.tokens, 18), vesting: bought.vesting, cliffEnd: nowIso(bought.cliffEnd), vestingEnd: nowIso(bought.vestingEnd) },
16777
- note: bought === null ? "the token is live; the creator vault owns the pool (settings, creator shares, airdrops) and receives the creator payouts" : "the token is live; the dev buy's tokens vest to this agent's token vault (vesting_release after the cliff); the creator vault owns the pool and receives the creator payouts"
17041
+ pair: pair3.symbol,
17042
+ marketCapUsd: marketCapUsd === null ? null : formatTokenAmount(marketCapUsd),
17043
+ marketCapInPair: marketCapUnits === null ? "" : pairUnits(marketCapUnits),
17044
+ pairPriceUsd: price === null ? null : formatUnitsAmount(price.priceE8, 8),
17045
+ pairPriceAt: price === null ? null : new Date(price.updatedAt * 1e3).toISOString(),
17046
+ devBuy: bought === null ? null : { paid: pairUnits(bought.quotePaid), tokens: formatUnitsAmount(bought.tokens, 18), vesting: bought.vesting, cliffEnd: nowIso(bought.cliffEnd), vestingEnd: nowIso(bought.vestingEnd) },
17047
+ note: (bought === null ? `the token is live; the ${pair3.symbol} creator vault owns the pool (settings, creator shares, airdrops) and receives the creator payouts` : `the token is live; the dev buy's tokens vest to this agent's token vault (vesting_release after the cliff); the ${pair3.symbol} creator vault owns the pool and receives the creator payouts`) + (pair3.kind === "usdg" ? "" : `, in ${pair3.kind === "eth" ? "WETH" : pair3.symbol}; publish_profile again shows this agent's profile on the pool`)
16778
17048
  };
16779
17049
  }
16780
- /** The pool's creator vault must be this agent's. */
17050
+ /** The pool's pair (null: a quote Ostra does not offer). */
17051
+ #pairOf(pool) {
17052
+ return findLaunchPair(this.#quoteOf(pool));
17053
+ }
17054
+ /** The pool's creator vault must be this agent's creator vault of the pool's pair: the pool, that vault and its sender. */
16781
17055
  async #ownPool(tokenText) {
16782
17056
  const pool = await this.#pool(tokenText);
16783
- const creatorVault = await this.#creator.vaultAddress();
16784
- if (getAddress15(pool.config.creatorVault) !== creatorVault) throw new Error(`the pool of ${pool.token} belongs to the creator vault ${pool.config.creatorVault}, not this agent's ${creatorVault}`);
16785
- return pool;
17057
+ const pair3 = this.#pairOf(pool);
17058
+ if (pair3 === null) throw new Error(`the pool of ${pool.token} trades against ${this.#quoteOf(pool)}, not a pair Ostra offers; no creator vault of this agent owns it`);
17059
+ const { vault: creator, sender } = await this.pairCreator(pair3.symbol);
17060
+ const creatorVault = await creator.vaultAddress();
17061
+ if (getAddress16(pool.config.creatorVault) !== creatorVault) throw new Error(`the pool of ${pool.token} belongs to the creator vault ${pool.config.creatorVault}, not this agent's ${pair3.symbol} creator vault ${creatorVault}`);
17062
+ return { pool, pair: pair3, creator, sender };
16786
17063
  }
16787
17064
  /**
16788
17065
  * Changes one of the pool's rules (POOL_SETTING_NAMES), only in the
@@ -16794,27 +17071,28 @@ var init_launchpad = __esm({
16794
17071
  const field = POOL_SETTING_NAMES[a.setting];
16795
17072
  if (field === void 0) throw new RangeError(`unknown setting ${a.setting}; one of ${Object.keys(POOL_SETTING_NAMES).join(", ")}`);
16796
17073
  if (!/^(0|[1-9][0-9]*)$/.test(a.value)) throw new RangeError("the value is a whole number (bps, unix seconds, ticks, or token base units for max_buy)");
16797
- const pool = await this.#ownPool(a.token);
17074
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16798
17075
  const params = configurePoolParams({ hook: this.#lp().hook, poolId: pool.id, field, value: BigInt(a.value) });
16799
- return this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.CONFIGURE_POOL, params, "pool setting");
17076
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, "pool setting");
16800
17077
  }
16801
17078
  /** Sets `recipient`'s share (bps; 0 removes it) of the pool's creator payouts; the creator vault keeps the rest. */
16802
17079
  async setCreatorShare(a) {
16803
17080
  if (!Number.isInteger(a.bps) || a.bps < 0 || a.bps > CREATOR_SPLIT.BPS) throw new RangeError(`bps must be 0..${CREATOR_SPLIT.BPS}`);
16804
- const pool = await this.#ownPool(a.token);
17081
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16805
17082
  const params = creatorShareParams({ split: this.#lp().creatorSplit, poolId: pool.id, recipient: address2(a.recipient, "recipient"), bps: a.bps });
16806
- return this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.CONFIGURE_POOL, params, "creator share");
17083
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, "creator share");
16807
17084
  }
16808
- /** Pays out what the creator split owes `account` (default: this agent's creator vault) for the pool. */
17085
+ /** Pays out what the creator split owes `account` (default: this agent's creator vault of the pool's pair) for the pool. */
16809
17086
  async releaseCreatorPayout(a) {
16810
17087
  const lp = this.#lp();
16811
17088
  const pool = await this.#pool(a.token);
16812
- const account = a.account === void 0 ? await this.#creator.vaultAddress() : address2(a.account, "account");
17089
+ const pair3 = this.#pairOf(pool);
17090
+ const own = pair3 === null ? this.#creator : (await this.pairCreator(pair3.symbol)).vault;
17091
+ const account = a.account === void 0 ? await own.vaultAddress() : address2(a.account, "account");
16813
17092
  const owed = await this.#client.readContract({ address: lp.creatorSplit, abi: ostraCreatorSplitAbi, functionName: "owedOf", args: [pool.id, account] });
16814
17093
  if (owed === 0n) throw new Error(`the creator split owes ${account} nothing for this pool now`);
16815
17094
  const txHash = await this.#public({ to: lp.creatorSplit, data: encodeFunctionData8({ abi: ostraCreatorSplitAbi, functionName: "release", args: [pool.id, account] }) }, "release the creator payout");
16816
- const quote = this.#quoteOf(pool);
16817
- return { txHash, account, amount: formatUnitsAmount(owed, quote === this.#usdg() ? TOKEN_DECIMALS : 18) };
17095
+ return { txHash, account, amount: formatUnitsAmount(owed, pair3 === null ? await tokenDecimals(this.#client, this.#quoteOf(pool)) : pair3.decimals) };
16818
17096
  }
16819
17097
  /** A call anyone may make, sent for the buyer vault (its relayer channel pays). */
16820
17098
  async #public(call, purpose) {
@@ -16824,27 +17102,27 @@ var init_launchpad = __esm({
16824
17102
  // ---------------------------------------------------------------------------
16825
17103
  // Airdrops
16826
17104
  // ---------------------------------------------------------------------------
16827
- /** Buys `amount` USDG of the token from the creator vault into OstraAirdrop, for the pool's next round. */
17105
+ /** Buys `amount` of the pool's pair worth of the token from its creator vault into OstraAirdrop, for the pool's next round (USDG and stock pools). */
16828
17106
  async airdropFund(a) {
16829
17107
  const lp = this.#lp();
16830
- const pool = await this.#ownPool(a.token);
16831
- if (this.#quoteOf(pool) !== this.#usdg()) throw new Error("this agent funds airdrops of USDG pools");
16832
- const amountIn = parseTokenAmount(a.amount);
17108
+ const { pool, pair: pair3, creator, sender } = await this.#ownPool(a.token);
17109
+ if (pair3.kind === "eth") throw new Error("an ETH pool's airdrop is bought from a wallet: a vault cannot pay native ETH; nothing was signed");
17110
+ const amountIn = parseUnitsAmount(a.amount, pair3.decimals);
16833
17111
  if (amountIn === 0n) throw new RangeError("amount must be greater than zero");
16834
- const s = await this.#creator.state();
16835
- if (s.balance < amountIn) throw new Error(`the creator vault holds ${formatTokenAmount(s.balance)}, less than ${a.amount}; fund it with fund_creator_vault`);
16836
- const usdgIs0 = getAddress15(pool.key.currency0) === this.#usdg();
17112
+ const s = await creator.state();
17113
+ if (s.balance < amountIn) throw new Error(`the ${pair3.symbol} creator vault holds ${formatUnitsAmount(s.balance, pair3.decimals)}, less than ${a.amount}; fund it with fund_creator_vault`);
17114
+ const quoteIs0 = getAddress16(pool.key.currency0) === pair3.address;
16837
17115
  const q = await quoteVaultSwap(this.#client, this.#config.deployment, {
16838
17116
  vault: s.vault,
16839
- relayer: await this.#creatorSender.payer(),
16840
- swap: { key: pool.key, zeroForOne: usdgIs0, amountIn, recipient: lp.airdrop },
17117
+ relayer: await sender.payer(),
17118
+ swap: { key: pool.key, zeroForOne: quoteIs0, amountIn, recipient: lp.airdrop },
16841
17119
  slippageBps: slippage(a.slippageBps)
16842
17120
  });
16843
- const result = await this.#creator.operate({
17121
+ const result = await creator.operate({
16844
17122
  gas: swapSpendGas(q.swap.swapGas),
16845
17123
  moves: amountIn,
16846
17124
  what: "airdrop purchase",
16847
- build: async ({ v, vault, keychain, chainId }) => [spendCall(vault, await signVaultSwap(keychain, v, { chainId, token: this.#usdg(), swap: q.swap }))]
17125
+ build: async ({ v, vault, keychain, chainId }) => [spendCall(vault, await signVaultSwap(keychain, v, { chainId, token: pair3.address, swap: q.swap }))]
16848
17126
  });
16849
17127
  if (result.status !== "sent") return result;
16850
17128
  const outcome = await this.#swapOutcome(result.txHash, s.vault);
@@ -16852,7 +17130,7 @@ var init_launchpad = __esm({
16852
17130
  status: "sent",
16853
17131
  txHash: result.txHash,
16854
17132
  swapped: outcome.swapped,
16855
- amountIn: formatTokenAmount(outcome.amountIn),
17133
+ amountIn: formatUnitsAmount(outcome.amountIn, pair3.decimals),
16856
17134
  amountOut: formatUnitsAmount(outcome.amountOut, 18),
16857
17135
  minOut: formatUnitsAmount(q.swap.minOut, 18),
16858
17136
  from: s.vault,
@@ -16871,7 +17149,7 @@ var init_launchpad = __esm({
16871
17149
  */
16872
17150
  async airdropStart(a) {
16873
17151
  const lp = this.#lp();
16874
- const pool = await this.#ownPool(a.token);
17152
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16875
17153
  const entries = a.entries.map((e) => ({ account: address2(e.account, "account"), amount: parseUnitsAmount(e.amount, 18) }));
16876
17154
  const read = (functionName, args) => this.#client.readContract({ address: lp.airdrop, abi: ostraAirdropAbi, functionName, args });
16877
17155
  const current = await read("round", [pool.id]);
@@ -16895,7 +17173,7 @@ var init_launchpad = __esm({
16895
17173
  `);
16896
17174
  const sent = [];
16897
17175
  const step2 = async (params, what) => {
16898
- const r = await this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.CONFIGURE_POOL, params, what);
17176
+ const r = await this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, what);
16899
17177
  if (r.status !== "sent") throw new Error(`${r.note}; airdrop_start was not finished, call it again with the same entries`);
16900
17178
  sent.push(r.txHash);
16901
17179
  };
@@ -16919,7 +17197,7 @@ var init_launchpad = __esm({
16919
17197
  } else {
16920
17198
  const text2 = await this.#folder.read(this.#airdropName(pool.id, r.number));
16921
17199
  if (text2 === void 0) throw new Error(`no tree of round ${r.number} is kept here; give the amount and proof`);
16922
- const claim = JSON.parse(text2).claims.find((c) => getAddress15(c.account) === account);
17200
+ const claim = JSON.parse(text2).claims.find((c) => getAddress16(c.account) === account);
16923
17201
  if (claim === void 0) throw new Error(`${account} is not in round ${r.number}`);
16924
17202
  amount = BigInt(claim.amount);
16925
17203
  proof = claim.proof;
@@ -16954,7 +17232,7 @@ var init_launchpad = __esm({
16954
17232
  const text2 = await this.#folder.read(this.#launchName(token));
16955
17233
  const v = text2 === void 0 ? null : JSON.parse(text2).vesting ?? null;
16956
17234
  if (v === null) throw new Error(`this agent's records hold no dev buy vesting of ${token}; give its address`);
16957
- vesting = getAddress15(v);
17235
+ vesting = getAddress16(v);
16958
17236
  }
16959
17237
  const id = await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "vestingPool", args: [vesting] });
16960
17238
  if (BigInt(id) === 0n) throw new Error(`${vesting} is not an Ostra dev buy's vesting`);
@@ -16965,8 +17243,8 @@ var init_launchpad = __esm({
16965
17243
  const vesting = await this.#vesting(a.token, a.vesting);
16966
17244
  const read = (functionName) => this.#client.readContract({ address: vesting, abi: ostraVestingAbi, functionName });
16967
17245
  const [terms, schedule, releasable] = await Promise.all([read("terms"), read("schedule"), read("releasable")]);
16968
- const held = await tokenBalance(this.#client, getAddress15(terms.token), vesting);
16969
- return { vesting, beneficiary: getAddress15(terms.beneficiary), cliffEnd: nowIso(schedule[0]), end: nowIso(schedule[1]), held: formatUnitsAmount(held, 18), releasable: formatUnitsAmount(releasable, 18) };
17246
+ const held = await tokenBalance(this.#client, getAddress16(terms.token), vesting);
17247
+ return { vesting, beneficiary: getAddress16(terms.beneficiary), cliffEnd: nowIso(schedule[0]), end: nowIso(schedule[1]), held: formatUnitsAmount(held, 18), releasable: formatUnitsAmount(releasable, 18) };
16970
17248
  }
16971
17249
  /** Releases what the vesting has vested to its beneficiary (this agent's token vault). */
16972
17250
  async vestingRelease(a) {
@@ -16982,14 +17260,17 @@ var init_launchpad = __esm({
16982
17260
  const txHash = await this.#public({ to: vesting, data: encodeFunctionData8({ abi: ostraVestingAbi, functionName: "claimDividends" }) }, "claim the vesting's dividends");
16983
17261
  return { txHash };
16984
17262
  }
16985
- /** Claims what swaps that named `referrer` (default: this agent's buyer vault) earned it, in USDG. */
17263
+ /** Claims what swaps that named `referrer` earned it in the pair `pair` (default USDG; referrer default: this agent's vault of that token), paid in that token (WETH for ETH). */
16986
17264
  async claimReferral(a = {}) {
16987
17265
  const lp = this.#lp();
16988
- const referrer = a.referrer === void 0 ? await this.#buyer.vaultAddress() : address2(a.referrer, "referrer");
16989
- const owed = await this.#client.readContract({ address: lp.hook, abi: ostraHookAbi, functionName: "referralOwed", args: [referrer, this.#usdg()] });
16990
- if (owed === 0n) throw new Error(`${referrer} has no referral earnings in USDG to claim`);
16991
- const txHash = await this.#public({ to: lp.dispatcher, data: encodeFunctionData8({ abi: ostraDispatcherAbi, functionName: "claimReferral", args: [referrer, this.#usdg()] }) }, "claim the referral earnings");
16992
- return { txHash, referrer, amount: formatTokenAmount(owed) };
17266
+ const pair3 = launchPair(a.pair ?? "USDG");
17267
+ if (pair3.kind === "eth" && a.referrer === void 0) throw new RangeError("ETH referral earnings are paid in WETH: name the referrer they are owed to");
17268
+ const referrer = a.referrer === void 0 ? await this.tokenVault(pair3.address).vaultAddress() : address2(a.referrer, "referrer");
17269
+ await this.#notStranded(referrer, this.#pairToken(pair3), "the referral earnings");
17270
+ const owed = await this.#client.readContract({ address: lp.hook, abi: ostraHookAbi, functionName: "referralOwed", args: [referrer, pair3.address] });
17271
+ if (owed === 0n) throw new Error(`${referrer} has no referral earnings in ${pair3.symbol} to claim`);
17272
+ const txHash = await this.#public({ to: lp.dispatcher, data: encodeFunctionData8({ abi: ostraDispatcherAbi, functionName: "claimReferral", args: [referrer, pair3.address] }) }, "claim the referral earnings");
17273
+ return { txHash, referrer, amount: formatUnitsAmount(owed, pair3.decimals), pair: pair3.symbol };
16993
17274
  }
16994
17275
  // ---------------------------------------------------------------------------
16995
17276
  // The token list (every Ostra launch, for any buyer)
@@ -17000,7 +17281,7 @@ var init_launchpad = __esm({
17000
17281
  await this.#folder.ensure();
17001
17282
  const text2 = await this.#folder.read("token-index.json");
17002
17283
  let index = text2 === void 0 ? { version: 1, launchpad: lp.launchpad, scannedTo: (BigInt(this.#config.deployment.fromBlock) - 1n).toString(), launches: [] } : JSON.parse(text2);
17003
- if (getAddress15(index.launchpad) !== getAddress15(lp.launchpad)) index = { version: 1, launchpad: lp.launchpad, scannedTo: (BigInt(this.#config.deployment.fromBlock) - 1n).toString(), launches: [] };
17284
+ if (getAddress16(index.launchpad) !== getAddress16(lp.launchpad)) index = { version: 1, launchpad: lp.launchpad, scannedTo: (BigInt(this.#config.deployment.fromBlock) - 1n).toString(), launches: [] };
17004
17285
  const latest = await this.#client.getBlockNumber();
17005
17286
  let from = BigInt(index.scannedTo) + 1n;
17006
17287
  if (from > latest) return index;
@@ -17009,7 +17290,7 @@ var init_launchpad = __esm({
17009
17290
  const logs = await this.#client.getLogs({ address: lp.launchpad, event: launchedEvent, fromBlock: from, toBlock: to });
17010
17291
  for (const l of logs) {
17011
17292
  if (l.args.token === void 0 || l.args.poolId === void 0 || l.args.creatorVault === void 0 || l.args.quote === void 0 || l.transactionHash === null || l.blockNumber === null) continue;
17012
- index.launches.push({ token: getAddress15(l.args.token), poolId: l.args.poolId, creatorVault: getAddress15(l.args.creatorVault), quote: getAddress15(l.args.quote), block: l.blockNumber.toString(), txHash: l.transactionHash });
17293
+ index.launches.push({ token: getAddress16(l.args.token), poolId: l.args.poolId, creatorVault: getAddress16(l.args.creatorVault), quote: getAddress16(l.args.quote), block: l.blockNumber.toString(), txHash: l.transactionHash });
17013
17294
  }
17014
17295
  index.scannedTo = to.toString();
17015
17296
  }
@@ -17060,11 +17341,15 @@ var init_launchpad = __esm({
17060
17341
  // ---------------------------------------------------------------------------
17061
17342
  // The agent's identity: profile, ERC-8004 registration, token metadata (vault version 2)
17062
17343
  // ---------------------------------------------------------------------------
17063
- /** The creator vault, once it exists and runs vault version 2 with OstraRegistry in the deployment record; else throws before anything is signed. */
17064
- async #identityVault() {
17344
+ /** Throws before anything is signed when the deployment record has no OstraRegistry (vault version 2 not executed yet). */
17345
+ #requireRegistry() {
17065
17346
  if (this.#config.deployment.registry === void 0) {
17066
17347
  throw new Error(`this deployment record has no OstraRegistry: profiles, token metadata and agent identity open once the vaults run version ${VAULT_VERSION} (the upgrade waits its public delay in OstraUpgradeOwner); nothing was signed`);
17067
17348
  }
17349
+ }
17350
+ /** The creator vault, once it exists and runs vault version 2 with OstraRegistry in the deployment record; else throws before anything is signed. */
17351
+ async #identityVault() {
17352
+ this.#requireRegistry();
17068
17353
  const s = await this.#creator.state();
17069
17354
  if (!s.exists) throw new Error("the creator vault, the agent's identity, does not exist yet: fund it first (fund_creator_vault); nothing was signed");
17070
17355
  let version = 1;
@@ -17105,7 +17390,7 @@ var init_launchpad = __esm({
17105
17390
  const receipt = await this.#client.getTransactionReceipt({ hash: r.txHash });
17106
17391
  let agentId;
17107
17392
  for (const log2 of receipt.logs) {
17108
- if (getAddress15(log2.address) !== vault) continue;
17393
+ if (getAddress16(log2.address) !== vault) continue;
17109
17394
  try {
17110
17395
  const e = decodeEventLog3({ abi: ostraVaultAbi, data: log2.data, topics: log2.topics });
17111
17396
  if (e.eventName === "AgentRegistered") agentId = e.args.agentId.toString();
@@ -17127,13 +17412,25 @@ var init_launchpad = __esm({
17127
17412
  if (r.status === "sent") await this.#folder.write("agent.json", JSON.stringify({ ...record, uri: uri2, at: Math.floor(Date.now() / 1e3) }, null, 2));
17128
17413
  return r;
17129
17414
  }
17130
- /** Sets the metadata (a URI to its JSON: image, description, links) of a token this agent's creator vault launched. */
17415
+ /** Sets the metadata (a URI to its JSON: image, description, links) of a token this agent launched, from the creator vault of its pair. */
17131
17416
  async setTokenMetadata(a) {
17132
17417
  checkUri(a.uri);
17133
- const vault = await this.#identityVault();
17134
- const pool = await this.#pool(a.token);
17135
- if (getAddress15(pool.config.creatorVault) !== vault) throw new Error(`${pool.token} was launched by the creator vault ${getAddress15(pool.config.creatorVault)}, not by this agent's ${vault}; nothing was signed`);
17136
- return this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.SET_TOKEN_METADATA, setTokenMetadataParams({ token: pool.token, poolId: pool.id, uri: a.uri }), "token metadata");
17418
+ this.#requireRegistry();
17419
+ const { pool, creator, sender } = await this.#ownPool(a.token);
17420
+ await this.#registryVersion(creator);
17421
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.SET_TOKEN_METADATA, setTokenMetadataParams({ token: pool.token, poolId: pool.id, uri: a.uri }), "token metadata");
17422
+ }
17423
+ /** `vault` runs vault version VAULT_VERSION (OstraRegistry's writers); else throws before anything is signed. */
17424
+ async #registryVersion(vault) {
17425
+ const s = await vault.state();
17426
+ if (!s.exists) throw new Error(`the creator vault ${s.vault} does not exist yet; nothing was signed`);
17427
+ let version = 1;
17428
+ try {
17429
+ version = Number(await this.#client.readContract({ address: s.vault, abi: ostraVaultAbi, functionName: "VERSION" }));
17430
+ } catch {
17431
+ version = 1;
17432
+ }
17433
+ if (version < VAULT_VERSION) throw new Error(`the creator vault ${s.vault} runs version ${version}, not ${VAULT_VERSION}; nothing was signed`);
17137
17434
  }
17138
17435
  /**
17139
17436
  * Publishes the agent's profile: builds its ERC-8004 registration file (name, description, image, links, the
@@ -17146,14 +17443,29 @@ var init_launchpad = __esm({
17146
17443
  const file = profileFile({ ...p, vault, agentId: record?.agentId ?? null });
17147
17444
  const uri2 = await uploadJson(this.#config.relayerUrl, file);
17148
17445
  const profile = await this.setProfile(uri2);
17149
- if (profile.status !== "sent" || record === null) return { uri: uri2, profile, agentUri: null };
17150
- return { uri: uri2, profile, agentUri: await this.setAgentUri(uri2) };
17446
+ const pairCreators = [];
17447
+ if (profile.status === "sent") {
17448
+ const pairs = new Set((await this.launches()).map((l) => l.pair ?? "USDG").filter((x) => x !== "USDG"));
17449
+ for (const symbol of pairs) {
17450
+ const { vault: creator, sender } = await this.pairCreator(symbol);
17451
+ const at = await creator.vaultAddress();
17452
+ try {
17453
+ await this.#registryVersion(creator);
17454
+ pairCreators.push({ pair: symbol, vault: at, result: await this.#simulatedSpend(creator, sender, VAULT_ACTION.SET_PROFILE, setProfileParams(uri2), `${symbol} creator vault profile`) });
17455
+ } catch (err) {
17456
+ pairCreators.push({ pair: symbol, vault: at, error: err.message });
17457
+ }
17458
+ }
17459
+ }
17460
+ if (profile.status !== "sent" || record === null) return { uri: uri2, profile, agentUri: null, pairCreators };
17461
+ return { uri: uri2, profile, agentUri: await this.setAgentUri(uri2), pairCreators };
17151
17462
  }
17152
17463
  /** Publishes the metadata of a token this agent launched (description, image, links), pins it and sets it in OstraRegistry. */
17153
17464
  async publishTokenMetadata(a) {
17154
- const vault = await this.#identityVault();
17155
- const pool = await this.#pool(a.token);
17156
- if (getAddress15(pool.config.creatorVault) !== vault) throw new Error(`${pool.token} was launched by the creator vault ${getAddress15(pool.config.creatorVault)}, not by this agent's ${vault}; nothing was signed`);
17465
+ this.#requireRegistry();
17466
+ const { pool, creator } = await this.#ownPool(a.token);
17467
+ await this.#registryVersion(creator);
17468
+ const vault = await creator.vaultAddress();
17157
17469
  const [name, symbol] = await Promise.all([
17158
17470
  this.#client.readContract({ address: pool.token, abi: ostraTokenAbi, functionName: "name" }),
17159
17471
  this.#client.readContract({ address: pool.token, abi: ostraTokenAbi, functionName: "symbol" })
@@ -17167,14 +17479,14 @@ var init_launchpad = __esm({
17167
17479
  });
17168
17480
 
17169
17481
  // ../core/src/pay.ts
17170
- import { getAddress as getAddress16, isAddress as isAddress10 } from "viem";
17482
+ import { getAddress as getAddress17, isAddress as isAddress10 } from "viem";
17171
17483
  import { x402Client as x402Client2, x402HTTPClient as x402HTTPClient2 } from "@x402/core/client";
17172
17484
  import { toClientEvmSigner as toClientEvmSigner2 } from "@x402/evm";
17173
17485
  import { BatchSettlementEvmScheme as BatchSettlementEvmScheme2 } from "@x402/evm/batch-settlement/client";
17174
17486
  function offerProblem(r, network, token) {
17175
17487
  if (r.scheme !== "batch-settlement") return `${r.scheme} (this agent pays batch-settlement)`;
17176
17488
  if (r.network !== network) return `on ${r.network} (this agent pays on ${network})`;
17177
- if (!isAddress10(r.asset, { strict: false }) || getAddress16(r.asset) !== token) return `in ${r.asset} (this agent pays in ${token})`;
17489
+ if (!isAddress10(r.asset, { strict: false }) || getAddress17(r.asset) !== token) return `in ${r.asset} (this agent pays in ${token})`;
17178
17490
  return null;
17179
17491
  }
17180
17492
  var PAY_CHANNEL_DEPOSIT, PAY_MAX_BODY_CHARS, REQUEST_TIMEOUT_MS2, MIN_LIFE_LEFT_SECONDS, now2, PayService;
@@ -17239,7 +17551,7 @@ var init_pay = __esm({
17239
17551
  async #channelFor(payee, receiver, withdrawDelay, price) {
17240
17552
  for (const r of await this.#channels.payRecords()) {
17241
17553
  if (r.pay === void 0 || r.closeRequested) continue;
17242
- if (getAddress16(r.payee) !== payee || getAddress16(r.config.receiver) !== receiver || r.gracePeriod !== withdrawDelay) continue;
17554
+ if (getAddress17(r.payee) !== payee || getAddress17(r.config.receiver) !== receiver || r.gracePeriod !== withdrawDelay) continue;
17243
17555
  if (r.pay.expiresAt < now2() + MIN_LIFE_LEFT_SECONDS || payCapacity(r) < price) continue;
17244
17556
  const ch = await readChannel(this.#client, this.#config.deployment.batchSettlement, r.channel);
17245
17557
  if (ch.pendingWithdrawal !== null || ch.balance < BigInt(r.pay.cumulative) + price) continue;
@@ -17250,10 +17562,10 @@ var init_pay = __esm({
17250
17562
  /** The stock x402 client paying from channel record `r` (the vault as payer, the channel's voucher key signing, never depositing). */
17251
17563
  #x402(r) {
17252
17564
  const voucherKey = this.#channels.voucherSigner(r);
17253
- if (voucherKey.address !== getAddress16(r.config.payerAuthorizer)) throw new Error(`the voucher key of channel ${r.channel} is not derived from this machine's master seed`);
17565
+ if (voucherKey.address !== getAddress17(r.config.payerAuthorizer)) throw new Error(`the voucher key of channel ${r.channel} is not derived from this machine's master seed`);
17254
17566
  const client = this.#client;
17255
17567
  const payer = {
17256
- address: getAddress16(r.config.payer),
17568
+ address: getAddress17(r.config.payer),
17257
17569
  signTypedData: () => Promise.reject(new Error("a vault never signs typed data")),
17258
17570
  readContract: (args) => client.readContract(args)
17259
17571
  };
@@ -17322,13 +17634,13 @@ var init_pay = __esm({
17322
17634
  if (price > maxPrice) throw new Error(`${input.url} asks ${formatTokenAmount(price)}, more than maxPrice ${input.maxPrice}; nothing was paid`);
17323
17635
  const extra = offer.extra ?? {};
17324
17636
  if (typeof extra.receiverAuthorizer !== "string" || !isAddress10(extra.receiverAuthorizer, { strict: false })) throw new Error("the offer names no receiverAuthorizer; nothing was paid");
17325
- const payee = getAddress16(extra.receiverAuthorizer);
17637
+ const payee = getAddress17(extra.receiverAuthorizer);
17326
17638
  const withdrawDelay = extra.withdrawDelay;
17327
17639
  if (typeof withdrawDelay !== "number" || !Number.isInteger(withdrawDelay) || withdrawDelay < 900 || withdrawDelay > BATCH_MAX_WITHDRAW_DELAY_SECONDS) {
17328
17640
  throw new Error(`the offer's withdrawDelay ${String(withdrawDelay)} is not 900..${BATCH_MAX_WITHDRAW_DELAY_SECONDS} s; nothing was paid`);
17329
17641
  }
17330
17642
  if (!isAddress10(offer.payTo, { strict: false })) throw new Error("the offer's payTo is not an address; nothing was paid");
17331
- const receiver = getAddress16(offer.payTo);
17643
+ const receiver = getAddress17(offer.payTo);
17332
17644
  const payeeVault = await readVault(this.#client, this.#config.deployment, payee);
17333
17645
  if (payeeVault === null || payeeVault.token !== token) throw new Error(`the seller's receiverAuthorizer ${payee} is not an Ostra vault in ${token}: this agent pays Ostra sellers from its vault; nothing was paid`);
17334
17646
  if (payee === await this.#vault.vaultAddress()) throw new Error("the seller is this agent's own vault");
@@ -17396,8 +17708,21 @@ var init_pay = __esm({
17396
17708
  }
17397
17709
  });
17398
17710
 
17711
+ // ../core/src/seed-backup.ts
17712
+ import { scryptAsync } from "@noble/hashes/scrypt.js";
17713
+ var SCRYPT, AAD, SCRYPT_MAXMEM;
17714
+ var init_seed_backup = __esm({
17715
+ "../core/src/seed-backup.ts"() {
17716
+ "use strict";
17717
+ init_define_OSTRA_PACKAGED();
17718
+ SCRYPT = { N: 1 << 17, r: 8, p: 1 };
17719
+ AAD = new TextEncoder().encode("OSTRA/SEED-BACKUP/v1");
17720
+ SCRYPT_MAXMEM = 256 * 1024 * 1024;
17721
+ }
17722
+ });
17723
+
17399
17724
  // ../core/src/sessions.ts
17400
- import { bytesToHex as bytesToHex12, getAddress as getAddress17, isAddress as isAddress11 } from "viem";
17725
+ import { bytesToHex as bytesToHex12, getAddress as getAddress18, isAddress as isAddress11 } from "viem";
17401
17726
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
17402
17727
  var init_sessions = __esm({
17403
17728
  "../core/src/sessions.ts"() {
@@ -17409,19 +17734,19 @@ var init_sessions = __esm({
17409
17734
  });
17410
17735
 
17411
17736
  // ../core/src/shield.ts
17412
- import { bytesToHex as bytesToHex13, encodeFunctionData as encodeFunctionData9, getAddress as getAddress18, hexToBytes as hexToBytes8, parseSignature } from "viem";
17737
+ import { bytesToHex as bytesToHex13, encodeFunctionData as encodeFunctionData9, getAddress as getAddress19, hexToBytes as hexToBytes8, parseSignature } from "viem";
17413
17738
  function leafKeyIndex(attempt, j) {
17414
17739
  if (!Number.isInteger(attempt) || attempt < 0 || attempt >= MAX_LEAF_ATTEMPTS) throw new RangeError(`leaf set ${String(attempt)} is out of range`);
17415
17740
  if (!Number.isInteger(j) || j < 0 || j >= LEAF_KEYS_PER_ATTEMPT) throw new RangeError(`leaf ${String(j)} is out of range`);
17416
17741
  return attempt * LEAF_KEYS_PER_ATTEMPT + j;
17417
17742
  }
17418
17743
  function recordName(chainId, token, index) {
17419
- return `${chainId}-${getAddress18(token)}-${index}.json`;
17744
+ return `${chainId}-${getAddress19(token)}-${index}.json`;
17420
17745
  }
17421
17746
  function decodeApproveMessage(message) {
17422
17747
  if (message.length !== APPROVE_MESSAGE_LEN) throw new Error("not an approval message");
17423
17748
  const chainId = Number(BigInt(bytesToHex13(message.subarray(20, 52))));
17424
- const pool = getAddress18(bytesToHex13(message.subarray(52, 72)));
17749
+ const pool = getAddress19(bytesToHex13(message.subarray(52, 72)));
17425
17750
  const key = bytesToHex13(message.subarray(72, 104));
17426
17751
  const batchId = BigInt(bytesToHex13(message.subarray(104, 112)));
17427
17752
  const state = bytesToHex13(message.subarray(112, 144));
@@ -17496,7 +17821,7 @@ var init_shield = __esm({
17496
17821
  /** True when `record` belongs to this service's vault. */
17497
17822
  async #mine(record) {
17498
17823
  if (record.vault === void 0) return this.#vault.kind === "buyer";
17499
- return getAddress18(record.vault) === await this.#vault.vaultAddress();
17824
+ return getAddress19(record.vault) === await this.#vault.vaultAddress();
17500
17825
  }
17501
17826
  /** A new record under the first free deposit number (created exclusively: another vault's service may be creating one too). */
17502
17827
  async #createRecord(make) {
@@ -17657,8 +17982,8 @@ var init_shield = __esm({
17657
17982
  const { batchCount } = await readPool(this.#client, this.#pool());
17658
17983
  const health = await this.#aggregator("GET", "/v1/health");
17659
17984
  if (typeof health.aggregator !== "string") throw new Error("the aggregator's health names no aggregator account");
17660
- const aggregator = getAddress18(health.aggregator);
17661
- const accounts = /* @__PURE__ */ new Set([...(record.aggregators ?? []).map((a) => getAddress18(a)), aggregator]);
17985
+ const aggregator = getAddress19(health.aggregator);
17986
+ const accounts = /* @__PURE__ */ new Set([...(record.aggregators ?? []).map((a) => getAddress19(a)), aggregator]);
17662
17987
  if (accounts.size !== (record.aggregators ?? []).length) {
17663
17988
  record.aggregators = [...accounts];
17664
17989
  await this.#save(record);
@@ -17713,11 +18038,11 @@ var init_shield = __esm({
17713
18038
  const room = await this.#room(p.maxTotal);
17714
18039
  if (amount > room) throw new RangeError(`the pool has room for ${formatTokenAmount(room)} more, and a deposit of ${leaves} leaves is ${formatTokenAmount(amount)}; nothing was signed`);
17715
18040
  const terms = await this.#aggregator("GET", "/v1/terms");
17716
- if (terms.chainId !== this.#config.chainId || typeof terms.pool !== "string" || getAddress18(terms.pool) !== pool || typeof terms.token !== "string" || getAddress18(terms.token) !== this.#token()) {
18041
+ if (terms.chainId !== this.#config.chainId || typeof terms.pool !== "string" || getAddress19(terms.pool) !== pool || typeof terms.token !== "string" || getAddress19(terms.token) !== this.#token()) {
17717
18042
  throw new Error(`the aggregator serves pool ${String(terms.pool)} on chain ${String(terms.chainId)}, not ${pool} on ${this.#config.chainId}`);
17718
18043
  }
17719
18044
  if (typeof terms.aggregator !== "string") throw new Error("the aggregator's terms name no aggregator account");
17720
- const aggregatorAccount = getAddress18(terms.aggregator);
18045
+ const aggregatorAccount = getAddress19(terms.aggregator);
17721
18046
  const status = await this.#vault.status();
17722
18047
  if (!status.vaultExists) throw new Error("open the vault and fund it first (open_vault)");
17723
18048
  const vault = await this.#vault.vaultAddress();
@@ -17974,7 +18299,7 @@ var init_shield = __esm({
17974
18299
  leavesInPool.push({ batch: r.batch, leafIndex: i, key: leafKeyIndex(r.attempt ?? 0, j), amount: formatTokenAmount(LEAF_DENOMINATION), spent: b.status === "none" || leaf === void 0 || leaf.spent });
17975
18300
  });
17976
18301
  }
17977
- out.push({ index: r.index, vault: r.vault === void 0 ? null : getAddress18(r.vault), deposit: r.deposit, leaves: r.leaves, amount: formatTokenAmount(BigInt(r.amount)), status: r.status, batch: r.batch, leavesInPool });
18302
+ out.push({ index: r.index, vault: r.vault === void 0 ? null : getAddress19(r.vault), deposit: r.deposit, leaves: r.leaves, amount: formatTokenAmount(BigInt(r.amount)), status: r.status, batch: r.batch, leavesInPool });
17978
18303
  }
17979
18304
  return out;
17980
18305
  });
@@ -17992,9 +18317,9 @@ var init_storage = __esm({
17992
18317
  });
17993
18318
 
17994
18319
  // ../core/src/unshield.ts
17995
- import { encodeFunctionData as encodeFunctionData10, getAddress as getAddress19 } from "viem";
18320
+ import { encodeFunctionData as encodeFunctionData10, getAddress as getAddress20 } from "viem";
17996
18321
  function recordName2(chainId, token, index) {
17997
- return `${chainId}-${getAddress19(token)}-${index}.json`;
18322
+ return `${chainId}-${getAddress20(token)}-${index}.json`;
17998
18323
  }
17999
18324
  function unshieldDelaySeconds(windowHours) {
18000
18325
  if (!Number.isFinite(windowHours) || windowHours < 0 || windowHours > UNSHIELD_MAX_WINDOW_HOURS) {
@@ -18067,7 +18392,7 @@ var init_unshield = __esm({
18067
18392
  }
18068
18393
  /** The private vaults created so far (their leaf spent), by index. */
18069
18394
  vaults() {
18070
- return this.#serial(async () => (await this.#records()).filter((r) => r.status === "spent").map((r) => ({ index: BigInt(r.index), vault: getAddress19(r.vault) })));
18395
+ return this.#serial(async () => (await this.#records()).filter((r) => r.status === "spent").map((r) => ({ index: BigInt(r.index), vault: getAddress20(r.vault) })));
18071
18396
  }
18072
18397
  /** The number of the private vault at `vault`, or throws when it is none of this agent's. */
18073
18398
  async indexOf(vault) {
@@ -18166,7 +18491,7 @@ var init_unshield = __esm({
18166
18491
  async #spend(r) {
18167
18492
  const ctx = await this.#context(BigInt(r.index));
18168
18493
  const vault = await ctx.vault.vaultAddress();
18169
- if (vault !== getAddress19(r.vault)) throw new Error(`private vault ${r.index} is ${vault}, not the recorded ${r.vault}`);
18494
+ if (vault !== getAddress20(r.vault)) throw new Error(`private vault ${r.index} is ${vault}, not the recorded ${r.vault}`);
18170
18495
  const pool = this.#config.deployment.pool;
18171
18496
  const batch = await readBatch(this.#client, pool, BigInt(r.batch));
18172
18497
  const leaf = batch.leaves[r.leafIndex];
@@ -18205,10 +18530,10 @@ import {
18205
18530
  decodeErrorResult as decodeErrorResult2,
18206
18531
  encodeAbiParameters as encodeAbiParameters6,
18207
18532
  encodeFunctionData as encodeFunctionData11,
18208
- getAddress as getAddress20,
18533
+ getAddress as getAddress21,
18209
18534
  maxUint256,
18210
- parseAbi as parseAbi2,
18211
- zeroAddress as zeroAddress10
18535
+ parseAbi as parseAbi3,
18536
+ zeroAddress as zeroAddress11
18212
18537
  } from "viem";
18213
18538
  var MAX_UINT160, MAX_UINT128, universalRouterAbi, permit2Abi, wrappedErrorAbi;
18214
18539
  var init_wallet_swap = __esm({
@@ -18219,21 +18544,21 @@ var init_wallet_swap = __esm({
18219
18544
  init_pools();
18220
18545
  MAX_UINT160 = (1n << 160n) - 1n;
18221
18546
  MAX_UINT128 = (1n << 128n) - 1n;
18222
- universalRouterAbi = parseAbi2([
18547
+ universalRouterAbi = parseAbi3([
18223
18548
  "function execute(bytes commands, bytes[] inputs, uint256 deadline) payable",
18224
18549
  "error TransactionDeadlinePassed()",
18225
18550
  "error ExecutionFailed(uint256 commandIndex, bytes message)",
18226
18551
  "error V4TooLittleReceived(uint256 minAmountOutReceived, uint256 amountReceived)",
18227
18552
  "error V4TooMuchRequested(uint256 maxAmountInRequested, uint256 amountRequested)"
18228
18553
  ]);
18229
- permit2Abi = parseAbi2([
18554
+ permit2Abi = parseAbi3([
18230
18555
  "function allowance(address owner, address token, address spender) view returns (uint160 amount, uint48 expiration, uint48 nonce)",
18231
18556
  "error AllowanceExpired(uint256 deadline)",
18232
18557
  "error InsufficientAllowance(uint256 amount)",
18233
18558
  "error InvalidNonce()",
18234
18559
  "error SignatureExpired(uint256 signatureDeadline)"
18235
18560
  ]);
18236
- wrappedErrorAbi = parseAbi2(["error WrappedError(address target, bytes4 selector, bytes reason, bytes details)"]);
18561
+ wrappedErrorAbi = parseAbi3(["error WrappedError(address target, bytes4 selector, bytes reason, bytes details)"]);
18237
18562
  }
18238
18563
  });
18239
18564
 
@@ -18257,6 +18582,7 @@ var init_src2 = __esm({
18257
18582
  init_pools();
18258
18583
  init_random();
18259
18584
  init_relay();
18585
+ init_seed_backup();
18260
18586
  init_sessions();
18261
18587
  init_shield();
18262
18588
  init_storage();
@@ -18355,7 +18681,7 @@ import { join as join4 } from "node:path";
18355
18681
  import { createInterface } from "node:readline";
18356
18682
  import { Writable } from "node:stream";
18357
18683
  function scrypt(passphrase, salt) {
18358
- return new Promise((resolve, reject) => scryptCb(passphrase, salt, 32, SCRYPT, (err, key) => err === null ? resolve(key) : reject(err)));
18684
+ return new Promise((resolve, reject) => scryptCb(passphrase, salt, 32, SCRYPT2, (err, key) => err === null ? resolve(key) : reject(err)));
18359
18685
  }
18360
18686
  async function sealSeedWithPassphrase(seed, passphrase) {
18361
18687
  if (seed.length !== 32) throw new RangeError("the seed is 32 bytes");
@@ -18363,18 +18689,18 @@ async function sealSeedWithPassphrase(seed, passphrase) {
18363
18689
  const iv = randomBytes5(12);
18364
18690
  const key = await scrypt(passphrase, salt);
18365
18691
  const cipher = createCipheriv("aes-256-gcm", key, iv);
18366
- cipher.setAAD(AAD);
18692
+ cipher.setAAD(AAD2);
18367
18693
  const ciphertext = Buffer.concat([cipher.update(seed), cipher.final()]);
18368
- return `${JSON.stringify({ version: 1, kdf: { name: "scrypt", N: SCRYPT.N, r: SCRYPT.r, p: SCRYPT.p }, salt: salt.toString("hex"), iv: iv.toString("hex"), ciphertext: ciphertext.toString("hex"), tag: cipher.getAuthTag().toString("hex") })}
18694
+ return `${JSON.stringify({ version: 1, kdf: { name: "scrypt", N: SCRYPT2.N, r: SCRYPT2.r, p: SCRYPT2.p }, salt: salt.toString("hex"), iv: iv.toString("hex"), ciphertext: ciphertext.toString("hex"), tag: cipher.getAuthTag().toString("hex") })}
18369
18695
  `;
18370
18696
  }
18371
18697
  async function openSeedWithPassphrase(text2, passphrase) {
18372
18698
  const b = JSON.parse(text2);
18373
- if (b.version !== 1 || b.kdf?.name !== "scrypt" || b.kdf.N !== SCRYPT.N || b.kdf.r !== SCRYPT.r || b.kdf.p !== SCRYPT.p) throw new Error("not an Ostra seed backup (version 1)");
18699
+ if (b.version !== 1 || b.kdf?.name !== "scrypt" || b.kdf.N !== SCRYPT2.N || b.kdf.r !== SCRYPT2.r || b.kdf.p !== SCRYPT2.p) throw new Error("not an Ostra seed backup (version 1)");
18374
18700
  for (const f of [b.salt, b.iv, b.ciphertext, b.tag]) if (typeof f !== "string" || !/^[0-9a-f]+$/.test(f)) throw new Error("not an Ostra seed backup");
18375
18701
  const key = await scrypt(passphrase, Buffer.from(b.salt, "hex"));
18376
18702
  const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(b.iv, "hex"));
18377
- decipher.setAAD(AAD);
18703
+ decipher.setAAD(AAD2);
18378
18704
  decipher.setAuthTag(Buffer.from(b.tag, "hex"));
18379
18705
  let seed;
18380
18706
  try {
@@ -18437,7 +18763,7 @@ async function runBackup(args) {
18437
18763
  process.stderr.write(`the seed is in ${join4(home, MASTER_FILE)}; the MCP server recovers the vaults from it (the relayer's signature escrow sends any signature still in flight)
18438
18764
  `);
18439
18765
  }
18440
- var AAD, SCRYPT, MIN_PASSPHRASE;
18766
+ var AAD2, SCRYPT2, MIN_PASSPHRASE;
18441
18767
  var init_backup_cli = __esm({
18442
18768
  "src/backup-cli.ts"() {
18443
18769
  "use strict";
@@ -18445,8 +18771,8 @@ var init_backup_cli = __esm({
18445
18771
  init_disk();
18446
18772
  init_keys2();
18447
18773
  init_packaged();
18448
- AAD = Buffer.from("OSTRA/SEED-BACKUP/v1", "utf8");
18449
- SCRYPT = { N: 1 << 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 };
18774
+ AAD2 = Buffer.from("OSTRA/SEED-BACKUP/v1", "utf8");
18775
+ SCRYPT2 = { N: 1 << 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 };
18450
18776
  MIN_PASSPHRASE = 12;
18451
18777
  if (packaged === null && import.meta.url === `file://${process.argv[1]}`) {
18452
18778
  runBackup(process.argv.slice(2)).catch((err) => {
@@ -18461,7 +18787,7 @@ var init_backup_cli = __esm({
18461
18787
  // src/agent.ts
18462
18788
  import { readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
18463
18789
  import { join as join5 } from "node:path";
18464
- import { createPublicClient as createPublicClient2, getAddress as getAddress21, isAddress as isAddress12 } from "viem";
18790
+ import { createPublicClient as createPublicClient2, getAddress as getAddress22, isAddress as isAddress12 } from "viem";
18465
18791
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
18466
18792
  var SWEEP_SECONDS, SELLER_RECORD, MERCHANT_DIR, Agent;
18467
18793
  var init_agent = __esm({
@@ -18554,7 +18880,7 @@ var init_agent = __esm({
18554
18880
  if (text2 === void 0) return;
18555
18881
  const r = JSON.parse(text2);
18556
18882
  if (r.chainId !== this.config.chainId || typeof r.crank !== "string" || !isAddress12(r.crank, { strict: false })) throw new Error(`${SELLER_RECORD} is not a seller record of chain ${this.config.chainId}`);
18557
- this.seller.setInitCrank(getAddress21(r.crank));
18883
+ this.seller.setInitCrank(getAddress22(r.crank));
18558
18884
  }
18559
18885
  /** True once accept_payments set the seller vault up. */
18560
18886
  async sellerReady() {
@@ -18574,7 +18900,7 @@ var init_agent = __esm({
18574
18900
  let crank;
18575
18901
  const known = await this.keys.folder.read(SELLER_RECORD);
18576
18902
  if (known !== void 0) {
18577
- crank = getAddress21(JSON.parse(known).crank);
18903
+ crank = getAddress22(JSON.parse(known).crank);
18578
18904
  } else {
18579
18905
  let res;
18580
18906
  try {
@@ -18584,7 +18910,7 @@ var init_agent = __esm({
18584
18910
  }
18585
18911
  const body = await res.json().catch(() => null);
18586
18912
  if (res.status !== 200 || typeof body?.crank !== "string" || !isAddress12(body.crank, { strict: false })) throw new Error(`the keeper answered ${res.status} without a crank`);
18587
- crank = getAddress21(body.crank);
18913
+ crank = getAddress22(body.crank);
18588
18914
  this.seller.setInitCrank(crank);
18589
18915
  await this.keys.folder.write(SELLER_RECORD, `${JSON.stringify({ chainId: this.config.chainId, token, crank, merchant })}
18590
18916
  `);
@@ -18786,42 +19112,65 @@ function registerTools(server, agent) {
18786
19112
  tool("pool_info", "A token the Ostra launchpad launched: price, market cap, taxes, circuit breaker band, airdrop round, creator shares.", { token: address3 }, (a) => lp().poolInfo(a.token));
18787
19113
  tool(
18788
19114
  "quote_swap",
18789
- "Quotes a buy (USDG in) or sell (token in) as it would be signed now: expected and minimum output, the gas the swap reserves, the share of the circuit breaker's room used. Nothing is signed.",
19115
+ "Quotes a buy (the pool's pair in: USDG or a stock token) or sell (token in) as it would be signed now: expected and minimum output, the gas the swap reserves, the share of the circuit breaker's room used. Nothing is signed.",
18790
19116
  { side: z.enum(["buy", "sell"]), token: address3, amount: units, slippageBps: slippage2, referrer: address3.optional() },
18791
19117
  (a) => lp().quote({ side: a.side, token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
18792
19118
  );
18793
19119
  tool(
18794
19120
  "buy",
18795
- "Buys a launched token with USDG from the vault; the tokens go to the agent's vault for that token (created in the same transaction).",
18796
- { token: address3, amount: usdg, slippageBps: slippage2, referrer: address3.optional() },
19121
+ "Buys a launched token with its pool's pair: USDG from the vault, or a stock token from the agent's vault of that stock token (pairs: tradingVault). The tokens go to the agent's vault for that token (created in the same transaction). An ETH pool is traded from a wallet, not a vault.",
19122
+ { token: address3, amount: units.describe("in the pool's pair: USDG (6 decimals) or the stock token (18)"), slippageBps: slippage2, referrer: address3.optional() },
18797
19123
  (a) => lp().buy({ token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
18798
19124
  );
18799
19125
  tool(
18800
19126
  "sell",
18801
- "Sells a launched token from the agent's token vault for USDG into the vault.",
19127
+ "Sells a launched token from the agent's token vault for its pool's pair, into the agent's vault of that pair (USDG: the vault).",
18802
19128
  { token: address3, amount: units, slippageBps: slippage2, referrer: address3.optional() },
18803
19129
  (a) => lp().sell({ token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
18804
19130
  );
18805
19131
  tool("token_vault", "The agent's vault for a launched token: address, balance, dividends to claim.", { token: address3 }, (a) => lp().tokenVaultInfo(a.token));
18806
19132
  tool("transfer_token", "Sends a launched token from the agent's token vault to an address.", { token: address3, destination: address3, amount: units }, (a) => lp().transferToken(a));
18807
19133
  tool("claim_dividends", "Claims the dividends the agent's token vault earned, into the agent's vault of the dividend token.", { token: address3 }, (a) => lp().claimDividends(a.token));
18808
- tool("claim_referral", "Claims the USDG that swaps naming `referrer` (default: the agent's vault) earned it.", { referrer: address3.optional() }, (a) => lp().claimReferral(a.referrer === void 0 ? {} : { referrer: a.referrer }));
18809
- tool("fund_creator_vault", "Moves USDG from the vault into the creator vault, which pays launches, dev buys and airdrops (and its own relayer requests).", { amount: usdg }, (a) => lp().fundCreatorVault(a.amount));
18810
- tool("creator_withdraw", "Sends USDG from the creator vault (default: to the agent's vault): creator payouts it received, for example.", { amount: usdg, destination: address3.optional() }, async (a) => agent.creator.withdraw(a.destination ?? await agent.buyer.vaultAddress(), a.amount));
19134
+ tool(
19135
+ "claim_referral",
19136
+ "Claims what swaps naming `referrer` earned it in a pair (default USDG; referrer default: the agent's vault of that pair's token; ETH earnings are paid in WETH to the referrer named).",
19137
+ { referrer: address3.optional(), pair: pair2.optional() },
19138
+ (a) => lp().claimReferral({ ...a.referrer === void 0 ? {} : { referrer: a.referrer }, ...a.pair === void 0 ? {} : { pair: a.pair } })
19139
+ );
19140
+ tool(
19141
+ "pairs",
19142
+ "The pairs a launch can take (USDG, ETH and the listed stock tokens), each with its USD price now, this agent's creator vault for it (what that pair's dev buys are paid from, and what owns its pools and receives their creator payouts) and the agent's vault of its token (what its pools are bought with).",
19143
+ {},
19144
+ () => lp().pairs({ prices: true })
19145
+ );
19146
+ tool(
19147
+ "fund_creator_vault",
19148
+ "Moves funds into the creator vault of a pair (default USDG): USDG from the vault (USDG launches, dev buys, airdrops and the USDG creator vault's relayer requests); a stock token or WETH from the agent's vault of it. Funds can also be sent straight to a pair's creator vault (pairs).",
19149
+ { amount: units.describe("in the pair's token: USDG (6 decimals), WETH or the stock token (18)"), pair: pair2.optional() },
19150
+ (a) => lp().fundCreatorVault(a.amount, a.pair ?? "USDG")
19151
+ );
19152
+ tool(
19153
+ "creator_withdraw",
19154
+ "Sends funds from the creator vault of a pair (default USDG; default destination: the agent's vault of that token; a WETH creator vault names it): creator payouts it received, for example.",
19155
+ { amount: units.describe("in the pair's token"), destination: address3.optional(), pair: pair2.optional() },
19156
+ (a) => lp().creatorWithdraw({ amount: a.amount, ...a.destination === void 0 ? {} : { destination: a.destination }, ...a.pair === void 0 ? {} : { pair: a.pair } })
19157
+ );
18811
19158
  tool(
18812
19159
  "launch",
18813
- "Launches a token on the Ostra launchpad from the creator vault (USDG pool, 1 billion supply), optionally with a dev buy paid by the creator vault in the same transaction and vesting to the agent's token vault. The relayer fronts the 0.0002 ETH launch fee and charges it in USDG. The launch is simulated first; nothing is signed if it would fail. `options` sets LaunchParams fields (taxes, buckets, anti-snipe, limits, graduation, circuit breaker) beyond Ostra's defaults.",
19160
+ "Launches a token on the Ostra launchpad (1 billion supply) paired with USDG (default), ETH or a listed stock token (pairs), from the agent's creator vault of that pair, optionally with a dev buy in the pair's token paid by that creator vault in the same transaction and vesting to the agent's token vault. The market cap is in US dollars, set in the pair's token at its price now (Chainlink for ETH and stocks). The relayer fronts the 0.0002 ETH launch fee and charges it in USDG. The launch is simulated first; nothing is signed if it would fail. `options` sets LaunchParams fields (taxes, buckets, anti-snipe, limits, graduation, circuit breaker) beyond Ostra's defaults.",
18814
19161
  {
18815
19162
  name: z.string().min(1).max(64),
18816
19163
  symbol: z.string().regex(/^[A-Za-z0-9]{1,11}$/),
18817
- marketCap: usdg.optional().describe("the starting market cap in USDG (or startTick)"),
19164
+ pair: pair2.optional().describe("the pool's pair: USDG (default), ETH or a listed stock token's ticker"),
19165
+ marketCap: usdg.optional().describe("the starting market cap in US dollars (or startTick)"),
18818
19166
  startTick: z.number().int().optional(),
18819
- devBuy: z.object({ amount: usdg, cliffDays: z.number().min(7).max(365).optional(), vestingDays: z.number().min(0).max(365).optional() }).optional(),
19167
+ devBuy: z.object({ amount: units.describe("in the pair's token"), cliffDays: z.number().min(7).max(365).optional(), vestingDays: z.number().min(0).max(365).optional() }).optional(),
18820
19168
  options: z.record(z.string(), z.unknown()).optional()
18821
19169
  },
18822
19170
  (a) => lp().launch({
18823
19171
  name: a.name,
18824
19172
  symbol: a.symbol,
19173
+ ...a.pair === void 0 ? {} : { pair: a.pair },
18825
19174
  ...a.marketCap === void 0 ? {} : { marketCap: a.marketCap },
18826
19175
  ...a.startTick === void 0 ? {} : { startTick: a.startTick },
18827
19176
  ...a.devBuy === void 0 ? {} : { devBuy: { amount: a.devBuy.amount, ...a.devBuy.cliffDays === void 0 ? {} : { cliffDays: a.devBuy.cliffDays }, ...a.devBuy.vestingDays === void 0 ? {} : { vestingDays: a.devBuy.vestingDays } } },
@@ -18920,7 +19269,7 @@ function launchOptions(raw) {
18920
19269
  }
18921
19270
  return out;
18922
19271
  }
18923
- var toJson, address3, usdg, units, slippage2, bytes323, uri, publicFields, BIGINT_FIELDS, ALLOWED_OPTIONS;
19272
+ var toJson, address3, usdg, units, pair2, slippage2, bytes323, uri, publicFields, BIGINT_FIELDS, ALLOWED_OPTIONS;
18924
19273
  var init_tools = __esm({
18925
19274
  "src/tools.ts"() {
18926
19275
  "use strict";
@@ -18931,6 +19280,7 @@ var init_tools = __esm({
18931
19280
  address3 = z.string().regex(/^0x[0-9a-fA-F]{40}$/, "an address: 0x and 40 hex characters");
18932
19281
  usdg = z.string().regex(/^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$/, "a USDG amount: a plain decimal with at most 6 decimals");
18933
19282
  units = z.string().regex(/^(0|[1-9][0-9]*)(\.[0-9]{1,18})?$/, "an amount: a plain decimal with at most 18 decimals");
19283
+ pair2 = z.enum(LAUNCH_PAIRS.map((p) => p.symbol));
18934
19284
  slippage2 = z.number().int().min(0).max(1e3).optional().describe("slippage in bps (default 100 = 1%, at most 1000)");
18935
19285
  bytes323 = z.string().regex(/^0x[0-9a-fA-F]{64}$/, "32 bytes of 0x hex");
18936
19286
  uri = z.string().max(256).regex(/^(ipfs|https):\/\/[^\s]+$/, "an ipfs:// or https:// URI of at most 256 bytes");