@finchip.ai/foga 0.0.0-stage → 0.6.0-alpha.4
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 +201 -0
- package/NOTICE +21 -0
- package/README.md +216 -2
- package/artifacts/bundle.json +74250 -0
- package/artifacts/bundle.sha256 +1 -0
- package/bin/foga.js +14 -0
- package/build/known-interface-ids.json +18 -0
- package/build/profiles.json +120 -0
- package/dist/build/bundle.js +417 -0
- package/dist/build/vendor.js +74 -0
- package/dist/bundle/load.js +24 -0
- package/dist/bundle/types.js +1 -0
- package/dist/cli-cell.js +395 -0
- package/dist/cli.js +505 -0
- package/dist/core/acdf.js +162 -0
- package/dist/core/adrf.js +273 -0
- package/dist/core/aid.js +83 -0
- package/dist/core/ard.js +146 -0
- package/dist/core/asf.js +157 -0
- package/dist/core/canonical.js +79 -0
- package/dist/core/erc8004.js +52 -0
- package/dist/core/kya.js +225 -0
- package/dist/core/measure.js +96 -0
- package/dist/core/pack/cbor.js +130 -0
- package/dist/core/pack/skillpack.js +142 -0
- package/dist/core/skills.js +40 -0
- package/dist/core/swarm.js +206 -0
- package/dist/core/tenders.js +121 -0
- package/dist/core/units.js +122 -0
- package/dist/core/world.js +104 -0
- package/dist/demo/demander.js +76 -0
- package/dist/demo/evidence.js +1 -0
- package/dist/demo/loop-d.js +449 -0
- package/dist/doctor/checks.js +51 -0
- package/dist/doctor/doctor.js +385 -0
- package/dist/doctor/loop-d.js +103 -0
- package/dist/doctor/probe.js +97 -0
- package/dist/doctor/semantic.js +83 -0
- package/dist/genesis/extract.js +64 -0
- package/dist/genesis/funding.js +126 -0
- package/dist/genesis/operator-scope.js +90 -0
- package/dist/genesis/preflight.js +54 -0
- package/dist/genesis/probe.js +164 -0
- package/dist/genesis/roles.js +96 -0
- package/dist/genesis/run.js +71 -0
- package/dist/genesis/scenario.js +765 -0
- package/dist/genesis/skill-runner.js +103 -0
- package/dist/genesis/skill-worker.js +41 -0
- package/dist/genesis/t1-zk.js +62 -0
- package/dist/genesis/t3-zk.js +105 -0
- package/dist/genesis/t4.js +259 -0
- package/dist/genesis/t5.js +168 -0
- package/dist/genesis/t6.js +222 -0
- package/dist/genesis/t7.js +198 -0
- package/dist/genesis/t8.js +189 -0
- package/dist/genesis/t9.js +188 -0
- package/dist/genesis/zk.js +128 -0
- package/dist/http/server.js +276 -0
- package/dist/kernel/cell.js +104 -0
- package/dist/kernel/errors.js +78 -0
- package/dist/kernel/kernel.js +251 -0
- package/dist/kernel/provider.js +125 -0
- package/dist/kernel/store.js +470 -0
- package/dist/kernel/tx.js +385 -0
- package/dist/mcp/server.js +170 -0
- package/dist/node/config.js +195 -0
- package/dist/node/init.js +200 -0
- package/dist/node/keys.js +95 -0
- package/dist/node/migrate.js +173 -0
- package/dist/node/model/anthropic.js +97 -0
- package/dist/node/model/stub.js +129 -0
- package/dist/node/model/types.js +9 -0
- package/dist/node/node.js +198 -0
- package/dist/node/policy.js +95 -0
- package/dist/node/runner.js +256 -0
- package/dist/node/watchers.js +88 -0
- package/dist/tools/admin.js +184 -0
- package/dist/tools/catalog.js +261 -0
- package/dist/tools/common.js +143 -0
- package/dist/tools/identity.js +107 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/read.js +471 -0
- package/dist/tools/tenders.js +400 -0
- package/dist/tools/types.js +4 -0
- package/dist/util/bytecode.js +123 -0
- package/dist/util/fs.js +58 -0
- package/dist/util/hash.js +4 -0
- package/dist/util/log.js +11 -0
- package/dist/util/version.js +12 -0
- package/dist/world/deploy.js +277 -0
- package/dist/world/plan.js +93 -0
- package/dist/world/types.js +1 -0
- package/docs/build.md +45 -0
- package/docs/cell-account.md +58 -0
- package/docs/compatibility-matrix.md +38 -0
- package/docs/design-memo.md +113 -0
- package/docs/doctor.md +53 -0
- package/docs/examples/README.md +11 -0
- package/docs/examples/claude-code-mcp.json +13 -0
- package/docs/examples/claude_desktop_config.json +12 -0
- package/docs/examples/cursor-mcp.json +11 -0
- package/docs/genesis.md +312 -0
- package/docs/kernel.md +101 -0
- package/docs/loop-d.md +90 -0
- package/docs/mcp.md +105 -0
- package/docs/node.md +66 -0
- package/docs/version-lock.md +67 -0
- package/docs/world.md +50 -0
- package/docs/zk.md +48 -0
- package/package.json +82 -4
- package/prover/README.md +26 -0
- package/prover/build-predicate.mjs +77 -0
- package/prover/package-lock.json +1837 -0
- package/prover/package.json +15 -0
- package/prover/run.cjs +199 -0
- package/scenarios/fixtures/sample-filing.txt +16 -0
- package/scenarios/skills/finch-extract-verify/LICENSE +2 -0
- package/scenarios/skills/finch-extract-verify/SKILL.md +34 -0
- package/scenarios/skills/finch-extract-verify/fields.json +56 -0
- package/scenarios/skills/finch-extract-verify/manifest.json +16 -0
- package/scenarios/skills/foga-probe/LICENSE +2 -0
- package/scenarios/skills/foga-probe/SKILL.md +35 -0
- package/scenarios/skills/foga-probe/manifest.json +16 -0
- package/scenarios/skills/foga-probe/probe.schema.json +40 -0
- package/server.json +79 -0
- package/vendor/LOCK.json +107 -0
- package/vendor/README.md +13 -0
- package/worlds/local.json +34 -0
- package/worlds/sepolia.json +314 -0
- package/zk/build/verification_key.json +174 -0
- package/zk/circuits/kya_predicate_count_v1.circom +231 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { concat, keccak256, toUtf8Bytes } from "ethers";
|
|
2
|
+
import { accountRef, dataUriJcs, erc721Ref, jcs, keccakUtf8 } from "./canonical.js";
|
|
3
|
+
import { eventArgs } from "./world.js";
|
|
4
|
+
export function elementId(kind, ref) {
|
|
5
|
+
return keccak256(concat([toUtf8Bytes(kind), new Uint8Array([0x3a]), toUtf8Bytes(ref)]));
|
|
6
|
+
}
|
|
7
|
+
export function unitRefString(chainId, registry, unitId, version) {
|
|
8
|
+
return `auf:eip155:${chainId}:${registry.toLowerCase()}:${unitId}@${version}`;
|
|
9
|
+
}
|
|
10
|
+
/** The unit of a single cell: the agent element, its treasury position and the deliverable/funds ports. */
|
|
11
|
+
export function cellUnitDefinition(p) {
|
|
12
|
+
const agent = { kind: "agent", ref: accountRef(p.chainId, p.cell), label: `${p.name} (cell account)` };
|
|
13
|
+
const treasury = { kind: "financial-position", ref: accountRef(p.chainId, p.cell), label: "cell account balance" };
|
|
14
|
+
return {
|
|
15
|
+
auf: "1.0",
|
|
16
|
+
kind: "unit-definition",
|
|
17
|
+
unit: { name: p.name, summary: p.summary ?? "A FOGA cell: one address acting under the ten-ERC stack", tags: ["foga", "cell"] },
|
|
18
|
+
elements: [agent, treasury],
|
|
19
|
+
relations: [
|
|
20
|
+
{ type: "owns", from: "unit", to: elementId(agent.kind, agent.ref), exclusive: true, share: "1", basis: ["erc8434:anchor"] },
|
|
21
|
+
{ type: "holds", from: "unit", to: elementId(treasury.kind, treasury.ref), quantity: { amount: "0", unit: "ETH" }, scope: "native balance of the cell account" },
|
|
22
|
+
],
|
|
23
|
+
ports: [
|
|
24
|
+
{ id: "deliverable-out", direction: "out", class: "deliverable", template: "auf:port:deliverable-out@1", conditions: "accepted fulfillments on ERC-8414 tenders" },
|
|
25
|
+
{ id: "funds-in", direction: "in", class: "funds", template: "auf:port:funds-in@1", asset: `eip155:${p.chainId}/slip44:60`, account: accountRef(p.chainId, p.cell), conditions: "tender rewards and service fees" },
|
|
26
|
+
{ id: "funds-out", direction: "out", class: "funds", template: "auf:port:funds-out@1", asset: `eip155:${p.chainId}/slip44:60`, account: accountRef(p.chainId, p.cell), conditions: "gas and purchased services" },
|
|
27
|
+
],
|
|
28
|
+
authorities: { definitionController: accountRef(p.chainId, p.cell), settlementAccounts: [accountRef(p.chainId, p.cell)] },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** The unit of a skill token held by a cell: a source element the cell licenses out. */
|
|
32
|
+
export function skillUnitDefinition(p) {
|
|
33
|
+
const skill = { kind: "skill", ref: erc721Ref(p.chainId, p.skillToken, p.tokenId), label: p.name };
|
|
34
|
+
return {
|
|
35
|
+
auf: "1.0",
|
|
36
|
+
kind: "unit-definition",
|
|
37
|
+
unit: { name: p.name, summary: p.summary ?? "An ERC-8338 skill package as an economic source", tags: ["foga", "skill", "source"] },
|
|
38
|
+
elements: [skill],
|
|
39
|
+
relations: [{ type: "owns", from: "unit", to: elementId(skill.kind, skill.ref), exclusive: true, share: "1", basis: ["erc8338:owner"] }],
|
|
40
|
+
ports: [
|
|
41
|
+
{ id: "calls-out", direction: "out", class: "deliverable", template: "auf:port:deliverable-out@1", conditions: "executions of the skill" },
|
|
42
|
+
{ id: "fees-in", direction: "in", class: "funds", template: "auf:port:funds-in@1", asset: `eip155:${p.chainId}/slip44:60`, account: accountRef(p.chainId, p.holder), conditions: "per-call fees and tender rewards earned with the skill" },
|
|
43
|
+
],
|
|
44
|
+
authorities: { definitionController: accountRef(p.chainId, p.holder), settlementAccounts: [accountRef(p.chainId, p.holder)] },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export class Units {
|
|
48
|
+
world;
|
|
49
|
+
constructor(world) {
|
|
50
|
+
this.world = world;
|
|
51
|
+
}
|
|
52
|
+
registry(runner) {
|
|
53
|
+
return this.world.contract("8454/AUFUnitRegistry", runner);
|
|
54
|
+
}
|
|
55
|
+
get registryAddress() {
|
|
56
|
+
return this.world.address("8454/AUFUnitRegistry");
|
|
57
|
+
}
|
|
58
|
+
boundaryHash(def) {
|
|
59
|
+
return keccakUtf8(jcs(def));
|
|
60
|
+
}
|
|
61
|
+
encodeRegisterUnit(controller, def, contains = []) {
|
|
62
|
+
return this.registry().interface.encodeFunctionData("registerUnit", [controller, this.boundaryHash(def), dataUriJcs(def), contains.map((c) => [c.unitId, c.version])]);
|
|
63
|
+
}
|
|
64
|
+
async registerUnit(signer, controller, def, contains = []) {
|
|
65
|
+
const planned = (await this.unitCount()) + 1n;
|
|
66
|
+
const tx = await this.registry(signer).registerUnit(controller, this.boundaryHash(def), dataUriJcs(def), contains.map((c) => [c.unitId, c.version]));
|
|
67
|
+
const receipt = await tx.wait();
|
|
68
|
+
return { unitId: planned, receipt };
|
|
69
|
+
}
|
|
70
|
+
unitIdFromReceipt(receipt) {
|
|
71
|
+
return BigInt(eventArgs(this.registry().interface, receipt, "UnitRegistered", this.registryAddress).unitId);
|
|
72
|
+
}
|
|
73
|
+
async unitCount() {
|
|
74
|
+
return BigInt(await this.registry().unitCount());
|
|
75
|
+
}
|
|
76
|
+
async controllerOf(unitId) {
|
|
77
|
+
return this.registry().controllerOf(unitId);
|
|
78
|
+
}
|
|
79
|
+
async latestVersion(unitId) {
|
|
80
|
+
return Number(await this.registry().latestVersion(unitId));
|
|
81
|
+
}
|
|
82
|
+
async versionOf(unitId, version) {
|
|
83
|
+
const v = await this.registry().versionOf(unitId, version);
|
|
84
|
+
return { boundaryHash: v.boundaryHash ?? v[0], descriptorURI: v.descriptorURI ?? v[1] };
|
|
85
|
+
}
|
|
86
|
+
// ---- the assertion registry (observations, valuations, descriptors) ----
|
|
87
|
+
assertions(runner) {
|
|
88
|
+
return this.world.contract("8454/AUFAssertionRegistry", runner);
|
|
89
|
+
}
|
|
90
|
+
get assertionRegistryAddress() {
|
|
91
|
+
return this.world.address("8454/AUFAssertionRegistry");
|
|
92
|
+
}
|
|
93
|
+
encodeRegisterDescriptor(descriptorHash, kind, uri) {
|
|
94
|
+
return this.assertions().interface.encodeFunctionData("registerDescriptor", [descriptorHash, kind, uri]);
|
|
95
|
+
}
|
|
96
|
+
encodeAssertStatement(input) {
|
|
97
|
+
return this.assertions().interface.encodeFunctionData("assertStatement", [input]);
|
|
98
|
+
}
|
|
99
|
+
assertionIdFromReceipt(receipt) {
|
|
100
|
+
return eventArgs(this.assertions().interface, receipt, "Asserted", this.assertionRegistryAddress).assertionId;
|
|
101
|
+
}
|
|
102
|
+
async descriptorOf(hash) {
|
|
103
|
+
try {
|
|
104
|
+
const d = await this.assertions().descriptorOf(hash);
|
|
105
|
+
return { publisher: d.publisher ?? d[0], kind: d.descriptorKind ?? d[1], registeredAt: Number(d.registeredAt ?? d[2]), uri: d.uri ?? d[3] };
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async assertionOf(assertionId) {
|
|
112
|
+
const a = await this.assertions().assertionOf(assertionId);
|
|
113
|
+
return { kind: Number(a.kind), status: Number(a.status), byController: Boolean(a.byController), version: Number(a.version), asserter: a.asserter, unitId: BigInt(a.unitId), profileId: a.profileId, contentHash: a.contentHash, contentURI: a.contentURI, validFrom: Number(a.validFrom), validUntil: Number(a.validUntil) };
|
|
114
|
+
}
|
|
115
|
+
async observationOf(assertionId) {
|
|
116
|
+
const o = await this.assertions().observationCoreOf(assertionId);
|
|
117
|
+
return { snapshotHash: o.snapshotHash ?? o[0], windowStart: Number(o.windowStart ?? o[1]), windowEnd: Number(o.windowEnd ?? o[2]) };
|
|
118
|
+
}
|
|
119
|
+
async assertionCount(unitId, version) {
|
|
120
|
+
return Number(await this.assertions().assertionCount(unitId, version));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Contract, Interface } from "ethers";
|
|
2
|
+
import { PinnedProvider } from "../kernel/provider.js";
|
|
3
|
+
import { loadBundle } from "../bundle/load.js";
|
|
4
|
+
import { readJson } from "../util/fs.js";
|
|
5
|
+
/**
|
|
6
|
+
* A handle on one world instance: the address book plus the ABIs of everything in it (from the bundle),
|
|
7
|
+
* so every module can open a typed ethers Contract by step id ("8414/TaskToken") without carrying ABIs
|
|
8
|
+
* around. Contracts FOGA deploys during a scenario (cell accounts, adapters) are registered on the handle
|
|
9
|
+
* with the bundle key they were built from.
|
|
10
|
+
*/
|
|
11
|
+
export class World {
|
|
12
|
+
file;
|
|
13
|
+
bundle;
|
|
14
|
+
provider;
|
|
15
|
+
extras;
|
|
16
|
+
dynamic = new Map();
|
|
17
|
+
constructor(file, bundle, provider) {
|
|
18
|
+
this.file = file;
|
|
19
|
+
this.bundle = bundle;
|
|
20
|
+
this.provider = provider;
|
|
21
|
+
this.extras = bundle.extras;
|
|
22
|
+
}
|
|
23
|
+
static load(worldPath, rpc, bundlePath) {
|
|
24
|
+
const file = readJson(worldPath);
|
|
25
|
+
if (file.schema !== "foga.world/1")
|
|
26
|
+
throw new Error(`${worldPath} is not a world file`);
|
|
27
|
+
const bundle = loadBundle(bundlePath);
|
|
28
|
+
const url = rpc ?? process.env.FOGA_RPC_URL ?? (file.network === "anvil" ? "http://127.0.0.1:8545" : undefined);
|
|
29
|
+
if (!url)
|
|
30
|
+
throw new Error("no RPC: pass --rpc or set FOGA_RPC_URL");
|
|
31
|
+
// Reads are pinned to the highest block the provider has seen (kernel/provider.ts): a public endpoint is a
|
|
32
|
+
// cluster whose nodes lag each other by a block, and the kernel reads what it has just written.
|
|
33
|
+
const provider = new PinnedProvider(url, undefined, { staticNetwork: true, cacheTimeout: -1 });
|
|
34
|
+
// A local node answers in microseconds and mines on demand; a public endpoint is shared, rate-limited and
|
|
35
|
+
// makes a block every 12 s, so it is asked a few times per block, not several times per second.
|
|
36
|
+
provider.pollingInterval = file.network === "anvil" ? 250 : 4_000;
|
|
37
|
+
return new World(file, bundle, provider);
|
|
38
|
+
}
|
|
39
|
+
get chainId() {
|
|
40
|
+
return this.file.chainId;
|
|
41
|
+
}
|
|
42
|
+
has(id) {
|
|
43
|
+
return Boolean(this.file.contracts[id] || this.dynamic.get(id));
|
|
44
|
+
}
|
|
45
|
+
address(id) {
|
|
46
|
+
const entry = this.file.contracts[id] ?? this.dynamic.get(id);
|
|
47
|
+
if (!entry)
|
|
48
|
+
throw new Error(`world ${this.file.worldId} has no contract ${id}`);
|
|
49
|
+
return entry.address;
|
|
50
|
+
}
|
|
51
|
+
bundleKeyOf(id) {
|
|
52
|
+
const entry = this.file.contracts[id] ?? this.dynamic.get(id);
|
|
53
|
+
if (!entry?.bundleKey)
|
|
54
|
+
throw new Error(`world contract ${id} has no bundle key`);
|
|
55
|
+
return entry.bundleKey;
|
|
56
|
+
}
|
|
57
|
+
artifact(bundleKey) {
|
|
58
|
+
const c = this.bundle.contracts[bundleKey];
|
|
59
|
+
if (!c)
|
|
60
|
+
throw new Error(`bundle has no contract ${bundleKey}`);
|
|
61
|
+
return c;
|
|
62
|
+
}
|
|
63
|
+
abi(id) {
|
|
64
|
+
return this.artifact(this.bundleKeyOf(id)).abi;
|
|
65
|
+
}
|
|
66
|
+
iface(id) {
|
|
67
|
+
return new Interface(this.abi(id));
|
|
68
|
+
}
|
|
69
|
+
ifaceOf(bundleKey) {
|
|
70
|
+
return new Interface(this.artifact(bundleKey).abi);
|
|
71
|
+
}
|
|
72
|
+
/** Open a world contract; pass a signer to send transactions. */
|
|
73
|
+
contract(id, runner) {
|
|
74
|
+
return new Contract(this.address(id), this.abi(id), runner ?? this.provider);
|
|
75
|
+
}
|
|
76
|
+
/** Open any address with the ABI of a bundle contract. */
|
|
77
|
+
at(bundleKey, address, runner) {
|
|
78
|
+
return new Contract(address, this.artifact(bundleKey).abi, runner ?? this.provider);
|
|
79
|
+
}
|
|
80
|
+
/** Register a scenario-deployed contract under a step id for later lookups. */
|
|
81
|
+
attach(id, address, bundleKey) {
|
|
82
|
+
this.dynamic.set(id, { address, bundleKey });
|
|
83
|
+
}
|
|
84
|
+
async blockTimestamp() {
|
|
85
|
+
const b = await this.provider.getBlock("latest");
|
|
86
|
+
return Number(b.timestamp);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Decode the first event of a given name from a receipt using an Interface. */
|
|
90
|
+
export function eventArgs(iface, receipt, name, address) {
|
|
91
|
+
for (const log of receipt.logs) {
|
|
92
|
+
if (address && log.address.toLowerCase() !== address.toLowerCase())
|
|
93
|
+
continue;
|
|
94
|
+
try {
|
|
95
|
+
const parsed = iface.parseLog({ topics: [...log.topics], data: log.data });
|
|
96
|
+
if (parsed && parsed.name === name)
|
|
97
|
+
return parsed.args;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* not ours */
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error(`event ${name} not found in receipt`);
|
|
104
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Wallet } from "ethers";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { dataUri, keccakJcs, sha256Hex } from "../core/canonical.js";
|
|
4
|
+
import { hashlock, nativeTerms } from "../core/tenders.js";
|
|
5
|
+
import { extractVerifyFile } from "../genesis/extract.js";
|
|
6
|
+
import { runProbe } from "../genesis/probe.js";
|
|
7
|
+
import { Kernel } from "../kernel/kernel.js";
|
|
8
|
+
import { repoRoot } from "../util/fs.js";
|
|
9
|
+
export const ALL_CASES = ["a", "b", "c", "d", "d2", "e", "f"];
|
|
10
|
+
/** A small deterministic generator (mulberry32), so a seed reproduces the order and the probe block. */
|
|
11
|
+
export function prng(seed) {
|
|
12
|
+
let a = seed >>> 0;
|
|
13
|
+
return () => {
|
|
14
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
15
|
+
let t = a;
|
|
16
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
17
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
18
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function shuffle(items, rnd) {
|
|
22
|
+
const out = [...items];
|
|
23
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
24
|
+
const j = Math.floor(rnd() * (i + 1));
|
|
25
|
+
[out[i], out[j]] = [out[j], out[i]];
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
const DAY = 86400;
|
|
30
|
+
export async function mintCases(opts) {
|
|
31
|
+
const log = opts.log ?? (() => { });
|
|
32
|
+
const k = await Kernel.open({ worldPath: opts.worldPath, rpc: opts.rpc, storePath: opts.storePath, keys: { demander: opts.key } });
|
|
33
|
+
try {
|
|
34
|
+
const me = new Wallet(opts.key).address;
|
|
35
|
+
const rnd = prng(opts.seed);
|
|
36
|
+
const reward = opts.rewardWei ?? 1000000000000000n; // 0.001 ETH
|
|
37
|
+
const below = opts.belowFloorWei ?? 100000000000000n; // 0.0001 ETH
|
|
38
|
+
const root = repoRoot();
|
|
39
|
+
const head = await k.blockNumber();
|
|
40
|
+
const probeBlock = Math.max(1, head - Math.floor(rnd() * Math.min(head - 1, 50)));
|
|
41
|
+
const probeInput = { subject: opts.cell, block: probeBlock, lookback: 9000, kya: { schemeId: opts.schemeId, issuers: opts.issuers } };
|
|
42
|
+
const probeAnswer = (await runProbe(k.world, probeInput)).answer;
|
|
43
|
+
const fixture = join(root, "scenarios", "fixtures", "sample-filing.txt");
|
|
44
|
+
const extractAnswer = extractVerifyFile(fixture, join(root, "scenarios", "skills", "finch-extract-verify", "fields.json"), opts.packages["finch-extract-verify"]).canonical;
|
|
45
|
+
const specs = {
|
|
46
|
+
a: { skill: "foga-probe", packageHash: opts.packages["foga-probe"], input: probeInput, answer: probeAnswer, rewardWei: reward, submitIn: DAY, settleIn: 7 * DAY, expect: { outcome: "taken" }, note: `foga-probe of the cell at block ${probeBlock}` },
|
|
47
|
+
b: { skill: "finch-extract-verify", packageHash: opts.packages["finch-extract-verify"], input: { document: "sample-filing.txt" }, answer: extractAnswer, rewardWei: reward, submitIn: DAY, settleIn: 7 * DAY, expect: { outcome: "taken" }, note: "finch-extract-verify over the fixture named by file name" },
|
|
48
|
+
c: { skill: "foga-oracle", packageHash: "0x" + "ab".repeat(32), input: { question: "what is the price" }, answer: "unknown", rewardWei: reward, submitIn: DAY, settleIn: 7 * DAY, expect: { outcome: "refused", gate: "skill" }, note: "a skill the cell does not have" },
|
|
49
|
+
d: { skill: "foga-probe", packageHash: opts.packages["foga-probe"], input: probeInput, answer: probeAnswer, rewardWei: below, submitIn: DAY, settleIn: 7 * DAY, expect: { outcome: "refused", gate: "policy" }, note: "a reward below the policy floor" },
|
|
50
|
+
d2: { skill: "foga-probe", packageHash: opts.packages["foga-probe"], input: probeInput, answer: probeAnswer, rewardWei: reward, submitIn: 120, settleIn: 7 * DAY, expect: { outcome: "refused", gate: "policy" }, note: "a submit deadline too close" },
|
|
51
|
+
e: { skill: "foga-probe", packageHash: opts.packages["foga-probe"], input: probeInput, answer: null, rewardWei: reward, submitIn: DAY, settleIn: 7 * DAY, authority: me, expect: { outcome: "refused", gate: "verifier" }, note: "a judged tender with the demander as its authority" },
|
|
52
|
+
f: { skill: "foga-probe", packageHash: opts.packages["foga-probe"], input: probeInput, answer: probeAnswer, rewardWei: reward, submitIn: DAY, settleIn: 7 * DAY, expect: { outcome: "refused-then-taken", gate: "safety" }, note: "a tender like a, met during the guardian's suspension" },
|
|
53
|
+
};
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const id of shuffle(opts.cases, rnd)) {
|
|
56
|
+
const p = specs[id];
|
|
57
|
+
const t0 = await k.blockTimestamp();
|
|
58
|
+
const answerHash = p.answer === null ? "0x" + "11".repeat(32) : hashlock.answerHash(p.answer);
|
|
59
|
+
const task = { schema: "foga.task/1", skill: p.skill, packageHash: p.packageHash, input: p.input, acceptance: { mode: "verifier", profile: "x-hashlock-v1", verifier: k.tenders.hashlockAddress, answerHash }, deliverable: `loop D case ${id}: ${p.note}` };
|
|
60
|
+
const terms = nativeTerms(p.rewardWei, { submitBy: BigInt(t0 + p.submitIn), settleBy: BigInt(t0 + p.settleIn) });
|
|
61
|
+
const key = `demand.${opts.seed}.${id}`;
|
|
62
|
+
const m = await k.call(`${key}.mint`, "demander", k.tenders.address, k.tenders.contract().interface, "mintTask", [me, me, p.authority ?? k.tenders.hashlockAddress, keccakJcs({ td: "foga.task/1", skill: p.skill }), sha256Hex(JSON.stringify(task)), dataUri(task), terms]);
|
|
63
|
+
const tokenId = k.tenders.tokenIdFromReceipt(m.receipt);
|
|
64
|
+
await k.call(`${key}.fund`, "demander", k.tenders.address, k.tenders.contract().interface, "fundTask", [tokenId, p.rewardWei], p.rewardWei);
|
|
65
|
+
if (!p.authority)
|
|
66
|
+
await k.call(`${key}.commit`, "demander", k.tenders.hashlockAddress, k.tenders.hashlockVerifier().interface, "commitAnswer", [k.tenders.address, tokenId, answerHash]);
|
|
67
|
+
out.push({ id, tokenId: tokenId.toString(), skill: p.skill, rewardWei: p.rewardWei.toString(), submitBy: t0 + p.submitIn, settleBy: t0 + p.settleIn, expect: p.expect, note: p.note, answer: p.answer, mintTx: m.txHash });
|
|
68
|
+
log(`demander: case ${id} is tender ${tokenId} (${p.note}; expect ${p.expect.outcome}${"gate" in p.expect ? ` at ${p.expect.gate}` : ""})`);
|
|
69
|
+
}
|
|
70
|
+
return { demander: me, cases: out };
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
k.close();
|
|
74
|
+
k.world.provider.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|