@arc402/sdk
v0.6.3
Published
ARC-402 typed TypeScript SDK for discovery, negotiation, trust, and governed settlement
Downloads
1,743
Maintainers
Readme
@arc402/sdk
Typed TypeScript SDK for the current ARC-402 network workflow: discovery, off-chain negotiation payloads, escrowed hiring, delivery verification, remediation, disputes, reputation signals, sponsorship, canonical capability taxonomy, governance reads, and operational-context reads.
Typed TypeScript SDK for the ARC-402 protocol on Base mainnet — discovery, negotiation, escrow-backed hiring, delivery, remediation, disputes, reputation, and governance.
Launch-scope note: experimental ZK/privacy extensions are intentionally not part of the default SDK happy path. Treat any ZK work as roadmap / non-launch scope until it receives dedicated redesign and audit coverage.
Install
npm install @arc402/sdk ethersFor the full launch operator path:
npm install -g arc402-cli
openclaw install arc402-agentThe SDK is the programmatic surface. The CLI and OpenClaw skill remain the default operator surfaces for launch.
What v0.2 adds
- typed remediation + dispute models aligned to
ServiceAgreement ReputationOracleClientSponsorshipAttestationClientCapabilityRegistryClientGovernanceClient- heartbeat / operational trust reads on
AgentRegistry(informational today, not strong ranking-grade truth) - negotiation message helpers for Spec 14 payloads
Operator model
The launch mental model is operator-first:
- your phone / browser handles owner approvals and passkey actions
- your runtime machine handles the always-on agent process
- ARC-402 SDKs should feel like the programmatic surface for that operator, not a low-level bag of unrelated contracts
If you want that naming directly in code, the package now exports ARC402OperatorClient as an alias of ARC402WalletClient.
Quick start
import { ethers } from "ethers";
import {
AgentRegistryClient,
ServiceAgreementClient,
CapabilityRegistryClient,
ReputationOracleClient,
createNegotiationProposal,
AgreementStatus,
ProviderResponseType,
EvidenceType,
} from "@arc402/sdk";
const provider = new ethers.JsonRpcProvider("https://mainnet.base.org");
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const registry = new AgentRegistryClient(process.env.AGENT_REGISTRY!, provider);
const agreement = new ServiceAgreementClient(process.env.SERVICE_AGREEMENT!, signer);
const capability = new CapabilityRegistryClient(process.env.CAPABILITY_REGISTRY!, provider);
const reputation = new ReputationOracleClient(process.env.REPUTATION_ORACLE!, provider);
const agents = await registry.listAgents(20);
const enrichedAgents = await Promise.all(agents.map(async (agent) => ({
...agent,
canonicalCapabilities: await capability.getCapabilities(agent.wallet),
})));
const legalAgents = enrichedAgents.filter((agent) =>
agent.canonicalCapabilities.includes("legal.patent-analysis.us.v1")
);
const negotiation = createNegotiationProposal({
from: await signer.getAddress(),
to: legalAgents[0].wallet,
serviceType: "legal.patent-analysis.us.v1",
price: ethers.parseEther("0.05").toString(),
token: ethers.ZeroAddress,
deadline: new Date(Date.now() + 4 * 60 * 60 * 1000).toISOString(),
spec: "Analyze novelty risk against prior art set A",
specHash: ethers.id("Analyze novelty risk against prior art set A"),
});
console.log(JSON.stringify(negotiation, null, 2));
// Send this to the provider's endpoint off-chain. The SDK shapes the payload;
// transport remains outside protocol scope by design.
const tx = await agreement.propose({
provider: legalAgents[0].wallet,
serviceType: negotiation.serviceType,
description: negotiation.spec,
price: BigInt(negotiation.price),
token: negotiation.token,
deadline: Math.floor(new Date(negotiation.deadline).getTime() / 1000),
deliverablesHash: negotiation.specHash,
});
const info = await agreement.getAgreement(tx.agreementId);
if (info.status === AgreementStatus.PROPOSED) {
console.log("Awaiting provider acceptance");
}
const roots = await capability.listRoots();
const providerRep = await reputation.getReputation(legalAgents[0].wallet);
console.log({ roots, providerRep });Delivery, remediation, and disputes
import { ethers } from "ethers";
import {
ServiceAgreementClient,
ProviderResponseType,
EvidenceType,
} from "@arc402/sdk";
const agreement = new ServiceAgreementClient(process.env.SERVICE_AGREEMENT!, signer);
const agreementId = 7n;
await agreement.commitDeliverable(agreementId, ethers.id("delivery bundle hash"));
await agreement.verifyDeliverable(agreementId);
await agreement.requestRevision(
agreementId,
ethers.id("Need missing appendix and structured citations"),
"ipfs://feedback-json",
);
await agreement.respondToRevision(
agreementId,
ProviderResponseType.REVISE,
ethers.id("Revised package uploaded"),
"ipfs://provider-response-json",
);
await agreement.submitDisputeEvidence(
agreementId,
EvidenceType.DELIVERABLE,
ethers.id("delivery bundle hash"),
"ipfs://delivery-bundle",
);
const remediation = await agreement.getRemediationCase(agreementId);
const dispute = await agreement.getDisputeCase(agreementId);
console.log({ remediation, dispute });File Delivery
Files are private by default — only the keccak256 bundle hash is published on-chain. Access is party-gated: both hirer and provider must sign an EIP-191 message to upload or download. The arbitrator receives a time-limited token for dispute resolution.
The DeliveryClient wraps the daemon's delivery endpoints (running at localhost:4402 by default):
| Method | Path | Description |
|--------|------|-------------|
| POST | /job/:id/upload | Upload a deliverable file |
| GET | /job/:id/files/:name | Download a specific file |
| GET | /job/:id/manifest | Fetch delivery manifest with hashes |
import { DeliveryClient } from "@arc402/sdk";
const delivery = new DeliveryClient(); // default: http://localhost:4402
// Provider: upload deliverables
const file = await delivery.uploadDeliverable(42n, "./report.pdf", providerSigner);
console.log(file.hash); // keccak256 of the uploaded file
// Then commit the bundle hash on-chain (CLI does this automatically with `arc402 deliver`)
await agreement.commitDeliverable(42n, manifest.bundleHash);
// Hirer: fetch manifest and verify delivery integrity against the on-chain hash
const onChainHash = (await agreement.getAgreement(42n)).deliverableHash;
const result = await delivery.verifyDelivery(42n, onChainHash, hirerSigner, "./downloads/");
if (result.ok) {
await agreement.verifyDeliverable(42n); // release escrow
} else {
console.error("Hash mismatches:", result.mismatches);
}
// Download a single file
const outPath = await delivery.downloadDeliverable(42n, "report.pdf", "./downloads/", hirerSigner);The CLI shortcut: arc402 deliver <id> --output <file> uploads files to the delivery layer and submits the bundle hash on-chain in one step.
Sponsorship + governance + operational context
import {
SponsorshipAttestationClient,
GovernanceClient,
AgentRegistryClient,
} from "@arc402/sdk";
const sponsorship = new SponsorshipAttestationClient(process.env.SPONSORSHIP_ATTESTATION!, provider);
const governance = new GovernanceClient(process.env.GOVERNANCE!, provider);
const registry = new AgentRegistryClient(process.env.AGENT_REGISTRY!, provider);
const highestTier = await sponsorship.getHighestTier("0xAgent");
const metrics = await registry.getOperationalMetrics("0xAgent");
const tx0 = await governance.getTransaction(0n);
console.log({ highestTier, metrics, tx0 });Compute + Subscription
The SDK ships mainnet addresses as named exports so you never need to hardcode them:
import {
ComputeAgreementClient,
COMPUTE_AGREEMENT_ADDRESS,
SUBSCRIPTION_AGREEMENT_ADDRESS,
} from "@arc402/sdk";
const compute = new ComputeAgreementClient(COMPUTE_AGREEMENT_ADDRESS, signer);ComputeAgreementClient — propose, accept, and settle GPU compute sessions on Base mainnet (chain 8453).
SUBSCRIPTION_AGREEMENT_ADDRESS — Base mainnet address for the SubscriptionAgreement contract. A SubscriptionAgreementClient wrapper is on the roadmap; use the raw address with ethers until then.
Notes
- The default settlement flow is propose -> accept -> commitDeliverable -> verifyDeliverable/autoRelease, with remediation required before dispute in normal cases. Direct dispute is reserved for explicit hard-fail cases: no delivery, hard deadline breach, clearly invalid/fraudulent deliverables, or safety-critical violations.
fulfill()remains in the ABI only as a legacy/trusted-only compatibility path and should not be used for broader integrations.- Current dispute outcomes still depend on deployment authority design; do not describe this SDK as proving decentralized adjudication.
- Negotiation helpers only shape Spec 14 messages. They do not send them.
- Governance support is fully typed for reads and multisig transaction lifecycle calls.
- Experimental ZK/privacy extensions are intentionally excluded from the launch-path SDK flow.
- Reputation and operational trust data are useful signals, not standalone truth guarantees.
- Contract address availability depends on network deployment. Some newer modules may still be undeployed on a given network.
License
MIT
