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