@pickhooks/sdk 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +418 -3
- package/dist/abis/agents.d.ts +2418 -0
- package/dist/abis/agents.js +4 -0
- package/dist/abis/any3.d.ts +2123 -0
- package/dist/abis/any3.js +3 -0
- package/dist/abis/anytax.d.ts +1786 -0
- package/dist/abis/anytax.js +3 -0
- package/dist/abis/calls.d.ts +2515 -0
- package/dist/abis/calls.js +4 -0
- package/dist/abis/common.d.ts +449 -0
- package/dist/abis/common.js +91 -0
- package/dist/abis/dips.d.ts +897 -0
- package/dist/abis/dips.js +2 -0
- package/dist/abis/index.d.ts +14 -0
- package/dist/abis/index.js +14 -0
- package/dist/abis/infra.d.ts +701 -0
- package/dist/abis/infra.js +4 -0
- package/dist/abis/lever.d.ts +1710 -0
- package/dist/abis/lever.js +5 -0
- package/dist/abis/lever2.d.ts +2710 -0
- package/dist/abis/lever2.js +5 -0
- package/dist/abis/pools.d.ts +1898 -0
- package/dist/abis/pools.js +4 -0
- package/dist/abis/sticky.d.ts +953 -0
- package/dist/abis/sticky.js +4 -0
- package/dist/abis/sticky2.d.ts +3750 -0
- package/dist/abis/sticky2.js +8 -0
- package/dist/abis/v1.d.ts +1627 -0
- package/dist/abis/v1.js +5 -0
- package/dist/abis/v2.d.ts +3264 -0
- package/dist/abis/v2.js +8 -0
- package/dist/addresses.d.ts +100 -0
- package/dist/addresses.js +71 -0
- package/dist/agents-catalog.d.ts +318 -0
- package/dist/agents-catalog.js +353 -0
- package/dist/agents.d.ts +543 -0
- package/dist/agents.js +667 -0
- package/dist/any.d.ts +447 -0
- package/dist/any.js +729 -0
- package/dist/api.d.ts +62 -0
- package/dist/api.js +30 -0
- package/dist/blocks.d.ts +107 -0
- package/dist/blocks.js +126 -0
- package/dist/coin-rules-config.d.ts +8 -0
- package/dist/coin-rules-config.js +13 -0
- package/dist/coin-rules.d.ts +383 -0
- package/dist/coin-rules.js +878 -0
- package/dist/coins.d.ts +217 -0
- package/dist/coins.js +520 -0
- package/dist/desks.d.ts +298 -0
- package/dist/desks.js +343 -0
- package/dist/dips.d.ts +133 -0
- package/dist/dips.js +148 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +414 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.js +91 -0
- package/dist/infinity.d.ts +63 -0
- package/dist/infinity.js +96 -0
- package/dist/infra.d.ts +112 -0
- package/dist/infra.js +114 -0
- package/dist/lever.d.ts +391 -0
- package/dist/lever.js +419 -0
- package/dist/pools.d.ts +274 -0
- package/dist/pools.js +405 -0
- package/dist/sticky-rules.d.ts +179 -0
- package/dist/sticky-rules.js +580 -0
- package/dist/sticky-sim.d.ts +125 -0
- package/dist/sticky-sim.js +637 -0
- package/dist/sticky.d.ts +286 -0
- package/dist/sticky.js +420 -0
- package/dist/tlock.d.ts +67 -0
- package/dist/tlock.js +185 -0
- package/dist/tx.d.ts +72 -0
- package/dist/tx.js +78 -0
- package/dist/v2.d.ts +106 -0
- package/dist/v2.js +61 -0
- package/dist/v3.d.ts +42 -0
- package/dist/v3.js +98 -0
- package/dist/wallet.d.ts +80 -0
- package/dist/wallet.js +115 -0
- package/dist/watch.d.ts +82 -0
- package/dist/watch.js +194 -0
- package/package.json +67 -4
package/dist/agents.js
ADDED
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { encodeFunctionData, formatEther, isAddressEqual, parseAbi, parseEventLogs } from "viem";
|
|
2
|
+
import { agentsDeskAbi, agentsFactoryAbi, agentsHookAbi } from "./abis/agents.js";
|
|
3
|
+
import { clPoolManagerAbi, erc20Abi } from "./abis/common.js";
|
|
4
|
+
import { ZERO_ADDRESS } from "./addresses.js";
|
|
5
|
+
import { AGENT_LIMITS, AGENT_RULES, AGENT_RULE_BY_KIND, ALLOWED_MASK, ANSWER_SCHEMA, BUCKETS, ENVELOPE_TUPLE, OBJECTIVES, PERSONALITIES, PROGRAM_TUPLE, agentKindsOf, decodeProgram, describeProgram, noteHash, readPlan, starterEnvelope, starterProgram, systemPrompt, toPlan, toProgram, userPrompt, } from "./agents-catalog.js";
|
|
6
|
+
import { ruleText } from "./coins.js";
|
|
7
|
+
import { PickhooksError, decodeRevert, explain, findRevertData, toPickhooksError } from "./errors.js";
|
|
8
|
+
import { blockedKind, buildInfiSwap, infiPriceBnb, infiQuote, permit2Approvals, poolIdOf, toKey } from "./infinity.js";
|
|
9
|
+
import { chainDeadline, minusBps, sendAll, sendTx, signer } from "./tx.js";
|
|
10
|
+
import { approvalFor } from "./v3.js";
|
|
11
|
+
const PLAIN = parseAbi(["function name() view returns (string)", "function symbol() view returns (string)", "function metadata() view returns (string)"]);
|
|
12
|
+
const randomSalt = () => {
|
|
13
|
+
const b = new Uint8Array(32);
|
|
14
|
+
globalThis.crypto.getRandomValues(b);
|
|
15
|
+
return `0x${Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("")}`;
|
|
16
|
+
};
|
|
17
|
+
/** AgentsLimits' Bad(what) reasons, in words (the chain's strings are the keys). */
|
|
18
|
+
const AGENT_BAD = {
|
|
19
|
+
maxFee: "The coin's fee cap must be between 1 % and 10 %.",
|
|
20
|
+
maxCreator: "The creator can get at most 50 % of the coin's part.",
|
|
21
|
+
allowed: "The envelope allows a rule an agent may never use (a gate, or one that can hold sells).",
|
|
22
|
+
notice: "The notice must be between 10 minutes and a day.",
|
|
23
|
+
minGap: "The gap between changes must be between 1 hour and 7 days.",
|
|
24
|
+
veto: "The veto share must be between 1 % and 50 % of the coins outside the pool.",
|
|
25
|
+
fire: "The fire share must be between 1 % and 50 % of the coins outside the pool.",
|
|
26
|
+
buyFee: "The buy fee must be between 1 % and the coin's cap.",
|
|
27
|
+
sellFee: "The sell fee must be between 1 % and the coin's cap.",
|
|
28
|
+
creator: "The creator's share is over the coin's cap.",
|
|
29
|
+
routing: "The routing shares add up to more than 100 %.",
|
|
30
|
+
"rule not allowed": "A rule in this program is not allowed by the coin's envelope.",
|
|
31
|
+
"too many rules": "At most 8 rules at once.",
|
|
32
|
+
"rewards without rule": "A rewards share needs the buyer_rewards rule on.",
|
|
33
|
+
"jackpot without rule": "A jackpot share needs the jackpot rule on.",
|
|
34
|
+
maxTrade: "Max per buy: 0.1 % to 100 % of supply.",
|
|
35
|
+
buysPerBlock: "Anti bundle: at least 1 buy per block.",
|
|
36
|
+
maxGasPrice: "Gas cap: at least 0.05 gwei.",
|
|
37
|
+
raiseBps: "Raise: 0.01 % to 100 %.",
|
|
38
|
+
raiseReset: "Raise: the reset between 10 seconds and a day.",
|
|
39
|
+
minBuy: "Min buy: at most 1 % of supply.",
|
|
40
|
+
slowSeconds: "Slow mode: 1 second to an hour.",
|
|
41
|
+
buyLimit: "Buy limit: at least 1 buy.",
|
|
42
|
+
dumpBuy: "Anti dump: the buy cap between 0.1 % and 100 % of supply.",
|
|
43
|
+
dumpSell: "Anti dump: the sell cap between 0.1 % and 100 % of supply.",
|
|
44
|
+
taperEnd: "Taper: the end cap between 0.05 % and 100 %.",
|
|
45
|
+
taperStart: "Taper: the start cap must be at least the end cap and at most 100 %.",
|
|
46
|
+
taperSold: "Taper: the curve share between 1 % and 100 %.",
|
|
47
|
+
cooldown: "Cooldown: 1 second to a day.",
|
|
48
|
+
hours: "Market hours: open and close hours from 0 to 23, not the same.",
|
|
49
|
+
weekdays: "Market hours: at least one weekday.",
|
|
50
|
+
"market hours must keep sells open": "An agent's market hours only ever close buys: sells stay open.",
|
|
51
|
+
sellPeriod: "Sell window: the period between a minute and a day.",
|
|
52
|
+
sellWindow: "Sell window: at least a minute and shorter than the period.",
|
|
53
|
+
tickSeconds: "Tick tock: a turn between a minute and a day.",
|
|
54
|
+
bellSeconds: "Opening bell: 1 second to a day.",
|
|
55
|
+
bellSold: "Opening bell: the curve share at most 100 %.",
|
|
56
|
+
rewardPerBuy: "Buyer rewards: the reward must be more than 0.",
|
|
57
|
+
jackpotSilence: "Jackpot: the quiet time between 30 seconds and a day.",
|
|
58
|
+
jackpotShare: "Jackpot: the share between 0.01 % and 100 % of the pot.",
|
|
59
|
+
dipBps: "Dip only: 1 % to 99 % under the peak.",
|
|
60
|
+
jackpotMinBuy: "Jackpot: the smallest qualifying buy at most 1 % of supply.",
|
|
61
|
+
rewardMinBuy: "Buyer rewards: the smallest qualifying buy at most 1 % of supply.",
|
|
62
|
+
"tick tock with sell window": "Tick tock and a sell window cannot be on together.",
|
|
63
|
+
};
|
|
64
|
+
const f = (x) => formatEther(x);
|
|
65
|
+
const iso = (t) => new Date(t * 1000).toISOString();
|
|
66
|
+
export function agentsModule(ctx) {
|
|
67
|
+
const { pub, a } = ctx;
|
|
68
|
+
const F = a.agentsFactory;
|
|
69
|
+
const FA = agentsFactoryAbi;
|
|
70
|
+
const HA = agentsHookAbi;
|
|
71
|
+
const DA = agentsDeskAbi;
|
|
72
|
+
let addrs = null;
|
|
73
|
+
const keys = new Map();
|
|
74
|
+
const on = () => !isAddressEqual(F, ZERO_ADDRESS);
|
|
75
|
+
const need = () => {
|
|
76
|
+
if (!on())
|
|
77
|
+
throw new PickhooksError("Agents are not on mainnet yet.", { code: "input" });
|
|
78
|
+
return F;
|
|
79
|
+
};
|
|
80
|
+
const call = (address, abi, functionName, args = []) => pub.readContract({ address, abi, functionName, args });
|
|
81
|
+
async function contracts() {
|
|
82
|
+
if (!addrs) {
|
|
83
|
+
const [hook, desk] = await Promise.all([call(need(), FA, "hook"), call(need(), FA, "desk")]);
|
|
84
|
+
addrs = { hook, desk };
|
|
85
|
+
}
|
|
86
|
+
return addrs;
|
|
87
|
+
}
|
|
88
|
+
const hook = async () => (await contracts()).hook;
|
|
89
|
+
const desk = async () => (await contracts()).desk;
|
|
90
|
+
async function key(token) {
|
|
91
|
+
const hit = keys.get(token.toLowerCase());
|
|
92
|
+
if (hit)
|
|
93
|
+
return hit;
|
|
94
|
+
const k = toKey(await call(await hook(), HA, "poolKeyOf", [token]));
|
|
95
|
+
if (/^0x0{40}$/i.test(k.hooks))
|
|
96
|
+
throw new PickhooksError("This is not an agent coin.", { code: "input" });
|
|
97
|
+
keys.set(token.toLowerCase(), k);
|
|
98
|
+
return k;
|
|
99
|
+
}
|
|
100
|
+
const count = async () => Number(await call(need(), FA, "coinCount"));
|
|
101
|
+
const isCoin = async (token) => on() && (await call(F, FA, "isCoin", [token]).catch(() => false)) === true;
|
|
102
|
+
/** an error of the desk, the hook or the factory as a sentence (AgentsLimits' reasons, a refusing rule, the rest) */
|
|
103
|
+
async function agentError(e, fallback, token) {
|
|
104
|
+
if (e instanceof PickhooksError)
|
|
105
|
+
return e;
|
|
106
|
+
const data = findRevertData(e);
|
|
107
|
+
const d = data ? decodeRevert(data) : null;
|
|
108
|
+
if (d?.name === "Bad" && AGENT_BAD[String(d.args[0])])
|
|
109
|
+
return new PickhooksError(AGENT_BAD[String(d.args[0])], { code: "Bad", args: d.args, cause: e });
|
|
110
|
+
const kind = blockedKind(e);
|
|
111
|
+
if (kind !== null && token) {
|
|
112
|
+
const p = await program(token).catch(() => null);
|
|
113
|
+
return new PickhooksError(ruleText(kind, p?.r ?? null).text, { code: "Blocked", args: [kind], cause: e });
|
|
114
|
+
}
|
|
115
|
+
return toPickhooksError(e, fallback);
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------- reads
|
|
118
|
+
/** Coins newest first. */
|
|
119
|
+
async function coins(o = {}) {
|
|
120
|
+
const n = await count();
|
|
121
|
+
const end = Math.max(0, n - (o.offset ?? 0));
|
|
122
|
+
const from = Math.max(0, end - (o.limit ?? 50));
|
|
123
|
+
if (end <= from)
|
|
124
|
+
return [];
|
|
125
|
+
const tokens = (await pub.multicall({
|
|
126
|
+
contracts: Array.from({ length: end - from }, (_, i) => ({ address: F, abi: FA, functionName: "coins", args: [BigInt(end - 1 - i)] })),
|
|
127
|
+
allowFailure: false,
|
|
128
|
+
}));
|
|
129
|
+
return summaries(tokens);
|
|
130
|
+
}
|
|
131
|
+
/** One coin, or null when it is not an agent coin. */
|
|
132
|
+
async function coin(token) {
|
|
133
|
+
if (!(await isCoin(token)))
|
|
134
|
+
return null;
|
|
135
|
+
return (await summaries([token]))[0] ?? null;
|
|
136
|
+
}
|
|
137
|
+
async function summaries(tokens) {
|
|
138
|
+
if (!tokens.length)
|
|
139
|
+
return [];
|
|
140
|
+
const { hook: h, desk: d } = await contracts();
|
|
141
|
+
const per = 9;
|
|
142
|
+
const [res, meta, head] = await Promise.all([
|
|
143
|
+
pub.multicall({
|
|
144
|
+
contracts: tokens.flatMap((t) => [
|
|
145
|
+
{ address: h, abi: HA, functionName: "coinOf", args: [t] },
|
|
146
|
+
{ address: h, abi: HA, functionName: "programOf", args: [t] },
|
|
147
|
+
{ address: h, abi: HA, functionName: "stateOf", args: [t] },
|
|
148
|
+
{ address: h, abi: HA, functionName: "poolKeyOf", args: [t] },
|
|
149
|
+
{ address: h, abi: HA, functionName: "outside", args: [t] },
|
|
150
|
+
{ address: d, abi: DA, functionName: "agentOf", args: [t] },
|
|
151
|
+
{ address: d, abi: DA, functionName: "pendingOf", args: [t] },
|
|
152
|
+
{ address: d, abi: DA, functionName: "fireOf", args: [t] },
|
|
153
|
+
{ address: d, abi: DA, functionName: "dueAt", args: [t] },
|
|
154
|
+
]),
|
|
155
|
+
allowFailure: true,
|
|
156
|
+
}),
|
|
157
|
+
pub.multicall({ contracts: tokens.flatMap((t) => ["name", "symbol", "metadata"].map((functionName) => ({ address: t, abi: PLAIN, functionName }))), allowFailure: true }),
|
|
158
|
+
pub.getBlock({ blockTag: "latest" }),
|
|
159
|
+
]);
|
|
160
|
+
const ks = tokens.map((_, i) => (res[i * per + 3].status === "success" ? toKey(res[i * per + 3].result) : null));
|
|
161
|
+
const slots = await pub.multicall({
|
|
162
|
+
contracts: ks.map((k) => ({ address: a.clPoolManager, abi: clPoolManagerAbi, functionName: "getSlot0", args: [k ? poolIdOf(k) : `0x${"0".repeat(64)}`] })),
|
|
163
|
+
allowFailure: true,
|
|
164
|
+
});
|
|
165
|
+
const now = Number(head.timestamp);
|
|
166
|
+
const out = [];
|
|
167
|
+
tokens.forEach((token, i) => {
|
|
168
|
+
const g = (j) => res[i * per + j];
|
|
169
|
+
const k = ks[i];
|
|
170
|
+
if (!k || [0, 1, 2, 4, 5, 6, 7, 8].some((j) => g(j).status !== "success"))
|
|
171
|
+
return;
|
|
172
|
+
keys.set(token.toLowerCase(), k);
|
|
173
|
+
const c = g(0).result;
|
|
174
|
+
const p = decodeProgram(g(1).result);
|
|
175
|
+
const [pots, totals, stats, jackpotLast, jackpotTime] = g(2).result;
|
|
176
|
+
const [pilot, epoch, lastChange, version, env] = g(5).result;
|
|
177
|
+
const pend = g(6).result;
|
|
178
|
+
const fire = g(7).result;
|
|
179
|
+
const m = (j) => meta[i * 3 + j];
|
|
180
|
+
const sl = slots[i];
|
|
181
|
+
const priceBnb = sl.status === "success" ? infiPriceBnb(sl.result[0]) : 0;
|
|
182
|
+
const envelope = {
|
|
183
|
+
maxFeeBps: Number(env.maxFeeBps),
|
|
184
|
+
maxCreatorBps: Number(env.maxCreatorBps),
|
|
185
|
+
allowedMask: BigInt(env.allowedMask),
|
|
186
|
+
notice: Number(env.notice),
|
|
187
|
+
minGap: Number(env.minGap),
|
|
188
|
+
vetoBps: Number(env.vetoBps),
|
|
189
|
+
fireBps: Number(env.fireBps),
|
|
190
|
+
};
|
|
191
|
+
out.push({
|
|
192
|
+
token,
|
|
193
|
+
name: m(0).status === "success" ? String(m(0).result) : "",
|
|
194
|
+
symbol: m(1).status === "success" ? String(m(1).result) : "",
|
|
195
|
+
metadata: m(2).status === "success" ? String(m(2).result) : "",
|
|
196
|
+
creator: c[0],
|
|
197
|
+
floor: c[1],
|
|
198
|
+
start: Number(c[2]),
|
|
199
|
+
platformBps: Number(c[3]),
|
|
200
|
+
version: Number(version),
|
|
201
|
+
pilot: isAddressEqual(pilot, ZERO_ADDRESS) ? null : pilot,
|
|
202
|
+
fired: isAddressEqual(pilot, ZERO_ADDRESS),
|
|
203
|
+
epoch: Number(epoch),
|
|
204
|
+
lastChange: Number(lastChange),
|
|
205
|
+
env: envelope,
|
|
206
|
+
allowed: AGENT_RULES.filter((r) => envelope.allowedMask & (1n << BigInt(r.kind))).map((r) => r.id),
|
|
207
|
+
program: p,
|
|
208
|
+
lines: describeProgram(p),
|
|
209
|
+
rules: agentKindsOf(p.r.mask).map((kind) => {
|
|
210
|
+
const def = AGENT_RULE_BY_KIND.get(kind);
|
|
211
|
+
return { kind, id: def.id, name: def.name, text: def.describe(p.r) };
|
|
212
|
+
}),
|
|
213
|
+
pending: pend[0] > 0n
|
|
214
|
+
? { id: pend[0], activeAt: Number(pend[1]), dueAt: Number(g(8).result), note: pend[2], vetoLocked: pend[3], vetoNeeded: pend[4], program: pend[5], lines: describeProgram(pend[5]) }
|
|
215
|
+
: null,
|
|
216
|
+
fire: { locked: fire[0], needed: fire[1] },
|
|
217
|
+
pots: Object.fromEntries(BUCKETS.map((b) => [b, pots[b]])),
|
|
218
|
+
totals,
|
|
219
|
+
stats: {
|
|
220
|
+
buyers: Number(stats.buyers),
|
|
221
|
+
buys: Number(stats.buys),
|
|
222
|
+
sells: Number(stats.sells),
|
|
223
|
+
buysSinceSell: Number(stats.buysSinceSell),
|
|
224
|
+
lastBuyTime: Number(stats.lastBuyTime),
|
|
225
|
+
lastBuyer: stats.lastBuyer,
|
|
226
|
+
qualifyingBuys: Number(stats.qualifyingBuys),
|
|
227
|
+
volume: stats.volume,
|
|
228
|
+
net: stats.net,
|
|
229
|
+
peak: stats.peak,
|
|
230
|
+
},
|
|
231
|
+
jackpotLast,
|
|
232
|
+
jackpotTime: Number(jackpotTime),
|
|
233
|
+
outside: g(4).result,
|
|
234
|
+
id: poolIdOf(k),
|
|
235
|
+
key: k,
|
|
236
|
+
priceBnb,
|
|
237
|
+
mcapBnb: priceBnb * 1e9,
|
|
238
|
+
chainNow: now,
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
/** The live program of a coin. */
|
|
244
|
+
const program = async (token) => decodeProgram(await call(await hook(), HA, "programOf", [token]));
|
|
245
|
+
/** The coin's limits and its pilot. */
|
|
246
|
+
async function envelopeOf(token) {
|
|
247
|
+
const [pilot, , lastChange, version, env] = await call(await desk(), DA, "agentOf", [token]);
|
|
248
|
+
return {
|
|
249
|
+
pilot: isAddressEqual(pilot, ZERO_ADDRESS) ? null : pilot,
|
|
250
|
+
version: Number(version),
|
|
251
|
+
lastChange: Number(lastChange),
|
|
252
|
+
env: { maxFeeBps: Number(env.maxFeeBps), maxCreatorBps: Number(env.maxCreatorBps), allowedMask: BigInt(env.allowedMask), notice: Number(env.notice), minGap: Number(env.minGap), vetoBps: Number(env.vetoBps), fireBps: Number(env.fireBps) },
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/** A wallet's lock at the desk for a coin (coins locked against a change or against the agent). */
|
|
256
|
+
async function locks(token, who) {
|
|
257
|
+
const r = await call(await desk(), DA, "locks", [token, who]);
|
|
258
|
+
return { amount: r[0], vetoId: r[1], fireEpoch: Number(r[2]), vetoing: r[3], firing: r[4] };
|
|
259
|
+
}
|
|
260
|
+
/** BNB the hook owes a wallet (jackpot wins, rewards, a creator's share), over every agent coin. */
|
|
261
|
+
const owed = async (who) => call(await hook(), HA, "owed", [who]);
|
|
262
|
+
/** A wallet and a coin: balance, what is locked at the desk, BNB owed. */
|
|
263
|
+
async function wallet(token, who) {
|
|
264
|
+
const [balance, l, o] = await Promise.all([call(token, erc20Abi, "balanceOf", [who]), locks(token, who), owed(who)]);
|
|
265
|
+
return { balance, owed: o, ...l };
|
|
266
|
+
}
|
|
267
|
+
/** The agent's journal from pickhooks.com (the pickhooks pilot writes one; other pilots' coins have none there). */
|
|
268
|
+
async function journal(token, limit = 20) {
|
|
269
|
+
const res = await fetch(`${ctx.site}/api/agents/${token}`, { headers: { accept: "application/json" } });
|
|
270
|
+
const j = (await res.json().catch(() => null));
|
|
271
|
+
if (!res.ok || !j)
|
|
272
|
+
throw new PickhooksError(j?.error ?? `The site answered ${res.status}.`, { code: res.status === 429 ? "rate" : "site" });
|
|
273
|
+
return (j.journal ?? []).slice(0, limit);
|
|
274
|
+
}
|
|
275
|
+
/** The agent's profile from the coin's metadata JSON (the brief its creator wrote), or just the symbol. */
|
|
276
|
+
async function profileOf(c) {
|
|
277
|
+
const base = { symbol: c.symbol, name: c.name };
|
|
278
|
+
if (!/^https:\/\//.test(c.metadata))
|
|
279
|
+
return base;
|
|
280
|
+
try {
|
|
281
|
+
const j = (await (await fetch(c.metadata, { signal: AbortSignal.timeout(5000) })).json());
|
|
282
|
+
const ag = j.agent ?? {};
|
|
283
|
+
const s = (x, n) => (typeof x === "string" ? x.slice(0, n) : "");
|
|
284
|
+
return {
|
|
285
|
+
...base,
|
|
286
|
+
description: s(j.description, 500),
|
|
287
|
+
agentName: s(ag.name, 40),
|
|
288
|
+
personality: s(ag.personality, 40),
|
|
289
|
+
objectives: Array.isArray(ag.objectives) ? ag.objectives.slice(0, 3).map((o) => s(o, 40)) : [],
|
|
290
|
+
mission: s(ag.mission, 200),
|
|
291
|
+
ideas: Array.isArray(ag.ideas) ? ag.ideas.slice(0, 3).map((o) => s(o, 280)) : [],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return base;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* A look at the coin: the JSON the pickhooks pilot hands its model each round (market, live program, pending change,
|
|
300
|
+
* holders against it, buckets, what was done so far, its last journal entries). `tradesSinceLastLook` is yours to pass.
|
|
301
|
+
*/
|
|
302
|
+
async function look(token, o = {}) {
|
|
303
|
+
const c = await coin(token);
|
|
304
|
+
if (!c)
|
|
305
|
+
throw new PickhooksError("This is not an agent coin.", { code: "input" });
|
|
306
|
+
const entries = o.entries ?? (await journal(token, 5).catch(() => [])).map((e) => ({ at: e.at, title: e.title, body: e.body, action: e.action, expect: e.expect, result: e.result }));
|
|
307
|
+
return {
|
|
308
|
+
now_utc: iso(c.chainNow),
|
|
309
|
+
coin: { name: c.name, symbol: c.symbol, token, age_hours: +((c.chainNow - c.start) / 3600).toFixed(2), creator: c.creator, floor: c.floor },
|
|
310
|
+
market: {
|
|
311
|
+
price_bnb_per_million_coins: +(c.priceBnb * 1e6).toPrecision(4),
|
|
312
|
+
market_cap_bnb: +c.mcapBnb.toPrecision(4),
|
|
313
|
+
buys: c.stats.buys,
|
|
314
|
+
sells: c.stats.sells,
|
|
315
|
+
trades_since_last_look: o.tradesSinceLastLook ?? null,
|
|
316
|
+
buyers: c.stats.buyers,
|
|
317
|
+
volume_coins: f(c.stats.volume),
|
|
318
|
+
coins_outside_pool: f(c.outside),
|
|
319
|
+
},
|
|
320
|
+
live_program: { version: c.version, ...toPlan(c.program) },
|
|
321
|
+
pending_change: c.pending ? { id: Number(c.pending.id), lands_at_utc: iso(c.pending.activeAt), veto_locked: f(c.pending.vetoLocked), veto_needed: f(c.pending.vetoNeeded), plan: toPlan(c.pending.program) } : null,
|
|
322
|
+
holders_against_you: { locked: f(c.fire.locked), fires_you_at: f(c.fire.needed) },
|
|
323
|
+
buckets_bnb: Object.fromEntries(BUCKETS.map((b) => [b, f(c.pots[b])])),
|
|
324
|
+
done_so_far: { burned_bnb: f(c.totals.burnedBnb), burned_coins: f(c.totals.burnedCoins), liquidity_bnb: f(c.totals.lpBnb), floor_bnb: f(c.totals.floorBnb) },
|
|
325
|
+
last_change_utc: iso(c.lastChange),
|
|
326
|
+
your_last_entries: entries,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Everything a model needs to be this coin's agent for one look: the pickhooks pilot's system prompt (the coin's
|
|
331
|
+
* character and brief, how fees route, the rules it may use, its limits), the user message with the look, and the
|
|
332
|
+
* strict JSON schema of the answer. Feed `system` and `user` to any model with `schema` as its output format, then
|
|
333
|
+
* hand the answer to act().
|
|
334
|
+
*/
|
|
335
|
+
async function brief(token, o = {}) {
|
|
336
|
+
const c = await coin(token);
|
|
337
|
+
if (!c)
|
|
338
|
+
throw new PickhooksError("This is not an agent coin.", { code: "input" });
|
|
339
|
+
const [profile, lk] = await Promise.all([profileOf(c), look(token, o)]);
|
|
340
|
+
return { system: systemPrompt(profile, c.env, c.platformBps / 100), user: userPrompt(lk), schema: ANSWER_SCHEMA, look: lk, profile };
|
|
341
|
+
}
|
|
342
|
+
// ---------------------------------------------------------------- checks
|
|
343
|
+
/** The problems AgentsLimits.check would name, offline (the main ones; the chain has the last word). Empty = fine. */
|
|
344
|
+
function problemsOf(p, env = starterEnvelope()) {
|
|
345
|
+
const out = [];
|
|
346
|
+
const has = (id) => (BigInt(p.r.mask) & (1n << BigInt(id))) !== 0n;
|
|
347
|
+
if (p.buyFeeBps < AGENT_LIMITS.baseFeeBps || p.buyFeeBps > env.maxFeeBps)
|
|
348
|
+
out.push(AGENT_BAD.buyFee);
|
|
349
|
+
if (p.sellFeeBps < AGENT_LIMITS.baseFeeBps || p.sellFeeBps > env.maxFeeBps)
|
|
350
|
+
out.push(AGENT_BAD.sellFee);
|
|
351
|
+
if (p.creatorBps > env.maxCreatorBps)
|
|
352
|
+
out.push(AGENT_BAD.creator);
|
|
353
|
+
if (p.burnBps + p.lpBps + p.floorBps + p.rewardsBps + p.jackpotBps + p.creatorBps > 10_000)
|
|
354
|
+
out.push(AGENT_BAD.routing);
|
|
355
|
+
if ((BigInt(p.r.mask) & ~env.allowedMask) !== 0n)
|
|
356
|
+
out.push(AGENT_BAD["rule not allowed"]);
|
|
357
|
+
if (agentKindsOf(p.r.mask).length > AGENT_LIMITS.maxRules)
|
|
358
|
+
out.push(AGENT_BAD["too many rules"]);
|
|
359
|
+
if ((BigInt(p.r.mask) & ~ALLOWED_MASK) !== 0n)
|
|
360
|
+
out.push("A rule in this program is not one an agent may use.");
|
|
361
|
+
if (p.rewardsBps > 0 && !has(31))
|
|
362
|
+
out.push(AGENT_BAD["rewards without rule"]);
|
|
363
|
+
if (p.jackpotBps > 0 && !has(36))
|
|
364
|
+
out.push(AGENT_BAD["jackpot without rule"]);
|
|
365
|
+
if (has(15) && !p.r.sellsOpen)
|
|
366
|
+
out.push(AGENT_BAD["market hours must keep sells open"]);
|
|
367
|
+
if (has(20) && has(16))
|
|
368
|
+
out.push(AGENT_BAD["tick tock with sell window"]);
|
|
369
|
+
if (has(16) && (p.r.sellWindow < 60 || p.r.sellWindow >= p.r.sellPeriod))
|
|
370
|
+
out.push(AGENT_BAD.sellWindow);
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
/** The problems AgentsLimits.checkEnvelope would name, offline. Empty = fine. */
|
|
374
|
+
function envelopeProblems(e) {
|
|
375
|
+
const out = [];
|
|
376
|
+
if (e.maxFeeBps < AGENT_LIMITS.baseFeeBps || e.maxFeeBps > AGENT_LIMITS.maxFeeBps)
|
|
377
|
+
out.push(AGENT_BAD.maxFee);
|
|
378
|
+
if (e.maxCreatorBps > AGENT_LIMITS.maxCreatorBps)
|
|
379
|
+
out.push(AGENT_BAD.maxCreator);
|
|
380
|
+
if ((e.allowedMask & ~ALLOWED_MASK) !== 0n)
|
|
381
|
+
out.push(AGENT_BAD.allowed);
|
|
382
|
+
if (e.notice < AGENT_LIMITS.notice.min || e.notice > AGENT_LIMITS.notice.max)
|
|
383
|
+
out.push(AGENT_BAD.notice);
|
|
384
|
+
if (e.minGap < AGENT_LIMITS.minGap.min || e.minGap > AGENT_LIMITS.minGap.max)
|
|
385
|
+
out.push(AGENT_BAD.minGap);
|
|
386
|
+
if (e.vetoBps < AGENT_LIMITS.vetoBps.min || e.vetoBps > AGENT_LIMITS.vetoBps.max)
|
|
387
|
+
out.push(AGENT_BAD.veto);
|
|
388
|
+
if (e.fireBps < AGENT_LIMITS.fireBps.min || e.fireBps > AGENT_LIMITS.fireBps.max)
|
|
389
|
+
out.push(AGENT_BAD.fire);
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
/** Asks the desk (an eth_call as the coin's pilot) whether it would take this program: the chain's own check, in words. */
|
|
393
|
+
async function checkProgram(token, p) {
|
|
394
|
+
const { pilot } = await envelopeOf(token);
|
|
395
|
+
if (!pilot)
|
|
396
|
+
return { ok: false, text: AGENT_BAD.NoPilot ?? "This coin has no pilot any more: the holders fired it." };
|
|
397
|
+
try {
|
|
398
|
+
await pub.simulateContract({ address: await desk(), abi: DA, functionName: "propose", args: [token, p, `0x${"0".repeat(64)}`], account: pilot });
|
|
399
|
+
return { ok: true };
|
|
400
|
+
}
|
|
401
|
+
catch (e) {
|
|
402
|
+
return { ok: false, text: (await agentError(e, "The desk refused this program.")).message };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------- trades (the pool: PancakeSwap Infinity with the agents hook)
|
|
406
|
+
async function quote(q) {
|
|
407
|
+
try {
|
|
408
|
+
const r = await infiQuote(ctx, { key: await key(q.token), buy: q.side === "buy", amount: q.amount, from: q.from });
|
|
409
|
+
return { amountOut: r.amountOut };
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
throw await agentError(e, "The quoter refused this trade.", q.token);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/** Would this trade go through for this wallet: the quote runs the hook's rules as the trader. */
|
|
416
|
+
async function check(c) {
|
|
417
|
+
try {
|
|
418
|
+
await infiQuote(ctx, { key: await key(c.token), buy: c.side === "buy", amount: c.amount, from: c.wallet });
|
|
419
|
+
return { ok: true };
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
const kind = blockedKind(e);
|
|
423
|
+
if (kind === null)
|
|
424
|
+
return { ok: false, kind: 0, name: "", text: explain(e, "The pool refused this trade.").text };
|
|
425
|
+
const p = await program(c.token).catch(() => null);
|
|
426
|
+
return { ok: false, kind, ...ruleText(kind, p?.r ?? null) };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const buildBuy = (k, bnbIn, minOut, deadline) => buildInfiSwap(a, { key: k, buy: true, legs: [bnbIn], minOut, deadline, gasPct: 150 });
|
|
430
|
+
const buildSell = (k, amount, minOut, deadline) => buildInfiSwap(a, { key: k, buy: false, legs: [amount], minOut, deadline, gasPct: 150 });
|
|
431
|
+
/** Quotes as the trader (a refusing rule stops it before anything is signed), then buys through the UniversalRouter. */
|
|
432
|
+
async function buy(b, s = {}) {
|
|
433
|
+
const { address } = await signer(ctx, s.wallet);
|
|
434
|
+
const q = await quote({ token: b.token, side: "buy", amount: b.bnbIn, from: address });
|
|
435
|
+
const minOut = b.minOut ?? minusBps(q.amountOut, b.slippageBps ?? 100);
|
|
436
|
+
try {
|
|
437
|
+
return { ...(await sendTx(ctx, buildBuy(await key(b.token), b.bnbIn, minOut, b.deadline ?? (await chainDeadline(ctx))), s)), quoted: q.amountOut, minOut };
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
throw await agentError(e, "The buy would fail.", b.token);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/** Approves Permit2 if needed, quotes as the trader, sells through the UniversalRouter. */
|
|
444
|
+
async function sell(x, s = {}) {
|
|
445
|
+
const { address } = await signer(ctx, s.wallet);
|
|
446
|
+
const q = await quote({ token: x.token, side: "sell", amount: x.amount, from: address });
|
|
447
|
+
await sendAll(ctx, await permit2Approvals(ctx, address, x.token, x.amount), s);
|
|
448
|
+
const minOut = x.minOut ?? minusBps(q.amountOut, x.slippageBps ?? 100);
|
|
449
|
+
try {
|
|
450
|
+
return { ...(await sendTx(ctx, buildSell(await key(x.token), x.amount, minOut, x.deadline ?? (await chainDeadline(ctx))), s)), quoted: q.amountOut, minOut };
|
|
451
|
+
}
|
|
452
|
+
catch (e) {
|
|
453
|
+
throw await agentError(e, "The sell would fail.", x.token);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// ---------------------------------------------------------------- writes: the desk (pilot, holders) and the hook (anyone)
|
|
457
|
+
const deskTx = async (fn, args, label, value = 0n) => ({
|
|
458
|
+
to: await desk(),
|
|
459
|
+
value,
|
|
460
|
+
data: encodeFunctionData({ abi: DA, functionName: fn, args }),
|
|
461
|
+
label,
|
|
462
|
+
});
|
|
463
|
+
const hookTx = async (fn, args, label, value = 0n) => ({
|
|
464
|
+
to: await hook(),
|
|
465
|
+
value,
|
|
466
|
+
data: encodeFunctionData({ abi: HA, functionName: fn, args }),
|
|
467
|
+
label,
|
|
468
|
+
});
|
|
469
|
+
const bucketIndex = (b) => {
|
|
470
|
+
const i = typeof b === "number" ? b : BUCKETS.indexOf(b);
|
|
471
|
+
if (i < 0 || i >= BUCKETS.length)
|
|
472
|
+
throw new PickhooksError(`Bucket: one of ${BUCKETS.join(", ")}.`, { code: "input" });
|
|
473
|
+
return i;
|
|
474
|
+
};
|
|
475
|
+
/** A program from a Program or a plan in plain units. */
|
|
476
|
+
const programOf = (p) => ("r" in p && p.r ? p : toProgram(p).program);
|
|
477
|
+
/** The note a proposal carries: a Hex as given, a text hashed (keccak256 of "title\nbody" when title and body are given), or zero. */
|
|
478
|
+
const noteOf = (note) => {
|
|
479
|
+
if (!note)
|
|
480
|
+
return `0x${"0".repeat(64)}`;
|
|
481
|
+
if (typeof note === "string")
|
|
482
|
+
return /^0x[0-9a-fA-F]{64}$/.test(note) ? note : noteHash({ title: note, body: "" });
|
|
483
|
+
return noteHash(note);
|
|
484
|
+
};
|
|
485
|
+
const build = {
|
|
486
|
+
/** the pilot proposes a whole new program; it lands after the notice (anyone may call activate then) */
|
|
487
|
+
propose: async (token, p, note) => deskTx("propose", [token, programOf(p), noteOf(note)], "Proposing the change"),
|
|
488
|
+
cancel: (token) => deskTx("cancel", [token], "Cancelling the change"),
|
|
489
|
+
/** the pilot moves treasury BNB (or a pot whose rule is off) into burn, lp, floor, rewards or jackpot */
|
|
490
|
+
move: (token, from, to, wei) => deskTx("move", [token, bucketIndex(from), bucketIndex(to), wei], "Moving BNB between buckets"),
|
|
491
|
+
handOver: (token, to) => deskTx("handOver", [token, to], "Handing the coin to another pilot"),
|
|
492
|
+
/** anyone lands a change that is due */
|
|
493
|
+
activate: (token) => deskTx("activate", [token], "Landing the change"),
|
|
494
|
+
/** a holder locks coins against the pending change (0: with coins locked already) */
|
|
495
|
+
veto: (token, amount) => deskTx("veto", [token, amount], "Vetoing the change"),
|
|
496
|
+
/** a holder locks coins against the agent (0: with coins locked already) */
|
|
497
|
+
fire: (token, amount) => deskTx("fire", [token, amount], "Firing the agent"),
|
|
498
|
+
withdraw: (token) => deskTx("withdraw", [token], "Taking the locked coins back"),
|
|
499
|
+
/** anyone adds BNB to a coin's bucket */
|
|
500
|
+
give: (token, bucket, wei) => hookTx("give", [token, bucketIndex(bucket)], "Giving BNB to the coin", wei),
|
|
501
|
+
/** jackpot wins, rewards and a creator's share, over every agent coin */
|
|
502
|
+
claim: () => hookTx("claim", [], "The claim"),
|
|
503
|
+
/** the runs anyone may trigger: the burn bucket buys and burns, the lp bucket becomes liquidity, the floor bucket feeds the floor */
|
|
504
|
+
burn: (token) => hookTx("burn", [token], "The burn run"),
|
|
505
|
+
addLiquidity: (token) => hookTx("addLiquidity", [token], "The liquidity run"),
|
|
506
|
+
feedFloor: (token) => hookTx("feedFloor", [token], "Feeding the floor"),
|
|
507
|
+
settleJackpot: (token) => hookTx("settleJackpot", [token], "Settling the jackpot"),
|
|
508
|
+
buy: (k, bnbIn, minOut, deadline) => buildBuy(k, bnbIn, minOut, deadline),
|
|
509
|
+
sell: (k, amount, minOut, deadline) => buildSell(k, amount, minOut, deadline),
|
|
510
|
+
launch: (l) => {
|
|
511
|
+
const program = l.program ?? (l.plan ? toProgram(l.plan).program : starterProgram());
|
|
512
|
+
const env = { ...starterEnvelope(), ...l.envelope };
|
|
513
|
+
const problems = [...envelopeProblems(env), ...problemsOf(program, env)];
|
|
514
|
+
if (problems.length)
|
|
515
|
+
throw new PickhooksError(problems.join(" "), { code: "input" });
|
|
516
|
+
const pilot = l.pilot ?? a.agentsPilot;
|
|
517
|
+
if (isAddressEqual(pilot, ZERO_ADDRESS))
|
|
518
|
+
throw new PickhooksError("Give a pilot: the agent's wallet.", { code: "input" });
|
|
519
|
+
return {
|
|
520
|
+
to: need(),
|
|
521
|
+
value: l.devBuy ?? 0n,
|
|
522
|
+
data: encodeFunctionData({ abi: FA, functionName: "launch", args: [l.name, l.symbol, l.metadata, program, env, pilot, l.salt ?? randomSalt()] }),
|
|
523
|
+
gasPct: 120,
|
|
524
|
+
label: "The launch",
|
|
525
|
+
};
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
const sendDesk = async (tx, s, token, fallback = "The desk refused this.") => {
|
|
529
|
+
try {
|
|
530
|
+
return await sendTx(ctx, await tx, s);
|
|
531
|
+
}
|
|
532
|
+
catch (e) {
|
|
533
|
+
throw await agentError(e, fallback, token);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
/** The desk pulls the coins for veto and fire: an approval first when the allowance is short. */
|
|
537
|
+
const deskApproval = async (owner, token, amount) => approvalFor(ctx, owner, token, amount, await desk(), "Allowing the desk");
|
|
538
|
+
/**
|
|
539
|
+
* The transaction a model's answer asks for: propose (the plan as a program, the entry's hash as the note), move, or
|
|
540
|
+
* nothing for "none". `proposal` carries the proposed program as sentences, `notes` the rules toProgram dropped.
|
|
541
|
+
*/
|
|
542
|
+
async function act(token, answer) {
|
|
543
|
+
const entry = { title: String(answer.title).slice(0, 80), body: String(answer.body).slice(0, 1200) };
|
|
544
|
+
const note = noteHash(entry);
|
|
545
|
+
if (answer.action === "propose") {
|
|
546
|
+
const { program: p, notes } = toProgram(readPlan(answer.plan));
|
|
547
|
+
return { tx: await build.propose(token, p, note), proposal: describeProgram(p), notes, note };
|
|
548
|
+
}
|
|
549
|
+
if (answer.action === "move" && answer.move?.bnb > 0) {
|
|
550
|
+
const wei = BigInt(Math.round(answer.move.bnb * 1e9)) * 10n ** 9n;
|
|
551
|
+
return { tx: await build.move(token, answer.move.from, answer.move.to, wei), notes: [], note };
|
|
552
|
+
}
|
|
553
|
+
return { tx: null, notes: [], note };
|
|
554
|
+
}
|
|
555
|
+
/** The launch: checks the program and the limits offline, then the factory's own check in an eth_call, then sends. */
|
|
556
|
+
async function launch(l, s = {}) {
|
|
557
|
+
const tx = build.launch(l);
|
|
558
|
+
const { address } = await signer(ctx, s.wallet);
|
|
559
|
+
try {
|
|
560
|
+
await pub.call({ account: address, to: tx.to, data: tx.data, value: tx.value });
|
|
561
|
+
}
|
|
562
|
+
catch (e) {
|
|
563
|
+
throw await agentError(e, "The factory refused this launch.");
|
|
564
|
+
}
|
|
565
|
+
const sent = await sendTx(ctx, tx, s);
|
|
566
|
+
const ev = parseEventLogs({ abi: agentsFactoryAbi, logs: sent.receipt.logs.filter((x) => isAddressEqual(x.address, F)), eventName: "Launched" })[0];
|
|
567
|
+
if (!ev)
|
|
568
|
+
throw new PickhooksError("Launched, but the coin was not found in the receipt.", { code: "receipt", hash: sent.hash });
|
|
569
|
+
return { ...sent, token: ev.args.token, id: ev.args.poolId, floor: ev.args.floor };
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Stores the image and the metadata with the agent's profile on pickhooks.com (the site's /api/agents/prepare, rate
|
|
573
|
+
* limited) and returns the metadata link and a fresh salt. A launchpad can host its own JSON and pass that link instead.
|
|
574
|
+
*/
|
|
575
|
+
async function prepareMetadata(m) {
|
|
576
|
+
const B = globalThis.Buffer;
|
|
577
|
+
const base64 = B ? B.from(m.image.bytes).toString("base64") : btoa(Array.from(m.image.bytes, (x) => String.fromCharCode(x)).join(""));
|
|
578
|
+
const res = await fetch(`${ctx.site}/api/agents/prepare`, {
|
|
579
|
+
method: "POST",
|
|
580
|
+
headers: { "content-type": "application/json" },
|
|
581
|
+
body: JSON.stringify({ ...m, image: { mime: m.image.mime, base64 } }),
|
|
582
|
+
});
|
|
583
|
+
const j = (await res.json().catch(() => ({})));
|
|
584
|
+
if (!res.ok || !j.metadata)
|
|
585
|
+
throw new PickhooksError(j.error ?? `The site answered ${res.status}.`, { code: "site" });
|
|
586
|
+
return { metadata: j.metadata, image: j.image ?? "", salt: j.salt ?? randomSalt() };
|
|
587
|
+
}
|
|
588
|
+
/** The catalog as plain data: the rules an agent may use, the limits, the buckets, the profile vocabulary, the ABI tuples. */
|
|
589
|
+
const catalog = () => ({
|
|
590
|
+
rules: AGENT_RULES.map((r) => ({ kind: r.kind, id: r.id, name: r.name, line: r.line, fields: r.fields })),
|
|
591
|
+
limits: AGENT_LIMITS,
|
|
592
|
+
buckets: [...BUCKETS],
|
|
593
|
+
personalities: [...PERSONALITIES],
|
|
594
|
+
objectives: OBJECTIVES,
|
|
595
|
+
starter: { program: starterProgram(), envelope: starterEnvelope() },
|
|
596
|
+
abi: { program: PROGRAM_TUPLE, envelope: ENVELOPE_TUPLE },
|
|
597
|
+
answerSchema: ANSWER_SCHEMA,
|
|
598
|
+
});
|
|
599
|
+
return {
|
|
600
|
+
get on() {
|
|
601
|
+
return on();
|
|
602
|
+
},
|
|
603
|
+
factory: F,
|
|
604
|
+
hook,
|
|
605
|
+
desk,
|
|
606
|
+
key,
|
|
607
|
+
count,
|
|
608
|
+
isCoin,
|
|
609
|
+
coins,
|
|
610
|
+
coin,
|
|
611
|
+
program,
|
|
612
|
+
envelopeOf,
|
|
613
|
+
locks,
|
|
614
|
+
owed,
|
|
615
|
+
wallet,
|
|
616
|
+
journal,
|
|
617
|
+
look,
|
|
618
|
+
brief,
|
|
619
|
+
catalog,
|
|
620
|
+
describe: describeProgram,
|
|
621
|
+
toProgram,
|
|
622
|
+
toPlan,
|
|
623
|
+
problemsOf,
|
|
624
|
+
envelopeProblems,
|
|
625
|
+
checkProgram,
|
|
626
|
+
quote,
|
|
627
|
+
check,
|
|
628
|
+
sellApprovals: (owner, token, amount) => permit2Approvals(ctx, owner, token, amount),
|
|
629
|
+
build,
|
|
630
|
+
buy,
|
|
631
|
+
sell,
|
|
632
|
+
act,
|
|
633
|
+
/** the pilot sends what a model's answer asks for; { tx: null } for "none" */
|
|
634
|
+
pilot: async (token, answer, s = {}) => {
|
|
635
|
+
const r = await act(token, answer);
|
|
636
|
+
return { ...r, sent: r.tx ? await sendDesk(Promise.resolve(r.tx), s, token, "The desk refused the change.") : null };
|
|
637
|
+
},
|
|
638
|
+
propose: (token, p, note, s = {}) => sendDesk(build.propose(token, p, note), s, token, "The desk refused this program."),
|
|
639
|
+
cancel: (token, s = {}) => sendDesk(build.cancel(token), s, token),
|
|
640
|
+
move: (token, from, to, wei, s = {}) => sendDesk(build.move(token, from, to, wei), s, token),
|
|
641
|
+
handOver: (token, to, s = {}) => sendDesk(build.handOver(token, to), s, token),
|
|
642
|
+
activate: (token, s = {}) => sendDesk(build.activate(token), s, token, "The change is not due yet."),
|
|
643
|
+
veto: async (token, amount, s = {}) => {
|
|
644
|
+
const { address } = await signer(ctx, s.wallet);
|
|
645
|
+
const ap = amount > 0n ? await deskApproval(address, token, amount) : null;
|
|
646
|
+
if (ap)
|
|
647
|
+
await sendTx(ctx, ap, s);
|
|
648
|
+
return sendDesk(build.veto(token, amount), s, token, "The desk refused the veto.");
|
|
649
|
+
},
|
|
650
|
+
fire: async (token, amount, s = {}) => {
|
|
651
|
+
const { address } = await signer(ctx, s.wallet);
|
|
652
|
+
const ap = amount > 0n ? await deskApproval(address, token, amount) : null;
|
|
653
|
+
if (ap)
|
|
654
|
+
await sendTx(ctx, ap, s);
|
|
655
|
+
return sendDesk(build.fire(token, amount), s, token, "The desk refused this.");
|
|
656
|
+
},
|
|
657
|
+
withdraw: (token, s = {}) => sendDesk(build.withdraw(token), s, token),
|
|
658
|
+
give: (token, bucket, wei, s = {}) => sendDesk(build.give(token, bucket, wei), s, token),
|
|
659
|
+
claim: (s = {}) => sendDesk(build.claim(), s),
|
|
660
|
+
burn: (token, s = {}) => sendDesk(build.burn(token), s, token, "The burn run is not possible right now."),
|
|
661
|
+
addLiquidity: (token, s = {}) => sendDesk(build.addLiquidity(token), s, token, "The liquidity run is not possible right now."),
|
|
662
|
+
feedFloor: (token, s = {}) => sendDesk(build.feedFloor(token), s, token, "The floor run is not possible right now."),
|
|
663
|
+
settleJackpot: (token, s = {}) => sendDesk(build.settleJackpot(token), s, token),
|
|
664
|
+
launch,
|
|
665
|
+
prepareMetadata,
|
|
666
|
+
};
|
|
667
|
+
}
|