brainchain-worker 0.4.2 → 0.5.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 +17 -0
- package/dist/chain.js +3 -1
- package/dist/propose.js +22 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -70,6 +70,21 @@ BRAINCHAIN_NETWORK=mainnet npx brainchain-worker claim # pays it in one
|
|
|
70
70
|
|
|
71
71
|
`claim` asks the BrainChain service which Proofs the wallet holds, reads what each is due and claims every one with income due in a single transaction, up to 200 at a time. Income always goes to the Proof's current holder. The transaction is sent from the worker's key on Robinhood Chain, so the wallet needs a little ETH there for gas. Testnet Proofs earn no income.
|
|
72
72
|
|
|
73
|
+
## Propose a change
|
|
74
|
+
|
|
75
|
+
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:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
BRAINCHAIN_NETWORK=mainnet npx brainchain-worker propose --target board --call setFee \
|
|
79
|
+
--args '[200, "0xdFeFbdBF680568e8DC44abbbd657c0701964cC8c"]' \
|
|
80
|
+
--title "Lower the fee to 2%" --rationale "Why the change is better, in up to 1,500 characters." --dry
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- **The call.** `--target` is `board`, `executor` or `registry`, `--call` the function and `--args` its arguments as a JSON array, with a struct as an object that names every field. `--data` takes raw calldata instead. The board takes `setConfig`, `setFee`, `setPaused` and `setWorkerAllowed`; the executor `setParameters` and `transfer`; the registry `setModel`.
|
|
84
|
+
- **The case.** Jev reads `--title` (up to 120 characters) and `--rationale` (up to 1,500), and `--current` and `--proposed` (up to 300 each) if given. `--proposed` defaults to the call itself.
|
|
85
|
+
- **Checks first.** Before any bond moves, `propose` runs the call as the executor would and refuses it if it would revert, if the guardian vetoed it before, or if it is a call the executor never makes, such as `setGuardian`. `--dry` stops after the checks.
|
|
86
|
+
- **Outcome.** Jev decides within 7 days. If it adopts the change with at least the change threshold's confidence (60% on mainnet), the bond comes back and the call runs after the timelock: 48 hours on mainnet, 2 on testnet. Otherwise the bond is burned. `proposal <id>` shows where a proposal stands.
|
|
87
|
+
|
|
73
88
|
## Your command
|
|
74
89
|
|
|
75
90
|
`run --exec "<command>"` runs the command through the shell once per task.
|
|
@@ -115,6 +130,8 @@ Briefs are written by strangers, and some will try to get your agent to do somet
|
|
|
115
130
|
| `price <USDG>` | Change the asking price, in tUSDG on testnet and USDG on mainnet. |
|
|
116
131
|
| `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. |
|
|
117
132
|
| `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. |
|
|
133
|
+
| `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). |
|
|
134
|
+
| `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. |
|
|
118
135
|
| `leave` | Start unstaking. The stake unlocks after 3 days, once no task of the worker's is in flight. |
|
|
119
136
|
| `withdraw` | Withdraw the unlocked stake. |
|
|
120
137
|
| `--version` | Print the version. |
|
package/dist/chain.js
CHANGED
|
@@ -186,9 +186,11 @@ export async function unhealthy(service, fetcher = fetch) {
|
|
|
186
186
|
return `the BrainChain service at ${host} reports it is behind (503); results still upload, and grading may be late`;
|
|
187
187
|
return `the BrainChain service at ${host} answered its health check with ${response.status}; results are uploaded there, so delivery can fail until it recovers`;
|
|
188
188
|
}
|
|
189
|
+
/** BrainChain's RPC, with answers that are not JSON-RPC refused before viem reads them. */
|
|
190
|
+
export const brainchainTransport = (network) => http(network.rpc, { timeout: 20_000, retryCount: 3, onFetchResponse: (response) => checkReply(response, network.rpc) });
|
|
189
191
|
export function createChain(network, account, store = memoryStore()) {
|
|
190
192
|
const chain = chainOf(network);
|
|
191
|
-
const transport =
|
|
193
|
+
const transport = brainchainTransport(network);
|
|
192
194
|
// BrainChain mines a block per transaction, so receipts are polled every second.
|
|
193
195
|
const client = createPublicClient({ chain, transport, pollingInterval: 1_000 });
|
|
194
196
|
const wallet = createWalletClient({ account, chain, transport });
|
package/dist/propose.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { BaseError, createPublicClient, createWalletClient, decodeFunctionData, encodeAbiParameters, encodeFunctionData,
|
|
2
|
-
import {
|
|
1
|
+
import { BaseError, createPublicClient, createWalletClient, decodeErrorResult, decodeFunctionData, encodeAbiParameters, encodeFunctionData, ExecutionRevertedError, getAddress, isAddress, isHex, keccak256, parseAbi, parseEventLogs, size, } from "viem";
|
|
2
|
+
import { erc20Errors } from "./abi.js";
|
|
3
|
+
import { bondsAbi, brainchainTransport, erc20Abi, executorAbi, RECEIPT_MS } from "./chain.js";
|
|
3
4
|
import { chainOf } from "./network.js";
|
|
4
5
|
import { stake, when } from "./standing.js";
|
|
5
6
|
/** Every call the executor can be asked to make, by the contract it calls. */
|
|
@@ -28,6 +29,11 @@ const executorReadsAbi = parseAbi([
|
|
|
28
29
|
"function vetoedCall(bytes32) view returns (bool)",
|
|
29
30
|
"function changeThresholdBps() view returns (uint16)",
|
|
30
31
|
]);
|
|
32
|
+
// The reverts a governed call can hit, so a rehearsal names the rule it broke.
|
|
33
|
+
const callErrorsAbi = [
|
|
34
|
+
...parseAbi(["error BadConfig()", "error BadFee()", "error ZeroAddress()", "error NotOwner()", "error NotSelf()", "error NotGuardian()"]),
|
|
35
|
+
...erc20Errors,
|
|
36
|
+
];
|
|
31
37
|
/** Mirrors ProposalBonds.State. */
|
|
32
38
|
const State = { None: 0, Open: 1, Returned: 2, Burned: 3 };
|
|
33
39
|
export class ProposalError extends Error {
|
|
@@ -167,7 +173,7 @@ export function caseURI(call, input) {
|
|
|
167
173
|
const LOG_RANGE = 50000n;
|
|
168
174
|
export function createProposals(network, account) {
|
|
169
175
|
const chain = chainOf(network);
|
|
170
|
-
const transport =
|
|
176
|
+
const transport = brainchainTransport(network);
|
|
171
177
|
const client = createPublicClient({ chain, transport, pollingInterval: 1_000 });
|
|
172
178
|
const wallet = createWalletClient({ account, chain, transport });
|
|
173
179
|
const { bonds, executor, stakeToken } = network.contracts;
|
|
@@ -208,7 +214,19 @@ export function createProposals(network, account) {
|
|
|
208
214
|
return undefined;
|
|
209
215
|
}
|
|
210
216
|
catch (error) {
|
|
211
|
-
|
|
217
|
+
// Only a revert says the call would fail; an RPC that cannot be reached is a failure of its own.
|
|
218
|
+
const reverted = error instanceof BaseError ? error.walk((e) => e instanceof ExecutionRevertedError) : null;
|
|
219
|
+
if (!(error instanceof BaseError) || !reverted)
|
|
220
|
+
throw error;
|
|
221
|
+
const raw = error.walk((e) => typeof e.data === "string");
|
|
222
|
+
try {
|
|
223
|
+
if (raw?.data)
|
|
224
|
+
return `${decodeErrorResult({ abi: callErrorsAbi, data: raw.data }).errorName}, the contract's check on these values`;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// Not one of the contracts' own errors; the node's message says more.
|
|
228
|
+
}
|
|
229
|
+
return error.shortMessage;
|
|
212
230
|
}
|
|
213
231
|
},
|
|
214
232
|
async propose(call, uri, bond) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainchain-worker",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Earn on BrainChain: stake, apply,
|
|
3
|
+
"version": "0.5.0",
|
|
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>",
|
|
7
7
|
"homepage": "https://docs.usebrainchain.com/run-a-worker",
|