openzoo 0.3.0 → 0.4.0
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 +8 -4
- package/bin/openzoo.js +1 -1
- package/lib/config.js +46 -0
- package/lib/demo.js +2 -2
- package/lib/info.js +10 -10
- package/lib/mcp.js +8 -6
- package/lib/pay.js +2 -2
- package/lib/proxy.js +20 -6
- package/lib/wrap.js +40 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ bound once in 14.8s: 3.7MB → context ctx_01KZZY8YQE…
|
|
|
96
96
|
quote for the ask: $0.000480 · pricing=markup (3× a tiny body — the 3.7MB corpus is not re-priced)
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
If the wallet is funded with USDC it pays (capped at `OPENZOO_DEMO_MAX_USD`, default $0.01) and prints the answer, the tokens the model actually read, and the receipt. If not, it prints exactly what to fund. Long waits (the one-time upload, pricing, payment, the answer) show a live progress line with stage + elapsed seconds.
|
|
99
|
+
If the wallet is funded with USDC or TOKEN it pays (capped at `OPENZOO_DEMO_MAX_USD`, default $0.01) and prints the answer, the tokens the model actually read, and the receipt. If not, it prints exactly what to fund. Long waits (the one-time upload, pricing, payment, the answer) show a live progress line with stage + elapsed seconds.
|
|
100
100
|
|
|
101
101
|
**Run it twice.** The second run finds the corpus in the local manifest and never uploads it:
|
|
102
102
|
|
|
@@ -129,10 +129,14 @@ Opt out with `OPENZOO_NO_CONTEXT_CACHE=1` (always ship the full body); tune the
|
|
|
129
129
|
## The wallet model
|
|
130
130
|
|
|
131
131
|
- **Burner, local, yours.** A keypair in `~/.openzoo/wallet.json`, created on first run, chmod 600. Keys never leave your machine — the zoo only ever sees signed transfers.
|
|
132
|
-
- **Fund it with
|
|
132
|
+
- **Fund it with USDC or TOKEN.** Send a few cents of either to the address `npx openzoo address` prints:
|
|
133
|
+
- **USDC** — `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`
|
|
134
|
+
- **TOKEN** — `EVULoNF4DeMBN4dGiZiDfpiiTfNZgoCvXWWgaV3epump`
|
|
135
|
+
|
|
136
|
+
Both are first-class: each is the underlying of a live Solana rail the zoo quotes (USDC → yUSDCx, TOKEN → wTOKENx). That's the whole funding story — the shim converts whichever the 402 quotes internally, at payment time, for exactly the amount needed.
|
|
133
137
|
- **SOL is optional but nice.** The gateway sponsors payment-transaction fees. If the wallet holds a pinch of SOL (~0.003), the internal conversion settles as its own transaction first; with zero SOL the conversion rides inside the gateway-sponsored payment transaction instead.
|
|
134
138
|
- **Spend caps.** The proxy refuses any single quote above `OPENZOO_MAX_USD_PER_CALL` (default $0.50).
|
|
135
|
-
- `npx openzoo balance` / `npx openzoo address` — check funds (USDC + SOL + USD value) / print the address.
|
|
139
|
+
- `npx openzoo balance` / `npx openzoo address` — check funds (USDC + TOKEN + SOL + USD value) / print the address.
|
|
136
140
|
|
|
137
141
|
## Honest pricing note
|
|
138
142
|
|
|
@@ -147,7 +151,7 @@ The receipt names which base you got; `extra.directUsd` / `extra.savesVsDirect`
|
|
|
147
151
|
|
|
148
152
|
| rail | network | status |
|
|
149
153
|
|---|---|---|
|
|
150
|
-
| **Solana** (default) | `solana:5eykt…` | **live** — Token-2022 `TransferChecked`, partial-signed, gateway pays fees. Tested end-to-end against the production 402. Settlement uses a wrapped settlement mint as internal plumbing; you only ever hold and send USDC. |
|
|
154
|
+
| **Solana** (default) | `solana:5eykt…` | **live** — Token-2022 `TransferChecked`, partial-signed, gateway pays fees. Tested end-to-end against the production 402. Settlement uses a wrapped settlement mint as internal plumbing; you only ever hold and send USDC or TOKEN. |
|
|
151
155
|
| Base | `eip155:8453` | implemented (standard x402 EIP-3009 `transferWithAuthorization`), **live-untested** — the zoo's 402s currently offer only Solana rows. |
|
|
152
156
|
| Robinhood Chain | `eip155:4663` | experimental, behind `OPENZOO_ENABLE_RH=1` — the zoo ships this rail dark and facilitator settlement there is unverified. |
|
|
153
157
|
|
package/bin/openzoo.js
CHANGED
|
@@ -10,7 +10,7 @@ usage:
|
|
|
10
10
|
(run it twice — the second run reuses the bound corpus and is near-free)
|
|
11
11
|
npx openzoo contexts list corpora bound to the zoo (never re-uploaded)
|
|
12
12
|
npx openzoo contexts --forget <hash|all> drop manifest entries
|
|
13
|
-
npx openzoo balance wallet balance (USDC + SOL, with USD value)
|
|
13
|
+
npx openzoo balance wallet balance (USDC + TOKEN + SOL, with USD value)
|
|
14
14
|
npx openzoo address print the funding address
|
|
15
15
|
npx openzoo help this text
|
|
16
16
|
|
package/lib/config.js
CHANGED
|
@@ -13,3 +13,49 @@ export const config = {
|
|
|
13
13
|
// Demo will only actually pay if the quote is at or below this.
|
|
14
14
|
demoMaxUsd: Number(process.env.OPENZOO_DEMO_MAX_USD || 0.01),
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Assets a user can fund the burner wallet with. Both are first-class: each is
|
|
19
|
+
* the underlying of a live Solana rail the zoo quotes (USDC -> yUSDCx,
|
|
20
|
+
* TOKEN -> wTOKENx), and the shim wraps whichever one it needs at payment time,
|
|
21
|
+
* for exactly the amount needed. Never mention one of these without the other.
|
|
22
|
+
*/
|
|
23
|
+
export const FUNDING_ASSETS = [
|
|
24
|
+
{ symbol: 'USDC', mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' },
|
|
25
|
+
{ symbol: 'TOKEN', mint: 'EVULoNF4DeMBN4dGiZiDfpiiTfNZgoCvXWWgaV3epump' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const USDC_MINT = FUNDING_ASSETS[0].mint;
|
|
29
|
+
export const TOKEN_MINT = FUNDING_ASSETS[1].mint;
|
|
30
|
+
|
|
31
|
+
/** The one canonical way to tell a user how to fund. */
|
|
32
|
+
export function fundingLine(address) {
|
|
33
|
+
return `send a few cents of USDC (${USDC_MINT}) or TOKEN (${TOKEN_MINT}) to ${address}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What the resource will actually settle, right now, read off a live 402 —
|
|
38
|
+
* never a hardcoded claim. Rails the code implements but the zoo is not
|
|
39
|
+
* currently offering are reported separately so nobody funds a dead lane.
|
|
40
|
+
* Underlying assets only: the wrapped settlement mints are internal plumbing.
|
|
41
|
+
*/
|
|
42
|
+
export async function liveRails() {
|
|
43
|
+
const r = await fetch(`${config.apiBase}/v1/chat/completions`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'content-type': 'application/json' },
|
|
46
|
+
body: JSON.stringify({ model: 'nvidia/nemotron-3.5-lightning', messages: [{ role: 'user', content: 'ping' }], max_tokens: 1 }),
|
|
47
|
+
});
|
|
48
|
+
if (r.status !== 402) { await r.text().catch(() => {}); return null; }
|
|
49
|
+
const body = await r.json();
|
|
50
|
+
const rows = Array.isArray(body?.accepts) ? body.accepts : [];
|
|
51
|
+
const live = new Set();
|
|
52
|
+
for (const a of rows) {
|
|
53
|
+
const net = a?.network || '';
|
|
54
|
+
if (net.startsWith('solana:')) live.add('solana');
|
|
55
|
+
else if (/^eip155:8453$/.test(net)) live.add('base');
|
|
56
|
+
else if (/^eip155:4663$/.test(net)) live.add('robinhood');
|
|
57
|
+
else if (net) live.add(net);
|
|
58
|
+
}
|
|
59
|
+
const implemented = ['solana', 'base', 'robinhood'];
|
|
60
|
+
return { live: [...live], dark: implemented.filter((x) => !live.has(x)) };
|
|
61
|
+
}
|
package/lib/demo.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { config } from './config.js';
|
|
1
|
+
import { config, fundingLine } from './config.js';
|
|
2
2
|
import { PayClient, UnderfundedError } from './pay.js';
|
|
3
3
|
import { parse402, pickAccept, tokenBalance } from './x402.js';
|
|
4
4
|
import { startSpinner, postWithUploadSignal } from './spinner.js';
|
|
@@ -133,7 +133,7 @@ export async function runDemo() {
|
|
|
133
133
|
console.log('3) wallet not funded yet, so stopping before payment.');
|
|
134
134
|
console.log(` quote : ≈ $${Number(x.billedUsd).toFixed(6)}`);
|
|
135
135
|
console.log(` wallet: $${(cov.usdcUi ?? 0).toFixed(2)} USDC`);
|
|
136
|
-
console.log(` fund :
|
|
136
|
+
console.log(` fund : ${fundingLine(pub)}`);
|
|
137
137
|
console.log(' then re-run `npx openzoo demo`.');
|
|
138
138
|
return;
|
|
139
139
|
}
|
package/lib/info.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { Connection } from '@solana/web3.js';
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
|
-
import { config } from './config.js';
|
|
3
|
+
import { config, FUNDING_ASSETS, fundingLine } from './config.js';
|
|
4
4
|
import { loadOrCreateWallet } from './wallet.js';
|
|
5
5
|
import { tokenBalance } from './x402.js';
|
|
6
6
|
|
|
7
|
-
const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
8
|
-
|
|
9
7
|
export function printAddress() {
|
|
10
8
|
const { keypair, evmPrivateKey, created, path } = loadOrCreateWallet();
|
|
11
9
|
if (created) console.log(`new burner wallet created at ${path} (chmod 600)`);
|
|
@@ -16,16 +14,18 @@ export function printAddress() {
|
|
|
16
14
|
export async function printBalance() {
|
|
17
15
|
const { keypair } = loadOrCreateWallet();
|
|
18
16
|
const connection = new Connection(config.rpcUrl, 'confirmed');
|
|
19
|
-
const [
|
|
20
|
-
tokenBalance(connection, keypair.publicKey,
|
|
17
|
+
const [balances, lamports] = await Promise.all([
|
|
18
|
+
Promise.all(FUNDING_ASSETS.map((a) => tokenBalance(connection, keypair.publicKey, a.mint))),
|
|
21
19
|
connection.getBalance(keypair.publicKey),
|
|
22
20
|
]);
|
|
23
|
-
const usdcUi =
|
|
21
|
+
const usdcUi = balances[0].ui ?? 0;
|
|
24
22
|
console.log(`wallet : ${keypair.publicKey.toBase58()}`);
|
|
25
|
-
|
|
23
|
+
FUNDING_ASSETS.forEach((a, i) => {
|
|
24
|
+
console.log(`${a.symbol.padEnd(7)}: ${balances[i].ui ?? 0}`);
|
|
25
|
+
});
|
|
26
26
|
console.log(`SOL : ${lamports / 1e9}`);
|
|
27
|
-
console.log(`value : ≈ $${usdcUi.toFixed(2)}`);
|
|
28
|
-
if (!
|
|
29
|
-
console.log(`fund :
|
|
27
|
+
console.log(`value : ≈ $${usdcUi.toFixed(2)} (USDC leg; TOKEN valued at the 402)`);
|
|
28
|
+
if (!balances.some((b) => b.raw)) {
|
|
29
|
+
console.log(`fund : ${fundingLine(keypair.publicKey.toBase58())}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
package/lib/mcp.js
CHANGED
|
@@ -2,14 +2,13 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { config } from './config.js';
|
|
5
|
+
import { config, FUNDING_ASSETS } from './config.js';
|
|
6
6
|
import { PayClient, QuoteTooHighError, UnderfundedError } from './pay.js';
|
|
7
7
|
import { tokenBalance } from './x402.js';
|
|
8
8
|
import { askWithContext, contextCacheDisabled, BIND_MIN_CHARS } from './hrr.js';
|
|
9
9
|
import { listContexts } from './contexts.js';
|
|
10
10
|
|
|
11
11
|
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
12
|
-
const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
13
12
|
const DEFAULT_MODEL = process.env.OPENZOO_DEMO_MODEL || 'nvidia/nemotron-3.5-lightning';
|
|
14
13
|
|
|
15
14
|
function text(obj) {
|
|
@@ -89,14 +88,17 @@ export async function startMcp() {
|
|
|
89
88
|
description: 'The local burner wallet: funding address, balances, and this session\'s payment receipts.',
|
|
90
89
|
inputSchema: {},
|
|
91
90
|
}, async () => {
|
|
92
|
-
const [
|
|
93
|
-
tokenBalance(client.connection, client.keypair.publicKey,
|
|
91
|
+
const [bals, lamports] = await Promise.all([
|
|
92
|
+
Promise.all(FUNDING_ASSETS.map((a) => tokenBalance(client.connection, client.keypair.publicKey, a.mint))),
|
|
94
93
|
client.connection.getBalance(client.keypair.publicKey),
|
|
95
94
|
]);
|
|
95
|
+
const balances = { SOL: lamports / 1e9 };
|
|
96
|
+
FUNDING_ASSETS.forEach((a, i) => { balances[a.symbol] = bals[i].ui ?? 0; });
|
|
96
97
|
return text({
|
|
97
98
|
solanaAddress: client.address,
|
|
98
|
-
fundWith:
|
|
99
|
-
|
|
99
|
+
fundWith: FUNDING_ASSETS.map((a) => `${a.symbol} (${a.mint})`).join(' or '),
|
|
100
|
+
fundHint: 'send a few cents of either to this address — the shim wraps whichever the 402 quotes, at payment time',
|
|
101
|
+
balances,
|
|
100
102
|
receipts: client.receipts.map((r) => ({ at: r.at, line: r.line })),
|
|
101
103
|
});
|
|
102
104
|
});
|
package/lib/pay.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
2
|
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
3
|
-
import { config } from './config.js';
|
|
3
|
+
import { config, fundingLine } from './config.js';
|
|
4
4
|
import { loadOrCreateWallet } from './wallet.js';
|
|
5
5
|
import {
|
|
6
6
|
parse402, pickAccept, railOf, buildPaymentOnline, tokenBalance,
|
|
@@ -24,7 +24,7 @@ export class UnderfundedError extends Error {
|
|
|
24
24
|
const usd = Number(accept?.extra?.billedUsd);
|
|
25
25
|
const needs = Number.isFinite(usd) ? `this call needs ≈$${usd.toFixed(6)}` : 'this call needs more than the wallet holds';
|
|
26
26
|
const holds = usdcUi != null ? ` — the wallet holds $${Number(usdcUi).toFixed(2)} USDC` : '';
|
|
27
|
-
super(`openzoo wallet underfunded: ${needs}${holds}.
|
|
27
|
+
super(`openzoo wallet underfunded: ${needs}${holds}. ${fundingLine(address).replace(/^s/, 'S')}.`);
|
|
28
28
|
this.accept = accept;
|
|
29
29
|
this.usdcUi = usdcUi;
|
|
30
30
|
this.address = address;
|
package/lib/proxy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
|
-
import { config } from './config.js';
|
|
3
|
+
import { config, FUNDING_ASSETS, fundingLine, liveRails } from './config.js';
|
|
4
4
|
import { PayClient, QuoteTooHighError, UnderfundedError } from './pay.js';
|
|
5
5
|
import { tokenBalance } from './x402.js';
|
|
6
6
|
import { bindCorpus, contextCacheDisabled, BIND_MIN_CHARS } from './hrr.js';
|
|
@@ -179,13 +179,27 @@ export async function startProxy({ silent = false } = {}) {
|
|
|
179
179
|
if (client.walletCreated) console.log(`new burner wallet created at ${client.walletPath} (chmod 600)`);
|
|
180
180
|
console.log(`wallet (fund me): ${client.address}`);
|
|
181
181
|
try {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
182
|
+
const bals = await Promise.all(
|
|
183
|
+
FUNDING_ASSETS.map((a) => tokenBalance(client.connection, client.keypair.publicKey, a.mint)),
|
|
184
|
+
);
|
|
185
|
+
const parts = FUNDING_ASSETS.map((a, i) => `${bals[i].ui ?? 0} ${a.symbol}`);
|
|
186
|
+
console.log(`balance: ${parts.join(' · ')}`);
|
|
187
|
+
if (!bals.some((b) => b.raw)) {
|
|
188
|
+
console.log(`fund it: ${fundingLine('the address above')} — a few cents goes a long way.`);
|
|
187
189
|
}
|
|
188
190
|
} catch { /* RPC hiccup: balance is advisory */ }
|
|
191
|
+
// Which rails the zoo will actually settle right now, straight off a live
|
|
192
|
+
// 402 — so nobody funds a lane the resource is not currently offering.
|
|
193
|
+
try {
|
|
194
|
+
const rails = await liveRails();
|
|
195
|
+
if (rails) {
|
|
196
|
+
console.log(`rails live now: ${rails.live.join(' · ')} (fund with USDC or TOKEN on solana)`);
|
|
197
|
+
if (rails.dark.length) {
|
|
198
|
+
const rh = rails.dark.includes('robinhood') ? ' — robinhood also needs OPENZOO_ENABLE_RH=1' : '';
|
|
199
|
+
console.log(`rails implemented but not offered by the zoo right now: ${rails.dark.join(' · ')}${rh}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch { /* quote probe is advisory */ }
|
|
189
203
|
console.log('');
|
|
190
204
|
console.log('point any OpenAI-compatible harness at:');
|
|
191
205
|
console.log(` base_url = http://localhost:${config.port}/v1`);
|
package/lib/wrap.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Internal funding plumbing for Solana 402 rails.
|
|
3
3
|
*
|
|
4
4
|
* The zoo's Solana quotes settle in a NAV-wrapped Token-2022 mint issued by
|
|
5
|
-
* the wrap-nav program (FrSER…). Users only ever hold and send plain USDC
|
|
5
|
+
* the wrap-nav program (FrSER…). Users only ever hold and send plain USDC or
|
|
6
|
+
* TOKEN (EVULoNF4DeMBN4dGiZiDfpiiTfNZgoCvXWWgaV3epump) —
|
|
6
7
|
* this module converts the exact amount needed, at payment time, inside the
|
|
7
8
|
* shim. Nothing here is user-facing; never surface wrapped tickers or mints
|
|
8
9
|
* in messages that reach the user.
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
* authority = PDA(['mint_authority', wrappedMint]); escrow = ATA(underlying,
|
|
19
20
|
* authority); registry = PDA(['backpointer', wrappedMint]) when present.
|
|
20
21
|
*/
|
|
21
|
-
import { PublicKey, Transaction, TransactionInstruction
|
|
22
|
+
import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js';
|
|
22
23
|
import {
|
|
23
24
|
TOKEN_PROGRAM_ID,
|
|
24
25
|
TOKEN_2022_PROGRAM_ID,
|
|
@@ -200,9 +201,45 @@ export function buildWrapInstructions({ pool, owner, depositRaw, rentPayer = own
|
|
|
200
201
|
];
|
|
201
202
|
}
|
|
202
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Confirm a signature by polling getSignatureStatuses over HTTP. Deliberately
|
|
206
|
+
* avoids connection.confirmTransaction: that path opens a websocket
|
|
207
|
+
* signatureSubscribe, and some RPC providers (fluxrpc) send a notification
|
|
208
|
+
* shape web3.js's schema rejects — the StructError fires inside the ws
|
|
209
|
+
* callback where no caller can catch it and takes down the whole process.
|
|
210
|
+
*/
|
|
211
|
+
export async function confirmSignatureByPolling(connection, signature, {
|
|
212
|
+
commitment = 'confirmed',
|
|
213
|
+
timeoutMs = 90000,
|
|
214
|
+
pollMs = 1500,
|
|
215
|
+
} = {}) {
|
|
216
|
+
const accept = commitment === 'finalized' ? ['finalized'] : ['confirmed', 'finalized'];
|
|
217
|
+
const deadline = Date.now() + timeoutMs;
|
|
218
|
+
for (;;) {
|
|
219
|
+
let status = null;
|
|
220
|
+
try {
|
|
221
|
+
const res = await connection.getSignatureStatuses([signature]);
|
|
222
|
+
status = res?.value?.[0] ?? null;
|
|
223
|
+
} catch { /* transient RPC hiccup — keep polling until the deadline */ }
|
|
224
|
+
if (status) {
|
|
225
|
+
if (status.err) throw new Error(`transaction ${signature} failed on-chain: ${JSON.stringify(status.err)}`);
|
|
226
|
+
if (accept.includes(status.confirmationStatus)) return signature;
|
|
227
|
+
}
|
|
228
|
+
if (Date.now() >= deadline) {
|
|
229
|
+
throw new Error(`timed out after ${timeoutMs}ms waiting for confirmation of ${signature}`);
|
|
230
|
+
}
|
|
231
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
203
235
|
/** Send a self-paid conversion tx and wait for confirmation. Returns the signature. */
|
|
204
236
|
export async function sendWrap(connection, keypair, pool, depositRaw) {
|
|
205
237
|
const ixs = buildWrapInstructions({ pool, owner: keypair.publicKey, depositRaw });
|
|
206
238
|
const tx = new Transaction().add(...ixs);
|
|
207
|
-
|
|
239
|
+
const { blockhash } = await connection.getLatestBlockhash('confirmed');
|
|
240
|
+
tx.recentBlockhash = blockhash;
|
|
241
|
+
tx.feePayer = keypair.publicKey;
|
|
242
|
+
tx.sign(keypair);
|
|
243
|
+
const sig = await connection.sendRawTransaction(tx.serialize());
|
|
244
|
+
return confirmSignatureByPolling(connection, sig, { commitment: 'confirmed' });
|
|
208
245
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana rail live; Base/Robinhood rails experimental.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|