@manhcuongsev/arcapis-sdk
v0.2.0
Published
TypeScript SDK for arcapis — packet-based prepaid AI API marketplace on Arc Testnet. Per-call EIP-712 auth and automatic cross-agent cache hit detection.
Downloads
159
Maintainers
Readme
@manhcuongsev/arcapis-sdk
TypeScript SDK for arcapis — packet-based prepaid AI API marketplace on Arc Testnet.
- EIP-712 per-call auth (no long-lived bearer tokens)
- Automatic cache HIT detection — cross-agent dedupe is transparent
- On-chain reads — packet info, endpoint catalog
Install
npm i @manhcuongsev/arcapis-sdk ethersQuick start
import { ArcApis } from '@manhcuongsev/arcapis-sdk';
import { Wallet } from 'ethers';
const arc = new ArcApis({
signer: new Wallet(process.env.AGENT_PRIVATE_KEY!),
packetTokenId: 5, // buy a packet first at arcapis.com
});
const r = await arc.call('embed', { input: 'hello' });
if (r._cached) {
console.log('free hit, age=', r._cached.age_seconds, 's, cid=', r._cached.cid);
} else {
console.log('paid, quota left=', r.calls_remaining);
}API surface
| Method | Cost | Returns |
|---|---|---|
| call(endpointId, body, opts?) | 1 quota or 0 (HIT) | CallResponse<T> |
| packetInfo(tokenId) | free RPC read | OnChainPacket \| null |
| packetsOf(owner) | free RPC read | number[] |
| callsRemaining(tokenId) | free RPC read | number |
| endpoints() | free HTTP | EndpointSpec[] |
EIP-712 vs bearer
The SDK uses per-call EIP-712 signatures — every request carries a fresh
signature binding packetId + endpointId + requestBodyHash + nonce + validUntil.
If a signature leaks, only that single call is at risk vs full quota with bearer.
Legacy bearer (Authorization: Bearer ak_...) is still supported server-side
for older clients but not exposed here.
Cache response shape
// Cache HIT — calls_remaining unchanged
{
endpoint: 'embed',
calls_remaining: 5,
calls_total: 5,
auth: { mode: 'eip712', signer: '0x...' },
_cached: {
hit: true,
age_seconds: 18,
ttl_seconds: 2592000,
cid: 'bafkreigh2...' // IPFS CID — fetch independently if needed
},
result: { /* upstream response, fetched from IPFS */ }
}
// Cache MISS — quota decremented, response pinned
{
endpoint: 'embed',
calls_remaining: 4,
calls_total: 5,
auth: { mode: 'eip712', signer: '0x...' },
result: { /* fresh upstream response */ }
}Easy to differentiate:
if ('_cached' in res) { /* free */ } else { /* paid */ }Dry-run mode
For unit tests + CI without real wallets:
const arc = new ArcApis({ dryRun: true, packetTokenId: 99 });
const r = await arc.call('embed', { input: 'mock' });
// r.result.dryRun === true, no network call, no quota spentNetwork
| | |
|---|---|
| Chain | Arc Testnet (eip155:5042002) |
| USDC | 0x3600000000000000000000000000000000000000 |
| PacketContract | 0xB7B66bC873A4e9D415c1C4C4a8876AEC3892f798 |
| CacheRegistry | 0xD06d70f356FF0ceC32582FB06177e163dBA76e02 |
| EndpointRegistry | 0x417694A53666E62d058cAa35eB85cFCacBC28965 |
The SDK reads the live address list from https://arcapis.com/contracts.json
so you don't need to hardcode any of them.
Build from source
npm install
npm run buildRun the reference research-bot example:
export ARCAPIS_PACKET_ID=5
export AGENT_PRIVATE_KEY=0x...
npm run example:researchLicense
MIT.
