@arc402/sdk 0.2.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 +184 -0
- package/dist/agent.d.ts +29 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +95 -0
- package/dist/agreement.d.ts +57 -0
- package/dist/agreement.d.ts.map +1 -0
- package/dist/agreement.js +156 -0
- package/dist/capability.d.ts +17 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +19 -0
- package/dist/channel.d.ts +39 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +160 -0
- package/dist/coldstart.d.ts +15 -0
- package/dist/coldstart.d.ts.map +1 -0
- package/dist/coldstart.js +44 -0
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/contractinteraction.d.ts +47 -0
- package/dist/contractinteraction.d.ts.map +1 -0
- package/dist/contractinteraction.js +80 -0
- package/dist/contracts.d.ts +27 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +187 -0
- package/dist/deliverable.d.ts +118 -0
- package/dist/deliverable.d.ts.map +1 -0
- package/dist/deliverable.js +156 -0
- package/dist/dispute-arbitration.d.ts +42 -0
- package/dist/dispute-arbitration.d.ts.map +1 -0
- package/dist/dispute-arbitration.js +160 -0
- package/dist/governance.d.ts +13 -0
- package/dist/governance.d.ts.map +1 -0
- package/dist/governance.js +15 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/intent.d.ts +13 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +26 -0
- package/dist/metadata.d.ts +55 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +106 -0
- package/dist/migration.d.ts +11 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +38 -0
- package/dist/multiparty.d.ts +56 -0
- package/dist/multiparty.d.ts.map +1 -0
- package/dist/multiparty.js +86 -0
- package/dist/negotiation-guard.d.ts +20 -0
- package/dist/negotiation-guard.d.ts.map +1 -0
- package/dist/negotiation-guard.js +96 -0
- package/dist/negotiation.d.ts +25 -0
- package/dist/negotiation.d.ts.map +1 -0
- package/dist/negotiation.js +102 -0
- package/dist/policy.d.ts +33 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +72 -0
- package/dist/reputation.d.ts +13 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +21 -0
- package/dist/session-manager.d.ts +13 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +102 -0
- package/dist/settlement.d.ts +14 -0
- package/dist/settlement.d.ts.map +1 -0
- package/dist/settlement.js +35 -0
- package/dist/sponsorship.d.ts +17 -0
- package/dist/sponsorship.d.ts.map +1 -0
- package/dist/sponsorship.js +19 -0
- package/dist/trust.d.ts +22 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +52 -0
- package/dist/types.d.ts +391 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +119 -0
- package/dist/wallet.d.ts +31 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +83 -0
- package/dist/watchtower.d.ts +60 -0
- package/dist/watchtower.d.ts.map +1 -0
- package/dist/watchtower.js +93 -0
- package/package.json +30 -0
- package/src/agent.ts +122 -0
- package/src/agreement.ts +236 -0
- package/src/capability.ts +18 -0
- package/src/channel.ts +203 -0
- package/src/coldstart.ts +52 -0
- package/src/context.ts +2 -0
- package/src/contractinteraction.d.ts +47 -0
- package/src/contractinteraction.d.ts.map +1 -0
- package/src/contractinteraction.js +81 -0
- package/src/contractinteraction.js.map +1 -0
- package/src/contractinteraction.ts +157 -0
- package/src/contracts.d.ts +27 -0
- package/src/contracts.d.ts.map +1 -0
- package/src/contracts.js +188 -0
- package/src/contracts.js.map +1 -0
- package/src/contracts.ts +186 -0
- package/src/deliverable.ts +231 -0
- package/src/demos/demo-insurance.ts +148 -0
- package/src/demos/demo-multiagent.ts +197 -0
- package/src/demos/demo-research.ts +124 -0
- package/src/dispute-arbitration.ts +196 -0
- package/src/governance.ts +14 -0
- package/src/index.ts +31 -0
- package/src/intent.ts +22 -0
- package/src/metadata.ts +158 -0
- package/src/migration.ts +43 -0
- package/src/multiparty.ts +132 -0
- package/src/negotiation-guard.ts +125 -0
- package/src/negotiation.ts +135 -0
- package/src/policy.ts +71 -0
- package/src/reputation.ts +20 -0
- package/src/session-manager.ts +80 -0
- package/src/settlement.ts +31 -0
- package/src/sponsorship.ts +18 -0
- package/src/trust.ts +43 -0
- package/src/types.d.ts +391 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +113 -0
- package/src/types.js.map +1 -0
- package/src/types.ts +484 -0
- package/src/wallet.ts +86 -0
- package/src/watchtower.ts +124 -0
- package/test/negotiation-signing.test.js +157 -0
- package/test/sdk.test.js +19 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { JsonRpcProvider, Wallet, ethers, ContractFactory, NonceManager } from "ethers"
|
|
2
|
+
import { spawn, ChildProcess } from "child_process"
|
|
3
|
+
import { readFileSync } from "fs"
|
|
4
|
+
import { join } from "path"
|
|
5
|
+
|
|
6
|
+
const FOUNDRY_BIN = process.env.HOME + "/.foundry/bin"
|
|
7
|
+
// Use a PID-based port to avoid conflicts between concurrent runs
|
|
8
|
+
const PORT = 10000 + (process.pid % 10000)
|
|
9
|
+
|
|
10
|
+
let anvilProcess: ChildProcess | null = null
|
|
11
|
+
process.on("exit", () => { try { anvilProcess?.kill("SIGKILL") } catch {} })
|
|
12
|
+
|
|
13
|
+
async function startAnvil(): Promise<ChildProcess> {
|
|
14
|
+
anvilProcess = spawn(
|
|
15
|
+
FOUNDRY_BIN + "/anvil",
|
|
16
|
+
["--port", String(PORT)],
|
|
17
|
+
{ detached: false, stdio: "ignore" }
|
|
18
|
+
)
|
|
19
|
+
await new Promise(r => setTimeout(r, 1500))
|
|
20
|
+
return anvilProcess
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function loadArtifact(name: string) {
|
|
24
|
+
const outDir = join(__dirname, "../../../out")
|
|
25
|
+
const path = join(outDir, `${name}.sol`, `${name}.json`)
|
|
26
|
+
const artifact = JSON.parse(readFileSync(path, "utf8"))
|
|
27
|
+
return { abi: artifact.abi, bytecode: artifact.bytecode.object }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function main() {
|
|
31
|
+
console.log("=== ARC-402 DEMO: Insurance Claims Agent ===\n")
|
|
32
|
+
|
|
33
|
+
const anvil = await startAnvil()
|
|
34
|
+
|
|
35
|
+
const provider = new JsonRpcProvider(`http://127.0.0.1:${PORT}`)
|
|
36
|
+
// Use NonceManager to ensure correct sequential nonces
|
|
37
|
+
const owner = new NonceManager(new Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", provider))
|
|
38
|
+
const medicalProvider = new Wallet("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", provider)
|
|
39
|
+
|
|
40
|
+
// Deploy contracts
|
|
41
|
+
const policyArt = loadArtifact("PolicyEngine")
|
|
42
|
+
const trustArt = loadArtifact("TrustRegistry")
|
|
43
|
+
const intentArt = loadArtifact("IntentAttestation")
|
|
44
|
+
const walletArt = loadArtifact("ARC402Wallet")
|
|
45
|
+
|
|
46
|
+
const policyEngine = await (await new ContractFactory(policyArt.abi, policyArt.bytecode, owner).deploy()).waitForDeployment()
|
|
47
|
+
const trustRegistry = await (await new ContractFactory(trustArt.abi, trustArt.bytecode, owner).deploy()).waitForDeployment()
|
|
48
|
+
const intentAttestation = await (await new ContractFactory(intentArt.abi, intentArt.bytecode, owner).deploy()).waitForDeployment()
|
|
49
|
+
|
|
50
|
+
const walletContract = await (await new ContractFactory(walletArt.abi, walletArt.bytecode, owner).deploy(
|
|
51
|
+
await policyEngine.getAddress(),
|
|
52
|
+
await trustRegistry.getAddress(),
|
|
53
|
+
await intentAttestation.getAddress()
|
|
54
|
+
)).waitForDeployment()
|
|
55
|
+
|
|
56
|
+
const walletAddr = await walletContract.getAddress()
|
|
57
|
+
|
|
58
|
+
// Add wallet as trust updater
|
|
59
|
+
await (await (trustRegistry as any).connect(owner).addUpdater(walletAddr)).wait()
|
|
60
|
+
|
|
61
|
+
// Register wallet in PolicyEngine so owner can set limits
|
|
62
|
+
await (await (policyEngine as any).connect(owner).registerWallet(walletAddr, await owner.getAddress())).wait()
|
|
63
|
+
|
|
64
|
+
// Fund the wallet with ETH
|
|
65
|
+
await (await owner.sendTransaction({ to: walletAddr, value: ethers.parseEther("1.0") })).wait()
|
|
66
|
+
|
|
67
|
+
// ─── PRIMITIVE 1: POLICY OBJECT ───────────────────────────────────────
|
|
68
|
+
console.log("[PRIMITIVE 1: POLICY OBJECT]")
|
|
69
|
+
console.log("Setting policy for claims agent wallet...")
|
|
70
|
+
console.log(" Category: claims_processing — limit: 0.1 ETH per tx")
|
|
71
|
+
console.log(" Category: protocol_fee — limit: 0.01 ETH per tx")
|
|
72
|
+
|
|
73
|
+
await (await (policyEngine as any).connect(owner).setCategoryLimitFor(walletAddr, "claims_processing", ethers.parseEther("0.1"))).wait()
|
|
74
|
+
await (await (policyEngine as any).connect(owner).setCategoryLimitFor(walletAddr, "protocol_fee", ethers.parseEther("0.01"))).wait()
|
|
75
|
+
console.log("Policy set\n")
|
|
76
|
+
|
|
77
|
+
// ─── PRIMITIVE 2: CONTEXT BINDING ─────────────────────────────────────
|
|
78
|
+
console.log("[PRIMITIVE 2: CONTEXT BINDING]")
|
|
79
|
+
console.log("Opening context: task_type=claims_processing, task_id=claim-4821")
|
|
80
|
+
|
|
81
|
+
const contextId = ethers.keccak256(ethers.toUtf8Bytes("claims_processing-claim-4821-" + Date.now()))
|
|
82
|
+
await (await (walletContract as any).connect(owner).openContext(contextId, "claims_processing")).wait()
|
|
83
|
+
console.log(`Context ID: ${contextId}`)
|
|
84
|
+
console.log("Context open\n")
|
|
85
|
+
|
|
86
|
+
// ─── PRIMITIVE 3: TRUST PRIMITIVE ─────────────────────────────────────
|
|
87
|
+
console.log("[PRIMITIVE 3: TRUST PRIMITIVE]")
|
|
88
|
+
const scoreBefore = await (trustRegistry as any).getScore(walletAddr)
|
|
89
|
+
const levelBefore = await (trustRegistry as any).getTrustLevel(walletAddr)
|
|
90
|
+
console.log(`Trust score before: ${scoreBefore} (${levelBefore})\n`)
|
|
91
|
+
|
|
92
|
+
// ─── PRIMITIVE 4: INTENT ATTESTATION ──────────────────────────────────
|
|
93
|
+
console.log("[PRIMITIVE 4: INTENT ATTESTATION]")
|
|
94
|
+
console.log("Creating intent attestation...")
|
|
95
|
+
|
|
96
|
+
const medicalProviderAddr = medicalProvider.address
|
|
97
|
+
const spendAmount = ethers.parseEther("0.05")
|
|
98
|
+
console.log(` Action: acquire_medical_records`)
|
|
99
|
+
console.log(` Reason: Claim #4821 requires medical records from Dr. Smith Clinic to assess injury claim`)
|
|
100
|
+
console.log(` Recipient: ${medicalProviderAddr} (Medical Records Provider)`)
|
|
101
|
+
console.log(` Amount: 0.05 ETH`)
|
|
102
|
+
|
|
103
|
+
const attestationId = ethers.keccak256(ethers.toUtf8Bytes("acquire_medical_records-claim-4821-" + Date.now()))
|
|
104
|
+
|
|
105
|
+
// Use anvil_impersonateAccount to call attest from wallet contract
|
|
106
|
+
await provider.send("anvil_impersonateAccount", [walletAddr])
|
|
107
|
+
await provider.send("anvil_setBalance", [walletAddr, "0xDE0B6B3A7640000"]) // 1 ETH
|
|
108
|
+
|
|
109
|
+
const impersonatedWallet = await provider.getSigner(walletAddr)
|
|
110
|
+
await (await (intentAttestation as any).connect(impersonatedWallet).attest(
|
|
111
|
+
attestationId,
|
|
112
|
+
"acquire_medical_records",
|
|
113
|
+
"Claim #4821 requires medical records from Dr. Smith Clinic to assess injury claim",
|
|
114
|
+
medicalProviderAddr,
|
|
115
|
+
spendAmount
|
|
116
|
+
)).wait()
|
|
117
|
+
await provider.send("anvil_stopImpersonatingAccount", [walletAddr])
|
|
118
|
+
|
|
119
|
+
console.log(`Attestation ID: ${attestationId}`)
|
|
120
|
+
console.log("Intent attested\n")
|
|
121
|
+
|
|
122
|
+
// Execute spend
|
|
123
|
+
console.log("Executing spend...")
|
|
124
|
+
const balanceBefore = await provider.send("eth_getBalance", [medicalProviderAddr, "latest"]).then((b: string) => BigInt(b))
|
|
125
|
+
const tx = await (walletContract as any).connect(owner).executeSpend(medicalProviderAddr, spendAmount, "claims_processing", attestationId)
|
|
126
|
+
const receipt = await tx.wait()
|
|
127
|
+
const balanceAfter = await provider.send("eth_getBalance", [medicalProviderAddr, "latest"]).then((b: string) => BigInt(b))
|
|
128
|
+
console.log(`Spend executed (tx: ${receipt.hash})`)
|
|
129
|
+
console.log(` Provider received: ${ethers.formatEther(balanceAfter - balanceBefore)} ETH\n`)
|
|
130
|
+
|
|
131
|
+
// Close context
|
|
132
|
+
console.log("Closing context...")
|
|
133
|
+
await (await (walletContract as any).connect(owner).closeContext()).wait()
|
|
134
|
+
const scoreAfter = await (trustRegistry as any).getScore(walletAddr)
|
|
135
|
+
const levelAfter = await (trustRegistry as any).getTrustLevel(walletAddr)
|
|
136
|
+
const diff = Number(scoreAfter) - Number(scoreBefore)
|
|
137
|
+
console.log(`Trust score after: ${scoreAfter} (${levelAfter}) — earned +${diff} for clean context`)
|
|
138
|
+
console.log("Context closed\n")
|
|
139
|
+
|
|
140
|
+
console.log("=== Demo complete ===")
|
|
141
|
+
|
|
142
|
+
anvil.kill()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main().catch(e => {
|
|
146
|
+
console.error("\nDemo failed:", e.message?.split('\n')[0] || e)
|
|
147
|
+
process.exitCode = 1
|
|
148
|
+
})
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { JsonRpcProvider, Wallet, ethers, ContractFactory, NonceManager } from "ethers"
|
|
2
|
+
import { spawn, ChildProcess } from "child_process"
|
|
3
|
+
import { readFileSync } from "fs"
|
|
4
|
+
import { join } from "path"
|
|
5
|
+
|
|
6
|
+
const FOUNDRY_BIN = process.env.HOME + "/.foundry/bin"
|
|
7
|
+
const PORT = 12000 + (process.pid % 10000)
|
|
8
|
+
|
|
9
|
+
let anvilProcess: ChildProcess | null = null
|
|
10
|
+
process.on("exit", () => { try { anvilProcess?.kill("SIGKILL") } catch {} })
|
|
11
|
+
|
|
12
|
+
async function startAnvil(): Promise<ChildProcess> {
|
|
13
|
+
anvilProcess = spawn(FOUNDRY_BIN + "/anvil", ["--port", String(PORT)], { detached: false, stdio: "ignore" })
|
|
14
|
+
await new Promise(r => setTimeout(r, 1500))
|
|
15
|
+
return anvilProcess
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadArtifact(name: string) {
|
|
19
|
+
const outDir = join(__dirname, "../../../out")
|
|
20
|
+
const artifact = JSON.parse(readFileSync(join(outDir, `${name}.sol`, `${name}.json`), "utf8"))
|
|
21
|
+
return { abi: artifact.abi, bytecode: artifact.bytecode.object }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ANVIL_KEYS = [
|
|
25
|
+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
|
|
26
|
+
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
|
|
27
|
+
"0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
async function deployWallet(
|
|
31
|
+
owner: NonceManager,
|
|
32
|
+
policyEngineAddr: string,
|
|
33
|
+
trustRegistryAddr: string,
|
|
34
|
+
intentAttestationAddr: string,
|
|
35
|
+
trustRegistry: any,
|
|
36
|
+
walletArt: any
|
|
37
|
+
) {
|
|
38
|
+
const wallet = await (await new ContractFactory(walletArt.abi, walletArt.bytecode, owner).deploy(
|
|
39
|
+
policyEngineAddr, trustRegistryAddr, intentAttestationAddr
|
|
40
|
+
)).waitForDeployment()
|
|
41
|
+
const addr = await wallet.getAddress()
|
|
42
|
+
await (await trustRegistry.connect(owner).addUpdater(addr)).wait()
|
|
43
|
+
return { wallet, addr }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
console.log("=== ARC-402 DEMO: Multi-Agent Settlement ===\n")
|
|
48
|
+
|
|
49
|
+
const anvil = await startAnvil()
|
|
50
|
+
|
|
51
|
+
const provider = new JsonRpcProvider(`http://127.0.0.1:${PORT}`)
|
|
52
|
+
const deployer = new NonceManager(new Wallet(ANVIL_KEYS[0], provider))
|
|
53
|
+
const orchestratorOwner = new NonceManager(new Wallet(ANVIL_KEYS[1], provider))
|
|
54
|
+
const specialistOwner = new NonceManager(new Wallet(ANVIL_KEYS[2], provider))
|
|
55
|
+
|
|
56
|
+
// Deploy shared infrastructure
|
|
57
|
+
const policyArt = loadArtifact("PolicyEngine")
|
|
58
|
+
const trustArt = loadArtifact("TrustRegistry")
|
|
59
|
+
const intentArt = loadArtifact("IntentAttestation")
|
|
60
|
+
const walletArt = loadArtifact("ARC402Wallet")
|
|
61
|
+
const settlementArt = loadArtifact("SettlementCoordinator")
|
|
62
|
+
|
|
63
|
+
const policyEngine = await (await new ContractFactory(policyArt.abi, policyArt.bytecode, deployer).deploy()).waitForDeployment()
|
|
64
|
+
const trustRegistry = await (await new ContractFactory(trustArt.abi, trustArt.bytecode, deployer).deploy()).waitForDeployment()
|
|
65
|
+
const intentAttestation = await (await new ContractFactory(intentArt.abi, intentArt.bytecode, deployer).deploy()).waitForDeployment()
|
|
66
|
+
const settlementCoordinator = await (await new ContractFactory(settlementArt.abi, settlementArt.bytecode, deployer).deploy()).waitForDeployment()
|
|
67
|
+
|
|
68
|
+
const peAddr = await policyEngine.getAddress()
|
|
69
|
+
const trAddr = await trustRegistry.getAddress()
|
|
70
|
+
const iaAddr = await intentAttestation.getAddress()
|
|
71
|
+
const scAddr = await settlementCoordinator.getAddress()
|
|
72
|
+
|
|
73
|
+
console.log("[INFRASTRUCTURE]")
|
|
74
|
+
console.log(` PolicyEngine: ${peAddr}`)
|
|
75
|
+
console.log(` TrustRegistry: ${trAddr}`)
|
|
76
|
+
console.log(` IntentAttestation: ${iaAddr}`)
|
|
77
|
+
console.log(` SettlementCoordinator: ${scAddr}\n`)
|
|
78
|
+
|
|
79
|
+
// Deploy two wallets (using deployer as owner for both for simplicity)
|
|
80
|
+
const { wallet: orchWallet, addr: orchAddr } = await deployWallet(
|
|
81
|
+
deployer, peAddr, trAddr, iaAddr, trustRegistry, walletArt
|
|
82
|
+
)
|
|
83
|
+
const { wallet: specWallet, addr: specAddr } = await deployWallet(
|
|
84
|
+
deployer, peAddr, trAddr, iaAddr, trustRegistry, walletArt
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
console.log("[WALLETS]")
|
|
88
|
+
console.log(` Orchestrator wallet: ${orchAddr}`)
|
|
89
|
+
console.log(` Specialist wallet: ${specAddr}\n`)
|
|
90
|
+
|
|
91
|
+
// Fund orchestrator
|
|
92
|
+
await (await deployer.sendTransaction({ to: orchAddr, value: ethers.parseEther("5.0") })).wait()
|
|
93
|
+
|
|
94
|
+
// Register wallets in PolicyEngine
|
|
95
|
+
const deployerAddr = await deployer.getAddress()
|
|
96
|
+
await (await (policyEngine as any).connect(deployer).registerWallet(orchAddr, deployerAddr)).wait()
|
|
97
|
+
await (await (policyEngine as any).connect(deployer).registerWallet(specAddr, deployerAddr)).wait()
|
|
98
|
+
|
|
99
|
+
// Set policies for both wallets
|
|
100
|
+
await (await (policyEngine as any).connect(deployer).setCategoryLimitFor(orchAddr, "specialist_fee", ethers.parseEther("1.0"))).wait()
|
|
101
|
+
await (await (policyEngine as any).connect(deployer).setCategoryLimitFor(specAddr, "orchestrator_payment", ethers.parseEther("1.0"))).wait()
|
|
102
|
+
|
|
103
|
+
console.log("[PRIMITIVE 1: POLICIES SET FOR BOTH WALLETS]")
|
|
104
|
+
console.log(` Orchestrator: specialist_fee limit = 1.0 ETH`)
|
|
105
|
+
console.log(` Specialist: orchestrator_payment limit = 1.0 ETH`)
|
|
106
|
+
console.log("Policies set\n")
|
|
107
|
+
|
|
108
|
+
// Orchestrator opens context
|
|
109
|
+
console.log("[PRIMITIVE 2: ORCHESTRATOR OPENS CONTEXT]")
|
|
110
|
+
const orchContextId = ethers.keccak256(ethers.toUtf8Bytes("orchestrator-task-" + Date.now()))
|
|
111
|
+
await (await (orchWallet as any).connect(deployer).openContext(orchContextId, "coordinate_specialist")).wait()
|
|
112
|
+
console.log(` Context ID: ${orchContextId}`)
|
|
113
|
+
console.log("Orchestrator context open\n")
|
|
114
|
+
|
|
115
|
+
// Check trust scores
|
|
116
|
+
const orchScore = await (trustRegistry as any).getScore(orchAddr)
|
|
117
|
+
const specScore = await (trustRegistry as any).getScore(specAddr)
|
|
118
|
+
console.log("[PRIMITIVE 3: TRUST SCORES]")
|
|
119
|
+
console.log(` Orchestrator trust: ${orchScore} (${await (trustRegistry as any).getTrustLevel(orchAddr)})`)
|
|
120
|
+
console.log(` Specialist trust: ${specScore} (${await (trustRegistry as any).getTrustLevel(specAddr)})\n`)
|
|
121
|
+
|
|
122
|
+
// Orchestrator creates intent attestation for settlement
|
|
123
|
+
console.log("[PRIMITIVE 4: INTENT ATTESTATION]")
|
|
124
|
+
const settleAmount = ethers.parseEther("0.5")
|
|
125
|
+
const intentId = ethers.keccak256(ethers.toUtf8Bytes(`settle-orch-spec-${Date.now()}`))
|
|
126
|
+
|
|
127
|
+
await provider.send("anvil_impersonateAccount", [orchAddr])
|
|
128
|
+
await provider.send("anvil_setBalance", [orchAddr, "0x4563918244F40000"]) // 5 ETH
|
|
129
|
+
const impOrch = await provider.getSigner(orchAddr)
|
|
130
|
+
await (await (intentAttestation as any).connect(impOrch).attest(
|
|
131
|
+
intentId,
|
|
132
|
+
"pay_specialist",
|
|
133
|
+
"Payment for data analysis task completed by specialist agent",
|
|
134
|
+
specAddr,
|
|
135
|
+
settleAmount
|
|
136
|
+
)).wait()
|
|
137
|
+
await provider.send("anvil_stopImpersonatingAccount", [orchAddr])
|
|
138
|
+
console.log(` Intent attested: ${intentId}`)
|
|
139
|
+
console.log("Intent attestation created\n")
|
|
140
|
+
|
|
141
|
+
// Propose settlement (anyone can propose)
|
|
142
|
+
console.log("[SETTLEMENT FLOW]")
|
|
143
|
+
const scAny = settlementCoordinator as any
|
|
144
|
+
const expiresAt = Math.floor(Date.now() / 1000) + 3600
|
|
145
|
+
const proposeTx = await (await scAny.connect(deployer).propose(orchAddr, specAddr, settleAmount, intentId, expiresAt)).wait()
|
|
146
|
+
|
|
147
|
+
// Extract proposalId from logs
|
|
148
|
+
let proposalId = ""
|
|
149
|
+
const iface = settlementCoordinator.interface
|
|
150
|
+
for (const log of proposeTx.logs) {
|
|
151
|
+
try {
|
|
152
|
+
const parsed = iface.parseLog(log)
|
|
153
|
+
if (parsed && parsed.name === "ProposalCreated") {
|
|
154
|
+
proposalId = parsed.args[0]
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
} catch {}
|
|
158
|
+
}
|
|
159
|
+
console.log(` Proposal ID: ${proposalId}`)
|
|
160
|
+
console.log("Proposal created")
|
|
161
|
+
|
|
162
|
+
// Specialist accepts - specAddr is the contract wallet, needs impersonation + ETH for gas
|
|
163
|
+
await provider.send("anvil_impersonateAccount", [specAddr])
|
|
164
|
+
await provider.send("anvil_setBalance", [specAddr, "0x1BC16D674EC80000"]) // 2 ETH for gas
|
|
165
|
+
const impSpec = await provider.getSigner(specAddr)
|
|
166
|
+
await (await scAny.connect(impSpec).accept(proposalId)).wait()
|
|
167
|
+
await provider.send("anvil_stopImpersonatingAccount", [specAddr])
|
|
168
|
+
console.log("Specialist accepted")
|
|
169
|
+
|
|
170
|
+
// Policy verification
|
|
171
|
+
console.log("\n[POLICY VERIFICATION BEFORE EXECUTION]")
|
|
172
|
+
const [orchValid] = await (policyEngine as any).validateSpend(orchAddr, "specialist_fee", settleAmount, orchContextId)
|
|
173
|
+
console.log(` Orchestrator policy check (specialist_fee, 0.5 ETH): ${orchValid ? "PASS" : "FAIL"}`)
|
|
174
|
+
|
|
175
|
+
// Execute settlement - orchAddr must call execute with value
|
|
176
|
+
const specBalBefore = await provider.send("eth_getBalance", [specAddr, "latest"]).then((b: string) => BigInt(b))
|
|
177
|
+
await provider.send("anvil_impersonateAccount", [orchAddr])
|
|
178
|
+
const impOrch2 = await provider.getSigner(orchAddr)
|
|
179
|
+
await (await scAny.connect(impOrch2).execute(proposalId, { value: settleAmount })).wait()
|
|
180
|
+
await provider.send("anvil_stopImpersonatingAccount", [orchAddr])
|
|
181
|
+
|
|
182
|
+
const specBalAfter = await provider.send("eth_getBalance", [specAddr, "latest"]).then((b: string) => BigInt(b))
|
|
183
|
+
console.log(`\n Specialist received: ${ethers.formatEther(specBalAfter - specBalBefore)} ETH`)
|
|
184
|
+
console.log("Settlement executed\n")
|
|
185
|
+
|
|
186
|
+
// Close orchestrator context
|
|
187
|
+
await (await (orchWallet as any).connect(deployer).closeContext()).wait()
|
|
188
|
+
const orchScoreAfter = await (trustRegistry as any).getScore(orchAddr)
|
|
189
|
+
console.log(`[TRUST UPDATE] Orchestrator: ${orchScore} -> ${orchScoreAfter} (+${Number(orchScoreAfter) - Number(orchScore)})`)
|
|
190
|
+
console.log("Orchestrator context closed\n")
|
|
191
|
+
|
|
192
|
+
console.log("=== Multi-Agent Settlement Demo complete ===")
|
|
193
|
+
|
|
194
|
+
anvil.kill()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
main().catch(e => { console.error("\nDemo failed:", e.message?.split('\n')[0] || e); process.exitCode = 1 })
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { JsonRpcProvider, Wallet, ethers, ContractFactory, NonceManager } from "ethers"
|
|
2
|
+
import { spawn, ChildProcess } from "child_process"
|
|
3
|
+
import { readFileSync } from "fs"
|
|
4
|
+
import { join } from "path"
|
|
5
|
+
|
|
6
|
+
const FOUNDRY_BIN = process.env.HOME + "/.foundry/bin"
|
|
7
|
+
const PORT = 11000 + (process.pid % 10000)
|
|
8
|
+
|
|
9
|
+
let anvilProcess: ChildProcess | null = null
|
|
10
|
+
process.on("exit", () => { try { anvilProcess?.kill("SIGKILL") } catch {} })
|
|
11
|
+
|
|
12
|
+
async function startAnvil(): Promise<ChildProcess> {
|
|
13
|
+
anvilProcess = spawn(FOUNDRY_BIN + "/anvil", ["--port", String(PORT)], { detached: false, stdio: "ignore" })
|
|
14
|
+
await new Promise(r => setTimeout(r, 1500))
|
|
15
|
+
return anvilProcess
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadArtifact(name: string) {
|
|
19
|
+
const outDir = join(__dirname, "../../../out")
|
|
20
|
+
const artifact = JSON.parse(readFileSync(join(outDir, `${name}.sol`, `${name}.json`), "utf8"))
|
|
21
|
+
return { abi: artifact.abi, bytecode: artifact.bytecode.object }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ANVIL_KEYS = [
|
|
25
|
+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
|
|
26
|
+
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
|
|
27
|
+
"0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
|
|
28
|
+
"0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
async function main() {
|
|
32
|
+
console.log("=== ARC-402 DEMO: Research Agent ===\n")
|
|
33
|
+
|
|
34
|
+
const anvil = await startAnvil()
|
|
35
|
+
|
|
36
|
+
const provider = new JsonRpcProvider(`http://127.0.0.1:${PORT}`)
|
|
37
|
+
const owner = new NonceManager(new Wallet(ANVIL_KEYS[0], provider))
|
|
38
|
+
const dataProviderWallets = [
|
|
39
|
+
new Wallet(ANVIL_KEYS[1], provider),
|
|
40
|
+
new Wallet(ANVIL_KEYS[2], provider),
|
|
41
|
+
new Wallet(ANVIL_KEYS[3], provider),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
// Deploy
|
|
45
|
+
const policyArt = loadArtifact("PolicyEngine")
|
|
46
|
+
const trustArt = loadArtifact("TrustRegistry")
|
|
47
|
+
const intentArt = loadArtifact("IntentAttestation")
|
|
48
|
+
const walletArt = loadArtifact("ARC402Wallet")
|
|
49
|
+
|
|
50
|
+
const policyEngine = await (await new ContractFactory(policyArt.abi, policyArt.bytecode, owner).deploy()).waitForDeployment()
|
|
51
|
+
const trustRegistry = await (await new ContractFactory(trustArt.abi, trustArt.bytecode, owner).deploy()).waitForDeployment()
|
|
52
|
+
const intentAttestation = await (await new ContractFactory(intentArt.abi, intentArt.bytecode, owner).deploy()).waitForDeployment()
|
|
53
|
+
const walletContract = await (await new ContractFactory(walletArt.abi, walletArt.bytecode, owner).deploy(
|
|
54
|
+
await policyEngine.getAddress(),
|
|
55
|
+
await trustRegistry.getAddress(),
|
|
56
|
+
await intentAttestation.getAddress()
|
|
57
|
+
)).waitForDeployment()
|
|
58
|
+
|
|
59
|
+
const walletAddr = await walletContract.getAddress()
|
|
60
|
+
await (await (trustRegistry as any).connect(owner).addUpdater(walletAddr)).wait()
|
|
61
|
+
await (await (policyEngine as any).connect(owner).registerWallet(walletAddr, await owner.getAddress())).wait()
|
|
62
|
+
await (await owner.sendTransaction({ to: walletAddr, value: ethers.parseEther("2.0") })).wait()
|
|
63
|
+
|
|
64
|
+
// Set research policy (different limits than insurance)
|
|
65
|
+
console.log("[PRIMITIVE 1: POLICY OBJECT]")
|
|
66
|
+
console.log("Setting policy for research agent wallet...")
|
|
67
|
+
console.log(" Category: data_acquisition — limit: 0.05 ETH per tx")
|
|
68
|
+
console.log(" Category: api_access — limit: 0.02 ETH per tx")
|
|
69
|
+
console.log(" Category: analysis — limit: 0.03 ETH per tx")
|
|
70
|
+
await (await (policyEngine as any).connect(owner).setCategoryLimitFor(walletAddr, "data_acquisition", ethers.parseEther("0.05"))).wait()
|
|
71
|
+
await (await (policyEngine as any).connect(owner).setCategoryLimitFor(walletAddr, "api_access", ethers.parseEther("0.02"))).wait()
|
|
72
|
+
await (await (policyEngine as any).connect(owner).setCategoryLimitFor(walletAddr, "analysis", ethers.parseEther("0.03"))).wait()
|
|
73
|
+
console.log("Policy set\n")
|
|
74
|
+
|
|
75
|
+
// Open context
|
|
76
|
+
console.log("[PRIMITIVE 2: CONTEXT BINDING]")
|
|
77
|
+
const contextId = ethers.keccak256(ethers.toUtf8Bytes("research-market-analysis-" + Date.now()))
|
|
78
|
+
await (await (walletContract as any).connect(owner).openContext(contextId, "market_research")).wait()
|
|
79
|
+
console.log(`Context ID: ${contextId}`)
|
|
80
|
+
console.log("Research context open\n")
|
|
81
|
+
|
|
82
|
+
const dataProviders = [
|
|
83
|
+
{ name: "Bloomberg Data Feed", category: "data_acquisition", amount: "0.05", action: "acquire_market_data" },
|
|
84
|
+
{ name: "Reuters API", category: "api_access", amount: "0.02", action: "fetch_news_feed" },
|
|
85
|
+
{ name: "Quant Analysis Service", category: "analysis", amount: "0.03", action: "run_sentiment_analysis" },
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
console.log("[PRIMITIVE 3 & 4: SEQUENTIAL DATA PROVIDER PAYMENTS]")
|
|
89
|
+
for (let i = 0; i < dataProviders.length; i++) {
|
|
90
|
+
const dp = dataProviders[i]
|
|
91
|
+
const providerAddr = dataProviderWallets[i].address
|
|
92
|
+
const amount = ethers.parseEther(dp.amount)
|
|
93
|
+
const attestId = ethers.keccak256(ethers.toUtf8Bytes(`${dp.action}-${i}-${Date.now()}`))
|
|
94
|
+
|
|
95
|
+
console.log(`\nPayment ${i + 1}/3: ${dp.name}`)
|
|
96
|
+
console.log(` Action: ${dp.action}`)
|
|
97
|
+
console.log(` Category: ${dp.category} — ${dp.amount} ETH`)
|
|
98
|
+
|
|
99
|
+
await provider.send("anvil_impersonateAccount", [walletAddr])
|
|
100
|
+
const impersonated = await provider.getSigner(walletAddr)
|
|
101
|
+
await (await (intentAttestation as any).connect(impersonated).attest(
|
|
102
|
+
attestId, dp.action, `Research payment to ${dp.name}`, providerAddr, amount
|
|
103
|
+
)).wait()
|
|
104
|
+
await provider.send("anvil_stopImpersonatingAccount", [walletAddr])
|
|
105
|
+
|
|
106
|
+
const balBefore = await provider.send("eth_getBalance", [providerAddr, "latest"]).then((b: string) => BigInt(b))
|
|
107
|
+
await (await (walletContract as any).connect(owner).executeSpend(providerAddr, amount, dp.category, attestId)).wait()
|
|
108
|
+
const balAfter = await provider.send("eth_getBalance", [providerAddr, "latest"]).then((b: string) => BigInt(b))
|
|
109
|
+
console.log(` Payment executed — provider received ${ethers.formatEther(balAfter - balBefore)} ETH`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log("\n[TRUST & CONTEXT CLOSE]")
|
|
113
|
+
const scoreBefore = await (trustRegistry as any).getScore(walletAddr)
|
|
114
|
+
await (await (walletContract as any).connect(owner).closeContext()).wait()
|
|
115
|
+
const scoreAfter = await (trustRegistry as any).getScore(walletAddr)
|
|
116
|
+
console.log(`Trust score: ${scoreBefore} -> ${scoreAfter} (+${Number(scoreAfter) - Number(scoreBefore)})`)
|
|
117
|
+
console.log("Research context closed\n")
|
|
118
|
+
|
|
119
|
+
console.log("=== Research Demo complete ===")
|
|
120
|
+
|
|
121
|
+
anvil.kill()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
main().catch(e => { console.error("\nDemo failed:", e.message?.split('\n')[0] || e); process.exitCode = 1 })
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ContractRunner, ethers } from "ethers";
|
|
2
|
+
import { ArbitratorBondState, DisputeClass, DisputeFeeState, DisputeMode } from "./types";
|
|
3
|
+
|
|
4
|
+
const DISPUTE_ARBITRATION_ABI = [
|
|
5
|
+
// Views
|
|
6
|
+
"function getDisputeFeeState(uint256 agreementId) view returns (tuple(uint8 mode, uint8 disputeClass, address opener, address client, address provider, address token, uint256 agreementPrice, uint256 feeRequired, uint256 openerPaid, uint256 respondentPaid, uint256 openedAt, bool active, bool resolved))",
|
|
7
|
+
"function getArbitratorBondState(address arbitrator, uint256 agreementId) view returns (tuple(uint256 bondAmount, uint256 lockedAt, bool locked, bool slashed, bool returned))",
|
|
8
|
+
"function getFeeQuote(uint256 agreementPrice, address token, uint8 mode, uint8 disputeClass) view returns (uint256 feeInTokens)",
|
|
9
|
+
"function getAcceptedArbitrators(uint256 agreementId) view returns (address[])",
|
|
10
|
+
"function isEligibleArbitrator(address arbitrator) view returns (bool)",
|
|
11
|
+
"function tokenUsdRate18(address token) view returns (uint256)",
|
|
12
|
+
"function feeFloorUsd18() view returns (uint256)",
|
|
13
|
+
"function feeCapUsd18() view returns (uint256)",
|
|
14
|
+
"function minBondFloorUsd18() view returns (uint256)",
|
|
15
|
+
// Transactions
|
|
16
|
+
"function joinMutualDispute(uint256 agreementId) payable",
|
|
17
|
+
"function acceptAssignment(uint256 agreementId) payable",
|
|
18
|
+
"function triggerFallback(uint256 agreementId) returns (bool)",
|
|
19
|
+
"function slashArbitrator(uint256 agreementId, address arbitrator, string reason)",
|
|
20
|
+
"function reclaimExpiredBond(uint256 agreementId)",
|
|
21
|
+
"function transferOwnership(address newOwner)",
|
|
22
|
+
"function acceptOwnership()",
|
|
23
|
+
"function setTokenUsdRate(address token, uint256 usdRate18)",
|
|
24
|
+
"function setFeeFloorUsd(uint256 floorUsd18)",
|
|
25
|
+
"function setFeeCapUsd(uint256 capUsd18)",
|
|
26
|
+
"function setMinBondFloorUsd(uint256 floorUsd18)",
|
|
27
|
+
"function setServiceAgreement(address sa)",
|
|
28
|
+
"function setTrustRegistry(address tr)",
|
|
29
|
+
"function setTreasury(address treasury)",
|
|
30
|
+
// Events
|
|
31
|
+
"event DisputeFeeOpened(uint256 indexed agreementId, uint8 mode, uint8 disputeClass, uint256 feeRequired, address token)",
|
|
32
|
+
"event MutualDisputeFunded(uint256 indexed agreementId, address respondent, uint256 respondentFee)",
|
|
33
|
+
"event DisputeFeeResolved(uint256 indexed agreementId, uint8 outcome, uint256 openerRefund)",
|
|
34
|
+
"event ArbitratorAssigned(uint256 indexed agreementId, address indexed arbitrator, uint256 bondAmount)",
|
|
35
|
+
"event ArbitratorBondReturned(uint256 indexed agreementId, address indexed arbitrator, uint256 amount)",
|
|
36
|
+
"event ArbitratorBondSlashed(uint256 indexed agreementId, address indexed arbitrator, uint256 amount, string reason)",
|
|
37
|
+
"event ArbitratorFeePaid(uint256 indexed agreementId, address indexed arbitrator, uint256 feeShare)",
|
|
38
|
+
"event DisputeFallbackTriggered(uint256 indexed agreementId, string reason)",
|
|
39
|
+
"event TokenRateSet(address indexed token, uint256 usdRate18)",
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export class DisputeArbitrationClient {
|
|
43
|
+
private contract: ethers.Contract;
|
|
44
|
+
|
|
45
|
+
constructor(address: string, runner: ContractRunner) {
|
|
46
|
+
this.contract = new ethers.Contract(address, DISPUTE_ARBITRATION_ABI, runner);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Views ────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
async getDisputeFeeState(agreementId: bigint): Promise<DisputeFeeState> {
|
|
52
|
+
const r = await this.contract.getDisputeFeeState(agreementId);
|
|
53
|
+
return {
|
|
54
|
+
mode: Number(r.mode) as DisputeMode,
|
|
55
|
+
disputeClass: Number(r.disputeClass) as DisputeClass,
|
|
56
|
+
opener: r.opener,
|
|
57
|
+
client: r.client,
|
|
58
|
+
provider: r.provider,
|
|
59
|
+
token: r.token,
|
|
60
|
+
agreementPrice: BigInt(r.agreementPrice),
|
|
61
|
+
feeRequired: BigInt(r.feeRequired),
|
|
62
|
+
openerPaid: BigInt(r.openerPaid),
|
|
63
|
+
respondentPaid: BigInt(r.respondentPaid),
|
|
64
|
+
openedAt: BigInt(r.openedAt),
|
|
65
|
+
active: r.active,
|
|
66
|
+
resolved: r.resolved,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getArbitratorBondState(arbitrator: string, agreementId: bigint): Promise<ArbitratorBondState> {
|
|
71
|
+
const r = await this.contract.getArbitratorBondState(arbitrator, agreementId);
|
|
72
|
+
return {
|
|
73
|
+
bondAmount: BigInt(r.bondAmount),
|
|
74
|
+
lockedAt: BigInt(r.lockedAt),
|
|
75
|
+
locked: r.locked,
|
|
76
|
+
slashed: r.slashed,
|
|
77
|
+
returned: r.returned,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getFeeQuote(
|
|
82
|
+
agreementPrice: bigint,
|
|
83
|
+
token: string,
|
|
84
|
+
mode: DisputeMode,
|
|
85
|
+
disputeClass: DisputeClass
|
|
86
|
+
): Promise<bigint> {
|
|
87
|
+
return BigInt(await this.contract.getFeeQuote(agreementPrice, token, mode, disputeClass));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getAcceptedArbitrators(agreementId: bigint): Promise<string[]> {
|
|
91
|
+
return this.contract.getAcceptedArbitrators(agreementId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async isEligibleArbitrator(address: string): Promise<boolean> {
|
|
95
|
+
return this.contract.isEligibleArbitrator(address);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getTokenUsdRate(token: string): Promise<bigint> {
|
|
99
|
+
return BigInt(await this.contract.tokenUsdRate18(token));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Transactions ─────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/** Respondent in MUTUAL dispute funds their half of the fee. */
|
|
105
|
+
async joinMutualDispute(
|
|
106
|
+
agreementId: bigint,
|
|
107
|
+
halfFeeEth: bigint = 0n // 0 for ERC-20 agreements (pre-approve instead)
|
|
108
|
+
): Promise<ethers.TransactionReceipt> {
|
|
109
|
+
const tx = await this.contract.joinMutualDispute(agreementId, { value: halfFeeEth });
|
|
110
|
+
return (await tx.wait())!;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Nominated arbitrator accepts assignment and posts bond. */
|
|
114
|
+
async acceptAssignment(
|
|
115
|
+
agreementId: bigint,
|
|
116
|
+
bondEth: bigint = 0n // 0 for ERC-20 agreements (pre-approve instead)
|
|
117
|
+
): Promise<ethers.TransactionReceipt> {
|
|
118
|
+
const tx = await this.contract.acceptAssignment(agreementId, { value: bondEth });
|
|
119
|
+
return (await tx.wait())!;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Trigger fallback to human backstop queue (mutual unfunded or panel not formed). */
|
|
123
|
+
async triggerFallback(agreementId: bigint): Promise<ethers.TransactionReceipt> {
|
|
124
|
+
const tx = await this.contract.triggerFallback(agreementId);
|
|
125
|
+
return (await tx.wait())!;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Owner-only: slash an arbitrator for manual rules violation. */
|
|
129
|
+
async slashArbitrator(
|
|
130
|
+
agreementId: bigint,
|
|
131
|
+
arbitrator: string,
|
|
132
|
+
reason: string
|
|
133
|
+
): Promise<ethers.TransactionReceipt> {
|
|
134
|
+
const tx = await this.contract.slashArbitrator(agreementId, arbitrator, reason);
|
|
135
|
+
return (await tx.wait())!;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Arbitrators can reclaim their bond after 45 days if the dispute was never resolved via resolveDisputeFee.
|
|
140
|
+
* Prevents permanent bond lock on stalled disputes.
|
|
141
|
+
*/
|
|
142
|
+
async reclaimExpiredBond(agreementId: bigint): Promise<ethers.TransactionReceipt> {
|
|
143
|
+
const tx = await this.contract.reclaimExpiredBond(agreementId);
|
|
144
|
+
return (await tx.wait())!;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Step 1 of two-step ownership transfer. Owner-only. */
|
|
148
|
+
async proposeOwner(newOwner: string): Promise<ethers.TransactionReceipt> {
|
|
149
|
+
const tx = await this.contract.transferOwnership(newOwner);
|
|
150
|
+
return (await tx.wait())!;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Step 2 of two-step ownership transfer. Must be called by the pending owner. */
|
|
154
|
+
async acceptOwnership(): Promise<ethers.TransactionReceipt> {
|
|
155
|
+
const tx = await this.contract.acceptOwnership();
|
|
156
|
+
return (await tx.wait())!;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── Admin ────────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Set the USD rate for a payment token. Owner only.
|
|
163
|
+
* @param token Token address (address(0) for ETH)
|
|
164
|
+
* @param usdRate18 USD per token with 18 decimals (e.g. 2000e18 for ETH at $2000)
|
|
165
|
+
* IMPORTANT: This is an admin-set rate, not a trustless oracle. Keep it current.
|
|
166
|
+
*/
|
|
167
|
+
async setTokenUsdRate(token: string, usdRate18: bigint): Promise<ethers.TransactionReceipt> {
|
|
168
|
+
const tx = await this.contract.setTokenUsdRate(token, usdRate18);
|
|
169
|
+
return (await tx.wait())!;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async setFeeFloorUsd(floorUsd18: bigint): Promise<ethers.TransactionReceipt> {
|
|
173
|
+
const tx = await this.contract.setFeeFloorUsd(floorUsd18);
|
|
174
|
+
return (await tx.wait())!;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async setFeeCapUsd(capUsd18: bigint): Promise<ethers.TransactionReceipt> {
|
|
178
|
+
const tx = await this.contract.setFeeCapUsd(capUsd18);
|
|
179
|
+
return (await tx.wait())!;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async setServiceAgreement(address: string): Promise<ethers.TransactionReceipt> {
|
|
183
|
+
const tx = await this.contract.setServiceAgreement(address);
|
|
184
|
+
return (await tx.wait())!;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async setTrustRegistry(address: string): Promise<ethers.TransactionReceipt> {
|
|
188
|
+
const tx = await this.contract.setTrustRegistry(address);
|
|
189
|
+
return (await tx.wait())!;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async setTreasury(address: string): Promise<ethers.TransactionReceipt> {
|
|
193
|
+
const tx = await this.contract.setTreasury(address);
|
|
194
|
+
return (await tx.wait())!;
|
|
195
|
+
}
|
|
196
|
+
}
|