arcbounty-agent-sdk 0.1.0 → 0.3.1

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/dist/index.d.ts CHANGED
@@ -1,8 +1,19 @@
1
1
  import { Address, Hash } from 'viem';
2
2
 
3
- type ArcBountyAgentConfig = {
4
- /** Private key of the agent wallet (0x-prefixed hex) */
5
- privateKey: Hash;
3
+ type CircleWalletConfig = {
4
+ /** Circle API key (Circle Console Testnet/Mainnet API Keys → API Key, Standard). */
5
+ apiKey: string;
6
+ /** Registered entity secret — see docs/circle-wallet.md. Controls every wallet under this API key. */
7
+ entitySecret: string;
8
+ /** Circle wallet ID (from `createWallets`/`listWallets`), not the on-chain address. */
9
+ walletId: string;
10
+ /** The wallet's on-chain address — fetch once via `getWallet({ id })` and store it; avoids an extra round-trip on every agent startup. */
11
+ address: Address;
12
+ /** Override Circle's API base URL (defaults to https://api.circle.com). */
13
+ baseUrl?: string;
14
+ };
15
+
16
+ type ArcBountyAgentConfigBase = {
6
17
  /** Arc RPC URL */
7
18
  rpcUrl?: string;
8
19
  /** IPFS metadata URI for agent registration (ipfs://Qm...) */
@@ -10,6 +21,15 @@ type ArcBountyAgentConfig = {
10
21
  /** BountyAdapter contract address (overrides default) */
11
22
  bountyAdapterAddress?: Address;
12
23
  };
24
+ type ArcBountyAgentConfig = ArcBountyAgentConfigBase & ({
25
+ /** Private key of the agent wallet (0x-prefixed hex). Mutually exclusive with `circleWallet`. */
26
+ privateKey: Hash;
27
+ circleWallet?: never;
28
+ } | {
29
+ privateKey?: never;
30
+ /** Sign via a Circle developer-controlled wallet instead of a raw private key. Mutually exclusive with `privateKey`. */
31
+ circleWallet: CircleWalletConfig;
32
+ });
13
33
  type BountyMeta = {
14
34
  jobId: bigint;
15
35
  poster: Address;
@@ -35,6 +55,8 @@ type BountyMeta = {
35
55
  disputeReasonHash: string;
36
56
  disputeResponseHash: string;
37
57
  disputeRulingHash: string;
58
+ requireWorkerBond: boolean;
59
+ workerBond: bigint;
38
60
  };
39
61
  type ReputationScore = {
40
62
  averageScore: bigint;
@@ -65,6 +87,8 @@ type CreateBountyOptions = {
65
87
  provider?: Address;
66
88
  agentOnly?: boolean;
67
89
  humanOnly?: boolean;
90
+ /** V4: require the worker to post a bond (refunded at submitWork, forfeited to you if they vanish). */
91
+ requireWorkerBond?: boolean;
68
92
  };
69
93
  type SubmitWorkOptions = {
70
94
  /** Raw text/markdown result — will be pinned to IPFS */
@@ -90,8 +114,7 @@ type TxResult = {
90
114
 
91
115
  declare class ArcBountyAgent {
92
116
  private readonly publicClient;
93
- private readonly walletClient;
94
- private readonly account;
117
+ private readonly signer;
95
118
  private readonly bountyAdapter;
96
119
  private readonly metadataURI;
97
120
  private readonly chain;
@@ -99,8 +122,6 @@ declare class ArcBountyAgent {
99
122
  constructor(config: ArcBountyAgentConfig);
100
123
  get address(): Address;
101
124
  register(): Promise<bigint>;
102
- /** Pull the minted tokenId from a Transfer(from=0x0, to=self) log in a receipt. */
103
- private _agentIdFromReceiptLogs;
104
125
  get agentId(): bigint;
105
126
  setAgentId(id: bigint): void;
106
127
  listOpenBounties(filter?: OpenBountiesFilter): Promise<BountyMeta[]>;
@@ -133,14 +154,39 @@ declare class ArcBountyAgent {
133
154
  resolveDispute(jobId: bigint, payProvider: boolean, ruling: DisputeEvidenceOptions, reputationPenalty?: number): Promise<TxResult>;
134
155
  /** After 48h with no response, anyone may claim the default ruling. */
135
156
  claimDefaultRuling(jobId: bigint): Promise<TxResult>;
157
+ /**
158
+ * V3.3 liveness fallback: if the respondent DID reply (so claimDefaultRuling
159
+ * no longer applies) but the arbitrator never called resolveDispute within
160
+ * ARBITRATOR_TIMEOUT (30d) of disputeRaisedAt, anyone may trigger a neutral
161
+ * 50/50 split between poster and worker. No reputation penalty either way.
162
+ */
163
+ claimArbitratorTimeout(jobId: bigint): Promise<TxResult>;
136
164
  /** Worker challenges a pending rejection — flips bounty into dispute with worker as initiator. */
137
165
  challengeRejection(jobId: bigint, evidence: DisputeEvidenceOptions): Promise<TxResult>;
138
166
  /** Open a dispute (either party — after submission, before resolution). */
139
167
  disputeBounty(jobId: bigint, evidence: DisputeEvidenceOptions): Promise<TxResult>;
140
168
  /** Respond to an open dispute (only the non-initiator may call). */
141
169
  respondToDispute(jobId: bigint, evidence: DisputeEvidenceOptions): Promise<TxResult>;
170
+ /**
171
+ * Scans the full bounty set and calls expireBounty() on anything past its
172
+ * deadline with no submission and not yet resolved. Stops after finding
173
+ * `limit` candidates to expire.
174
+ *
175
+ * NOTE: `getOpenBounties` (used pre-V3.3) can NEVER return a candidate for
176
+ * this — it excludes any bounty whose deadline has already passed by
177
+ * definition (`_isOpenMatch` checks `block.timestamp <= deadline`). This
178
+ * scan walks `allJobIds` directly instead, mirroring the keeper cron route
179
+ * (`frontend/app/api/cron/keeper/route.ts`).
180
+ */
142
181
  expireStale(category?: string, limit?: number): Promise<bigint[]>;
143
182
  getReputation(agentId?: bigint): Promise<ReputationScore>;
183
+ /**
184
+ * V4 anti-Sybil signal: count of distinct posters who've actually paid out
185
+ * a completed bounty to this agent. Costs N real funded wallets to fake N —
186
+ * unlike the raw ERC-8004 average score, which one alt account can inflate
187
+ * for a few cents. See V4_DESIGN_ANTI_SYBIL.md.
188
+ */
189
+ getUniquePosterCount(agentId?: bigint): Promise<bigint>;
144
190
  getAgentInfo(): Promise<AgentInfo>;
145
191
  usdcBalance(): Promise<bigint>;
146
192
  formatUsdc(raw: bigint): string;
@@ -153,7 +199,39 @@ declare class ArcBountyAgent {
153
199
  * If you need durable dedup across restarts, persist `seenJobIds` yourself.
154
200
  */
155
201
  subscribeToNewBounties(filter: OpenBountiesFilter, onMatch: (meta: BountyMeta) => void | Promise<void>): () => void;
156
- private _matchesFilter;
202
+ /**
203
+ * Background watchdog over this agent's own assigned bounties. An agent
204
+ * that only calls `takeBounty`/`submitWork` and then goes idle is exposed
205
+ * to every counterparty-controlled window in the contract: a poster can
206
+ * reject a correct submission (48h to challenge), open a dispute the agent
207
+ * never responds to (48h to respond, then the *other* side wins by
208
+ * default), or the agent may simply be owed a payout nobody triggered yet
209
+ * (14d autoApprove / 30d claimArbitratorTimeout). `protect()` polls
210
+ * `getMyBounties()` and reacts automatically:
211
+ *
212
+ * - **Pending rejection, not yet challenged** → calls `onRejection` (if
213
+ * provided) for evidence and calls `challengeRejection`. Without a
214
+ * callback, a rejection is only logged, never auto-challenged — silently
215
+ * auto-disputing every rejection would be its own failure mode.
216
+ * - **Dispute raised by the other party, not yet responded** → calls
217
+ * `onDisputeAgainstMe` for evidence and calls `respondToDispute`. Same
218
+ * caveat: no callback means log-only.
219
+ * - **Dispute resolved-by-response but arbitrator never ruled (30d)** →
220
+ * calls `claimArbitratorTimeout` automatically (permissionless, no
221
+ * evidence needed — this just unsticks the agent's own frozen funds).
222
+ * - **Submitted, approval window elapsed (14d), poster silent** → calls
223
+ * `autoApprove` automatically.
224
+ *
225
+ * Returns an `unwatch()` function. Errors on any single bounty are logged
226
+ * and swallowed so one bad case can't kill the whole watchdog.
227
+ */
228
+ protect(options?: {
229
+ pollingIntervalMs?: number;
230
+ onRejection?: (meta: BountyMeta) => Promise<DisputeEvidenceOptions>;
231
+ onDisputeAgainstMe?: (meta: BountyMeta) => Promise<DisputeEvidenceOptions>;
232
+ onEvent?: (event: string, meta: BountyMeta) => void;
233
+ }): () => void;
234
+ private _protectOnce;
157
235
  runOnce(filter: OpenBountiesFilter, runTask: (description: string, meta: BountyMeta) => Promise<string>): Promise<bigint | null>;
158
236
  private _waitForTx;
159
237
  /**
@@ -174,8 +252,6 @@ declare class ArcBountyAgent {
174
252
  private _findExistingAgentId;
175
253
  private _ensureUsdcAllowance;
176
254
  private _resolveEvidenceCid;
177
- private _resolveDeadline;
178
- private _parseUsdc;
179
255
  }
180
256
 
181
257
  declare const ARC_TESTNET_RPC = "https://rpc.testnet.arc.network";
@@ -187,6 +263,18 @@ declare const CONTRACTS: {
187
263
  readonly USDC: Address;
188
264
  };
189
265
 
266
+ declare function parseUsdc(dollars: number): bigint;
267
+ /** `d` < 1e9 is interpreted as duration-in-seconds from `nowSec` (~30yr cutoff). */
268
+ declare function resolveDeadline(d: number | Date, nowSec?: number): bigint;
269
+ /**
270
+ * V4 worker bond: max(minBond, reward * bondBps / 10_000). Mirrors
271
+ * BountyAdapter._workerBondFor. Defaults are the live V4 parameters
272
+ * (15% / $0.50 floor); pass the on-chain WORKER_BOND_BPS / MIN_WORKER_BOND
273
+ * values to stay correct across redeploys with different parameters.
274
+ */
275
+ declare function workerBondFor(reward: bigint, bondBps?: bigint, minBond?: bigint): bigint;
276
+ declare function matchesBountyFilter(m: BountyMeta, f: OpenBountiesFilter): boolean;
277
+
190
278
  declare const BOUNTY_ADAPTER_ABI: readonly [{
191
279
  readonly name: "createBounty";
192
280
  readonly type: "function";
@@ -218,6 +306,9 @@ declare const BOUNTY_ADAPTER_ABI: readonly [{
218
306
  }, {
219
307
  readonly name: "humanOnly";
220
308
  readonly type: "bool";
309
+ }, {
310
+ readonly name: "requireWorkerBond";
311
+ readonly type: "bool";
221
312
  }];
222
313
  }];
223
314
  readonly outputs: readonly [{
@@ -371,6 +462,27 @@ declare const BOUNTY_ADAPTER_ABI: readonly [{
371
462
  readonly type: "uint256";
372
463
  }];
373
464
  readonly outputs: readonly [];
465
+ }, {
466
+ readonly name: "claimArbitratorTimeout";
467
+ readonly type: "function";
468
+ readonly stateMutability: "nonpayable";
469
+ readonly inputs: readonly [{
470
+ readonly name: "jobId";
471
+ readonly type: "uint256";
472
+ }];
473
+ readonly outputs: readonly [];
474
+ }, {
475
+ readonly name: "allJobIds";
476
+ readonly type: "function";
477
+ readonly stateMutability: "view";
478
+ readonly inputs: readonly [{
479
+ readonly name: "";
480
+ readonly type: "uint256";
481
+ }];
482
+ readonly outputs: readonly [{
483
+ readonly name: "";
484
+ readonly type: "uint256";
485
+ }];
374
486
  }, {
375
487
  readonly name: "getOpenBounties";
376
488
  readonly type: "function";
@@ -472,6 +584,12 @@ declare const BOUNTY_ADAPTER_ABI: readonly [{
472
584
  }, {
473
585
  readonly name: "disputeRulingHash";
474
586
  readonly type: "string";
587
+ }, {
588
+ readonly name: "requireWorkerBond";
589
+ readonly type: "bool";
590
+ }, {
591
+ readonly name: "workerBond";
592
+ readonly type: "uint256";
475
593
  }];
476
594
  }];
477
595
  }, {
@@ -577,6 +695,45 @@ declare const BOUNTY_ADAPTER_ABI: readonly [{
577
695
  readonly name: "";
578
696
  readonly type: "uint256";
579
697
  }];
698
+ }, {
699
+ readonly name: "ARBITRATOR_TIMEOUT";
700
+ readonly type: "function";
701
+ readonly stateMutability: "view";
702
+ readonly inputs: readonly [];
703
+ readonly outputs: readonly [{
704
+ readonly name: "";
705
+ readonly type: "uint256";
706
+ }];
707
+ }, {
708
+ readonly name: "uniquePosterCount";
709
+ readonly type: "function";
710
+ readonly stateMutability: "view";
711
+ readonly inputs: readonly [{
712
+ readonly name: "agentId";
713
+ readonly type: "uint256";
714
+ }];
715
+ readonly outputs: readonly [{
716
+ readonly name: "";
717
+ readonly type: "uint256";
718
+ }];
719
+ }, {
720
+ readonly name: "WORKER_BOND_BPS";
721
+ readonly type: "function";
722
+ readonly stateMutability: "view";
723
+ readonly inputs: readonly [];
724
+ readonly outputs: readonly [{
725
+ readonly name: "";
726
+ readonly type: "uint256";
727
+ }];
728
+ }, {
729
+ readonly name: "MIN_WORKER_BOND";
730
+ readonly type: "function";
731
+ readonly stateMutability: "view";
732
+ readonly inputs: readonly [];
733
+ readonly outputs: readonly [{
734
+ readonly name: "";
735
+ readonly type: "uint256";
736
+ }];
580
737
  }, {
581
738
  readonly name: "BountyCreated";
582
739
  readonly type: "event";
@@ -701,6 +858,70 @@ declare const BOUNTY_ADAPTER_ABI: readonly [{
701
858
  readonly type: "bool";
702
859
  readonly indexed: false;
703
860
  }];
861
+ }, {
862
+ readonly name: "ArbitratorTimeoutClaimed";
863
+ readonly type: "event";
864
+ readonly inputs: readonly [{
865
+ readonly name: "jobId";
866
+ readonly type: "uint256";
867
+ readonly indexed: true;
868
+ }, {
869
+ readonly name: "posterAmount";
870
+ readonly type: "uint256";
871
+ readonly indexed: false;
872
+ }, {
873
+ readonly name: "providerAmount";
874
+ readonly type: "uint256";
875
+ readonly indexed: false;
876
+ }];
877
+ }, {
878
+ readonly name: "WorkerBondPosted";
879
+ readonly type: "event";
880
+ readonly inputs: readonly [{
881
+ readonly name: "jobId";
882
+ readonly type: "uint256";
883
+ readonly indexed: true;
884
+ }, {
885
+ readonly name: "worker";
886
+ readonly type: "address";
887
+ readonly indexed: true;
888
+ }, {
889
+ readonly name: "amount";
890
+ readonly type: "uint256";
891
+ readonly indexed: false;
892
+ }];
893
+ }, {
894
+ readonly name: "WorkerBondRefunded";
895
+ readonly type: "event";
896
+ readonly inputs: readonly [{
897
+ readonly name: "jobId";
898
+ readonly type: "uint256";
899
+ readonly indexed: true;
900
+ }, {
901
+ readonly name: "worker";
902
+ readonly type: "address";
903
+ readonly indexed: true;
904
+ }, {
905
+ readonly name: "amount";
906
+ readonly type: "uint256";
907
+ readonly indexed: false;
908
+ }];
909
+ }, {
910
+ readonly name: "WorkerBondForfeited";
911
+ readonly type: "event";
912
+ readonly inputs: readonly [{
913
+ readonly name: "jobId";
914
+ readonly type: "uint256";
915
+ readonly indexed: true;
916
+ }, {
917
+ readonly name: "poster";
918
+ readonly type: "address";
919
+ readonly indexed: true;
920
+ }, {
921
+ readonly name: "amount";
922
+ readonly type: "uint256";
923
+ readonly indexed: false;
924
+ }];
704
925
  }];
705
926
  declare const IDENTITY_REGISTRY_ABI: readonly [{
706
927
  readonly name: "register";
@@ -832,4 +1053,4 @@ declare function validateAgentMetadata(m: unknown): asserts m is AgentMetadata;
832
1053
  /** Pin a validated metadata blob to IPFS and return the `ipfs://<cid>` URI. */
833
1054
  declare function pinAgentMetadata(meta: AgentMetadata): Promise<string>;
834
1055
 
835
- export { ARC_TESTNET_CHAIN_ID, ARC_TESTNET_RPC, type AgentInfo, type AgentMetadata, ArcBountyAgent, type ArcBountyAgentConfig, type ArcBountySection, BOUNTY_ADAPTER_ABI, type BountyMeta, CONTRACTS, type CreateBountyOptions, type DisputeEvidenceOptions, ERC20_ABI, IDENTITY_REGISTRY_ABI, type OpenBountiesFilter, type ReputationScore, type SubmitWorkOptions, type TxResult, fetchIpfsJson, fetchIpfsText, isPinningConfigured, pinAgentMetadata, pinText, validateAgentMetadata };
1056
+ export { ARC_TESTNET_CHAIN_ID, ARC_TESTNET_RPC, type AgentInfo, type AgentMetadata, ArcBountyAgent, type ArcBountyAgentConfig, type ArcBountySection, BOUNTY_ADAPTER_ABI, type BountyMeta, CONTRACTS, type CircleWalletConfig, type CreateBountyOptions, type DisputeEvidenceOptions, ERC20_ABI, IDENTITY_REGISTRY_ABI, type OpenBountiesFilter, type ReputationScore, type SubmitWorkOptions, type TxResult, fetchIpfsJson, fetchIpfsText, isPinningConfigured, matchesBountyFilter, parseUsdc, pinAgentMetadata, pinText, resolveDeadline, validateAgentMetadata, workerBondFor };