Poseidon OTC
by @romovow
Execute trustless P2P token swaps on Solana via the Poseidon OTC protocol. Create trade rooms, negotiate offers, lock tokens with time-based escrow, and execute atomic on-chain swaps. Supports agent-to-agent trading with real-time WebSocket updates.
clawhub install poseidon-otcπ About This Skill
name: poseidon-otc description: Execute trustless P2P token swaps on Solana via the Poseidon OTC protocol. Create trade rooms, negotiate offers, lock tokens with time-based escrow, and execute atomic on-chain swaps. Supports agent-to-agent trading with real-time WebSocket updates. metadata: { "openclaw": { "emoji": "π±", "requires": { "env": ["POSEIDON_BURNER_KEY"] }, "primaryEnv": "POSEIDON_BURNER_KEY", "homepage": "https://poseidon.cash" } }
Poseidon OTC Skill
TL;DR for Agents: This skill lets you trade tokens with humans or other agents on Solana. You create a room, both parties deposit tokens to escrow, confirm, and execute an atomic swap. No trust required - it's all on-chain.
When to Use This Skill
Quick Start for Agents
1. Initialize (requires wallet)
import { PoseidonOTC } from 'poseidon-otc-skill';const client = new PoseidonOTC({
burnerKey: process.env.POSEIDON_BURNER_KEY // base58 private key
});
2. Create a Trade Room
const { roomId, link } = await client.createRoom();
// Share link with counterparty or another agent
3. Wait for Counterparty & Set Offer
// Check room status
const room = await client.getRoom(roomId);// Set what you're offering (100 USDC example)
await client.updateOffer(roomId, [{
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC mint
amount: 100000000, // 100 USDC (6 decimals)
decimals: 6
}]);
4. Confirm & Execute
// First confirmation = "I agree to these terms"
await client.confirmTrade(roomId, 'first');// After deposits, second confirmation
await client.confirmTrade(roomId, 'second');
// Execute the atomic swap
const { txSignature } = await client.executeSwap(roomId);
Complete Trade Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRADE LIFECYCLE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. CREATE ROOM β
β ββ> Party A calls createRoom() β
β Returns: roomId, shareable link β
β β
β 2. JOIN ROOM β
β ββ> Party B calls joinRoom(roomId) β
β Room now has both participants β
β β
β 3. SET OFFERS β
β ββ> Both parties call updateOffer(roomId, tokens) β
β Each specifies what they're putting up β
β β
β 4. FIRST CONFIRM (agree on terms) β
β ββ> Both call confirmTrade(roomId, 'first') β
β "I agree to swap my X for your Y" β
β β
β 5. DEPOSIT TO ESCROW β
β ββ> Tokens move to on-chain escrow β
β (Handled by frontend or depositToEscrow) β
β β
β 6. SECOND CONFIRM (verify deposits) β
β ββ> Both call confirmTrade(roomId, 'second') β
β "I see the deposits, ready to swap" β
β β
β 7. EXECUTE SWAP β
β ββ> Either party calls executeSwap(roomId) β
β Atomic on-chain swap via relayer β
β Returns: txSignature β
β β
β [OPTIONAL] LOCKUP FLOW β
β ββ> Before step 4, Party A can proposeLockup(roomId, secs) β
β ββ> Party B must acceptLockup(roomId) to continue β
β ββ> After execute, locked tokens claimed via claimLockedTokens β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
API Reference
Room Management
| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| |createRoom(options?) | { inviteCode?: string } | { roomId, link } | Create new room |
| getRoom(roomId) | roomId: string | TradeRoom | Get full room state |
| getUserRooms(wallet?) | wallet?: string | TradeRoom[] | List your rooms |
| joinRoom(roomId, inviteCode?) | roomId, inviteCode? | { success } | Join as Party B |
| cancelRoom(roomId) | roomId: string | { success } | Cancel & refund |
| getRoomLink(roomId) | roomId: string | string | Get share URL |Trading
| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| |updateOffer(roomId, tokens) | roomId, [{mint, amount, decimals}] | { success } | Set your offer |
| withdrawFromOffer(roomId, tokens) | roomId, tokens[] | { success } | Pull back tokens |
| confirmTrade(roomId, stage) | roomId, 'first'β'second' | { success } | Confirm stage |
| executeSwap(roomId) | roomId: string | { txSignature } | Execute swap |
| declineOffer(roomId) | roomId: string | { success } | Reject terms |Lockups (Anti-Dump)
| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| |proposeLockup(roomId, seconds) | roomId, seconds | { success } | Propose lock |
| acceptLockup(roomId) | roomId: string | { success } | Accept lock |
| getLockupStatus(roomId) | roomId: string | { canClaim, timeRemaining } | Check timer |
| claimLockedTokens(roomId) | roomId: string | { txSignature } | Claim after expiry |Utility
| Method | Parameters | Returns | Description | |--------|------------|---------|-------------| |getBalance() | none | { sol: number } | Check SOL balance |
| isAutonomous() | none | boolean | Has signing wallet? |
| getWebSocketUrl() | none | string | Get WS endpoint |WebSocket Real-Time Updates
Don't poll. Subscribe.
Instead of repeatedly calling getRoom(), connect to WebSocket for instant updates:
Endpoint: wss://poseidon.cash/ws/trade-room
Subscribe to Room Events
const { unsubscribe } = await client.subscribeToRoom(roomId, (event) => {
switch (event.type) {
case 'join':
console.log('Counterparty joined!');
break;
case 'offer':
console.log('Offer updated:', event.data.tokens);
break;
case 'confirm':
console.log('Confirmation received');
break;
case 'execute':
console.log('Swap complete! TX:', event.data.txSignature);
break;
case 'cancel':
console.log('Trade cancelled');
break;
}
});
Event Types
| Event | When It Fires | |-------|--------------| |full-state | Immediately on subscribe - complete room state |
| join | Counterparty joined the room |
| offer | Someone updated their offer |
| confirm | Someone confirmed (first or second) |
| lockup | Lockup proposed or accepted |
| execute | Swap executed successfully |
| cancel | Room was cancelled |
| terminated | Room expired or terminated |
| error | Something went wrong |WebSocket Actions (Faster than HTTP)
await client.sendOfferViaWs(roomId, tokens); // Update offer
await client.sendConfirmViaWs(roomId, 'first'); // Confirm
await client.sendLockupProposalViaWs(roomId, 3600); // Propose 1hr lock
await client.sendAcceptLockupViaWs(roomId); // Accept lock
await client.sendExecuteViaWs(roomId); // Execute swap
Agent-to-Agent Trading Example
Scenario: Agent A wants to sell 1000 USDC for 5 SOL to Agent B
Agent A (Seller):
// 1. Create room
const { roomId } = await client.createRoom();// 2. Set offer (1000 USDC)
await client.updateOffer(roomId, [{
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
amount: 1000000000, // 1000 USDC
decimals: 6
}]);
// 3. Share roomId with Agent B via your inter-agent protocol
// 4. Subscribe to updates
await client.subscribeToRoom(roomId, async (event) => {
if (event.type === 'offer') {
// Check if Agent B's offer is acceptable (5 SOL)
const room = await client.getRoom(roomId);
if (room.partyBTokenSlots?.[0]?.amount >= 5 * 1e9) {
await client.confirmTrade(roomId, 'first');
}
}
if (event.type === 'confirm' && room.partyBFirstConfirm) {
await client.confirmTrade(roomId, 'second');
}
});
Agent B (Buyer):
// 1. Join the room
await client.joinRoom(roomId);// 2. Set offer (5 SOL)
await client.updateOffer(roomId, [{
mint: 'So11111111111111111111111111111111111111112', // wSOL
amount: 5000000000, // 5 SOL
decimals: 9
}]);
// 3. Subscribe and react
await client.subscribeToRoom(roomId, async (event) => {
if (event.type === 'confirm') {
const room = await client.getRoom(roomId);
if (room.partyAFirstConfirm && !room.partyBFirstConfirm) {
await client.confirmTrade(roomId, 'first');
}
if (room.partyASecondConfirm && room.partyBSecondConfirm) {
// Both confirmed, execute!
await client.executeSwap(roomId);
}
}
});
Common Token Mints
| Token | Mint Address | Decimals |
|-------|--------------|----------|
| USDC | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | 6 |
| USDT | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB | 6 |
| wSOL | So11111111111111111111111111111111111111112 | 9 |
| BONK | DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 | 5 |
Environment Variables
POSEIDON_BURNER_KEY= # Required for autonomous mode
POSEIDON_API_URL=https://poseidon.cash # API endpoint (default: mainnet)
POSEIDON_RPC_URL=https://api.mainnet-beta.solana.com # Solana RPC
Security Notes
Program ID
Mainnet: AfiRReYhvykHhKXhwjhcsXFejHdxqYLk2QLWnjvvLKUN