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