@goplausible/openclaw-algorand-plugin 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/openclaw.plugin.json +4 -2
- package/package.json +1 -1
- package/skills/algorand-interaction/SKILL.md +4 -2
- package/skills/algorand-interaction/references/algorand-mcp.md +49 -2
- package/skills/algorand-interaction/references/examples-algorand-mcp.md +49 -0
- package/skills/haystack-router-development/SKILL.md +85 -0
- package/skills/haystack-router-development/references/api-reference.md +381 -0
- package/skills/haystack-router-development/references/configuration.md +184 -0
- package/skills/haystack-router-development/references/fees-and-referrals.md +91 -0
- package/skills/haystack-router-development/references/getting-started.md +93 -0
- package/skills/haystack-router-development/references/migration.md +53 -0
- package/skills/haystack-router-development/references/node-automation.md +113 -0
- package/skills/haystack-router-development/references/quotes.md +155 -0
- package/skills/haystack-router-development/references/react-integration.md +260 -0
- package/skills/haystack-router-development/references/swaps.md +161 -0
- package/skills/haystack-router-interaction/SKILL.md +146 -0
- package/skills/haystack-router-interaction/references/configuration.md +53 -0
- package/skills/haystack-router-interaction/references/getting-started.md +48 -0
- package/skills/haystack-router-interaction/references/node-automation.md +51 -0
- package/skills/haystack-router-interaction/references/quotes.md +80 -0
- package/skills/haystack-router-interaction/references/swaps.md +84 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# React Integration
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Install dependencies:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @txnlab/haystack-router @txnlab/use-wallet-react algosdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Wrap your app with `WalletProvider`:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { WalletProvider, WalletId } from '@txnlab/use-wallet-react'
|
|
15
|
+
|
|
16
|
+
const walletProviders = [
|
|
17
|
+
{ id: WalletId.PERA },
|
|
18
|
+
{ id: WalletId.DEFLY },
|
|
19
|
+
{ id: WalletId.LUTE },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
return (
|
|
24
|
+
<WalletProvider wallets={walletProviders}>
|
|
25
|
+
<SwapInterface />
|
|
26
|
+
</WalletProvider>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Basic Swap Component
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useState } from 'react'
|
|
35
|
+
import { useWallet } from '@txnlab/use-wallet-react'
|
|
36
|
+
import { RouterClient, type SwapQuote } from '@txnlab/haystack-router'
|
|
37
|
+
|
|
38
|
+
function SwapInterface() {
|
|
39
|
+
const { activeAddress, transactionSigner } = useWallet()
|
|
40
|
+
const [amount, setAmount] = useState('')
|
|
41
|
+
const [fromAsset, setFromAsset] = useState(0) // ALGO
|
|
42
|
+
const [toAsset, setToAsset] = useState(31566704) // USDC
|
|
43
|
+
const [slippage, setSlippage] = useState('1')
|
|
44
|
+
const [quote, setQuote] = useState<SwapQuote | null>(null)
|
|
45
|
+
|
|
46
|
+
const getQuote = async () => {
|
|
47
|
+
const router = new RouterClient({
|
|
48
|
+
apiKey: import.meta.env.VITE_HAYSTACK_API_KEY,
|
|
49
|
+
autoOptIn: true,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const amountInBaseUnits = BigInt(Math.floor(parseFloat(amount) * 1_000_000))
|
|
53
|
+
|
|
54
|
+
const result = await router.newQuote({
|
|
55
|
+
fromASAID: fromAsset,
|
|
56
|
+
toASAID: toAsset,
|
|
57
|
+
amount: amountInBaseUnits,
|
|
58
|
+
address: activeAddress!,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
setQuote(result)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const executeSwap = async () => {
|
|
65
|
+
if (!quote || !activeAddress) return
|
|
66
|
+
|
|
67
|
+
const router = new RouterClient({
|
|
68
|
+
apiKey: import.meta.env.VITE_HAYSTACK_API_KEY,
|
|
69
|
+
autoOptIn: true,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const swap = await router.newSwap({
|
|
73
|
+
quote,
|
|
74
|
+
address: activeAddress,
|
|
75
|
+
signer: transactionSigner,
|
|
76
|
+
slippage: parseFloat(slippage),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const result = await swap.execute()
|
|
80
|
+
console.log(`Confirmed in round ${result.confirmedRound}`)
|
|
81
|
+
|
|
82
|
+
setQuote(null) // Clear stale quote
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div>
|
|
87
|
+
<input
|
|
88
|
+
type="number"
|
|
89
|
+
value={amount}
|
|
90
|
+
onChange={(e) => setAmount(e.target.value)}
|
|
91
|
+
placeholder="Amount"
|
|
92
|
+
/>
|
|
93
|
+
<button onClick={getQuote} disabled={!activeAddress || !amount}>
|
|
94
|
+
Get Quote
|
|
95
|
+
</button>
|
|
96
|
+
|
|
97
|
+
{quote && (
|
|
98
|
+
<div>
|
|
99
|
+
<p>Output: {(Number(quote.quote) / 1e6).toFixed(4)} USDC</p>
|
|
100
|
+
<p>USD value: ${quote.usdOut.toFixed(2)}</p>
|
|
101
|
+
<p>
|
|
102
|
+
Route:{' '}
|
|
103
|
+
{Object.entries(quote.flattenedRoute)
|
|
104
|
+
.map(([protocol, pct]) => `${protocol}: ${pct}%`)
|
|
105
|
+
.join(', ')}
|
|
106
|
+
</p>
|
|
107
|
+
<button onClick={executeSwap}>Execute Swap</button>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## TanStack Query Integration
|
|
116
|
+
|
|
117
|
+
For production UIs, use TanStack Query for auto-refreshing quotes and better state management.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm install @tanstack/react-query
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
125
|
+
import {
|
|
126
|
+
useQuery,
|
|
127
|
+
useMutation,
|
|
128
|
+
QueryClient,
|
|
129
|
+
QueryClientProvider,
|
|
130
|
+
} from '@tanstack/react-query'
|
|
131
|
+
import { useWallet } from '@txnlab/use-wallet-react'
|
|
132
|
+
import { RouterClient, type SwapQuote } from '@txnlab/haystack-router'
|
|
133
|
+
|
|
134
|
+
const queryClient = new QueryClient()
|
|
135
|
+
|
|
136
|
+
function SwapWithAutoRefresh() {
|
|
137
|
+
const { activeAddress, transactionSigner } = useWallet()
|
|
138
|
+
const [amount, setAmount] = useState('')
|
|
139
|
+
const [debouncedAmount, setDebouncedAmount] = useState('')
|
|
140
|
+
const [fromAsset] = useState(0)
|
|
141
|
+
const [toAsset] = useState(31566704)
|
|
142
|
+
const [slippage] = useState(1)
|
|
143
|
+
|
|
144
|
+
// Debounce amount input (500ms)
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
const timer = setTimeout(() => setDebouncedAmount(amount), 500)
|
|
147
|
+
return () => clearTimeout(timer)
|
|
148
|
+
}, [amount])
|
|
149
|
+
|
|
150
|
+
const router = useMemo(
|
|
151
|
+
() =>
|
|
152
|
+
new RouterClient({
|
|
153
|
+
apiKey: import.meta.env.VITE_HAYSTACK_API_KEY,
|
|
154
|
+
autoOptIn: true,
|
|
155
|
+
}),
|
|
156
|
+
[],
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const isValidRequest =
|
|
160
|
+
activeAddress && debouncedAmount && parseFloat(debouncedAmount) > 0
|
|
161
|
+
|
|
162
|
+
// Auto-fetch and refresh quotes
|
|
163
|
+
const {
|
|
164
|
+
data: quote,
|
|
165
|
+
error,
|
|
166
|
+
isLoading,
|
|
167
|
+
} = useQuery({
|
|
168
|
+
queryKey: ['quote', fromAsset, toAsset, debouncedAmount, activeAddress],
|
|
169
|
+
queryFn: () =>
|
|
170
|
+
router.newQuote({
|
|
171
|
+
fromASAID: fromAsset,
|
|
172
|
+
toASAID: toAsset,
|
|
173
|
+
amount: BigInt(Math.floor(parseFloat(debouncedAmount) * 1_000_000)),
|
|
174
|
+
address: activeAddress!,
|
|
175
|
+
}),
|
|
176
|
+
enabled: !!isValidRequest,
|
|
177
|
+
refetchInterval: 15_000, // Refresh every 15 seconds
|
|
178
|
+
retry: 1,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Swap mutation
|
|
182
|
+
const swapMutation = useMutation({
|
|
183
|
+
mutationFn: async () => {
|
|
184
|
+
if (!quote || !activeAddress) throw new Error('Missing quote or address')
|
|
185
|
+
|
|
186
|
+
const swap = await router.newSwap({
|
|
187
|
+
quote,
|
|
188
|
+
address: activeAddress,
|
|
189
|
+
signer: transactionSigner,
|
|
190
|
+
slippage,
|
|
191
|
+
})
|
|
192
|
+
return swap.execute()
|
|
193
|
+
},
|
|
194
|
+
onSuccess: (result) => {
|
|
195
|
+
console.log(`Confirmed in round ${result.confirmedRound}`)
|
|
196
|
+
queryClient.invalidateQueries({ queryKey: ['quote'] })
|
|
197
|
+
},
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<div>
|
|
202
|
+
<input
|
|
203
|
+
type="number"
|
|
204
|
+
value={amount}
|
|
205
|
+
onChange={(e) => setAmount(e.target.value)}
|
|
206
|
+
placeholder="Amount"
|
|
207
|
+
/>
|
|
208
|
+
|
|
209
|
+
{isLoading && <p>Fetching quote...</p>}
|
|
210
|
+
{error && <p>Error: {(error as Error).message}</p>}
|
|
211
|
+
|
|
212
|
+
{quote && (
|
|
213
|
+
<div>
|
|
214
|
+
<p>Output: {(Number(quote.quote) / 1e6).toFixed(4)} USDC</p>
|
|
215
|
+
<p>USD: ${quote.usdOut.toFixed(2)}</p>
|
|
216
|
+
<button
|
|
217
|
+
onClick={() => swapMutation.mutate()}
|
|
218
|
+
disabled={swapMutation.isPending}
|
|
219
|
+
>
|
|
220
|
+
{swapMutation.isPending ? 'Swapping...' : 'Swap'}
|
|
221
|
+
</button>
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Key patterns:
|
|
230
|
+
|
|
231
|
+
- **Debounced input**: Prevent quote requests on every keystroke
|
|
232
|
+
- **Auto-refresh**: `refetchInterval: 15_000` keeps quotes current
|
|
233
|
+
- **Invalidation**: Clear stale quotes after a successful swap
|
|
234
|
+
- **Loading/error states**: TanStack Query manages all async state
|
|
235
|
+
|
|
236
|
+
## Displaying Route Details
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
function RouteDisplay({ quote }: { quote: SwapQuote }) {
|
|
240
|
+
return (
|
|
241
|
+
<div>
|
|
242
|
+
<h4>Route</h4>
|
|
243
|
+
{quote.route.map((route, i) => (
|
|
244
|
+
<div key={i}>
|
|
245
|
+
<strong>{route.percentage}%</strong>
|
|
246
|
+
{route.path.map((hop, j) => (
|
|
247
|
+
<span key={j}>
|
|
248
|
+
{j > 0 && ' → '}
|
|
249
|
+
{hop.in.unit_name} → {hop.out.unit_name} ({hop.name})
|
|
250
|
+
</span>
|
|
251
|
+
))}
|
|
252
|
+
</div>
|
|
253
|
+
))}
|
|
254
|
+
{quote.userPriceImpact !== undefined && (
|
|
255
|
+
<p>Price impact: {quote.userPriceImpact.toFixed(2)}%</p>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
```
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Swaps
|
|
2
|
+
|
|
3
|
+
## Executing a Swap
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { RouterClient } from '@txnlab/haystack-router'
|
|
7
|
+
|
|
8
|
+
const router = new RouterClient({
|
|
9
|
+
apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
// 1. Get a quote
|
|
13
|
+
const quote = await router.newQuote({
|
|
14
|
+
fromASAID: 0,
|
|
15
|
+
toASAID: 31566704,
|
|
16
|
+
amount: 1_000_000,
|
|
17
|
+
address: activeAddress,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// 2. Create and execute swap
|
|
21
|
+
const swap = await router.newSwap({
|
|
22
|
+
quote,
|
|
23
|
+
address: activeAddress,
|
|
24
|
+
signer: transactionSigner,
|
|
25
|
+
slippage: 1, // 1%
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const result = await swap.execute()
|
|
29
|
+
console.log(`Confirmed in round ${result.confirmedRound}`)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## newSwap() Parameters
|
|
33
|
+
|
|
34
|
+
| Parameter | Type | Required | Description |
|
|
35
|
+
| ---------- | ------------------------------------- | -------- | ----------------------------------------- |
|
|
36
|
+
| `quote` | `SwapQuote \| FetchQuoteResponse` | Yes | Quote from `newQuote()` or `fetchQuote()` |
|
|
37
|
+
| `address` | `string` | Yes | Signer's Algorand address |
|
|
38
|
+
| `signer` | `TransactionSigner \| SignerFunction` | Yes | Transaction signing function |
|
|
39
|
+
| `slippage` | `number` | Yes | Slippage tolerance (e.g., `1` = 1%) |
|
|
40
|
+
| `note` | `Uint8Array` | No | Note attached to input transaction |
|
|
41
|
+
|
|
42
|
+
## Signer Patterns
|
|
43
|
+
|
|
44
|
+
### Browser: use-wallet
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { useWallet } from '@txnlab/use-wallet-react'
|
|
48
|
+
|
|
49
|
+
const { activeAddress, transactionSigner } = useWallet()
|
|
50
|
+
|
|
51
|
+
const swap = await router.newSwap({
|
|
52
|
+
quote,
|
|
53
|
+
address: activeAddress,
|
|
54
|
+
signer: transactionSigner,
|
|
55
|
+
slippage: 1,
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The SDK supports two signer return patterns:
|
|
60
|
+
|
|
61
|
+
- **Pera/Defly style**: Returns `Uint8Array[]` matching `indexesToSign` length
|
|
62
|
+
- **Lute/ARC-1 style**: Returns `(Uint8Array | null)[]` matching full group length, `null` for unsigned
|
|
63
|
+
|
|
64
|
+
Both are detected and handled automatically.
|
|
65
|
+
|
|
66
|
+
## Slippage Guidance
|
|
67
|
+
|
|
68
|
+
| Pair Type | Recommended Slippage |
|
|
69
|
+
| ----------------- | -------------------- |
|
|
70
|
+
| Stable pairs | 0.5–1% |
|
|
71
|
+
| Volatile pairs | 1–3% |
|
|
72
|
+
| Low liquidity | 3–5% |
|
|
73
|
+
|
|
74
|
+
Slippage is verified on the **final output** of the swap, not individual hops. This means intermediate steps can have higher variance as long as the final result is within tolerance.
|
|
75
|
+
|
|
76
|
+
## Execution Result
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const result = await swap.execute()
|
|
80
|
+
|
|
81
|
+
result.confirmedRound // bigint — block where swap confirmed
|
|
82
|
+
result.txIds // string[] — all transaction IDs
|
|
83
|
+
result.methodResults // ABIResult[] — ABI method call results
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Swap Summary
|
|
87
|
+
|
|
88
|
+
After execution, get exact amounts (may differ from quote due to slippage):
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const result = await swap.execute()
|
|
92
|
+
const summary = swap.getSummary()
|
|
93
|
+
|
|
94
|
+
if (summary) {
|
|
95
|
+
summary.inputAssetId // bigint
|
|
96
|
+
summary.outputAssetId // bigint
|
|
97
|
+
summary.inputAmount // bigint — exact input sent
|
|
98
|
+
summary.outputAmount // bigint — exact output received
|
|
99
|
+
summary.totalFees // bigint — total ALGO fees (microAlgos)
|
|
100
|
+
summary.transactionCount // number
|
|
101
|
+
summary.inputTxnId // string — user-signed input txn ID
|
|
102
|
+
summary.outputTxnId // string — app call containing output
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Advanced: Custom Transaction Composition
|
|
107
|
+
|
|
108
|
+
Add custom transactions before/after the swap in the same atomic group:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const swap = await router.newSwap({
|
|
112
|
+
quote,
|
|
113
|
+
address: activeAddress,
|
|
114
|
+
signer: transactionSigner,
|
|
115
|
+
slippage: 1,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const result = await swap
|
|
119
|
+
.addTransaction(customTxn) // Add before swap
|
|
120
|
+
.addSwapTransactions() // Add swap txns + middleware
|
|
121
|
+
.addMethodCall(abiCall) // Add ABI call after swap
|
|
122
|
+
.execute()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Constraints:
|
|
126
|
+
|
|
127
|
+
- Max 16 transactions per group
|
|
128
|
+
- Non-composable swaps (Tinyman v1) cannot add custom transactions
|
|
129
|
+
- Call `addSwapTransactions()` to manually control ordering
|
|
130
|
+
|
|
131
|
+
## Advanced: Step-by-Step Execution
|
|
132
|
+
|
|
133
|
+
For more control over the signing and submission flow:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const swap = await router.newSwap({ quote, address, signer, slippage: 1 })
|
|
137
|
+
|
|
138
|
+
// Build (assigns group IDs)
|
|
139
|
+
const txnsWithSigners = swap.buildGroup()
|
|
140
|
+
|
|
141
|
+
// Sign
|
|
142
|
+
const signedTxns = await swap.sign()
|
|
143
|
+
|
|
144
|
+
// Submit (doesn't wait)
|
|
145
|
+
const txIds = await swap.submit()
|
|
146
|
+
|
|
147
|
+
// Check status
|
|
148
|
+
swap.getStatus() // BUILDING → BUILT → SIGNED → SUBMITTED → COMMITTED
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Error Handling
|
|
152
|
+
|
|
153
|
+
Common errors:
|
|
154
|
+
|
|
155
|
+
- **Slippage exceeded** — price moved beyond tolerance, refetch quote
|
|
156
|
+
- **Insufficient balance** — check account balance
|
|
157
|
+
- **Asset not opted in** — include opt-in in quote or opt in manually
|
|
158
|
+
- **Transaction rejected** — user declined or signing failed
|
|
159
|
+
- **Network timeout** — retry after brief delay
|
|
160
|
+
|
|
161
|
+
Quotes are time-sensitive. If a quote is stale (prices moved significantly), refetch before executing.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: haystack-router-interaction
|
|
3
|
+
description: Route and execute optimal token swaps on Algorand using Haystack Router via Algorand MCP tools. Use when getting best-price quotes across multiple Algorand DEXes and LST protocols, executing atomic swaps, and checking asset opt-in — all through the Algorand MCP server. Strong triggers include haystack swap, best price swap, DEX aggregator swap, swap ALGO to USDC, api_haystack, haystack quote, execute swap, swap routing, optimal swap, multi-DEX swap.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Haystack Router (Interaction)
|
|
7
|
+
|
|
8
|
+
This is the parent skill for interacting with Haystack Router through Algorand MCP tools — getting quotes, executing swaps, and checking opt-in status. All operations use the Algorand MCP wallet for signing; no SDK installation or API keys needed.
|
|
9
|
+
|
|
10
|
+
> **Building an application** that integrates Haystack Router directly? Use the **haystack-router-development** skill instead — it covers the `@txnlab/haystack-router` SDK, React integration, Node.js automation, middleware, and the full API surface.
|
|
11
|
+
> **Need general wallet/transaction guidance?** See the **algorand-interaction** skill for wallet setup, session start checklist, network selection, and pre-transaction validation.
|
|
12
|
+
|
|
13
|
+
Haystack Router is a DEX aggregator and smart order routing protocol on Algorand. It finds optimal swap routes across multiple DEXes (Tinyman V2, Pact, Folks) and LST protocols (tALGO, xALGO), then executes them atomically through on-chain smart contracts.
|
|
14
|
+
|
|
15
|
+
## mcporter Syntax (OpenClaw CLI)
|
|
16
|
+
|
|
17
|
+
When calling Haystack Router MCP tools via mcporter in OpenClaw CLI:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
mcporter call algorand-mcp.api_haystack_get_swap_quote --args '{"fromASAID": 0, "toASAID": 31566704, "amount": 1000000, "network": "mainnet"}'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Key: use `server.tool` (dot notation) with `--args '{"json"}'`. See `algorand-interaction` skill for full mcporter reference.
|
|
24
|
+
|
|
25
|
+
## Network Selection
|
|
26
|
+
|
|
27
|
+
Every Haystack Router tool accepts a `network` parameter:
|
|
28
|
+
|
|
29
|
+
| Value | Description |
|
|
30
|
+
|-------|-------------|
|
|
31
|
+
| `mainnet` | Algorand mainnet (default) — **real value, exercise caution** |
|
|
32
|
+
| `testnet` | Algorand testnet — safe for development |
|
|
33
|
+
|
|
34
|
+
Default to `testnet` during development. Always confirm with the user before mainnet swaps.
|
|
35
|
+
|
|
36
|
+
## Algorand MCP Haystack Router Tools
|
|
37
|
+
|
|
38
|
+
Three dedicated Algorand MCP tools provide full Haystack Router functionality. These handle quoting, execution (with wallet signing), and opt-in checking — no raw mnemonics or secret keys needed.
|
|
39
|
+
|
|
40
|
+
### `api_haystack_get_swap_quote`
|
|
41
|
+
|
|
42
|
+
Get an optimized swap quote without executing. Use to preview pricing, route, and price impact before confirming with user.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
→ api_haystack_get_swap_quote {
|
|
46
|
+
fromASAID: 0, // Input asset (0 = ALGO)
|
|
47
|
+
toASAID: 31566704, // Output asset (USDC)
|
|
48
|
+
amount: 1000000, // 1 ALGO in base units
|
|
49
|
+
type: "fixed-input", // or "fixed-output"
|
|
50
|
+
address: "<address>", // optional, for opt-in detection
|
|
51
|
+
maxGroupSize: 16, // optional
|
|
52
|
+
maxDepth: 4, // optional
|
|
53
|
+
network: "mainnet"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Returns: expectedOutput, inputAmount, usdIn, usdOut, userPriceImpact,
|
|
57
|
+
route, flattenedRoute, requiredAppOptIns, protocolFees
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `api_haystack_execute_swap`
|
|
61
|
+
|
|
62
|
+
All-in-one swap: quote → sign (via wallet) → submit → confirm. Uses the active wallet account for signing. Enforces wallet spending limits.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
→ api_haystack_execute_swap {
|
|
66
|
+
fromASAID: 0, // Input asset
|
|
67
|
+
toASAID: 31566704, // Output asset
|
|
68
|
+
amount: 1000000, // Amount in base units
|
|
69
|
+
slippage: 1, // 1% slippage tolerance
|
|
70
|
+
type: "fixed-input", // optional
|
|
71
|
+
note: "my swap", // optional text note
|
|
72
|
+
maxGroupSize: 16, // optional
|
|
73
|
+
maxDepth: 4, // optional
|
|
74
|
+
network: "mainnet"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Returns: status, confirmedRound, txIds, signer, nickname, quote details,
|
|
78
|
+
summary (inputAmount, outputAmount, totalFees, transactionCount)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `api_haystack_needs_optin`
|
|
82
|
+
|
|
83
|
+
Check if an address needs to opt into an asset before swapping.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
→ api_haystack_needs_optin {
|
|
87
|
+
address: "<address>",
|
|
88
|
+
assetId: 31566704,
|
|
89
|
+
network: "mainnet"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Returns: { address, assetId, needsOptIn: true/false, network }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Algorand MCP Agent Swap Workflow
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Step 1: Check wallet
|
|
99
|
+
→ wallet_get_info { network }
|
|
100
|
+
|
|
101
|
+
Step 2: Check opt-in (if swapping to an ASA)
|
|
102
|
+
→ api_haystack_needs_optin { address, assetId, network }
|
|
103
|
+
→ If needed: wallet_optin_asset { assetId, network }
|
|
104
|
+
|
|
105
|
+
Step 3: Preview quote (recommended — show user before executing)
|
|
106
|
+
→ api_haystack_get_swap_quote { fromASAID, toASAID, amount, address, network }
|
|
107
|
+
→ Present to user: expected output, USD values, route, price impact
|
|
108
|
+
|
|
109
|
+
Step 4: User confirms → Execute
|
|
110
|
+
→ api_haystack_execute_swap { fromASAID, toASAID, amount, slippage, network }
|
|
111
|
+
→ Returns confirmed result with summary
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Key rules:**
|
|
115
|
+
- Always check wallet with `wallet_get_info` before any swap
|
|
116
|
+
- Always confirm with the user before executing (show quote details)
|
|
117
|
+
- The execute tool handles signing via the active wallet — no manual signing needed
|
|
118
|
+
- Default to testnet during development; confirm before mainnet
|
|
119
|
+
- Quotes are time-sensitive — execute promptly after user confirms
|
|
120
|
+
|
|
121
|
+
## Key Concepts
|
|
122
|
+
|
|
123
|
+
- **Amounts** are always in base units (microAlgos for ALGO, smallest unit for ASAs)
|
|
124
|
+
- **ASA IDs**: 0 = ALGO, 31566704 = USDC, etc.
|
|
125
|
+
- **Quote types**: `fixed-input` (default) — specify input amount; `fixed-output` — specify desired output
|
|
126
|
+
- **Slippage**: Percentage tolerance on output (e.g., 1 = 1%). Applied to the final output, not individual hops
|
|
127
|
+
- **Routing**: Supports multi-hop and parallel (combo) swaps for optimal pricing
|
|
128
|
+
|
|
129
|
+
## Reference Files
|
|
130
|
+
|
|
131
|
+
Read the appropriate file based on the task:
|
|
132
|
+
|
|
133
|
+
| Task | Reference |
|
|
134
|
+
| ------------------------------------------- | ----------------------------------------------------- |
|
|
135
|
+
| Quick start and Algorand MCP tool reference | [getting-started.md](references/getting-started.md) |
|
|
136
|
+
| Get swap quotes, display pricing | [quotes.md](references/quotes.md) |
|
|
137
|
+
| Execute swaps via Algorand MCP tools | [swaps.md](references/swaps.md) |
|
|
138
|
+
| Automate swaps via Algorand MCP tools | [node-automation.md](references/node-automation.md) |
|
|
139
|
+
| Network, slippage, rate limits, ASA IDs | [configuration.md](references/configuration.md) |
|
|
140
|
+
|
|
141
|
+
## How to Use This Skill
|
|
142
|
+
|
|
143
|
+
1. **Start here** to understand the three Haystack Router MCP tools and the swap workflow
|
|
144
|
+
2. **Read the topic `.md`** file for detailed guidance on quotes, swaps, or automation
|
|
145
|
+
3. **For SDK-based development** (React UIs, Node.js apps), see the `haystack-router-development` skill
|
|
146
|
+
4. **For wallet setup and general blockchain interaction**, see the `algorand-interaction` skill
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Configuration (Algorand MCP)
|
|
2
|
+
|
|
3
|
+
## Network
|
|
4
|
+
|
|
5
|
+
All Algorand MCP Haystack Router tools accept a `network` parameter:
|
|
6
|
+
|
|
7
|
+
- `"mainnet"` — MainNet (default)
|
|
8
|
+
- `"testnet"` — TestNet
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
→ api_haystack_get_swap_quote {
|
|
12
|
+
fromASAID: 0, toASAID: 31566704, amount: 1000000,
|
|
13
|
+
network: "testnet"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Default to **testnet** during development; confirm with user before using mainnet.
|
|
18
|
+
|
|
19
|
+
## Slippage
|
|
20
|
+
|
|
21
|
+
Set slippage (percentage tolerance on output) when executing swaps:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
→ api_haystack_execute_swap {
|
|
25
|
+
fromASAID: 0, toASAID: 31566704, amount: 1000000,
|
|
26
|
+
slippage: 1, // 1% — receive at least 99% of quoted output
|
|
27
|
+
network: "testnet"
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Recommendations:**
|
|
32
|
+
|
|
33
|
+
- **Stable pairs** (ALGO/USDC): 0.5–1%
|
|
34
|
+
- **Volatile pairs**: 1–3%
|
|
35
|
+
- **Low liquidity**: 3–5%
|
|
36
|
+
|
|
37
|
+
Slippage is verified on the **final output** of the swap, not on individual hops.
|
|
38
|
+
|
|
39
|
+
## Rate Limits
|
|
40
|
+
|
|
41
|
+
The Algorand MCP server uses a free tier API key (60 requests/min). This applies to all Haystack Router tool calls. Override via `HAYSTACK_API_KEY` environment variable.
|
|
42
|
+
|
|
43
|
+
## Common ASA IDs
|
|
44
|
+
|
|
45
|
+
| Asset | ASA ID |
|
|
46
|
+
| ----- | --------- |
|
|
47
|
+
| ALGO | 0 |
|
|
48
|
+
| USDC | 31566704 |
|
|
49
|
+
| USDt | 312769 |
|
|
50
|
+
| goBTC | 386192725 |
|
|
51
|
+
| goETH | 386195940 |
|
|
52
|
+
|
|
53
|
+
Look up ASA IDs on [Allo.info](https://allo.info) or [Pera Explorer](https://explorer.perawallet.app/).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
When operating as an Algorand MCP agent, use Algorand MCP tools directly. No SDK installation or API key management needed — the Algorand MCP server handles this internally.
|
|
4
|
+
|
|
5
|
+
## Quick Swap (2 steps)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
1. wallet_get_info { network: "testnet" }
|
|
9
|
+
→ Get wallet address and balance
|
|
10
|
+
|
|
11
|
+
2. api_haystack_execute_swap {
|
|
12
|
+
fromASAID: 0, toASAID: 31566704, amount: 1000000,
|
|
13
|
+
slippage: 1, network: "testnet"
|
|
14
|
+
}
|
|
15
|
+
→ Quotes, signs via wallet, submits, confirms — all in one call
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## With Preview (recommended for user-facing swaps)
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
1. wallet_get_info { network: "testnet" }
|
|
22
|
+
2. api_haystack_needs_optin { address, assetId: 31566704, network: "testnet" }
|
|
23
|
+
→ If needed: wallet_optin_asset { assetId: 31566704, network: "testnet" }
|
|
24
|
+
3. api_haystack_get_swap_quote { fromASAID: 0, toASAID: 31566704, amount: 1000000, network: "testnet" }
|
|
25
|
+
→ Show user the quote → get confirmation
|
|
26
|
+
4. api_haystack_execute_swap { fromASAID: 0, toASAID: 31566704, amount: 1000000, slippage: 1, network: "testnet" }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Algorand MCP Tool Reference
|
|
30
|
+
|
|
31
|
+
| Tool | Purpose |
|
|
32
|
+
| ------------------------------- | ------------------------------------------------ |
|
|
33
|
+
| `api_haystack_get_swap_quote` | Preview swap quote with routing and pricing |
|
|
34
|
+
| `api_haystack_execute_swap` | Execute swap: quote + sign + submit + confirm |
|
|
35
|
+
| `api_haystack_needs_optin` | Check if address needs asset opt-in |
|
|
36
|
+
| `wallet_get_info` | Get wallet address and ALGO balance |
|
|
37
|
+
| `wallet_optin_asset` | Opt into an ASA (one-step build+sign+submit) |
|
|
38
|
+
|
|
39
|
+
See [swaps.md](swaps.md) for detailed workflow and [quotes.md](quotes.md) for quote parameters.
|
|
40
|
+
|
|
41
|
+
## Amounts and Units
|
|
42
|
+
|
|
43
|
+
All amounts are in **base units** (smallest denomination):
|
|
44
|
+
|
|
45
|
+
| Asset | Decimals | 1 unit in base | Example |
|
|
46
|
+
| ------------------- | -------- | -------------- | -------------------- |
|
|
47
|
+
| ALGO (ASA 0) | 6 | 1,000,000 | `1_000_000` = 1 ALGO |
|
|
48
|
+
| USDC (ASA 31566704) | 6 | 1,000,000 | `5_000_000` = 5 USDC |
|