@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,60 @@
|
|
|
1
|
+
import { ContractRunner } from "ethers";
|
|
2
|
+
import type { ChannelState } from "./types";
|
|
3
|
+
import { ChannelClient } from "./channel";
|
|
4
|
+
export interface WatchtowerMetadata {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
capabilities: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface WatchtowerStatus {
|
|
10
|
+
addr: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
capabilities: string[];
|
|
14
|
+
active: boolean;
|
|
15
|
+
registeredAt: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class WatchtowerClient {
|
|
18
|
+
private registry;
|
|
19
|
+
private channelContract;
|
|
20
|
+
private signer;
|
|
21
|
+
private _channelClient;
|
|
22
|
+
constructor(registryAddress: string, channelContractAddress: string, runner: ContractRunner);
|
|
23
|
+
/**
|
|
24
|
+
* Register this node as a watchtower in the WatchtowerRegistry.
|
|
25
|
+
*/
|
|
26
|
+
registerWatchtower(metadata: WatchtowerMetadata): Promise<{
|
|
27
|
+
txHash: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Submit a watchtower challenge on behalf of `beneficiary`.
|
|
31
|
+
* The `state` must be a doubly-signed ChannelState with a higher sequenceNumber
|
|
32
|
+
* than the one currently recorded on-chain.
|
|
33
|
+
*/
|
|
34
|
+
submitChallenge(channelId: string, state: ChannelState, beneficiary: string): Promise<{
|
|
35
|
+
txHash: string;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the on-chain registration status of a watchtower address.
|
|
39
|
+
*/
|
|
40
|
+
getWatchtowerStatus(watchtowerAddress: string): Promise<WatchtowerStatus>;
|
|
41
|
+
/**
|
|
42
|
+
* Authorize a watchtower to challenge on your behalf for a specific channel.
|
|
43
|
+
*/
|
|
44
|
+
authorizeWatchtower(channelId: string, watchtower: string): Promise<{
|
|
45
|
+
txHash: string;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Revoke a watchtower's authorization for a channel.
|
|
49
|
+
*/
|
|
50
|
+
revokeWatchtower(channelId: string, watchtower: string): Promise<{
|
|
51
|
+
txHash: string;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Check whether a watchtower is authorized for a channel.
|
|
55
|
+
*/
|
|
56
|
+
isWatchtowerAuthorized(channelId: string, watchtower: string): Promise<boolean>;
|
|
57
|
+
/** Expose ChannelClient for polling in watch loops. */
|
|
58
|
+
get channelClient(): ChannelClient;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=watchtower.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchtower.d.ts","sourceRoot":"","sources":["../src/watchtower.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAU,MAAM,QAAQ,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAgB1C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,cAAc,CAAgB;gBAGpC,eAAe,EAAE,MAAM,EACvB,sBAAsB,EAAE,MAAM,EAC9B,MAAM,EAAE,cAAc;IAQxB;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAOnF;;;;OAIG;IACG,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ9B;;OAEG;IACG,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY/E;;OAEG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAO7F;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAO1F;;OAEG;IACG,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrF,uDAAuD;IACvD,IAAI,aAAa,IAAI,aAAa,CAEjC;CACF"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WatchtowerClient = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const channel_1 = require("./channel");
|
|
6
|
+
const WATCHTOWER_REGISTRY_ABI = [
|
|
7
|
+
"function register(string name, string description, string[] capabilities) external",
|
|
8
|
+
"function getWatchtower(address watchtower) external view returns (tuple(address addr, string name, string description, string[] capabilities, bool active, uint256 registeredAt))",
|
|
9
|
+
"function isRegistered(address watchtower) external view returns (bool)",
|
|
10
|
+
"event WatchtowerRegistered(address indexed watchtower, string name)",
|
|
11
|
+
];
|
|
12
|
+
const SESSION_CHANNEL_WATCHTOWER_ABI = [
|
|
13
|
+
"function authorizeWatchtower(bytes32 channelId, address watchtower) external",
|
|
14
|
+
"function revokeWatchtower(bytes32 channelId, address watchtower) external",
|
|
15
|
+
"function submitWatchtowerChallenge(bytes32 channelId, bytes latestState, address beneficiary) external",
|
|
16
|
+
"function isWatchtowerAuthorized(bytes32 channelId, address watchtower) external view returns (bool)",
|
|
17
|
+
];
|
|
18
|
+
class WatchtowerClient {
|
|
19
|
+
constructor(registryAddress, channelContractAddress, runner) {
|
|
20
|
+
this.registry = new ethers_1.ethers.Contract(registryAddress, WATCHTOWER_REGISTRY_ABI, runner);
|
|
21
|
+
this.channelContract = new ethers_1.ethers.Contract(channelContractAddress, SESSION_CHANNEL_WATCHTOWER_ABI, runner);
|
|
22
|
+
this.signer = runner instanceof ethers_1.AbstractSigner ? runner : null;
|
|
23
|
+
this._channelClient = new channel_1.ChannelClient(channelContractAddress, runner);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Register this node as a watchtower in the WatchtowerRegistry.
|
|
27
|
+
*/
|
|
28
|
+
async registerWatchtower(metadata) {
|
|
29
|
+
if (!this.signer)
|
|
30
|
+
throw new Error("Signer required");
|
|
31
|
+
const tx = await this.registry.register(metadata.name, metadata.description, metadata.capabilities);
|
|
32
|
+
const receipt = await tx.wait();
|
|
33
|
+
return { txHash: receipt.hash };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Submit a watchtower challenge on behalf of `beneficiary`.
|
|
37
|
+
* The `state` must be a doubly-signed ChannelState with a higher sequenceNumber
|
|
38
|
+
* than the one currently recorded on-chain.
|
|
39
|
+
*/
|
|
40
|
+
async submitChallenge(channelId, state, beneficiary) {
|
|
41
|
+
if (!this.signer)
|
|
42
|
+
throw new Error("Signer required");
|
|
43
|
+
const encoded = this._channelClient.encodeChannelState(state);
|
|
44
|
+
const tx = await this.channelContract.submitWatchtowerChallenge(channelId, encoded, beneficiary);
|
|
45
|
+
const receipt = await tx.wait();
|
|
46
|
+
return { txHash: receipt.hash };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the on-chain registration status of a watchtower address.
|
|
50
|
+
*/
|
|
51
|
+
async getWatchtowerStatus(watchtowerAddress) {
|
|
52
|
+
const raw = await this.registry.getWatchtower(watchtowerAddress);
|
|
53
|
+
return {
|
|
54
|
+
addr: raw.addr,
|
|
55
|
+
name: raw.name,
|
|
56
|
+
description: raw.description,
|
|
57
|
+
capabilities: [...raw.capabilities],
|
|
58
|
+
active: raw.active,
|
|
59
|
+
registeredAt: Number(raw.registeredAt),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Authorize a watchtower to challenge on your behalf for a specific channel.
|
|
64
|
+
*/
|
|
65
|
+
async authorizeWatchtower(channelId, watchtower) {
|
|
66
|
+
if (!this.signer)
|
|
67
|
+
throw new Error("Signer required");
|
|
68
|
+
const tx = await this.channelContract.authorizeWatchtower(channelId, watchtower);
|
|
69
|
+
const receipt = await tx.wait();
|
|
70
|
+
return { txHash: receipt.hash };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Revoke a watchtower's authorization for a channel.
|
|
74
|
+
*/
|
|
75
|
+
async revokeWatchtower(channelId, watchtower) {
|
|
76
|
+
if (!this.signer)
|
|
77
|
+
throw new Error("Signer required");
|
|
78
|
+
const tx = await this.channelContract.revokeWatchtower(channelId, watchtower);
|
|
79
|
+
const receipt = await tx.wait();
|
|
80
|
+
return { txHash: receipt.hash };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check whether a watchtower is authorized for a channel.
|
|
84
|
+
*/
|
|
85
|
+
async isWatchtowerAuthorized(channelId, watchtower) {
|
|
86
|
+
return this.channelContract.isWatchtowerAuthorized(channelId, watchtower);
|
|
87
|
+
}
|
|
88
|
+
/** Expose ChannelClient for polling in watch loops. */
|
|
89
|
+
get channelClient() {
|
|
90
|
+
return this._channelClient;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.WatchtowerClient = WatchtowerClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arc402/sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "ARC-402 typed TypeScript SDK for discovery, negotiation, trust, and governed settlement",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"repository": "https://github.com/LegoGigaBrain/arc-402",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"arc402",
|
|
11
|
+
"web3",
|
|
12
|
+
"agents",
|
|
13
|
+
"wallet",
|
|
14
|
+
"governance",
|
|
15
|
+
"blockchain"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"test": "node --test test/**/*.test.js"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ethers": "^6.9.0",
|
|
23
|
+
"tweetnacl": "^1.0.3"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
27
|
+
"ts-node": "^10.9.0",
|
|
28
|
+
"typescript": "^5.3.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/agent.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ContractRunner } from "ethers";
|
|
2
|
+
import { AGENT_REGISTRY_ABI } from "./contracts";
|
|
3
|
+
import { ethers } from "ethers";
|
|
4
|
+
import { AgentInfo, OperationalMetrics } from "./types";
|
|
5
|
+
|
|
6
|
+
export interface AgentRegistrationInput {
|
|
7
|
+
name: string;
|
|
8
|
+
capabilities: string[];
|
|
9
|
+
serviceType: string;
|
|
10
|
+
endpoint: string;
|
|
11
|
+
metadataURI?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class AgentRegistryClient {
|
|
15
|
+
private contract: ethers.Contract;
|
|
16
|
+
|
|
17
|
+
constructor(address: string, runner: ContractRunner) {
|
|
18
|
+
this.contract = new ethers.Contract(address, AGENT_REGISTRY_ABI, runner);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async register(input: AgentRegistrationInput) {
|
|
22
|
+
const tx = await this.contract.register(input.name, input.capabilities, input.serviceType, input.endpoint, input.metadataURI ?? "");
|
|
23
|
+
return tx.wait();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async update(input: AgentRegistrationInput) {
|
|
27
|
+
const tx = await this.contract.update(input.name, input.capabilities, input.serviceType, input.endpoint, input.metadataURI ?? "");
|
|
28
|
+
return tx.wait();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async deactivate() { const tx = await this.contract.deactivate(); return tx.wait(); }
|
|
32
|
+
async reactivate() { const tx = await this.contract.reactivate(); return tx.wait(); }
|
|
33
|
+
async submitHeartbeat(latencyMs: number) { const tx = await this.contract.submitHeartbeat(latencyMs); return tx.wait(); }
|
|
34
|
+
async setHeartbeatPolicy(intervalSeconds: number, gracePeriodSeconds: number) {
|
|
35
|
+
const tx = await this.contract.setHeartbeatPolicy(intervalSeconds, gracePeriodSeconds);
|
|
36
|
+
return tx.wait();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getAgent(wallet: string): Promise<AgentInfo> {
|
|
40
|
+
const [raw, trustScore] = await Promise.all([this.contract.getAgent(wallet), this.contract.getTrustScore(wallet)]);
|
|
41
|
+
return {
|
|
42
|
+
wallet: raw.wallet,
|
|
43
|
+
name: raw.name,
|
|
44
|
+
capabilities: [...raw.capabilities],
|
|
45
|
+
serviceType: raw.serviceType,
|
|
46
|
+
endpoint: raw.endpoint,
|
|
47
|
+
metadataURI: raw.metadataURI,
|
|
48
|
+
active: raw.active,
|
|
49
|
+
registeredAt: BigInt(raw.registeredAt),
|
|
50
|
+
endpointChangedAt: BigInt(raw.endpointChangedAt),
|
|
51
|
+
endpointChangeCount: BigInt(raw.endpointChangeCount),
|
|
52
|
+
trustScore: BigInt(trustScore),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getOperationalMetrics(wallet: string): Promise<OperationalMetrics> {
|
|
57
|
+
const raw = await this.contract.getOperationalMetrics(wallet);
|
|
58
|
+
return {
|
|
59
|
+
heartbeatInterval: BigInt(raw.heartbeatInterval),
|
|
60
|
+
heartbeatGracePeriod: BigInt(raw.heartbeatGracePeriod),
|
|
61
|
+
lastHeartbeatAt: BigInt(raw.lastHeartbeatAt),
|
|
62
|
+
rollingLatency: BigInt(raw.rollingLatency),
|
|
63
|
+
heartbeatCount: BigInt(raw.heartbeatCount),
|
|
64
|
+
missedHeartbeatCount: BigInt(raw.missedHeartbeatCount),
|
|
65
|
+
uptimeScore: BigInt(raw.uptimeScore),
|
|
66
|
+
responseScore: BigInt(raw.responseScore),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async listAgents(limit?: number): Promise<AgentInfo[]> {
|
|
71
|
+
const count = Number(await this.contract.agentCount());
|
|
72
|
+
const end = limit ? Math.min(limit, count) : count;
|
|
73
|
+
const addresses = await Promise.all(Array.from({ length: end }, (_, i) => this.contract.getAgentAtIndex(i)));
|
|
74
|
+
return Promise.all(addresses.map((address: string) => this.getAgent(address)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async findByCapability(capability: string, limit?: number): Promise<AgentInfo[]> {
|
|
78
|
+
const agents = await this.listAgents(limit);
|
|
79
|
+
return agents.filter((agent) => agent.capabilities.includes(capability));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async searchByCapability(capability: string): Promise<AgentInfo[]> {
|
|
83
|
+
const agents = await this.listAgents();
|
|
84
|
+
return agents.filter(
|
|
85
|
+
(agent) => agent.active && agent.capabilities.some((c) => c === capability || c.startsWith(capability + "."))
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async searchByServiceType(serviceType: string): Promise<AgentInfo[]> {
|
|
90
|
+
const agents = await this.listAgents();
|
|
91
|
+
const query = serviceType.toLowerCase();
|
|
92
|
+
return agents.filter(
|
|
93
|
+
(agent) => agent.active && agent.serviceType.toLowerCase().includes(query)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async searchByName(query: string): Promise<AgentInfo[]> {
|
|
98
|
+
const agents = await this.listAgents();
|
|
99
|
+
const lower = query.toLowerCase();
|
|
100
|
+
return agents.filter(
|
|
101
|
+
(agent) => agent.active && agent.name.toLowerCase().includes(lower)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getTopByTrust(limit = 20): Promise<AgentInfo[]> {
|
|
106
|
+
const agents = await this.listAgents();
|
|
107
|
+
return agents
|
|
108
|
+
.filter((agent) => agent.active)
|
|
109
|
+
.sort((a, b) => {
|
|
110
|
+
const aScore = BigInt(a.trustScore ?? 0n);
|
|
111
|
+
const bScore = BigInt(b.trustScore ?? 0n);
|
|
112
|
+
return bScore > aScore ? 1 : bScore < aScore ? -1 : 0;
|
|
113
|
+
})
|
|
114
|
+
.slice(0, limit);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getActive(limit?: number): Promise<AgentInfo[]> {
|
|
118
|
+
const agents = await this.listAgents();
|
|
119
|
+
const active = agents.filter((agent) => agent.active);
|
|
120
|
+
return limit ? active.slice(0, limit) : active;
|
|
121
|
+
}
|
|
122
|
+
}
|
package/src/agreement.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { ContractRunner, ethers } from "ethers";
|
|
2
|
+
import { SERVICE_AGREEMENT_ABI } from "./contracts";
|
|
3
|
+
import {
|
|
4
|
+
Agreement,
|
|
5
|
+
AgreementStatus,
|
|
6
|
+
ArbitrationCase,
|
|
7
|
+
ArbitrationVote,
|
|
8
|
+
DisputeCase,
|
|
9
|
+
DisputeClass,
|
|
10
|
+
DisputeEvidence,
|
|
11
|
+
DisputeMode,
|
|
12
|
+
DisputeOutcome,
|
|
13
|
+
DirectDisputeReason,
|
|
14
|
+
EvidenceType,
|
|
15
|
+
ProposeParams,
|
|
16
|
+
ProviderResponseType,
|
|
17
|
+
RemediationCase,
|
|
18
|
+
RemediationFeedback,
|
|
19
|
+
RemediationResponse,
|
|
20
|
+
} from "./types";
|
|
21
|
+
|
|
22
|
+
export class ServiceAgreementClient {
|
|
23
|
+
private contract: ethers.Contract;
|
|
24
|
+
|
|
25
|
+
constructor(address: string, runner: ContractRunner) {
|
|
26
|
+
this.contract = new ethers.Contract(address, SERVICE_AGREEMENT_ABI, runner);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async propose(params: ProposeParams): Promise<{ agreementId: bigint; receipt: ethers.TransactionReceipt }> {
|
|
30
|
+
const isEth = params.token === ethers.ZeroAddress;
|
|
31
|
+
const tx = await this.contract.propose(
|
|
32
|
+
params.provider,
|
|
33
|
+
params.serviceType,
|
|
34
|
+
params.description,
|
|
35
|
+
params.price,
|
|
36
|
+
params.token,
|
|
37
|
+
params.deadline,
|
|
38
|
+
params.deliverablesHash,
|
|
39
|
+
{ value: isEth ? params.price : 0n },
|
|
40
|
+
);
|
|
41
|
+
const receipt = await tx.wait();
|
|
42
|
+
const iface = new ethers.Interface(SERVICE_AGREEMENT_ABI);
|
|
43
|
+
for (const log of receipt.logs) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = iface.parseLog(log);
|
|
46
|
+
if (parsed?.name === "AgreementProposed") return { agreementId: BigInt(parsed.args.id), receipt };
|
|
47
|
+
} catch {}
|
|
48
|
+
}
|
|
49
|
+
throw new Error("Could not parse AgreementProposed event");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async accept(id: bigint) { const tx = await this.contract.accept(id); return tx.wait(); }
|
|
53
|
+
/** @deprecated Legacy/trusted-only immediate release path. Prefer commitDeliverable() + verifyDeliverable()/autoRelease(). */
|
|
54
|
+
async fulfill(id: bigint, hash: string) { const tx = await this.contract.fulfill(id, hash); return tx.wait(); }
|
|
55
|
+
async fulfillLegacyTrustedOnly(id: bigint, hash: string) { return this.fulfill(id, hash); }
|
|
56
|
+
async commitDeliverable(id: bigint, hash: string) { const tx = await this.contract.commitDeliverable(id, hash); return tx.wait(); }
|
|
57
|
+
async verifyDeliverable(id: bigint) { const tx = await this.contract.verifyDeliverable(id); return tx.wait(); }
|
|
58
|
+
async autoRelease(id: bigint) { const tx = await this.contract.autoRelease(id); return tx.wait(); }
|
|
59
|
+
async dispute(id: bigint, reason: string, feeEth = 0n) { const tx = await this.contract.dispute(id, reason, { value: feeEth }); return tx.wait(); }
|
|
60
|
+
async directDispute(id: bigint, directReason: DirectDisputeReason, reason: string, feeEth = 0n) { const tx = await this.contract.directDispute(id, directReason, reason, { value: feeEth }); return tx.wait(); }
|
|
61
|
+
async escalateToDispute(id: bigint, reason: string, feeEth = 0n) { const tx = await this.contract.escalateToDispute(id, reason, { value: feeEth }); return tx.wait(); }
|
|
62
|
+
|
|
63
|
+
/** Open a dispute with explicit mode and class. For ETH agreements, pass fee as feeEth. */
|
|
64
|
+
async openDisputeWithMode(
|
|
65
|
+
id: bigint,
|
|
66
|
+
mode: DisputeMode,
|
|
67
|
+
disputeClass: DisputeClass,
|
|
68
|
+
reason: string,
|
|
69
|
+
feeEth = 0n
|
|
70
|
+
): Promise<ethers.TransactionReceipt> {
|
|
71
|
+
const tx = await this.contract.openDisputeWithMode(id, mode, disputeClass, reason, { value: feeEth });
|
|
72
|
+
return tx.wait();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Shorthand: UNILATERAL dispute with specified class. */
|
|
76
|
+
async openUnilateralDispute(
|
|
77
|
+
id: bigint,
|
|
78
|
+
disputeClass: DisputeClass,
|
|
79
|
+
reason: string,
|
|
80
|
+
feeEth = 0n
|
|
81
|
+
): Promise<ethers.TransactionReceipt> {
|
|
82
|
+
return this.openDisputeWithMode(id, DisputeMode.UNILATERAL, disputeClass, reason, feeEth);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Shorthand: MUTUAL dispute — opener pays half. Respondent calls DisputeArbitrationClient.joinMutualDispute(). */
|
|
86
|
+
async openMutualDispute(
|
|
87
|
+
id: bigint,
|
|
88
|
+
disputeClass: DisputeClass,
|
|
89
|
+
reason: string,
|
|
90
|
+
halfFeeEth = 0n
|
|
91
|
+
): Promise<ethers.TransactionReceipt> {
|
|
92
|
+
return this.openDisputeWithMode(id, DisputeMode.MUTUAL, disputeClass, reason, halfFeeEth);
|
|
93
|
+
}
|
|
94
|
+
async canDirectDispute(id: bigint, directReason: DirectDisputeReason): Promise<boolean> { return this.contract.canDirectDispute(id, directReason); }
|
|
95
|
+
async cancel(id: bigint) { const tx = await this.contract.cancel(id); return tx.wait(); }
|
|
96
|
+
async expiredCancel(id: bigint) { const tx = await this.contract.expiredCancel(id); return tx.wait(); }
|
|
97
|
+
async resolveDisputeDetailed(id: bigint, outcome: DisputeOutcome, providerAward: bigint, clientAward: bigint) {
|
|
98
|
+
const tx = await this.contract.resolveDisputeDetailed(id, outcome, providerAward, clientAward); return tx.wait();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Owner-only: resolve a dispute directly in favor of provider or client. Requires DISPUTED or ESCALATED_TO_HUMAN status. */
|
|
102
|
+
async ownerResolveDispute(agreementId: bigint, favorProvider: boolean): Promise<ethers.TransactionReceipt> {
|
|
103
|
+
const tx = await this.contract.ownerResolveDispute(agreementId, favorProvider);
|
|
104
|
+
return tx.wait();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Called by the DisputeArbitration contract to resolve a dispute with split amounts.
|
|
109
|
+
* @param agreementId Agreement to resolve
|
|
110
|
+
* @param recipient Address of the winning party
|
|
111
|
+
* @param providerAmount Provider payout in token units
|
|
112
|
+
* @param clientAmount Client refund in token units
|
|
113
|
+
*/
|
|
114
|
+
async resolveFromArbitration(
|
|
115
|
+
agreementId: bigint,
|
|
116
|
+
recipient: string,
|
|
117
|
+
providerAmount: bigint,
|
|
118
|
+
clientAmount: bigint,
|
|
119
|
+
): Promise<ethers.TransactionReceipt> {
|
|
120
|
+
const tx = await this.contract.resolveFromArbitration(agreementId, recipient, providerAmount, clientAmount);
|
|
121
|
+
return tx.wait();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async requestRevision(agreementId: bigint, feedbackHash: string, feedbackURI = "", previousTranscriptHash = ethers.ZeroHash) {
|
|
125
|
+
const tx = await this.contract.requestRevision(agreementId, feedbackHash, feedbackURI, previousTranscriptHash);
|
|
126
|
+
return tx.wait();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async respondToRevision(
|
|
130
|
+
agreementId: bigint,
|
|
131
|
+
responseType: ProviderResponseType,
|
|
132
|
+
responseHash: string,
|
|
133
|
+
responseURI = "",
|
|
134
|
+
previousTranscriptHash = ethers.ZeroHash,
|
|
135
|
+
proposedProviderPayout = 0n,
|
|
136
|
+
) {
|
|
137
|
+
const tx = await this.contract.respondToRevision(
|
|
138
|
+
agreementId,
|
|
139
|
+
responseType,
|
|
140
|
+
proposedProviderPayout,
|
|
141
|
+
responseHash,
|
|
142
|
+
responseURI,
|
|
143
|
+
previousTranscriptHash,
|
|
144
|
+
);
|
|
145
|
+
return tx.wait();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async submitDisputeEvidence(agreementId: bigint, evidenceType: EvidenceType, evidenceHash: string, evidenceURI = "") {
|
|
149
|
+
const tx = await this.contract.submitDisputeEvidence(agreementId, evidenceType, evidenceHash, evidenceURI);
|
|
150
|
+
return tx.wait();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async nominateArbitrator(agreementId: bigint, arbitrator: string) {
|
|
154
|
+
const tx = await this.contract.nominateArbitrator(agreementId, arbitrator);
|
|
155
|
+
return tx.wait();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async castArbitrationVote(agreementId: bigint, vote: ArbitrationVote, providerAward: bigint, clientAward: bigint) {
|
|
159
|
+
const tx = await this.contract.castArbitrationVote(agreementId, vote, providerAward, clientAward);
|
|
160
|
+
return tx.wait();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async requestHumanEscalation(agreementId: bigint, reason: string) {
|
|
164
|
+
const tx = await this.contract.requestHumanEscalation(agreementId, reason);
|
|
165
|
+
return tx.wait();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async getAgreement(id: bigint): Promise<Agreement> {
|
|
169
|
+
const raw = await this.contract.getAgreement(id);
|
|
170
|
+
return {
|
|
171
|
+
id: BigInt(raw.id), client: raw.client, provider: raw.provider, serviceType: raw.serviceType,
|
|
172
|
+
description: raw.description, price: BigInt(raw.price), token: raw.token, deadline: BigInt(raw.deadline),
|
|
173
|
+
deliverablesHash: raw.deliverablesHash, status: Number(raw.status) as AgreementStatus,
|
|
174
|
+
createdAt: BigInt(raw.createdAt), resolvedAt: BigInt(raw.resolvedAt), verifyWindowEnd: BigInt(raw.verifyWindowEnd), committedHash: raw.committedHash,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async getRemediationCase(id: bigint): Promise<RemediationCase> {
|
|
179
|
+
const raw = await this.contract.getRemediationCase(id);
|
|
180
|
+
return { cycleCount: Number(raw.cycleCount), openedAt: BigInt(raw.openedAt), deadlineAt: BigInt(raw.deadlineAt), lastActionAt: BigInt(raw.lastActionAt), latestTranscriptHash: raw.latestTranscriptHash, active: raw.active };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async getRemediationFeedback(id: bigint, index: bigint): Promise<RemediationFeedback> {
|
|
184
|
+
const raw = await this.contract.getRemediationFeedback(id, index);
|
|
185
|
+
return { cycle: Number(raw.cycle), author: raw.author, feedbackHash: raw.feedbackHash, feedbackURI: raw.feedbackURI, previousTranscriptHash: raw.previousTranscriptHash, transcriptHash: raw.transcriptHash, timestamp: BigInt(raw.timestamp) };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getRemediationResponse(id: bigint, index: bigint): Promise<RemediationResponse> {
|
|
189
|
+
const raw = await this.contract.getRemediationResponse(id, index);
|
|
190
|
+
return { cycle: Number(raw.cycle), author: raw.author, responseType: Number(raw.responseType) as ProviderResponseType, proposedProviderPayout: BigInt(raw.proposedProviderPayout), responseHash: raw.responseHash, responseURI: raw.responseURI, previousTranscriptHash: raw.previousTranscriptHash, transcriptHash: raw.transcriptHash, timestamp: BigInt(raw.timestamp) };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async getDisputeCase(id: bigint): Promise<DisputeCase> {
|
|
194
|
+
const raw = await this.contract.getDisputeCase(id);
|
|
195
|
+
return { agreementId: BigInt(raw.agreementId), openedAt: BigInt(raw.openedAt), responseDeadlineAt: BigInt(raw.responseDeadlineAt), outcome: Number(raw.outcome) as DisputeOutcome, providerAward: BigInt(raw.providerAward), clientAward: BigInt(raw.clientAward), humanReviewRequested: raw.humanReviewRequested, evidenceCount: BigInt(raw.evidenceCount) };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async getDisputeEvidence(id: bigint, index: bigint): Promise<DisputeEvidence> {
|
|
199
|
+
const raw = await this.contract.getDisputeEvidence(id, index);
|
|
200
|
+
return { submitter: raw.submitter, evidenceType: Number(raw.evidenceType) as EvidenceType, evidenceHash: raw.evidenceHash, evidenceURI: raw.evidenceURI, timestamp: BigInt(raw.timestamp) };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async getArbitrationCase(id: bigint): Promise<ArbitrationCase> {
|
|
204
|
+
const raw = await this.contract.getArbitrationCase(id);
|
|
205
|
+
return {
|
|
206
|
+
agreementId: BigInt(raw.agreementId),
|
|
207
|
+
arbitrators: [...raw.arbitrators],
|
|
208
|
+
arbitratorCount: Number(raw.arbitratorCount),
|
|
209
|
+
providerVotes: Number(raw.providerVotes),
|
|
210
|
+
clientVotes: Number(raw.clientVotes),
|
|
211
|
+
splitVotes: Number(raw.splitVotes),
|
|
212
|
+
humanVotes: Number(raw.humanVotes),
|
|
213
|
+
selectionDeadlineAt: BigInt(raw.selectionDeadlineAt),
|
|
214
|
+
decisionDeadlineAt: BigInt(raw.decisionDeadlineAt),
|
|
215
|
+
splitProviderAward: BigInt(raw.splitProviderAward),
|
|
216
|
+
splitClientAward: BigInt(raw.splitClientAward),
|
|
217
|
+
finalized: raw.finalized,
|
|
218
|
+
humanBackstopUsed: raw.humanBackstopUsed,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async getDisputeEvidenceAll(id: bigint): Promise<DisputeEvidence[]> {
|
|
223
|
+
const dispute = await this.getDisputeCase(id);
|
|
224
|
+
return Promise.all(Array.from({ length: Number(dispute.evidenceCount) }, (_, i) => this.getDisputeEvidence(id, BigInt(i))));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async getClientAgreements(client: string): Promise<Agreement[]> {
|
|
228
|
+
const ids = await this.contract.getAgreementsByClient(client) as bigint[];
|
|
229
|
+
return Promise.all(ids.map((id) => this.getAgreement(BigInt(id))));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async getProviderAgreements(provider: string): Promise<Agreement[]> {
|
|
233
|
+
const ids = await this.contract.getAgreementsByProvider(provider) as bigint[];
|
|
234
|
+
return Promise.all(ids.map((id) => this.getAgreement(BigInt(id))));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ContractRunner, ethers } from "ethers";
|
|
2
|
+
import { CAPABILITY_REGISTRY_ABI } from "./contracts";
|
|
3
|
+
import { CapabilityRoot } from "./types";
|
|
4
|
+
|
|
5
|
+
export class CapabilityRegistryClient {
|
|
6
|
+
private contract: ethers.Contract;
|
|
7
|
+
constructor(address: string, runner: ContractRunner) { this.contract = new ethers.Contract(address, CAPABILITY_REGISTRY_ABI, runner); }
|
|
8
|
+
async registerRoot(root: string) { const tx = await this.contract.registerRoot(root); return tx.wait(); }
|
|
9
|
+
async setRootStatus(root: string, active: boolean) { const tx = await this.contract.setRootStatus(root, active); return tx.wait(); }
|
|
10
|
+
async claim(capability: string) { const tx = await this.contract.claim(capability); return tx.wait(); }
|
|
11
|
+
async revoke(capability: string) { const tx = await this.contract.revoke(capability); return tx.wait(); }
|
|
12
|
+
isRootActive(root: string) { return this.contract.isRootActive(root); }
|
|
13
|
+
async getRoot(root: string): Promise<CapabilityRoot> { const raw = await this.contract.getRoot(root); return { root: raw.root, rootId: raw.rootId, active: raw.active }; }
|
|
14
|
+
async listRoots(): Promise<CapabilityRoot[]> { const count = Number(await this.contract.rootCount()); return Promise.all(Array.from({ length: count }, async (_, i) => { const raw = await this.contract.getRootAt(i); return { root: raw.root, rootId: raw.rootId, active: raw.active }; })); }
|
|
15
|
+
getCapabilities(agent: string): Promise<string[]> { return this.contract.getCapabilities(agent); }
|
|
16
|
+
capabilityCount(agent: string) { return this.contract.capabilityCount(agent).then(BigInt); }
|
|
17
|
+
isCapabilityClaimed(agent: string, capability: string) { return this.contract.isCapabilityClaimed(agent, capability); }
|
|
18
|
+
}
|