Try AGIRAILS in mock mode — no wallet, no keys, no actp init needed:
bash
npm install @agirails/sdk
Save as quickstart.js and run with node quickstart.js:
javascript
const { ACTPClient } = require('@agirails/sdk');
const { parseUnits } = require('ethers');
async function main() {
// No actp init needed — mock mode works standalone
const client = await ACTPClient.create({ mode: 'mock' });
// Mint test USDC (mock only). parseUnits handles the 6-decimal math for you.
await client.mintTokens(client.getAddress(), parseUnits('10000', 6));
// All payment amounts are human-readable strings
const result = await client.pay({
to: '0x0000000000000000000000000000000000000001',
amount: '5.00', // 5 USDC
});
console.log('Payment:', result.txId, '| State:', result.state);
console.log('Escrow:', result.escrowId, '| Release required:', result.releaseRequired);
}
main().catch(console.error);
Note: This quick start runs without actp init. If you use actp init -m mock first (recommended for real projects), it auto-mints 10,000 test USDC — no need to call mintTokens() in code.
Already set up? Just say: "Pay 10 USDC to 0xProvider for translation service"
The SDK ships as CommonJS. It works with require() and with bundlers (webpack, esbuild, Rollup). ESM projects can import CJS modules via Node.js auto-interop — no extra configuration needed.
This creates .actp/ config directory. On testnet/mainnet with wallet: generate, it also creates an encrypted keystore at .actp/keystore.json (chmod 600, gitignored) and registers the agent on-chain via gasless UserOp (Smart Wallet + 1,000 test USDC minted on testnet). On mock, it mints 10,000 test USDC locally.
Set the keystore password (testnet/mainnet only):
bash
export ACTP_KEY_PASSWORD="your-password"
Or for Python:
bash
pip install agirails
mode vs network — same values, different parameter names:
new Agent({ network: 'mock' }), provide('svc', fn, { network: 'mock' }) — high-level APIs use network
Both accept: 'mock', 'testnet', 'mainnet'
Step 4: Write Code
Prerequisites: Steps 1-3 complete, user confirmed with "yes".
All generated code MUST follow these rules:
Wrap in async function main() { ... } main().catch(console.error); (SDK is CommonJS, no top-level await)
See mode vs network note above — use the correct parameter name for each API
Testnet/mainnet requesters: release escrow after verifying delivery (mock auto-releases, real networks do NOT)
Based on the owner's answers, generate the appropriate code.
If intent = "earn" (Provider)
Level 0 — Simplest (one function call):
typescript
import { provide } from '@agirails/sdk';
async function main() {
const provider = provide('{{serviceTypes[0]}}', async (job) => {
// job.input — the data to process (object with request payload)
// job.budget — how much the requester is paying (USDC)
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
}, {
network: '{{network}}', // 'mock' | 'testnet' | 'mainnet'
filter: { minBudget: {{price}} }, // reject jobs below your price
});
console.log(`Provider running at ${provider.address}`);
// provider.status, provider.stats
// provider.on('payment:received', (amount) => ...)
// provider.pause(), provider.resume(), provider.stop()
}
main().catch(console.error);
Level 1 — Agent class (multiple services, lifecycle control):
typescript
import { Agent } from '@agirails/sdk';
async function main() {
const agent = new Agent({
name: '{{name}}',
network: '{{network}}',
behavior: {
concurrency: {{concurrency}},
},
});
agent.provide('{{serviceTypes[0]}}', async (job, ctx) => {
ctx.progress(50, 'Working...');
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
});
agent.on('payment:received', (amount) => {
console.log(`Earned ${amount} USDC`);
});
await agent.start();
console.log(`Agent running at ${agent.address}`);
}
main().catch(console.error);
If intent = "pay" (Requester)
If payment_mode = "actp" (escrow, disputes, multi-step):
typescript
import { request } from '@agirails/sdk';
async function main() {
const { result, transaction } = await request('{{services_needed}}', {
provider: '0xProviderAddress',
input: { /* your data here */ },
budget: {{budget}},
network: '{{network}}',
});
console.log(result);
console.log(`Transaction: ${transaction.id}, Amount: ${transaction.amount}`);
// IMPORTANT: On testnet/mainnet, release escrow after verifying delivery.
// Mock mode auto-releases after the dispute window — real networks do NOT.
// const client = await ACTPClient.create({ mode: '{{network}}' });
// await client.standard.releaseEscrow(transaction.id);
}
main().catch(console.error);
If payment_mode = "x402" (instant HTTP payment, no escrow):
x402 requires a real HTTP endpoint that returns 402 Payment Required. It works on testnet and mainnet — in mock mode, use ACTP for everything.
Critical 402 header format:x-payment-token must be USDC (symbol), not a token address.
An Agent can both provide() services and request() services in the same process:
typescript
import { Agent } from '@agirails/sdk';
async function main() {
const agent = new Agent({
name: '{{name}}',
network: '{{network}}',
behavior: { concurrency: {{concurrency}} },
});
// EARN: Provide a service
agent.provide('{{serviceTypes[0]}}', async (job, ctx) => {
ctx.progress(50, 'Working...');
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
});
agent.on('payment:received', (amount) => {
console.log(`Earned ${amount} USDC`);
});
await agent.start();
console.log(`Agent running at ${agent.address}`);
// PAY: Request a service from another agent (ACTP escrow)
const { result, transaction } = await agent.request('{{services_needed}}', {
input: { text: 'Hello world' },
budget: {{budget}},
});
console.log(result);
// IMPORTANT: On testnet/mainnet, release escrow after verifying delivery:
// const client = await ACTPClient.create({ mode: '{{network}}' });
// await client.standard.releaseEscrow(transaction.id);
}
main().catch(console.error);
If payment_mode = "both": In mock mode, generate ACTP-only code (above). x402 requires real HTTPS endpoints and only works on testnet/mainnet. When the user switches to testnet or mainnet, add x402 support by registering X402Adapter — see the x402 template above. You do NOT need to generate x402 code for mock mode.
Step 5: Verify
Confirm that initialization (Step 3) succeeded:
bash
npx actp balance # confirm USDC (10,000 in mock, 1,000 on testnet)
npx actp config show # confirm mode + address
In mock mode, everything runs locally with simulated USDC. The actp CLI is for inspection and manual operations (balance, tx status) — your agent code is what runs. Switch to testnet when ready to test on-chain, then mainnet for production.
The generated agent.ts calls provide('code-review', handler). When a requester calls request('code-review', { provider: '<your-address>', ... }), your handler runs, and USDC is released after the dispute window.
No marketplace matching exists yet. The requester must know your address and call your exact service name.
import { request } from '@agirails/sdk';
const { result } = await request('code-review', {
provider: '0xProviderAddress', // specific address, or omit for ServiceDirectory lookup
input: { code: '...' },
budget: 5,
network: 'mock',
});
There is no provider discovery. You specify the provider address directly, or omit provider to use the local ServiceDirectory. The serviceTypes taxonomy in the YAML above is a local naming convention — not a global registry.
For instant API payments (x402): Register X402Adapter via client.registerAdapter(), then use client.basic.pay({ to: 'https://...' }). x402 is NOT registered by default — see Step 4 for the full setup.
Testnet/mainnet limitation:request() does not auto-release escrow on real networks — you must call release() manually after verifying delivery. Proofs can be generated via ProofGenerator (hashing) or DeliveryProofBuilder (full EAS + IPFS); IPFS/Arweave upload is optional and requires client configuration.
Where to set env vars for OpenClaw: Add ACTP_KEY_PASSWORD to your openclaw.json under env.vars, not .bashrc. This keeps the password scoped to the agent process and avoids shell-wide exposure. Example:
Note: SDK includes default RPC endpoints. For high-volume production use, set up your own RPC via Alchemy or QuickNode and pass rpcUrl to client config.
Private Key Policy
Using ACTP_PRIVATE_KEY directly is discouraged. The SDK enforces a fail-closed policy:
mainnet / unknown — hard fail (throws error, refuses to start)
testnet — warns once, then proceeds (backward compatibility)
mock — silent (no real funds at risk)
Always prefer encrypted keystores (.actp/keystore.json or ACTP_KEYSTORE_BASE64). Raw private keys in env vars are a deployment security risk — they appear in process listings, CI logs, and crash dumps.
To check your deployment for leaked secrets:
bash
actp deploy:check # Scan for exposed keys, missing .dockerignore, etc.
actp deploy:env # Generate .dockerignore/.railwayignore with safe defaults
Both sides can open a DISPUTED (6) state after delivery. Either party can CANCELLED (7) early states.
Key Guarantees
Escrow Solvency — vault always holds >= active transaction amounts
State Monotonicity — states only move forward, never backwards
Deadline Enforcement — no delivery after deadline passes
Dispute Protection — 48h window to raise issues before settlement
State Machine
text
INITIATED --+-> QUOTED --> COMMITTED --> IN_PROGRESS --> DELIVERED --> SETTLED
| | | |
+--> COMMITTED | | +--> DISPUTED
| | | |
v v v v
CANCELLED CANCELLED SETTLED CANCELLED
Any of INITIATED, QUOTED, COMMITTED, IN_PROGRESS can -> CANCELLED
Only DELIVERED can -> DISPUTED
SETTLED and CANCELLED are terminal (no outbound transitions)
Valid transitions (from state.ts):
INITIATED → QUOTED, COMMITTED, CANCELLED
QUOTED → COMMITTED, CANCELLED
COMMITTED → IN_PROGRESS, CANCELLED
IN_PROGRESS → DELIVERED, CANCELLED
DELIVERED → SETTLED, DISPUTED
DISPUTED → SETTLED, CANCELLED
SETTLED → (terminal)
CANCELLED → (terminal)
Note: INITIATED can go directly to COMMITTED (skipping QUOTED).
Escrow
All payments flow through the EscrowVault smart contract:
Lock — On COMMITTED: requester's USDC is transferred to EscrowVault
Hold — During IN_PROGRESS and DELIVERED: funds are locked
Release — On SETTLED: USDC released to provider (minus 1% fee)
Refund — On CANCELLED: USDC returned to requester
In mock mode, escrow is simulated locally and request() auto-releases after the dispute window. On testnet/mainnet, you must call release() explicitly — the SDK will not auto-release real funds. Adapters set releaseRequired: true on real networks.
Fee
Rate: 1% of transaction amount
Minimum: $0.05 per transaction
Calculation: fee = max(amount * 0.01, 0.05)
ACTP: fee deducted on escrow release (SETTLED state) via ACTPKernel
agent.provide({
name: 'translation',
pricing: {
cost: {
base: 0.50, // $0.50 fixed cost per job
perUnit: { unit: 'word', rate: 0.005 } // $0.005 per word
},
margin: 0.40, // 40% profit margin
minimum: 1.00, // never accept less than $1
},
}, handler);
How it works:
SDK calculates: price = cost / (1 - margin)
If job budget >= price: accept
If job budget < price but > cost: counter-offer (via QUOTED state)
If job budget < cost: reject
There are no predefined "competitive/market/premium" strategies. You set your costs and margin directly.
The QUOTED state and PricingStrategy both exist in the SDK. However, the counter-offer flow requires both agents to be online — there is no persistent job board or stored quotes.
// Development
const client = await ACTPClient.create({
mode: 'mock',
});
await client.mintTokens('0x...', '1000000000'); // Mint test USDC
// Production (auto-detects keystore or ACTP_PRIVATE_KEY)
const client = await ACTPClient.create({
mode: 'mainnet',
});
Identity (ERC-8004)
Every agent gets a portable on-chain identity:
Optional — resolve agents via ERC8004Bridge from @agirails/sdk. Neither actp init nor Agent.start() registers identity automatically.
Portable — if registered, any marketplace reading ERC-8004 recognizes you
Reputation — settlement outcomes are reported on-chain only if the agent has a non-zero agentId set during transaction creation and release() is called explicitly
The SDK handles all contract addresses automatically — no manual configuration needed.
The SDK uses an adapter router. By default, only ACTP adapters (basic + standard) are registered. Other adapters require explicit registration:
0x1234... (Ethereum address) → ACTP (basic/standard) — registered by default
https://api.example.com/... → x402 — must register X402Adapter via client.registerAdapter()
agent-name or agent ID → ERC-8004 — must configure ERC-8004 bridge
typescript
// ACTP — works out of the box (default adapters)
await client.basic.pay({ to: '0xProviderAddress', amount: '5' });
// x402 — requires registering the adapter first:
import { X402Adapter } from '@agirails/sdk';
client.registerAdapter(new X402Adapter(client.getAddress(), {
expectedNetwork: 'base-sepolia', // or 'base-mainnet'
// Provide your own USDC transfer function (signer = your ethers.Wallet)
transferFn: async (to, amount) => {
const usdc = new ethers.Contract(USDC_ADDRESS, ['function transfer(address,uint256) returns (bool)'], signer);
return (await usdc.transfer(to, amount)).hash;
},
}));
await client.basic.pay({ to: 'https://api.provider.com/service', amount: '1' });
// ERC-8004 — requires bridge configuration:
import { ERC8004Bridge } from '@agirails/sdk';
const bridge = new ERC8004Bridge({ network: 'base-sepolia' });
const agent = await bridge.resolveAgent('12345');
await client.basic.pay({ to: agent.wallet, amount: '5', erc8004AgentId: '12345' });
Only ACTP address routing works out of the box. x402 and ERC-8004 require explicit setup.
You can also force a specific adapter via metadata:
typescript
await client.basic.pay({
to: '0xProvider',
amount: '5.00',
metadata: { paymentMethod: 'x402' }, // force x402
});
x402 Fee Splitting
Both ACTP (escrow) and x402 (instant) payments carry the same 1% platform fee ($0.05 minimum).
For x402 payments, fees are split atomically on-chain via the X402Relay contract:
Provider receives 99% (or gross minus $0.05 minimum)
Treasury receives 1% fee
Single transaction — no partial failure risk
typescript
// Fee breakdown is included in the result
const result = await client.basic.pay({
to: 'https://api.provider.com/service',
amount: '100.00',
});
console.log(result.feeBreakdown);
// { grossAmount: '100000000', providerNet: '99000000',
// platformFee: '1000000', feeBps: 100, estimated: true }
The estimated: true flag means the breakdown was calculated client-side. The on-chain X402Relay contract is the source of truth for actual fee amounts.
Config Management (AGIRAILS.md as Source of Truth)
This file is your agent's canonical configuration. You can publish its hash on-chain for verifiable config management:
bash
actp publish # Hash AGIRAILS.md -> store configHash + configCID in AgentRegistry
actp diff # Compare local AGIRAILS.md hash vs on-chain — detect drift
actp pull # Restore AGIRAILS.md from on-chain configCID (IPFS)
This enables:
Verifiable config: anyone can verify your agent's stated service types match on-chain
Drift detection: SDK checks config hash on startup (non-blocking warning if mismatch)
Recovery: restore your config from on-chain if local file is lost
AGIRAILS.md format — must start with YAML frontmatter (---):
actp deploy:check recursively scans your project (depth 5, skips node_modules/.git) for exposed keys.
--quiet flag hides PASS and WARN, showing only FAIL results.
Service Types (MVP limitation)
The serviceTypes taxonomy in the YAML frontmatter is a suggested naming convention, not a discovery mechanism.
provide('code-review') only matches request('code-review') — exact string match, case-sensitive
Typos like content-writting instead of content-writing will silently fail to match — double-check spelling
There is no global registry, search, or automatic matching between agents
Requesters must know the provider's address and service name
ServiceDirectory is in-memory, per-process. A provider in one process is not visible to a requester in another process. For cross-process communication, pass the provider's address explicitly via the provider: field.
The planned Job Board (Phase 1D) will add public job posting and bidding
Discovery (Optional)
Agents can publish an A2A-compatible Agent Card for discovery:
Host at /.well-known/agent.json for directory listings.
Discovery is not built into the SDK. This Agent Card follows the A2A spec and can be consumed by external directories or marketplaces. The SDK itself does not query or consume Agent Cards.
Integration by Runtime
AGIRAILS works with any AI runtime. Here's how to integrate with specific platforms:
Then tell your agent: "Pay 10 USDC to 0xProvider for translation service"
See {baseDir}/openclaw/QUICKSTART.md for the 5-minute setup guide.
n8n
Install the community node in your n8n instance:
bash
npm install n8n-nodes-actp
Adds ACTP nodes to any workflow: create transactions, track state, release escrow.
Any Other Runtime
Install the SDK (npm install @agirails/sdk or pip install agirails), use provide() / request() or the Agent class. The SDK handles wallet creation, escrow, and settlement automatically.
"Address required for testnet" — run actp init -m testnet to generate a keystore, or set ACTP_PRIVATE_KEY.
"Unknown network" — SDK supports base-sepolia (testnet) and base-mainnet (mainnet).
Transaction stuck in INITIATED — no provider registered for that service name. Ensure a provider is running with provide('exact-service-name', handler) on the same network.
"Invalid state transition" — states only move forward, never backward. Check the state machine above.
Private keys are stored in an AES-128-CTR encrypted keystore (.actp/keystore.json, chmod 600, auto-gitignored). They are decrypted in-memory only when signing transactions, never transmitted.
Raw private keys (ACTP_PRIVATE_KEY) are blocked on mainnet by the SDK's fail-closed policy. On testnet they trigger a one-time warning.
On-chain transactions are public on Base L2. Wallet addresses and USDC amounts are visible to anyone.
No telemetry or analytics are collected by the SDK or this skill.
No data is sent to AGIRAILS servers. All transactions go directly to Base L2 via public or user-configured RPC endpoints.
Trust
ACTP is an open-source protocol. Smart contracts are verified on-chain:
Contracts verified on Sourcify (Base Sepolia + Base Mainnet)
Gas sponsorship is provided by Coinbase CDP (ERC-4337 Paymaster). Review Coinbase's terms at docs.cdp.coinbase.com.
Agent Configuration (Machine-Readable)
AI agents: parse this YAML block to extract onboarding questions, service types, and state definitions. The SDK handles all contract addresses and network configuration automatically.
yaml
name: AGIRAILS Payments
version: 3.0.0
protocol: AGIRAILS
spec: ACTP
description: Official ACTP (Agent Commerce Transaction Protocol) SDK — the first trustless payment layer for AI agents. Pay for services via escrow (ACTP) or instant HTTP payments (x402). Receive payments, check transaction status, resolve agent identities, or handle disputes — all with USDC on Base L2.
author: AGIRAILS Inc.
homepage: https://agirails.io
repository: https://github.com/agirails/openclaw-skill
license: MIT
network: base
currency: USDC
fee: "1% ($0.05 min)"
sdk:
npm: "@agirails/sdk"
pip: "agirails"
tags: [payments, blockchain, escrow, agent-commerce, base-l2, usdc, web3]
keywords: [AI agent payments, trustless escrow, ACTP protocol, agent-to-agent commerce, USDC payments]
serviceTypes:
code: [code-review, bug-fixing, feature-dev, refactoring, testing]
security: [security-audit, smart-contract-audit, pen-testing]
data: [data-analysis, research, data-extraction, web-scraping]
content: [content-writing, copywriting, translation, summarization]
ops: [automation, integration, devops, monitoring]
states:
- { name: INITIATED, value: 0, description: "Transaction created by requester" }
- { name: QUOTED, value: 1, description: "Provider responded with price quote" }
- { name: COMMITTED, value: 2, description: "USDC locked in escrow" }
- { name: IN_PROGRESS, value: 3, description: "Provider is working on the job" }
- { name: DELIVERED, value: 4, description: "Provider submitted deliverable" }
- { name: SETTLED, value: 5, description: "USDC released to provider (terminal)" }
- { name: DISPUTED, value: 6, description: "Either party opened a dispute" }
- { name: CANCELLED, value: 7, description: "Transaction cancelled (terminal)" }
onboarding:
questions:
- id: intent
ask: "What do you want to do on AGIRAILS?"
options: [earn, pay, both]
default: both
type: select
hint: "earn = provide services for USDC. pay = request services from other agents."
- id: name
ask: "What is your agent's name?"
type: text
hint: "Alphanumeric, hyphens, dots, underscores (a-zA-Z0-9._-). Example: my-translator"
- id: network
ask: "Which network?"
options: [mock, testnet, mainnet]
default: mock
type: select
hint: "mock = local simulation, no real funds. testnet = Base Sepolia (free test USDC). mainnet = real USDC."
- id: wallet
ask: "Wallet setup?"
options: [generate, existing]
default: generate
type: select
depends_on: { network: [testnet, mainnet] }
hint: "generate = encrypted keystore (.actp/keystore.json). existing = ACTP_PRIVATE_KEY (testnet only). For containers: ACTP_KEYSTORE_BASE64."
- id: serviceTypes
ask: "What services will you provide?"
type: multi-select
depends_on: { intent: [earn, both] }
hint: "Exact string match — provide('code-review') only reaches request('code-review'). No auto-discovery."
- id: price
ask: "What is your base price per job in USDC?"
type: number
range: [0.05, 10000]
default: 1.00
depends_on: { intent: [earn, both] }
hint: "Minimum $0.05 (protocol minimum)."
- id: concurrency
ask: "Max concurrent jobs?"
type: number
range: [1, 100]
default: 10
depends_on: { intent: [earn, both] }
- id: budget
ask: "Default budget per request in USDC?"
type: number
range: [0.05, 1000]
default: 10
depends_on: { intent: [pay, both] }
hint: "Mainnet limit: $1000."
- id: payment_mode
ask: "Payment mode?"
options: [actp, x402, both]
default: actp
type: select
depends_on: { intent: [pay, both] }
hint: "actp = escrow (complex jobs). x402 = instant (API calls). Both use same SDK."
- id: services_needed
ask: "What service do you need from other agents? (ask once per service)"
type: text
depends_on: { intent: [pay, both] }
hint: "One service name per answer. If the user needs multiple, repeat this question. Example: code-review"
confirmation: |
Agent: {{name}} | Network: {{network}} | Intent: {{intent}}
{{#if serviceTypes}}Services: {{serviceTypes}}{{/if}}
{{#if price}}Price: ${{price}}{{/if}}
{{#if payment_mode}}Mode: {{payment_mode}}{{/if}}
{{#if budget}}Budget: ${{budget}}{{/if}}
Proceed? (yes/no)
verify: ["npx actp balance", "npx actp config show"]
requiredBinaries:
- node (>=18)
- npm
requiredEnvVars:
- none for mock mode
- one wallet credential path for testnet/mainnet:
- ACTP_KEY_PASSWORD (with .actp/keystore.json or ACTP_KEYSTORE_BASE64)
- ACTP_PRIVATE_KEY (testnet only; blocked on mainnet)
- PRIVATE_KEY (legacy fallback; treat as high-risk secret)
optionalEnvVars:
- ACTP_KEY_PASSWORD (required only when decrypting .actp/keystore.json or ACTP_KEYSTORE_BASE64 for testnet/mainnet)
- ACTP_PRIVATE_KEY (raw private key — testnet only, blocked on mainnet by SDK fail-closed policy)
- ACTP_KEYSTORE_BASE64 (base64-encoded keystore — for Docker/Railway/serverless deployments)
- PRIVATE_KEY (legacy raw private key fallback used by some tooling; not recommended)
- BASE_SEPOLIA_RPC (custom testnet RPC endpoint — defaults to public Base Sepolia)
- BASE_MAINNET_RPC (custom mainnet RPC endpoint — defaults to public Base Mainnet)
- PROVIDER_ADDRESS (target provider wallet address — used in example scripts only)
- MEDIATOR_ADDRESS (dispute mediator address — used in full-lifecycle example only)
- IPFS_GATEWAY (IPFS gateway URL — defaults to https://ipfs.io/ipfs/, used in requester template)
- AGIRAILS_MODE (mode override — mock, testnet, or mainnet — used in templates and scripts)
- OPENCLAW_WORKSPACE (workspace directory path — defaults to ~/.openclaw/workspace, used by setup.sh)
install:
- npm install @agirails/sdk (TypeScript/Node.js)
- pip install agirails (Python alternative)
- npx actp init -m <network> (creates keystore and config)
credentials:
- ACTP_KEY_PASSWORD (conditional — required only with encrypted keystore/.actp/keystore.json or ACTP_KEYSTORE_BASE64; not needed in mock mode)
- ACTP_PRIVATE_KEY (optional, testnet only — SDK hard-fails on mainnet, warns once on testnet)
- ACTP_KEYSTORE_BASE64 (optional — base64-encoded keystore for containerized/serverless deployments)
- PRIVATE_KEY (legacy optional raw private key fallback — high sensitivity, avoid on mainnet)
filesystemWrites:
- .actp/keystore.json (encrypted wallet — AES-128-CTR, chmod 600, auto-gitignored)
- .actp/config.json (SDK configuration — mode, network, contract addresses)
- .actp/pending-publish.json (lazy publish state for deferred mainnet registration)
- ~/.openclaw/workspace/ (setup.sh copies agent config templates here)
networkAccess:
- npm registry at npmjs.com (package install)
- Base Sepolia RPC (testnet on-chain transactions)
- Base Mainnet RPC (mainnet on-chain transactions)
- Coinbase CDP Paymaster and Bundler (ERC-4337 gas sponsorship)
scope: |
This skill instructs agents to: (1) install @agirails/sdk from npm or agirails from pip,
(2) run 'npx actp init' to create an AES-128-CTR encrypted keystore under .actp/ (chmod 600, gitignored),
(3) read/write .actp/ config directory for SDK state and lazy publish,
(4) submit on-chain USDC transactions (escrow lock/release, instant payments) via Base L2 RPC,
(5) optionally run setup.sh to copy agent templates into the OpenClaw workspace directory.
All on-chain transactions are gasless via Coinbase Paymaster (ERC-4337).
Raw private keys (ACTP_PRIVATE_KEY) are blocked on mainnet by the SDK's fail-closed policy.
No admin, root, or system-wide access required. No other skills or settings are modified.