Skip to main content

๐Ÿ” FX Swap Module

The FX Swap system is a purpose-built Stable Swap AMM infrastructure optimized for low-volatility, fiat-backed stablecoin pairs. It integrates real-time oracle-based pricing to achieve efficient, thin-margin and slippage-minimized swaps across multi-currency corridors.

The system architecture is modular and designed around a factory-pool model, comprising three primary smart contract components โ€“ FxSwapManager, FXSwapRouter & FXSwap.

FxSwapManager (Factory Contract)โ€‹

The FxSwapManager is the central factory responsible for deploying and managing individual FX trading pools.

Core Mechanicsโ€‹

The Swap Manager deploys new FX pair pools via a single transaction as well as registers and tracks all active pool addresses. It is also the control panel for system-level parameters like base fees, allowed stablecoin pairs, and governance hooks.

Its core function is to deploy a new FX pool between two fiat-backed stablecoins with initial liquidity, & registers the oracle feed used for pricing with the method below.

function create(

address[2] memory _tokens,
address[2] memory _oracles,
uint256[2] memory amounts,
uint256 minShares,
uint256 _A,
uint256 _baseFee,
uint256 _feeMultiplier,
bytes32 _salt

) external returns (address poolAddress, uint256 shares)

where;

  1. _tokens is an array of two stablecoins โ€” ERC20 token addresses (must be sorted by address)
  2. _oracles is an array of two oracle addresses providing price feeds for each token
  3. amounts is an array of initial token amounts to deposit [token0Amount, token1Amount]
  4. minShares is the minimum LP tokens expected from initial deposit (slippage protection)
  5. _A is the amplification parameter for StableSwap curve (higher = more stable)
  6. _baseFee is the base trading fee in 10 decimals (e.g., 1e8 = 1%)
  7. _feeMultiplier is the dynamic fee multiplier in 10 decimals
  8. _salt is Bytes32 salt for deterministic pool address generation

It returns the poolAddress , address of the newly created pool contract & shares, Number of LP tokens minted to the caller.

FxSwap (Per-Pair AMM Contract)โ€‹

The FXSwap comprises the StablePool & the StableSwap. Each StablePool contract holds two stablecoins (e.g., chNGN and USDC) and maintains a liquidity invariant using the StableSwap AMM algorithm.

Core Mechanicsโ€‹

The contract maintains token reserves and fee balances & uses oracle-fed virtual pricing to compute the reference FX curve. It also tracks LP positions and handles liquidity entry/exit.

Key Functions

  • getVirtualPrice() โ€” gets normalized price based on current oracle feed of the pool's LP tokens.
  • getDynamicFee() โ€” Calculates the current dynamic fee rate based on pool imbalance.
  • getAmountOut() โ€” Returns output amount for a given input, including applied dynamic fees.
  • removeLiquidity() โ€” Allows LPs to withdraw proportional assets or a specific token, subject to pool balance constraints.
  • exchange(poolAddress, fromToken, toToken, amountIn) โ€” This is a core swap function. It executes the swap using the StableSwap invariant by confirming that fromToken and toToken are supported by the selected pool. It first applies oracle-derived FX rates to calculate the equivalent balance across tokens, then an adaptive swap fee after assessing pool imbalance and volatility. It executes the trade to minimize slippage. Finally, it updates token reserves and collects fees into the poolโ€™s fee vault.
warning

This function is not advisable to be called directly to avoid front-running attacks.

function exchange(

address tokenIn,
address tokenOut,
uint256 amountIn

) external nonReentrant returns (uint256 amountOut)

where;

  1. tokenIn is an address of input token to swap from
  2. tokenOut is an address of output token to swap to
  3. amountIn is the amount of input tokens to swap

As expected, the method returns the output tokens to be received, amountOut

  • addLiquidity(poolAddress, amounts) โ€” Allows liquidity providers to add liquidity into the pool. Uses virtual balances informed by oracle prices to normalize the ratio across currencies.
function addLiquidity(

uint256[2] memory amounts,
uint256 minShares,
address receiver

) external nonReentrant returns (uint256 shares)

where;

  1. amounts is an array of token amounts to deposit [token0Amount, token1Amount]
  2. minShares is minimum LP tokens expected (slippage protection)
  3. receiver is the address to receive LP tokens

The function returns, shares, th number of LP tokens minted.

FxRouter (Routing Contract)โ€‹

The FxRouter contract enables multi-hop swaps across multiple FxSwapPools to support indirect currency pairs and cross-rate efficiency.

Key Functionalityโ€‹

  • Accepts a path like: chNGN โ†’ USDC โ†’ chTRY.
  • Calculates optimal intermediary routing and slippage-adjusted output.
  • Calls swap() on each pool in sequence with interim routing logic handled atomically.
function swap(

address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,

Path[] memory _path

) external nonReentrant

For a multi-hop Swap from chNGN โ†’ chTRY

FxRouter.Path[] memory path = new FxRouter.Path[](2);

path[0] = FxRouter.Path({
pool: chNGN_USDC_pool,
direction: 0 // chNGN โ†’ USDC

});

path[1] = FxRouter.Path({
pool: chTRY_USDC_pool,
direction: 1 // USDC โ†’ chTRY

});

router.swap(chNGN, chTRY, amountIn, minAmountOut, path);

where;

  • direction: 0 is swap from token0 to token1
  • direction: 1 is swap from token1 to token0
  • tokenIn is address of initial input token router.swap(chNGN, USDC, amountIn, minAmountOut, path);

Security and Upgradabilityโ€‹

  • All swap contracts are version-locked (e.g. StablePoolV1) and deployed immutably.
  • System-wide parameters are governed by the DAO via the $CHISS token.
  • Oracle manipulation protections are in place to prevent flash loan-induced arbitrage.
  • Price impact safeguards are in place to prevent any price spike.