@ostraagent/mcp 0.1.11 → 0.1.13
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 +5 -4
- package/dist/ostra-mcp.js +588 -127
- 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.13", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xec0ca4A1393D91c42cA809F7F9dd82Bd597E8E58", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", registry: "0xcE6Cc1f93744f4090DA1444Bb8590d9206157506", vaultModule: "0x55A89fF907f5F5756470e2ec5Dc5E4E1C54Da770", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" }, breaker: "0x2E33244dE6A64007204f763e9ef81FC33F1a8e01" }, swapDeployment: { chainId: 4663, fromBlock: "94940585", router: "0x5b9686f384A5Fb768B2360cF0B893F0d1E960B01", stake: "0xaDA4FaB9c8aB9165bf4d6cD9d4Da6d6b32Bb4BC6", buyback: "0xd86083031b7Af66ecD5C9c5e90D4C6d8E661F3f5", spendVerifier: "0x0a0E28a81DE6289e5044950163FfE95b37180e3B", privatePool: "0xe4250895Da2499602Cee4e178Fb46A0cF39F5b76" }, prover: { wasm: "0xd5f3069334ab49a2f955e4b4be9493b80c3f7b2a85f4ff2073a327ef5fbc2747", glue: "0x4bc8bef7c755c83daaf833623f85473a20a7c40b390d8d639d9e4353618a0537" }, relayerUrl: "https://relayer.ostraagent.xyz", keeperUrl: "https://keeper.ostraagent.xyz", aggregatorUrl: "https://aggregator.ostraagent.xyz", aiUrl: "https://ai.ostraagent.xyz" };
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -13581,6 +13581,14 @@ var init_abi = __esm({
|
|
|
13581
13581
|
}
|
|
13582
13582
|
});
|
|
13583
13583
|
|
|
13584
|
+
// ../sdk/src/agent-card.ts
|
|
13585
|
+
var init_agent_card = __esm({
|
|
13586
|
+
"../sdk/src/agent-card.ts"() {
|
|
13587
|
+
"use strict";
|
|
13588
|
+
init_define_OSTRA_PACKAGED();
|
|
13589
|
+
}
|
|
13590
|
+
});
|
|
13591
|
+
|
|
13584
13592
|
// ../sdk/src/hash.ts
|
|
13585
13593
|
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
13586
13594
|
import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
|
|
@@ -15445,7 +15453,7 @@ var init_morpho = __esm({
|
|
|
15445
15453
|
});
|
|
15446
15454
|
|
|
15447
15455
|
// ../sdk/src/borro.ts
|
|
15448
|
-
import { getAddress as getAddress5, parseAbi as parseAbi2, zeroAddress as zeroAddress4 } from "viem";
|
|
15456
|
+
import { getAbiItem, getAddress as getAddress5, parseAbi as parseAbi2, zeroAddress as zeroAddress4 } from "viem";
|
|
15449
15457
|
function borroStocks() {
|
|
15450
15458
|
return BORRO_STOCKS.map((s) => {
|
|
15451
15459
|
const market2 = { loanToken: USDG, collateralToken: s.vault, oracle: s.oracle, irm: BORRO_IRM, lltv: s.lltv };
|
|
@@ -15466,6 +15474,26 @@ function halfLltvMaxDebt(collateral, price, lltv) {
|
|
|
15466
15474
|
const value = collateral * price / 10n ** 36n;
|
|
15467
15475
|
return value * lltv * VAULT_MAX_LTV_OF_LLTV_BPS / (10n ** 18n * 10000n);
|
|
15468
15476
|
}
|
|
15477
|
+
async function readHashApprovals(client, logsClient, vault, fromBlock) {
|
|
15478
|
+
const version = Number(await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "VERSION" }));
|
|
15479
|
+
if (version < VAULT_V3) return [];
|
|
15480
|
+
const event = getAbiItem({ abi: ostraVaultModuleAbi, name: "HashApproved" });
|
|
15481
|
+
const logs = await getLogsChunked(logsClient, { address: vault, event, fromBlock, toBlock: await logsClient.getBlockNumber({ cacheTime: 0 }) });
|
|
15482
|
+
const head = await blockTime(client);
|
|
15483
|
+
const hashes = [];
|
|
15484
|
+
for (const l of logs) {
|
|
15485
|
+
const h = l.args.hash.toLowerCase();
|
|
15486
|
+
if (!hashes.includes(h)) hashes.push(h);
|
|
15487
|
+
}
|
|
15488
|
+
const out = [];
|
|
15489
|
+
for (const hash of hashes) {
|
|
15490
|
+
const validFrom = BigInt(
|
|
15491
|
+
await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "hashValidFrom", args: [hash], blockNumber: head.blockNumber })
|
|
15492
|
+
);
|
|
15493
|
+
if (validFrom !== 0n) out.push({ hash, validFrom, valid: head.timestamp >= validFrom });
|
|
15494
|
+
}
|
|
15495
|
+
return out;
|
|
15496
|
+
}
|
|
15469
15497
|
async function readVaultGuard(client, vault) {
|
|
15470
15498
|
const version = Number(await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "VERSION" }));
|
|
15471
15499
|
const off = { withdrawal: zeroAddress4, delay: 0, dailyLimit: 0n };
|
|
@@ -15652,21 +15680,54 @@ var init_breaker = __esm({
|
|
|
15652
15680
|
|
|
15653
15681
|
// ../sdk/src/chain.ts
|
|
15654
15682
|
import { BaseError as BaseError2, createPublicClient, defineChain, http } from "viem";
|
|
15655
|
-
function
|
|
15656
|
-
const
|
|
15657
|
-
|
|
15683
|
+
function laggingRead(e) {
|
|
15684
|
+
const lag = (x) => {
|
|
15685
|
+
const o = x;
|
|
15686
|
+
if (o.code === -32602) return true;
|
|
15687
|
+
return o.code === -32e3 && /unsupported block number/i.test(`${String(o.details ?? "")} ${String(o.message ?? "")}`);
|
|
15688
|
+
};
|
|
15689
|
+
return e instanceof BaseError2 ? e.walk(lag) !== null : lag(e);
|
|
15690
|
+
}
|
|
15691
|
+
function transient(e) {
|
|
15692
|
+
const o = e;
|
|
15693
|
+
if (o === null || typeof o !== "object") return true;
|
|
15694
|
+
if (o.name === "AbortError") return false;
|
|
15695
|
+
if (typeof o.code === "number") return [-1, -32005, -32603, 429, -32007].includes(o.code);
|
|
15696
|
+
if (typeof o.status === "number" && o.status !== 0) return [403, 408, 413, 429, 500, 502, 503, 504].includes(o.status);
|
|
15697
|
+
return true;
|
|
15658
15698
|
}
|
|
15659
|
-
function
|
|
15660
|
-
const
|
|
15699
|
+
function failureText(e, url) {
|
|
15700
|
+
const o = e;
|
|
15701
|
+
const parts = [o?.shortMessage ?? o?.name ?? String(e)];
|
|
15702
|
+
if (typeof o?.code === "number") parts.push(`code ${o.code}`);
|
|
15703
|
+
if (typeof o?.status === "number" && o.status !== 0) parts.push(`HTTP ${o.status}`);
|
|
15704
|
+
if (typeof o?.details === "string" && o.details !== "" && o.details !== parts[0]) parts.push(o.details);
|
|
15705
|
+
return redact(parts.join("; "), url).slice(0, 300);
|
|
15706
|
+
}
|
|
15707
|
+
function retryingHttp(url, o) {
|
|
15708
|
+
const own = o !== void 0;
|
|
15709
|
+
const base = own ? http(url, { retryCount: 0, timeout: o.timeoutMs ?? RPC_TIMEOUT_MS }) : http(url, { retryCount: RPC_RETRIES, retryDelay: RPC_RETRY_WAIT_MS, timeout: RPC_TIMEOUT_MS });
|
|
15710
|
+
const retries = o?.retryCount ?? RPC_RETRIES;
|
|
15661
15711
|
return ((params) => {
|
|
15662
15712
|
const t = base(params);
|
|
15663
15713
|
const request = (async (args, options) => {
|
|
15664
|
-
|
|
15714
|
+
let lagging = 0;
|
|
15715
|
+
let failed = 0;
|
|
15716
|
+
for (; ; ) {
|
|
15665
15717
|
try {
|
|
15666
15718
|
return await t.request(args, options);
|
|
15667
15719
|
} catch (e) {
|
|
15668
|
-
|
|
15669
|
-
|
|
15720
|
+
let wait;
|
|
15721
|
+
if (lagging < INVALID_PARAMS_RETRIES && IDEMPOTENT_READS.has(args.method) && laggingRead(e)) {
|
|
15722
|
+
wait = INVALID_PARAMS_WAIT_MS * 2 ** lagging;
|
|
15723
|
+
lagging++;
|
|
15724
|
+
} else if (own && failed < retries && transient(e)) {
|
|
15725
|
+
const after = e.headers?.get?.("Retry-After");
|
|
15726
|
+
wait = after !== null && after !== void 0 && /^[0-9]+$/.test(after) ? Math.min(Number(after) * 1e3, RETRY_AFTER_MAX_MS) : RPC_RETRY_WAIT_MS * 2 ** failed;
|
|
15727
|
+
failed++;
|
|
15728
|
+
} else throw e;
|
|
15729
|
+
o?.onRetry?.({ method: args.method, retry: lagging + failed, waitMs: wait, error: failureText(e, url) });
|
|
15730
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
15670
15731
|
}
|
|
15671
15732
|
}
|
|
15672
15733
|
});
|
|
@@ -15683,7 +15744,7 @@ async function assertRobinhoodChain(client) {
|
|
|
15683
15744
|
function redact(text3, secret2) {
|
|
15684
15745
|
return secret2 === "" ? text3 : text3.split(secret2).join("<rpc>");
|
|
15685
15746
|
}
|
|
15686
|
-
var robinhoodChain, IDEMPOTENT_READS, INVALID_PARAMS_RETRIES, INVALID_PARAMS_WAIT_MS;
|
|
15747
|
+
var robinhoodChain, IDEMPOTENT_READS, INVALID_PARAMS_RETRIES, INVALID_PARAMS_WAIT_MS, RPC_RETRIES, RPC_RETRY_WAIT_MS, RPC_TIMEOUT_MS, RETRY_AFTER_MAX_MS;
|
|
15687
15748
|
var init_chain = __esm({
|
|
15688
15749
|
"../sdk/src/chain.ts"() {
|
|
15689
15750
|
"use strict";
|
|
@@ -15700,6 +15761,10 @@ var init_chain = __esm({
|
|
|
15700
15761
|
IDEMPOTENT_READS = /* @__PURE__ */ new Set(["eth_call", "eth_getCode", "eth_getStorageAt", "eth_getBalance", "eth_getBlockByNumber", "eth_getBlockByHash", "eth_blockNumber", "eth_chainId", "eth_getTransactionReceipt", "eth_getTransactionByHash", "eth_getTransactionCount", "eth_gasPrice"]);
|
|
15701
15762
|
INVALID_PARAMS_RETRIES = 4;
|
|
15702
15763
|
INVALID_PARAMS_WAIT_MS = 1e3;
|
|
15764
|
+
RPC_RETRIES = 8;
|
|
15765
|
+
RPC_RETRY_WAIT_MS = 1e3;
|
|
15766
|
+
RPC_TIMEOUT_MS = 3e4;
|
|
15767
|
+
RETRY_AFTER_MAX_MS = 1e4;
|
|
15703
15768
|
}
|
|
15704
15769
|
});
|
|
15705
15770
|
|
|
@@ -19135,7 +19200,7 @@ var init_relayer = __esm({
|
|
|
19135
19200
|
init_messages();
|
|
19136
19201
|
init_external_abi();
|
|
19137
19202
|
RELAYER_MARGIN_BPS = 1000n;
|
|
19138
|
-
RELAYER_QUOTE_TOLERANCE_BPS =
|
|
19203
|
+
RELAYER_QUOTE_TOLERANCE_BPS = 1000n;
|
|
19139
19204
|
ETH_USD_DEVIATION_BPS = 50n;
|
|
19140
19205
|
ETH_USD_MAX_AGE_SECONDS = 90000n;
|
|
19141
19206
|
MAX_RELAY_CALLS = 8;
|
|
@@ -19873,6 +19938,7 @@ var init_browser = __esm({
|
|
|
19873
19938
|
"use strict";
|
|
19874
19939
|
init_define_OSTRA_PACKAGED();
|
|
19875
19940
|
init_abi();
|
|
19941
|
+
init_agent_card();
|
|
19876
19942
|
init_aggregator();
|
|
19877
19943
|
init_airdrop();
|
|
19878
19944
|
init_backup();
|
|
@@ -20406,9 +20472,11 @@ var init_relay = __esm({
|
|
|
20406
20472
|
}
|
|
20407
20473
|
};
|
|
20408
20474
|
PendingSpendUnpaidError = class extends Error {
|
|
20409
|
-
|
|
20475
|
+
onCredit;
|
|
20476
|
+
constructor(message, onCredit) {
|
|
20410
20477
|
super(message);
|
|
20411
20478
|
this.name = "PendingSpendUnpaidError";
|
|
20479
|
+
this.onCredit = onCredit;
|
|
20412
20480
|
}
|
|
20413
20481
|
};
|
|
20414
20482
|
RelayerError = class extends Error {
|
|
@@ -20705,6 +20773,7 @@ var init_relay = __esm({
|
|
|
20705
20773
|
async #post(calls, extra, headers = {}) {
|
|
20706
20774
|
const body2 = { calls: calls.map((c) => ({ to: getAddress17(c.to), data: c.data })) };
|
|
20707
20775
|
if (extra.prepay === true) body2.prepay = true;
|
|
20776
|
+
if (extra.onCredit === true) body2.onCredit = true;
|
|
20708
20777
|
if (extra.payer !== null) body2.payer = extra.payer;
|
|
20709
20778
|
if (extra.allowFailedSwaps === true) body2.allowFailedSwaps = true;
|
|
20710
20779
|
return this.#http("POST", "/v1/relay", JSON.stringify(body2), headers);
|
|
@@ -20899,9 +20968,11 @@ var init_relay = __esm({
|
|
|
20899
20968
|
}
|
|
20900
20969
|
async send(calls, options = {}) {
|
|
20901
20970
|
await this.facts();
|
|
20971
|
+
const credit = options.onCredit === true;
|
|
20972
|
+
const post2 = async () => this.#post(calls, { prepay: options.prepay === true, payer: credit ? null : await this.#payer(), allowFailedSwaps: options.allowFailedSwaps === true, onCredit: credit });
|
|
20902
20973
|
let notSent = "";
|
|
20903
20974
|
for (let resend = 0; resend < MAX_RESENDS; resend++) {
|
|
20904
|
-
let answer = await
|
|
20975
|
+
let answer = await post2();
|
|
20905
20976
|
let again = false;
|
|
20906
20977
|
for (let step2 = 0; step2 < MAX_STEPS && !again; step2++) {
|
|
20907
20978
|
const b = answer.body;
|
|
@@ -20911,14 +20982,15 @@ var init_relay = __esm({
|
|
|
20911
20982
|
await this.#landed(hash);
|
|
20912
20983
|
return hash;
|
|
20913
20984
|
}
|
|
20985
|
+
if (answer.status === 402 && credit) throw new RelayerError(402, "no_credit", "the relayer does not send this spend on credit (it asks for payment); nothing was sent");
|
|
20914
20986
|
if (answer.status === 402) {
|
|
20915
20987
|
answer = await this.#pay(calls, answer, options);
|
|
20916
20988
|
continue;
|
|
20917
20989
|
}
|
|
20918
20990
|
if (answer.status === 403 && (error === "vault_in_debt" || error === "open_unpaid")) {
|
|
20919
|
-
if (options.reservation !== void 0 && options.reservation !== null) throw new RelayerError(403, error, String(b?.message ?? error));
|
|
20991
|
+
if (credit || options.reservation !== void 0 && options.reservation !== null) throw new RelayerError(403, error, String(b?.message ?? error));
|
|
20920
20992
|
await this.#ensureChannel(1n, options.openChannel, null);
|
|
20921
|
-
answer = await
|
|
20993
|
+
answer = await post2();
|
|
20922
20994
|
continue;
|
|
20923
20995
|
}
|
|
20924
20996
|
if (answer.status === 409 && error === "not_sent") {
|
|
@@ -21463,10 +21535,14 @@ var init_vault = __esm({
|
|
|
21463
21535
|
* Runs `fn` with what a vault spend needs to be paid. With the relayer: a
|
|
21464
21536
|
* reservation of unused relayer channel deposit for its price (a channel is
|
|
21465
21537
|
* opened first if none has room), released once `fn` is done. When a spend
|
|
21466
|
-
* signed earlier waits and no relayer channel can pay for it
|
|
21467
|
-
*
|
|
21468
|
-
*
|
|
21469
|
-
*
|
|
21538
|
+
* signed earlier waits and no relayer channel can pay for it (its key is
|
|
21539
|
+
* the one a new channel's open needs), the relayer is asked to send it on
|
|
21540
|
+
* credit, as the vault's debt, and the channel opened next pays that debt
|
|
21541
|
+
* with its first voucher; `fn` then runs as usual and its result names what
|
|
21542
|
+
* was sent first (`sentFirst`). Only when the relayer does not send it so,
|
|
21543
|
+
* the local fee payer sends it if it holds the ETH. `moves` is what the
|
|
21544
|
+
* spend takes out of this vault: a relayer channel opened from the buyer
|
|
21545
|
+
* vault for it leaves that much in the buyer vault.
|
|
21470
21546
|
*/
|
|
21471
21547
|
async #paidSpend(sender, fn, moves = 0n, gas = VAULT_SPEND_GAS, extraWei = 0n) {
|
|
21472
21548
|
await this.#finishRecoveryFirst(sender);
|
|
@@ -21478,10 +21554,30 @@ var init_vault = __esm({
|
|
|
21478
21554
|
reservation = await sender.reserve(gas, sender === this.#sender ? this.#opener() : void 0, this.#paysRelayer() ? moves : 0n, extraWei);
|
|
21479
21555
|
} catch (err) {
|
|
21480
21556
|
if (!(err instanceof PendingSpendUnpaidError)) throw err;
|
|
21557
|
+
let refused = "";
|
|
21558
|
+
const credit = err.onCredit;
|
|
21559
|
+
if (sender.relayed && credit !== void 0) {
|
|
21560
|
+
let sentFirst = null;
|
|
21561
|
+
try {
|
|
21562
|
+
sentFirst = await (credit.owner === this ? credit.unqueued() : credit.queued());
|
|
21563
|
+
} catch (e) {
|
|
21564
|
+
if (!(e instanceof RelayerError)) throw e;
|
|
21565
|
+
refused = `; the relayer did not send it on credit (${e.message})`;
|
|
21566
|
+
}
|
|
21567
|
+
if (refused === "") {
|
|
21568
|
+
const again = await sender.reserve(gas, sender === this.#sender ? this.#opener() : void 0, this.#paysRelayer() ? moves : 0n, extraWei);
|
|
21569
|
+
try {
|
|
21570
|
+
const r = await fn(sender, again);
|
|
21571
|
+
return sentFirst !== null && r !== null && typeof r === "object" ? { ...r, sentFirst } : r;
|
|
21572
|
+
} finally {
|
|
21573
|
+
sender.release(again);
|
|
21574
|
+
}
|
|
21575
|
+
}
|
|
21576
|
+
}
|
|
21481
21577
|
const local = new LocalSender(this.#client, this.#transport, this.#keys.feePayer, this.#keys.escrow);
|
|
21482
21578
|
const need = gas * await this.#client.getGasPrice() * ETH_HEADROOM + extraWei;
|
|
21483
21579
|
if (await ethBalance(this.#client, this.#keys.feePayer.address) < need) {
|
|
21484
|
-
throw new Error(`${err.message}; send at least ${formatEth(need)} ETH to the fee payer ${this.#keys.feePayer.address} and call again: it sends that operation itself`);
|
|
21580
|
+
throw new Error(`${err.message}${refused}; send at least ${formatEth(need)} ETH to the fee payer ${this.#keys.feePayer.address} and call again: it sends that operation itself`);
|
|
21485
21581
|
}
|
|
21486
21582
|
return fn(local, null);
|
|
21487
21583
|
}
|
|
@@ -21491,6 +21587,25 @@ var init_vault = __esm({
|
|
|
21491
21587
|
sender.release(reservation);
|
|
21492
21588
|
}
|
|
21493
21589
|
}
|
|
21590
|
+
/**
|
|
21591
|
+
* Sends the spend signed earlier through the relayer on credit (SendOptions.onCredit): the vault owes its price, which
|
|
21592
|
+
* the first voucher of the channel opened next pays. Returns what was sent, or null when nothing waits any more.
|
|
21593
|
+
*/
|
|
21594
|
+
async #sendPendingOnCredit() {
|
|
21595
|
+
const sender = this.#sender;
|
|
21596
|
+
if (!sender.relayed) return null;
|
|
21597
|
+
const vault = await this.vaultAddress();
|
|
21598
|
+
const v = await this.#read(vault);
|
|
21599
|
+
if (v === null) return null;
|
|
21600
|
+
const credit = {
|
|
21601
|
+
relayed: true,
|
|
21602
|
+
payer: () => sender.payer(),
|
|
21603
|
+
send: (calls, o = {}) => sender.send(calls, { ...o, reservation: null, onCredit: true }),
|
|
21604
|
+
reserve: async () => null,
|
|
21605
|
+
release: () => void 0
|
|
21606
|
+
};
|
|
21607
|
+
return this.#sendPending(vault, v, credit, null);
|
|
21608
|
+
}
|
|
21494
21609
|
/**
|
|
21495
21610
|
* Version 3 vaults: the spend is run first with a throwaway key (sdk simulateVaultSpend) and refused before it is
|
|
21496
21611
|
* signed when it would revert, or land as a failure (a SupportRecovery the other vault refuses). A signed spend that
|
|
@@ -21720,7 +21835,11 @@ the signed channel open is kept; the next open_channel or withdraw call sends it
|
|
|
21720
21835
|
const v = await this.#read(vault);
|
|
21721
21836
|
if (v === null) throw new Error(this.#missing());
|
|
21722
21837
|
if (await this.#pending(v) !== null) {
|
|
21723
|
-
throw new PendingSpendUnpaidError("a vault operation signed earlier waits to be sent and no channel to the relayer has deposit left to pay for it"
|
|
21838
|
+
throw new PendingSpendUnpaidError("a vault operation signed earlier waits to be sent and no channel to the relayer has deposit left to pay for it", {
|
|
21839
|
+
owner: this,
|
|
21840
|
+
queued: () => this.#serial(() => this.#sendPendingOnCredit()),
|
|
21841
|
+
unqueued: () => this.#sendPendingOnCredit()
|
|
21842
|
+
});
|
|
21724
21843
|
}
|
|
21725
21844
|
await this.#notFrozen(vault, "the vault");
|
|
21726
21845
|
const balance = await this.#balance(vault);
|
|
@@ -22292,6 +22411,26 @@ var init_borro2 = __esm({
|
|
|
22292
22411
|
const at = await this.#address(vault, what.toUpperCase());
|
|
22293
22412
|
return { vault: at, ...await readVaultGuard(this.#p.client, at) };
|
|
22294
22413
|
}
|
|
22414
|
+
/**
|
|
22415
|
+
* The lock's status (guard) with the hashes the vault approves for EIP-1271 now (sdk readHashApprovals): of the vault
|
|
22416
|
+
* of `what`, or, given `otherText`, of any Ostra vault (a guardian watching the vault it guards). Read only.
|
|
22417
|
+
*/
|
|
22418
|
+
async lockStatus(what, otherText) {
|
|
22419
|
+
let at;
|
|
22420
|
+
if (otherText === void 0) {
|
|
22421
|
+
const { vault } = this.#vaultOf(what);
|
|
22422
|
+
at = await this.#address(vault, what.toUpperCase());
|
|
22423
|
+
} else {
|
|
22424
|
+
if (!isAddress8(otherText, { strict: false })) throw new RangeError(`${otherText} is not an address`);
|
|
22425
|
+
at = getAddress19(otherText);
|
|
22426
|
+
if (await readVault(this.#p.client, this.#p.config.deployment, at) === null) throw new Error(`${at} is not an Ostra vault`);
|
|
22427
|
+
}
|
|
22428
|
+
const [g, approvals] = await Promise.all([
|
|
22429
|
+
readVaultGuard(this.#p.client, at),
|
|
22430
|
+
readHashApprovals(this.#p.client, this.#p.logsClient ?? this.#p.client, at, BigInt(this.#p.config.deployment.fromBlock))
|
|
22431
|
+
]);
|
|
22432
|
+
return { vault: at, ...g, approvals };
|
|
22433
|
+
}
|
|
22295
22434
|
/**
|
|
22296
22435
|
* Turns the withdrawal lock of the vault of `what` on (a withdrawal address, a delay, a daily limit in the vault's
|
|
22297
22436
|
* token) or changes it; `withdrawal` "off" turns it off (that waits the delay). The withdrawal address must keep
|
|
@@ -22375,11 +22514,27 @@ var init_borro2 = __esm({
|
|
|
22375
22514
|
const names = ["cancel", "revoke", "sweep", "redeem", "withdraw collateral"];
|
|
22376
22515
|
return this.#spend(this.#p.buyer, VAULT_ACTION.GUARD, guardParams(target, call), `guard (${names[call.op] ?? call.op}) of ${target}`);
|
|
22377
22516
|
}
|
|
22378
|
-
/**
|
|
22517
|
+
/**
|
|
22518
|
+
* Approves (or withdraws) `hash` for EIP-1271 on the buyer vault: under the lock an approval waits its delay. For this
|
|
22519
|
+
* package's own digests only (Raeto's login message, OstraBreaker's limits), which it builds and checks itself: an
|
|
22520
|
+
* approved hash makes the vault answer isValidSignature for it, and USDG takes such an answer for a permit and for a
|
|
22521
|
+
* transfer authorization, so a hash from anywhere else could hand the vault's funds out. No tool approves a hash it is
|
|
22522
|
+
* given (withdrawApproval only withdraws).
|
|
22523
|
+
*/
|
|
22379
22524
|
async approveHash(hash, approved) {
|
|
22380
22525
|
if (!/^0x[0-9a-fA-F]{64}$/.test(hash) || /^0x0{64}$/.test(hash)) throw new RangeError("the hash is 32 bytes of 0x hex, not zero");
|
|
22381
22526
|
return this.#spend(this.#p.buyer, VAULT_ACTION.APPROVE_HASH, approveHashParams({ hash, approved }), approved ? "EIP-1271 approval" : "EIP-1271 approval withdrawal");
|
|
22382
22527
|
}
|
|
22528
|
+
/**
|
|
22529
|
+
* Withdraws the EIP-1271 approval of `hash` by the vault of `what` ("USDG", the buyer vault, or a Borro stock's
|
|
22530
|
+
* vault): lands when there is none (nothing to do). Withdrawing is at once, under the lock too.
|
|
22531
|
+
*/
|
|
22532
|
+
async withdrawApproval(what, hash) {
|
|
22533
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(hash) || /^0x0{64}$/.test(hash)) throw new RangeError("the hash is 32 bytes of 0x hex, not zero");
|
|
22534
|
+
const { vault } = this.#vaultOf(what);
|
|
22535
|
+
await this.#address(vault, what.toUpperCase());
|
|
22536
|
+
return this.#spend(vault, VAULT_ACTION.APPROVE_HASH, approveHashParams({ hash, approved: false }), `EIP-1271 approval withdrawal (${what.toUpperCase()} vault)`);
|
|
22537
|
+
}
|
|
22383
22538
|
};
|
|
22384
22539
|
}
|
|
22385
22540
|
});
|
|
@@ -23548,24 +23703,17 @@ function imageUri(value) {
|
|
|
23548
23703
|
return value;
|
|
23549
23704
|
}
|
|
23550
23705
|
function profileFile(p) {
|
|
23551
|
-
const
|
|
23552
|
-
|
|
23553
|
-
const x = link2(p.x, "x");
|
|
23554
|
-
const telegram = link2(p.telegram, "telegram");
|
|
23555
|
-
if (website !== void 0) services.push({ name: "web", endpoint: website });
|
|
23556
|
-
if (x !== void 0) services.push({ name: "X", endpoint: x });
|
|
23557
|
-
if (telegram !== void 0) services.push({ name: "Telegram", endpoint: telegram });
|
|
23558
|
-
services.push({ name: "agentWallet", endpoint: `eip155:${ROBINHOOD_CHAIN_ID}:${p.vault}` });
|
|
23706
|
+
const id = p.agentId === void 0 || p.agentId === null ? null : p.agentId;
|
|
23707
|
+
if (id !== null && !/^(0|[1-9][0-9]{0,29})$/.test(id)) throw new RangeError("an agent id is a decimal number");
|
|
23559
23708
|
const image = imageUri(p.image);
|
|
23560
|
-
const description = text2(p.description, "description", 1e3);
|
|
23561
23709
|
return {
|
|
23562
23710
|
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
23563
|
-
name:
|
|
23564
|
-
|
|
23711
|
+
name: id === null ? AGENT_NAME : `${AGENT_NAME} #${id}`,
|
|
23712
|
+
description: AGENT_DESCRIPTION,
|
|
23565
23713
|
...image === void 0 ? {} : { image },
|
|
23566
|
-
services,
|
|
23714
|
+
services: [{ name: "agentWallet", endpoint: `eip155:${ROBINHOOD_CHAIN_ID}:${p.vault}` }],
|
|
23567
23715
|
active: true,
|
|
23568
|
-
registrations:
|
|
23716
|
+
registrations: id === null ? [] : [{ agentId: Number(id), agentRegistry: `eip155:${ROBINHOOD_CHAIN_ID}:${ERC8004_IDENTITY_REGISTRY}` }]
|
|
23569
23717
|
};
|
|
23570
23718
|
}
|
|
23571
23719
|
function tokenMetadataFile(m) {
|
|
@@ -23604,6 +23752,9 @@ function uploaderUrl(relayerUrl) {
|
|
|
23604
23752
|
function uploadJson(relayerUrl, value) {
|
|
23605
23753
|
return post(`${uploaderUrl(relayerUrl)}/json`, JSON.stringify(value), "application/json");
|
|
23606
23754
|
}
|
|
23755
|
+
function agentCardUri(relayerUrl, agentId) {
|
|
23756
|
+
return post(`${uploaderUrl(relayerUrl)}/agent-card`, JSON.stringify({ agentId }), "application/json");
|
|
23757
|
+
}
|
|
23607
23758
|
function uploadImage(relayerUrl, bytes) {
|
|
23608
23759
|
const base = uploaderUrl(relayerUrl);
|
|
23609
23760
|
if (bytes.length === 0 || bytes.length > MAX_UPLOAD_IMAGE_BYTES) throw new RangeError(`the image must be 1 to ${MAX_UPLOAD_IMAGE_BYTES} bytes (it is ${bytes.length})`);
|
|
@@ -23611,13 +23762,15 @@ function uploadImage(relayerUrl, bytes) {
|
|
|
23611
23762
|
if (type === null) throw new RangeError("the image is not a PNG, JPEG, WebP or GIF");
|
|
23612
23763
|
return post(`${base}/image`, new Blob([new Uint8Array(bytes)], { type }), type);
|
|
23613
23764
|
}
|
|
23614
|
-
var MAX_UPLOAD_IMAGE_BYTES;
|
|
23765
|
+
var MAX_UPLOAD_IMAGE_BYTES, AGENT_NAME, AGENT_DESCRIPTION;
|
|
23615
23766
|
var init_publish = __esm({
|
|
23616
23767
|
"../core/src/publish.ts"() {
|
|
23617
23768
|
"use strict";
|
|
23618
23769
|
init_define_OSTRA_PACKAGED();
|
|
23619
23770
|
init_src();
|
|
23620
23771
|
MAX_UPLOAD_IMAGE_BYTES = 1048576;
|
|
23772
|
+
AGENT_NAME = "Ostra Agent";
|
|
23773
|
+
AGENT_DESCRIPTION = "An AI agent with an Ostra post-quantum privacy vault on Robinhood Chain. Its earnings and payments stay in its own vault, signed with one-time keys.";
|
|
23621
23774
|
}
|
|
23622
23775
|
});
|
|
23623
23776
|
|
|
@@ -23652,7 +23805,7 @@ function simulatedGas(gasUsed) {
|
|
|
23652
23805
|
const g = gasUsed * 5n / 4n + SPEND_GAS_HEADROOM2;
|
|
23653
23806
|
return g > VAULT_SPEND_GAS ? g : VAULT_SPEND_GAS;
|
|
23654
23807
|
}
|
|
23655
|
-
var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM2, DAY, TOKEN_LIST_MAX, METADATA_MAX_BYTES, launchedEvent, POOL_SETTING_NAMES, WOTS_GAS_SPREAD, STAND_IN_URI, LaunchpadService;
|
|
23808
|
+
var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM2, DAY, LOGS_CATCH_UP_SECONDS, TOKEN_LIST_MAX, METADATA_MAX_BYTES, launchedEvent, POOL_SETTING_NAMES, WOTS_GAS_SPREAD, STAND_IN_URI, LaunchpadService;
|
|
23656
23809
|
var init_launchpad = __esm({
|
|
23657
23810
|
"../core/src/launchpad.ts"() {
|
|
23658
23811
|
"use strict";
|
|
@@ -23673,6 +23826,7 @@ var init_launchpad = __esm({
|
|
|
23673
23826
|
PAIRS_READ_CONCURRENCY = 6;
|
|
23674
23827
|
SPEND_GAS_HEADROOM2 = 100000n;
|
|
23675
23828
|
DAY = 86400;
|
|
23829
|
+
LOGS_CATCH_UP_SECONDS = 30;
|
|
23676
23830
|
TOKEN_LIST_MAX = 50;
|
|
23677
23831
|
METADATA_MAX_BYTES = 65536;
|
|
23678
23832
|
launchedEvent = parseAbiItem5("event Launched(bytes32 indexed poolId, address indexed token, address indexed creatorVault, address quote)");
|
|
@@ -23692,6 +23846,7 @@ var init_launchpad = __esm({
|
|
|
23692
23846
|
STAND_IN_URI = `ipfs://bafkrei${"a".repeat(52)}`;
|
|
23693
23847
|
LaunchpadService = class {
|
|
23694
23848
|
#client;
|
|
23849
|
+
#logsClient;
|
|
23695
23850
|
#transport;
|
|
23696
23851
|
#config;
|
|
23697
23852
|
#keys;
|
|
@@ -23706,6 +23861,7 @@ var init_launchpad = __esm({
|
|
|
23706
23861
|
constructor(p) {
|
|
23707
23862
|
if (p.creator.kind !== "creator" || p.buyer.kind !== "buyer") throw new Error("the launchpad takes the agent's buyer and creator vaults");
|
|
23708
23863
|
this.#client = p.client;
|
|
23864
|
+
this.#logsClient = p.logsClient ?? p.client;
|
|
23709
23865
|
this.#transport = p.transport;
|
|
23710
23866
|
this.#config = p.config;
|
|
23711
23867
|
this.#keys = p.keys;
|
|
@@ -24494,6 +24650,71 @@ var init_launchpad = __esm({
|
|
|
24494
24650
|
const text3 = await this.#folder.read("agent.json");
|
|
24495
24651
|
return text3 === void 0 ? null : JSON.parse(text3);
|
|
24496
24652
|
}
|
|
24653
|
+
/**
|
|
24654
|
+
* The agent's registration read from the chain when agent.json does not hold it: an identity its creator vault
|
|
24655
|
+
* registered (the Identity Registry's Registered events from the deployment's first block) and still owns. A
|
|
24656
|
+
* registration that landed after its call stopped (the relayer refused the send and a later operation sent the kept
|
|
24657
|
+
* spend, or the process stopped before agent.json was written) is found here and written to agent.json, so the agent
|
|
24658
|
+
* is never registered twice. An identity someone else registered and sent to the creator vault (an ERC-721 transfer
|
|
24659
|
+
* the vault accepts) is no registration of this agent: with no Registered event of the vault, this is null. A
|
|
24660
|
+
* registration sent again just now is read from its receipt instead (#recordFromReceipt), not from logs that may lag.
|
|
24661
|
+
* The vault's identities are counted at the chain's head and the events read up to that same block, after the logs
|
|
24662
|
+
* RPC has reached it: a registration the count holds is never missed because the logs RPC was behind (which would
|
|
24663
|
+
* register the agent twice); when the logs RPC does not reach that block soon, this throws before anything is signed.
|
|
24664
|
+
*/
|
|
24665
|
+
async #recoverAgentRecord() {
|
|
24666
|
+
const vault = getAddress24(await this.#creator.vaultAddress());
|
|
24667
|
+
const head = await this.#client.getBlockNumber({ cacheTime: 0 });
|
|
24668
|
+
const owned = await this.#client.readContract({ address: ERC8004_IDENTITY_REGISTRY, abi: identityRegistryAbi, functionName: "balanceOf", args: [vault], blockNumber: head });
|
|
24669
|
+
if (owned === 0n) return null;
|
|
24670
|
+
for (let waited = 0; await this.#logsClient.getBlockNumber({ cacheTime: 0 }) < head; waited++) {
|
|
24671
|
+
if (waited >= LOGS_CATCH_UP_SECONDS) throw new Error(`the logs RPC has not reached block ${head} of the chain RPC after ${LOGS_CATCH_UP_SECONDS} seconds, so the creator vault's ERC-8004 registration cannot be read yet: call again shortly; nothing was signed`);
|
|
24672
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
24673
|
+
}
|
|
24674
|
+
const logs = await getLogsChunked(this.#logsClient, { address: ERC8004_IDENTITY_REGISTRY, event: agentRegisteredEvent, args: { owner: vault }, fromBlock: BigInt(this.#config.deployment.fromBlock), toBlock: head });
|
|
24675
|
+
for (const log2 of logs) {
|
|
24676
|
+
const { agentId, agentURI } = log2.args;
|
|
24677
|
+
const who = await readAgentIdentity(this.#client, agentId);
|
|
24678
|
+
if (who === null || getAddress24(who.owner) !== vault) continue;
|
|
24679
|
+
const record = { agentId: agentId.toString(), uri: agentURI, txHash: log2.transactionHash, at: Math.floor(Date.now() / 1e3) };
|
|
24680
|
+
await this.#folder.write("agent.json", JSON.stringify(record, null, 2));
|
|
24681
|
+
return record;
|
|
24682
|
+
}
|
|
24683
|
+
return null;
|
|
24684
|
+
}
|
|
24685
|
+
/**
|
|
24686
|
+
* The registration transaction `txHash` of the creator vault, read from its receipt (the vault's AgentRegistered
|
|
24687
|
+
* event) and written to agent.json; null when it registered nothing. The file it registered with is the Identity
|
|
24688
|
+
* Registry's Registered event's agentURI in that receipt (a registration signed by an earlier call carries that
|
|
24689
|
+
* call's file); `uri` only when the receipt has no such event.
|
|
24690
|
+
*/
|
|
24691
|
+
async #recordFromReceipt(txHash, uri2) {
|
|
24692
|
+
const vault = getAddress24(await this.#creator.vaultAddress());
|
|
24693
|
+
const receipt = await this.#client.getTransactionReceipt({ hash: txHash });
|
|
24694
|
+
let agentId;
|
|
24695
|
+
for (const log2 of receipt.logs) {
|
|
24696
|
+
if (getAddress24(log2.address) !== vault) continue;
|
|
24697
|
+
try {
|
|
24698
|
+
const e = decodeEventLog3({ abi: ostraVaultAbi, data: log2.data, topics: log2.topics });
|
|
24699
|
+
if (e.eventName === "AgentRegistered") agentId = e.args.agentId.toString();
|
|
24700
|
+
} catch {
|
|
24701
|
+
}
|
|
24702
|
+
}
|
|
24703
|
+
if (agentId === void 0) return null;
|
|
24704
|
+
let registered;
|
|
24705
|
+
for (const log2 of receipt.logs) {
|
|
24706
|
+
if (getAddress24(log2.address) !== getAddress24(ERC8004_IDENTITY_REGISTRY)) continue;
|
|
24707
|
+
try {
|
|
24708
|
+
const e = decodeEventLog3({ abi: [agentRegisteredEvent], data: log2.data, topics: log2.topics });
|
|
24709
|
+
const a = e.args;
|
|
24710
|
+
if (a.agentId.toString() === agentId && getAddress24(a.owner) === vault) registered = a.agentURI;
|
|
24711
|
+
} catch {
|
|
24712
|
+
}
|
|
24713
|
+
}
|
|
24714
|
+
const record = { agentId, uri: registered ?? uri2, txHash, at: Math.floor(Date.now() / 1e3) };
|
|
24715
|
+
await this.#folder.write("agent.json", JSON.stringify(record, null, 2));
|
|
24716
|
+
return record;
|
|
24717
|
+
}
|
|
24497
24718
|
/** The agent's identity: its creator vault, the profile URI it set and its ERC-8004 agent id; or, given `account`, that account's profile URI. */
|
|
24498
24719
|
async profile(accountText) {
|
|
24499
24720
|
const account = accountText === void 0 ? await this.#creator.vaultAddress() : address2(accountText, "account");
|
|
@@ -24510,32 +24731,21 @@ var init_launchpad = __esm({
|
|
|
24510
24731
|
/** Registers the creator vault as an agent in the ERC-8004 Identity Registry, its registration file at `uri`; the vault owns the identity and is its agentWallet. */
|
|
24511
24732
|
async registerAgent(uri2) {
|
|
24512
24733
|
checkUri(uri2);
|
|
24513
|
-
|
|
24514
|
-
const existing = await this.#agentRecord();
|
|
24515
|
-
if (existing !== null) throw new Error(`this agent is registered already as agent ${existing.agentId}:
|
|
24734
|
+
await this.#identityVault();
|
|
24735
|
+
const existing = await this.#agentRecord() ?? await this.#recoverAgentRecord();
|
|
24736
|
+
if (existing !== null) throw new Error(`this agent is registered already as agent ${existing.agentId}: publish_profile publishes its profile again; nothing was signed`);
|
|
24516
24737
|
const r = await this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.REGISTER_AGENT, registerAgentParams(uri2), "agent registration");
|
|
24517
24738
|
if (r.status !== "sent") return r;
|
|
24518
|
-
const
|
|
24519
|
-
|
|
24520
|
-
|
|
24521
|
-
if (getAddress24(log2.address) !== vault) continue;
|
|
24522
|
-
try {
|
|
24523
|
-
const e = decodeEventLog3({ abi: ostraVaultAbi, data: log2.data, topics: log2.topics });
|
|
24524
|
-
if (e.eventName === "AgentRegistered") agentId = e.args.agentId.toString();
|
|
24525
|
-
} catch {
|
|
24526
|
-
}
|
|
24527
|
-
}
|
|
24528
|
-
if (agentId === void 0) throw new Error(`the registration ${r.txHash} landed without an AgentRegistered event`);
|
|
24529
|
-
const record = { agentId, uri: uri2, txHash: r.txHash, at: Math.floor(Date.now() / 1e3) };
|
|
24530
|
-
await this.#folder.write("agent.json", JSON.stringify(record, null, 2));
|
|
24531
|
-
return { ...r, agentId };
|
|
24739
|
+
const record = await this.#recordFromReceipt(r.txHash, uri2);
|
|
24740
|
+
if (record === null) throw new Error(`the registration ${r.txHash} landed without an AgentRegistered event`);
|
|
24741
|
+
return { ...r, agentId: record.agentId };
|
|
24532
24742
|
}
|
|
24533
24743
|
/** Points the agent's ERC-8004 identity at a new registration file. */
|
|
24534
24744
|
async setAgentUri(uri2) {
|
|
24535
24745
|
checkUri(uri2);
|
|
24536
24746
|
await this.#identityVault();
|
|
24537
24747
|
const record = await this.#agentRecord();
|
|
24538
|
-
if (record === null) throw new Error("this agent has no ERC-8004 identity yet:
|
|
24748
|
+
if (record === null) throw new Error("this agent has no ERC-8004 identity yet: publish_profile registers it; nothing was signed");
|
|
24539
24749
|
const r = await this.#simulatedSpend(this.#creator, this.#creatorSender, VAULT_ACTION.SET_AGENT_URI, setAgentUriParams({ agentId: BigInt(record.agentId), uri: uri2 }), "agent registration file");
|
|
24540
24750
|
if (r.status === "sent") await this.#folder.write("agent.json", JSON.stringify({ ...record, uri: uri2, at: Math.floor(Date.now() / 1e3) }, null, 2));
|
|
24541
24751
|
return r;
|
|
@@ -24561,15 +24771,39 @@ var init_launchpad = __esm({
|
|
|
24561
24771
|
if (version < VAULT_VERSION) throw new Error(`the creator vault ${s.vault} runs version ${version}, not ${VAULT_VERSION}; nothing was signed`);
|
|
24562
24772
|
}
|
|
24563
24773
|
/**
|
|
24564
|
-
* Publishes the agent's profile
|
|
24565
|
-
*
|
|
24566
|
-
*
|
|
24774
|
+
* Publishes the agent's Ostra profile, the same for every Ostra agent (profileFile: its name and number, Ostra's
|
|
24775
|
+
* description, its Ostra card, its creator vault as agentWallet), and registers the agent in ERC-8004 when it is not
|
|
24776
|
+
* registered yet: the profile without a number is published, the creator vault registers the agent with it, and the
|
|
24777
|
+
* profile is published again with the agent's number and card, its ERC-8004 identity pointed at it. Called again, it
|
|
24778
|
+
* carries on from where it stopped (what the chain and agent.json hold decides).
|
|
24567
24779
|
*/
|
|
24568
|
-
async publishProfile(
|
|
24569
|
-
const
|
|
24780
|
+
async publishProfile() {
|
|
24781
|
+
const before = await this.#agentRecord() ?? await this.#recoverAgentRecord();
|
|
24782
|
+
if (before !== null) return { agentId: before.agentId, registration: null, unnumbered: null, ...await this.#publishProfileFile(before) };
|
|
24783
|
+
const first = await this.#publishProfileFile(null);
|
|
24784
|
+
const unnumbered = { uri: first.uri, profile: first.profile };
|
|
24785
|
+
if (first.profile.status !== "sent") {
|
|
24786
|
+
const resent = first.profile.resent;
|
|
24787
|
+
const landed = resent.operation === "vault_spend" && resent.action === VAULT_ACTION.REGISTER_AGENT ? await this.#recordFromReceipt(resent.txHash, first.uri) : null;
|
|
24788
|
+
if (landed !== null) return { agentId: landed.agentId, registration: null, unnumbered, ...await this.#publishProfileFile(landed) };
|
|
24789
|
+
const after = await this.#agentRecord() ?? await this.#recoverAgentRecord();
|
|
24790
|
+
if (after !== null) return { agentId: after.agentId, registration: null, unnumbered, ...await this.#publishProfileFile(after) };
|
|
24791
|
+
}
|
|
24792
|
+
const registration = await this.registerAgent(first.uri);
|
|
24570
24793
|
const record = await this.#agentRecord();
|
|
24571
|
-
|
|
24794
|
+
if (registration.status !== "sent" || record === null) return { agentId: null, registration, unnumbered, ...first };
|
|
24795
|
+
return { agentId: record.agentId, registration, unnumbered, ...await this.#publishProfileFile(record) };
|
|
24796
|
+
}
|
|
24797
|
+
/**
|
|
24798
|
+
* Builds the profile of the agent (`record`: its registration, or null before it has one), pins it through the
|
|
24799
|
+
* uploader, sets it in OstraRegistry (for the creator vaults of the other pairs it launched from too), and points the
|
|
24800
|
+
* ERC-8004 identity at it when the agent is registered.
|
|
24801
|
+
*/
|
|
24802
|
+
async #publishProfileFile(record) {
|
|
24803
|
+
const vault = await this.#identityVault();
|
|
24804
|
+
const agentId = record?.agentId ?? null;
|
|
24572
24805
|
uploaderUrl(this.#config.relayerUrl);
|
|
24806
|
+
const file = profileFile({ vault, agentId, image: await agentCardUri(this.#config.relayerUrl, agentId) });
|
|
24573
24807
|
const beforeUri = await this.#profileUri(vault);
|
|
24574
24808
|
const before = await readIpfsJson(beforeUri);
|
|
24575
24809
|
if (beforeUri !== null && beforeUri.startsWith("ipfs://") && before === null) throw new Error(`the profile published before (${beforeUri}) cannot be read now, so its Ostrapad listing could not be kept: try again later; nothing was signed`);
|
|
@@ -24927,7 +25161,7 @@ var init_pad2 = __esm({
|
|
|
24927
25161
|
publish(offers) {
|
|
24928
25162
|
return this.#serial(async () => {
|
|
24929
25163
|
const { agentId, creatorVault } = await this.#p.identity();
|
|
24930
|
-
if (agentId === null) throw new Error("this agent is not registered in ERC-8004 yet: publish_profile
|
|
25164
|
+
if (agentId === null) throw new Error("this agent is not registered in ERC-8004 yet: publish_profile registers it; nothing was signed");
|
|
24931
25165
|
const vault = await this.#vault();
|
|
24932
25166
|
const current = await this.#p.currentFile();
|
|
24933
25167
|
if (current === null) throw new Error("this agent has no published profile yet: publish_profile first; nothing was signed");
|
|
@@ -25843,7 +26077,7 @@ var init_raeto = __esm({
|
|
|
25843
26077
|
await this.#keepApprovals((await this.#keptApprovals()).filter((h) => h !== hash));
|
|
25844
26078
|
return { done: true, note };
|
|
25845
26079
|
}
|
|
25846
|
-
return { done: false, note: `${note}; the approval may still read or land: call raeto login again to withdraw it, or
|
|
26080
|
+
return { done: false, note: `${note}; the approval may still read or land: call raeto login again to withdraw it, or vault_revoke_hash ${hash}` };
|
|
25847
26081
|
}
|
|
25848
26082
|
/**
|
|
25849
26083
|
* Signs the vault in to Raeto as an agent (see the file comment): its own Raeto account and API key, kept here. With a
|
|
@@ -26024,10 +26258,10 @@ var init_raeto = __esm({
|
|
|
26024
26258
|
}
|
|
26025
26259
|
});
|
|
26026
26260
|
|
|
26027
|
-
//
|
|
26261
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/abi.ts
|
|
26028
26262
|
var ostraRouterAbi, ostraStakeAbi, ostraPrivatePoolAbi, OSTRA_SWAP_ESCROW_CREATION_CODE, OSTRA_SHIELD_ESCROW_CREATION_CODE, ostraQuoteBatchAbi, OSTRA_QUOTE_BATCH_CREATION_CODE;
|
|
26029
26263
|
var init_abi2 = __esm({
|
|
26030
|
-
"
|
|
26264
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/abi.ts"() {
|
|
26031
26265
|
"use strict";
|
|
26032
26266
|
init_define_OSTRA_PACKAGED();
|
|
26033
26267
|
ostraRouterAbi = [
|
|
@@ -28892,10 +29126,10 @@ var init_abi2 = __esm({
|
|
|
28892
29126
|
}
|
|
28893
29127
|
});
|
|
28894
29128
|
|
|
28895
|
-
//
|
|
29129
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/constants.ts
|
|
28896
29130
|
var LOG_WINDOW_BLOCKS2, USDG2, WETH2, HUBS, ETH, UNISWAP_V3_FACTORY, UNISWAP_V3_QUOTER_V2, V3_FEE_TIERS, UNISWAP_V4_POOL_MANAGER2, UNISWAP_V4_QUOTER, MULTICALL32, OSTRA_HOOK, PONS_HOOK2, ROUTABLE_HOOKS, FEE_TIERS, COMMAND, V4_ACTION, CONTRACT_BALANCE, MSG_SENDER, ADDRESS_THIS, OPEN_DELTA, QUOTE_GAS_LIMIT, QUOTES_PER_CALL, QUOTE_GAS_LIMIT_RETRY, QUOTES_PER_RETRY_CALL, QUOTE_BATCH_OVERHEAD_GAS, V3_MIN_SQRT_PRICE_LIMIT, V3_MAX_SQRT_PRICE_LIMIT, DEFAULT_SLIPPAGE_BPS, MAX_SLIPPAGE_BPS, DEFAULT_ORDER_TTL_SECONDS;
|
|
28897
29131
|
var init_constants2 = __esm({
|
|
28898
|
-
"
|
|
29132
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/constants.ts"() {
|
|
28899
29133
|
"use strict";
|
|
28900
29134
|
init_define_OSTRA_PACKAGED();
|
|
28901
29135
|
LOG_WINDOW_BLOCKS2 = 10000000n;
|
|
@@ -28937,11 +29171,11 @@ var init_constants2 = __esm({
|
|
|
28937
29171
|
}
|
|
28938
29172
|
});
|
|
28939
29173
|
|
|
28940
|
-
//
|
|
29174
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/uniswap.ts
|
|
28941
29175
|
import { parseAbi as parseAbi7, parseAbiItem as parseAbiItem6 } from "viem";
|
|
28942
29176
|
var v3FactoryAbi, v3PoolAbi, quoterV2Abi, poolKeyComponents, v4QuoterAbi, exactInputSingleParam, poolManagerAbi, initializeEvent, erc20Abi, multicall3Abi2;
|
|
28943
29177
|
var init_uniswap = __esm({
|
|
28944
|
-
"
|
|
29178
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/uniswap.ts"() {
|
|
28945
29179
|
"use strict";
|
|
28946
29180
|
init_define_OSTRA_PACKAGED();
|
|
28947
29181
|
v3FactoryAbi = parseAbi7(["function getPool(address tokenA, address tokenB, uint24 fee) view returns (address)"]);
|
|
@@ -29033,7 +29267,7 @@ var init_uniswap = __esm({
|
|
|
29033
29267
|
}
|
|
29034
29268
|
});
|
|
29035
29269
|
|
|
29036
|
-
//
|
|
29270
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/legs.ts
|
|
29037
29271
|
import { concatHex, encodeAbiParameters as encodeAbiParameters10, encodePacked as encodePacked4, getAddress as getAddress28, toHex as toHex5 } from "viem";
|
|
29038
29272
|
function legFor(pool, tokenIn, tokenOut, recipient = MSG_SENDER) {
|
|
29039
29273
|
const inAddr = getAddress28(tokenIn);
|
|
@@ -29070,7 +29304,7 @@ function legFor(pool, tokenIn, tokenOut, recipient = MSG_SENDER) {
|
|
|
29070
29304
|
}
|
|
29071
29305
|
var NO_LEG, byte;
|
|
29072
29306
|
var init_legs = __esm({
|
|
29073
|
-
"
|
|
29307
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/legs.ts"() {
|
|
29074
29308
|
"use strict";
|
|
29075
29309
|
init_define_OSTRA_PACKAGED();
|
|
29076
29310
|
init_constants2();
|
|
@@ -29080,7 +29314,7 @@ var init_legs = __esm({
|
|
|
29080
29314
|
}
|
|
29081
29315
|
});
|
|
29082
29316
|
|
|
29083
|
-
//
|
|
29317
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/pools.ts
|
|
29084
29318
|
import { encodeAbiParameters as encodeAbiParameters11, encodeFunctionData as encodeFunctionData12, decodeFunctionResult as decodeFunctionResult3, getAddress as getAddress29, hexToBigInt as hexToBigInt2, keccak256 as keccak25617, toHex as toHex6, zeroAddress as zeroAddress16 } from "viem";
|
|
29085
29319
|
function stateSlot(id, offset) {
|
|
29086
29320
|
const base = hexToBigInt2(keccak25617(encodeAbiParameters11([{ type: "bytes32" }, { type: "uint256" }], [id, POOLS_SLOT3])));
|
|
@@ -29143,7 +29377,7 @@ async function hubPools(client, logsClient, index, token, hub) {
|
|
|
29143
29377
|
}
|
|
29144
29378
|
var POOLS_SLOT3, LIQUIDITY_OFFSET2, PoolIndex;
|
|
29145
29379
|
var init_pools2 = __esm({
|
|
29146
|
-
"
|
|
29380
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/pools.ts"() {
|
|
29147
29381
|
"use strict";
|
|
29148
29382
|
init_define_OSTRA_PACKAGED();
|
|
29149
29383
|
init_constants2();
|
|
@@ -29189,7 +29423,7 @@ var init_pools2 = __esm({
|
|
|
29189
29423
|
}
|
|
29190
29424
|
});
|
|
29191
29425
|
|
|
29192
|
-
//
|
|
29426
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/route.ts
|
|
29193
29427
|
import { decodeAbiParameters as decodeAbiParameters5, decodeFunctionResult as decodeFunctionResult4, encodeDeployData as encodeDeployData2, encodeFunctionData as encodeFunctionData13, getAddress as getAddress30 } from "viem";
|
|
29194
29428
|
function routedToken(token) {
|
|
29195
29429
|
const t = getAddress30(token);
|
|
@@ -29313,7 +29547,7 @@ function routeLegs(route) {
|
|
|
29313
29547
|
}
|
|
29314
29548
|
var NoRouteError, quoteResults;
|
|
29315
29549
|
var init_route = __esm({
|
|
29316
|
-
"
|
|
29550
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/route.ts"() {
|
|
29317
29551
|
"use strict";
|
|
29318
29552
|
init_define_OSTRA_PACKAGED();
|
|
29319
29553
|
init_abi2();
|
|
@@ -29339,7 +29573,7 @@ var init_route = __esm({
|
|
|
29339
29573
|
}
|
|
29340
29574
|
});
|
|
29341
29575
|
|
|
29342
|
-
//
|
|
29576
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/orders.ts
|
|
29343
29577
|
import { encodeAbiParameters as encodeAbiParameters12, encodeFunctionData as encodeFunctionData14, getAddress as getAddress31, getContractAddress as getContractAddress4, isAddress as isAddress14, keccak256 as keccak25618, toHex as toHex7 } from "viem";
|
|
29344
29578
|
function parseSwapDeployment(json) {
|
|
29345
29579
|
const r = json;
|
|
@@ -29393,7 +29627,7 @@ function executeEscrowData(order, route) {
|
|
|
29393
29627
|
}
|
|
29394
29628
|
var ZERO, MAX_UINT32, MAX_UINT64, MAX_STANDING_ORDER_SECONDS, legParam, orderParam;
|
|
29395
29629
|
var init_orders = __esm({
|
|
29396
|
-
"
|
|
29630
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/orders.ts"() {
|
|
29397
29631
|
"use strict";
|
|
29398
29632
|
init_define_OSTRA_PACKAGED();
|
|
29399
29633
|
init_abi2();
|
|
@@ -29432,8 +29666,8 @@ var init_orders = __esm({
|
|
|
29432
29666
|
}
|
|
29433
29667
|
});
|
|
29434
29668
|
|
|
29435
|
-
//
|
|
29436
|
-
import { encodeAbiParameters as encodeAbiParameters13, encodeFunctionData as encodeFunctionData15, getAbiItem, getAddress as getAddress32, getContractAddress as getContractAddress5, keccak256 as keccak25619, parseEventLogs, toHex as toHex8 } from "viem";
|
|
29669
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/private.ts
|
|
29670
|
+
import { encodeAbiParameters as encodeAbiParameters13, encodeFunctionData as encodeFunctionData15, getAbiItem as getAbiItem2, getAddress as getAddress32, getContractAddress as getContractAddress5, keccak256 as keccak25619, parseEventLogs, toHex as toHex8 } from "viem";
|
|
29437
29671
|
function packDigest(elements) {
|
|
29438
29672
|
if (elements.length !== 8) throw new Error("a digest has 8 field elements");
|
|
29439
29673
|
let v = 0n;
|
|
@@ -29632,7 +29866,7 @@ async function swapOutputNote(prover, sk, outRho, spend, swap, receipt, pool) {
|
|
|
29632
29866
|
}
|
|
29633
29867
|
var FIELD_P, TREE_DEPTH, PRIVATE_SWAP_FEE_BPS, UNSHIELD_DOMAIN, SWAP_DOMAIN, MAX_AMOUNT, MAX_UINT642, ZERO2, noteAddedEvent, PoolTrees;
|
|
29634
29868
|
var init_private = __esm({
|
|
29635
|
-
"
|
|
29869
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/private.ts"() {
|
|
29636
29870
|
"use strict";
|
|
29637
29871
|
init_define_OSTRA_PACKAGED();
|
|
29638
29872
|
init_abi2();
|
|
@@ -29647,7 +29881,7 @@ var init_private = __esm({
|
|
|
29647
29881
|
MAX_AMOUNT = 1n << 128n;
|
|
29648
29882
|
MAX_UINT642 = (1n << 64n) - 1n;
|
|
29649
29883
|
ZERO2 = "0x0000000000000000000000000000000000000000";
|
|
29650
|
-
noteAddedEvent =
|
|
29884
|
+
noteAddedEvent = getAbiItem2({ abi: ostraPrivatePoolAbi, name: "NoteAdded" });
|
|
29651
29885
|
PoolTrees = class {
|
|
29652
29886
|
#data;
|
|
29653
29887
|
/** The sync running now: syncs run one at a time (each one's rollback assumes no other changes the trees). */
|
|
@@ -29748,19 +29982,19 @@ var init_private = __esm({
|
|
|
29748
29982
|
}
|
|
29749
29983
|
});
|
|
29750
29984
|
|
|
29751
|
-
//
|
|
29985
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/reads.ts
|
|
29752
29986
|
import { getAddress as getAddress33 } from "viem";
|
|
29753
29987
|
var init_reads2 = __esm({
|
|
29754
|
-
"
|
|
29988
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/reads.ts"() {
|
|
29755
29989
|
"use strict";
|
|
29756
29990
|
init_define_OSTRA_PACKAGED();
|
|
29757
29991
|
init_abi2();
|
|
29758
29992
|
}
|
|
29759
29993
|
});
|
|
29760
29994
|
|
|
29761
|
-
//
|
|
29995
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/index.ts
|
|
29762
29996
|
var init_src2 = __esm({
|
|
29763
|
-
"
|
|
29997
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/index.ts"() {
|
|
29764
29998
|
"use strict";
|
|
29765
29999
|
init_define_OSTRA_PACKAGED();
|
|
29766
30000
|
init_abi2();
|
|
@@ -31437,7 +31671,7 @@ var init_strategy2 = __esm({
|
|
|
31437
31671
|
if (who !== null && getAddress37(who.owner) === creatorVault && getAddress37(who.wallet) === creatorVault) return { agentId: id.toString(), creatorVault };
|
|
31438
31672
|
}
|
|
31439
31673
|
throw new StrategyGateError(
|
|
31440
|
-
`Ostra's strategies are for agents registered in the ERC-8004 Identity Registry through Ostra: the creator vault ${creatorVault} must own the agent's identity and be its agentWallet${ids.length === 0 ? " (it has none)" : ` (agent ${ids.map(String).join(", ")} is not)`}. Register with
|
|
31674
|
+
`Ostra's strategies are for agents registered in the ERC-8004 Identity Registry through Ostra: the creator vault ${creatorVault} must own the agent's identity and be its agentWallet${ids.length === 0 ? " (it has none)" : ` (agent ${ids.map(String).join(", ")} is not)`}. Register with publish_profile (it registers the agent), then ask again.`
|
|
31441
31675
|
);
|
|
31442
31676
|
}
|
|
31443
31677
|
// -------------------------------------------------------------------------------------------------------------------
|
|
@@ -32307,7 +32541,7 @@ var init_strategy2 = __esm({
|
|
|
32307
32541
|
});
|
|
32308
32542
|
|
|
32309
32543
|
// ../core/src/swap.ts
|
|
32310
|
-
import { decodeErrorResult as decodeErrorResult2, decodeEventLog as decodeEventLog4, getAbiItem as
|
|
32544
|
+
import { decodeErrorResult as decodeErrorResult2, decodeEventLog as decodeEventLog4, getAbiItem as getAbiItem3, getAddress as getAddress38 } from "viem";
|
|
32311
32545
|
function refusalOf(reason, outDecimals) {
|
|
32312
32546
|
if (reason === "0x") return "the swap reverted without a reason (out of gas, or a revert with no data)";
|
|
32313
32547
|
try {
|
|
@@ -32336,7 +32570,7 @@ function refusalOf(reason, outDecimals) {
|
|
|
32336
32570
|
return `revert data ${reason.slice(0, 2 + 8 * 2)}${reason.length > 18 ? "\u2026" : ""} (not one of OstraRouter's errors: from a pool or the Universal Router)`;
|
|
32337
32571
|
}
|
|
32338
32572
|
}
|
|
32339
|
-
var SWAP_DIR, ESCROW_SWAP_GAS, DEFAULT_SWAP_SLIPPAGE_BPS, MAX_SWAP_SLIPPAGE_BPS, DEFAULT_SWAP_MAX_IMPACT_BPS, MAX_SWAP_MAX_IMPACT_BPS, SPLIT_MAX_PARTS, SPLIT_INTERVAL_SECONDS, SPLIT_SLACK_SECONDS, POOL_INDEX2, ORDER_PREFIX, SPLIT_PREFIX, address4, SwapService;
|
|
32573
|
+
var SWAP_DIR, ESCROW_SWAP_GAS, DEFAULT_SWAP_SLIPPAGE_BPS, MAX_SWAP_SLIPPAGE_BPS, DEFAULT_SWAP_MAX_IMPACT_BPS, MAX_SWAP_MAX_IMPACT_BPS, SPLIT_MAX_PARTS, SPLIT_INTERVAL_SECONDS, SPLIT_SLACK_SECONDS, POOL_INDEX2, ORDER_PREFIX, SPLIT_PREFIX, DONE_PREFIX, DONE_ORDERS_KEPT, address4, SwapService;
|
|
32340
32574
|
var init_swap = __esm({
|
|
32341
32575
|
"../core/src/swap.ts"() {
|
|
32342
32576
|
"use strict";
|
|
@@ -32360,6 +32594,8 @@ var init_swap = __esm({
|
|
|
32360
32594
|
POOL_INDEX2 = "pool-index.json";
|
|
32361
32595
|
ORDER_PREFIX = "order-";
|
|
32362
32596
|
SPLIT_PREFIX = "split-";
|
|
32597
|
+
DONE_PREFIX = "done-";
|
|
32598
|
+
DONE_ORDERS_KEPT = 100;
|
|
32363
32599
|
address4 = (text3, what) => {
|
|
32364
32600
|
if (!/^0x[0-9a-fA-F]{40}$/.test(text3)) throw new RangeError(`${what} is not an address`);
|
|
32365
32601
|
return getAddress38(text3);
|
|
@@ -32572,7 +32808,23 @@ var init_swap = __esm({
|
|
|
32572
32808
|
const escrow = escrowAddress(this.#p.swap.router, order);
|
|
32573
32809
|
const execute = executeEscrowData(order, p.route);
|
|
32574
32810
|
await p.from.checkSpend(VAULT_ACTION.TRANSFER, transferParams({ to: escrow, amount: p.amountIn }), "the swap's transfer to its escrow", this.#p.sender);
|
|
32575
|
-
await this.#keep({
|
|
32811
|
+
await this.#keep({
|
|
32812
|
+
version: 1,
|
|
32813
|
+
hash: orderHash(order),
|
|
32814
|
+
escrow,
|
|
32815
|
+
tokenIn: p.tokenIn,
|
|
32816
|
+
amountIn: p.amountIn.toString(),
|
|
32817
|
+
vaultNonce: v.nonce.toString(),
|
|
32818
|
+
deadline: order.deadline.toString(),
|
|
32819
|
+
execute,
|
|
32820
|
+
at: Date.now(),
|
|
32821
|
+
fromBlock: fromBlock.toString(),
|
|
32822
|
+
tokenOut: p.tokenOut,
|
|
32823
|
+
from: vault,
|
|
32824
|
+
to: p.to,
|
|
32825
|
+
inDecimals: p.inDecimals,
|
|
32826
|
+
outDecimals: p.outDecimals
|
|
32827
|
+
});
|
|
32576
32828
|
await o.onOrder?.(orderHash(order), fromBlock, escrow);
|
|
32577
32829
|
const signed = await signVaultTransfer(keychain, v, { chainId, to: escrow, amount: p.amountIn });
|
|
32578
32830
|
const calls = [spendCall(vault, signed), { to: this.#p.swap.router, data: execute }];
|
|
@@ -32601,6 +32853,21 @@ var init_swap = __esm({
|
|
|
32601
32853
|
note: `the swap landed in ${result.txHash}; what it paid could not be read now (${e.message.split("\n")[0]}): do not send it again, its order ${orderHash(placed2)} is read again later`
|
|
32602
32854
|
};
|
|
32603
32855
|
}
|
|
32856
|
+
await this.#keepDone({
|
|
32857
|
+
version: 1,
|
|
32858
|
+
order: orderHash(placed2),
|
|
32859
|
+
txHash: result.txHash,
|
|
32860
|
+
swapped: outcome.swapped,
|
|
32861
|
+
tokenIn: p.tokenIn,
|
|
32862
|
+
tokenOut: p.tokenOut,
|
|
32863
|
+
amountIn: formatUnitsAmount(p.amountIn, p.inDecimals),
|
|
32864
|
+
amountOut: formatUnitsAmount(outcome.amountOut, p.outDecimals),
|
|
32865
|
+
minOut: formatUnitsAmount(p.route.minOut, p.outDecimals),
|
|
32866
|
+
from: p.fromVault,
|
|
32867
|
+
to: p.to,
|
|
32868
|
+
...outcome.swapped ? {} : { refusal: outcome.why ?? "OstraRouter gave no reason" },
|
|
32869
|
+
at: Date.now()
|
|
32870
|
+
});
|
|
32604
32871
|
await this.#p.folder.remove(`${ORDER_PREFIX}${orderHash(placed2)}.json`);
|
|
32605
32872
|
return {
|
|
32606
32873
|
status: "sent",
|
|
@@ -32891,7 +33158,7 @@ var init_swap = __esm({
|
|
|
32891
33158
|
*/
|
|
32892
33159
|
async orderResult(order, fromBlock, escrow) {
|
|
32893
33160
|
const toBlock = await this.#p.logsClient.getBlockNumber({ cacheTime: 0 });
|
|
32894
|
-
const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event:
|
|
33161
|
+
const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem3({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: order }, fromBlock, toBlock });
|
|
32895
33162
|
let spent = 0n;
|
|
32896
33163
|
let received = 0n;
|
|
32897
33164
|
let moved = false;
|
|
@@ -32937,6 +33204,92 @@ var init_swap = __esm({
|
|
|
32937
33204
|
if (swapped) return { swapped, amountOut };
|
|
32938
33205
|
return { swapped, amountOut: 0n, ...reason === null ? {} : { why: refusalOf(reason, outDecimals) } };
|
|
32939
33206
|
}
|
|
33207
|
+
/** Keeps a finished order for ostra_swap_orders, and drops the oldest beyond DONE_ORDERS_KEPT. */
|
|
33208
|
+
async #keepDone(d) {
|
|
33209
|
+
await this.#p.folder.ensure();
|
|
33210
|
+
await this.#p.folder.write(`${DONE_PREFIX}${d.order.toLowerCase()}.json`, `${JSON.stringify(d)}
|
|
33211
|
+
`);
|
|
33212
|
+
const names = (await this.#p.folder.list()).filter((n) => n.startsWith(DONE_PREFIX) && n.endsWith(".json"));
|
|
33213
|
+
if (names.length <= DONE_ORDERS_KEPT) return;
|
|
33214
|
+
const dated = [];
|
|
33215
|
+
for (const n of names) {
|
|
33216
|
+
const text3 = await this.#p.folder.read(n);
|
|
33217
|
+
dated.push({ name: n, at: text3 === void 0 ? 0 : Number(JSON.parse(text3).at) });
|
|
33218
|
+
}
|
|
33219
|
+
dated.sort((a, b) => a.at - b.at);
|
|
33220
|
+
for (const x of dated.slice(0, dated.length - DONE_ORDERS_KEPT)) await this.#p.folder.remove(x.name);
|
|
33221
|
+
}
|
|
33222
|
+
/**
|
|
33223
|
+
* Keeps what a kept order did once the sweep finishes it: from the transaction that executed it (`tx`), else from
|
|
33224
|
+
* OstraRouter's events from the record's block. Best effort: an order whose events cannot be read now is not kept
|
|
33225
|
+
* (a record from before 0.1.12 has no block to search from).
|
|
33226
|
+
*/
|
|
33227
|
+
async #doneFrom(r, tx) {
|
|
33228
|
+
try {
|
|
33229
|
+
let hash = tx;
|
|
33230
|
+
if (hash === null && r.fromBlock !== void 0) {
|
|
33231
|
+
const toBlock = await this.#p.logsClient.getBlockNumber({ cacheTime: 0 });
|
|
33232
|
+
const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem3({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: r.hash }, fromBlock: BigInt(r.fromBlock), toBlock });
|
|
33233
|
+
const moved = logs.find((l) => l.args.received !== 0n);
|
|
33234
|
+
hash = moved === void 0 ? null : moved.transactionHash;
|
|
33235
|
+
}
|
|
33236
|
+
if (hash === null) return;
|
|
33237
|
+
const inDecimals = r.inDecimals ?? await tokenDecimals(this.#p.client, r.tokenIn);
|
|
33238
|
+
const outDecimals = r.outDecimals ?? (r.tokenOut === void 0 ? 18 : await tokenDecimals(this.#p.client, r.tokenOut));
|
|
33239
|
+
const o = await this.#outcome(hash, r.hash, outDecimals);
|
|
33240
|
+
await this.#keepDone({
|
|
33241
|
+
version: 1,
|
|
33242
|
+
order: r.hash,
|
|
33243
|
+
txHash: hash,
|
|
33244
|
+
swapped: o.swapped,
|
|
33245
|
+
tokenIn: r.tokenIn,
|
|
33246
|
+
tokenOut: r.tokenOut ?? null,
|
|
33247
|
+
amountIn: formatUnitsAmount(BigInt(r.amountIn), inDecimals),
|
|
33248
|
+
amountOut: formatUnitsAmount(o.amountOut, outDecimals),
|
|
33249
|
+
minOut: null,
|
|
33250
|
+
from: r.from ?? null,
|
|
33251
|
+
to: r.to ?? null,
|
|
33252
|
+
...o.swapped ? {} : { refusal: o.why ?? "OstraRouter gave no reason" },
|
|
33253
|
+
at: Date.now()
|
|
33254
|
+
});
|
|
33255
|
+
} catch {
|
|
33256
|
+
}
|
|
33257
|
+
}
|
|
33258
|
+
/**
|
|
33259
|
+
* The agent's orders kept here: those not finished (signed or about to be, or landed and being finished; oldest
|
|
33260
|
+
* first) and the finished ones (newest first, at most `limit`). With `order`, that order alone in either list.
|
|
33261
|
+
*/
|
|
33262
|
+
async orders(o = {}) {
|
|
33263
|
+
const want = o.order?.toLowerCase();
|
|
33264
|
+
const open3 = [];
|
|
33265
|
+
const finished = [];
|
|
33266
|
+
for (const name of await this.#p.folder.list()) {
|
|
33267
|
+
const isOrder = name.startsWith(ORDER_PREFIX);
|
|
33268
|
+
if (!isOrder && !name.startsWith(DONE_PREFIX) || !name.endsWith(".json")) continue;
|
|
33269
|
+
if (want !== void 0 && name.slice((isOrder ? ORDER_PREFIX : DONE_PREFIX).length, -".json".length).toLowerCase() !== want) continue;
|
|
33270
|
+
const text3 = await this.#p.folder.read(name);
|
|
33271
|
+
if (text3 === void 0) continue;
|
|
33272
|
+
if (!isOrder) {
|
|
33273
|
+
finished.push(JSON.parse(text3));
|
|
33274
|
+
continue;
|
|
33275
|
+
}
|
|
33276
|
+
const r = JSON.parse(text3);
|
|
33277
|
+
const inDecimals = r.inDecimals ?? await tokenDecimals(this.#p.client, r.tokenIn);
|
|
33278
|
+
const x = { order: r.hash, tokenIn: r.tokenIn, tokenOut: r.tokenOut ?? null, amountIn: formatUnitsAmount(BigInt(r.amountIn), inDecimals), escrow: r.escrow, state: "open", at: r.at };
|
|
33279
|
+
if (r.fromBlock !== void 0) {
|
|
33280
|
+
const res = await this.orderResult(r.hash, BigInt(r.fromBlock), r.escrow);
|
|
33281
|
+
if (res.spent > 0n || res.received > 0n) {
|
|
33282
|
+
x.state = "landed";
|
|
33283
|
+
x.spent = formatUnitsAmount(res.spent, inDecimals);
|
|
33284
|
+
x.received = formatUnitsAmount(res.received, r.outDecimals ?? 18);
|
|
33285
|
+
}
|
|
33286
|
+
}
|
|
33287
|
+
open3.push(x);
|
|
33288
|
+
}
|
|
33289
|
+
open3.sort((a, b) => a.at - b.at);
|
|
33290
|
+
finished.sort((a, b) => b.at - a.at);
|
|
33291
|
+
return { open: open3, finished: finished.slice(0, o.limit ?? 20) };
|
|
33292
|
+
}
|
|
32940
33293
|
/**
|
|
32941
33294
|
* Finishes the orders whose escrow was paid without being executed (a signed spend sent again on its own):
|
|
32942
33295
|
* executeEscrow sends the input along the order's route, or back to its vault past the deadline. Records of orders
|
|
@@ -32953,11 +33306,15 @@ var init_swap = __esm({
|
|
|
32953
33306
|
const nonce = (await this.#p.vaultFor(r.tokenIn).state()).nonce;
|
|
32954
33307
|
const held = await tokenBalance(this.#p.client, r.tokenIn, r.escrow);
|
|
32955
33308
|
if (held >= BigInt(r.amountIn)) {
|
|
32956
|
-
await this.#p.sender.send([{ to: this.#p.swap.router, data: r.execute }]);
|
|
33309
|
+
const tx = await this.#p.sender.send([{ to: this.#p.swap.router, data: r.execute }]);
|
|
33310
|
+
await this.#doneFrom(r, tx);
|
|
32957
33311
|
await this.#p.folder.remove(name);
|
|
32958
33312
|
continue;
|
|
32959
33313
|
}
|
|
32960
|
-
if (nonce !== null && nonce > BigInt(r.vaultNonce))
|
|
33314
|
+
if (nonce !== null && nonce > BigInt(r.vaultNonce)) {
|
|
33315
|
+
await this.#doneFrom(r, null);
|
|
33316
|
+
await this.#p.folder.remove(name);
|
|
33317
|
+
}
|
|
32961
33318
|
} catch (err) {
|
|
32962
33319
|
failures.push({ order: r.hash, error: err instanceof Error ? err.message : String(err) });
|
|
32963
33320
|
}
|
|
@@ -33821,7 +34178,7 @@ var init_backup_cli = __esm({
|
|
|
33821
34178
|
}
|
|
33822
34179
|
});
|
|
33823
34180
|
|
|
33824
|
-
//
|
|
34181
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/cli-prover.ts
|
|
33825
34182
|
import { spawn } from "node:child_process";
|
|
33826
34183
|
function cliProver(binary, timeoutMs = PROVER_TIMEOUT_MS) {
|
|
33827
34184
|
return (command, request) => new Promise((resolve, reject) => {
|
|
@@ -33862,14 +34219,14 @@ function cliProver(binary, timeoutMs = PROVER_TIMEOUT_MS) {
|
|
|
33862
34219
|
}
|
|
33863
34220
|
var PROVER_TIMEOUT_MS;
|
|
33864
34221
|
var init_cli_prover = __esm({
|
|
33865
|
-
"
|
|
34222
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/cli-prover.ts"() {
|
|
33866
34223
|
"use strict";
|
|
33867
34224
|
init_define_OSTRA_PACKAGED();
|
|
33868
34225
|
PROVER_TIMEOUT_MS = 12e4;
|
|
33869
34226
|
}
|
|
33870
34227
|
});
|
|
33871
34228
|
|
|
33872
|
-
//
|
|
34229
|
+
// ../../../../../../srv/apps/ostra-swap/sdk/src/wasm-prover.ts
|
|
33873
34230
|
import { bytesToHex as bytesToHex17 } from "viem";
|
|
33874
34231
|
async function sha256Hex(bytes) {
|
|
33875
34232
|
return bytesToHex17(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
|
|
@@ -33905,7 +34262,7 @@ async function wasmProver(glue, bytes, sha2564) {
|
|
|
33905
34262
|
}
|
|
33906
34263
|
var WasmProverTrapped, initialized, trapped;
|
|
33907
34264
|
var init_wasm_prover = __esm({
|
|
33908
|
-
"
|
|
34265
|
+
"../../../../../../srv/apps/ostra-swap/sdk/src/wasm-prover.ts"() {
|
|
33909
34266
|
"use strict";
|
|
33910
34267
|
init_define_OSTRA_PACKAGED();
|
|
33911
34268
|
WasmProverTrapped = class extends Error {
|
|
@@ -34026,11 +34383,19 @@ var init_agent = __esm({
|
|
|
34026
34383
|
this.creator = new VaultService(client, transport, config, p.creatorKeys, creatorSender, "creator");
|
|
34027
34384
|
this.creatorRelayer?.attachBuyerVault(this.creator);
|
|
34028
34385
|
this.creatorChannels = new ChannelService(client, config, p.creatorKeys, this.creator, creatorSender);
|
|
34029
|
-
this.launchpad = config.deployment.launchpad === void 0 ? null : new LaunchpadService({ client, transport, config, keys, buyer: this.buyer, buyerSender: this.sender, creator: this.creator, creatorSender });
|
|
34386
|
+
this.launchpad = config.deployment.launchpad === void 0 ? null : new LaunchpadService({ client, transport, config, keys, buyer: this.buyer, buyerSender: this.sender, creator: this.creator, creatorSender, logsClient: createPublicClient2({ chain: robinhoodChain, transport: http2(config.logsRpcUrl, { retryCount: 6, retryDelay: 2e3 }) }) });
|
|
34030
34387
|
this.shield = new ShieldService(client, config, keys, this.buyer, this.sender);
|
|
34031
34388
|
this.creatorShield = new ShieldService(client, config, keys, this.creator, creatorSender);
|
|
34032
34389
|
this.unshield = new UnshieldService(client, config, keys, this.shield, (index) => this.privateVault(index));
|
|
34033
|
-
this.borro = new BorroService({
|
|
34390
|
+
this.borro = new BorroService({
|
|
34391
|
+
client,
|
|
34392
|
+
config,
|
|
34393
|
+
buyer: this.buyer,
|
|
34394
|
+
tokenVault: (token) => this.vaultFor(token),
|
|
34395
|
+
sender: this.sender,
|
|
34396
|
+
ownVaults: () => this.ownVaults(),
|
|
34397
|
+
logsClient: createPublicClient2({ chain: robinhoodChain, transport: http2(config.logsRpcUrl, { retryCount: 6, retryDelay: 2e3 }) })
|
|
34398
|
+
});
|
|
34034
34399
|
this.breaker = new BreakerService({ client, config, buyer: this.buyer, borro: this.borro, sender: this.sender, folder: keys.folder.folder("breaker") });
|
|
34035
34400
|
this.raeto = config.raetoUrl === null ? null : new RaetoService({ client, buyer: this.buyer, borro: this.borro, folder: keys.folder.folder("raeto"), apiUrl: config.raetoUrl });
|
|
34036
34401
|
if (config.swap === null) {
|
|
@@ -34325,6 +34690,12 @@ async function proverConfig(env) {
|
|
|
34325
34690
|
}
|
|
34326
34691
|
return { kind: "wasm", dir, sha256: sha2564 };
|
|
34327
34692
|
}
|
|
34693
|
+
function swapAnswerMs(env) {
|
|
34694
|
+
const v = env.OSTRA_SWAP_ANSWER_MS;
|
|
34695
|
+
if (v === void 0 || v === "") return DEFAULT_SWAP_ANSWER_MS;
|
|
34696
|
+
if (!/^[1-9][0-9]{0,5}$/.test(v) || Number(v) > 6e5) throw new Error("OSTRA_SWAP_ANSWER_MS is a whole number of milliseconds from 1 to 600000");
|
|
34697
|
+
return Number(v);
|
|
34698
|
+
}
|
|
34328
34699
|
function serviceUrl(env, name, fallback) {
|
|
34329
34700
|
const value = env[name];
|
|
34330
34701
|
if (value === void 0 || value === "") return fallback;
|
|
@@ -34374,6 +34745,7 @@ async function loadConfig(env = process.env) {
|
|
|
34374
34745
|
swap,
|
|
34375
34746
|
logsRpcUrl,
|
|
34376
34747
|
privateLocalSpends: env.OSTRA_PRIVATE_LOCAL_SPENDS === "yes",
|
|
34748
|
+
swapAnswerMs: swapAnswerMs(env),
|
|
34377
34749
|
prover,
|
|
34378
34750
|
chainId: deployment.chainId,
|
|
34379
34751
|
rpcUrl: rpcUrl ?? ROBINHOOD_PUBLIC_RPC,
|
|
@@ -34385,7 +34757,7 @@ async function loadConfig(env = process.env) {
|
|
|
34385
34757
|
aiUrl: serviceUrl(env, "OSTRA_AI_URL", services?.aiUrl ?? null)
|
|
34386
34758
|
};
|
|
34387
34759
|
}
|
|
34388
|
-
var PROVER_WASM_FILES, PAD_INDEXER_URL, ETHEREUM_PUBLIC_RPC;
|
|
34760
|
+
var PROVER_WASM_FILES, PAD_INDEXER_URL, ETHEREUM_PUBLIC_RPC, DEFAULT_SWAP_ANSWER_MS;
|
|
34389
34761
|
var init_config2 = __esm({
|
|
34390
34762
|
"src/config.ts"() {
|
|
34391
34763
|
"use strict";
|
|
@@ -34398,10 +34770,12 @@ var init_config2 = __esm({
|
|
|
34398
34770
|
PROVER_WASM_FILES = ["ostra_prove.js", "ostra_prove_bg.wasm"];
|
|
34399
34771
|
PAD_INDEXER_URL = "https://app.ostraagent.xyz";
|
|
34400
34772
|
ETHEREUM_PUBLIC_RPC = "https://ethereum-rpc.publicnode.com";
|
|
34773
|
+
DEFAULT_SWAP_ANSWER_MS = 4e4;
|
|
34401
34774
|
}
|
|
34402
34775
|
});
|
|
34403
34776
|
|
|
34404
34777
|
// src/tools.ts
|
|
34778
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
34405
34779
|
import { readFile as readFile6, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
34406
34780
|
import { join as join7 } from "node:path";
|
|
34407
34781
|
import { z } from "zod";
|
|
@@ -34445,7 +34819,79 @@ function registerTools(server, agent) {
|
|
|
34445
34819
|
return agent.privateBalance;
|
|
34446
34820
|
};
|
|
34447
34821
|
const opt = (key, value) => value === void 0 ? {} : { [key]: value };
|
|
34448
|
-
const
|
|
34822
|
+
const rpcUrls2 = [agent.config.rpcUrl, agent.config.logsRpcUrl, ...agent.config.ethRpcUrl === null ? [] : [agent.config.ethRpcUrl]];
|
|
34823
|
+
const tool = (name, description, shape, fn) => CLOSED_LAUNCHPAD_TOOLS.has(name) ? void 0 : server.registerTool(name, { description, inputSchema: shape }, ((args) => run2(() => fn(args), rpcUrls2)));
|
|
34824
|
+
const answerMs = agent.config.swapAnswerMs;
|
|
34825
|
+
const swaps = /* @__PURE__ */ new Map();
|
|
34826
|
+
const swapView = (r) => ({
|
|
34827
|
+
swap: r.id,
|
|
34828
|
+
what: r.what,
|
|
34829
|
+
tokenIn: r.tokenIn,
|
|
34830
|
+
tokenOut: r.tokenOut,
|
|
34831
|
+
amount: r.amount,
|
|
34832
|
+
startedAt: new Date(r.startedAt).toISOString(),
|
|
34833
|
+
state: r.endedAt === void 0 ? "running" : r.error === void 0 ? "ended" : "failed",
|
|
34834
|
+
// False while its call still waits for it (a client that gave up on that call learns the result here).
|
|
34835
|
+
answeredPending: r.answered,
|
|
34836
|
+
...r.order === void 0 ? {} : { order: r.order },
|
|
34837
|
+
...r.split === void 0 ? {} : { split: r.split },
|
|
34838
|
+
...r.endedAt === void 0 ? {} : { endedAt: new Date(r.endedAt).toISOString() },
|
|
34839
|
+
...r.result === void 0 ? {} : { result: r.result },
|
|
34840
|
+
...r.error === void 0 ? {} : { error: r.error }
|
|
34841
|
+
});
|
|
34842
|
+
const boundedSwap = async (a, start) => {
|
|
34843
|
+
const busy = [...swaps.values()].find((x) => x.endedAt === void 0 && x.tokenIn.toLowerCase() === a.tokenIn.toLowerCase());
|
|
34844
|
+
if (busy !== void 0) {
|
|
34845
|
+
throw new Error(`a swap from the agent's vault of ${a.tokenIn} is still running (${busy.what} of ${busy.amount}, begun ${new Date(busy.startedAt).toISOString()}): nothing was signed for this call; ostra_swap_orders shows how that one ends, call again once it has`);
|
|
34846
|
+
}
|
|
34847
|
+
const r = { id: randomBytes6(8).toString("hex"), ...a, startedAt: Date.now(), answered: false };
|
|
34848
|
+
swaps.set(r.id, r);
|
|
34849
|
+
const work = Promise.resolve().then(() => start({
|
|
34850
|
+
onOrder: async (order) => {
|
|
34851
|
+
r.order = order;
|
|
34852
|
+
},
|
|
34853
|
+
onSplit: async (id) => {
|
|
34854
|
+
r.split = id;
|
|
34855
|
+
}
|
|
34856
|
+
}));
|
|
34857
|
+
let timer;
|
|
34858
|
+
const late = new Promise((resolve) => {
|
|
34859
|
+
timer = setTimeout(() => resolve(null), answerMs);
|
|
34860
|
+
});
|
|
34861
|
+
let first;
|
|
34862
|
+
try {
|
|
34863
|
+
first = await Promise.race([work.then((value) => ({ value })), late]);
|
|
34864
|
+
} catch (err) {
|
|
34865
|
+
swaps.delete(r.id);
|
|
34866
|
+
throw err;
|
|
34867
|
+
} finally {
|
|
34868
|
+
clearTimeout(timer);
|
|
34869
|
+
}
|
|
34870
|
+
if (first !== null) {
|
|
34871
|
+
swaps.delete(r.id);
|
|
34872
|
+
return first.value;
|
|
34873
|
+
}
|
|
34874
|
+
r.answered = true;
|
|
34875
|
+
work.then(
|
|
34876
|
+
(value) => {
|
|
34877
|
+
r.result = value;
|
|
34878
|
+
r.endedAt = Date.now();
|
|
34879
|
+
},
|
|
34880
|
+
(err) => {
|
|
34881
|
+
r.error = rpcUrls2.reduce((text3, url) => redact(text3, url), err instanceof Error ? err.message : String(err));
|
|
34882
|
+
r.endedAt = Date.now();
|
|
34883
|
+
}
|
|
34884
|
+
);
|
|
34885
|
+
const ended = [...swaps.values()].filter((x) => x.endedAt !== void 0).sort((x, y) => x.endedAt - y.endedAt);
|
|
34886
|
+
for (const x of ended.slice(0, Math.max(0, ended.length - ENDED_SWAPS_KEPT))) swaps.delete(x.id);
|
|
34887
|
+
return {
|
|
34888
|
+
status: "pending",
|
|
34889
|
+
swap: r.id,
|
|
34890
|
+
order: r.order ?? null,
|
|
34891
|
+
split: r.split ?? null,
|
|
34892
|
+
note: `${a.what} is still running after ${answerMs / 1e3} s and goes on in the MCP server: do not send it again. ostra_swap_orders shows how it ends (swap ${r.id}${r.order === void 0 ? "" : `, order ${r.order}`}${r.split === void 0 ? "" : `; split swap ${r.split}: ostra_swap_split list`}).`
|
|
34893
|
+
};
|
|
34894
|
+
};
|
|
34449
34895
|
tool("open_vault", "Creates the agent's vault (USDG on Robinhood Chain, post-quantum WOTS keys) or finds it. With the Ostra relayer, its creation is paid later from the vault.", {}, () => agent.buyer.open());
|
|
34450
34896
|
tool("balance", "The vault's balance, nonce, any operation signed and not landed, the relayer channels, and the creator vault.", {}, async () => ({
|
|
34451
34897
|
vault: await agent.buyer.status(),
|
|
@@ -34539,9 +34985,27 @@ function registerTools(server, agent) {
|
|
|
34539
34985
|
);
|
|
34540
34986
|
tool(
|
|
34541
34987
|
"ostra_swap",
|
|
34542
|
-
|
|
34988
|
+
`Swaps on Ostra Swap from the agent's vault of tokenIn to its vault of tokenOut, as an escrow order of OstraRouter: the vault signs one transfer into the order's escrow and the relayer executes it in the same transaction. The router pays at least minOut or sends the input back; where the tokens go is fixed by the escrow's address. A swap that moves the price more than maxImpactBps (default 1%) is split into up to 16 parts 60 seconds apart, each its own escrow order quoted again right before it is signed: a part waits while it would move the price more than the limit or pay less than the first part's quote less the slippage. The first part runs now and the next ones while the MCP server runs (ostra_swap_split); what is not swapped by the deadline stays in the vault. A swap not done within ${answerMs / 1e3} s answers status "pending" and goes on: do not send it again; ostra_swap_orders tells how it ended.`,
|
|
34543
34989
|
{ tokenIn: swapToken, tokenOut: swapToken, amount: units.describe("in tokenIn"), slippageBps: slippage2, maxImpactBps: maxImpact },
|
|
34544
|
-
(a) =>
|
|
34990
|
+
(a) => {
|
|
34991
|
+
const tokenIn = tokenAddress(a.tokenIn);
|
|
34992
|
+
const tokenOut = tokenAddress(a.tokenOut);
|
|
34993
|
+
return boundedSwap(
|
|
34994
|
+
{ what: "the swap", tokenIn, tokenOut, amount: a.amount },
|
|
34995
|
+
(hooks) => ostraSwap().swap({ tokenIn, tokenOut, amount: a.amount, ...opt("slippageBps", a.slippageBps), ...opt("maxImpactBps", a.maxImpactBps) }, hooks)
|
|
34996
|
+
);
|
|
34997
|
+
}
|
|
34998
|
+
);
|
|
34999
|
+
tool(
|
|
35000
|
+
"ostra_swap_orders",
|
|
35001
|
+
"The agent's Ostra Swap orders: swaps of this MCP server still running (their call waiting, or answered pending) and those that answered pending and then ended or failed, orders kept and not finished (signed or about to be, or landed and being finished by the MCP server's sweep each minute), and the last finished ones with what they spent and paid (refusal: why one did not swap, its input back in the vault). order: that order alone. Nothing is signed.",
|
|
35002
|
+
{ order: bytes323.optional() },
|
|
35003
|
+
async (a) => {
|
|
35004
|
+
const kept = await ostraSwap().orders(a.order === void 0 ? {} : { order: a.order });
|
|
35005
|
+
const want = a.order?.toLowerCase();
|
|
35006
|
+
const pending = [...swaps.values()].filter((x) => want === void 0 || x.order?.toLowerCase() === want).sort((x, y) => y.startedAt - x.startedAt);
|
|
35007
|
+
return { pending: pending.map(swapView), open: kept.open, finished: kept.finished };
|
|
35008
|
+
}
|
|
34545
35009
|
);
|
|
34546
35010
|
tool(
|
|
34547
35011
|
"ostra_swap_split",
|
|
@@ -34642,10 +35106,11 @@ function registerTools(server, agent) {
|
|
|
34642
35106
|
);
|
|
34643
35107
|
tool(
|
|
34644
35108
|
"vault_lock",
|
|
34645
|
-
"The withdrawal lock of the vault (USDG) or of the agent's vault of a Borro stock. status: the lock, the daily limit left, the linked vault and the
|
|
35109
|
+
"The withdrawal lock of the vault (USDG) or of the agent's vault of a Borro stock. status: the lock, the daily limit left, the linked vault, the changes waiting, and the hashes the vault approves for EIP-1271 (approvals: each hash, from when it counts and whether that time has come; a hash can be a permit or a transfer authorization of the vault's funds; under the lock no flow of this server approves anything, so every hash listed then came from elsewhere and should go; without the lock the list also holds this server's own approvals, such as Ostrapad's, which look alike: vault_revoke_hash withdraws one, a guardian of a locked vault revokes it with guard_vault); with vault, the same for any Ostra vault (a guardian watching the vault it guards). set: turns the lock on or changes it (withdrawal: a wallet whose key is not on this device, or another seed's Ostra vault in the same token that is locked already; never one of this agent's vaults; delaySeconds 129600 to 1209600; dailyLimit in the vault's token): turning it on and tightening are at once, loosening waits the delay. off: turns it off after the delay. cancel: drops the changes still waiting.",
|
|
34646
35110
|
{
|
|
34647
35111
|
action: z.enum(["status", "set", "off", "cancel"]),
|
|
34648
35112
|
asset: borroAsset.optional().describe("USDG (default) or a Borro stock's ticker"),
|
|
35113
|
+
vault: address5.optional().describe("status only: another Ostra vault to read"),
|
|
34649
35114
|
withdrawal: address5.optional(),
|
|
34650
35115
|
delaySeconds: z.number().int().min(129600).max(1209600).optional(),
|
|
34651
35116
|
dailyLimit: units.optional(),
|
|
@@ -34653,7 +35118,8 @@ function registerTools(server, agent) {
|
|
|
34653
35118
|
},
|
|
34654
35119
|
async (a) => {
|
|
34655
35120
|
const asset = a.asset ?? "USDG";
|
|
34656
|
-
if (a.action
|
|
35121
|
+
if (a.vault !== void 0 && a.action !== "status") throw new RangeError("vault is for status only: set, off and cancel act on this agent's own vaults");
|
|
35122
|
+
if (a.action === "status") return agent.borro.lockStatus(asset, a.vault);
|
|
34657
35123
|
if (a.action === "cancel") return agent.borro.cancelPending(asset);
|
|
34658
35124
|
if (a.action === "off") return agent.borro.setLock(asset, "off", 0, "0");
|
|
34659
35125
|
if (a.withdrawal === void 0 || a.delaySeconds === void 0 || a.dailyLimit === void 0) throw new RangeError("set takes withdrawal, delaySeconds and dailyLimit");
|
|
@@ -34677,10 +35143,10 @@ function registerTools(server, agent) {
|
|
|
34677
35143
|
(a) => agent.borro.setLink(a.asset ?? "USDG", a.vault)
|
|
34678
35144
|
);
|
|
34679
35145
|
tool(
|
|
34680
|
-
"
|
|
34681
|
-
"
|
|
34682
|
-
{ hash: bytes323,
|
|
34683
|
-
(a) => agent.borro.
|
|
35146
|
+
"vault_revoke_hash",
|
|
35147
|
+
"Withdraws the EIP-1271 approval of a 32-byte hash by the vault (USDG) or by the agent's vault of a Borro stock (vault_lock status lists them); at once, under the lock too; lands as done when there is none. No tool approves a hash it is given: USDG takes the vault's EIP-1271 answer as a permit or a transfer authorization of its funds, so the vault only approves digests this server builds and checks itself (Raeto's login message, OstraBreaker's limits, Ostrapad's actions).",
|
|
35148
|
+
{ hash: bytes323, asset: borroAsset.optional().describe("USDG (default) or a Borro stock's ticker") },
|
|
35149
|
+
(a) => agent.borro.withdrawApproval(a.asset ?? "USDG", a.hash)
|
|
34684
35150
|
);
|
|
34685
35151
|
tool(
|
|
34686
35152
|
"guard_vault",
|
|
@@ -34706,7 +35172,7 @@ function registerTools(server, agent) {
|
|
|
34706
35172
|
const earnings = z.record(z.string().regex(/^[A-Z]{1,8}$/), z.number().int().min(0)).optional().describe("earnings dates the agent knows, unix seconds by stock ticker (without one NVDA stays at most 60% of its LLTV)");
|
|
34707
35173
|
tool(
|
|
34708
35174
|
"strategy_plan",
|
|
34709
|
-
"Plans what the agent's vaults hold, computed from the chain now: a conservative plan (USDG into a cash vault that passes every check of Strategy 1) and, when a carry of the agent's SPY, QQQ or NVDA may open, a balanced one (Strategy 2: the stock lent to Borro as collateral, USDG borrowed into the cash vault), each with its steps, gas, what it earns in 30 days net of gas, risks, when it stops, and whether OstraBreaker watches (Strategy 3). Every step earns over 3x its gas in 30 days or is left out. The trading capital's cash (trading) stays in the vault. Nothing is signed. Only for an agent registered in ERC-8004 (
|
|
35175
|
+
"Plans what the agent's vaults hold, computed from the chain now: a conservative plan (USDG into a cash vault that passes every check of Strategy 1) and, when a carry of the agent's SPY, QQQ or NVDA may open, a balanced one (Strategy 2: the stock lent to Borro as collateral, USDG borrowed into the cash vault), each with its steps, gas, what it earns in 30 days net of gas, risks, when it stops, and whether OstraBreaker watches (Strategy 3). Every step earns over 3x its gas in 30 days or is left out. The trading capital's cash (trading) stays in the vault. Nothing is signed. Only for an agent registered in ERC-8004 (publish_profile).",
|
|
34710
35176
|
{ buffer: usdg.optional().describe("USDG kept for payments (default: what the vault spent over 7 days)"), earnings },
|
|
34711
35177
|
(a) => agent.strategy.plan({
|
|
34712
35178
|
...a.buffer === void 0 ? {} : { buffer: parseUsdg(a.buffer) },
|
|
@@ -34728,7 +35194,7 @@ function registerTools(server, agent) {
|
|
|
34728
35194
|
const bpsLimit = z.number().int().min(1).max(1e4).optional();
|
|
34729
35195
|
tool(
|
|
34730
35196
|
"trading",
|
|
34731
|
-
`The trading capital: USDG of the vault set aside for trades the agent decides, with Ostra's limits (an agent may only tighten them): a position at most 25% of the capital (positionMaxBps) and no larger than a buy whose quote moves the price 1% (maxImpactBps); buys stop for the day after a 5% loss of the capital that day (dailyLossBps) and for the week after 10% that week (weeklyLossBps). set: sets it (capital in USDG, the limits), only with no position open; the plans keep its cash out of the cash vaults. status: its value now (cash, positions at the quote of selling them), whether buys may happen and, with token, the most that position may be. buy: buys token with amount USDG of the capital, refused past those limits. sell: sells amount (or "all") of the capital's token, split like any large swap. end: ends it (positions stay in their vaults, untracked). Trades go through Ostra Swap; only for an agent registered in ERC-8004 (
|
|
35197
|
+
`The trading capital: USDG of the vault set aside for trades the agent decides, with Ostra's limits (an agent may only tighten them): a position at most 25% of the capital (positionMaxBps) and no larger than a buy whose quote moves the price 1% (maxImpactBps); buys stop for the day after a 5% loss of the capital that day (dailyLossBps) and for the week after 10% that week (weeklyLossBps). set: sets it (capital in USDG, the limits), only with no position open; the plans keep its cash out of the cash vaults. status: its value now (cash, positions at the quote of selling them), whether buys may happen and, with token, the most that position may be. buy: buys token with amount USDG of the capital, refused past those limits. sell: sells amount (or "all") of the capital's token, split like any large swap. A buy or sell not done in time (${agent.config.swapAnswerMs / 1e3} s) answers status "pending" and goes on: do not send it again; ostra_swap_orders tells how it ended. end: ends it (positions stay in their vaults, untracked). Trades go through Ostra Swap; only for an agent registered in ERC-8004 (publish_profile) to set it or buy.`,
|
|
34732
35198
|
{
|
|
34733
35199
|
action: z.enum(["set", "status", "buy", "sell", "end"]),
|
|
34734
35200
|
capital: usdg.optional(),
|
|
@@ -34758,9 +35224,11 @@ function registerTools(server, agent) {
|
|
|
34758
35224
|
const token = tokenAddress(a.token);
|
|
34759
35225
|
if (a.action === "buy") {
|
|
34760
35226
|
if (a.amount === "all") throw new RangeError("buy takes an amount of USDG");
|
|
34761
|
-
|
|
35227
|
+
const amount2 = a.amount;
|
|
35228
|
+
return boundedSwap({ what: "the trading buy", tokenIn: USDG, tokenOut: token, amount: amount2 }, () => t.buy({ token, amount: amount2, ...opt("slippageBps", a.slippageBps) }));
|
|
34762
35229
|
}
|
|
34763
|
-
|
|
35230
|
+
const amount = a.amount;
|
|
35231
|
+
return boundedSwap({ what: "the trading sell", tokenIn: token, tokenOut: USDG, amount }, () => t.sell({ token, amount, ...opt("slippageBps", a.slippageBps) }));
|
|
34764
35232
|
}
|
|
34765
35233
|
);
|
|
34766
35234
|
tool("strategy_report", "What the strategies earn: yield per day by route at the rates now, the share growth since the first run that held each cash vault, and each carry's distance to liquidation.", {}, () => agent.strategy.report());
|
|
@@ -34829,7 +35297,7 @@ function registerTools(server, agent) {
|
|
|
34829
35297
|
const files = z.array(z.string().max(200).regex(/^ipfs:\/\/[A-Za-z0-9]+(\/[A-Za-z0-9._-]+)*$/)).max(10).optional().describe("ipfs:// files (upload_image makes one)");
|
|
34830
35298
|
tool(
|
|
34831
35299
|
"pad_publish",
|
|
34832
|
-
"Ostrapad as a provider. publish: lists offers in the agent's ERC-8004 registration file, paid into its USDG vault (the vault approves the listing once, so a listing is its own); needs publish_profile
|
|
35300
|
+
"Ostrapad as a provider. publish: lists offers in the agent's ERC-8004 registration file, paid into its USDG vault (the vault approves the listing once, so a listing is its own); needs publish_profile first (it registers the agent), and USDG in the creator vault for its relayed transactions (checked before the approval). unpublish: removes the listing. status: the agent's own listing as clients read it. bond / withdraw_bond: a voluntary provider bond (raises the limit per job to the bond, up to 500 USDG; a rejected job costs it 10% of the budget plus the evaluator fee). return_bond_box: returns USDG left in the provider bond box (a deposit whose second step failed) to the USDG vault.",
|
|
34833
35301
|
{ action: z.enum(["publish", "unpublish", "status", "bond", "withdraw_bond", "return_bond_box"]), offers: z.array(offer).min(1).max(20).optional(), amount: usdg.optional() },
|
|
34834
35302
|
async (a) => {
|
|
34835
35303
|
if (a.action === "publish") {
|
|
@@ -34979,17 +35447,9 @@ function registerTools(server, agent) {
|
|
|
34979
35447
|
(a) => lp().tokenInfo(a.token)
|
|
34980
35448
|
);
|
|
34981
35449
|
tool("profile", "The agent's public identity, its creator vault: the profile URI it set, that profile read from IPFS, and its ERC-8004 agent id; or another account's profile.", { account: address5.optional() }, (a) => lp().profile(a.account));
|
|
34982
|
-
tool(
|
|
34983
|
-
"set_profile",
|
|
34984
|
-
"Sets the agent's profile in OstraRegistry from its creator vault, with a WOTS-signed spend: uri is an ipfs:// (or https://) JSON file with name, description, image and links, ideally an ERC-8004 registration file.",
|
|
34985
|
-
{ uri },
|
|
34986
|
-
(a) => lp().setProfile(a.uri)
|
|
34987
|
-
);
|
|
34988
|
-
tool("register_agent", "Registers the agent in the ERC-8004 Identity Registry, once: its creator vault owns the identity and is its agentWallet; uri is its ERC-8004 registration file.", { uri }, (a) => lp().registerAgent(a.uri));
|
|
34989
|
-
tool("set_agent_uri", "Points the agent's ERC-8004 identity at a new registration file.", { uri }, (a) => lp().setAgentUri(a.uri));
|
|
34990
35450
|
tool(
|
|
34991
35451
|
"upload_image",
|
|
34992
|
-
`Pins an image file on this machine (PNG, JPEG, WebP or GIF, at most ${MAX_UPLOAD_IMAGE_BYTES} bytes) to IPFS through the Ostra uploader and returns its ipfs:// URI, for
|
|
35452
|
+
`Pins an image file on this machine (PNG, JPEG, WebP or GIF, at most ${MAX_UPLOAD_IMAGE_BYTES} bytes) to IPFS through the Ostra uploader and returns its ipfs:// URI, for the files of an Ostrapad order or delivery.`,
|
|
34993
35453
|
{ path: z.string().min(1).max(4096) },
|
|
34994
35454
|
async (a) => {
|
|
34995
35455
|
const s = await stat4(a.path);
|
|
@@ -34999,9 +35459,9 @@ function registerTools(server, agent) {
|
|
|
34999
35459
|
);
|
|
35000
35460
|
tool(
|
|
35001
35461
|
"publish_profile",
|
|
35002
|
-
|
|
35003
|
-
{
|
|
35004
|
-
(
|
|
35462
|
+
`Publishes the agent's Ostra profile and registers it in the ERC-8004 Identity Registry, with nothing to fill in: the same for every Ostra agent ("Ostra Agent #<id>", Ostra's description, the Ostra agent card as its image, its creator vault as agentWallet). The first call publishes the profile, registers the agent (its creator vault owns the identity) and publishes the profile again with the agent's number; a later call publishes it again (an Ostrapad listing in it is kept). Needs USDG in the creator vault (fund_creator_vault).`,
|
|
35463
|
+
{},
|
|
35464
|
+
() => lp().publishProfile()
|
|
35005
35465
|
);
|
|
35006
35466
|
tool(
|
|
35007
35467
|
"publish_token_metadata",
|
|
@@ -35064,7 +35524,7 @@ function launchOptions(raw) {
|
|
|
35064
35524
|
}
|
|
35065
35525
|
return out;
|
|
35066
35526
|
}
|
|
35067
|
-
var toJson, address5, usdg, units, pair2, slippage2, bytes323, maxImpact, swapToken, tokenAddress, uri, publicFields, AI_DIR, MAX_VIDEO_BYTES, FINAL_JOB, CLOSED_LAUNCHPAD_TOOLS, BIGINT_FIELDS, ALLOWED_OPTIONS;
|
|
35527
|
+
var toJson, address5, usdg, units, pair2, slippage2, bytes323, maxImpact, swapToken, tokenAddress, uri, publicFields, AI_DIR, MAX_VIDEO_BYTES, FINAL_JOB, CLOSED_LAUNCHPAD_TOOLS, ENDED_SWAPS_KEPT, BIGINT_FIELDS, ALLOWED_OPTIONS;
|
|
35068
35528
|
var init_tools = __esm({
|
|
35069
35529
|
"src/tools.ts"() {
|
|
35070
35530
|
"use strict";
|
|
@@ -35117,6 +35577,7 @@ var init_tools = __esm({
|
|
|
35117
35577
|
"airdrop_fund",
|
|
35118
35578
|
"airdrop_start"
|
|
35119
35579
|
]);
|
|
35580
|
+
ENDED_SWAPS_KEPT = 20;
|
|
35120
35581
|
BIGINT_FIELDS = /* @__PURE__ */ new Set(["minDividendBalance", "maxBuy", "maxWallet", "graduationThreshold"]);
|
|
35121
35582
|
ALLOWED_OPTIONS = /* @__PURE__ */ new Set([
|
|
35122
35583
|
"tiers",
|