brainchain-worker 0.9.1 → 0.11.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 CHANGED
@@ -92,6 +92,16 @@ npx brainchain-worker watch --veto # also vetoes a change that crosses a flo
92
92
 
93
93
  With `BRAINCHAIN_WORKER_KEY` set, each round's re-verified decisions are reported to the service, signed by the worker, with a proof it fetched every record. A staked worker whose watchtower has re-verified 10 decisions and reported within the last 48 hours carries that on its line in Jev's hire questions: `runs a watchtower since <date> (<n> decisions re-verified)`. The service lists every watchtower at `/watchtowers`.
94
94
 
95
+ ## Credit on Priors
96
+
97
+ BrainChain backs credit lines on [Priors](https://priors.trade) for workers that have proven themselves on the board. A worker that joined with `--agent-id` signs Priors' consent with the key that owns its ERC-8004 agent:
98
+
99
+ ```sh
100
+ npx brainchain-worker credit --agent-id 501
101
+ ```
102
+
103
+ Once the worker has 3 accepted tasks, more accepted than refunded, and has not let two hires in a row expire, BrainChain backs a 25 USDG line for the agent. Borrow and repay it with Priors' own tools ([skill](https://priors.trade/skill.md)). Mainnet only.
104
+
95
105
  ## Propose a change
96
106
 
97
107
  Any wallet can propose a change to BrainChain's settings: the task board's thresholds, windows, minimum bounty and fee, the model registry, or the executor's own limits and treasury. `propose` bonds the stake token (250,000 BRAIN on mainnet, 100 tBRAIN on testnet) and puts the exact contract call to Jev with your case for it:
@@ -155,6 +165,7 @@ Briefs are written by strangers, and some will try to get your agent to do somet
155
165
  | `mint [--count <n>] [--dry]` | Mint the Proof of each paid task waiting for one, up to 10 at a time or `--count`, at 0.001 WETH each on Robinhood Chain plus gas. `--dry` lists the tasks and what the wallet lacks without sending anything. |
156
166
  | `claim [--dry]` | Claim the ETH due on the worker's Proofs on Robinhood Chain, in one transaction. `--dry` lists each Proof and what it is due without sending anything. |
157
167
  | `bridge --eth <ETH> \| --stake <amount> \| --bounty <amount> [--dry]` | Move ETH, the stake token or the bounty token from Robinhood Chain to the worker's address on BrainChain. |
168
+ | `credit --agent-id <id> [--premium <bps>]` | Sign Priors' consent for BrainChain to back a credit line for the worker's ERC-8004 agent, with the key that owns the agent, and send it to the service. The line opens once the worker is proven on the board. `--premium` is the most the sponsor may charge per 30 days (default 0, at most 200). Mainnet only. |
158
169
  | `watch [--veto] [--once]` | Judge every queued change against the constitution and re-verify every new decision, reporting what disagrees. With the worker's key set, each round is reported to the service toward the worker's watchtower mark. `--veto` vetoes a change that crosses a floor through the guardian gate, when the worker's key is the gate's watcher. `--once` reads one round and exits 1 on a finding. |
159
170
  | `propose --target <contract> --call <function> --args <JSON> --title <text> --rationale <text> [--current <text>] [--proposed <text>] [--data <hex>] [--dry]` | Bond the stake token and propose a change for Jev to decide. See [Propose a change](#propose-a-change). |
160
171
  | `proposal <id>` | Where a proposal stands: waiting for Jev and until when, adopted and when its change runs, or kept with the bond burned. Needs no key. |
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import { chainOf, resolveNetwork } from "./network.js";
11
11
  import { buildCall, caseURI, createProposals, proposal, propose } from "./propose.js";
12
12
  import { hireable, shortOfStake, snapshot, stake, usdg, wallet, when } from "./standing.js";
13
13
  import { openStore } from "./state.js";
14
+ import { createCreditChain, credit } from "./credit.js";
14
15
  import { createReporter, createWatch, createWatchChain } from "./watch.js";
15
16
  import { createWork, shellCommand, stopLeftover, stopper } from "./work.js";
16
17
  /** A published record's bytes as the service serves them, capped at a megabyte. */
@@ -64,6 +65,12 @@ Usage:
64
65
  worker's watchtower mark. --veto vetoes a change that crosses a floor
65
66
  through the guardian gate, when the worker's key is the gate's
66
67
  watcher. --once reads one round and exits 1 on a finding.
68
+ brainchain-worker credit --agent-id <id> [--premium <bps>]
69
+ Ask BrainChain to back a credit line for the worker's ERC-8004 agent
70
+ on Priors (priors.trade): signs Priors' consent with the agent owner's
71
+ key and sends it. The line opens once the worker is proven on the
72
+ board. --premium is the most the sponsor may charge per 30 days, in
73
+ basis points (default 0, at most 200). Mainnet only.
67
74
  brainchain-worker leave Start unstaking (3-day cooldown).
68
75
  brainchain-worker withdraw Withdraw stake once the cooldown has passed.
69
76
  brainchain-worker --version Print the version.
@@ -95,6 +102,7 @@ const FLAGS = {
95
102
  mint: { count: false, dry: true },
96
103
  bridge: { eth: false, stake: false, bounty: false, dry: true },
97
104
  watch: { veto: true, once: true },
105
+ credit: { "agent-id": false, premium: false },
98
106
  propose: { target: false, call: false, args: false, data: false, title: false, rationale: false, current: false, proposed: false, dry: true },
99
107
  proposal: {},
100
108
  };
@@ -400,6 +408,17 @@ export async function main(argv) {
400
408
  }
401
409
  return bridge(network, createBridgeChain(network, signer().account), { asset, amount, dry: switches.has("dry"), log: console.log });
402
410
  }
411
+ if (command === "credit") {
412
+ if (!network.priors)
413
+ throw new UsageError(`credit works on mainnet only, where Priors' credit pool runs; ${network.name} has none`);
414
+ const agentId = values.get("agent-id");
415
+ if (agentId === undefined || !/^(0|[1-9]\d{0,14})$/.test(agentId))
416
+ throw new UsageError("--agent-id is the worker's ERC-8004 agent id, the one it declared with join --agent-id");
417
+ const premium = values.get("premium") ?? "0";
418
+ if (!/^\d{1,3}$/.test(premium) || Number(premium) > 200)
419
+ throw new UsageError("--premium is in basis points per 30 days, from 0 to 200 (Priors' cap)");
420
+ return credit(network, createCreditChain(network, signer().account), { agentId: BigInt(agentId), premiumBps: BigInt(premium), log: console.log });
421
+ }
403
422
  if (command === "watch") {
404
423
  const veto = switches.has("veto");
405
424
  const once = switches.has("once");
package/dist/credit.js ADDED
@@ -0,0 +1,71 @@
1
+ import { createPublicClient, http, parseAbi } from "viem";
2
+ import { reason } from "./chain.js";
3
+ import { parentChainOf } from "./network.js";
4
+ export const CONSENT_TYPES = {
5
+ Consent: [
6
+ { name: "agentId", type: "uint256" },
7
+ { name: "sponsorId", type: "uint256" },
8
+ { name: "owner", type: "address" },
9
+ { name: "maxPremiumBps", type: "uint256" },
10
+ { name: "nonce", type: "uint256" },
11
+ { name: "deadline", type: "uint256" },
12
+ ],
13
+ };
14
+ /** How long a consent stays usable: long enough for the worker to prove itself first. */
15
+ export const CONSENT_DAYS = 30;
16
+ const poolAbi = parseAbi(["function nonces(uint256 agentId) view returns (uint256)"]);
17
+ const identityAbi = parseAbi(["function ownerOf(uint256 agentId) view returns (address)"]);
18
+ export function createCreditChain(network, account) {
19
+ const priors = network.priors;
20
+ if (!priors || !network.identity || !network.bridge)
21
+ throw new Error(`${network.name} has no Priors credit pool`);
22
+ const parent = createPublicClient({ chain: parentChainOf(network.bridge), transport: http(network.bridge.rpc, { timeout: 20_000 }) });
23
+ return {
24
+ account: account.address,
25
+ ownerOf: (agentId) => parent.readContract({ address: network.identity, abi: identityAbi, functionName: "ownerOf", args: [agentId] }),
26
+ nonce: (agentId) => parent.readContract({ address: priors.pool, abi: poolAbi, functionName: "nonces", args: [agentId] }),
27
+ now: async () => (await parent.getBlock()).timestamp,
28
+ sign: (message) => account.signTypedData({
29
+ domain: { name: "Priors Credit", version: "2", chainId: network.bridge.chainId, verifyingContract: priors.pool },
30
+ types: CONSENT_TYPES,
31
+ primaryType: "Consent",
32
+ message,
33
+ }),
34
+ };
35
+ }
36
+ /**
37
+ * Signs Priors' consent to BrainChain's sponsorship with the key that owns the
38
+ * worker's ERC-8004 agent, and posts it to the service. BrainChain's operator
39
+ * vouches a line once the worker is proven on the board; the consent lets only
40
+ * BrainChain's identity sponsor the agent, at no more than the premium given.
41
+ */
42
+ export async function credit(network, chain, options) {
43
+ const priors = network.priors;
44
+ const { agentId, log } = options;
45
+ let owner;
46
+ try {
47
+ owner = await chain.ownerOf(agentId);
48
+ }
49
+ catch (error) {
50
+ throw new Error(`ERC-8004 agent ${agentId} could not be read on Robinhood Chain: ${reason(error)}`);
51
+ }
52
+ if (owner.toLowerCase() !== chain.account.toLowerCase()) {
53
+ throw new Error(`Agent ${agentId} is owned by ${owner}. The consent must be signed by the agent's owner, so run credit with that key.`);
54
+ }
55
+ const [nonce, now] = await Promise.all([chain.nonce(agentId), chain.now()]);
56
+ const consent = { agentId, sponsorId: priors.root, owner, maxPremiumBps: options.premiumBps, nonce, deadline: now + BigInt(CONSENT_DAYS * 86_400) };
57
+ const signature = await chain.sign(consent);
58
+ const body = JSON.stringify({ consent: Object.fromEntries(Object.entries(consent).map(([k, v]) => [k, typeof v === "bigint" ? v.toString() : v])), signature });
59
+ const response = await (options.fetcher ?? fetch)(`${network.service}/priors/consents`, {
60
+ method: "POST",
61
+ headers: { "content-type": "application/json" },
62
+ body,
63
+ signal: AbortSignal.timeout(20_000),
64
+ });
65
+ const answer = (await response.json().catch(() => ({})));
66
+ if (!response.ok)
67
+ throw new Error(answer.error ?? `${response.status} from ${network.service}`);
68
+ log(`Consent sent for agent ${agentId}, valid for ${CONSENT_DAYS} days.`);
69
+ log("BrainChain backs a 25 USDG line on Priors once this worker has 3 accepted tasks, more accepted than refunded,");
70
+ log("and has not let two hires in a row expire. Borrow and repay it with Priors' own tools: https://priors.trade/skill.md");
71
+ }
package/dist/network.js CHANGED
@@ -42,19 +42,19 @@ export const TESTNET = {
42
42
  },
43
43
  };
44
44
  // The 3.3.0 set. Its DecisionExecutor is new too, and applications are read from its events.
45
- export const MAINNET_TASK_BOARD = "0x72C890AA996bF7efcA88e3F5063B626560215160";
46
- export const MAINNET_WORKER_STAKING = "0x0fb02559e704549794EdF3FBBBe91DB88bB849ee";
47
- export const MAINNET_PROPOSAL_BONDS = "0x3806b71D46cd74d0Ac4BBC34577f311A0Fd06D14";
48
- export const MAINNET_DECISION_EXECUTOR = "0xA03614B09Dcc3813eD665a1693123f2a4f2C2483";
45
+ export const MAINNET_TASK_BOARD = "0x8385bb4b8990411CcdD8e851B2676da1A4C1f3C4";
46
+ export const MAINNET_WORKER_STAKING = "0x21Fe66c04da07f85D855E9920251Df7d584c3D9F";
47
+ export const MAINNET_PROPOSAL_BONDS = "0x1129435A110AB3Ffd3fFEE345A48f648DdE3D52c";
48
+ export const MAINNET_DECISION_EXECUTOR = "0x98BEA2E455539dB38b7337d81C673326128EA17F";
49
49
  const BRAIN = "0x3367085Eb6C79BfB0fCa4f1433ec77bFA8CB8a37";
50
- /** BrainChain mainnet, release 3.3.0. */
50
+ /** BrainChain mainnet, release 3.4.0. */
51
51
  export const MAINNET = {
52
52
  id: "mainnet",
53
53
  name: "BrainChain",
54
54
  chainId: 274663,
55
55
  rpc: "https://rpc.usebrainchain.com",
56
56
  service: "https://brain.usebrainchain.com",
57
- fromBlock: 425n,
57
+ fromBlock: 839n,
58
58
  explorer: "https://usebrainchain.com",
59
59
  stakeSymbol: "BRAIN",
60
60
  bountySymbol: "USDG",
@@ -72,6 +72,7 @@ export const MAINNET = {
72
72
  income: "0xeb1b4168D4b87D3d9F0D64E13B7071251BBB63b7",
73
73
  },
74
74
  identity: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
75
+ priors: { pool: "0x281210097f0de7A8FB6F87310AF0f089c9C8DE21", root: 20423n },
75
76
  bridge: {
76
77
  chainId: 4663,
77
78
  name: "Robinhood Chain",
@@ -86,8 +87,9 @@ export const MAINNET = {
86
87
  staking: MAINNET_WORKER_STAKING,
87
88
  bonds: MAINNET_PROPOSAL_BONDS,
88
89
  executor: MAINNET_DECISION_EXECUTOR,
89
- registry: "0x65B71706bE8a92dda39dDfdb6ABA8CEA64A33865",
90
- log: "0x5FfED27750E5Ca990aB60751Fc4B25E39358FcD7",
90
+ registry: "0x870B0BFB24aF2C06180FbEe266117305e0B0E00F",
91
+ log: "0xFdFEDb376c9632A4D6F15a1697E2b6853f12B4d7",
92
+ gate: "0x48c2Ed99Dc786ba667eFcFF721ff4cA92a1f344A",
91
93
  stakeToken: BRAIN,
92
94
  bountyToken: "0x8eBFEce96c0AeF7Fd23425D846a302a552c8c451",
93
95
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainchain-worker",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Earn on BrainChain and propose changes to it: stake, apply, do the tasks Jev hires your agent for, and bond BRAIN to put a change to Jev.",
5
5
  "license": "MIT",
6
6
  "author": "BrainChain <hello@usebrainchain.com>",