@ostraagent/mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.2", 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
 
@@ -11721,7 +11721,7 @@ var init_hash = __esm({
11721
11721
  });
11722
11722
 
11723
11723
  // ../sdk/src/constants.ts
11724
- var ROBINHOOD_CHAIN_ID, ROBINHOOD_PUBLIC_RPC, USDG, USDG_EIP712_DOMAIN, X402_BATCH_SETTLEMENT, X402_BATCH_SETTLEMENT_DOMAIN, X402_MIN_WITHDRAW_DELAY, X402_MAX_WITHDRAW_DELAY, UNISWAP_V4_POOL_MANAGER, CHAINLINK_ETH_USD, CHAINLINK_ETH_USD_DECIMALS, ERC8004_IDENTITY_REGISTRY, WETH, MULTICALL3, ARBITRUM_NODE_INTERFACE, MAX_FEE_BPS, DEPOSIT_TIMEOUT_SECONDS, VAULT_SWAP_GAS_RESERVE, VAULT_VERSION, VAULT_MAX_URI_LENGTH, VAULT_ACTION, MIN_RECOVERY_DELAY, MAX_RECOVERY_DELAY, POOL_SETTING, CIRCUIT_BREAKER, VAULT_SWAP_GAS, LAUNCHPAD, DEV_BUY, AIRDROP, CREATOR_SPLIT, DEPOSIT_STATUS, BATCH_STATUS;
11724
+ var ROBINHOOD_CHAIN_ID, ROBINHOOD_PUBLIC_RPC, USDG, USDG_EIP712_DOMAIN, X402_BATCH_SETTLEMENT, X402_BATCH_SETTLEMENT_DOMAIN, X402_MIN_WITHDRAW_DELAY, X402_MAX_WITHDRAW_DELAY, UNISWAP_V4_POOL_MANAGER, CHAINLINK_ETH_USD, CHAINLINK_ETH_USD_DECIMALS, ERC8004_IDENTITY_REGISTRY, WETH, OSTRA, OSTRA_DECIMALS, OSTRA_PONS_CURVE, MULTICALL3, ARBITRUM_NODE_INTERFACE, MAX_FEE_BPS, DEPOSIT_TIMEOUT_SECONDS, VAULT_SWAP_GAS_RESERVE, VAULT_VERSION, VAULT_MAX_URI_LENGTH, VAULT_ACTION, MIN_RECOVERY_DELAY, MAX_RECOVERY_DELAY, POOL_SETTING, CIRCUIT_BREAKER, VAULT_SWAP_GAS, LAUNCHPAD, DEV_BUY, AIRDROP, CREATOR_SPLIT, DEPOSIT_STATUS, BATCH_STATUS;
11725
11725
  var init_constants = __esm({
11726
11726
  "../sdk/src/constants.ts"() {
11727
11727
  "use strict";
@@ -11744,6 +11744,9 @@ var init_constants = __esm({
11744
11744
  CHAINLINK_ETH_USD_DECIMALS = 8;
11745
11745
  ERC8004_IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
11746
11746
  WETH = "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73";
11747
+ OSTRA = "0x68Ff81dFFF6eccefb74706fE1e0a86320CB2Eb96";
11748
+ OSTRA_DECIMALS = 18;
11749
+ OSTRA_PONS_CURVE = "0xef89C3a9832Ae6f8Dc5F785484b1B5cb1120F372";
11747
11750
  MULTICALL3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
11748
11751
  ARBITRUM_NODE_INTERFACE = "0x00000000000000000000000000000000000000C8";
11749
11752
  MAX_FEE_BPS = 500;
@@ -12120,7 +12123,7 @@ var init_deployment = __esm({
12120
12123
 
12121
12124
  // ../sdk/src/external-abi.ts
12122
12125
  import { parseAbi } from "viem";
12123
- var usdgAbi, chainlinkAggregatorAbi, multicall3Abi, nodeInterfaceAbi;
12126
+ var usdgAbi, chainlinkAggregatorAbi, ponsCurveAbi, multicall3Abi, nodeInterfaceAbi;
12124
12127
  var init_external_abi = __esm({
12125
12128
  "../sdk/src/external-abi.ts"() {
12126
12129
  "use strict";
@@ -12147,6 +12150,13 @@ var init_external_abi = __esm({
12147
12150
  "function description() view returns (string)",
12148
12151
  "function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
12149
12152
  ]);
12153
+ ponsCurveAbi = parseAbi([
12154
+ "function token() view returns (address)",
12155
+ "function graduated() view returns (bool)",
12156
+ "function graduationThreshold() view returns (uint256)",
12157
+ "function getReserves() view returns (uint256 ethReserve, uint256 tokenReserve)",
12158
+ "function buy(uint256 ethIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)"
12159
+ ]);
12150
12160
  multicall3Abi = parseAbi([
12151
12161
  "struct Call3 { address target; bool allowFailure; bytes callData; }",
12152
12162
  "struct Result { bool success; bytes returnData; }",
@@ -13598,6 +13608,120 @@ var init_quote = __esm({
13598
13608
  }
13599
13609
  });
13600
13610
 
13611
+ // ../sdk/src/pairs.ts
13612
+ import { getAddress as getAddress9, parseAbi as parseAbi2, zeroAddress as zeroAddress6 } from "viem";
13613
+ function findLaunchPair(symbolOrAddress) {
13614
+ const s = symbolOrAddress.trim();
13615
+ if (/^0x[0-9a-fA-F]{40}$/.test(s)) {
13616
+ const a = getAddress9(s);
13617
+ return LAUNCH_PAIRS.find((q) => q.address === a) ?? null;
13618
+ }
13619
+ const u = s.toUpperCase();
13620
+ return LAUNCH_PAIRS.find((q) => q.symbol === u) ?? null;
13621
+ }
13622
+ function launchPair(symbolOrAddress) {
13623
+ const q = findLaunchPair(symbolOrAddress);
13624
+ if (q === null) throw new RangeError(`${symbolOrAddress} is not a pair Ostra offers; the pairs are ${LAUNCH_PAIRS.map((x) => x.symbol).join(", ")}`);
13625
+ return q;
13626
+ }
13627
+ async function readPairUsdPrice(client, quote) {
13628
+ const block = await client.getBlock();
13629
+ const now3 = Number(block.timestamp);
13630
+ if (quote.feed === null) return { quote, priceE8: 100000000n, updatedAt: now3 };
13631
+ const [decimals, round] = await Promise.all([
13632
+ client.readContract({ address: quote.feed, abi: chainlinkAggregatorAbi, functionName: "decimals" }),
13633
+ client.readContract({ address: quote.feed, abi: chainlinkAggregatorAbi, functionName: "latestRoundData" })
13634
+ ]);
13635
+ if (Number(decimals) !== 8) throw new Error(`the ${quote.symbol}/USD feed ${quote.feed} has ${decimals} decimals, not 8`);
13636
+ const answer = round[1];
13637
+ const updatedAt = Number(round[3]);
13638
+ if (answer <= 0n) throw new Error(`the ${quote.symbol}/USD feed ${quote.feed} answers ${answer}`);
13639
+ const maxAge = quote.kind === "stock" ? STOCK_PRICE_MAX_AGE : ETH_PRICE_MAX_AGE;
13640
+ if (now3 - updatedAt > maxAge) {
13641
+ 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`);
13642
+ }
13643
+ let priceE8 = answer;
13644
+ if (quote.kind === "ostra") {
13645
+ const [graduated, reserves] = await Promise.all([
13646
+ client.readContract({ address: OSTRA_PONS_CURVE, abi: ponsCurveAbi, functionName: "graduated" }),
13647
+ client.readContract({ address: OSTRA_PONS_CURVE, abi: ponsCurveAbi, functionName: "getReserves" })
13648
+ ]);
13649
+ if (graduated) throw new Error("OSTRA has graduated from its Pons curve and migrated; its price is not read from its new pool yet");
13650
+ const [ethReserve, tokenReserve] = reserves;
13651
+ if (ethReserve <= 0n || tokenReserve <= 0n) throw new Error(`the OSTRA Pons curve reports reserves of ${ethReserve} wei and ${tokenReserve} tokens`);
13652
+ priceE8 = answer * ethReserve / tokenReserve;
13653
+ if (priceE8 < 100n) throw new Error(`OSTRA's price (${priceE8} x 0.00000001 USD) is too small to set a market cap in within 1%`);
13654
+ }
13655
+ if (quote.kind === "stock") {
13656
+ const multiplier = await client.readContract({ address: quote.address, abi: stockTokenAbi, functionName: "uiMultiplier" });
13657
+ if (multiplier <= 0n) throw new Error(`${quote.symbol} reports a uiMultiplier of ${multiplier}`);
13658
+ priceE8 = answer * multiplier / 10n ** 18n;
13659
+ }
13660
+ return { quote, priceE8, updatedAt };
13661
+ }
13662
+ function usdToQuoteUnits(usdE6, priceE8, decimals) {
13663
+ if (usdE6 <= 0n) throw new RangeError("the amount in USD must be positive");
13664
+ if (priceE8 <= 0n) throw new RangeError("the price must be positive");
13665
+ return usdE6 * 10n ** BigInt(decimals) * 100n / priceE8;
13666
+ }
13667
+ var STOCK_PRICE_MAX_AGE, ETH_PRICE_MAX_AGE, stock, LAUNCH_PAIRS, stockTokenAbi;
13668
+ var init_pairs = __esm({
13669
+ "../sdk/src/pairs.ts"() {
13670
+ "use strict";
13671
+ init_define_OSTRA_PACKAGED();
13672
+ init_constants();
13673
+ init_external_abi();
13674
+ STOCK_PRICE_MAX_AGE = 4 * 86400;
13675
+ ETH_PRICE_MAX_AGE = 86400 + 3600;
13676
+ stock = (symbol, name, address4, feed) => ({
13677
+ symbol,
13678
+ name,
13679
+ kind: "stock",
13680
+ address: getAddress9(address4),
13681
+ decimals: 18,
13682
+ feed: getAddress9(feed)
13683
+ });
13684
+ LAUNCH_PAIRS = [
13685
+ { symbol: "USDG", name: "Global Dollar", kind: "usdg", address: USDG, decimals: 6, feed: null },
13686
+ { symbol: "ETH", name: "Ether", kind: "eth", address: zeroAddress6, decimals: 18, feed: CHAINLINK_ETH_USD },
13687
+ { symbol: "OSTRA", name: "Ostra Agent", kind: "ostra", address: OSTRA, decimals: OSTRA_DECIMALS, feed: CHAINLINK_ETH_USD },
13688
+ stock("AAPL", "Apple", "0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9", "0x6B22A786bAa607d76728168703a39Ea9C99f2cD0"),
13689
+ stock("AMZN", "Amazon", "0x12f190a9F9d7D37a250758b26824B97CE941bF54", "0xD5a1508ceD74c084eBf3cBe853e2C968fB2a651C"),
13690
+ stock("ASML", "ASML Holding", "0x47F93d52cBeC7C6D2CfC080e154002370a60dAEA", "0xB4106147E8cce40b7d46124090d373A71b70f87D"),
13691
+ stock("BABA", "Alibaba Group", "0xad25Ac6C84D497db898fa1E8387bf6Af3532a1c4", "0x62Cc8F9b5f56a33c9C8A60c8B92779f523c4E984"),
13692
+ stock("CLSK", "CleanSpark", "0xcBB95BBF36099d34dA091dc6Fa6F49EfA257Cee3", "0x810c12D3a554Bc47fd39597Fe3b3AAC4941F50eF"),
13693
+ stock("COIN", "Coinbase", "0x6330D8C3178a418788dF01a47479c0ce7CCF450b", "0xA3a468A452940B7D6b69991207B508c609a98Ef2"),
13694
+ stock("CRCL", "Circle Internet Group", "0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5", "0x6652eDf64bA3731C4F2D3ce821A0Fb1f1f6b482a"),
13695
+ stock("DELL", "Dell Technologies", "0x941AE714EC6D8130c7B75d67160Ca08f1e7d11Dd", "0x1C6c8cADBe02E19129c39dDB92281cE4c0bf206b"),
13696
+ stock("EWY", "iShares MSCI South Korea ETF", "0x7f0aBeF0C07280F82c6a08ead09dEd6BAE2C13Fc", "0xEFdf54610B62A7753Ec30bDc380847c12D32e1D1"),
13697
+ stock("GOOGL", "Alphabet", "0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3", "0xF6f373a037c30F0e5010d854385cA89185AE638b"),
13698
+ stock("INTC", "Intel", "0xc72b96e0E48ecd4DC75E1e45396e26300BC39681", "0x3f390C5C24628Ac7C489515402235FeAD71D1913"),
13699
+ stock("IONQ", "IonQ", "0x558378E000D634A36593E338eBacdd6207640EfE", "0x22EfeC4919baf55F360E0EDee4AbEB26DE4971eb"),
13700
+ stock("META", "Meta", "0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35", "0x7C38C00C30BEe9378381E7B6135d7283356D71b1"),
13701
+ stock("MSFT", "Microsoft", "0xe93237C50D904957Cf27E7B1133b510C669c2e74", "0x45C3C877C15E6BA2EBB19eA114Ea508d14C1Af2E"),
13702
+ stock("MSTR", "MicroStrategy", "0xec262a75e413fAfD0dF80480274532C79D42da09", "0x396118bdFB181e6240E74D243F266B061c0edc3D"),
13703
+ stock("MU", "Micron Technology", "0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD", "0x425EEFdCf05ed6526C3cE61Af99429A228a6d596"),
13704
+ stock("NBIS", "Nebius Group", "0x9D9c6684F596F66a64C030B93A886D51Fd4D7931", "0xE1D87B116Ba0fe898998f1D140339D1fA1E09705"),
13705
+ stock("NVDA", "Nvidia", "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC", "0x379EC4f7C378F34a1B47E4F3cbeBCbAC3E8E9F15"),
13706
+ stock("ORCL", "Oracle", "0xb0992820E760d836549ba69BC7598b4af75dEE03", "0x0e6a64a2B58A6693a531E6c555f3A5d042eEA844"),
13707
+ stock("PLTR", "Palantir Technologies", "0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A", "0x820ABedFF239034956B7A9d2F0a331f9F075eB4c"),
13708
+ stock("QQQ", "Invesco QQQ ETF", "0xD5f3879160bc7c32ebb4dC785F8a4F505888de68", "0x80901d846d5D7B030F26B480776EE3b29374C2ae"),
13709
+ stock("RGTI", "Rigetti Computing", "0x284358abc07F9359f19f4b5b4aC91901Be2597Ba", "0x2A045cF1C49c61c166C036d2f06FA2D2d984f765"),
13710
+ stock("RKLB", "Rocket Lab", "0x3b14C39E89D60D627b42a1A4CA45b5bb45Fc12e2", "0x045477BF65Aef6f4F2386ad0164579e48381CC74"),
13711
+ stock("SGOV", "iShares 0-3 Month Treasury Bond ETF", "0x92FD66527192E3e61d4DDd13322Aa222DE86F9B5", "0xa0DF4ee0fFf975306345875E3548Fcc519577A11"),
13712
+ stock("SLV", "iShares Silver Trust ETF", "0x411eFb0E7f985935DAec3D4C3ebaEa0d0AD7D89f", "0x209b73908e92Ae021826eD79609845451Ecba2ce"),
13713
+ stock("SNDK", "SanDisk", "0xB90A19fF0Af67f7779afF50A882A9CfF42446400", "0xfb133Fa4B7b385802B693a293606682Df47109A3"),
13714
+ stock("SPCX", "Space Exploration Technologies", "0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa", "0xB265810950ba6c5C0Ff821c9963014a56fD8Bffb"),
13715
+ stock("SPY", "SPDR S&P 500 ETF", "0x117cc2133c37B721F49dE2A7a74833232B3B4C0C", "0x319724394D3A0e3669269846abE664Cd621f9f6A"),
13716
+ stock("TSLA", "Tesla", "0x322F0929c4625eD5bAd873c95208D54E1c003b2d", "0x4A1166a659A55625345e9515b32adECea5547C38"),
13717
+ stock("TSM", "Taiwan Semiconductor", "0x58FfE4a942d3885bAa22D7520691F611EF09e7AA", "0x874cF94aa8eC88Fd9560094dD065f2fB3E41Fc2F"),
13718
+ stock("USAR", "USA Rare Earth", "0xd917B029C761D264c6A312BBbcDA868658eF86a6", "0xA994d3684e8400A6c8078226925779FdeE682DD9"),
13719
+ stock("USO", "United States Oil Fund ETF", "0xa30FA36Db767ad9eD3f7a60fC79526fB4d56D344", "0x75a9c76Ef439e2C7c2E5a34Ab105EcFe3766431c")
13720
+ ];
13721
+ stockTokenAbi = parseAbi2(["function uiMultiplier() view returns (uint256)"]);
13722
+ }
13723
+ });
13724
+
13601
13725
  // ../sdk/src/browser.ts
13602
13726
  var init_browser = __esm({
13603
13727
  "../sdk/src/browser.ts"() {
@@ -13617,6 +13741,7 @@ var init_browser = __esm({
13617
13741
  init_lthash();
13618
13742
  init_messages();
13619
13743
  init_quote();
13744
+ init_pairs();
13620
13745
  init_reads();
13621
13746
  init_relayer();
13622
13747
  init_wots();
@@ -13624,7 +13749,7 @@ var init_browser = __esm({
13624
13749
  });
13625
13750
 
13626
13751
  // ../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";
13752
+ import { concat as concat3, encodeDeployData, encodeFunctionData as encodeFunctionData5, getAddress as getAddress10, getContractAddress as getContractAddress2, keccak256 as keccak2569, numberToHex as numberToHex2 } from "viem";
13628
13753
  var init_deploy = __esm({
13629
13754
  "../sdk/src/deploy.ts"() {
13630
13755
  "use strict";
@@ -13859,13 +13984,13 @@ var init_config = __esm({
13859
13984
  });
13860
13985
 
13861
13986
  // ../core/src/relay.ts
13862
- import { decodeFunctionData as decodeFunctionData3, getAddress as getAddress10, isAddress as isAddress5 } from "viem";
13987
+ import { decodeFunctionData as decodeFunctionData3, getAddress as getAddress11, isAddress as isAddress5 } from "viem";
13863
13988
  import { x402Client, x402HTTPClient } from "@x402/core/client";
13864
13989
  import { toClientEvmSigner } from "@x402/evm";
13865
13990
  import { BatchSettlementEvmScheme } from "@x402/evm/batch-settlement/client";
13866
13991
  function address(value, what) {
13867
13992
  if (typeof value !== "string" || !isAddress5(value, { strict: false })) throw new Error(`${what} is not an address`);
13868
- return getAddress10(value);
13993
+ return getAddress11(value);
13869
13994
  }
13870
13995
  function checkSplits(raw, what) {
13871
13996
  if (!Array.isArray(raw)) throw new Error(`${what} are not a list`);
@@ -13886,7 +14011,7 @@ function checkRelayerFacts(raw, want) {
13886
14011
  const f = raw;
13887
14012
  if (f === null || typeof f !== "object") throw new Error("the relayer's terms are not an object");
13888
14013
  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}`);
14014
+ 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
14015
  if (f.decimals !== TOKEN_DECIMALS) throw new Error(`the relayer's token has ${String(f.decimals)} decimals, not ${TOKEN_DECIMALS}`);
13891
14016
  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
14017
  if (f.maxCalls !== MAX_RELAY_CALLS) throw new Error(`the relayer takes ${String(f.maxCalls)} calls per request, not ${MAX_RELAY_CALLS}`);
@@ -13914,10 +14039,10 @@ function relayerChannelDeposit(needed, vaultBalance, keep = 0n) {
13914
14039
  function relayerOfferProblem(r, want) {
13915
14040
  if (r.scheme !== "batch-settlement") return `the offer is ${r.scheme}, not batch-settlement`;
13916
14041
  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}`;
14042
+ if (!isAddress5(r.asset, { strict: false }) || getAddress11(r.asset) !== getAddress11(want.config.token)) return `the offer is in ${r.asset}, not ${want.config.token}`;
14043
+ 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
14044
  const extra = r.extra ?? {};
13920
- if (typeof extra.receiverAuthorizer !== "string" || !isAddress5(extra.receiverAuthorizer, { strict: false }) || getAddress10(extra.receiverAuthorizer) !== getAddress10(want.config.receiverAuthorizer)) {
14045
+ if (typeof extra.receiverAuthorizer !== "string" || !isAddress5(extra.receiverAuthorizer, { strict: false }) || getAddress11(extra.receiverAuthorizer) !== getAddress11(want.config.receiverAuthorizer)) {
13921
14046
  return `the offer's receiverAuthorizer is ${String(extra.receiverAuthorizer)}, not the relayer's vault ${want.config.receiverAuthorizer}`;
13922
14047
  }
13923
14048
  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 +14055,7 @@ function relayerOfferProblem(r, want) {
13930
14055
  }
13931
14056
  function openChannelCall(calls, vault, token) {
13932
14057
  for (const c of calls) {
13933
- if (getAddress10(c.to) !== getAddress10(vault)) continue;
14058
+ if (getAddress11(c.to) !== getAddress11(vault)) continue;
13934
14059
  let decoded;
13935
14060
  try {
13936
14061
  decoded = decodeFunctionData3({ abi: ostraVaultAbi, data: c.data });
@@ -13945,7 +14070,7 @@ function openChannelCall(calls, vault, token) {
13945
14070
  }
13946
14071
  return null;
13947
14072
  }
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;
14073
+ 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
14074
  var init_relay = __esm({
13950
14075
  "../core/src/relay.ts"() {
13951
14076
  "use strict";
@@ -13960,6 +14085,8 @@ var init_relay = __esm({
13960
14085
  MAX_STEPS = 8;
13961
14086
  MAX_RESENDS = 3;
13962
14087
  LANDED_TIMEOUT_MS = 6e4;
14088
+ OPEN_READ_TRIES = 20;
14089
+ OPEN_READ_INTERVAL_MS = 500;
13963
14090
  BPS_DENOMINATOR = 1e4;
13964
14091
  now = () => Math.floor(Date.now() / 1e3);
13965
14092
  LocalSender = class {
@@ -14014,7 +14141,7 @@ var init_relay = __esm({
14014
14141
  #name;
14015
14142
  constructor(agentFolder, chainId, token, relayerVault) {
14016
14143
  this.#folder = agentFolder.folder(RELAYER_DIR);
14017
- this.#name = `${chainId}-${getAddress10(token)}-${getAddress10(relayerVault)}.json`;
14144
+ this.#name = `${chainId}-${getAddress11(token)}-${getAddress11(relayerVault)}.json`;
14018
14145
  }
14019
14146
  async load() {
14020
14147
  const text2 = await this.#folder.read(this.#name);
@@ -14099,7 +14226,7 @@ var init_relay = __esm({
14099
14226
  functionName: "splitAddress",
14100
14227
  args: [d.token, facts.vault, facts.splits.map((s) => s.recipient), facts.splits.map((s) => s.bps)]
14101
14228
  });
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})`);
14229
+ 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
14230
  this.#facts = facts;
14104
14231
  this.#store = new RelayerStore(this.#keys.folder, this.#config.chainId, d.token, facts.vault);
14105
14232
  return facts;
@@ -14121,7 +14248,7 @@ var init_relay = __esm({
14121
14248
  channel: String(open3.channel).toLowerCase(),
14122
14249
  amount: BigInt(String(open3.amount)),
14123
14250
  txHash: String(open3.txHash),
14124
- calls: Array.isArray(open3.calls) ? open3.calls.map((c) => ({ to: getAddress10(c.to), data: c.data })) : []
14251
+ calls: Array.isArray(open3.calls) ? open3.calls.map((c) => ({ to: getAddress11(c.to), data: c.data })) : []
14125
14252
  }
14126
14253
  };
14127
14254
  }
@@ -14175,7 +14302,7 @@ var init_relay = __esm({
14175
14302
  continue;
14176
14303
  }
14177
14304
  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;
14305
+ 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
14306
  if (!usable) {
14180
14307
  c.retired = true;
14181
14308
  await this.#save();
@@ -14243,7 +14370,7 @@ var init_relay = __esm({
14243
14370
  else this.#reserved.delete(reservation.channel);
14244
14371
  }
14245
14372
  async #post(calls, extra, headers = {}) {
14246
- const body = { calls: calls.map((c) => ({ to: getAddress10(c.to), data: c.data })) };
14373
+ const body = { calls: calls.map((c) => ({ to: getAddress11(c.to), data: c.data })) };
14247
14374
  if (extra.prepay === true) body.prepay = true;
14248
14375
  if (extra.payer !== null) body.payer = extra.payer;
14249
14376
  if (extra.allowFailedSwaps === true) body.allowFailedSwaps = true;
@@ -14252,10 +14379,10 @@ var init_relay = __esm({
14252
14379
  /** 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
14380
  #x402(c) {
14254
14381
  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`);
14382
+ 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
14383
  const client = this.#client;
14257
14384
  const payer = {
14258
- address: getAddress10(c.config.payer),
14385
+ address: getAddress11(c.config.payer),
14259
14386
  signTypedData: () => Promise.reject(new Error("a vault never signs typed data")),
14260
14387
  readContract: (args) => client.readContract(args)
14261
14388
  };
@@ -14338,7 +14465,7 @@ var init_relay = __esm({
14338
14465
  * records it, and pays the open and the vault's debt with its first
14339
14466
  * voucher (the same request sent again).
14340
14467
  */
14341
- async #payOpen(calls, channel, paymentDue, debt) {
14468
+ async #payOpen(calls, channel, openTx, paymentDue, debt) {
14342
14469
  const facts = await this.facts();
14343
14470
  const d = this.#config.deployment;
14344
14471
  const vault = await this.#buyerVault().vaultAddress();
@@ -14347,12 +14474,12 @@ var init_relay = __esm({
14347
14474
  const config = found.config;
14348
14475
  const id = channelId(config, this.#config.chainId, d.batchSettlement).toLowerCase();
14349
14476
  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) {
14477
+ if (getAddress11(config.receiver) !== facts.split || getAddress11(config.receiverAuthorizer) !== facts.vault || config.withdrawDelay !== facts.gracePeriodSeconds) {
14351
14478
  throw new Error(`channel ${channel} is not on the relayer's terms`);
14352
14479
  }
14353
14480
  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);
14481
+ 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`);
14482
+ const ch = await this.#openedChannel(id, openTx);
14356
14483
  if (ch.balance !== found.amount || ch.pendingWithdrawal !== null) throw new Error(`relayer channel ${channel} does not hold its deposit of ${found.amount}`);
14357
14484
  const most = debt + await this.#spendBound();
14358
14485
  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 +14506,22 @@ var init_relay = __esm({
14379
14506
  /** Pays an open the relayer sent before payment that this machine did not pay (it stopped, or was restored from the seed). */
14380
14507
  async #payUnpaidOpen(unpaid, debt) {
14381
14508
  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));
14509
+ await this.#landed(await this.#payOpen(unpaid.calls, unpaid.channel, unpaid.txHash, unpaid.amount, debt));
14510
+ }
14511
+ /**
14512
+ * Channel `id` as its open `openTx` left it: the open's receipt on this RPC
14513
+ * first (it must have landed and succeeded), then reads until the channel
14514
+ * shows on the node that answers, for at most OPEN_READ_TRIES reads.
14515
+ */
14516
+ async #openedChannel(id, openTx) {
14517
+ await this.#landed(openTx);
14518
+ const settlement = this.#config.deployment.batchSettlement;
14519
+ let ch = await readChannel(this.#client, settlement, id);
14520
+ for (let i = 1; i < OPEN_READ_TRIES && ch.balance === 0n && ch.pendingWithdrawal === null; i++) {
14521
+ await new Promise((resolve) => setTimeout(resolve, OPEN_READ_INTERVAL_MS));
14522
+ ch = await readChannel(this.#client, settlement, id);
14523
+ }
14524
+ return ch;
14383
14525
  }
14384
14526
  /** Waits until `hash` has a successful receipt on this RPC. */
14385
14527
  async #landed(hash) {
@@ -14431,7 +14573,7 @@ var init_relay = __esm({
14431
14573
  const b = answer.body;
14432
14574
  const error = typeof b?.error === "string" ? b.error : "";
14433
14575
  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;
14576
+ 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
14577
  await this.#landed(hash);
14436
14578
  return hash;
14437
14579
  }
@@ -14491,7 +14633,7 @@ var init_relay = __esm({
14491
14633
  });
14492
14634
 
14493
14635
  // ../core/src/channels.ts
14494
- import { bytesToHex as bytesToHex7, decodeFunctionData as decodeFunctionData4, encodeFunctionData as encodeFunctionData6, getAddress as getAddress11, isAddress as isAddress6, parseAbiItem } from "viem";
14636
+ import { bytesToHex as bytesToHex7, decodeFunctionData as decodeFunctionData4, encodeFunctionData as encodeFunctionData6, getAddress as getAddress12, isAddress as isAddress6, parseAbiItem } from "viem";
14495
14637
  async function channelOpenedLogs(client, vault, channel, fromBlock, toBlock) {
14496
14638
  const out = [];
14497
14639
  for (let from = fromBlock; from <= toBlock; from += LOG_WINDOW_BLOCKS) {
@@ -14507,7 +14649,7 @@ function parseRecipients(input) {
14507
14649
  const out = [];
14508
14650
  for (const r of input) {
14509
14651
  if (!isAddress6(r.recipient, { strict: false })) throw new RangeError(`${r.recipient} is not an address`);
14510
- const recipient = getAddress11(r.recipient);
14652
+ const recipient = getAddress12(r.recipient);
14511
14653
  if (BigInt(recipient) === 0n) throw new RangeError("a recipient must not be the zero address");
14512
14654
  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
14655
  if (seen.has(recipient)) throw new RangeError(`${recipient} is listed twice`);
@@ -14526,7 +14668,7 @@ function parseGracePeriod(seconds) {
14526
14668
  }
14527
14669
  async function splitAddress(client, config, vault, recipients) {
14528
14670
  const d = config.deployment;
14529
- return getAddress11(
14671
+ return getAddress12(
14530
14672
  await client.readContract({
14531
14673
  address: d.splitFactory,
14532
14674
  abi: ostraSplitFactoryAbi,
@@ -14560,12 +14702,12 @@ async function recoverChannelConfig(client, config, vault, channel) {
14560
14702
  if (log2.transactionHash === null) continue;
14561
14703
  const tx = await client.getTransaction({ hash: log2.transactionHash });
14562
14704
  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) {
14705
+ if (tx.to !== null && tx.to !== void 0 && getAddress12(tx.to) === getAddress12(vault)) calls.push({ to: vault, data: tx.input });
14706
+ if (tx.to !== null && tx.to !== void 0 && getAddress12(tx.to) === MULTICALL3) {
14565
14707
  try {
14566
14708
  const decoded = decodeFunctionData4({ abi: multicall3Abi, data: tx.input });
14567
14709
  const inner = decoded.args[0];
14568
- for (const c of inner) if (getAddress11(c.target) === getAddress11(vault)) calls.push({ to: vault, data: c.callData });
14710
+ for (const c of inner) if (getAddress12(c.target) === getAddress12(vault)) calls.push({ to: vault, data: c.callData });
14569
14711
  } catch {
14570
14712
  }
14571
14713
  }
@@ -14645,7 +14787,7 @@ var init_channels = __esm({
14645
14787
  const text2 = await this.#folder.read(name);
14646
14788
  if (text2 === void 0) continue;
14647
14789
  const record = this.#check(JSON.parse(text2), name);
14648
- if (record.chainId === chainId && getAddress11(record.token) === getAddress11(token)) out.push(record);
14790
+ if (record.chainId === chainId && getAddress12(record.token) === getAddress12(token)) out.push(record);
14649
14791
  }
14650
14792
  return out.sort((x, y) => x.createdAt - y.createdAt);
14651
14793
  }
@@ -14693,8 +14835,8 @@ var init_channels = __esm({
14693
14835
  vault,
14694
14836
  channel: opening.channel,
14695
14837
  config: opening.config,
14696
- payee: getAddress11(o.receiverAuthorizer),
14697
- voucherKey: getAddress11(o.payerAuthorizer),
14838
+ payee: getAddress12(o.receiverAuthorizer),
14839
+ voucherKey: getAddress12(o.payerAuthorizer),
14698
14840
  deposit: o.amount.toString(),
14699
14841
  gracePeriod: o.withdrawDelay,
14700
14842
  recipients,
@@ -14730,7 +14872,7 @@ var init_channels = __esm({
14730
14872
  open(input) {
14731
14873
  return this.#serial(async () => {
14732
14874
  if (!isAddress6(input.payee, { strict: false })) throw new RangeError(`${input.payee} is not an address`);
14733
- const payee = getAddress11(input.payee);
14875
+ const payee = getAddress12(input.payee);
14734
14876
  const deposit = parseTokenAmount(input.deposit);
14735
14877
  if (deposit === 0n) throw new RangeError("deposit must be greater than zero");
14736
14878
  const gracePeriod = parseGracePeriod(input.gracePeriod);
@@ -14739,8 +14881,8 @@ var init_channels = __esm({
14739
14881
  if (payeeVault === null) throw new Error(`${payee} is not an Ostra vault; open_channel pays Ostra vaults only (pay other sellers with pay)`);
14740
14882
  if (payeeVault.token !== this.#token()) throw new Error(`the payee vault holds ${payeeVault.token}, not ${this.#token()}`);
14741
14883
  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");
14884
+ const receiver = input.receiver !== void 0 ? getAddress12(input.receiver) : await splitAddress(this.#client, this.#config, payee, recipients);
14885
+ if (receiver === getAddress12("0x0000000000000000000000000000000000000000")) throw new RangeError("the receiver must not be the zero address");
14744
14886
  const salt = randomSalt();
14745
14887
  const voucher = this.#keys.channelVoucherSigner(this.#config.chainId, this.#token(), salt);
14746
14888
  const open3 = { payerAuthorizer: voucher.address, receiver, receiverAuthorizer: payee, withdrawDelay: gracePeriod, salt, amount: deposit };
@@ -14784,7 +14926,7 @@ var init_channels = __esm({
14784
14926
  const view2 = {
14785
14927
  channel: record.channel,
14786
14928
  payee: record.payee,
14787
- receiver: getAddress11(record.config.receiver),
14929
+ receiver: getAddress12(record.config.receiver),
14788
14930
  gracePeriod: record.gracePeriod,
14789
14931
  voucherKey: record.voucherKey,
14790
14932
  closeRequested: record.closeRequested,
@@ -14821,11 +14963,11 @@ var init_channels = __esm({
14821
14963
  const cfg = config ?? await recoverChannelConfig(this.#client, this.#config, vault, channel);
14822
14964
  if (cfg === null) throw new Error(`channel ${channel} was not opened by this agent's vault`);
14823
14965
  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) {
14966
+ if (channelId(cfg, this.#config.chainId, d.batchSettlement).toLowerCase() !== channel || getAddress12(cfg.payer) !== vault || getAddress12(cfg.token) !== d.token) {
14825
14967
  throw new Error(`channel ${channel} was not opened by this agent's vault`);
14826
14968
  }
14827
14969
  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`);
14970
+ if (voucher.address !== getAddress12(cfg.payerAuthorizer)) throw new Error(`channel ${channel} uses a voucher key this machine cannot derive`);
14829
14971
  const ch = await this.#read(channel);
14830
14972
  const open3 = {
14831
14973
  payerAuthorizer: cfg.payerAuthorizer,
@@ -14903,7 +15045,7 @@ var init_channels = __esm({
14903
15045
  sent,
14904
15046
  ...extra
14905
15047
  });
14906
- if (getAddress11(record.config.payer) !== getAddress11(record.vault)) throw new Error(`channel ${channel} does not belong to this vault's record`);
15048
+ if (getAddress12(record.config.payer) !== getAddress12(record.vault)) throw new Error(`channel ${channel} does not belong to this vault's record`);
14907
15049
  for (let i = 0; i < MAX_STEPS_PER_CALL; i++) {
14908
15050
  const ch = await this.#read(channel);
14909
15051
  const status = channelStatus(ch);
@@ -14918,7 +15060,7 @@ var init_channels = __esm({
14918
15060
  try {
14919
15061
  if (status === "open") {
14920
15062
  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`);
15063
+ if (voucher.address !== getAddress12(record.config.payerAuthorizer)) throw new Error(`the voucher key of channel ${channel} could not be derived`);
14922
15064
  const amount = ch.balance - ch.totalClaimed;
14923
15065
  const closeNonce = await this.#client.readContract({ address: record.vault, abi: ostraVaultAbi, functionName: "closeNonce", args: [channel] });
14924
15066
  const deadline = await chainTime(this.#client) + CLOSE_REQUEST_TTL_SECONDS;
@@ -15108,14 +15250,14 @@ var init_passkey = __esm({
15108
15250
  });
15109
15251
 
15110
15252
  // ../core/src/keys.ts
15111
- import { bytesToHex as bytesToHex9, getAddress as getAddress12, hexToBytes as hexToBytes7 } from "viem";
15253
+ import { bytesToHex as bytesToHex9, getAddress as getAddress13, hexToBytes as hexToBytes7 } from "viem";
15112
15254
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
15113
15255
  function chainBytes(chainId) {
15114
15256
  if (!Number.isSafeInteger(chainId) || chainId <= 0) throw new RangeError("chainId must be a positive integer");
15115
15257
  return hexToBytes7(`0x${BigInt(chainId).toString(16).padStart(64, "0")}`);
15116
15258
  }
15117
15259
  function tokenBytes(token) {
15118
- return hexToBytes7(getAddress12(token));
15260
+ return hexToBytes7(getAddress13(token));
15119
15261
  }
15120
15262
  function u64Be(value) {
15121
15263
  if (value < 0n || value > U64_MAX5) throw new RangeError("index out of range");
@@ -15207,7 +15349,7 @@ var init_keys = __esm({
15207
15349
  * payer and the WOTS store stay the agent's.
15208
15350
  */
15209
15351
  async forPrivateVault(chainId, token, index) {
15210
- const folder = this.#root.folder(PRIVATE_DIR).folder(`${chainId}-${getAddress12(token)}-${index}`);
15352
+ const folder = this.#root.folder(PRIVATE_DIR).folder(`${chainId}-${getAddress13(token)}-${index}`);
15211
15353
  await folder.ensure();
15212
15354
  return new _Keys({ master: this.#master, feePayer: this.feePayer, store: this.store, folder, root: this.#root, backupExported: this.#backupExported, escrow: this.escrow });
15213
15355
  }
@@ -15218,7 +15360,7 @@ var init_keys = __esm({
15218
15360
  * agent's folder (ShieldService takes the agent's keys).
15219
15361
  */
15220
15362
  async forCreatorVault(chainId, token) {
15221
- const folder = this.#root.folder(CREATOR_DIR).folder(`${chainId}-${getAddress12(token)}`);
15363
+ const folder = this.#root.folder(CREATOR_DIR).folder(`${chainId}-${getAddress13(token)}`);
15222
15364
  await folder.ensure();
15223
15365
  return new _Keys({ master: this.#master, feePayer: this.feePayer, store: this.store, folder, root: this.#root, backupExported: this.#backupExported, escrow: this.escrow });
15224
15366
  }
@@ -15299,13 +15441,13 @@ var init_keys = __esm({
15299
15441
  });
15300
15442
 
15301
15443
  // ../core/src/pools.ts
15302
- import { getAddress as getAddress13, isAddress as isAddress7, zeroAddress as zeroAddress6 } from "viem";
15444
+ import { getAddress as getAddress14, isAddress as isAddress7, zeroAddress as zeroAddress7 } from "viem";
15303
15445
  function iso(seconds) {
15304
15446
  return new Date(Number(seconds) * 1e3).toISOString();
15305
15447
  }
15306
15448
  function launchedTokenAddress(text2, what = "token") {
15307
15449
  if (!isAddress7(text2, { strict: false })) throw new RangeError(`${what} ${text2} is not an address`);
15308
- return getAddress13(text2);
15450
+ return getAddress14(text2);
15309
15451
  }
15310
15452
  async function readLaunchedPool(client, lp, tokenText) {
15311
15453
  const token = launchedTokenAddress(tokenText);
@@ -15318,13 +15460,13 @@ async function readLaunchedPool(client, lp, tokenText) {
15318
15460
  return { token, id, key, config };
15319
15461
  }
15320
15462
  function quoteOfPool(pool) {
15321
- return getAddress13(pool.config.tokenIs0 ? pool.key.currency1 : pool.key.currency0);
15463
+ return getAddress14(pool.config.tokenIs0 ? pool.key.currency1 : pool.key.currency0);
15322
15464
  }
15323
15465
  async function readPoolView(client, lp, tokenText) {
15324
15466
  const pool = await readLaunchedPool(client, lp, tokenText);
15325
15467
  const c = pool.config;
15326
15468
  const quote = quoteOfPool(pool);
15327
- const quoteDecimals = quote === zeroAddress6 ? 18 : await tokenDecimals(client, quote);
15469
+ const quoteDecimals = quote === zeroAddress7 ? 18 : await tokenDecimals(client, quote);
15328
15470
  const read = (address4, abi, functionName, args = []) => client.readContract({ address: address4, abi, functionName, args });
15329
15471
  const [state, rates, tick, name, symbol, round, available, nextAmount, shares, maxWallet, limitsReleased] = await Promise.all([
15330
15472
  read(lp.hook, ostraHookAbi, "poolState", [pool.id]),
@@ -15352,7 +15494,7 @@ async function readPoolView(client, lp, tokenText) {
15352
15494
  name,
15353
15495
  symbol,
15354
15496
  quote,
15355
- creatorVault: getAddress13(c.creatorVault),
15497
+ creatorVault: getAddress14(c.creatorVault),
15356
15498
  graduated: state.graduated,
15357
15499
  tokensSold: formatUnitsAmount(state.tokensSold < 0n ? 0n : state.tokensSold, 18),
15358
15500
  quoteRaised: fmtQuote(state.quoteRaised),
@@ -15364,7 +15506,7 @@ async function readPoolView(client, lp, tokenText) {
15364
15506
  sellTaxBps: Number(rates[1]),
15365
15507
  antiSnipeBps: Number(rates[2]),
15366
15508
  maxBuy: c.maxBuy === 0n ? null : formatUnitsAmount(c.maxBuy, 18),
15367
- dividendToken: getAddress13(c.dividendToken) === zeroAddress6 ? null : getAddress13(c.dividendToken),
15509
+ dividendToken: getAddress14(c.dividendToken) === zeroAddress7 ? null : getAddress14(c.dividendToken),
15368
15510
  launchedAt: iso(BigInt(c.launchedAt)),
15369
15511
  airdrop: {
15370
15512
  round: round.number,
@@ -15375,7 +15517,7 @@ async function readPoolView(client, lp, tokenText) {
15375
15517
  available: formatUnitsAmount(available, 18),
15376
15518
  nextRoundAmount: formatUnitsAmount(nextAmount, 18)
15377
15519
  },
15378
- creatorShares: shares[0].map((recipient, i) => ({ recipient: getAddress13(recipient), bps: Number(shares[1][i]) })),
15520
+ creatorShares: shares[0].map((recipient, i) => ({ recipient: getAddress14(recipient), bps: Number(shares[1][i]) })),
15379
15521
  rules: {
15380
15522
  quoteDecimals,
15381
15523
  supply: formatUnitsAmount(state.supply, 18),
@@ -15516,7 +15658,7 @@ var init_publish = __esm({
15516
15658
  });
15517
15659
 
15518
15660
  // ../core/src/pending.ts
15519
- import { zeroAddress as zeroAddress7 } from "viem";
15661
+ import { zeroAddress as zeroAddress8 } from "viem";
15520
15662
  function decodeVaultSpendHeader(message) {
15521
15663
  const s = decodeVaultSpendMessage(message);
15522
15664
  return { chainId: s.chainId, vault: s.vault, nonce: s.nonce, nextKeyRoot: s.nextKeyRoot, action: s.action };
@@ -15537,7 +15679,7 @@ function decodeVaultOpenChannelMessage(message) {
15537
15679
  function decodeVaultSetCrankKeyMessage(message) {
15538
15680
  const { header, params } = decodeAs(message, VAULT_ACTION.SET_CRANK_KEY);
15539
15681
  const crank = decodeSetCrankParams(params);
15540
- return { ...header, crank: crank === zeroAddress7 ? null : crank };
15682
+ return { ...header, crank: crank === zeroAddress8 ? null : crank };
15541
15683
  }
15542
15684
  var init_pending = __esm({
15543
15685
  "../core/src/pending.ts"() {
@@ -15548,7 +15690,7 @@ var init_pending = __esm({
15548
15690
  });
15549
15691
 
15550
15692
  // ../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";
15693
+ 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
15694
  function spendGas(calls) {
15553
15695
  return calls.reduce((gas, c) => gas + reservedSwapGas(c), VAULT_SPEND_GAS);
15554
15696
  }
@@ -15591,10 +15733,11 @@ var init_vault = __esm({
15591
15733
  #onChannelOpening = null;
15592
15734
  constructor(client, transport, config, keys, sender, kind = "buyer", privateIndex = null, vaultToken = null) {
15593
15735
  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`);
15736
+ if (kind === "token" && vaultToken === null) throw new Error("a token vault names its token");
15737
+ if (vaultToken !== null && kind !== "token" && kind !== "creator") throw new Error("only a token vault or a creator vault names its token");
15738
+ 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
15739
  this.kind = kind;
15597
- this.token = vaultToken === null ? config.deployment.token : getAddress14(vaultToken);
15740
+ this.token = vaultToken === null ? config.deployment.token : getAddress15(vaultToken);
15598
15741
  this.privateIndex = privateIndex;
15599
15742
  this.#client = client;
15600
15743
  this.#transport = transport;
@@ -15603,17 +15746,21 @@ var init_vault = __esm({
15603
15746
  this.#sender = sender;
15604
15747
  const token = this.token;
15605
15748
  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;
15749
+ this.#initCrank = kind === "seller" ? null : zeroAddress9;
15607
15750
  }
15608
15751
  /** Seller vault: the crank it is (or will be) created with, which is part of its address. */
15609
15752
  setInitCrank(crank) {
15610
15753
  if (this.kind !== "seller") throw new Error(`the ${this.kind} vault is created without a crank`);
15611
15754
  if (!isAddress8(crank, { strict: false })) throw new RangeError("a crank is an address");
15612
- this.#initCrank = getAddress14(crank);
15755
+ this.#initCrank = getAddress15(crank);
15756
+ }
15757
+ /** A vault in the deployment's token (USDG); else a token vault or a pair's creator vault, paid for by the buyer vault. */
15758
+ #inUsdg() {
15759
+ return this.token === this.#config.deployment.token;
15613
15760
  }
15614
15761
  #missing() {
15615
15762
  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";
15763
+ 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
15764
  if (this.kind === "token") return `this agent has no vault in ${this.token} yet; buying the token creates it`;
15618
15765
  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
15766
  }
@@ -15645,10 +15792,10 @@ var init_vault = __esm({
15645
15792
  return null;
15646
15793
  }
15647
15794
  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 })) {
15795
+ 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
15796
  throw new Error(`${this.#adoptedName()} is not a recovered vault of chain ${this.#config.chainId} in ${this.token}`);
15650
15797
  }
15651
- this.#adopted = getAddress14(r.vault);
15798
+ this.#adopted = getAddress15(r.vault);
15652
15799
  return this.#adopted;
15653
15800
  }
15654
15801
  async vaultAddress() {
@@ -15679,6 +15826,7 @@ var init_vault = __esm({
15679
15826
  }
15680
15827
  async #checkToken() {
15681
15828
  if (this.kind === "token") throw new Error(`the vault in ${this.token} spends only through its token operations (sell, transfer, claim dividends)`);
15829
+ if (!this.#inUsdg()) throw new Error(`the creator vault in ${this.token} spends only through the launchpad (launch, pool settings, creator_withdraw)`);
15682
15830
  const decimals = await tokenDecimals(this.#client, this.#config.deployment.token);
15683
15831
  if (decimals !== TOKEN_DECIMALS) throw new Error(`token ${this.#config.deployment.token} has ${decimals} decimals; this package supports ${TOKEN_DECIMALS}`);
15684
15832
  }
@@ -15720,7 +15868,7 @@ var init_vault = __esm({
15720
15868
  }
15721
15869
  /** 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
15870
  #paysRelayer() {
15723
- return this.kind !== "seller" && this.kind !== "token";
15871
+ return this.kind !== "seller" && this.kind !== "token" && this.#inUsdg();
15724
15872
  }
15725
15873
  #options(extra = {}) {
15726
15874
  const openChannel = this.#opener();
@@ -15739,7 +15887,7 @@ var init_vault = __esm({
15739
15887
  open() {
15740
15888
  return this.#serial(async () => {
15741
15889
  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();
15890
+ if (this.kind !== "token" && this.#inUsdg()) await this.#checkToken();
15743
15891
  const vault = await this.vaultAddress();
15744
15892
  const token = this.token;
15745
15893
  const softLimit = formatTokenAmount(VAULT_SOFT_LIMIT);
@@ -15750,9 +15898,9 @@ var init_vault = __esm({
15750
15898
  return found;
15751
15899
  }
15752
15900
  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" }));
15901
+ const txHash = await this.#sender.send([await this.createCall()], this.#options({ prepay: this.kind === "seller" || !this.#paysRelayer() }));
15754
15902
  const result = { vault, token, nonce: "0", created: true, txHash, softLimit };
15755
- if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator")) {
15903
+ if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator") && this.#paysRelayer()) {
15756
15904
  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
15905
  }
15758
15906
  if (!await this.#keys.backupExported()) result.backupReminder = BACKUP_REMINDER;
@@ -15803,6 +15951,7 @@ var init_vault = __esm({
15803
15951
  status() {
15804
15952
  return this.#serial(async () => {
15805
15953
  if (this.kind === "token") throw new Error(`the vault in ${this.token} is read with its token's tools (token_vault)`);
15954
+ if (!this.#inUsdg()) throw new Error(`the creator vault in ${this.token} is read with the launchpad's pair tools`);
15806
15955
  const vault = await this.vaultAddress();
15807
15956
  const token = this.#config.deployment.token;
15808
15957
  const v = await this.#read(vault);
@@ -15872,7 +16021,7 @@ var init_vault = __esm({
15872
16021
  throw new Error("the stored signature does not belong to this vault and nonce");
15873
16022
  }
15874
16023
  const token = this.#config.deployment.token;
15875
- if (header.action === VAULT_ACTION.TRANSFER && this.kind !== "token") {
16024
+ if (header.action === VAULT_ACTION.TRANSFER && this.#inUsdg()) {
15876
16025
  const t = decodeVaultTransferMessage(message);
15877
16026
  await this.#notFrozen(t.to, "the destination of a withdrawal signed earlier,");
15878
16027
  const params = transferParams({ to: t.to, amount: t.amount });
@@ -15890,7 +16039,7 @@ var init_vault = __esm({
15890
16039
  }
15891
16040
  if (header.action === VAULT_ACTION.SET_CRANK_KEY) {
15892
16041
  const { crank, nextKeyRoot } = decodeVaultSetCrankKeyMessage(message);
15893
- const params = setCrankParams(crank ?? zeroAddress8);
16042
+ const params = setCrankParams(crank ?? zeroAddress9);
15894
16043
  const txHash2 = await sender.send([spendCall(vault, { action: VAULT_ACTION.SET_CRANK_KEY, nextKeyRoot, params, signature })], { reservation });
15895
16044
  return { operation: "set_crank", txHash: txHash2, crank };
15896
16045
  }
@@ -15898,7 +16047,7 @@ var init_vault = __esm({
15898
16047
  const s2 = decodeVaultSpendMessage(message);
15899
16048
  const { devBuy, launchHash: hash } = decodeDevBuyParams(s2.params);
15900
16049
  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`);
16050
+ if (lp === void 0 || getAddress15(devBuy) !== lp.devBuy) throw new Error(`the vault signed a DevBuy for ${devBuy}, not this deployment's OstraDevBuy`);
15902
16051
  const l = await this.#signedLaunch(vault, v.nonce, hash);
15903
16052
  const call = { to: lp.devBuy, data: launchFromVaultData(l.launch, l.dev, { nextKeyRoot: s2.nextKeyRoot, signature }) };
15904
16053
  const txHash2 = await sender.send([call], { reservation, value: LAUNCHPAD.FEE_WEI });
@@ -16001,8 +16150,8 @@ the signed ${op.what} is kept; the next call on this vault sends it again before
16001
16150
  const amount = parseTokenAmount(amountText);
16002
16151
  if (amount === 0n) throw new RangeError("amount must be greater than zero");
16003
16152
  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");
16153
+ const destination = getAddress15(destinationText);
16154
+ if (destination === zeroAddress9) throw new RangeError("the destination must not be the zero address");
16006
16155
  await this.#checkToken();
16007
16156
  const vault = await this.vaultAddress();
16008
16157
  if (await this.#read(vault) === null) throw new Error(this.#missing());
@@ -16052,7 +16201,7 @@ the signed withdrawal is kept; the next withdraw or open_channel call sends it a
16052
16201
  if (this.kind === "seller") throw new Error("channels are not opened from the seller vault");
16053
16202
  await this.#checkToken();
16054
16203
  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");
16204
+ if (getAddress15(open3.receiverAuthorizer) === vault || getAddress15(open3.receiver) === vault) throw new Error("the channel would pay this agent's own vault");
16056
16205
  if (await this.#read(vault) === null) throw new Error(this.#missing());
16057
16206
  await this.requireEth(VAULT_SPEND_GAS, "send the vault operation");
16058
16207
  return this.#paidSpend(this.#sender, async (sender, reservation) => {
@@ -16126,8 +16275,8 @@ the signed channel open is kept; the next open_channel or withdraw call sends it
16126
16275
  setCrankKey(crankText, via) {
16127
16276
  return this.#serial(async () => {
16128
16277
  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;
16278
+ const crank = getAddress15(crankText);
16279
+ const target = crank === zeroAddress9 ? null : crank;
16131
16280
  const vault = await this.vaultAddress();
16132
16281
  if (await this.#read(vault) === null) throw new Error(this.#missing());
16133
16282
  const chosen = via ?? this.#sender;
@@ -16214,8 +16363,8 @@ the signed operation is kept; the next call on this vault sends it again before
16214
16363
  /** Adds (enabled) or removes a guardian of this vault: another Ostra vault keeps the recovery post-quantum. */
16215
16364
  async setGuardian(guardianText, enabled, via) {
16216
16365
  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");
16366
+ const guardian = getAddress15(guardianText);
16367
+ if (guardian === zeroAddress9) throw new RangeError("a guardian is not the zero address");
16219
16368
  return this.#recoverySpend(
16220
16369
  (v) => signVaultSetGuardian(this.#keychain, v, { chainId: this.#config.chainId, guardian, enabled }),
16221
16370
  async (_v, vault) => {
@@ -16260,7 +16409,7 @@ the signed operation is kept; the next call on this vault sends it again before
16260
16409
  /** As a guardian (this vault), supports moving `vaultText` to `newKeyRootText` (the key root its owner's new device gave). */
16261
16410
  async supportRecovery(vaultText, newKeyRootText, via) {
16262
16411
  if (!isAddress8(vaultText, { strict: false })) throw new RangeError(`${vaultText} is not an address`);
16263
- const target = getAddress14(vaultText);
16412
+ const target = getAddress15(vaultText);
16264
16413
  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
16414
  const newKeyRoot = newKeyRootText.toLowerCase();
16266
16415
  return this.#recoverySpend(
@@ -16289,7 +16438,7 @@ the signed operation is kept; the next call on this vault sends it again before
16289
16438
  return this.#serial(async () => {
16290
16439
  if (this.kind === "private") throw new Error("a private vault is recovered with the seed that unshielded it");
16291
16440
  if (!isAddress8(vaultText, { strict: false })) throw new RangeError(`${vaultText} is not an address`);
16292
- const vault = getAddress14(vaultText);
16441
+ const vault = getAddress15(vaultText);
16293
16442
  const v = await readVault(this.#client, this.#config.deployment, vault);
16294
16443
  if (v === null || v.token !== this.token) throw new Error(`${vault} is not an Ostra vault in ${this.token}`);
16295
16444
  const adopted = await this.#adoptedVault();
@@ -16347,7 +16496,7 @@ the signed operation is kept; the next call on this vault sends it again before
16347
16496
  });
16348
16497
 
16349
16498
  // ../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";
16499
+ 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
16500
  async function readIpfsJson(uri2) {
16352
16501
  if (uri2 === null || !uri2.startsWith("ipfs://")) return null;
16353
16502
  const path = uri2.slice("ipfs://".length).replace(/^ipfs\//, "");
@@ -16384,7 +16533,7 @@ function nowIso(seconds) {
16384
16533
  }
16385
16534
  function address2(text2, what) {
16386
16535
  if (!isAddress9(text2, { strict: false })) throw new RangeError(`${what} ${text2} is not an address`);
16387
- return getAddress15(text2);
16536
+ return getAddress16(text2);
16388
16537
  }
16389
16538
  function slippage(bps) {
16390
16539
  const s = bps ?? QUOTE_DEFAULT_SLIPPAGE_BPS;
@@ -16398,7 +16547,7 @@ function simulatedGas(gasUsed) {
16398
16547
  const g = gasUsed * 5n / 4n + SPEND_GAS_HEADROOM;
16399
16548
  return g > VAULT_SPEND_GAS ? g : VAULT_SPEND_GAS;
16400
16549
  }
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;
16550
+ 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
16551
  var init_launchpad = __esm({
16403
16552
  "../core/src/launchpad.ts"() {
16404
16553
  "use strict";
@@ -16415,6 +16564,7 @@ var init_launchpad = __esm({
16415
16564
  LAUNCHPAD_DIR = "launchpad";
16416
16565
  CREATE_VAULT_GAS2 = 300000n;
16417
16566
  LAUNCH_GAS_HEADROOM = 300000n;
16567
+ PAIRS_READ_CONCURRENCY = 6;
16418
16568
  SPEND_GAS_HEADROOM = 100000n;
16419
16569
  DAY = 86400;
16420
16570
  TOKEN_LIST_MAX = 50;
@@ -16445,6 +16595,8 @@ var init_launchpad = __esm({
16445
16595
  #creatorSender;
16446
16596
  #folder;
16447
16597
  #tokenVaults = /* @__PURE__ */ new Map();
16598
+ /** A pair's creator vault (not USDG's), one per token: created once, so its operations share one queue. */
16599
+ #pairCreators = /* @__PURE__ */ new Map();
16448
16600
  constructor(p) {
16449
16601
  if (p.creator.kind !== "creator" || p.buyer.kind !== "buyer") throw new Error("the launchpad takes the agent's buyer and creator vaults");
16450
16602
  this.#client = p.client;
@@ -16476,6 +16628,87 @@ var init_launchpad = __esm({
16476
16628
  }
16477
16629
  return v;
16478
16630
  }
16631
+ /**
16632
+ * Refuses to pay `paid` to an Ostra vault (of this deployment) in another
16633
+ * token: a vault only ever moves its own token, so what it is paid in
16634
+ * another stays there for good. A vault not created yet cannot be told
16635
+ * from any other address.
16636
+ */
16637
+ async #notStranded(to, paid, what) {
16638
+ const v = await readVault(this.#client, this.#config.deployment, to);
16639
+ if (v !== null && v.token !== getAddress16(paid)) {
16640
+ 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`);
16641
+ }
16642
+ }
16643
+ /** The token a pair's vaults hold: the pair's own; WETH for ETH. */
16644
+ #pairToken(pair3) {
16645
+ return pair3.kind === "eth" ? WETH : pair3.address;
16646
+ }
16647
+ /**
16648
+ * The agent's creator vault for `pairText` and the sender of its operations:
16649
+ * for USDG the USDG creator vault and its own relayer client; for OSTRA, a
16650
+ * stock token or ETH, the creator vault in that token (WETH for ETH), paid for by
16651
+ * the buyer vault.
16652
+ */
16653
+ async pairCreator(pairText) {
16654
+ const pair3 = launchPair(pairText);
16655
+ if (pair3.kind === "usdg") return { pair: pair3, vault: this.#creator, sender: this.#creatorSender };
16656
+ const token = this.#pairToken(pair3);
16657
+ let made = this.#pairCreators.get(token);
16658
+ if (made === void 0) {
16659
+ made = this.#keys.forCreatorVault(this.#config.chainId, token).then((keys) => new VaultService(this.#client, this.#transport, this.#config, keys, this.#buyerSender, "creator", null, token));
16660
+ this.#pairCreators.set(token, made);
16661
+ made.catch(() => {
16662
+ if (this.#pairCreators.get(token) === made) this.#pairCreators.delete(token);
16663
+ });
16664
+ }
16665
+ return { pair: pair3, vault: await made, sender: this.#buyerSender };
16666
+ }
16667
+ /**
16668
+ * The pairs a launch can take, with this agent's vaults for each: its
16669
+ * creator vault (what a launch's dev buy is paid from) and its vault of the
16670
+ * pair's token (what it buys that pair's pools with); with `prices`, each
16671
+ * pair's USD price now.
16672
+ */
16673
+ async pairs(a = {}) {
16674
+ const out = new Array(LAUNCH_PAIRS.length);
16675
+ let next = 0;
16676
+ const worker = async () => {
16677
+ while (next < LAUNCH_PAIRS.length) {
16678
+ const i = next++;
16679
+ out[i] = await this.#pairRow(LAUNCH_PAIRS[i], a.prices === true);
16680
+ }
16681
+ };
16682
+ await Promise.all(Array.from({ length: PAIRS_READ_CONCURRENCY }, worker));
16683
+ return out;
16684
+ }
16685
+ /** One row of pairs(). */
16686
+ async #pairRow(pair3, prices) {
16687
+ const { vault } = await this.pairCreator(pair3.symbol);
16688
+ const s = await vault.state();
16689
+ const token = this.#pairToken(pair3);
16690
+ const row = {
16691
+ symbol: pair3.symbol,
16692
+ name: pair3.name,
16693
+ kind: pair3.kind,
16694
+ token: pair3.address,
16695
+ decimals: pair3.decimals,
16696
+ creatorVault: s.vault,
16697
+ creatorBalance: formatUnitsAmount(s.balance, pair3.decimals),
16698
+ tradingVault: pair3.kind === "eth" ? null : await this.tokenVault(token).vaultAddress()
16699
+ };
16700
+ if (prices) {
16701
+ try {
16702
+ const price = await readPairUsdPrice(this.#client, pair3);
16703
+ row.priceUsd = formatUnitsAmount(price.priceE8, 8);
16704
+ row.priceAt = new Date(price.updatedAt * 1e3).toISOString();
16705
+ } catch {
16706
+ row.priceUsd = null;
16707
+ row.priceAt = null;
16708
+ }
16709
+ }
16710
+ return row;
16711
+ }
16479
16712
  // ---------------------------------------------------------------------------
16480
16713
  // Reads
16481
16714
  // ---------------------------------------------------------------------------
@@ -16496,9 +16729,9 @@ var init_launchpad = __esm({
16496
16729
  const pool = await this.#pool(tokenText);
16497
16730
  const s = await this.tokenVault(pool.token).state();
16498
16731
  let dividends = null;
16499
- if (getAddress15(pool.config.dividendToken) !== zeroAddress9) {
16732
+ if (getAddress16(pool.config.dividendToken) !== zeroAddress10) {
16500
16733
  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));
16734
+ const decimals = getAddress16(pool.config.dividendToken) === this.#usdg() ? TOKEN_DECIMALS : await tokenDecimals(this.#client, getAddress16(pool.config.dividendToken));
16502
16735
  dividends = formatUnitsAmount(owed, decimals);
16503
16736
  }
16504
16737
  return { token: pool.token, vault: s.vault, exists: s.exists, balance: formatUnitsAmount(s.balance, 18), dividends, pendingOperation: s.pendingAction };
@@ -16520,38 +16753,44 @@ var init_launchpad = __esm({
16520
16753
  async #swapQuote(a, create) {
16521
16754
  const pool = await this.#pool(a.token);
16522
16755
  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`);
16756
+ if (quote === zeroAddress10) {
16757
+ 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`);
16758
+ }
16759
+ const pair3 = findLaunchPair(quote);
16760
+ if (pair3 === null) throw new Error(`the pool of ${pool.token} trades against ${quote}, not a pair Ostra offers (USDG, ETH, OSTRA or a listed stock token); nothing was signed`);
16524
16761
  const referrer = a.referrer === void 0 ? void 0 : address2(a.referrer, "referrer");
16525
- const usdgIs0 = getAddress15(pool.key.currency0) === this.#usdg();
16762
+ const quoteIs0 = getAddress16(pool.key.currency0) === quote;
16526
16763
  const buying = a.side === "buy";
16527
- const from = buying ? this.#buyer : this.tokenVault(pool.token);
16764
+ const quoteVault = this.tokenVault(quote);
16765
+ const from = buying ? quoteVault : this.tokenVault(pool.token);
16528
16766
  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);
16767
+ const to = buying ? await this.tokenVault(pool.token).vaultAddress() : await quoteVault.vaultAddress();
16768
+ const amountIn = buying ? parseUnitsAmount(a.amount, pair3.decimals) : parseUnitsAmount(a.amount, 18);
16531
16769
  if (amountIn === 0n) throw new RangeError("amount must be greater than zero");
16532
16770
  const state = await from.state();
16533
- if (!state.exists && buying) throw new Error("this agent has no vault yet; call open_vault first");
16771
+ if (!state.exists && from === this.#buyer) throw new Error("this agent has no vault yet; call open_vault first");
16534
16772
  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}`);
16773
+ const what = from === this.#buyer ? "vault" : `vault in ${buying ? pair3.symbol : pool.token}`;
16774
+ throw new Error(`the ${what} holds ${formatUnitsAmount(state.balance, buying ? pair3.decimals : 18)}, less than ${a.amount}`);
16536
16775
  }
16537
16776
  const sender = this.#buyerSender;
16538
16777
  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`);
16778
+ 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
16779
  await from.open();
16541
16780
  }
16542
16781
  const q = await quoteVaultSwap(this.#client, this.#config.deployment, {
16543
16782
  vault: fromVault,
16544
16783
  relayer: await sender.payer(),
16545
- swap: { key: pool.key, zeroForOne: buying ? usdgIs0 : !usdgIs0, amountIn, recipient: to, ...referrer === void 0 ? {} : { referrer } },
16784
+ swap: { key: pool.key, zeroForOne: buying ? quoteIs0 : !quoteIs0, amountIn, recipient: to, ...referrer === void 0 ? {} : { referrer } },
16546
16785
  slippageBps: slippage(a.slippageBps)
16547
16786
  });
16548
- return { q, pool, from, fromVault, to, inDecimals: buying ? TOKEN_DECIMALS : 18, outDecimals: buying ? 18 : TOKEN_DECIMALS, sender };
16787
+ return { q, pool, from, fromVault, to, inDecimals: buying ? pair3.decimals : 18, outDecimals: buying ? 18 : pair3.decimals, sender };
16549
16788
  }
16550
16789
  /** What the vault's Swap in `txHash` did: Swapped (in, out) or SwapFailed. */
16551
16790
  async #swapOutcome(txHash, vault) {
16552
16791
  const receipt = await this.#client.getTransactionReceipt({ hash: txHash });
16553
16792
  for (const log2 of receipt.logs) {
16554
- if (getAddress15(log2.address) !== vault) continue;
16793
+ if (getAddress16(log2.address) !== vault) continue;
16555
16794
  try {
16556
16795
  const e = decodeEventLog3({ abi: ostraVaultAbi, topics: log2.topics, data: log2.data });
16557
16796
  if (e.eventName === "Swapped") {
@@ -16564,11 +16803,11 @@ var init_launchpad = __esm({
16564
16803
  }
16565
16804
  throw new Error(`transaction ${txHash} has no Swap of ${vault}`);
16566
16805
  }
16567
- /** Buys `token` with `amount` USDG of the buyer vault, into the agent's token vault. */
16806
+ /** Buys `token` with `amount` of its pool's pair (USDG from the buyer vault, OSTRA or a stock token from its token vault), into the agent's token vault. */
16568
16807
  buy(a) {
16569
16808
  return this.#swap({ ...a, side: "buy" });
16570
16809
  }
16571
- /** Sells `amount` of `token` from the agent's token vault for USDG into the buyer vault. */
16810
+ /** 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
16811
  sell(a) {
16573
16812
  return this.#swap({ ...a, side: "sell" });
16574
16813
  }
@@ -16577,7 +16816,7 @@ var init_launchpad = __esm({
16577
16816
  const buying = a.side === "buy";
16578
16817
  const tokenVault = this.tokenVault(pool.token);
16579
16818
  const createTokenVault = buying && !(await tokenVault.state()).exists;
16580
- const inToken = buying ? this.#usdg() : pool.token;
16819
+ const inToken = buying ? this.#quoteOf(pool) : pool.token;
16581
16820
  const result = await from.operate({
16582
16821
  gas: swapSpendGas(q.swap.swapGas) + (createTokenVault ? CREATE_VAULT_GAS2 : 0n),
16583
16822
  moves: q.amountIn,
@@ -16623,7 +16862,7 @@ var init_launchpad = __esm({
16623
16862
  async transferToken(a) {
16624
16863
  const token = address2(a.token, "token");
16625
16864
  const destination = address2(a.destination, "destination");
16626
- if (destination === zeroAddress9) throw new RangeError("the destination must not be the zero address");
16865
+ if (destination === zeroAddress10) throw new RangeError("the destination must not be the zero address");
16627
16866
  const v = this.tokenVault(token);
16628
16867
  if (v === this.#buyer) throw new Error("USDG is withdrawn with withdraw");
16629
16868
  const decimals = await tokenDecimals(this.#client, token);
@@ -16633,6 +16872,7 @@ var init_launchpad = __esm({
16633
16872
  if (!s.exists) throw new Error(`this agent has no vault in ${token}`);
16634
16873
  if (s.balance < amount) throw new Error(`the vault in ${token} holds ${formatUnitsAmount(s.balance, decimals)}, less than ${a.amount}`);
16635
16874
  if (destination === s.vault) throw new Error("the destination is the vault itself");
16875
+ await this.#notStranded(destination, token, "the transfer");
16636
16876
  return this.#simulatedSpend(v, this.#buyerSender, VAULT_ACTION.TRANSFER, transferParams({ to: destination, amount }), "token transfer");
16637
16877
  }
16638
16878
  /** The agent's vault that receives `dividendToken`: the buyer vault for USDG, else the token vault of that token. */
@@ -16642,8 +16882,8 @@ var init_launchpad = __esm({
16642
16882
  /** Claims the dividends the agent's token vault for `token` earned, into the agent's vault of the dividend token. */
16643
16883
  async claimDividends(tokenText) {
16644
16884
  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`);
16885
+ const dividendToken = getAddress16(pool.config.dividendToken);
16886
+ if (dividendToken === zeroAddress10) throw new Error(`the pool of ${pool.token} pays no dividends`);
16647
16887
  const v = this.tokenVault(pool.token);
16648
16888
  const s = await v.state();
16649
16889
  if (!s.exists) throw new Error(`this agent has no vault in ${pool.token}`);
@@ -16670,11 +16910,51 @@ var init_launchpad = __esm({
16670
16910
  }
16671
16911
  return out.sort((x, y) => x.at - y.at);
16672
16912
  }
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);
16913
+ /**
16914
+ * Moves `amount` into the creator vault of `pairText` (default USDG): USDG
16915
+ * from the buyer vault (a launch's dev buy and the USDG creator vault's
16916
+ * relayer requests are paid from it); OSTRA, a stock token or WETH from the
16917
+ * agent's token vault of it (what a dev buy in that pair is paid from). The
16918
+ * creator vault is created first when it does not exist yet.
16919
+ */
16920
+ async fundCreatorVault(amount, pairText = "USDG") {
16921
+ const { pair: pair3, vault: creator } = await this.pairCreator(pairText);
16922
+ const to = await creator.vaultAddress();
16923
+ if (pair3.kind === "usdg") {
16924
+ if ((await creator.state()).exists === false) await creator.open();
16925
+ return this.#buyer.withdraw(to, amount);
16926
+ }
16927
+ const units2 = parseUnitsAmount(amount, pair3.decimals);
16928
+ if (units2 === 0n) throw new RangeError("amount must be greater than zero");
16929
+ const token = this.#pairToken(pair3);
16930
+ const from = this.tokenVault(token);
16931
+ const s = await from.state();
16932
+ const tokenName = pair3.kind === "eth" ? "WETH" : pair3.symbol;
16933
+ if (s.balance < units2) {
16934
+ throw new Error(
16935
+ `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`
16936
+ );
16937
+ }
16938
+ if (!s.exists) await from.open();
16939
+ if ((await creator.state()).exists === false) await creator.open();
16940
+ return this.#simulatedSpend(from, this.#buyerSender, VAULT_ACTION.TRANSFER, transferParams({ to, amount: units2 }), `${tokenName} transfer to the ${pair3.symbol} creator vault`);
16941
+ }
16942
+ /** Sends `amount` from the creator vault of `pair` (default USDG) to `destination` (default: the agent's vault of that token; for ETH, required). */
16943
+ async creatorWithdraw(a) {
16944
+ const { pair: pair3, vault: creator, sender } = await this.pairCreator(a.pair ?? "USDG");
16945
+ const token = this.#pairToken(pair3);
16946
+ if (pair3.kind === "eth" && a.destination === void 0) throw new RangeError(`a ${pair3.symbol} creator vault holds WETH: name the destination`);
16947
+ const destination = a.destination === void 0 ? await this.tokenVault(token).vaultAddress() : address2(a.destination, "destination");
16948
+ await this.#notStranded(destination, token, `the ${pair3.symbol} creator vault's transfer`);
16949
+ if (pair3.kind === "usdg") return creator.withdraw(destination, a.amount);
16950
+ if (destination === zeroAddress10) throw new RangeError("the destination must not be the zero address");
16951
+ const units2 = parseUnitsAmount(a.amount, pair3.decimals);
16952
+ if (units2 === 0n) throw new RangeError("amount must be greater than zero");
16953
+ const s = await creator.state();
16954
+ if (!s.exists) throw new Error(`this agent has no ${pair3.symbol} creator vault yet`);
16955
+ if (destination === s.vault) throw new Error("the destination is the vault itself");
16956
+ if (s.balance < units2) throw new Error(`the ${pair3.symbol} creator vault holds ${formatUnitsAmount(s.balance, pair3.decimals)}, less than ${a.amount}`);
16957
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.TRANSFER, transferParams({ to: destination, amount: units2 }), `${pair3.symbol} creator vault transfer`);
16678
16958
  }
16679
16959
  /**
16680
16960
  * Launches a token from the creator vault (OstraDevBuy.launchFromVault),
@@ -16685,13 +16965,20 @@ var init_launchpad = __esm({
16685
16965
  const lp = this.#lp();
16686
16966
  if (typeof input.name !== "string" || input.name.length === 0 || input.name.length > 64) throw new RangeError("a name is 1..64 characters");
16687
16967
  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));
16968
+ if (input.marketCap === void 0 === (input.startTick === void 0)) throw new RangeError("give the starting marketCap (US dollars) or the startTick, one of them");
16969
+ const { pair: pair3, vault: creator, sender } = await this.pairCreator(input.pair ?? "USDG");
16970
+ const marketCapUsd = input.marketCap === void 0 ? null : parseTokenAmount(input.marketCap);
16971
+ const price = marketCapUsd === null ? null : await readPairUsdPrice(this.#client, pair3);
16972
+ const marketCapUnits = marketCapUsd === null || price === null ? null : usdToQuoteUnits(marketCapUsd, price.priceE8, pair3.decimals);
16973
+ if (marketCapUnits === 0n) throw new RangeError(`a market cap of ${input.marketCap} USD is less than one base unit of ${pair3.symbol}`);
16974
+ const startTick = input.startTick ?? startTickForMarketCap(marketCapUnits);
16690
16975
  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();
16976
+ const vaultToken = this.#pairToken(pair3);
16977
+ const pairUnits = (x) => formatUnitsAmount(x, pair3.decimals);
16978
+ let creatorState = await creator.state();
16692
16979
  if (!creatorState.exists) {
16693
- await this.#creator.open();
16694
- creatorState = await this.#creator.state();
16980
+ await creator.open();
16981
+ creatorState = await creator.state();
16695
16982
  }
16696
16983
  const creatorVault = creatorState.vault;
16697
16984
  const p = launchParams({
@@ -16699,46 +16986,46 @@ var init_launchpad = __esm({
16699
16986
  name: input.name,
16700
16987
  symbol: input.symbol,
16701
16988
  salt: bytesToHex11(randomBytes3(32)),
16702
- quote: this.#usdg(),
16989
+ quote: pair3.address,
16703
16990
  creatorVault,
16704
16991
  startTick
16705
16992
  });
16706
- const token = getAddress15(await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "tokenAddressFromVault", args: [p] }));
16993
+ const token = getAddress16(await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "tokenAddressFromVault", args: [p] }));
16707
16994
  const tokenVault = await this.tokenVault(token).vaultAddress();
16708
16995
  let dev = NO_DEV_BUY;
16709
16996
  if (input.devBuy !== void 0) {
16710
- const quoteIn = parseTokenAmount(input.devBuy.amount);
16997
+ const quoteIn = parseUnitsAmount(input.devBuy.amount, pair3.decimals);
16711
16998
  if (quoteIn === 0n) throw new RangeError("the dev buy amount must be greater than zero (leave devBuy out for none)");
16712
16999
  const cliff = Math.round((input.devBuy.cliffDays ?? DEV_BUY.MIN_CLIFF / DAY) * DAY);
16713
17000
  const duration = Math.round((input.devBuy.vestingDays ?? 0) * DAY);
16714
- const dividendToken = getAddress15(p.dividendToken);
17001
+ const dividendToken = getAddress16(p.dividendToken);
16715
17002
  dev = {
16716
17003
  quoteIn,
16717
17004
  beneficiary: tokenVault,
16718
17005
  // The dividend token is the launched token itself, USDG or another token: the agent's vault of it.
16719
17006
  // (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),
17007
+ dividendRecipient: dividendToken === zeroAddress10 ? zeroAddress10 : dividendToken === lp.launchpad || dividendToken === token ? tokenVault : await this.#vaultFor(dividendToken),
16721
17008
  cliff,
16722
17009
  duration
16723
17010
  };
16724
17011
  if (creatorState.balance < quoteIn) {
16725
17012
  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)`
17013
+ `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
17014
  );
16728
17015
  }
16729
17016
  }
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({
17017
+ const q = await quoteVaultLaunch(this.#client, this.#config.deployment, { relayer: await sender.payer(), launch: p, dev });
17018
+ const result = await creator.operate({
16732
17019
  gas: q.gasUsed * 5n / 4n + LAUNCH_GAS_HEADROOM,
16733
17020
  value: LAUNCHPAD.FEE_WEI,
16734
17021
  moves: dev.quoteIn,
16735
17022
  what: "launch",
16736
17023
  build: async ({ v, keychain, chainId }) => {
16737
17024
  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`);
17025
+ const balance = await tokenBalance(this.#client, vaultToken, creatorVault);
17026
+ if (balance < dev.quoteIn) throw new Error(`the creator vault holds ${pairUnits(balance)} now, less than the dev buy; nothing was signed`);
16740
17027
  }
16741
- await this.#creator.keepLaunch(v.nonce, p, dev);
17028
+ await creator.keepLaunch(v.nonce, p, dev);
16742
17029
  const signed = await signVaultDevBuy(keychain, v, { chainId, devBuy: lp.devBuy, launch: p, dev });
16743
17030
  return [{ to: lp.devBuy, data: launchFromVaultData(p, dev, signed) }];
16744
17031
  }
@@ -16749,14 +17036,14 @@ var init_launchpad = __esm({
16749
17036
  let bought = null;
16750
17037
  for (const log2 of receipt.logs) {
16751
17038
  try {
16752
- if (getAddress15(log2.address) === lp.launchpad) {
17039
+ if (getAddress16(log2.address) === lp.launchpad) {
16753
17040
  const e = decodeEventLog3({ abi: ostraLaunchpadAbi, topics: log2.topics, data: log2.data });
16754
17041
  if (e.eventName === "Launched") poolId2 = e.args.poolId;
16755
- } else if (getAddress15(log2.address) === lp.devBuy) {
17042
+ } else if (getAddress16(log2.address) === lp.devBuy) {
16756
17043
  const e = decodeEventLog3({ abi: ostraDevBuyAbi, topics: log2.topics, data: log2.data });
16757
17044
  if (e.eventName === "DevBought") {
16758
17045
  const g = e.args;
16759
- bought = { ...g, vesting: getAddress15(g.vesting) };
17046
+ bought = { ...g, vesting: getAddress16(g.vesting) };
16760
17047
  }
16761
17048
  }
16762
17049
  } catch {
@@ -16764,7 +17051,7 @@ var init_launchpad = __esm({
16764
17051
  }
16765
17052
  if (poolId2 === null) throw new Error(`transaction ${result.txHash} landed without the launch`);
16766
17053
  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) })}
17054
+ 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
17055
  `);
16769
17056
  return {
16770
17057
  status: "launched",
@@ -16773,16 +17060,28 @@ var init_launchpad = __esm({
16773
17060
  poolId: poolId2,
16774
17061
  creatorVault,
16775
17062
  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"
17063
+ pair: pair3.symbol,
17064
+ marketCapUsd: marketCapUsd === null ? null : formatTokenAmount(marketCapUsd),
17065
+ marketCapInPair: marketCapUnits === null ? "" : pairUnits(marketCapUnits),
17066
+ pairPriceUsd: price === null ? null : formatUnitsAmount(price.priceE8, 8),
17067
+ pairPriceAt: price === null ? null : new Date(price.updatedAt * 1e3).toISOString(),
17068
+ devBuy: bought === null ? null : { paid: pairUnits(bought.quotePaid), tokens: formatUnitsAmount(bought.tokens, 18), vesting: bought.vesting, cliffEnd: nowIso(bought.cliffEnd), vestingEnd: nowIso(bought.vestingEnd) },
17069
+ 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
17070
  };
16779
17071
  }
16780
- /** The pool's creator vault must be this agent's. */
17072
+ /** The pool's pair (null: a quote Ostra does not offer). */
17073
+ #pairOf(pool) {
17074
+ return findLaunchPair(this.#quoteOf(pool));
17075
+ }
17076
+ /** 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
17077
  async #ownPool(tokenText) {
16782
17078
  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;
17079
+ const pair3 = this.#pairOf(pool);
17080
+ 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`);
17081
+ const { vault: creator, sender } = await this.pairCreator(pair3.symbol);
17082
+ const creatorVault = await creator.vaultAddress();
17083
+ 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}`);
17084
+ return { pool, pair: pair3, creator, sender };
16786
17085
  }
16787
17086
  /**
16788
17087
  * Changes one of the pool's rules (POOL_SETTING_NAMES), only in the
@@ -16794,27 +17093,28 @@ var init_launchpad = __esm({
16794
17093
  const field = POOL_SETTING_NAMES[a.setting];
16795
17094
  if (field === void 0) throw new RangeError(`unknown setting ${a.setting}; one of ${Object.keys(POOL_SETTING_NAMES).join(", ")}`);
16796
17095
  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);
17096
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16798
17097
  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");
17098
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, "pool setting");
16800
17099
  }
16801
17100
  /** Sets `recipient`'s share (bps; 0 removes it) of the pool's creator payouts; the creator vault keeps the rest. */
16802
17101
  async setCreatorShare(a) {
16803
17102
  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);
17103
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16805
17104
  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");
17105
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, "creator share");
16807
17106
  }
16808
- /** Pays out what the creator split owes `account` (default: this agent's creator vault) for the pool. */
17107
+ /** Pays out what the creator split owes `account` (default: this agent's creator vault of the pool's pair) for the pool. */
16809
17108
  async releaseCreatorPayout(a) {
16810
17109
  const lp = this.#lp();
16811
17110
  const pool = await this.#pool(a.token);
16812
- const account = a.account === void 0 ? await this.#creator.vaultAddress() : address2(a.account, "account");
17111
+ const pair3 = this.#pairOf(pool);
17112
+ const own = pair3 === null ? this.#creator : (await this.pairCreator(pair3.symbol)).vault;
17113
+ const account = a.account === void 0 ? await own.vaultAddress() : address2(a.account, "account");
16813
17114
  const owed = await this.#client.readContract({ address: lp.creatorSplit, abi: ostraCreatorSplitAbi, functionName: "owedOf", args: [pool.id, account] });
16814
17115
  if (owed === 0n) throw new Error(`the creator split owes ${account} nothing for this pool now`);
16815
17116
  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) };
17117
+ return { txHash, account, amount: formatUnitsAmount(owed, pair3 === null ? await tokenDecimals(this.#client, this.#quoteOf(pool)) : pair3.decimals) };
16818
17118
  }
16819
17119
  /** A call anyone may make, sent for the buyer vault (its relayer channel pays). */
16820
17120
  async #public(call, purpose) {
@@ -16824,27 +17124,27 @@ var init_launchpad = __esm({
16824
17124
  // ---------------------------------------------------------------------------
16825
17125
  // Airdrops
16826
17126
  // ---------------------------------------------------------------------------
16827
- /** Buys `amount` USDG of the token from the creator vault into OstraAirdrop, for the pool's next round. */
17127
+ /** Buys `amount` of the pool's pair worth of the token from its creator vault into OstraAirdrop, for the pool's next round (USDG, OSTRA and stock pools). */
16828
17128
  async airdropFund(a) {
16829
17129
  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);
17130
+ const { pool, pair: pair3, creator, sender } = await this.#ownPool(a.token);
17131
+ 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");
17132
+ const amountIn = parseUnitsAmount(a.amount, pair3.decimals);
16833
17133
  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();
17134
+ const s = await creator.state();
17135
+ 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`);
17136
+ const quoteIs0 = getAddress16(pool.key.currency0) === pair3.address;
16837
17137
  const q = await quoteVaultSwap(this.#client, this.#config.deployment, {
16838
17138
  vault: s.vault,
16839
- relayer: await this.#creatorSender.payer(),
16840
- swap: { key: pool.key, zeroForOne: usdgIs0, amountIn, recipient: lp.airdrop },
17139
+ relayer: await sender.payer(),
17140
+ swap: { key: pool.key, zeroForOne: quoteIs0, amountIn, recipient: lp.airdrop },
16841
17141
  slippageBps: slippage(a.slippageBps)
16842
17142
  });
16843
- const result = await this.#creator.operate({
17143
+ const result = await creator.operate({
16844
17144
  gas: swapSpendGas(q.swap.swapGas),
16845
17145
  moves: amountIn,
16846
17146
  what: "airdrop purchase",
16847
- build: async ({ v, vault, keychain, chainId }) => [spendCall(vault, await signVaultSwap(keychain, v, { chainId, token: this.#usdg(), swap: q.swap }))]
17147
+ build: async ({ v, vault, keychain, chainId }) => [spendCall(vault, await signVaultSwap(keychain, v, { chainId, token: pair3.address, swap: q.swap }))]
16848
17148
  });
16849
17149
  if (result.status !== "sent") return result;
16850
17150
  const outcome = await this.#swapOutcome(result.txHash, s.vault);
@@ -16852,7 +17152,7 @@ var init_launchpad = __esm({
16852
17152
  status: "sent",
16853
17153
  txHash: result.txHash,
16854
17154
  swapped: outcome.swapped,
16855
- amountIn: formatTokenAmount(outcome.amountIn),
17155
+ amountIn: formatUnitsAmount(outcome.amountIn, pair3.decimals),
16856
17156
  amountOut: formatUnitsAmount(outcome.amountOut, 18),
16857
17157
  minOut: formatUnitsAmount(q.swap.minOut, 18),
16858
17158
  from: s.vault,
@@ -16871,7 +17171,7 @@ var init_launchpad = __esm({
16871
17171
  */
16872
17172
  async airdropStart(a) {
16873
17173
  const lp = this.#lp();
16874
- const pool = await this.#ownPool(a.token);
17174
+ const { pool, creator, sender } = await this.#ownPool(a.token);
16875
17175
  const entries = a.entries.map((e) => ({ account: address2(e.account, "account"), amount: parseUnitsAmount(e.amount, 18) }));
16876
17176
  const read = (functionName, args) => this.#client.readContract({ address: lp.airdrop, abi: ostraAirdropAbi, functionName, args });
16877
17177
  const current = await read("round", [pool.id]);
@@ -16895,7 +17195,7 @@ var init_launchpad = __esm({
16895
17195
  `);
16896
17196
  const sent = [];
16897
17197
  const step2 = async (params, what) => {
16898
- const r = await this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.CONFIGURE_POOL, params, what);
17198
+ const r = await this.#simulatedSpend(creator, sender, VAULT_ACTION.CONFIGURE_POOL, params, what);
16899
17199
  if (r.status !== "sent") throw new Error(`${r.note}; airdrop_start was not finished, call it again with the same entries`);
16900
17200
  sent.push(r.txHash);
16901
17201
  };
@@ -16919,7 +17219,7 @@ var init_launchpad = __esm({
16919
17219
  } else {
16920
17220
  const text2 = await this.#folder.read(this.#airdropName(pool.id, r.number));
16921
17221
  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);
17222
+ const claim = JSON.parse(text2).claims.find((c) => getAddress16(c.account) === account);
16923
17223
  if (claim === void 0) throw new Error(`${account} is not in round ${r.number}`);
16924
17224
  amount = BigInt(claim.amount);
16925
17225
  proof = claim.proof;
@@ -16954,7 +17254,7 @@ var init_launchpad = __esm({
16954
17254
  const text2 = await this.#folder.read(this.#launchName(token));
16955
17255
  const v = text2 === void 0 ? null : JSON.parse(text2).vesting ?? null;
16956
17256
  if (v === null) throw new Error(`this agent's records hold no dev buy vesting of ${token}; give its address`);
16957
- vesting = getAddress15(v);
17257
+ vesting = getAddress16(v);
16958
17258
  }
16959
17259
  const id = await this.#client.readContract({ address: lp.devBuy, abi: ostraDevBuyAbi, functionName: "vestingPool", args: [vesting] });
16960
17260
  if (BigInt(id) === 0n) throw new Error(`${vesting} is not an Ostra dev buy's vesting`);
@@ -16965,8 +17265,8 @@ var init_launchpad = __esm({
16965
17265
  const vesting = await this.#vesting(a.token, a.vesting);
16966
17266
  const read = (functionName) => this.#client.readContract({ address: vesting, abi: ostraVestingAbi, functionName });
16967
17267
  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) };
17268
+ const held = await tokenBalance(this.#client, getAddress16(terms.token), vesting);
17269
+ return { vesting, beneficiary: getAddress16(terms.beneficiary), cliffEnd: nowIso(schedule[0]), end: nowIso(schedule[1]), held: formatUnitsAmount(held, 18), releasable: formatUnitsAmount(releasable, 18) };
16970
17270
  }
16971
17271
  /** Releases what the vesting has vested to its beneficiary (this agent's token vault). */
16972
17272
  async vestingRelease(a) {
@@ -16982,14 +17282,17 @@ var init_launchpad = __esm({
16982
17282
  const txHash = await this.#public({ to: vesting, data: encodeFunctionData8({ abi: ostraVestingAbi, functionName: "claimDividends" }) }, "claim the vesting's dividends");
16983
17283
  return { txHash };
16984
17284
  }
16985
- /** Claims what swaps that named `referrer` (default: this agent's buyer vault) earned it, in USDG. */
17285
+ /** 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
17286
  async claimReferral(a = {}) {
16987
17287
  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) };
17288
+ const pair3 = launchPair(a.pair ?? "USDG");
17289
+ 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");
17290
+ const referrer = a.referrer === void 0 ? await this.tokenVault(pair3.address).vaultAddress() : address2(a.referrer, "referrer");
17291
+ await this.#notStranded(referrer, this.#pairToken(pair3), "the referral earnings");
17292
+ const owed = await this.#client.readContract({ address: lp.hook, abi: ostraHookAbi, functionName: "referralOwed", args: [referrer, pair3.address] });
17293
+ if (owed === 0n) throw new Error(`${referrer} has no referral earnings in ${pair3.symbol} to claim`);
17294
+ const txHash = await this.#public({ to: lp.dispatcher, data: encodeFunctionData8({ abi: ostraDispatcherAbi, functionName: "claimReferral", args: [referrer, pair3.address] }) }, "claim the referral earnings");
17295
+ return { txHash, referrer, amount: formatUnitsAmount(owed, pair3.decimals), pair: pair3.symbol };
16993
17296
  }
16994
17297
  // ---------------------------------------------------------------------------
16995
17298
  // The token list (every Ostra launch, for any buyer)
@@ -17000,7 +17303,7 @@ var init_launchpad = __esm({
17000
17303
  await this.#folder.ensure();
17001
17304
  const text2 = await this.#folder.read("token-index.json");
17002
17305
  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: [] };
17306
+ if (getAddress16(index.launchpad) !== getAddress16(lp.launchpad)) index = { version: 1, launchpad: lp.launchpad, scannedTo: (BigInt(this.#config.deployment.fromBlock) - 1n).toString(), launches: [] };
17004
17307
  const latest = await this.#client.getBlockNumber();
17005
17308
  let from = BigInt(index.scannedTo) + 1n;
17006
17309
  if (from > latest) return index;
@@ -17009,7 +17312,7 @@ var init_launchpad = __esm({
17009
17312
  const logs = await this.#client.getLogs({ address: lp.launchpad, event: launchedEvent, fromBlock: from, toBlock: to });
17010
17313
  for (const l of logs) {
17011
17314
  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 });
17315
+ 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
17316
  }
17014
17317
  index.scannedTo = to.toString();
17015
17318
  }
@@ -17060,11 +17363,15 @@ var init_launchpad = __esm({
17060
17363
  // ---------------------------------------------------------------------------
17061
17364
  // The agent's identity: profile, ERC-8004 registration, token metadata (vault version 2)
17062
17365
  // ---------------------------------------------------------------------------
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() {
17366
+ /** Throws before anything is signed when the deployment record has no OstraRegistry (vault version 2 not executed yet). */
17367
+ #requireRegistry() {
17065
17368
  if (this.#config.deployment.registry === void 0) {
17066
17369
  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
17370
  }
17371
+ }
17372
+ /** The creator vault, once it exists and runs vault version 2 with OstraRegistry in the deployment record; else throws before anything is signed. */
17373
+ async #identityVault() {
17374
+ this.#requireRegistry();
17068
17375
  const s = await this.#creator.state();
17069
17376
  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
17377
  let version = 1;
@@ -17105,7 +17412,7 @@ var init_launchpad = __esm({
17105
17412
  const receipt = await this.#client.getTransactionReceipt({ hash: r.txHash });
17106
17413
  let agentId;
17107
17414
  for (const log2 of receipt.logs) {
17108
- if (getAddress15(log2.address) !== vault) continue;
17415
+ if (getAddress16(log2.address) !== vault) continue;
17109
17416
  try {
17110
17417
  const e = decodeEventLog3({ abi: ostraVaultAbi, data: log2.data, topics: log2.topics });
17111
17418
  if (e.eventName === "AgentRegistered") agentId = e.args.agentId.toString();
@@ -17127,13 +17434,25 @@ var init_launchpad = __esm({
17127
17434
  if (r.status === "sent") await this.#folder.write("agent.json", JSON.stringify({ ...record, uri: uri2, at: Math.floor(Date.now() / 1e3) }, null, 2));
17128
17435
  return r;
17129
17436
  }
17130
- /** Sets the metadata (a URI to its JSON: image, description, links) of a token this agent's creator vault launched. */
17437
+ /** 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
17438
  async setTokenMetadata(a) {
17132
17439
  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");
17440
+ this.#requireRegistry();
17441
+ const { pool, creator, sender } = await this.#ownPool(a.token);
17442
+ await this.#registryVersion(creator);
17443
+ return this.#simulatedSpend(creator, sender, VAULT_ACTION.SET_TOKEN_METADATA, setTokenMetadataParams({ token: pool.token, poolId: pool.id, uri: a.uri }), "token metadata");
17444
+ }
17445
+ /** `vault` runs vault version VAULT_VERSION (OstraRegistry's writers); else throws before anything is signed. */
17446
+ async #registryVersion(vault) {
17447
+ const s = await vault.state();
17448
+ if (!s.exists) throw new Error(`the creator vault ${s.vault} does not exist yet; nothing was signed`);
17449
+ let version = 1;
17450
+ try {
17451
+ version = Number(await this.#client.readContract({ address: s.vault, abi: ostraVaultAbi, functionName: "VERSION" }));
17452
+ } catch {
17453
+ version = 1;
17454
+ }
17455
+ if (version < VAULT_VERSION) throw new Error(`the creator vault ${s.vault} runs version ${version}, not ${VAULT_VERSION}; nothing was signed`);
17137
17456
  }
17138
17457
  /**
17139
17458
  * Publishes the agent's profile: builds its ERC-8004 registration file (name, description, image, links, the
@@ -17146,14 +17465,29 @@ var init_launchpad = __esm({
17146
17465
  const file = profileFile({ ...p, vault, agentId: record?.agentId ?? null });
17147
17466
  const uri2 = await uploadJson(this.#config.relayerUrl, file);
17148
17467
  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) };
17468
+ const pairCreators = [];
17469
+ if (profile.status === "sent") {
17470
+ const pairs = new Set((await this.launches()).map((l) => l.pair ?? "USDG").filter((x) => x !== "USDG"));
17471
+ for (const symbol of pairs) {
17472
+ const { vault: creator, sender } = await this.pairCreator(symbol);
17473
+ const at = await creator.vaultAddress();
17474
+ try {
17475
+ await this.#registryVersion(creator);
17476
+ pairCreators.push({ pair: symbol, vault: at, result: await this.#simulatedSpend(creator, sender, VAULT_ACTION.SET_PROFILE, setProfileParams(uri2), `${symbol} creator vault profile`) });
17477
+ } catch (err) {
17478
+ pairCreators.push({ pair: symbol, vault: at, error: err.message });
17479
+ }
17480
+ }
17481
+ }
17482
+ if (profile.status !== "sent" || record === null) return { uri: uri2, profile, agentUri: null, pairCreators };
17483
+ return { uri: uri2, profile, agentUri: await this.setAgentUri(uri2), pairCreators };
17151
17484
  }
17152
17485
  /** Publishes the metadata of a token this agent launched (description, image, links), pins it and sets it in OstraRegistry. */
17153
17486
  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`);
17487
+ this.#requireRegistry();
17488
+ const { pool, creator } = await this.#ownPool(a.token);
17489
+ await this.#registryVersion(creator);
17490
+ const vault = await creator.vaultAddress();
17157
17491
  const [name, symbol] = await Promise.all([
17158
17492
  this.#client.readContract({ address: pool.token, abi: ostraTokenAbi, functionName: "name" }),
17159
17493
  this.#client.readContract({ address: pool.token, abi: ostraTokenAbi, functionName: "symbol" })
@@ -17167,14 +17501,14 @@ var init_launchpad = __esm({
17167
17501
  });
17168
17502
 
17169
17503
  // ../core/src/pay.ts
17170
- import { getAddress as getAddress16, isAddress as isAddress10 } from "viem";
17504
+ import { getAddress as getAddress17, isAddress as isAddress10 } from "viem";
17171
17505
  import { x402Client as x402Client2, x402HTTPClient as x402HTTPClient2 } from "@x402/core/client";
17172
17506
  import { toClientEvmSigner as toClientEvmSigner2 } from "@x402/evm";
17173
17507
  import { BatchSettlementEvmScheme as BatchSettlementEvmScheme2 } from "@x402/evm/batch-settlement/client";
17174
17508
  function offerProblem(r, network, token) {
17175
17509
  if (r.scheme !== "batch-settlement") return `${r.scheme} (this agent pays batch-settlement)`;
17176
17510
  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})`;
17511
+ if (!isAddress10(r.asset, { strict: false }) || getAddress17(r.asset) !== token) return `in ${r.asset} (this agent pays in ${token})`;
17178
17512
  return null;
17179
17513
  }
17180
17514
  var PAY_CHANNEL_DEPOSIT, PAY_MAX_BODY_CHARS, REQUEST_TIMEOUT_MS2, MIN_LIFE_LEFT_SECONDS, now2, PayService;
@@ -17239,7 +17573,7 @@ var init_pay = __esm({
17239
17573
  async #channelFor(payee, receiver, withdrawDelay, price) {
17240
17574
  for (const r of await this.#channels.payRecords()) {
17241
17575
  if (r.pay === void 0 || r.closeRequested) continue;
17242
- if (getAddress16(r.payee) !== payee || getAddress16(r.config.receiver) !== receiver || r.gracePeriod !== withdrawDelay) continue;
17576
+ if (getAddress17(r.payee) !== payee || getAddress17(r.config.receiver) !== receiver || r.gracePeriod !== withdrawDelay) continue;
17243
17577
  if (r.pay.expiresAt < now2() + MIN_LIFE_LEFT_SECONDS || payCapacity(r) < price) continue;
17244
17578
  const ch = await readChannel(this.#client, this.#config.deployment.batchSettlement, r.channel);
17245
17579
  if (ch.pendingWithdrawal !== null || ch.balance < BigInt(r.pay.cumulative) + price) continue;
@@ -17250,10 +17584,10 @@ var init_pay = __esm({
17250
17584
  /** The stock x402 client paying from channel record `r` (the vault as payer, the channel's voucher key signing, never depositing). */
17251
17585
  #x402(r) {
17252
17586
  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`);
17587
+ 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
17588
  const client = this.#client;
17255
17589
  const payer = {
17256
- address: getAddress16(r.config.payer),
17590
+ address: getAddress17(r.config.payer),
17257
17591
  signTypedData: () => Promise.reject(new Error("a vault never signs typed data")),
17258
17592
  readContract: (args) => client.readContract(args)
17259
17593
  };
@@ -17322,13 +17656,13 @@ var init_pay = __esm({
17322
17656
  if (price > maxPrice) throw new Error(`${input.url} asks ${formatTokenAmount(price)}, more than maxPrice ${input.maxPrice}; nothing was paid`);
17323
17657
  const extra = offer.extra ?? {};
17324
17658
  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);
17659
+ const payee = getAddress17(extra.receiverAuthorizer);
17326
17660
  const withdrawDelay = extra.withdrawDelay;
17327
17661
  if (typeof withdrawDelay !== "number" || !Number.isInteger(withdrawDelay) || withdrawDelay < 900 || withdrawDelay > BATCH_MAX_WITHDRAW_DELAY_SECONDS) {
17328
17662
  throw new Error(`the offer's withdrawDelay ${String(withdrawDelay)} is not 900..${BATCH_MAX_WITHDRAW_DELAY_SECONDS} s; nothing was paid`);
17329
17663
  }
17330
17664
  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);
17665
+ const receiver = getAddress17(offer.payTo);
17332
17666
  const payeeVault = await readVault(this.#client, this.#config.deployment, payee);
17333
17667
  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
17668
  if (payee === await this.#vault.vaultAddress()) throw new Error("the seller is this agent's own vault");
@@ -17396,8 +17730,21 @@ var init_pay = __esm({
17396
17730
  }
17397
17731
  });
17398
17732
 
17733
+ // ../core/src/seed-backup.ts
17734
+ import { scryptAsync } from "@noble/hashes/scrypt.js";
17735
+ var SCRYPT, AAD, SCRYPT_MAXMEM;
17736
+ var init_seed_backup = __esm({
17737
+ "../core/src/seed-backup.ts"() {
17738
+ "use strict";
17739
+ init_define_OSTRA_PACKAGED();
17740
+ SCRYPT = { N: 1 << 17, r: 8, p: 1 };
17741
+ AAD = new TextEncoder().encode("OSTRA/SEED-BACKUP/v1");
17742
+ SCRYPT_MAXMEM = 256 * 1024 * 1024;
17743
+ }
17744
+ });
17745
+
17399
17746
  // ../core/src/sessions.ts
17400
- import { bytesToHex as bytesToHex12, getAddress as getAddress17, isAddress as isAddress11 } from "viem";
17747
+ import { bytesToHex as bytesToHex12, getAddress as getAddress18, isAddress as isAddress11 } from "viem";
17401
17748
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
17402
17749
  var init_sessions = __esm({
17403
17750
  "../core/src/sessions.ts"() {
@@ -17409,19 +17756,19 @@ var init_sessions = __esm({
17409
17756
  });
17410
17757
 
17411
17758
  // ../core/src/shield.ts
17412
- import { bytesToHex as bytesToHex13, encodeFunctionData as encodeFunctionData9, getAddress as getAddress18, hexToBytes as hexToBytes8, parseSignature } from "viem";
17759
+ import { bytesToHex as bytesToHex13, encodeFunctionData as encodeFunctionData9, getAddress as getAddress19, hexToBytes as hexToBytes8, parseSignature } from "viem";
17413
17760
  function leafKeyIndex(attempt, j) {
17414
17761
  if (!Number.isInteger(attempt) || attempt < 0 || attempt >= MAX_LEAF_ATTEMPTS) throw new RangeError(`leaf set ${String(attempt)} is out of range`);
17415
17762
  if (!Number.isInteger(j) || j < 0 || j >= LEAF_KEYS_PER_ATTEMPT) throw new RangeError(`leaf ${String(j)} is out of range`);
17416
17763
  return attempt * LEAF_KEYS_PER_ATTEMPT + j;
17417
17764
  }
17418
17765
  function recordName(chainId, token, index) {
17419
- return `${chainId}-${getAddress18(token)}-${index}.json`;
17766
+ return `${chainId}-${getAddress19(token)}-${index}.json`;
17420
17767
  }
17421
17768
  function decodeApproveMessage(message) {
17422
17769
  if (message.length !== APPROVE_MESSAGE_LEN) throw new Error("not an approval message");
17423
17770
  const chainId = Number(BigInt(bytesToHex13(message.subarray(20, 52))));
17424
- const pool = getAddress18(bytesToHex13(message.subarray(52, 72)));
17771
+ const pool = getAddress19(bytesToHex13(message.subarray(52, 72)));
17425
17772
  const key = bytesToHex13(message.subarray(72, 104));
17426
17773
  const batchId = BigInt(bytesToHex13(message.subarray(104, 112)));
17427
17774
  const state = bytesToHex13(message.subarray(112, 144));
@@ -17496,7 +17843,7 @@ var init_shield = __esm({
17496
17843
  /** True when `record` belongs to this service's vault. */
17497
17844
  async #mine(record) {
17498
17845
  if (record.vault === void 0) return this.#vault.kind === "buyer";
17499
- return getAddress18(record.vault) === await this.#vault.vaultAddress();
17846
+ return getAddress19(record.vault) === await this.#vault.vaultAddress();
17500
17847
  }
17501
17848
  /** A new record under the first free deposit number (created exclusively: another vault's service may be creating one too). */
17502
17849
  async #createRecord(make) {
@@ -17657,8 +18004,8 @@ var init_shield = __esm({
17657
18004
  const { batchCount } = await readPool(this.#client, this.#pool());
17658
18005
  const health = await this.#aggregator("GET", "/v1/health");
17659
18006
  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]);
18007
+ const aggregator = getAddress19(health.aggregator);
18008
+ const accounts = /* @__PURE__ */ new Set([...(record.aggregators ?? []).map((a) => getAddress19(a)), aggregator]);
17662
18009
  if (accounts.size !== (record.aggregators ?? []).length) {
17663
18010
  record.aggregators = [...accounts];
17664
18011
  await this.#save(record);
@@ -17713,11 +18060,11 @@ var init_shield = __esm({
17713
18060
  const room = await this.#room(p.maxTotal);
17714
18061
  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
18062
  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()) {
18063
+ if (terms.chainId !== this.#config.chainId || typeof terms.pool !== "string" || getAddress19(terms.pool) !== pool || typeof terms.token !== "string" || getAddress19(terms.token) !== this.#token()) {
17717
18064
  throw new Error(`the aggregator serves pool ${String(terms.pool)} on chain ${String(terms.chainId)}, not ${pool} on ${this.#config.chainId}`);
17718
18065
  }
17719
18066
  if (typeof terms.aggregator !== "string") throw new Error("the aggregator's terms name no aggregator account");
17720
- const aggregatorAccount = getAddress18(terms.aggregator);
18067
+ const aggregatorAccount = getAddress19(terms.aggregator);
17721
18068
  const status = await this.#vault.status();
17722
18069
  if (!status.vaultExists) throw new Error("open the vault and fund it first (open_vault)");
17723
18070
  const vault = await this.#vault.vaultAddress();
@@ -17974,7 +18321,7 @@ var init_shield = __esm({
17974
18321
  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
18322
  });
17976
18323
  }
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 });
18324
+ 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
18325
  }
17979
18326
  return out;
17980
18327
  });
@@ -17992,9 +18339,9 @@ var init_storage = __esm({
17992
18339
  });
17993
18340
 
17994
18341
  // ../core/src/unshield.ts
17995
- import { encodeFunctionData as encodeFunctionData10, getAddress as getAddress19 } from "viem";
18342
+ import { encodeFunctionData as encodeFunctionData10, getAddress as getAddress20 } from "viem";
17996
18343
  function recordName2(chainId, token, index) {
17997
- return `${chainId}-${getAddress19(token)}-${index}.json`;
18344
+ return `${chainId}-${getAddress20(token)}-${index}.json`;
17998
18345
  }
17999
18346
  function unshieldDelaySeconds(windowHours) {
18000
18347
  if (!Number.isFinite(windowHours) || windowHours < 0 || windowHours > UNSHIELD_MAX_WINDOW_HOURS) {
@@ -18067,7 +18414,7 @@ var init_unshield = __esm({
18067
18414
  }
18068
18415
  /** The private vaults created so far (their leaf spent), by index. */
18069
18416
  vaults() {
18070
- return this.#serial(async () => (await this.#records()).filter((r) => r.status === "spent").map((r) => ({ index: BigInt(r.index), vault: getAddress19(r.vault) })));
18417
+ return this.#serial(async () => (await this.#records()).filter((r) => r.status === "spent").map((r) => ({ index: BigInt(r.index), vault: getAddress20(r.vault) })));
18071
18418
  }
18072
18419
  /** The number of the private vault at `vault`, or throws when it is none of this agent's. */
18073
18420
  async indexOf(vault) {
@@ -18166,7 +18513,7 @@ var init_unshield = __esm({
18166
18513
  async #spend(r) {
18167
18514
  const ctx = await this.#context(BigInt(r.index));
18168
18515
  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}`);
18516
+ if (vault !== getAddress20(r.vault)) throw new Error(`private vault ${r.index} is ${vault}, not the recorded ${r.vault}`);
18170
18517
  const pool = this.#config.deployment.pool;
18171
18518
  const batch = await readBatch(this.#client, pool, BigInt(r.batch));
18172
18519
  const leaf = batch.leaves[r.leafIndex];
@@ -18205,10 +18552,10 @@ import {
18205
18552
  decodeErrorResult as decodeErrorResult2,
18206
18553
  encodeAbiParameters as encodeAbiParameters6,
18207
18554
  encodeFunctionData as encodeFunctionData11,
18208
- getAddress as getAddress20,
18555
+ getAddress as getAddress21,
18209
18556
  maxUint256,
18210
- parseAbi as parseAbi2,
18211
- zeroAddress as zeroAddress10
18557
+ parseAbi as parseAbi3,
18558
+ zeroAddress as zeroAddress11
18212
18559
  } from "viem";
18213
18560
  var MAX_UINT160, MAX_UINT128, universalRouterAbi, permit2Abi, wrappedErrorAbi;
18214
18561
  var init_wallet_swap = __esm({
@@ -18219,21 +18566,21 @@ var init_wallet_swap = __esm({
18219
18566
  init_pools();
18220
18567
  MAX_UINT160 = (1n << 160n) - 1n;
18221
18568
  MAX_UINT128 = (1n << 128n) - 1n;
18222
- universalRouterAbi = parseAbi2([
18569
+ universalRouterAbi = parseAbi3([
18223
18570
  "function execute(bytes commands, bytes[] inputs, uint256 deadline) payable",
18224
18571
  "error TransactionDeadlinePassed()",
18225
18572
  "error ExecutionFailed(uint256 commandIndex, bytes message)",
18226
18573
  "error V4TooLittleReceived(uint256 minAmountOutReceived, uint256 amountReceived)",
18227
18574
  "error V4TooMuchRequested(uint256 maxAmountInRequested, uint256 amountRequested)"
18228
18575
  ]);
18229
- permit2Abi = parseAbi2([
18576
+ permit2Abi = parseAbi3([
18230
18577
  "function allowance(address owner, address token, address spender) view returns (uint160 amount, uint48 expiration, uint48 nonce)",
18231
18578
  "error AllowanceExpired(uint256 deadline)",
18232
18579
  "error InsufficientAllowance(uint256 amount)",
18233
18580
  "error InvalidNonce()",
18234
18581
  "error SignatureExpired(uint256 signatureDeadline)"
18235
18582
  ]);
18236
- wrappedErrorAbi = parseAbi2(["error WrappedError(address target, bytes4 selector, bytes reason, bytes details)"]);
18583
+ wrappedErrorAbi = parseAbi3(["error WrappedError(address target, bytes4 selector, bytes reason, bytes details)"]);
18237
18584
  }
18238
18585
  });
18239
18586
 
@@ -18257,6 +18604,7 @@ var init_src2 = __esm({
18257
18604
  init_pools();
18258
18605
  init_random();
18259
18606
  init_relay();
18607
+ init_seed_backup();
18260
18608
  init_sessions();
18261
18609
  init_shield();
18262
18610
  init_storage();
@@ -18355,7 +18703,7 @@ import { join as join4 } from "node:path";
18355
18703
  import { createInterface } from "node:readline";
18356
18704
  import { Writable } from "node:stream";
18357
18705
  function scrypt(passphrase, salt) {
18358
- return new Promise((resolve, reject) => scryptCb(passphrase, salt, 32, SCRYPT, (err, key) => err === null ? resolve(key) : reject(err)));
18706
+ return new Promise((resolve, reject) => scryptCb(passphrase, salt, 32, SCRYPT2, (err, key) => err === null ? resolve(key) : reject(err)));
18359
18707
  }
18360
18708
  async function sealSeedWithPassphrase(seed, passphrase) {
18361
18709
  if (seed.length !== 32) throw new RangeError("the seed is 32 bytes");
@@ -18363,18 +18711,18 @@ async function sealSeedWithPassphrase(seed, passphrase) {
18363
18711
  const iv = randomBytes5(12);
18364
18712
  const key = await scrypt(passphrase, salt);
18365
18713
  const cipher = createCipheriv("aes-256-gcm", key, iv);
18366
- cipher.setAAD(AAD);
18714
+ cipher.setAAD(AAD2);
18367
18715
  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") })}
18716
+ 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
18717
  `;
18370
18718
  }
18371
18719
  async function openSeedWithPassphrase(text2, passphrase) {
18372
18720
  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)");
18721
+ 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
18722
  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
18723
  const key = await scrypt(passphrase, Buffer.from(b.salt, "hex"));
18376
18724
  const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(b.iv, "hex"));
18377
- decipher.setAAD(AAD);
18725
+ decipher.setAAD(AAD2);
18378
18726
  decipher.setAuthTag(Buffer.from(b.tag, "hex"));
18379
18727
  let seed;
18380
18728
  try {
@@ -18437,7 +18785,7 @@ async function runBackup(args) {
18437
18785
  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
18786
  `);
18439
18787
  }
18440
- var AAD, SCRYPT, MIN_PASSPHRASE;
18788
+ var AAD2, SCRYPT2, MIN_PASSPHRASE;
18441
18789
  var init_backup_cli = __esm({
18442
18790
  "src/backup-cli.ts"() {
18443
18791
  "use strict";
@@ -18445,8 +18793,8 @@ var init_backup_cli = __esm({
18445
18793
  init_disk();
18446
18794
  init_keys2();
18447
18795
  init_packaged();
18448
- AAD = Buffer.from("OSTRA/SEED-BACKUP/v1", "utf8");
18449
- SCRYPT = { N: 1 << 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 };
18796
+ AAD2 = Buffer.from("OSTRA/SEED-BACKUP/v1", "utf8");
18797
+ SCRYPT2 = { N: 1 << 17, r: 8, p: 1, maxmem: 256 * 1024 * 1024 };
18450
18798
  MIN_PASSPHRASE = 12;
18451
18799
  if (packaged === null && import.meta.url === `file://${process.argv[1]}`) {
18452
18800
  runBackup(process.argv.slice(2)).catch((err) => {
@@ -18461,7 +18809,7 @@ var init_backup_cli = __esm({
18461
18809
  // src/agent.ts
18462
18810
  import { readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
18463
18811
  import { join as join5 } from "node:path";
18464
- import { createPublicClient as createPublicClient2, getAddress as getAddress21, isAddress as isAddress12 } from "viem";
18812
+ import { createPublicClient as createPublicClient2, getAddress as getAddress22, isAddress as isAddress12 } from "viem";
18465
18813
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
18466
18814
  var SWEEP_SECONDS, SELLER_RECORD, MERCHANT_DIR, Agent;
18467
18815
  var init_agent = __esm({
@@ -18554,7 +18902,7 @@ var init_agent = __esm({
18554
18902
  if (text2 === void 0) return;
18555
18903
  const r = JSON.parse(text2);
18556
18904
  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));
18905
+ this.seller.setInitCrank(getAddress22(r.crank));
18558
18906
  }
18559
18907
  /** True once accept_payments set the seller vault up. */
18560
18908
  async sellerReady() {
@@ -18574,7 +18922,7 @@ var init_agent = __esm({
18574
18922
  let crank;
18575
18923
  const known = await this.keys.folder.read(SELLER_RECORD);
18576
18924
  if (known !== void 0) {
18577
- crank = getAddress21(JSON.parse(known).crank);
18925
+ crank = getAddress22(JSON.parse(known).crank);
18578
18926
  } else {
18579
18927
  let res;
18580
18928
  try {
@@ -18584,7 +18932,7 @@ var init_agent = __esm({
18584
18932
  }
18585
18933
  const body = await res.json().catch(() => null);
18586
18934
  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);
18935
+ crank = getAddress22(body.crank);
18588
18936
  this.seller.setInitCrank(crank);
18589
18937
  await this.keys.folder.write(SELLER_RECORD, `${JSON.stringify({ chainId: this.config.chainId, token, crank, merchant })}
18590
18938
  `);
@@ -18786,42 +19134,65 @@ function registerTools(server, agent) {
18786
19134
  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
19135
  tool(
18788
19136
  "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.",
19137
+ "Quotes a buy (the pool's pair in: USDG, OSTRA 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
19138
  { side: z.enum(["buy", "sell"]), token: address3, amount: units, slippageBps: slippage2, referrer: address3.optional() },
18791
19139
  (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
19140
  );
18793
19141
  tool(
18794
19142
  "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() },
19143
+ "Buys a launched token with its pool's pair: USDG from the vault, or OSTRA or a stock token from the agent's vault of that 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.",
19144
+ { token: address3, amount: units.describe("in the pool's pair: USDG (6 decimals), OSTRA or the stock token (18)"), slippageBps: slippage2, referrer: address3.optional() },
18797
19145
  (a) => lp().buy({ token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
18798
19146
  );
18799
19147
  tool(
18800
19148
  "sell",
18801
- "Sells a launched token from the agent's token vault for USDG into the vault.",
19149
+ "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
19150
  { token: address3, amount: units, slippageBps: slippage2, referrer: address3.optional() },
18803
19151
  (a) => lp().sell({ token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
18804
19152
  );
18805
19153
  tool("token_vault", "The agent's vault for a launched token: address, balance, dividends to claim.", { token: address3 }, (a) => lp().tokenVaultInfo(a.token));
18806
19154
  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
19155
  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));
19156
+ tool(
19157
+ "claim_referral",
19158
+ "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).",
19159
+ { referrer: address3.optional(), pair: pair2.optional() },
19160
+ (a) => lp().claimReferral({ ...a.referrer === void 0 ? {} : { referrer: a.referrer }, ...a.pair === void 0 ? {} : { pair: a.pair } })
19161
+ );
19162
+ tool(
19163
+ "pairs",
19164
+ "The pairs a launch can take (USDG, ETH, OSTRA 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).",
19165
+ {},
19166
+ () => lp().pairs({ prices: true })
19167
+ );
19168
+ tool(
19169
+ "fund_creator_vault",
19170
+ "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); OSTRA, 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).",
19171
+ { amount: units.describe("in the pair's token: USDG (6 decimals), WETH, OSTRA or the stock token (18)"), pair: pair2.optional() },
19172
+ (a) => lp().fundCreatorVault(a.amount, a.pair ?? "USDG")
19173
+ );
19174
+ tool(
19175
+ "creator_withdraw",
19176
+ "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.",
19177
+ { amount: units.describe("in the pair's token"), destination: address3.optional(), pair: pair2.optional() },
19178
+ (a) => lp().creatorWithdraw({ amount: a.amount, ...a.destination === void 0 ? {} : { destination: a.destination }, ...a.pair === void 0 ? {} : { pair: a.pair } })
19179
+ );
18811
19180
  tool(
18812
19181
  "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.",
19182
+ "Launches a token on the Ostra launchpad (1 billion supply) paired with USDG (default), ETH, OSTRA 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; OSTRA at Chainlink ETH/USD times its bonding curve's price). 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
19183
  {
18815
19184
  name: z.string().min(1).max(64),
18816
19185
  symbol: z.string().regex(/^[A-Za-z0-9]{1,11}$/),
18817
- marketCap: usdg.optional().describe("the starting market cap in USDG (or startTick)"),
19186
+ pair: pair2.optional().describe("the pool's pair: USDG (default), ETH, OSTRA or a listed stock token's ticker"),
19187
+ marketCap: usdg.optional().describe("the starting market cap in US dollars (or startTick)"),
18818
19188
  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(),
19189
+ 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
19190
  options: z.record(z.string(), z.unknown()).optional()
18821
19191
  },
18822
19192
  (a) => lp().launch({
18823
19193
  name: a.name,
18824
19194
  symbol: a.symbol,
19195
+ ...a.pair === void 0 ? {} : { pair: a.pair },
18825
19196
  ...a.marketCap === void 0 ? {} : { marketCap: a.marketCap },
18826
19197
  ...a.startTick === void 0 ? {} : { startTick: a.startTick },
18827
19198
  ...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 +19291,7 @@ function launchOptions(raw) {
18920
19291
  }
18921
19292
  return out;
18922
19293
  }
18923
- var toJson, address3, usdg, units, slippage2, bytes323, uri, publicFields, BIGINT_FIELDS, ALLOWED_OPTIONS;
19294
+ var toJson, address3, usdg, units, pair2, slippage2, bytes323, uri, publicFields, BIGINT_FIELDS, ALLOWED_OPTIONS;
18924
19295
  var init_tools = __esm({
18925
19296
  "src/tools.ts"() {
18926
19297
  "use strict";
@@ -18931,6 +19302,7 @@ var init_tools = __esm({
18931
19302
  address3 = z.string().regex(/^0x[0-9a-fA-F]{40}$/, "an address: 0x and 40 hex characters");
18932
19303
  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
19304
  units = z.string().regex(/^(0|[1-9][0-9]*)(\.[0-9]{1,18})?$/, "an amount: a plain decimal with at most 18 decimals");
19305
+ pair2 = z.enum(LAUNCH_PAIRS.map((p) => p.symbol));
18934
19306
  slippage2 = z.number().int().min(0).max(1e3).optional().describe("slippage in bps (default 100 = 1%, at most 1000)");
18935
19307
  bytes323 = z.string().regex(/^0x[0-9a-fA-F]{64}$/, "32 bytes of 0x hex");
18936
19308
  uri = z.string().max(256).regex(/^(ipfs|https):\/\/[^\s]+$/, "an ipfs:// or https:// URI of at most 256 bytes");