Quickstart
The path below has no venue-specific branches: the server says what needs signing, activate() signs it. A compiled, CI-checked version ships in the SDK as examples/multi-venue.ts.
Install
npm install copytrading
No registry configuration, no tokens β it's on the public npm registry.
Integrating with an AI coding agent? The package ships INTEGRATE.md β a
single self-contained file with the complete integration instructions,
written to be pasted straight into Claude Code, Cursor, or any coding agent.
The Copy page button above does the same for this page.
The five calls
import { CopyTradingClient, createWalletAuthHeaders } from 'copytrading'
const client = new CopyTradingClient({
baseUrl: API_URL,
apiKey: PARTNER_KEY,
// Owner-scoped routes need the user's signature too. Build this ONCE and
// reuse it β it holds the monotonic timestamp counter that prevents replay
// rejections.
getAuthHeaders: createWalletAuthHeaders({ address: userAddress, signer: personalSigner }),
})
// 1. What can you offer? Entitlements are per-partner, so ask rather than assume.
const venues = await client.listVenues()
// 2. Who is worth copying? Graded for copyability, not ranked by PnL.
// Clean by default β only leaders that pass the gates. Pass
// { mirrorableOnly: false } only for a "check any wallet" surface, where
// the gated-out entries and their assessment.reasons are the answer.
const leaders = await client.listLeaders({ venue: venues[0].id })
// 3. Create the agent.
const { agentId } = await client.createAgent({
venue: venues[0].id,
smartAccountAddress: userAddress,
leaderWalletAddress: leaders[0].address,
riskConfig: { maxUsd: '500', copyPercent: 10 },
})
// 4. One call, every venue. Walks whatever steps that venue needs.
await client.activate(agentId, signer)
// 5. Same feed shape everywhere.
const { executions } = await client.listExecutions(agentId)
Skipped copies are part of the feed, not an error: entries with
status: 'skipped' carry a machine-readable skipReason
(slippage_exceeded, edge_exhausted, too_close_to_resolution, β¦).
Render them β a silent gap in the feed reads as a bug to your users.
What signer must implement
Only signTypedData is required everywhere. The rest are requested per venue, and a venue that needs one you did not provide fails immediately with a message naming the method β not with a signature error from inside an adapter.
- Name
signTypedData- Type
- required
- Description
Needed by all venues.
- Name
signMessage- Type
- per venue
- Description
Needed by Polymarket.
- Name
sendTransaction- Type
- per venue
- Description
Needed by spot and Polymarket (enabling the smart session on-chain; Polymarket adds the one-time account deploy on Polygon).
Perps in three calls
Hyperliquid needs no smart-account infrastructure β any EOA that can signTypedData, one signature loop, no gas. Hyperliquid is not an EVM chain, so its strategies are catalogued under chainId: 0.
import { CopyTradingClient, PerpsCopyClient } from 'copytrading'
const perps = new PerpsCopyClient({
api: client,
signer: { signTypedData: (payload) => wallet.signTypedData(payload) },
})
// 1. pick a leader
const strategies = await client.listStrategies(0)
// 2. create the agent, sign the Hyperliquid approvals, submit them
const { agentId } = await perps.activate({
smartAccountAddress: userAddress,
strategyId: 'perps-copytrade-hyperliquid',
leaderWalletAddress: '0xβ¦',
maxUsd: '1000', // per-trade cap
copyPercent: '100', // share of the leader's size, whole percent 0β100
maxLeverage: 3,
})
// 3. show the user what it did
const { executions } = await client.listExecutions(agentId)
Spot
Spot copying installs an ERC-7579 smart session: a scoped, expiring permission that lets the backend's session key call swap routers on the user's behalf and nothing else. It cannot transfer tokens out, and it stops working on its own at validUntil.
Activation is wallet-specific. The SDK handles API calls, session policy shapes and the enable/revoke sequence; you provide a SpotSessionAdapter (build/install/revoke sessions in your wallet stack), a TransactionSender, and your actionPolicies.
Start from examples/spot-session-adapter.ts in the SDK β a working
reference extracted from the production Unhosted wallet, typechecked in CI
against @rhinestone/module-sdk and viem. If you don't run an ERC-7579
wallet stack, start with perps instead.