@ostraagent/mcp 0.1.5 → 0.1.6
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 +2 -3
- package/dist/ostra-mcp.js +309 -85
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -45,12 +45,11 @@ Keep the file and the passphrase off the machine. On a new machine, run `npx -y
|
|
|
45
45
|
- **Payments:** `pay` (calls a paid HTTP service and pays it from a channel of the vault, refused before signing above `maxPrice`), `open_channel`, `list_channels`, `close_channel`
|
|
46
46
|
- **Ostra AI:** `ostra_ai` (a text, image or video task; Ostra chooses the model by its quality rating within `maxPrice`, paid in USDG from a channel of the vault, and what a task did not cost stays as the vault's credit), `ostra_ai_job` (a video job, saved as a file once completed), `ostra_ai_credit`
|
|
47
47
|
- **Selling services:** `accept_payments`, `seller_balance`, `seller_withdraw`
|
|
48
|
-
- **Privacy:** `shield`, `shield_status`, `auto_shield`, `unshield`, `private_withdraw`
|
|
49
48
|
- **Recovery:** `recovery_status`, `set_guardian`, `set_recovery_policy`, `cancel_recovery`, `support_recovery`, `request_recovery`
|
|
50
49
|
- **Trading:** `tokens`, `token_info`, `pool_info`, `quote_swap`, `buy`, `sell`, `token_vault`, `transfer_token`, `claim_dividends`, `claim_referral`
|
|
51
50
|
- **Launching:** `pairs`, `launch` (paired with USDG, ETH, $OSTRA or a listed Robinhood stock token, at a starting market cap in US dollars, with an optional dev buy and vesting), `launches`, `set_pool_setting`, `set_creator_share`, `release_creator_payout`, `airdrop_fund`, `airdrop_start`, `airdrop_claim`, `airdrop_close`, `vesting_info`, `vesting_release`, `vesting_claim_dividends`
|
|
52
51
|
- **Ostra Swap:** `ostra_swap_quote`, `ostra_swap` (between the agent's vaults of USDG, WETH, $OSTRA, listed stock tokens and launched tokens, routed through Uniswap v3 and v4; tokens by address or symbol)
|
|
53
|
-
- **
|
|
52
|
+
- **Privacy:** `private_shield` (from a vault into a private note), `private_balance`, `private_swap`, `private_unshield` (to an address, or with `toPrivateVault` to a new private vault of the agent), `private_withdraw` (from a private vault), `private_recover` (finds the notes and private vaults again from the seed alone), `auto_shield` (everything the creator vault holds beyond what it keeps, into private notes). Notes are spent with a zero-knowledge proof made on the agent's machine by the WebAssembly prover the package carries. A spend does not show which note it spends; the token, the amounts, the times, the vault a shield comes from and an unshield's recipient are public, so a private vault's first balance matches the unshield that paid it
|
|
54
53
|
- **Identity:** `profile`, `upload_image`, `publish_profile`, `set_profile`, `publish_token_metadata`, `set_token_metadata`, `register_agent`, `set_agent_uri`
|
|
55
54
|
|
|
56
55
|
Every spend is simulated before its one-time key signs it. When a tool refuses, the error says whether it refused before anything was signed.
|
|
@@ -68,7 +67,7 @@ All settings are optional.
|
|
|
68
67
|
| `OSTRA_RPC_URL_FILE` | | A private file (mode 0600) holding the RPC URL, when it carries an API key |
|
|
69
68
|
| `OSTRA_RELAYER_URL` | `https://relayer.ostraagent.xyz` | Sends every transaction, paid in USDG. With `off`, a local fee payer key in `OSTRA_HOME` pays gas in ETH |
|
|
70
69
|
| `OSTRA_KEEPER_URL` | `https://keeper.ostraagent.xyz` | Claims payments for the seller side. `off` disables `accept_payments` |
|
|
71
|
-
| `OSTRA_AGGREGATOR_URL` | `https://aggregator.ostraagent.xyz` |
|
|
70
|
+
| `OSTRA_AGGREGATOR_URL` | `https://aggregator.ostraagent.xyz` | The shielded pool's batches, for shield deposits kept from before 0.1.6 only. `off` stops them |
|
|
72
71
|
| `OSTRA_AI_URL` | `https://ai.ostraagent.xyz` | Ostra AI, the gateway of the `ostra_ai` tools. `off` disables them |
|
|
73
72
|
| `OSTRA_SWAP_DEPLOYMENT` | Ostra Swap on Robinhood Chain mainnet | Path of another Ostra Swap deployment record (JSON). With `OSTRA_DEPLOYMENT` set and no Ostra Swap record, the Ostra Swap and private tools refuse |
|
|
74
73
|
| `OSTRA_PROVER` | The WebAssembly prover in the package | Path of a native `ostra-prove` binary to prove with instead, or `off` (the private tools then refuse to spend) |
|
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.6", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xD20c6744Cfa46d9ec564b119cC923bAb2f9f9331", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" }, registry: "0xcE6Cc1f93744f4090DA1444Bb8590d9206157506" }, 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
|
|
|
@@ -15997,7 +15997,7 @@ var init_vault = __esm({
|
|
|
15997
15997
|
return this.token === this.#config.deployment.token;
|
|
15998
15998
|
}
|
|
15999
15999
|
#missing() {
|
|
16000
|
-
if (this.kind === "private") return `private vault ${this.privateIndex} does not exist yet;
|
|
16000
|
+
if (this.kind === "private") return `private vault ${this.privateIndex} does not exist yet; it is created on its first use`;
|
|
16001
16001
|
if (this.kind === "creator") return `this agent has no creator vault${this.#inUsdg() ? "" : ` in ${this.token}`} yet; open it before a launch names it`;
|
|
16002
16002
|
if (this.kind === "token") return `this agent has no vault in ${this.token} yet; buying the token creates it`;
|
|
16003
16003
|
return this.kind === "buyer" ? "this agent has no vault yet; call open_vault first" : "this agent has no seller vault yet; call accept_payments first";
|
|
@@ -16124,7 +16124,6 @@ var init_vault = __esm({
|
|
|
16124
16124
|
}
|
|
16125
16125
|
open() {
|
|
16126
16126
|
return this.#serial(async () => {
|
|
16127
|
-
if (this.kind === "private") throw new Error("a private vault is created by unshield, together with the leaf spent into it");
|
|
16128
16127
|
if (this.kind !== "token" && this.#inUsdg()) await this.#checkToken();
|
|
16129
16128
|
const vault = await this.vaultAddress();
|
|
16130
16129
|
const token = this.token;
|
|
@@ -16138,7 +16137,7 @@ var init_vault = __esm({
|
|
|
16138
16137
|
await this.requireEth(CREATE_VAULT_GAS, "open the vault");
|
|
16139
16138
|
const txHash = await this.#sender.send([await this.createCall()], this.#options({ prepay: this.kind === "seller" || !this.#paysRelayer() }));
|
|
16140
16139
|
const result = { vault, token, nonce: "0", created: true, txHash, softLimit };
|
|
16141
|
-
if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator") && this.#paysRelayer()) {
|
|
16140
|
+
if (this.#sender.relayed && (this.kind === "buyer" || this.kind === "creator" || this.kind === "private") && this.#paysRelayer()) {
|
|
16142
16141
|
result.note = "the Ostra relayer paid the vault's creation; the vault owes that price, paid with the first voucher of its channel to the relayer, which this server opens from the vault once it holds tokens (nothing else is relayed for the vault until then)";
|
|
16143
16142
|
}
|
|
16144
16143
|
if (!await this.#keys.backupExported()) result.backupReminder = BACKUP_REMINDER;
|
|
@@ -21720,11 +21719,11 @@ var init_src2 = __esm({
|
|
|
21720
21719
|
});
|
|
21721
21720
|
|
|
21722
21721
|
// ../core/src/private-balance.ts
|
|
21723
|
-
import { getAddress as getAddress25, parseAbiItem as parseAbiItem4 } from "viem";
|
|
21722
|
+
import { bytesToHex as bytesToHex12, getAddress as getAddress25, parseAbiItem as parseAbiItem4, zeroAddress as zeroAddress12 } from "viem";
|
|
21724
21723
|
function privateTokens(config) {
|
|
21725
21724
|
return [config.deployment.token, WETH, ...LAUNCH_PAIRS.filter((p) => p.kind === "ostra" || p.kind === "stock").map((p) => getAddress25(p.address))];
|
|
21726
21725
|
}
|
|
21727
|
-
var PRIVATE_SWAP_DIR, PRIVATE_SHIELD_GAS, PRIVATE_SPEND_DEADLINE_SECONDS, MAX_SLIPPAGE_BPS2, PRIVATE_FEE_GAS, PRIVATE_FEE_MAX_FACTOR, SHIELD_SCAN_GAP, LOGS_CATCH_UP_TRIES, SHIELD_LOG_WAIT_MS, NOTES, TREES, NEXT_RHO, POOL_INDEX, PENDING_PREFIX, shieldedEvent, swappedEvent, unshieldedEvent, address3, PrivateBalanceService;
|
|
21726
|
+
var PRIVATE_SWAP_DIR, PRIVATE_SHIELD_GAS, PRIVATE_SPEND_DEADLINE_SECONDS, MAX_SLIPPAGE_BPS2, PRIVATE_FEE_GAS, PRIVATE_FEE_MAX_FACTOR, SHIELD_SCAN_GAP, LOGS_CATCH_UP_TRIES, SHIELD_LOG_WAIT_MS, PRIVATE_VAULT_SCAN_GAP, AUTO_SHIELD_MIN, NOTES, TREES, NEXT_RHO, POOL_INDEX, PENDING_PREFIX, PRIVATE_VAULTS, AUTO_SHIELD, shieldedEvent, swappedEvent, unshieldedEvent, address3, PrivateBalanceService;
|
|
21728
21727
|
var init_private_balance = __esm({
|
|
21729
21728
|
"../core/src/private-balance.ts"() {
|
|
21730
21729
|
"use strict";
|
|
@@ -21733,6 +21732,7 @@ var init_private_balance = __esm({
|
|
|
21733
21732
|
init_src2();
|
|
21734
21733
|
init_amount();
|
|
21735
21734
|
init_chain2();
|
|
21735
|
+
init_config();
|
|
21736
21736
|
init_relay();
|
|
21737
21737
|
init_vault();
|
|
21738
21738
|
PRIVATE_SWAP_DIR = "ostra-swap-private";
|
|
@@ -21744,11 +21744,15 @@ var init_private_balance = __esm({
|
|
|
21744
21744
|
SHIELD_SCAN_GAP = 20n;
|
|
21745
21745
|
LOGS_CATCH_UP_TRIES = 30;
|
|
21746
21746
|
SHIELD_LOG_WAIT_MS = 60 * 60 * 1e3;
|
|
21747
|
+
PRIVATE_VAULT_SCAN_GAP = 200n;
|
|
21748
|
+
AUTO_SHIELD_MIN = parseTokenAmount("1");
|
|
21747
21749
|
NOTES = "notes.json";
|
|
21748
21750
|
TREES = "trees.json";
|
|
21749
21751
|
NEXT_RHO = "next-rho";
|
|
21750
21752
|
POOL_INDEX = "pool-index.json";
|
|
21751
21753
|
PENDING_PREFIX = "pending-";
|
|
21754
|
+
PRIVATE_VAULTS = "private-vaults.json";
|
|
21755
|
+
AUTO_SHIELD = "auto-shield-creator.json";
|
|
21752
21756
|
shieldedEvent = parseAbiItem4("event Shielded(address indexed from, address indexed token, uint256 amount, bytes32 tag, bytes32 commitment)");
|
|
21753
21757
|
swappedEvent = parseAbiItem4(
|
|
21754
21758
|
"event PrivateSwapped(bytes32 indexed nullifier, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut, uint256 fee, address relayer, bytes32 outTag, bytes32 outCommitment)"
|
|
@@ -21834,7 +21838,7 @@ var init_private_balance = __esm({
|
|
|
21834
21838
|
shielded.set(tag, [...shielded.get(tag) ?? [], { token: getAddress25(l.args.token), amount: l.args.amount, commitment: l.args.commitment }]);
|
|
21835
21839
|
notes += 1;
|
|
21836
21840
|
} else if (l.eventName === "Unshielded") {
|
|
21837
|
-
spends.set(l.args.nullifier.toLowerCase(), { kind: "unshield", v: l.args.amount, fee: l.args.fee });
|
|
21841
|
+
spends.set(l.args.nullifier.toLowerCase(), { kind: "unshield", v: l.args.amount, fee: l.args.fee, recipient: getAddress25(l.args.recipient) });
|
|
21838
21842
|
notes += 1;
|
|
21839
21843
|
} else {
|
|
21840
21844
|
spends.set(l.args.nullifier.toLowerCase(), { kind: "swap", v: l.args.amountIn, fee: l.args.fee, tokenOut: getAddress25(l.args.tokenOut), amountOut: l.args.amountOut, outCommitment: l.args.outCommitment });
|
|
@@ -21919,6 +21923,7 @@ var init_private_balance = __esm({
|
|
|
21919
21923
|
const shields = queue.length;
|
|
21920
21924
|
const unspent = [];
|
|
21921
21925
|
const seen = /* @__PURE__ */ new Set();
|
|
21926
|
+
const recipients = /* @__PURE__ */ new Set();
|
|
21922
21927
|
let followed = 0;
|
|
21923
21928
|
for (let note = queue.pop(); note !== void 0; note = queue.pop()) {
|
|
21924
21929
|
if (seen.has(note.commitment.toLowerCase())) continue;
|
|
@@ -21930,6 +21935,7 @@ var init_private_balance = __esm({
|
|
|
21930
21935
|
unspent.push(note);
|
|
21931
21936
|
continue;
|
|
21932
21937
|
}
|
|
21938
|
+
if (spend.kind === "unshield") recipients.add(spend.recipient);
|
|
21933
21939
|
const change = note.amount - spend.v - spend.fee;
|
|
21934
21940
|
if (change > 0n) {
|
|
21935
21941
|
const rho = this.#spendRho("change", note.commitment);
|
|
@@ -21954,7 +21960,27 @@ var init_private_balance = __esm({
|
|
|
21954
21960
|
const next = counted + 1n > last + 1n ? counted + 1n : last + 1n;
|
|
21955
21961
|
await this.#p.folder.write(NEXT_RHO, `${next}
|
|
21956
21962
|
`);
|
|
21957
|
-
|
|
21963
|
+
await this.#recoverPrivateVaults(recipients);
|
|
21964
|
+
return { pool: this.#pool, shields, followed, notes: records.length, tokens: await this.#summary(records), privateVaults: await this.#privateVaultViews() };
|
|
21965
|
+
}
|
|
21966
|
+
/**
|
|
21967
|
+
* The private vaults among `recipients` (the agent's unshields' recipients): numbers 0, 1, ... until
|
|
21968
|
+
* PRIVATE_VAULT_SCAN_GAP numbers in a row past the last one found, and past every number kept here or taken by
|
|
21969
|
+
* unshield.ts, are none of them. Kept with the ones recorded here (a record whose address is not its number's goes).
|
|
21970
|
+
*/
|
|
21971
|
+
async #recoverPrivateVaults(recipients) {
|
|
21972
|
+
const kept = (await this.#privateVaults()).filter((r) => this.#privateVaultAddress(BigInt(r.index)) === getAddress25(r.vault));
|
|
21973
|
+
const byIndex = new Map(kept.map((r) => [r.index, r]));
|
|
21974
|
+
const known = [...kept.map((r) => BigInt(r.index)), ...await this.#takenPrivateIndexes()];
|
|
21975
|
+
const floor = known.reduce((m, i) => i + 1n > m ? i + 1n : m, 0n);
|
|
21976
|
+
let last = -1n;
|
|
21977
|
+
for (let i = 0n; recipients.size > 0 && (i <= last + PRIVATE_VAULT_SCAN_GAP || i < floor); i++) {
|
|
21978
|
+
const vault = this.#privateVaultAddress(i);
|
|
21979
|
+
if (!recipients.has(vault)) continue;
|
|
21980
|
+
last = i;
|
|
21981
|
+
if (!byIndex.has(i.toString())) byIndex.set(i.toString(), { index: i.toString(), vault, at: Date.now() });
|
|
21982
|
+
}
|
|
21983
|
+
await this.#savePrivateVaults([...byIndex.values()].sort((a, b) => BigInt(a.index) < BigInt(b.index) ? -1 : 1));
|
|
21958
21984
|
}
|
|
21959
21985
|
/** Per token: what `notes` hold, how many, and the largest. */
|
|
21960
21986
|
async #summary(notes) {
|
|
@@ -21996,6 +22022,53 @@ var init_private_balance = __esm({
|
|
|
21996
22022
|
await this.#p.folder.write(NOTES, `${JSON.stringify({ version: 1, notes })}
|
|
21997
22023
|
`);
|
|
21998
22024
|
}
|
|
22025
|
+
/** The address of private vault `index` (VaultService.vaultAddress of kind "private"; no crank). */
|
|
22026
|
+
#privateVaultAddress(index) {
|
|
22027
|
+
const { chainId, deployment: d } = this.#p.config;
|
|
22028
|
+
const initialKeyRoot = bytesToHex12(this.#p.keys.privateVaultKeychain(chainId, d.token, index).keyRoot(0n));
|
|
22029
|
+
return vaultAddress({ factory: d.vaultFactory, beacon: d.vaultBeacon, token: d.token, initialKeyRoot, initialCrank: zeroAddress12 });
|
|
22030
|
+
}
|
|
22031
|
+
async #privateVaults() {
|
|
22032
|
+
const text2 = await this.#p.folder.read(PRIVATE_VAULTS);
|
|
22033
|
+
return text2 === void 0 ? [] : JSON.parse(text2).vaults;
|
|
22034
|
+
}
|
|
22035
|
+
async #savePrivateVaults(vaults) {
|
|
22036
|
+
await this.#p.folder.ensure();
|
|
22037
|
+
await this.#p.folder.write(PRIVATE_VAULTS, `${JSON.stringify({ version: 1, vaults })}
|
|
22038
|
+
`);
|
|
22039
|
+
}
|
|
22040
|
+
async #takenPrivateIndexes() {
|
|
22041
|
+
return this.#p.takenPrivateIndexes === void 0 ? [] : this.#p.takenPrivateIndexes();
|
|
22042
|
+
}
|
|
22043
|
+
/**
|
|
22044
|
+
* A new private vault, kept before it is used: the first number past every one kept here or taken by unshield.ts
|
|
22045
|
+
* whose address has no code and no USDG yet. A number used from another copy of this OSTRA_HOME is passed over and
|
|
22046
|
+
* kept too (it is this seed's private vault), so private_balance and private_withdraw see it.
|
|
22047
|
+
*/
|
|
22048
|
+
async #newPrivateVault() {
|
|
22049
|
+
const vaults = await this.#privateVaults();
|
|
22050
|
+
let index = [...vaults.map((r) => BigInt(r.index)), ...await this.#takenPrivateIndexes()].reduce((m, i) => i + 1n > m ? i + 1n : m, 0n);
|
|
22051
|
+
for (; ; index++) {
|
|
22052
|
+
const vault = this.#privateVaultAddress(index);
|
|
22053
|
+
const record = { index: index.toString(), vault, at: Date.now() };
|
|
22054
|
+
vaults.push(record);
|
|
22055
|
+
const used = await this.#p.client.getCode({ address: vault }) !== void 0 || await tokenBalance(this.#p.client, this.#p.config.deployment.token, vault) > 0n;
|
|
22056
|
+
if (used) continue;
|
|
22057
|
+
await this.#savePrivateVaults(vaults);
|
|
22058
|
+
return record;
|
|
22059
|
+
}
|
|
22060
|
+
}
|
|
22061
|
+
/** The private vaults kept here, read now: the ones created or holding USDG (a number whose unshield never landed stays kept, unused). */
|
|
22062
|
+
async #privateVaultViews() {
|
|
22063
|
+
const out = [];
|
|
22064
|
+
for (const r of await this.#privateVaults()) {
|
|
22065
|
+
const vault = getAddress25(r.vault);
|
|
22066
|
+
const exists2 = await this.#p.client.getCode({ address: vault }) !== void 0;
|
|
22067
|
+
const balance = await tokenBalance(this.#p.client, this.#p.config.deployment.token, vault);
|
|
22068
|
+
if (exists2 || balance > 0n) out.push({ index: r.index, vault, exists: exists2, balance: formatTokenAmount(balance) });
|
|
22069
|
+
}
|
|
22070
|
+
return out;
|
|
22071
|
+
}
|
|
21999
22072
|
async #addNotes(add, remove = []) {
|
|
22000
22073
|
const drop = new Set(remove.map((c) => c.toLowerCase()));
|
|
22001
22074
|
const notes = (await this.#notes()).filter((n) => !drop.has(n.commitment.toLowerCase()));
|
|
@@ -22034,12 +22107,12 @@ var init_private_balance = __esm({
|
|
|
22034
22107
|
async #done(id) {
|
|
22035
22108
|
await this.#p.folder.remove(`${PENDING_PREFIX}${id}.json`);
|
|
22036
22109
|
}
|
|
22037
|
-
/** The agent's notes now (operations in flight finished first),
|
|
22110
|
+
/** The agent's notes now (operations in flight finished first), per token what they hold, and its private vaults. */
|
|
22038
22111
|
balance() {
|
|
22039
22112
|
return this.#serial(async () => {
|
|
22040
22113
|
const failures = await this.#refresh();
|
|
22041
22114
|
await this.#ensureRecovered();
|
|
22042
|
-
return { pool: this.#pool, tokens: await this.#summary(await this.#notes()), inFlight: failures.length };
|
|
22115
|
+
return { pool: this.#pool, tokens: await this.#summary(await this.#notes()), inFlight: failures.length, privateVaults: await this.#privateVaultViews() };
|
|
22043
22116
|
});
|
|
22044
22117
|
}
|
|
22045
22118
|
/** Shields `amount` of `token` from the agent's vault of it into a new note (see the file comment). */
|
|
@@ -22047,43 +22120,58 @@ var init_private_balance = __esm({
|
|
|
22047
22120
|
return this.#serial(async () => {
|
|
22048
22121
|
const token = address3(a.token, "token");
|
|
22049
22122
|
this.#checkToken(token);
|
|
22050
|
-
const prover = this.#prover();
|
|
22051
22123
|
const decimals = await tokenDecimals(this.#p.client, token);
|
|
22052
22124
|
const amount = parseUnitsAmount(a.amount, decimals);
|
|
22053
22125
|
if (amount === 0n) throw new RangeError("amount must be greater than zero");
|
|
22054
|
-
|
|
22055
|
-
const s = await from.state();
|
|
22056
|
-
if (!s.exists && from.kind === "buyer") throw new Error("this agent has no vault yet; call open_vault first");
|
|
22057
|
-
if (s.balance < amount) throw new Error(`the vault in ${token} (${s.vault}) holds ${formatUnitsAmount(s.balance, decimals)}, less than ${a.amount}; nothing was signed`);
|
|
22058
|
-
if (!s.exists) await from.open();
|
|
22059
|
-
const sk = this.#sk();
|
|
22060
|
-
await this.#ensureRecovered();
|
|
22061
|
-
let pending = null;
|
|
22062
|
-
const result = await from.operate({
|
|
22063
|
-
gas: PRIVATE_SHIELD_GAS,
|
|
22064
|
-
moves: amount,
|
|
22065
|
-
what: "shield",
|
|
22066
|
-
build: async ({ v, vault, keychain, chainId }) => {
|
|
22067
|
-
const balance = await tokenBalance(this.#p.client, token, vault);
|
|
22068
|
-
if (balance < amount) throw new Error(`the vault holds ${formatUnitsAmount(balance, decimals)} now, less than ${a.amount}; nothing was signed`);
|
|
22069
|
-
const rho = await this.#nextShieldRho();
|
|
22070
|
-
const tag = await tagOf(prover, sk, rho);
|
|
22071
|
-
const escrow = shieldEscrowAddress(this.#pool, token, tag);
|
|
22072
|
-
pending = { kind: "shield", id: tag.slice(2, 18), token, rho, tag, escrow, vaultNonce: v.nonce.toString(), block: (await this.#p.client.getBlockNumber({ cacheTime: 0 })).toString(), at: Date.now() };
|
|
22073
|
-
await this.#keep(pending);
|
|
22074
|
-
const signed = await signVaultTransfer(keychain, v, { chainId, to: escrow, amount });
|
|
22075
|
-
return [spendCall(vault, signed), shieldFromEscrowCall(this.#pool, token, tag)];
|
|
22076
|
-
}
|
|
22077
|
-
});
|
|
22078
|
-
if (result.status !== "sent") return result;
|
|
22079
|
-
const kept = pending;
|
|
22080
|
-
if (kept === null) throw new Error("the shield was sent without its record");
|
|
22081
|
-
const note = await shieldedNote(prover, sk, kept.rho, await this.#p.client.getTransactionReceipt({ hash: result.txHash }), this.#pool);
|
|
22082
|
-
await this.#addNotes([note]);
|
|
22083
|
-
await this.#done(kept.id);
|
|
22084
|
-
return { status: "sent", txHash: result.txHash, token, amount: formatUnitsAmount(note.amount, decimals), note: note.commitment };
|
|
22126
|
+
return this.#shieldFrom(this.#p.vaultFor(token), void 0, token, amount, decimals);
|
|
22085
22127
|
});
|
|
22086
22128
|
}
|
|
22129
|
+
/** Shields `amount` of `token` from the vault `from` (`source` "creator": the creator vault) into a new note. */
|
|
22130
|
+
async #shieldFrom(from, source, token, amount, decimals) {
|
|
22131
|
+
const prover = this.#prover();
|
|
22132
|
+
const text2 = formatUnitsAmount(amount, decimals);
|
|
22133
|
+
const s = await from.state();
|
|
22134
|
+
if (!s.exists && from.kind === "buyer") throw new Error("this agent has no vault yet; call open_vault first");
|
|
22135
|
+
if (s.balance < amount) throw new Error(`the vault in ${token} (${s.vault}) holds ${formatUnitsAmount(s.balance, decimals)}, less than ${text2}; nothing was signed`);
|
|
22136
|
+
if (!s.exists) await from.open();
|
|
22137
|
+
const sk = this.#sk();
|
|
22138
|
+
await this.#ensureRecovered();
|
|
22139
|
+
let pending = null;
|
|
22140
|
+
const result = await from.operate({
|
|
22141
|
+
gas: PRIVATE_SHIELD_GAS,
|
|
22142
|
+
moves: amount,
|
|
22143
|
+
what: "shield",
|
|
22144
|
+
build: async ({ v, vault, keychain, chainId }) => {
|
|
22145
|
+
const balance = await tokenBalance(this.#p.client, token, vault);
|
|
22146
|
+
if (balance < amount) throw new Error(`the vault holds ${formatUnitsAmount(balance, decimals)} now, less than ${text2}; nothing was signed`);
|
|
22147
|
+
const rho = await this.#nextShieldRho();
|
|
22148
|
+
const tag = await tagOf(prover, sk, rho);
|
|
22149
|
+
const escrow = shieldEscrowAddress(this.#pool, token, tag);
|
|
22150
|
+
pending = {
|
|
22151
|
+
kind: "shield",
|
|
22152
|
+
id: tag.slice(2, 18),
|
|
22153
|
+
token,
|
|
22154
|
+
rho,
|
|
22155
|
+
tag,
|
|
22156
|
+
escrow,
|
|
22157
|
+
...source === void 0 ? {} : { source },
|
|
22158
|
+
vaultNonce: v.nonce.toString(),
|
|
22159
|
+
block: (await this.#p.client.getBlockNumber({ cacheTime: 0 })).toString(),
|
|
22160
|
+
at: Date.now()
|
|
22161
|
+
};
|
|
22162
|
+
await this.#keep(pending);
|
|
22163
|
+
const signed = await signVaultTransfer(keychain, v, { chainId, to: escrow, amount });
|
|
22164
|
+
return [spendCall(vault, signed), shieldFromEscrowCall(this.#pool, token, tag)];
|
|
22165
|
+
}
|
|
22166
|
+
});
|
|
22167
|
+
if (result.status !== "sent") return result;
|
|
22168
|
+
const kept = pending;
|
|
22169
|
+
if (kept === null) throw new Error("the shield was sent without its record");
|
|
22170
|
+
const note = await shieldedNote(prover, sk, kept.rho, await this.#p.client.getTransactionReceipt({ hash: result.txHash }), this.#pool);
|
|
22171
|
+
await this.#addNotes([note]);
|
|
22172
|
+
await this.#done(kept.id);
|
|
22173
|
+
return { status: "sent", txHash: result.txHash, token, amount: formatUnitsAmount(note.amount, decimals), note: note.commitment };
|
|
22174
|
+
}
|
|
22087
22175
|
/** The smallest note of `token` holding at least `need`, or an error naming the largest. */
|
|
22088
22176
|
async #pick(token, need, decimals, what) {
|
|
22089
22177
|
const busy = await this.#notesInFlight();
|
|
@@ -22238,11 +22326,19 @@ var init_private_balance = __esm({
|
|
|
22238
22326
|
};
|
|
22239
22327
|
});
|
|
22240
22328
|
}
|
|
22241
|
-
/**
|
|
22329
|
+
/**
|
|
22330
|
+
* Sends `amount` of `token` out of one private note to `destination` (default: the agent's vault of that token), or,
|
|
22331
|
+
* with `toPrivateVault`, to a new private vault of the agent (USDG only; see the file comment).
|
|
22332
|
+
*/
|
|
22242
22333
|
unshield(a) {
|
|
22243
22334
|
return this.#serial(async () => {
|
|
22244
22335
|
const token = address3(a.token, "token");
|
|
22245
22336
|
this.#checkToken(token);
|
|
22337
|
+
if (a.toPrivateVault === true) {
|
|
22338
|
+
if (a.destination !== void 0) throw new RangeError("name a destination or toPrivateVault, not both");
|
|
22339
|
+
if (token !== this.#p.config.deployment.token) throw new RangeError("a private vault holds USDG: unshield another token to an address");
|
|
22340
|
+
if (this.#p.privateVault === void 0) throw new Error("private vaults are not set up here");
|
|
22341
|
+
}
|
|
22246
22342
|
this.#requireRelayer();
|
|
22247
22343
|
const prover = this.#prover();
|
|
22248
22344
|
await this.#refresh();
|
|
@@ -22250,10 +22346,11 @@ var init_private_balance = __esm({
|
|
|
22250
22346
|
const decimals = await tokenDecimals(this.#p.client, token);
|
|
22251
22347
|
const v = parseUnitsAmount(a.amount, decimals);
|
|
22252
22348
|
if (v === 0n) throw new RangeError("amount must be greater than zero");
|
|
22253
|
-
const recipient = a.destination === void 0 ? await this.#p.vaultFor(token).vaultAddress() : address3(a.destination, "destination");
|
|
22254
|
-
if (recipient === "0x0000000000000000000000000000000000000000") throw new RangeError("the destination must not be the zero address");
|
|
22255
22349
|
const { fee: priced } = await this.#price(token, "unshield");
|
|
22256
22350
|
const note = await this.#pick(token, v + priced, decimals, `${a.amount} and the relayer's fee`);
|
|
22351
|
+
const privateVault = a.toPrivateVault === true ? await this.#newPrivateVault() : null;
|
|
22352
|
+
const recipient = privateVault !== null ? privateVault.vault : a.destination === void 0 ? await this.#p.vaultFor(token).vaultAddress() : address3(a.destination, "destination");
|
|
22353
|
+
if (recipient === zeroAddress12) throw new RangeError("the destination must not be the zero address");
|
|
22257
22354
|
const sk = this.#sk();
|
|
22258
22355
|
const changeRho = this.#spendRho("change", note.commitment);
|
|
22259
22356
|
const r = await this.#spend(note, (fee) => v + fee, "unshield", async (fee, relayer, deadline, trees) => {
|
|
@@ -22264,7 +22361,101 @@ var init_private_balance = __esm({
|
|
|
22264
22361
|
const change = changeNote(spend, r.receipt, this.#pool);
|
|
22265
22362
|
await this.#addNotes(change === null ? [] : [change], [note.commitment]);
|
|
22266
22363
|
await this.#done(spend.terms.nullifier.slice(2, 18));
|
|
22267
|
-
return {
|
|
22364
|
+
return {
|
|
22365
|
+
status: "sent",
|
|
22366
|
+
txHash: r.txHash,
|
|
22367
|
+
amountIn: formatUnitsAmount(v, decimals),
|
|
22368
|
+
fee: formatUnitsAmount(r.fee, decimals),
|
|
22369
|
+
recipient,
|
|
22370
|
+
change: formatUnitsAmount(spend.change.amount, decimals),
|
|
22371
|
+
...privateVault === null ? {} : { privateVault: { index: privateVault.index, vault: privateVault.vault } }
|
|
22372
|
+
};
|
|
22373
|
+
});
|
|
22374
|
+
}
|
|
22375
|
+
/** The private vaults an unshield paid, read now. */
|
|
22376
|
+
privateVaults() {
|
|
22377
|
+
return this.#serial(() => this.#privateVaultViews());
|
|
22378
|
+
}
|
|
22379
|
+
/** The number of the private vault at `vault` kept here (found again from the seed first in a new OSTRA_HOME), or null. */
|
|
22380
|
+
async privateVaultIndex(vault) {
|
|
22381
|
+
const found = (await this.#serial(async () => {
|
|
22382
|
+
await this.#ensureRecovered();
|
|
22383
|
+
return this.#privateVaults();
|
|
22384
|
+
})).find((r) => r.vault.toLowerCase() === vault.toLowerCase());
|
|
22385
|
+
return found === void 0 ? null : BigInt(found.index);
|
|
22386
|
+
}
|
|
22387
|
+
/** Sends USDG from the private vault at `vault` (one an unshield paid), creating the vault first when it does not exist yet. */
|
|
22388
|
+
async withdrawPrivate(a) {
|
|
22389
|
+
const index = await this.privateVaultIndex(address3(a.vault, "vault"));
|
|
22390
|
+
if (index === null) throw new Error(`${a.vault} is not a private vault an unshield of this agent paid (private_balance lists them)`);
|
|
22391
|
+
if (this.#p.privateVault === void 0) throw new Error("private vaults are not set up here");
|
|
22392
|
+
this.#requireRelayer();
|
|
22393
|
+
const pv = await this.#p.privateVault(index);
|
|
22394
|
+
if (await pv.vaultAddress() !== getAddress25(a.vault)) throw new Error(`private vault ${index} is ${await pv.vaultAddress()}, not ${a.vault}`);
|
|
22395
|
+
const s = await pv.state();
|
|
22396
|
+
const created = !s.exists;
|
|
22397
|
+
if (created && this.#p.config.relayerUrl !== null) {
|
|
22398
|
+
const amount = parseTokenAmount(a.amount);
|
|
22399
|
+
if (s.balance < amount + RELAYER_CHANNEL_DEPOSIT) {
|
|
22400
|
+
throw new Error(`private vault ${index} holds ${formatTokenAmount(s.balance)}; its first withdrawal leaves ${formatTokenAmount(RELAYER_CHANNEL_DEPOSIT)} for its relayer channel, so at most ${formatTokenAmount(s.balance > RELAYER_CHANNEL_DEPOSIT ? s.balance - RELAYER_CHANNEL_DEPOSIT : 0n)} now; nothing was signed`);
|
|
22401
|
+
}
|
|
22402
|
+
}
|
|
22403
|
+
if (created) await pv.open();
|
|
22404
|
+
return { ...await pv.withdraw(a.destination, a.amount), ...created ? { created } : {} };
|
|
22405
|
+
}
|
|
22406
|
+
/** Auto-shield of the creator vault (off unless set). */
|
|
22407
|
+
autoShield() {
|
|
22408
|
+
return this.#serial(() => this.#autoShieldSetting());
|
|
22409
|
+
}
|
|
22410
|
+
#creator() {
|
|
22411
|
+
if (this.#p.creator === void 0) throw new Error("the creator vault is not set up here");
|
|
22412
|
+
return this.#p.creator;
|
|
22413
|
+
}
|
|
22414
|
+
async #autoShieldSetting() {
|
|
22415
|
+
const vault = await this.#creator().vaultAddress();
|
|
22416
|
+
const text2 = await this.#p.folder.read(AUTO_SHIELD);
|
|
22417
|
+
if (text2 === void 0) return { vault, enabled: false, keep: formatTokenAmount(RELAYER_CHANNEL_DEPOSIT) };
|
|
22418
|
+
const r = JSON.parse(text2);
|
|
22419
|
+
if (typeof r.enabled !== "boolean" || typeof r.keep !== "string") throw new Error(`${AUTO_SHIELD} is not an auto-shield setting`);
|
|
22420
|
+
return { vault, enabled: r.enabled, keep: formatTokenAmount(parseTokenAmount(r.keep)) };
|
|
22421
|
+
}
|
|
22422
|
+
/** Turns auto-shield of the creator vault on or off; `keep` (USDG, default RELAYER_CHANNEL_DEPOSIT) stays in the vault. */
|
|
22423
|
+
setAutoShield(enabled, keep) {
|
|
22424
|
+
return this.#serial(async () => {
|
|
22425
|
+
this.#creator();
|
|
22426
|
+
if (enabled) this.#prover();
|
|
22427
|
+
const kept = keep === void 0 ? RELAYER_CHANNEL_DEPOSIT : parseTokenAmount(keep);
|
|
22428
|
+
await this.#p.folder.ensure();
|
|
22429
|
+
await this.#p.folder.write(AUTO_SHIELD, `${JSON.stringify({ enabled, keep: formatTokenAmount(kept) })}
|
|
22430
|
+
`);
|
|
22431
|
+
return this.#autoShieldSetting();
|
|
22432
|
+
});
|
|
22433
|
+
}
|
|
22434
|
+
/**
|
|
22435
|
+
* One auto-shield step (the sweep): what the creator vault holds beyond `keep`, once at least AUTO_SHIELD_MIN, into
|
|
22436
|
+
* a note. Null when it is off, the creator vault does not exist yet (a launch creates it), it is below that, or a
|
|
22437
|
+
* shield from the creator vault is in flight that the vault no longer holds signed (refresh finishes it); one the
|
|
22438
|
+
* vault holds signed and not landed is sent again first (VaultService.operate), so a lost send does not stop it.
|
|
22439
|
+
*/
|
|
22440
|
+
autoShieldStep() {
|
|
22441
|
+
return this.#serial(async () => {
|
|
22442
|
+
if (this.#p.creator === void 0 || this.#p.prover === null) return null;
|
|
22443
|
+
const setting = await this.#autoShieldSetting();
|
|
22444
|
+
if (!setting.enabled) return null;
|
|
22445
|
+
let inFlight = false;
|
|
22446
|
+
for (const name of await this.#p.folder.list()) {
|
|
22447
|
+
if (!name.startsWith(PENDING_PREFIX)) continue;
|
|
22448
|
+
const text2 = await this.#p.folder.read(name);
|
|
22449
|
+
const p = text2 === void 0 ? null : JSON.parse(text2);
|
|
22450
|
+
if (p !== null && p.kind === "shield" && p.source === "creator") inFlight = true;
|
|
22451
|
+
}
|
|
22452
|
+
const token = this.#p.config.deployment.token;
|
|
22453
|
+
const s = await this.#creator().state();
|
|
22454
|
+
if (!s.exists) return null;
|
|
22455
|
+
if (inFlight && s.pendingAction === null) return null;
|
|
22456
|
+
const amount = s.balance - parseTokenAmount(setting.keep);
|
|
22457
|
+
if (amount < AUTO_SHIELD_MIN) return null;
|
|
22458
|
+
return this.#shieldFrom(this.#creator(), "creator", token, amount, await tokenDecimals(this.#p.client, token));
|
|
22268
22459
|
});
|
|
22269
22460
|
}
|
|
22270
22461
|
/** refresh() for the sweep: one at a time with the operations. Returns what is still in flight or failed. */
|
|
@@ -22303,7 +22494,7 @@ var init_private_balance = __esm({
|
|
|
22303
22494
|
await this.#addNotes([await shieldedNote(prover, sk, p.rho, await this.#p.client.getTransactionReceipt({ hash: l.transactionHash }), this.#pool)]);
|
|
22304
22495
|
found = true;
|
|
22305
22496
|
}
|
|
22306
|
-
const s = await this.#p.vaultFor(p.token).state();
|
|
22497
|
+
const s = await (p.source === "creator" ? this.#creator() : this.#p.vaultFor(p.token)).state();
|
|
22307
22498
|
if (s.nonce === null || s.nonce <= BigInt(p.vaultNonce)) return false;
|
|
22308
22499
|
if (await tokenBalance(this.#p.client, p.token, p.escrow) > 0n) {
|
|
22309
22500
|
try {
|
|
@@ -22356,7 +22547,7 @@ var init_seed_backup = __esm({
|
|
|
22356
22547
|
});
|
|
22357
22548
|
|
|
22358
22549
|
// ../core/src/sessions.ts
|
|
22359
|
-
import { bytesToHex as
|
|
22550
|
+
import { bytesToHex as bytesToHex13, getAddress as getAddress26, isAddress as isAddress12 } from "viem";
|
|
22360
22551
|
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
22361
22552
|
var init_sessions = __esm({
|
|
22362
22553
|
"../core/src/sessions.ts"() {
|
|
@@ -22368,7 +22559,7 @@ var init_sessions = __esm({
|
|
|
22368
22559
|
});
|
|
22369
22560
|
|
|
22370
22561
|
// ../core/src/shield.ts
|
|
22371
|
-
import { bytesToHex as
|
|
22562
|
+
import { bytesToHex as bytesToHex14, encodeFunctionData as encodeFunctionData13, getAddress as getAddress27, hexToBytes as hexToBytes8, parseSignature } from "viem";
|
|
22372
22563
|
function leafKeyIndex(attempt, j) {
|
|
22373
22564
|
if (!Number.isInteger(attempt) || attempt < 0 || attempt >= MAX_LEAF_ATTEMPTS) throw new RangeError(`leaf set ${String(attempt)} is out of range`);
|
|
22374
22565
|
if (!Number.isInteger(j) || j < 0 || j >= LEAF_KEYS_PER_ATTEMPT) throw new RangeError(`leaf ${String(j)} is out of range`);
|
|
@@ -22379,12 +22570,12 @@ function recordName(chainId, token, index) {
|
|
|
22379
22570
|
}
|
|
22380
22571
|
function decodeApproveMessage(message) {
|
|
22381
22572
|
if (message.length !== APPROVE_MESSAGE_LEN) throw new Error("not an approval message");
|
|
22382
|
-
const chainId = Number(BigInt(
|
|
22383
|
-
const pool = getAddress27(
|
|
22384
|
-
const key =
|
|
22385
|
-
const batchId = BigInt(
|
|
22386
|
-
const state =
|
|
22387
|
-
const nextKeyRoot =
|
|
22573
|
+
const chainId = Number(BigInt(bytesToHex14(message.subarray(20, 52))));
|
|
22574
|
+
const pool = getAddress27(bytesToHex14(message.subarray(52, 72)));
|
|
22575
|
+
const key = bytesToHex14(message.subarray(72, 104));
|
|
22576
|
+
const batchId = BigInt(bytesToHex14(message.subarray(104, 112)));
|
|
22577
|
+
const state = bytesToHex14(message.subarray(112, 144));
|
|
22578
|
+
const nextKeyRoot = bytesToHex14(message.subarray(144, 176));
|
|
22388
22579
|
const again = approvalMessage({ chainId, pool, key, batchId, state, nextKeyRoot });
|
|
22389
22580
|
if (again.length !== message.length || !again.every((x, i) => x === message[i])) throw new Error("not an approval message");
|
|
22390
22581
|
return { chainId, pool, key, batchId, state, nextKeyRoot };
|
|
@@ -22753,7 +22944,7 @@ var init_shield = __esm({
|
|
|
22753
22944
|
depositId: this.#keys.shieldDepositId(this.#config.chainId, this.#token(), index),
|
|
22754
22945
|
amount,
|
|
22755
22946
|
refundOwner: await this.#vault.vaultAddress(),
|
|
22756
|
-
approvalKeyRoot:
|
|
22947
|
+
approvalKeyRoot: bytesToHex14(this.#approvalKeys(record).keyRoot(0n))
|
|
22757
22948
|
};
|
|
22758
22949
|
if (depositKey(deposit.depositor, deposit.depositId) !== record.deposit) throw new Error("the deposit key does not match the record");
|
|
22759
22950
|
const validAfter = 0n;
|
|
@@ -22856,7 +23047,7 @@ var init_shield = __esm({
|
|
|
22856
23047
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
22857
23048
|
let index = -1n;
|
|
22858
23049
|
for (let i = 0n; i < 64n; i++) {
|
|
22859
|
-
if (
|
|
23050
|
+
if (bytesToHex14(keys.keyRoot(i)) === current.approvalKeyRoot.toLowerCase()) {
|
|
22860
23051
|
index = i;
|
|
22861
23052
|
break;
|
|
22862
23053
|
}
|
|
@@ -23161,7 +23352,7 @@ function view(r) {
|
|
|
23161
23352
|
txHash: r.txHash
|
|
23162
23353
|
};
|
|
23163
23354
|
}
|
|
23164
|
-
var UNSHIELD_DIR,
|
|
23355
|
+
var UNSHIELD_DIR, UNSHIELD_MAX_WINDOW_HOURS, LEAF_SPEND_GAS, UnshieldService;
|
|
23165
23356
|
var init_unshield = __esm({
|
|
23166
23357
|
"../core/src/unshield.ts"() {
|
|
23167
23358
|
"use strict";
|
|
@@ -23170,7 +23361,6 @@ var init_unshield = __esm({
|
|
|
23170
23361
|
init_amount();
|
|
23171
23362
|
init_random();
|
|
23172
23363
|
UNSHIELD_DIR = "unshield";
|
|
23173
|
-
UNSHIELD_DEFAULT_WINDOW_HOURS = 6;
|
|
23174
23364
|
UNSHIELD_MAX_WINDOW_HOURS = 24;
|
|
23175
23365
|
LEAF_SPEND_GAS = 300000n;
|
|
23176
23366
|
UnshieldService = class {
|
|
@@ -23354,7 +23544,7 @@ import {
|
|
|
23354
23544
|
getAddress as getAddress30,
|
|
23355
23545
|
maxUint256,
|
|
23356
23546
|
parseAbi as parseAbi4,
|
|
23357
|
-
zeroAddress as
|
|
23547
|
+
zeroAddress as zeroAddress13
|
|
23358
23548
|
} from "viem";
|
|
23359
23549
|
var MAX_UINT160, MAX_UINT128, universalRouterAbi, permit2Abi, wrappedErrorAbi;
|
|
23360
23550
|
var init_wallet_swap = __esm({
|
|
@@ -23420,7 +23610,7 @@ var init_src3 = __esm({
|
|
|
23420
23610
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
23421
23611
|
import { stat as stat2 } from "node:fs/promises";
|
|
23422
23612
|
import { join as join3 } from "node:path";
|
|
23423
|
-
import { bytesToHex as
|
|
23613
|
+
import { bytesToHex as bytesToHex15 } from "viem";
|
|
23424
23614
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
23425
23615
|
async function exists(path) {
|
|
23426
23616
|
try {
|
|
@@ -23458,7 +23648,7 @@ async function openKeys(home, escrow) {
|
|
|
23458
23648
|
await ensurePrivateDir(join3(home, WOTS_DIR));
|
|
23459
23649
|
return new Keys({
|
|
23460
23650
|
master: master.value,
|
|
23461
|
-
feePayer: privateKeyToAccount4(
|
|
23651
|
+
feePayer: privateKeyToAccount4(bytesToHex15(fee.value)),
|
|
23462
23652
|
store: new FileWotsStore(join3(home, WOTS_DIR)),
|
|
23463
23653
|
folder,
|
|
23464
23654
|
masterCreated: master.created,
|
|
@@ -23657,9 +23847,9 @@ var init_cli_prover = __esm({
|
|
|
23657
23847
|
});
|
|
23658
23848
|
|
|
23659
23849
|
// ../../ostra-swap/sdk/src/wasm-prover.ts
|
|
23660
|
-
import { bytesToHex as
|
|
23850
|
+
import { bytesToHex as bytesToHex16 } from "viem";
|
|
23661
23851
|
async function sha256Hex(bytes) {
|
|
23662
|
-
return
|
|
23852
|
+
return bytesToHex16(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
|
|
23663
23853
|
}
|
|
23664
23854
|
async function wasmProver(glue, bytes, sha2562) {
|
|
23665
23855
|
const got = await sha256Hex(bytes);
|
|
@@ -23821,7 +24011,10 @@ var init_agent = __esm({
|
|
|
23821
24011
|
vaultFor,
|
|
23822
24012
|
sender: this.sender,
|
|
23823
24013
|
folder: keys.folder.folder(PRIVATE_SWAP_DIR),
|
|
23824
|
-
localSpends: config.privateLocalSpends
|
|
24014
|
+
localSpends: config.privateLocalSpends,
|
|
24015
|
+
privateVault: async (index) => (await this.privateVault(index)).vault,
|
|
24016
|
+
takenPrivateIndexes: async () => (await this.unshield.list()).map((r) => BigInt(r.index)),
|
|
24017
|
+
creator: this.creator
|
|
23825
24018
|
});
|
|
23826
24019
|
}
|
|
23827
24020
|
}
|
|
@@ -23846,8 +24039,29 @@ var init_agent = __esm({
|
|
|
23846
24039
|
const creatorKeys = await keys.forCreatorVault(config.chainId, config.deployment.token);
|
|
23847
24040
|
const agent = new _Agent({ config, client, transport, keys, creatorKeys, escrow, log: log2 });
|
|
23848
24041
|
await agent.#loadSeller();
|
|
24042
|
+
await agent.#moveAutoShield();
|
|
23849
24043
|
return agent;
|
|
23850
24044
|
}
|
|
24045
|
+
/**
|
|
24046
|
+
* An auto-shield of the creator vault set on the shielded pool's leaves (before 0.1.6) is turned on for the private
|
|
24047
|
+
* notes with the same `keep`, then off there; without Ostra Swap or a prover it is left as it is (and logged) for a
|
|
24048
|
+
* start that has them. It never stops the server from starting.
|
|
24049
|
+
*/
|
|
24050
|
+
async #moveAutoShield() {
|
|
24051
|
+
try {
|
|
24052
|
+
const old = await this.creatorShield.autoShield();
|
|
24053
|
+
if (!old.enabled) return;
|
|
24054
|
+
if (this.privateBalance === null || this.config.prover === null) {
|
|
24055
|
+
this.#log("auto_shield_not_moved", { vault: old.vault, reason: "private notes need Ostra Swap and a prover" });
|
|
24056
|
+
return;
|
|
24057
|
+
}
|
|
24058
|
+
await this.privateBalance.setAutoShield(true, old.keep);
|
|
24059
|
+
await this.creatorShield.setAutoShield(false);
|
|
24060
|
+
this.#log("auto_shield_moved", { vault: old.vault, keep: old.keep });
|
|
24061
|
+
} catch (err) {
|
|
24062
|
+
this.#log("auto_shield_not_moved", { error: err instanceof Error ? err.message : String(err) });
|
|
24063
|
+
}
|
|
24064
|
+
}
|
|
23851
24065
|
/** Private vault `index` (unshield made it), with its own records and relayer client. */
|
|
23852
24066
|
async privateVault(index) {
|
|
23853
24067
|
const known = this.#privates.get(index);
|
|
@@ -23958,7 +24172,10 @@ var init_agent = __esm({
|
|
|
23958
24172
|
}
|
|
23959
24173
|
await step2("unshield", () => this.unshield.sweep());
|
|
23960
24174
|
if (this.swap !== null) await step2("ostra swap orders", () => this.swap.finishPending());
|
|
23961
|
-
if (this.privateBalance !== null && this.config.prover !== null)
|
|
24175
|
+
if (this.privateBalance !== null && this.config.prover !== null) {
|
|
24176
|
+
await step2("private notes", () => this.privateBalance.refresh());
|
|
24177
|
+
await step2("private auto-shield", () => this.privateBalance.autoShieldStep());
|
|
24178
|
+
}
|
|
23962
24179
|
} finally {
|
|
23963
24180
|
this.#sweeping = false;
|
|
23964
24181
|
}
|
|
@@ -24188,19 +24405,6 @@ function registerTools(server, agent) {
|
|
|
24188
24405
|
if (!await agent.sellerReady()) throw new Error("this agent has no seller vault yet; call accept_payments first");
|
|
24189
24406
|
return agent.seller.withdraw(a.destination ?? await agent.buyer.vaultAddress(), a.amount);
|
|
24190
24407
|
});
|
|
24191
|
-
tool("shield", "Deposits `leaves` standard leaves from the vault into the shielded pool (through the Ostra aggregator's batches).", { leaves: z.number().int().min(1).max(64) }, (a) => agent.shield.shield(a.leaves));
|
|
24192
|
-
tool(
|
|
24193
|
-
"unshield",
|
|
24194
|
-
`Spends \`leaves\` shielded leaves, each into a new private vault at a random time within windowHours (default ${UNSHIELD_DEFAULT_WINDOW_HOURS}, at most ${UNSHIELD_MAX_WINDOW_HOURS}; 0: now, which lets the spends be timed together).`,
|
|
24195
|
-
{ leaves: z.number().int().min(1).max(64), windowHours: z.number().min(0).max(UNSHIELD_MAX_WINDOW_HOURS).default(UNSHIELD_DEFAULT_WINDOW_HOURS) },
|
|
24196
|
-
(a) => agent.unshield.unshield(a.leaves, a.windowHours)
|
|
24197
|
-
);
|
|
24198
|
-
tool("shield_status", "Shield deposits, scheduled unshields and the private vaults.", {}, async () => ({ deposits: await agent.shield.list(), unshields: await agent.unshield.list(), privateVaults: await agent.unshield.vaults() }));
|
|
24199
|
-
tool("auto_shield", "Turns the creator vault's auto-shield on or off (it shields what the vault holds beyond `keep` USDG).", { enabled: z.boolean(), keep: usdg.optional() }, (a) => agent.creatorShield.setAutoShield(a.enabled, a.keep));
|
|
24200
|
-
tool("private_withdraw", "Sends USDG from one of the agent's private vaults.", { vault: address5, destination: address5, amount: usdg }, async (a) => {
|
|
24201
|
-
const ctx = await agent.privateVault(await agent.unshield.indexOf(a.vault));
|
|
24202
|
-
return ctx.vault.withdraw(a.destination, a.amount);
|
|
24203
|
-
});
|
|
24204
24408
|
tool(
|
|
24205
24409
|
"ostra_swap_quote",
|
|
24206
24410
|
"Quotes a swap on Ostra Swap from the agent's vault of tokenIn to its vault of tokenOut (USDG, WETH, OSTRA, listed stock tokens, tokens the Ostra launchpad launched), routed through Uniswap v3 and v4 against USDG or WETH: expected and minimum output, the hub and Ostra's fee (lower with OSTRA locked for the receiving vault). Nothing is signed.",
|
|
@@ -24221,11 +24425,11 @@ function registerTools(server, agent) {
|
|
|
24221
24425
|
);
|
|
24222
24426
|
tool(
|
|
24223
24427
|
"private_recover",
|
|
24224
|
-
"Finds the agent's private notes again from its seed and the pool's events alone (its shields, then each spend's change and swap output), and keeps the unspent ones: for a seed restored on another machine, or notes kept here that look wrong. It runs by itself on first use in a new OSTRA_HOME.",
|
|
24428
|
+
"Finds the agent's private notes again from its seed and the pool's events alone (its shields, then each spend's change and swap output), and keeps the unspent ones, with the private vaults its unshields paid: for a seed restored on another machine, or notes kept here that look wrong. It runs by itself on first use in a new OSTRA_HOME.",
|
|
24225
24429
|
{},
|
|
24226
24430
|
() => notes().recover()
|
|
24227
24431
|
);
|
|
24228
|
-
tool("private_balance", "The agent's private notes in OstraPrivatePool: per token the total, the number of notes and the largest one (a spend uses one note).", {}, () => notes().balance());
|
|
24432
|
+
tool("private_balance", "The agent's private notes in OstraPrivatePool: per token the total, the number of notes and the largest one (a spend uses one note); and its private vaults with what they hold.", {}, () => notes().balance());
|
|
24229
24433
|
tool(
|
|
24230
24434
|
"private_swap",
|
|
24231
24435
|
"Swaps `amount` of tokenIn held in a private note into a new private note of tokenOut, through Ostra Swap, with a zero-knowledge proof made on this machine. The Ostra relayer sends it and is paid from the note (a fee in tokenIn); the change stays private.",
|
|
@@ -24234,9 +24438,29 @@ function registerTools(server, agent) {
|
|
|
24234
24438
|
);
|
|
24235
24439
|
tool(
|
|
24236
24440
|
"private_unshield",
|
|
24237
|
-
"Pays `amount` of a token out of a private note to an address, with a zero-knowledge proof made on this machine. The Ostra relayer sends it and is paid from the note; the change stays private. The default destination is the agent's vault of that token, which an observer can link to the vault that shielded
|
|
24238
|
-
{ token: swapToken, amount: units, destination: address5.optional() },
|
|
24239
|
-
(a) => notes().unshield({ token: tokenAddress(a.token), amount: a.amount, ...opt("destination", a.destination) })
|
|
24441
|
+
"Pays `amount` of a token out of a private note to an address, with a zero-knowledge proof made on this machine. The Ostra relayer sends it and is paid from the note; the change stays private. The default destination is the agent's vault of that token, which an observer can link to the vault that shielded. With toPrivateVault (USDG only), it pays a new private vault of the agent instead, its own post-quantum vault found again from the seed (private_recover), which private_withdraw spends from. The amount is public, so a round amount, or a note split over several unshields, is harder to match to its shield.",
|
|
24442
|
+
{ token: swapToken, amount: units, destination: address5.optional(), toPrivateVault: z.boolean().optional() },
|
|
24443
|
+
(a) => notes().unshield({ token: tokenAddress(a.token), amount: a.amount, ...opt("destination", a.destination), ...opt("toPrivateVault", a.toPrivateVault) })
|
|
24444
|
+
);
|
|
24445
|
+
tool(
|
|
24446
|
+
"private_withdraw",
|
|
24447
|
+
"Sends USDG from one of the agent's private vaults (private_balance lists them) to an address; a private vault an unshield paid is created on this first use.",
|
|
24448
|
+
{ vault: address5, destination: address5, amount: usdg },
|
|
24449
|
+
async (a) => {
|
|
24450
|
+
const old = (await agent.unshield.vaults()).find((v) => v.vault.toLowerCase() === a.vault.toLowerCase());
|
|
24451
|
+
if (old !== void 0) return (await agent.privateVault(old.index)).vault.withdraw(a.destination, a.amount);
|
|
24452
|
+
if (agent.privateBalance !== null && await agent.privateBalance.privateVaultIndex(a.vault) !== null) return agent.privateBalance.withdrawPrivate(a);
|
|
24453
|
+
throw new Error(`${a.vault} is not a private vault of this agent (private_balance lists them)`);
|
|
24454
|
+
}
|
|
24455
|
+
);
|
|
24456
|
+
tool(
|
|
24457
|
+
"auto_shield",
|
|
24458
|
+
"Turns the creator vault's auto-shield on or off: every minute, everything the creator vault holds beyond `keep` USDG (default 1, which pays the relayer for its own transactions), once that is at least 1 USDG, is shielded into a private note (private_balance): launch payouts, and USDG put there for a launch too, so turn it off before funding a launch. The shield shows the creator vault it came from.",
|
|
24459
|
+
{ enabled: z.boolean(), keep: usdg.optional() },
|
|
24460
|
+
async (a) => {
|
|
24461
|
+
await agent.creatorShield.setAutoShield(false);
|
|
24462
|
+
return notes().setAutoShield(a.enabled, a.keep);
|
|
24463
|
+
}
|
|
24240
24464
|
);
|
|
24241
24465
|
tool("recovery_status", "The vault's guardians, recovery policy and any recovery waiting.", {}, () => agent.buyer.recovery());
|
|
24242
24466
|
tool("set_guardian", "Adds or removes a guardian of the vault (another Ostra vault keeps the recovery post-quantum).", { guardian: address5, enabled: z.boolean() }, (a) => agent.buyer.setGuardian(a.guardian, a.enabled));
|
|
@@ -24264,7 +24488,7 @@ function registerTools(server, agent) {
|
|
|
24264
24488
|
(a) => lp().sell({ token: a.token, amount: a.amount, ...a.slippageBps === void 0 ? {} : { slippageBps: a.slippageBps }, ...a.referrer === void 0 ? {} : { referrer: a.referrer } })
|
|
24265
24489
|
);
|
|
24266
24490
|
tool("token_vault", "The agent's vault for a launched token: address, balance, dividends to claim.", { token: address5 }, (a) => lp().tokenVaultInfo(a.token));
|
|
24267
|
-
tool("transfer_token", "Sends a launched token from the agent's
|
|
24491
|
+
tool("transfer_token", "Sends a token other than USDG (a launched token, WETH, OSTRA or a stock token) from the agent's vault of it to an address; USDG goes with withdraw.", { token: address5, destination: address5, amount: units }, (a) => lp().transferToken(a));
|
|
24268
24492
|
tool("claim_dividends", "Claims the dividends the agent's token vault earned, into the agent's vault of the dividend token.", { token: address5 }, (a) => lp().claimDividends(a.token));
|
|
24269
24493
|
tool(
|
|
24270
24494
|
"claim_referral",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ostraagent/mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Ostra Agent's MCP server: an AI agent's own post-quantum USDG vault on Robinhood Chain, with payments,
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Ostra Agent's MCP server: an AI agent's own post-quantum USDG vault on Robinhood Chain, with payments, private balances and the Ostra launchpad. The agent needs no ETH.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
7
7
|
"model-context-protocol",
|