autonomo-sdk 0.1.2 → 0.1.3
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 -0
- package/dist/index.d.ts +20 -1
- package/dist/index.js +56 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,11 @@ When the wallet can't afford a call, the SDK asks Borro's MCP server to prepare
|
|
|
145
145
|
sends it from the wallet (this needs a little ETH for gas), waits for the USDG and pays. The line needs x402 bills
|
|
146
146
|
turned on; see [Pay with a Borro credit line](https://autonomo.fun/docs/borro). Borro's own daily limit still applies.
|
|
147
147
|
|
|
148
|
+
Borro's answer is checked before anything is signed. The SDK sends only one transaction: a `borrow` on your own credit
|
|
149
|
+
line, into this wallet, with no ETH attached, of no more than the call's price. Anything else in the answer, such as a
|
|
150
|
+
transfer, an approval or a second transaction, is refused with `borro_unsafe` and nothing is sent. The call's price is
|
|
151
|
+
still checked against your caps first.
|
|
152
|
+
|
|
148
153
|
## The receipt
|
|
149
154
|
|
|
150
155
|
| Field | |
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Hex } from "viem";
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.3";
|
|
3
3
|
export type TokenName = "usdg" | "autonomo";
|
|
4
4
|
type Token = {
|
|
5
5
|
name: TokenName;
|
|
@@ -242,6 +242,25 @@ export declare function permit2TypedData(auth: {
|
|
|
242
242
|
};
|
|
243
243
|
};
|
|
244
244
|
};
|
|
245
|
+
/**
|
|
246
|
+
* The one transaction an honest Borro `credit_bill` answer holds: "one unsigned borrow of just what it is short, from the
|
|
247
|
+
* line to the wallet itself". Anything else is refused before it is signed: another target, ETH value, another function,
|
|
248
|
+
* another recipient, more than the call's price, or more than one transaction. A borrow's USDG lands in the agent's own
|
|
249
|
+
* wallet, so the caps on the call still bound what can be spent.
|
|
250
|
+
*/
|
|
251
|
+
export declare function checkBorrow(txs: Array<{
|
|
252
|
+
to: string;
|
|
253
|
+
data: string;
|
|
254
|
+
value?: string;
|
|
255
|
+
}>, opts: {
|
|
256
|
+
line: string;
|
|
257
|
+
wallet: string;
|
|
258
|
+
most: bigint;
|
|
259
|
+
}): {
|
|
260
|
+
to: Hex;
|
|
261
|
+
data: Hex;
|
|
262
|
+
amount: bigint;
|
|
263
|
+
};
|
|
245
264
|
/** Every transaction ({ to, data }) inside a tool result, in order. */
|
|
246
265
|
export declare function findTransactions(value: unknown): Array<{
|
|
247
266
|
to: string;
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { dirname, join } from "node:path";
|
|
16
|
-
import { createPublicClient, createWalletClient, defineChain, erc20Abi, formatUnits, getAddress, http, maxUint256, parseUnits, toHex, } from "viem";
|
|
16
|
+
import { createPublicClient, createWalletClient, defineChain, erc20Abi, formatEther, formatUnits, getAddress, http, maxUint256, parseUnits, toHex, } from "viem";
|
|
17
17
|
import { privateKeyToAccount } from "viem/accounts";
|
|
18
|
-
export const VERSION = "0.1.
|
|
18
|
+
export const VERSION = "0.1.3";
|
|
19
19
|
const NETWORK = "eip155:4663";
|
|
20
20
|
const CHAIN_ID = 4663;
|
|
21
21
|
const EXPLORER = "https://robinhoodchain.blockscout.com";
|
|
@@ -451,24 +451,22 @@ export class Autonomo {
|
|
|
451
451
|
payment_required: raw,
|
|
452
452
|
max_usdg: formatUnits(this.perCall.usdg, 6),
|
|
453
453
|
});
|
|
454
|
-
const txs = findTransactions(result);
|
|
455
|
-
const wallet = createWalletClient({ account, chain: this.chain, transport: http(this.rpc) });
|
|
456
|
-
const hashes = [];
|
|
457
|
-
for (const t of txs) {
|
|
458
|
-
const hash = await wallet.sendTransaction({ to: getAddress(t.to), data: t.data, value: t.value ? BigInt(t.value) : 0n });
|
|
459
|
-
const r = await pub.waitForTransactionReceipt({ hash });
|
|
460
|
-
if (r.status !== "success")
|
|
461
|
-
fail("borrow_failed", `Borro's borrow transaction failed: ${EXPLORER}/tx/${hash}`);
|
|
462
|
-
hashes.push(hash);
|
|
463
|
-
}
|
|
464
454
|
const need = BigInt(offer.amount);
|
|
455
|
+
// Borro's answer is checked before anything is signed: it may only borrow, on this line, into this wallet, at most the price.
|
|
456
|
+
const tx = checkBorrow(findTransactions(result), { line: borro.account, wallet: account.address, most: need });
|
|
457
|
+
const wallet = createWalletClient({ account, chain: this.chain, transport: http(this.rpc) });
|
|
458
|
+
const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, value: 0n });
|
|
459
|
+
const link = `${EXPLORER}/tx/${hash}`;
|
|
460
|
+
const r = await pub.waitForTransactionReceipt({ hash });
|
|
461
|
+
if (r.status !== "success")
|
|
462
|
+
fail("borrow_failed", `The borrow on your Borro line failed, so nothing was borrowed and the API isn't paid: ${link}`);
|
|
465
463
|
for (let i = 0; i < 20; i++) {
|
|
466
464
|
const bal = await pub.readContract({ address: TOKENS.usdg.address, abi: erc20Abi, functionName: "balanceOf", args: [account.address] });
|
|
467
465
|
if (bal >= need)
|
|
468
|
-
return
|
|
466
|
+
return [hash];
|
|
469
467
|
await sleep(1000);
|
|
470
468
|
}
|
|
471
|
-
return fail("borrow_failed",
|
|
469
|
+
return fail("borrow_failed", `Borrowed ${formatUnits(tx.amount, 6)} USDG on your Borro line (${link}), but it hasn't reached the wallet yet. The API isn't paid; try again shortly.`);
|
|
472
470
|
}
|
|
473
471
|
}
|
|
474
472
|
// ---------- Helpers ----------
|
|
@@ -561,6 +559,50 @@ async function mcpCall(f, url, name, args) {
|
|
|
561
559
|
return out;
|
|
562
560
|
}
|
|
563
561
|
}
|
|
562
|
+
/** borrow(uint256 amount, address to) on a Borro credit line. */
|
|
563
|
+
const BORROW_SELECTOR = "0x4b3fd148";
|
|
564
|
+
/**
|
|
565
|
+
* The one transaction an honest Borro `credit_bill` answer holds: "one unsigned borrow of just what it is short, from the
|
|
566
|
+
* line to the wallet itself". Anything else is refused before it is signed: another target, ETH value, another function,
|
|
567
|
+
* another recipient, more than the call's price, or more than one transaction. A borrow's USDG lands in the agent's own
|
|
568
|
+
* wallet, so the caps on the call still bound what can be spent.
|
|
569
|
+
*/
|
|
570
|
+
export function checkBorrow(txs, opts) {
|
|
571
|
+
const refuse = (why) => fail("borro_unsafe", `Borro's answer wasn't a plain borrow from your line into this wallet (${why}), so nothing was signed or sent.`);
|
|
572
|
+
if (txs.length === 0)
|
|
573
|
+
refuse("it held no borrow transaction");
|
|
574
|
+
if (txs.length > 1)
|
|
575
|
+
refuse(`it held ${txs.length} transactions, and a borrow is one`);
|
|
576
|
+
const [t] = txs;
|
|
577
|
+
if (!sameAddr(t.to, opts.line))
|
|
578
|
+
refuse(`it goes to ${t.to}, not your credit line ${opts.line}`);
|
|
579
|
+
let value;
|
|
580
|
+
try {
|
|
581
|
+
value = t.value === undefined || t.value === "" ? 0n : BigInt(t.value);
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
return refuse(`its value "${t.value}" isn't a number`);
|
|
585
|
+
}
|
|
586
|
+
if (value !== 0n)
|
|
587
|
+
refuse(`it sends ${formatEther(value)} ETH`);
|
|
588
|
+
const data = t.data.toLowerCase();
|
|
589
|
+
if (!data.startsWith(BORROW_SELECTOR))
|
|
590
|
+
refuse(`it calls ${data.slice(0, 10)}, not borrow`);
|
|
591
|
+
if (data.length !== 10 + 128)
|
|
592
|
+
refuse("its borrow arguments aren't an amount and a recipient");
|
|
593
|
+
const amount = BigInt(`0x${data.slice(10, 74)}`);
|
|
594
|
+
const word = data.slice(74);
|
|
595
|
+
if (!/^0{24}/.test(word))
|
|
596
|
+
refuse("its recipient isn't an address");
|
|
597
|
+
const to = `0x${word.slice(24)}`;
|
|
598
|
+
if (!sameAddr(to, opts.wallet))
|
|
599
|
+
refuse(`it borrows into ${getAddress(to)}, not this wallet`);
|
|
600
|
+
if (amount === 0n)
|
|
601
|
+
refuse("it borrows nothing");
|
|
602
|
+
if (amount > opts.most)
|
|
603
|
+
refuse(`it borrows ${formatUnits(amount, 6)} USDG, more than the call's ${formatUnits(opts.most, 6)}`);
|
|
604
|
+
return { to: getAddress(t.to), data: data, amount };
|
|
605
|
+
}
|
|
564
606
|
/** Every transaction ({ to, data }) inside a tool result, in order. */
|
|
565
607
|
export function findTransactions(value) {
|
|
566
608
|
const out = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autonomo-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "One call for AI agents: find the right paid API on Autonomo, pay in USDG or $AUTONOMO (privately through Gloam, or on Borro credit), within spending caps, and get a receipt.",
|
|
5
5
|
"homepage": "https://autonomo.fun",
|
|
6
6
|
"author": "Autonomo (https://autonomo.fun)",
|