๐ 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;
_tokensis an array of two stablecoins โ ERC20 token addresses (must be sorted by address)_oraclesis an array of two oracle addresses providing price feeds for each tokenamountsis an array of initial token amounts to deposit [token0Amount, token1Amount]minSharesis the minimum LP tokens expected from initial deposit (slippage protection)_Ais the amplification parameter for StableSwap curve (higher = more stable)_baseFeeis the base trading fee in 10 decimals (e.g., 1e8 = 1%)_feeMultiplieris the dynamic fee multiplier in 10 decimals_saltis 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.
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;
tokenInis an address of input token to swap fromtokenOutis an address of output token to swap toamountInis 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;
amountsis an array of token amounts to deposit [token0Amount, token1Amount]minSharesis minimum LP tokens expected (slippage protection)receiveris 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: 0is swap from token0 to token1direction: 1is swap from token1 to token0tokenInis 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.