{"_id":"@txnlab/haystack-router","_rev":"5-5bbf800ac51dcdb686f74217ae854faf","name":"@txnlab/haystack-router","dist-tags":{"latest":"2.0.5"},"versions":{"2.0.0":{"name":"@txnlab/haystack-router","version":"2.0.0","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"author":{"url":"https://txnlab.dev","name":"Doug Richar","email":"doug@txnlab.dev"},"license":"MIT","_id":"@txnlab/haystack-router@2.0.0","maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"homepage":"https://github.com/TxnLab/haystack-js","bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"dist":{"shasum":"44f2264d8a03a337727fef1eb7d78d3acc237090","tarball":"https://registry.npmjs.org/@txnlab/haystack-router/-/haystack-router-2.0.0.tgz","fileCount":11,"integrity":"sha512-WHSGX0XiczngSkUvB4Xc1AKlFg/TIlEXVXzYz8jOTCN+GAL8ntq+suBXazVHL/QKpFNOpod12+7CdphzkYpXLw==","signatures":[{"sig":"MEYCIQCwZ+fnPyQKevkGRyJfUUeSBx9wAAt8gjyEKyH2qkXxFwIhAKSxqbZG5iPVYSJQReUKw4AXl2E/2s7ggKgPl+luEukV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206676},"type":"module","_from":"file:txnlab-haystack-router-2.0.0.tgz","types":"./dist/index.d.mts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"scripts":{"test":"vitest run","build":"tsdown && publint --strict","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"txnlab","email":"admin@txnlab.dev"},"_resolved":"/private/var/folders/y3/71qsw2f148df22yjjfnw1zkc0000gn/T/2e810c346a4126dea7cf35203c333c77/txnlab-haystack-router-2.0.0.tgz","_integrity":"sha512-WHSGX0XiczngSkUvB4Xc1AKlFg/TIlEXVXzYz8jOTCN+GAL8ntq+suBXazVHL/QKpFNOpod12+7CdphzkYpXLw==","repository":{"url":"git+https://github.com/TxnLab/haystack-js.git","type":"git"},"_npmVersion":"11.6.2","description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsdown":"0.19.0","vitest":"4.0.16","algosdk":"3.5.2","publint":"0.3.16","typescript":"5.9.3","@types/node":"22.19.5","@vitest/coverage-v8":"4.0.16"},"peerDependencies":{"algosdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/haystack-router_2.0.0_1768379943827_0.7027712067266763","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@txnlab/haystack-router","version":"2.0.2","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"author":{"url":"https://txnlab.dev","name":"Doug Richar","email":"doug@txnlab.dev"},"license":"MIT","_id":"@txnlab/haystack-router@2.0.2","maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"homepage":"https://github.com/TxnLab/haystack-js","bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"dist":{"shasum":"5e966e6440e802573f3f7693d3a71d0f8e1a5850","tarball":"https://registry.npmjs.org/@txnlab/haystack-router/-/haystack-router-2.0.2.tgz","fileCount":11,"integrity":"sha512-jWo2oVySjmkbhLOMUh5ygst4787U6b5MpN0/WScWBW1c/XZKPYe8OL4YA8fFbVD5NdBEFmv9ZjVUFMU4grbHLA==","signatures":[{"sig":"MEUCIDSQRlVgKHMsNNihiOJy05ygQUnegA9iYfRuT1+zUDowAiEA6b9nE+bBKD/V2CcKJ6N4PacUhGFGmfNYlodmXy8AjAU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@txnlab%2fhaystack-router@2.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":206737},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"90abcf8f64c06964083ad3671b10a2fe2e140478","scripts":{"test":"vitest run","build":"tsdown && publint --strict","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d410c877-d3d1-425b-b983-295435c2fe54"}},"repository":{"url":"git+https://github.com/TxnLab/haystack-js.git","type":"git"},"_npmVersion":"11.7.0","description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@10.28.0","devDependencies":{"tsdown":"0.19.0","vitest":"4.0.16","algosdk":"3.5.2","publint":"0.3.16","typescript":"5.9.3","@types/node":"22.19.5","@vitest/coverage-v8":"4.0.16"},"peerDependencies":{"algosdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/haystack-router_2.0.2_1768424895563_0.9088434130296654","host":"s3://npm-registry-packages-npm-production"}},"2.0.3":{"name":"@txnlab/haystack-router","version":"2.0.3","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"author":{"url":"https://txnlab.dev","name":"Doug Richar","email":"doug@txnlab.dev"},"license":"MIT","_id":"@txnlab/haystack-router@2.0.3","maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"homepage":"https://github.com/TxnLab/haystack-js","bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"dist":{"shasum":"14a33c02cb867569006ca2a0ecbbfa385c57e2cf","tarball":"https://registry.npmjs.org/@txnlab/haystack-router/-/haystack-router-2.0.3.tgz","fileCount":11,"integrity":"sha512-hJKvbZETnBTgi7w/fDyWoAXLXruRxNiCakF+liC3Ap9OCkvLTVyWNkcUqyg/zNBGEp36qiGix+tS+xYv6YNetA==","signatures":[{"sig":"MEQCIE+h9Z9QPeNuacZLgb2w8GEYWmDOfVE1Syy+gd45LUKOAiAohIYfZXFlD+dzYLrMb4FQxZoosjEwgV084d0Pio359w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@txnlab%2fhaystack-router@2.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":206787},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"9b2110b0d86f3d483bb38d06f0fe3bd851f54e40","scripts":{"test":"vitest run","build":"tsdown && publint --strict","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d410c877-d3d1-425b-b983-295435c2fe54"}},"repository":{"url":"git+https://github.com/TxnLab/haystack-js.git","type":"git"},"_npmVersion":"11.7.0","description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@10.28.0","devDependencies":{"tsdown":"0.19.0","vitest":"4.0.16","algosdk":"3.5.2","publint":"0.3.16","typescript":"5.9.3","@types/node":"22.19.5","@vitest/coverage-v8":"4.0.16"},"peerDependencies":{"algosdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/haystack-router_2.0.3_1768426031712_0.16561139551099258","host":"s3://npm-registry-packages-npm-production"}},"2.0.4":{"name":"@txnlab/haystack-router","version":"2.0.4","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"author":{"url":"https://txnlab.dev","name":"Doug Richar","email":"doug@txnlab.dev"},"license":"MIT","_id":"@txnlab/haystack-router@2.0.4","maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"homepage":"https://github.com/TxnLab/haystack-js","bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"dist":{"shasum":"98ee74cfd7f1fbfb313e5111d12c9530d38f7ba5","tarball":"https://registry.npmjs.org/@txnlab/haystack-router/-/haystack-router-2.0.4.tgz","fileCount":11,"integrity":"sha512-6Hsm73bH8J66uzL1IWiF3rllar+yDdsls2OaP+wfIV0cuHpKZzmulewYgQKal1KAM1a19F6omo0RYjZyRFH7oA==","signatures":[{"sig":"MEUCIQDH2YUZXk6XFJN99zaCr6bfknp+8Y1A9/JuPrDwrDi8FQIgI75YtP7VoJT/vwM7c84BhWXhWcZBOy2vKGQpf3p5dw0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@txnlab%2fhaystack-router@2.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":206816},"type":"module","types":"./dist/index.d.mts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"gitHead":"349521fe53b8ded987ba4afad8da42036b883eeb","scripts":{"test":"vitest run","build":"tsdown && publint --strict","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d410c877-d3d1-425b-b983-295435c2fe54"}},"repository":{"url":"git+https://github.com/TxnLab/haystack-js.git","type":"git"},"_npmVersion":"11.7.0","description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@10.28.0","devDependencies":{"tsdown":"0.19.0","vitest":"4.0.16","algosdk":"3.5.2","publint":"0.3.16","typescript":"5.9.3","@types/node":"22.19.5","@vitest/coverage-v8":"4.0.16"},"peerDependencies":{"algosdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/haystack-router_2.0.4_1768855626997_0.2639494649375702","host":"s3://npm-registry-packages-npm-production"}},"2.0.5":{"name":"@txnlab/haystack-router","version":"2.0.5","description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"homepage":"https://github.com/TxnLab/haystack-js","bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"author":{"name":"Doug Richar","email":"doug@txnlab.dev","url":"https://txnlab.dev"},"repository":{"type":"git","url":"git+https://github.com/TxnLab/haystack-js.git"},"license":"MIT","type":"module","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"}},"types":"./dist/index.d.mts","publishConfig":{"access":"public","provenance":true},"engines":{"node":">=20"},"packageManager":"pnpm@10.28.0","scripts":{"build":"tsdown && publint --strict","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit"},"devDependencies":{"@types/node":"22.19.5","@vitest/coverage-v8":"4.0.16","algosdk":"3.5.2","publint":"0.3.16","tsdown":"0.19.0","typescript":"5.9.3","vitest":"4.0.16"},"peerDependencies":{"algosdk":"^3.0.0"},"gitHead":"344776aad182f7bbf49e4673a484dc76b3ef8e6d","_id":"@txnlab/haystack-router@2.0.5","_nodeVersion":"22.22.0","_npmVersion":"11.10.0","dist":{"integrity":"sha512-2d7nmf3Jfc2RyceewRsXhBB7pWN4+Zoj5Lw5iVCRmntCPL25oJb2hkv2xkkaoPcjMxFoqzP4qC+fp1eyRbFvZA==","shasum":"aa1c08e11ddd2d3f79912eea5a3f8b49a9e91fcd","tarball":"https://registry.npmjs.org/@txnlab/haystack-router/-/haystack-router-2.0.5.tgz","fileCount":11,"unpackedSize":208362,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@txnlab%2fhaystack-router@2.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFdrGileobLHGYAXcCKbBKc1TggoKBCaNkWE7f/TR+OLAiEA9hPHUyCzSYHX3suuIyo/RO5gNS3E/kmyCyPTQxkDsYA="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d410c877-d3d1-425b-b983-295435c2fe54"}},"directories":{},"maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/haystack-router_2.0.5_1771271942347_0.12673692560117855"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-14T08:39:03.686Z","modified":"2026-02-16T19:59:02.788Z","2.0.0":"2026-01-14T08:39:03.971Z","2.0.2":"2026-01-14T21:08:15.710Z","2.0.3":"2026-01-14T21:27:11.857Z","2.0.4":"2026-01-19T20:47:07.303Z","2.0.5":"2026-02-16T19:59:02.497Z"},"bugs":{"url":"https://github.com/TxnLab/haystack-js/issues"},"author":{"name":"Doug Richar","email":"doug@txnlab.dev","url":"https://txnlab.dev"},"license":"MIT","homepage":"https://github.com/TxnLab/haystack-js","keywords":["algorand","haystack","router","order-router","dex","swap","defi","amm","liquidity","aggregator","trading","blockchain","typescript","sdk"],"repository":{"type":"git","url":"git+https://github.com/TxnLab/haystack-js.git"},"description":"TypeScript/JavaScript SDK for Haystack Order Router - smart order routing and DEX aggregation on Algorand","maintainers":[{"name":"txnlab","email":"admin@txnlab.dev"}],"readme":"# Haystack Router SDK\n\n[![npm version](https://img.shields.io/npm/v/@txnlab/haystack-router.svg)](https://www.npmjs.com/package/@txnlab/haystack-router)\n[![bundle size](https://deno.bundlejs.com/badge?q=@txnlab/haystack-router@latest&treeshake=[*])](https://bundlejs.com/?q=%40txnlab%2Fhaystack-router%40latest&treeshake=%5B*%5D)\n[![CI](https://github.com/TxnLab/haystack-js/actions/workflows/ci.yml/badge.svg)](https://github.com/TxnLab/haystack-js/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)\n\nTypeScript/JavaScript SDK for [Haystack Order Router](https://txnlab.gitbook.io/haystack-router) - smart order routing and DEX aggregation on Algorand.\n\n## Prerequisites\n\n- **Haystack Router API Key** - A free tier key is included below for development and testing (60 requests/min). For production rate limits, request a dedicated key from [support@txnlab.dev](mailto:support@txnlab.dev).\n- algosdk 3.0.0 or later\n\n### API Key Tiers\n\n| Tier | Key | Rate Limit | Use Case |\n| --- | --- | --- | --- |\n| **Free** | `1b72df7e-1131-4449-8ce1-29b79dd3f51e` | 60 requests/min | Development, testing, low-volume integrations |\n| **Production** | Request from [support@txnlab.dev](mailto:support@txnlab.dev) | Higher limits | Production applications |\n\n## Installation\n\n```bash\nnpm install @txnlab/haystack-router algosdk\n```\n\n> **Note**: `algosdk` is a peer dependency and must be installed alongside `@txnlab/haystack-router`.\n\n## Quick Start\n\n```typescript\nimport { RouterClient } from '@txnlab/haystack-router'\nimport { useWallet } from '@txnlab/use-wallet-*' // react, vue, solid, or svelte\n\nconst { activeAddress, transactionSigner } = useWallet()\n\n// Initialize the client\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n})\n\n// Get a quote\nconst quote = await router.newQuote({\n  address: activeAddress,\n  fromAssetId: 0, // ALGO\n  toAssetId: 31566704, // USDC\n  amount: 1_000_000, // 1 ALGO (in microAlgos)\n})\n\n// Execute the swap\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: transactionSigner,\n  slippage: 1, // 1% slippage tolerance\n})\nconst result = await swap.execute()\n\nconsole.log(`Swap completed in round ${result.confirmedRound}`)\n```\n\n## Usage\n\n### Initialize the Client\n\n```typescript\nimport { RouterClient } from '@txnlab/haystack-router'\n\n// Basic initialization\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n})\n\n// Custom Algod configuration\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n  algodUri: 'https://mainnet-api.4160.nodely.dev/',\n  algodToken: '',\n  algodPort: 443,\n  autoOptIn: true, // Automatically handle asset opt-ins\n})\n\n// Earn fees with the referral program\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n  referrerAddress: 'YOUR_ALGORAND_ADDRESS', // Earns 25% of swap fees\n  feeBps: 15, // 0.15% fee (max: 300 = 3%)\n})\n```\n\nBy providing your Algorand address as the `referrerAddress` when initializing the client, you can earn 25% of the swap fees generated through your integration. Set the `feeBps` parameter to specify the total fee charged to users (default: 0.15%, max: 3.00%). Learn more about the [Haystack Router Referral Program](https://txnlab.gitbook.io/haystack-router/referral-treasury/referral-program).\n\n### Get a Swap Quote\n\nThe [`newQuote()`](#routerclientnewquote) method returns a [`SwapQuote`](#swapquote) object:\n\n```typescript\n// Basic quote\nconst quote = await router.newQuote({\n  fromASAID: 0, // ALGO\n  toASAID: 31566704, // USDC\n  amount: 1_000_000, // 1 ALGO\n  address: userAddress, // Required for auto opt-in detection\n})\n```\n\n### Execute a Swap\n\nThe [`newSwap()`](#routerclientnewswap) method returns a [`SwapComposer`](#swapcomposer) instance:\n\n```typescript\nimport { useWallet } from '@txnlab/use-wallet-*' // react, vue, solid, or svelte\n\nconst { activeAddress, transactionSigner } = useWallet()\n\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: transactionSigner,\n  slippage: 1, // 1% slippage tolerance\n})\nconst result = await swap.execute()\n\nconsole.log(`Confirmed in round ${result.confirmedRound}`)\nconsole.log('Transaction IDs:', result.txIds)\n```\n\n### Transaction Tracking\n\nAdd a custom note to the input transaction for tracking purposes, and retrieve its transaction ID after execution:\n\n```typescript\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: transactionSigner,\n  slippage: 1,\n  note: new TextEncoder().encode('tracking-id-123'), // Custom note for tracking\n})\n\nconst result = await swap.execute()\n\n// Get the transaction ID of the user-signed input transaction\nconst inputTxId = swap.getInputTransactionId()\nconsole.log('Input transaction ID:', inputTxId)\n```\n\nThe `note` is applied only to the user-signed payment or asset transfer transaction (not pre-signed or middleware transactions). The transaction ID is available after calling `buildGroup()`, `sign()`, or `execute()`.\n\n### Transaction Signing\n\nThe SDK supports both standard `algosdk.TransactionSigner` and ARC-1 compliant signer functions.\n\n#### 1. use-wallet Signer (Recommended)\n\nUse the `@txnlab/use-wallet` library for wallet management in your dApp:\n\n```typescript\nimport { useWallet } from '@txnlab/use-wallet-*' // react, vue, solid, or svelte\n\nconst { activeAddress, transactionSigner } = useWallet()\n\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: transactionSigner,\n  slippage: 1,\n})\nawait swap.execute()\n```\n\n> **Tip**: The [`@txnlab/use-wallet`](https://github.com/TxnLab/use-wallet) library supports multiple wallet providers (Pera, Defly, Lute, WalletConnect, etc.) and provides a unified interface. Choose the framework-specific adapter for your project: `@txnlab/use-wallet-react`, `@txnlab/use-wallet-vue`, `@txnlab/use-wallet-solid`, or `@txnlab/use-wallet-svelte`.\n\n#### 2. Custom Signer Function\n\nThe SDK accepts custom signer functions that receive the complete transaction group and an array of indexes indicating which transactions need signing:\n\n```typescript\nimport { Address, encodeUnsignedTransaction, type Transaction } from 'algosdk'\n\n// Example: Wrapping an ARC-1 compliant wallet\nconst customSigner = async (\n  txnGroup: Transaction[],\n  indexesToSign: number[],\n) => {\n  // Convert to wallet's expected format\n  const walletTxns = txnGroup.map((txn, index) => ({\n    txn: Buffer.from(encodeUnsignedTransaction(txn)).toString('base64'),\n    signers: indexesToSign.includes(index)\n      ? [Address.fromString(activeAddress)]\n      : [],\n  }))\n\n  // Sign with wallet provider\n  const signedTxns = await walletProvider.signTxns(walletTxns)\n\n  return signedTxns\n}\n\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: customSigner,\n  slippage: 1,\n})\nawait swap.execute()\n```\n\nThe signer function supports two return patterns:\n\n- **Pattern 1** (Pera, Defly, algosdk): Returns only the signed transactions as `Uint8Array[]`\n- **Pattern 2** (Lute, ARC-1 compliant): Returns an array matching the transaction group length with `null` for unsigned transactions as `(Uint8Array | null)[]`\n\nBoth patterns are automatically handled by the SDK.\n\n### Advanced Transaction Composition\n\nBuild the transaction group by adding custom transactions and ABI method calls before or after the swap using the [`SwapComposer`](#swapcomposer) instance:\n\n```typescript\nimport { ABIMethod, Transaction } from 'algosdk'\nimport { useWallet } from '@txnlab/use-wallet-*' // react, vue, solid, or svelte\n\nconst { activeAddress, transactionSigner } = useWallet()\n\n// Create your custom transactions\nconst customTxn = new Transaction({...})\n\n// Define an ABI method call\nconst methodCall = {\n  appID: 123456,\n  method: new ABIMethod({...}),\n  methodArgs: [...],\n  sender: activeAddress,\n  suggestedParams: await algodClient.getTransactionParams().do(),\n}\n\n// Build and execute the transaction group\nconst swap = await router.newSwap({\n  quote,\n  address: activeAddress,\n  signer: transactionSigner,\n  slippage: 1,\n})\n\nconst result = await swap\n  .addTransaction(customTxn)      // Add transaction before swap\n  .addSwapTransactions()          // Add swap transactions\n  .addMethodCall(methodCall)      // Add ABI method call after swap\n  .execute()                      // Sign and execute entire group\n```\n\n### Middleware for Custom Asset Requirements\n\nSome Algorand assets require additional transactions to be added to swap groups (e.g., assets with transfer restrictions, taxes, or custom smart contract logic). The Haystack Router SDK supports a middleware system that allows these special requirements to be handled by external packages without modifying the core SDK.\n\nMiddleware can:\n- Adjust quote parameters (e.g., reduce `maxGroupSize` to account for extra transactions)\n- Add transactions before the swap (e.g., unfreeze account, setup calls)\n- Add transactions after the swap (e.g., tax payments, cleanup calls)\n\n```typescript\nimport { RouterClient } from '@txnlab/haystack-router'\nimport { FirstStageMiddleware } from '@firststage/deflex-middleware' // Example external package\n\n// Initialize middleware\nconst firstStage = new FirstStageMiddleware({\n  contractAppId: 123456,\n})\n\n// Pass middleware to RouterClient\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n  middleware: [firstStage], // Middleware is applied automatically\n})\n\n// Use normally - middleware handles everything\nconst quote = await router.newQuote({\n  fromASAID: 0,        // ALGO\n  toASAID: 789012,     // Custom asset (e.g., MOOJ, DEAL)\n  amount: 1_000_000,\n  address: userAddress,\n})\n\nconst swap = await router.newSwap({ quote, address, signer, slippage: 1 })\nawait swap.execute() // Middleware transactions are automatically included\n```\n\n#### Built-in Middleware\n\nThe SDK includes `AutoOptOutMiddleware`, which automatically opts out of assets when swapping your full balance, cleaning up zero balance assets and reducing minimum balance requirements:\n\n```typescript\nimport { RouterClient, AutoOptOutMiddleware } from '@txnlab/haystack-router'\n\nconst autoOptOut = new AutoOptOutMiddleware({\n  excludedAssets: [31566704], // Optional: exclude specific assets like USDC\n})\n\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n  middleware: [autoOptOut],\n})\n\n// When swapping full balance, opt-out transaction is automatically added\nconst quote = await router.newQuote({\n  fromASAID: someAssetId,\n  toASAID: 0,\n  amount: fullBalance, // If this matches your full balance, asset will be opted out\n  address: userAddress,\n})\n```\n\nFor details on creating your own middleware, see [MIDDLEWARE.md](MIDDLEWARE.md).\n\n### Manual Asset Opt-In Detection\n\nIf you're not using `autoOptIn: true`, you can manually check if opt-in is needed:\n\n```typescript\nconst router = new RouterClient({\n  apiKey: '1b72df7e-1131-4449-8ce1-29b79dd3f51e', // Free tier (60 requests/min)\n  autoOptIn: false, // Default if not provided\n})\n\n// Check if user needs to opt into the output asset\nconst needsOptIn = await router.needsAssetOptIn(userAddress, toAssetId)\n\n// Include opt-in in quote if needed\nconst quote = await router.newQuote({\n  fromAssetId,\n  toAssetId,\n  amount,\n  optIn: needsOptIn,\n})\n```\n\n### Error Handling\n\n```typescript\nimport { useWallet } from '@txnlab/use-wallet-*' // react, vue, solid, or svelte\n\nconst { activeAddress, transactionSigner } = useWallet()\n\ntry {\n  const quote = await router.newQuote({\n    fromAssetId: 0,\n    toAssetId: 31566704,\n    amount: 1_000_000,\n    address: activeAddress,\n  })\n\n  const swap = await router.newSwap({\n    quote,\n    address: activeAddress,\n    signer: transactionSigner,\n    slippage: 1,\n  })\n  const result = await swap.execute()\n\n  console.log('Swap successful:', result)\n} catch (error) {\n  console.error('Swap failed:', error.message)\n}\n```\n\n## API Reference\n\n### RouterClient\n\nThe main client for interacting with the Haystack Router API.\n\n```typescript\nnew RouterClient(config: ConfigParams)\n```\n\n| Option            | Description                                                  | Type                  | Default                                |\n| ----------------- | ------------------------------------------------------------ | --------------------- | -------------------------------------- |\n| `apiKey`          | Your Haystack Router API key                                          | `string`              | **required**                           |\n| `apiBaseUrl`      | Base URL for the Haystack Router API                                  | `string`              | `https://hayrouter.txnlab.dev`            |\n| `algodUri`        | Algod node URI                                               | `string`              | `https://mainnet-api.4160.nodely.dev/` |\n| `algodToken`      | Algod node token                                             | `string`              | `''`                                   |\n| `algodPort`       | Algod node port                                              | `string \\| number`    | `443`                                  |\n| `referrerAddress` | Referrer address for fee sharing (receives 25% of swap fees) | `string`              | `undefined`                            |\n| `feeBps`          | Fee in basis points (0.15%, max: 300 = 3.00%)                | `number`              | `15`                                   |\n| `autoOptIn`       | Auto-detect and add required opt-in transactions             | `boolean`             | `false`                                |\n| `middleware`      | Array of middleware for custom asset requirements            | `SwapMiddleware[]`    | `[]`                                   |\n\n> **Referral Program**: By providing a `referrerAddress`, you can earn 25% of the swap fees generated through your integration. The `feeBps` parameter sets the total fee charged (default: 0.15%). Learn more about the [Haystack Router Referral Program](https://txnlab.gitbook.io/haystack-router/referral-treasury/referral-program).\n\n#### RouterClient.newQuote()\n\nFetch a swap quote and return a [`SwapQuote`](#swapquote) object.\n\n```typescript\nasync newQuote(params: FetchQuoteParams): Promise<SwapQuote>\n```\n\n| Parameter           | Description                                | Type                              | Default         |\n| ------------------- | ------------------------------------------ | --------------------------------- | --------------- |\n| `fromASAID`         | Input asset ID                             | `bigint \\| number`                | **required**    |\n| `toASAID`           | Output asset ID                            | `bigint \\| number`                | **required**    |\n| `amount`            | Amount to swap in base units               | `bigint \\| number`                | **required**    |\n| `type`              | Quote type                                 | `'fixed-input' \\| 'fixed-output'` | `'fixed-input'` |\n| `address`           | User address (recommended for auto opt-in) | `string`                          | `undefined`     |\n| `disabledProtocols` | Array of protocols to exclude              | `Protocol[]`                      | `[]`            |\n| `maxGroupSize`      | Maximum transactions in atomic group       | `number`                          | `16`            |\n| `maxDepth`          | Maximum swap hops                          | `number`                          | `4`             |\n| `optIn`             | Override auto opt-in behavior              | `boolean`                         | `undefined`     |\n\n#### RouterClient.newSwap()\n\nReturns a [`SwapComposer`](#swapcomposer) instance for building and executing swaps.\n\n```typescript\nasync newSwap(config: SwapComposerConfig): Promise<SwapComposer>\n```\n\n| Parameter  | Description                                       | Type                                                                                                                   |\n| ---------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| `quote`    | Quote result or raw API response                  | `SwapQuote \\| FetchQuoteResponse`                                                                                    |\n| `address`  | Signer address                                    | `string`                                                                                                               |\n| `slippage` | Slippage tolerance as percentage (e.g., 1 for 1%) | `number`                                                                                                               |\n| `signer`   | Transaction signer function                       | `algosdk.TransactionSigner \\| ((txnGroup: Transaction[], indexesToSign: number[]) => Promise<(Uint8Array \\| null)[]>)` |\n| `note`     | Optional note for the user-signed input transaction (for tracking purposes) | `Uint8Array`                                                                                             |\n\n#### RouterClient.needsAssetOptIn()\n\nChecks if an address needs to opt into an asset.\n\n```typescript\nasync needsAssetOptIn(address: string, assetId: bigint | number): Promise<boolean>\n```\n\n| Parameter | Description               | Type               |\n| --------- | ------------------------- | ------------------ |\n| `address` | Algorand address to check | `string`           |\n| `assetId` | Asset ID to check         | `bigint \\| number` |\n\n### SwapQuote\n\nPlain object returned by [`newQuote()`](#routerclientnewquote). Extends the raw API response with additional metadata.\n\n**Additional properties added by SDK:**\n\n| Property    | Description                         | Type                  |\n| ----------- | ----------------------------------- | --------------------- |\n| `quote`     | Quoted amount (coerced to `bigint`) | `bigint`              |\n| `amount`    | Original request amount             | `bigint`              |\n| `address`   | User address (if provided)          | `string \\| undefined` |\n| `createdAt` | Timestamp when quote was created    | `number`              |\n\n**All properties from API response:**\n\n| Property            | Description                                      | Type                     |\n| ------------------- | ------------------------------------------------ | ------------------------ |\n| `fromASAID`         | Input asset ID                                   | `number`                 |\n| `toASAID`           | Output asset ID                                  | `number`                 |\n| `type`              | Quote type (`'fixed-input'` or `'fixed-output'`) | `string`                 |\n| `profit`            | Profit information                               | `Profit`                 |\n| `priceBaseline`     | Baseline price without fees                      | `number`                 |\n| `userPriceImpact`   | Price impact for the user                        | `number \\| undefined`    |\n| `marketPriceImpact` | Overall market price impact                      | `number \\| undefined`    |\n| `usdIn`             | USD value of input                               | `number`                 |\n| `usdOut`            | USD value of output                              | `number`                 |\n| `route`             | Routing path information                         | `Route[]`                |\n| `flattenedRoute`    | Flattened routing percentages                    | `Record<string, number>` |\n| `quotes`            | Individual DEX quotes                            | `DexQuote[]`             |\n| `requiredAppOptIns` | Required app opt-ins                             | `number[]`               |\n| `txnPayload`        | Encrypted transaction payload                    | `TxnPayload \\| null`     |\n| `protocolFees`      | Fees by protocol                                 | `Record<string, number>` |\n| `timing`            | Performance timing data                          | `unknown \\| undefined`   |\n\n### SwapComposer\n\nBuilder for constructing and executing atomic swap transaction groups, returned by [`newSwap()`](#routerclientnewswap).\n\n| Method                                 | Description                                                                    | Parameters                                                     | Returns                                                                            |\n| -------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| `addTransaction(transaction, signer?)` | Add a transaction to the atomic group                                          | `transaction: algosdk.Transaction, signer?: TransactionSigner` | `SwapComposer`                                                                     |\n| `addMethodCall(methodCall, signer?)`   | Add an ABI method call to the atomic group                                     | `methodCall: MethodCall, signer?: TransactionSigner`           | `SwapComposer`                                                                     |\n| `addSwapTransactions()`                | Add swap transactions to the group (includes required app opt-ins)             | None                                                           | `Promise<SwapComposer>`                                                            |\n| `buildGroup()`                         | Build the transaction group and assign group IDs                               | None                                                           | `TransactionWithSigner[]`                                                          |\n| `sign()`                               | Sign the transaction group                                                     | None                                                           | `Promise<Uint8Array[]>`                                                            |\n| `submit()`                             | Sign and submit the transaction group                                          | None                                                           | `Promise<string[]>` (transaction IDs)                                              |\n| `execute(waitRounds?)`                 | Sign, submit, and wait for confirmation                                        | `waitRounds?: number` (default: 4)                             | `Promise<{ confirmedRound: bigint, txIds: string[], methodResults: ABIResult[] }>` |\n| `getStatus()`                          | Get current status: `BUILDING`, `BUILT`, `SIGNED`, `SUBMITTED`, or `COMMITTED` | None                                                           | `SwapComposerStatus`                                                               |\n| `count()`                              | Get the number of transactions in the group                                    | None                                                           | `number`                                                                           |\n| `getInputTransactionId()`              | Get the transaction ID of the user-signed input transaction (available after `buildGroup()`, `sign()`, or `execute()`) | None                                                           | `string \\| undefined`                                                              |\n\n## Documentation\n\nFor more information about the Haystack Order Router protocol, visit the [official documentation](https://txnlab.gitbook.io/haystack-router).\n\n## License\n\nMIT\n\n## Support\n\n- [GitHub Issues](https://github.com/TxnLab/haystack-js/issues)\n- [Discord](https://discord.gg/Ek3dNyzG)\n- [TxnLab](https://txnlab.dev)\n","readmeFilename":"README.md"}