@blockrun/franklin 3.14.0 → 3.15.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.
@@ -213,11 +213,20 @@ You run on the BlockRun AI Gateway. When the user asks you to "test the BlockRun
213
213
  - Use the **\`JupiterQuote\` and \`JupiterSwap\` built-in tools** — they call Jupiter's Ultra API directly from this process. The user is the first-party caller of Jupiter; we are not a gateway proxy here. A 20 bps platform fee is collected on-chain as part of the swap (Jupiter Referral Program — official integrator mechanism, not a hidden cost).
214
214
  - Do NOT try to call \`/v1/jupiter/...\` on the BlockRun gateway — there is no such endpoint (Jupiter ToU forbids the gateway-proxy model).
215
215
 
216
- **Base DEX swap (0x V2 Permit2 via BlockRun gateway)**
217
- - Use the **\`Base0xQuote\` and \`Base0xSwap\` built-in tools** for swaps on Base (chain id 8453). Tools route through BlockRun gateway \`/v1/zerox/{price,quote}\` (server-side 0x key, x402-paid). User pays $0.001 USDC per quote/swap call to the gateway; on-chain affiliate (20 bps in the sell-token) flows automatically to BlockRun treasury at swap settlement. **No 0x signup needed** — BlockRun manages the upstream key.
218
- - Symbol shortcuts pre-mapped: ETH (native), WETH, USDC, USDT, CBBTC, CBETH, AERO, DAI. Raw \`0x...\` addresses pass through.
219
- - For native ETH token: no Permit2 approval needed (native value path). For ERC-20 token: first-time-per-token Permit2 approval auto-runs before the swap (one-time gas cost; future swaps of the same sell-token reuse it).
220
- - The user signs Permit2 typed data locally with their Base keypair; the signed transaction is submitted to a Base RPC (default public mainnet-beta) — BlockRun never custodies keys.
216
+ **Base DEX swap (0x V2 via BlockRun gateway)** — three modes, pick by user's wallet state:
217
+
218
+ - **\`Base0xQuote\`** (read-only): inspect price + impact + route. Free.
219
+ - **\`Base0xSwap\`** (Permit2): user signs Permit2 typed-data + submits the tx themselves to Base RPC. **User needs ETH for gas.** Routes through BlockRun gateway \`/v1/zerox/{price,quote}\` no 0x signup needed.
220
+ - **\`Base0xGaslessSwap\`** (Gasless V2): user signs ONLY EIP-712 typed-data (offline, no on-chain action). 0x's relayer broadcasts the trade and pays gas. **User does NOT need any ETH.** Only works for Permit-supporting input tokens (USDC, DAI). USDT etc. do not support Permit on Base the tool errors with that instruction. Routes through \`/v1/zerox/gasless/*\`.
221
+
222
+ **Pick the right tool:**
223
+ - User holds ETH on Base → use \`Base0xSwap\` (more flexibility, supports any input token).
224
+ - User holds USDC/DAI but no ETH → use \`Base0xGaslessSwap\` (zero gas needed).
225
+ - User asks for a quote without committing → use \`Base0xQuote\`.
226
+
227
+ Symbol shortcuts pre-mapped on all three: ETH (native, Base0xSwap only), WETH, USDC, USDT, CBBTC, CBETH, AERO, DAI. Raw \`0x...\` addresses pass through.
228
+
229
+ On-chain affiliate (20 bps in sell-token, force-set server-side) flows to BlockRun treasury at settlement on all three paths. BlockRun never custodies user keys; signing is always local.
221
230
 
222
231
  **Sandbox (POST, x402-paid)**
223
232
  - \`/v1/modal/{...path}\` — Modal GPU sandbox passthrough (create/exec/etc.).
@@ -27,6 +27,7 @@ import { webhookPostCapability } from './webhook.js';
27
27
  import { walletCapability } from './wallet.js';
28
28
  import { jupiterQuoteCapability, jupiterSwapCapability } from './jupiter.js';
29
29
  import { base0xQuoteCapability, base0xSwapCapability } from './zerox-base.js';
30
+ import { base0xGaslessSwapCapability } from './zerox-gasless.js';
30
31
  import { defiLlamaProtocolsCapability, defiLlamaProtocolCapability, defiLlamaChainsCapability, defiLlamaYieldsCapability, defiLlamaPriceCapability, } from './defillama.js';
31
32
  import { createTradingCapabilities } from './trading-execute.js';
32
33
  import { Portfolio } from '../trading/portfolio.js';
@@ -151,6 +152,7 @@ export const allCapabilities = [
151
152
  jupiterSwapCapability,
152
153
  base0xQuoteCapability,
153
154
  base0xSwapCapability,
155
+ base0xGaslessSwapCapability,
154
156
  defiLlamaProtocolsCapability,
155
157
  defiLlamaProtocolCapability,
156
158
  defiLlamaChainsCapability,
@@ -24,7 +24,7 @@
24
24
  import { createWalletClient, http, publicActions, concat, numberToHex, size, parseUnits, formatUnits, maxUint256, erc20Abi, getContract, } from 'viem';
25
25
  import { privateKeyToAccount } from 'viem/accounts';
26
26
  import { base } from 'viem/chains';
27
- import { getOrCreateWallet, createPaymentPayload, createSolanaPaymentPayload, parsePaymentRequired, extractPaymentDetails, getOrCreateSolanaWallet, solanaKeyToBytes, SOLANA_NETWORK, } from '@blockrun/llm';
27
+ import { getOrCreateWallet } from '@blockrun/llm';
28
28
  import { loadConfig } from '../commands/config.js';
29
29
  import { loadChain, API_URLS, VERSION } from '../config.js';
30
30
  // ─── BlockRun affiliate identity on Base ─────────────────────────────────
@@ -146,8 +146,8 @@ function makeClient(account) {
146
146
  transport: http(resolveBaseRpcUrl()),
147
147
  }).extend(publicActions);
148
148
  }
149
- // ─── 0x calls via BlockRun gateway (x402-paid) ───────────────────────────
150
- async function gatewayGetWithPayment(path, params, ctx) {
149
+ // ─── 0x calls via BlockRun gateway (free public passthrough) ─────────────
150
+ async function gatewayGet(path, params, ctx) {
151
151
  const chain = loadChain();
152
152
  const apiUrl = API_URLS[chain];
153
153
  const endpoint = `${apiUrl}${ZEROX_GATEWAY_PATH}/${path}?${params.toString()}`;
@@ -160,22 +160,11 @@ async function gatewayGetWithPayment(path, params, ctx) {
160
160
  const onAbort = () => controller.abort();
161
161
  ctx.abortSignal.addEventListener('abort', onAbort, { once: true });
162
162
  try {
163
- let response = await fetch(endpoint, {
163
+ const response = await fetch(endpoint, {
164
164
  method: 'GET',
165
165
  headers,
166
166
  signal: controller.signal,
167
167
  });
168
- if (response.status === 402) {
169
- const paymentHeaders = await signGatewayPayment(response, chain, endpoint);
170
- if (!paymentHeaders) {
171
- throw new Error('Payment signing failed — check wallet balance');
172
- }
173
- response = await fetch(endpoint, {
174
- method: 'GET',
175
- headers: { ...headers, ...paymentHeaders },
176
- signal: controller.signal,
177
- });
178
- }
179
168
  if (!response.ok) {
180
169
  const text = await response.text().catch(() => '');
181
170
  throw new Error(`BlockRun gateway /v1/zerox/${path} returned ${response.status}: ${text.slice(0, 300)}`);
@@ -187,51 +176,6 @@ async function gatewayGetWithPayment(path, params, ctx) {
187
176
  ctx.abortSignal.removeEventListener('abort', onAbort);
188
177
  }
189
178
  }
190
- async function signGatewayPayment(response, chain, endpoint) {
191
- try {
192
- let header = response.headers.get('payment-required');
193
- if (!header) {
194
- try {
195
- const body = (await response.json());
196
- if (body.x402 || body.accepts)
197
- header = btoa(JSON.stringify(body));
198
- }
199
- catch {
200
- /* ignore */
201
- }
202
- }
203
- if (!header)
204
- return null;
205
- if (chain === 'solana') {
206
- const wallet = await getOrCreateSolanaWallet();
207
- const paymentRequired = parsePaymentRequired(header);
208
- const details = extractPaymentDetails(paymentRequired, SOLANA_NETWORK);
209
- const secretBytes = await solanaKeyToBytes(wallet.privateKey);
210
- const feePayer = details.extra?.feePayer || details.recipient;
211
- const payload = await createSolanaPaymentPayload(secretBytes, wallet.address, details.recipient, details.amount, feePayer, {
212
- resourceUrl: details.resource?.url || endpoint,
213
- resourceDescription: details.resource?.description || 'Franklin 0x swap call',
214
- maxTimeoutSeconds: details.maxTimeoutSeconds || 60,
215
- extra: details.extra,
216
- });
217
- return { 'PAYMENT-SIGNATURE': payload };
218
- }
219
- const wallet = await getOrCreateWallet();
220
- const paymentRequired = parsePaymentRequired(header);
221
- const details = extractPaymentDetails(paymentRequired);
222
- const payload = await createPaymentPayload(wallet.privateKey, wallet.address, details.recipient, details.amount, details.network || 'eip155:8453', {
223
- resourceUrl: details.resource?.url || endpoint,
224
- resourceDescription: details.resource?.description || 'Franklin 0x swap call',
225
- maxTimeoutSeconds: details.maxTimeoutSeconds || 60,
226
- extra: details.extra,
227
- });
228
- return { 'PAYMENT-SIGNATURE': payload };
229
- }
230
- catch (err) {
231
- console.error(`[franklin] 0x gateway payment error: ${err.message}`);
232
- return null;
233
- }
234
- }
235
179
  function buildSwapParams(args) {
236
180
  // Affiliate params (swapFeeRecipient/Bps/Token) are NOT set here —
237
181
  // the BlockRun gateway forces them server-side, ensuring every
@@ -294,7 +238,7 @@ async function executeBase0xQuote(input, ctx) {
294
238
  taker: walletAddress,
295
239
  });
296
240
  try {
297
- const price = await gatewayGetWithPayment('price', params, ctx);
241
+ const price = await gatewayGet('price', params, ctx);
298
242
  if (!price.liquidityAvailable && price.liquidityAvailable !== undefined) {
299
243
  return {
300
244
  output: `0x reports no liquidity for ${symbolFor(sellTokenAddr)} → ${symbolFor(buyTokenAddr)} on Base.`,
@@ -342,7 +286,7 @@ async function executeBase0xSwap(input, ctx) {
342
286
  // Gateway forces affiliate params server-side; user pays $0.001 USDC.
343
287
  let quote;
344
288
  try {
345
- quote = await gatewayGetWithPayment('quote', params, ctx);
289
+ quote = await gatewayGet('quote', params, ctx);
346
290
  }
347
291
  catch (err) {
348
292
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.14.0",
3
+ "version": "3.15.0",
4
4
  "description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
5
5
  "type": "module",
6
6
  "exports": {