Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

feat: add middleware system for custom asset support#19

Merged
drichar merged 7 commits intomainfrom
feat/middleware-system
Nov 6, 2025
Merged

feat: add middleware system for custom asset support#19
drichar merged 7 commits intomainfrom
feat/middleware-system

Conversation

@drichar
Copy link
Contributor

@drichar drichar commented Nov 5, 2025

Summary

Adds a middleware/plugin system to the Deflex SDK for supporting Algorand assets with special transfer requirements (taxes, freeze/unfreeze, smart contract interactions, custom tokenomics) without modifying the core SDK.

This enables external developers to create standalone middleware packages for special asset integrations (e.g., First Stage, custom tokenomics systems) that work seamlessly with the SDK.

Changes

Core Implementation

  • New SwapMiddleware interface with hooks:
    • shouldApply(): Conditional middleware activation per swap
    • adjustQuoteParams(): Pre-quote parameter adjustments (maxGroupSize, amount)
    • beforeSwap(): Inject transactions before swap transactions
    • afterSwap(): Inject transactions after swap transactions
  • New SwapContext interface providing quote, address, algodClient, suggestedParams, asset IDs, and signer
  • Updated DeflexClient to accept middleware array and apply transformations in newQuote()
  • Updated SwapComposer to execute middleware hooks during addSwapTransactions()

Documentation

  • New MIDDLEWARE.md: Comprehensive developer guide covering:
    • Interface documentation with detailed examples
    • Step-by-step middleware creation guide
    • Publishing guidelines with package.json and README templates
    • Critical best practices (maxGroupSize adjustment, opt-in handling)
    • Complete First Stage implementation example (190 lines)
  • Updated README.md: Middleware usage examples and API reference

Tests

  • Interface validation and structure tests
  • Quote parameter adjustment tests (single and chained middleware)
  • Conditional application via shouldApply()
  • All 121 tests passing ✅

Use Cases

  • First Stage Assets: MOOJ, DEAL, and other assets using the First Stage tokenomics contract
  • Transfer Taxes: Assets requiring tax payments on transfers
  • Freeze/Unfreeze: Assets with smart contract-enforced transfer restrictions
  • Custom Tokenomics: Any asset with special transaction requirements

Example Usage

import { DeflexClient } from '@txnlab/deflex'
import { FirstStageMiddleware } from '@first-stage/deflex-middleware'

const middleware = new FirstStageMiddleware({ contractAppId: 123456 })

const deflex = new DeflexClient({
  apiKey: 'your-api-key',
  middleware: [middleware], // Middleware applied automatically
})

// Use normally - middleware handles everything
const quote = await deflex.newQuote({ ... })
const swap = await deflex.newSwap({ quote, address, signer })
await swap.execute() // Middleware transactions included automatically

Benefits

Zero SDK bloat: Middleware is opt-in, external packages don't affect bundle size
Composable: Multiple middleware can be chained together
Community-driven: Developers can create and publish their own integrations
Type-safe: Full TypeScript support with exported interfaces
Well-documented: Complete guide with real-world examples

Breaking Changes

None - this is a purely additive feature with backward compatibility.

Add plugin architecture to support assets with special transfer requirements (taxes,
freeze/unfreeze, smart contract interactions) without modifying core SDK.

**Core Changes:**
- Add `SwapMiddleware` interface with hooks:
  - `shouldApply()`: Conditional middleware activation
  - `adjustQuoteParams()`: Pre-quote parameter adjustments (maxGroupSize, amount)
  - `beforeSwap()`: Inject transactions before swap
  - `afterSwap()`: Inject transactions after swap
- Add `SwapContext` interface providing quote, address, algodClient,
  suggestedParams, asset IDs, and signer
- Update `DeflexClient` to accept middleware array and apply transformations in
  `newQuote()`
- Update `SwapComposer` to execute middleware hooks during
  `addSwapTransactions()`

**Tests:**
- Interface validation and structure tests
- Quote parameter adjustment tests (single and chained middleware)
- Conditional application via `shouldApply()`
- All 121 tests passing

Enables external packages for special asset integrations (e.g., First Stage, custom
tokenomics) with zero SDK bloat.
Add comprehensive documentation for the middleware system:

**MIDDLEWARE.md:**
- Complete developer guide for creating middleware packages
- Interface documentation with detailed examples
- Step-by-step creation guide (setup, implementation, caching, error handling)
- Publishing guidelines (package.json, README template)
- Critical best practices:
  - Always adjust maxGroupSize when adding transactions
  - Disable SDK opt-ins when middleware adds its own
  - Minimize transactions, cache queries, handle errors gracefully
- Complete First Stage example implementation (190 lines)

**README.md:**
- Add middleware usage section with example
- Document middleware config option in API reference table
- Link to MIDDLEWARE.md guide

Enables developers to create external packages for special asset support (First Stage,
custom tokenomics) without SDK modifications.
Replace `main` and `types` fields with modern `exports` field for
consistency with ESM-only packages. This follows the same pattern used by
the Deflex SDK and provides better control over the package's public API.
Update middleware interface to use `bigint` for asset IDs instead of
`number`, aligning with algosdk v3's precision-focused approach and future-
proofing for asset IDs that may exceed JavaScript's max safe integer.

**Changes:**
- `SwapMiddleware.shouldApply()`: Asset ID params changed from `number`
  to `bigint`
- `SwapContext`: Asset ID properties changed from `number` to `bigint`
- `DeflexClient.newQuote()`: Changed from `Number()` to `BigInt()` coercion
  when calling middleware
- `SwapComposer`: Changed from passing raw IDs to `BigInt()` coercion in
  all middleware hook calls
- Updated MIDDLEWARE.md documentation with bigint types
- Updated First Stage example to use bigint throughout
- Updated tests to use bigint literals (`12345n`)

**Benefits:**
- Consistent with algosdk v3's bigint philosophy
- Safer precision handling for large asset IDs
- Future-proof: asset IDs are based on creation round and will eventually
  exceed 2^53 - 1
- Simpler mental model: middleware always receives bigints

All 121 tests passing. TypeCheck passing.
Update all middleware documentation examples to consistently use `bigint` for asset
IDs and `number | bigint` for app IDs, aligning with the middleware interface changes.

**Changes:**
- Updated `SwapMiddleware` interface example to show `bigint` params
- Updated `CustomAssetMiddleware` implementation examples with `bigint` asset IDs
- Updated `FirstStageMiddleware` example with `bigint` asset IDs
- Changed `contractAppId` to `number | bigint` (user-facing API flexibility)
- Updated caching examples to use `Map<bigint, T>`
- Updated all usage examples to show both number and bigint literal options

**Rationale:**
- **Middleware interface** (internal): `bigint` only for predictable, consistent behavior
- **Middleware config** (external): `number | bigint` for developer convenience
- Follows same pattern as algosdk transaction functions
- App IDs and asset IDs are generated identically and should be treated the same

All documentation now accurately reflects the bigint-first approach while maintaining
flexibility for developers using middleware packages.
Eliminate duplicate execution of afterSwap hooks and add early validation checkpoint
for beforeSwap transactions to fail fast.

**Performance improvements:**
- Execute `afterSwap` hooks once instead of twice (was called in
  `getAfterSwapCount()` then again to add transactions)
- Removed `getAfterSwapCount()` method entirely
- Avoids duplicate network requests if middleware queries contracts in afterSwap hooks

**Enhanced validation:**
- Added early validation after `beforeSwap` hooks execute
- Catches group size overflow before processing swap transactions or calling afterSwap
- Accounts for edge case where developers manually add transactions before
  `addSwapTransactions()`
- Provides clearer error messages distinguishing beforeSwap overflow vs total overflow

**New execution flow:**
1. Execute `beforeSwap` → validate group size → add to ATC
2. Process swap txns + execute `afterSwap` → validate final group size → add all to ATC

All 121 tests passing.
@drichar drichar merged commit f75963c into main Nov 6, 2025
1 check passed
@drichar drichar deleted the feat/middleware-system branch November 6, 2025 05:00
github-actions bot added a commit that referenced this pull request Nov 6, 2025
# [1.3.0](v1.2.0...v1.3.0) (2025-11-06)

### Features

* add middleware system for custom asset support ([#19](#19)) ([f75963c](f75963c))
@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

🎉 This PR is included in version 1.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant