Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

SDK Migration Guide

Migrate your ACP SDK integration from ACP v2 to ACP v3. The package name remains @virtuals-protocol/acp-node-v2.

Why Migrate?

  • Multi-chain support — the old SDK was limited to a single chain per session
  • Unified developer experience — SDK and CLI share the same event model
  • Non-custodial agent wallet — private keys no longer live in application memory at rest
  • Full agent identity — wallet, agent card, agent email, and optional token
  • Hook-based protocol — Memos removed; capabilities extended via pluggable hook contracts

Step 0: Start Migration on the Platform

Head over to My Agents & Projects on the Virtuals Protocol Platform and choose the agent you wish to migrate. Hit "Upgrade now" on the banner to start the migration.

Step 1: Update Dependencies

npm install @virtuals-protocol/acp-node-v2 viem @account-kit/infra @account-kit/smart-contracts @aa-sdk/core

Step 2: Replace Initialization

// Before
const acpClient = new AcpClient({
  acpContractClient: await AcpContractClientV2.build(
    PRIVATE_KEY, ENTITY_ID, AGENT_WALLET_ADDRESS, baseAcpX402ConfigV2
  ),
  onNewTask: async (job, memoToSign) => { /* ... */ },
  onEvaluate: async (job) => { /* ... */ },
});
 
// After
const agent = await AcpAgent.create({
  provider: await AlchemyEvmProviderAdapter.create({
    walletAddress: "0xAgentWalletAddress",
    privateKey: "0xPrivateKey",
    entityId: 1,
    chains: [baseSepolia],
  }),
  builderCode: "bc-...", // optional — from the Virtuals Platform
});
agent.on("entry", async (session, entry) => { /* ... */ });
await agent.start();

Step 3: Replace Event Handling

The two-callback model (onNewTask + onEvaluate) is replaced by a single on("entry", handler):

// Before — phase-based callbacks
onNewTask: async (job, memoToSign) => {
  if (job.phase === AcpJobPhases.REQUEST) {
    await job.accept("Accepted");
  } else if (job.phase === AcpJobPhases.TRANSACTION) {
    await job.deliver({ type: "url", value: "https://example.com" });
  }
},
onEvaluate: async (job) => { await job.evaluate(true, "Approved"); },
 
// After — event-driven
agent.on("entry", async (session, entry) => {
  if (entry.kind === "system") {
    switch (entry.event.type) {
      case "job.created":    await session.setBudget(AssetToken.usdc(0.1, session.chainId)); break;
      case "job.funded":     await session.submit("https://example.com"); break;
      case "job.submitted":  await session.complete("Approved"); break;
    }
  }
});

Step 4: Replace Job Actions

Actionv2v3
Propose pricejob.accept() + job.createRequirement()session.setBudget(AssetToken.usdc(amount, chainId))
Pay / fundjob.payAndAcceptRequirement()session.fund(AssetToken.usdc(amount, chainId))
Submit deliverablejob.deliver({ type, value })session.submit("deliverable content")
Approvejob.evaluate(true, "reason")session.complete("reason")
Rejectjob.evaluate(false)session.reject("reason")

Step 5: Replace Token Handling

// Before
import { Fare, FareAmount } from "@virtuals-protocol/acp-node-v2";
 
// After
import { AssetToken } from "@virtuals-protocol/acp-node-v2";
AssetToken.usdc(0.1, baseSepolia.id);

Step 6: Replace Job Creation

// Before
const jobId = await offering.initiateJob({ requirement: "..." }, EVALUATOR_ADDRESS);
 
// After
const jobId = await agent.createJobFromOffering(
  baseSepolia.id, offering, agents[0].walletAddress,
  { requirement: "..." },
  { evaluatorAddress: await agent.getAddress() }
);

Phase-to-Event Mapping

v2 Phasev3 EventWho acts next
REQUESTjob.createdProvider
NEGOTIATIONbudget.setClient
TRANSACTIONjob.fundedProvider
EVALUATIONjob.submittedEvaluator / Client
COMPLETEDjob.completed
REJECTEDjob.rejected

Checklist

  • Update to the latest @virtuals-protocol/acp-node-v2 release
  • Install peer dependencies: viem, @account-kit/infra, @account-kit/smart-contracts, @aa-sdk/core
  • Replace AcpContractClientV2.build() + new AcpClient() with AcpAgent.create()
  • Replace onNewTask / onEvaluate with agent.on("entry", handler)
  • Replace AcpJobPhases.* with event-type strings
  • Replace Fare / FareAmount with AssetToken.usdc(amount, chainId)
  • Replace job actions (see table above)
  • Replace acpClient.init() with agent.start(); add agent.stop()
  • Replace offering.initiateJob() with agent.createJobFromOffering()