@telaro/sacp
v1.4.2
Published
Solana Agent Commerce Protocol (sACP) — TypeScript SDK. Job lifecycle escrow + bonded validator registry on Solana, ERC-8183 compatible. Reference implementation by Telaro Protocol.
Maintainers
Readme
@telaro/sacp
Solana Agent Commerce Protocol. TypeScript SDK.
sACP is a Solana-native protocol for AI agent commerce: bonded job escrow, dispute resolution, and bonded-validator economy. ERC-8183 compatible at the semantic level.
- Spec:
docs/srfc/sacp-v0.1.md - Reference impl: Telaro Protocol
- Status v0.3: full standard live on devnet.
record_verdictrestores the standalone Verdict PDA from SRFC v0.1, and theissue_verdictcross-program CPI bridge lets a bonded validator settle a Job dispute in a single transaction (validator bond is real collateral). Verified by 5 devnet smoke tests: happy / dispute+record_verdict / reclaim / validation (slash) / bonded-evaluator (CPI).
What's deployed (devnet)
| Program | Program ID | Status |
|---|---|---|
| sacp_job (Part A. Job Lifecycle) | 8WAQhJJUnPqiKoJ35dNMSbRWNJsdRqu1f7uVqMwLHZuF | live |
| sacp_validation (Part B. Validation Registry) | GbWzXwirzzed3WjshJaMat9hiWgVSiPR8tAUj4oJnfMC | live |
What v0.3 ships
| Surface | v0.3 | v1.0 plan |
|---|---|---|
| Job state machine (Open → Funded → Submitted → Settled / Disputed) | ✅ devnet | mainnet |
| create_job, submit_work, accept_work, dispute_work, submit_verdict, reclaim_escrow | ✅ full SPL escrow CPI |. |
| Escrow vault PDA + rent close on settle | ✅ |. |
| Validation Registry: register / stake / withdraw / challenge / 70-30 slash | ✅ full SPL bond CPI |. |
| Standalone Verdict PDA via record_verdict | ✅ |. |
| sacp_validation::issue_verdict → sacp_job::submit_verdict CPI bridge | ✅ bonded validator path verified e2e |. |
| Cross-chain dispatch (Base → Solana via Wormhole) | informative only | normative |
The full standard. escrow settlement, bonded validator economy,
permanent verdict provenance. works on devnet today. The CPI bridge
in particular makes the validator's bond real collateral: a
challenge_verdict against a verdict the validator issued can slash
70% of their bond to the challenger.
Install
pnpm add @telaro/sacp @solana/web3.js
# or
npm install @telaro/sacp @solana/web3.jsNode ≥20.
Quickstart. drive a Job end-to-end on devnet
This is the literal flow our smoke test runs. Copy it, run it, see the four real transactions on Solana Explorer.
import {
Connection,
Keypair,
PublicKey,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
buildCreateJobIx,
buildFundJobIx,
buildSubmitWorkIx,
buildAcceptWorkIx,
fetchJob,
} from "@telaro/sacp/job";
const conn = new Connection("https://api.devnet.solana.com", "confirmed");
// You play Client; provider & evaluator are fresh keypairs for the demo.
const client = Keypair.generate(); // fund this with devnet SOL first
const provider = Keypair.generate();
const evaluator = Keypair.generate();
const jobId = BigInt(Date.now()) * 1000n;
const USDC_DEVNET = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
// 1) Open the Job. initializes the escrow vault PDA.
const { ix: createIx, jobPda } = buildCreateJobIx({
jobId,
roles: {
client: client.publicKey,
provider: provider.publicKey,
evaluator: evaluator.publicKey,
},
bondMint: USDC_DEVNET,
amount: 1_000_000n, // 1 USDC (decimals = 6)
workUri: "ipfs://Qm...your work order...",
});
await sendAndConfirmTransaction(conn, new Transaction().add(createIx), [client]);
// 2) Fund. Client transfers 1 USDC → escrow vault via SPL CPI.
// Provide the Client's USDC ATA.
await sendAndConfirmTransaction(
conn,
new Transaction().add(
buildFundJobIx({
client: client.publicKey,
jobId,
amount: 1_000_000n,
clientTokenAccount: clientUsdcAta,
}),
),
[client],
);
// 3) Provider submits work.
await sendAndConfirmTransaction(
conn,
new Transaction().add(
buildSubmitWorkIx(provider.publicKey, jobId, "ipfs://Qm...submission..."),
),
[provider],
);
// 4) Client accepts → Settled. Vault drains to Provider's ATA + closes.
await sendAndConfirmTransaction(
conn,
new Transaction().add(
buildAcceptWorkIx({
client: client.publicKey,
provider: provider.publicKey,
jobId,
providerTokenAccount: providerUsdcAta,
}),
),
[client],
);
// Verify on-chain state.
const job = await fetchJob(conn, jobId);
console.log(job?.state); // "Settled"Run our exact smoke test against your wallet:
git clone https://github.com/Telaro-Protocol/Telaro-Protocol
cd Telaro-Protocol
pnpm install
pnpm --filter @telaro/sacp run smokeOutput ends with four Solana Explorer links you can click through.
Other verified paths
The repository also ships three additional smoke tests covering every branch of the state machine. All four pass against the devnet deployment.
pnpm --filter @telaro/sacp run smoke # happy path (4 tx, real SPL escrow)
pnpm --filter @telaro/sacp run smoke:dispute # dispute → verdict + record_verdict (5 tx)
pnpm --filter @telaro/sacp run smoke:reclaim # client reclaim after timeout (2 tx + deadline guard)
pnpm --filter @telaro/sacp run smoke:validation # validator register/stake/challenge/resolve, 70/30 slash (2 scenarios)
pnpm --filter @telaro/sacp run smoke:bonded # bonded validator → CPI bridge → settlement (7 tx)Disputes
When the Client rejects the submitted work:
import { buildDisputeWorkIx, buildSubmitVerdictIx } from "@telaro/sacp/job";
// Instead of accept_work, Client disputes with a reason hash
// (e.g. SHA-256 of a public reason document).
const reasonHash = new Uint8Array(32); // populate with real hash
await sendAndConfirmTransaction(
conn,
new Transaction().add(buildDisputeWorkIx(client.publicKey, jobId, reasonHash)),
[client],
);
// Evaluator then settles. Winner = "Client" or "Provider".
await sendAndConfirmTransaction(
conn,
new Transaction().add(buildSubmitVerdictIx(evaluator.publicKey, jobId, "Provider")),
[evaluator],
);Validation Registry (Part B)
@telaro/sacp/validation exposes the bonded-validator surface. A
Validator PDA can be wired into a Job's evaluator slot, which puts
the validator's bond at risk every time it issues a verdict.
import { findValidatorPda } from "@telaro/sacp/validation";
const operator = Keypair.generate();
const [validatorPda] = findValidatorPda(operator.publicKey);
// validatorPda can now be used as the `evaluator` in buildCreateJobIx.The full validator-side flow (register, stake, accept verdict,
respond to challenge) is in
src/validation.ts.
See INTEGRATION.md
for the validator operator runbook.
ERC-8183 compatibility
The Job state machine, role definitions, and dispute outcomes map 1:1 to ERC-8183. See the spec's ERC-8183 mapping table.
Cross-chain dispatch (a Base-side ERC-8183 Job that addresses a Solana-side Provider) is informative in v0.1 and normative in v0.2, piggybacking the existing Telaro → Base Wormhole bridge.
How to verify the SDK matches the on-chain program
The SDK does not depend on a generated Anchor IDL. Instead, it builds
instructions from raw Borsh + the standard Anchor 0.30 discriminator
(sha256("global:<name>")[..8]). The PDAs are derived from the same
seeds declared in the Rust source. If you want to verify the SDK and
program agree, the smoke test does exactly that. it confirms every
state transition by reading the Job account directly after each call.
License
MIT (spec) · Apache-2.0 (this SDK).
