Pair Contract Overview
Deployment Address: AgoraStableSwap is deployed at [EXTERNAL LINK NEEDED: Deployment addresses] on all available chains. It is a proxy‐based upgradeable contract, so the address remains constant while logic can be upgraded. For other addresses in the Agora Stable Swap ecosystem, see [EXTERNAL LINK NEEDED: ecosystem addresses].
Access‑Control Roles
| Role constant | Capabilities |
|---|---|
ACCESS_CONTROL_MANAGER_ROLE | Grants/revokes any other role |
WHITELISTER_ROLE | Adds or removes APPROVED_SWAPPER |
APPROVED_SWAPPER | Allowed to execute swaps through swap, swapTokensForExactTokens, swapExactTokensForTokens |
FEE_SETTER_ROLE | Adjusts per‑asset purchase fees |
TOKEN_REMOVER_ROLE | Removes excess tokens & collected fees |
PAUSER_ROLE | Pauses / unpauses swaps |
PRICE_SETTER_ROLE | Pushes oracle price updates |
Events
Swap
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
Emitted at the end of every successful swap call. Tracks exact in/out amounts (including fees).
- Only a caller with the
APPROVED_SWAPPERrole can trigger this event.
Sync
event Sync(uint256 reserve0, uint256 reserve1);
Emitted whenever on‑chain reserves are brought in‑sync with the contract's internal state.
- Fired from
swap,sync,removeTokens, andcollectFees. - No roles required for
sync()
SwapFees
event SwapFees(uint256 token0FeesAccumulated, uint256 token1FeesAccumulated);
Logs the incremental fee amounts captured during a swap.
ConfigureOraclePrice
event ConfigureOraclePrice(uint256 basePrice, int256 annualizedInterestRate);
Fired by configureOraclePrice whenever the on‑chain oracle price curve is updated.
- Callable only by
PRICE_SETTER_ROLE.
SetOraclePriceBounds
event SetOraclePriceBounds(
uint256 minBasePrice,
uint256 maxBasePrice,
int256 minAnnualizedInterestRate,
int256 maxAnnualizedInterestRate
);
Signals an update to the guardrails enforced on future oracle‑price changes.
- Emitted by
setOraclePriceBounds; caller must holdACCESS_CONTROL_MANAGER_ROLE.
SetTokenPurchaseFees
event SetTokenPurchaseFees(uint256 token0PurchaseFee, uint256 token1PurchaseFee);
Emitted when live purchase fees are changed via setTokenPurchaseFees.
- Requires
FEE_SETTER_ROLE.
SetFeeBounds
event SetFeeBounds(
uint256 minToken0PurchaseFee,
uint256 maxToken0PurchaseFee,
uint256 minToken1PurchaseFee,
uint256 maxToken1PurchaseFee
);
Defines the outer limits within which future purchase‑fee updates must fall.
- Emitted by
setFeeBounds - caller must hold
ACCESS_CONTROL_MANAGER_ROLE.
SetApprovedSwapper
event SetApprovedSwapper(address indexed approvedSwapper, bool isApproved);
Logged when an address is granted or revoked permission to call the low‑level swap.
- Emitted by
setApprovedSwappers - Caller must hold
WHITELISTER_ROLE.
SetTokenReceiver
event SetTokenReceiver(address indexed tokenReceiver)
Fired by setTokenReceiver when the treasury destination for non‑fee token withdrawals is updated.
- Requires
ACCESS_CONTROL_MANAGER_ROLE.
SetFeeReceiver
event SetFeeReceiver(address indexed feeReceiver);
Fired by setFeeReceiver when the destination for fee withdrawals is updated.
- Requires
ACCESS_CONTROL_MANAGER_ROLE.
RemoveTokens
event RemoveTokens(address indexed tokenAddress, uint256 amount)
Emitted by both removeTokens and collectFees.
removeTokensmoves "excess" liquidity totokenReceiverAddress.collectFeestransfers accumulated fees tofeeReceiverAddress.- Both functions require
TOKEN_REMOVER_ROLE.
SetPaused
event SetPaused(bool isPaused);
Global circuit‑breaker toggle, emitted by setPaused.
- Callable only by
PAUSER_ROLE.
Read-Only Functions
| Function | Returns | Notes |
|---|---|---|
name() | Pair name string ("TOKEN0/TOKEN1‑x.y.z") | Versioned human label |
isPaused() | bool | Swap availability |
token0() / token1() | address | Immutable after init |
reserve0() / reserve1() | uint256 | Excludes un‑claimed fees |
token0PurchaseFee() / token1PurchaseFee() | uint256 (18 dec) | Current purchase fee |
priceLastUpdated() | uint256 (timestamp) | |
perSecondInterestRate() | int256 (18 dec) | Signed APR ÷ 365 days |
basePrice() | uint256 (18 dec) | Spot price at priceLastUpdated |
token0FeesAccumulated() / token1FeesAccumulated() | uint256 | Un‑collected fees |
minToken*PurchaseFee() / maxToken*PurchaseFee() | bounds | Config limits |
tokenReceiverAddress() / feeReceiverAddress() | address | Treasury destinations |
minBasePrice() / maxBasePrice() | uint256 | Oracle price guardrails |
minAnnualizedInterestRate() / maxAnnualizedInterestRate() | int256 | Oracle APR guardrails |
token0Decimals() / token1Decimals() | uint8 | Cached for gas |
getPrice() | Current price (18 dec) | Overloaded version also accepts timestamp |
getPriceNormalized() | Price scaled so both tokens appear at 18 dec precision | |
version() | {major,minor,patch} | Contract logic version |
getAmountsOut
function getAmountsOut(
uint256 amountIn,
address[] calldata path
) external view returns (uint256[] memory amounts);
Determines how many output tokens a router‑style swap would return for a given amountIn.
- Path rules – Must be length 2 and equal to
[token0, token1]or[token1, token0]; otherwise revertsInvalidPath/InvalidPathLength. - Pricing – Uses the live oracle price
getPrice()and the current purchase‑fee parameters for the outgoing token. - Liquidity check – Reverts
InsufficientLiquidity()if reserves can't satisfy the quote. - Return value –
amounts[0] == amountIn,amounts[1] == deterministic quote(fees already deducted).
This is a pure view helper—calling it does not move funds or mutate state. This function is used by swapExactTokensForTokens
getAmountsIn
function getAmountsIn(
uint256 amountOut,
address[] calldata path
) external view returns (uint256[] memory amounts);
Inverse of getAmountsOut: calculates the minimum tokens required to receive a specified amountOut.
- Path rules – Same as above; reverts on invalid path length or composition.
- Pricing – Considers current oracle price plus purchase fee for the incoming token.
- Reserve guard – Reverts
InsufficientLiquidity()ifamountOutexceeds current reserves of the outgoing asset. - Return value –
amounts[0] == required amountIn,amounts[1] == amountOut. This function is used byswapTokensForExactTokens
State-Changing Functions
Admin / Config
| Signature | Role | Effect |
|---|---|---|
setTokenReceiver(address) | ACCESS_CONTROL_MANAGER_ROLE | Redirects excess token withdrawals |
setFeeReceiver(address) | ACCESS_CONTROL_MANAGER_ROLE | Redirects fee outflows |
setApprovedSwappers(address[] addrs, bool flag) | WHITELISTER_ROLE | Batch grant / revoke APPROVED_SWAPPER |
setFeeBounds(uint256 min0,uint256 max0,uint256 min1,uint256 max1) | ACCESS_CONTROL_MANAGER_ROLE | Hard caps for future fee changes |
setTokenPurchaseFees(uint256 fee0,uint256 fee1) | FEE_SETTER_ROLE | Updates live purchase fees |
setPaused(bool) | PAUSER_ROLE | Global circuit‑breaker |
setOraclePriceBounds(uint256 minPrice,uint256 maxPrice,int256 minAPR,int256 maxAPR) | ACCESS_CONTROL_MANAGER_ROLE | Guardrails for oracle updates |
configureOraclePrice(uint256 basePrice,int256 annualizedInterestRate) | PRICE_SETTER_ROLE | Pushes new price and annualized interest rate |
Treasury
removeTokens
function removeTokens(address token, uint256 amount) external;
- Who / Role –
TOKEN_REMOVER_ROLE - Purpose – Withdraws surplus tokens (not counted as fees) to
tokenReceiverAddress. - Events –
RemoveTokens(token, amount) thenSync(reserve0,reserve1) - Reverts –
InsufficientTokens()– trying to pull more than surplus balanceInvalidTokenAddress()– token not part of the pair
collectFees
function collectFees(address token, uint256 amount) external;
- Who / Role –
TOKEN_REMOVER_ROLE - Purpose – Transfers accrued fees to
feeReceiverAddress. - Events –
RemoveTokens(token, amount) thenSync(...) - Reverts – Same conditions as
removeTokens, but checked against_FeesAccumulated.
Swapping APIs
swapExactTokensForTokens
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
High‑level "exact‑in" swap helper (router style).
- Flow
- Quotes output via internal math.
- Requires
amountOut ≥ amountOutMin; elseInsufficientOutputAmount(). - Pulls
amountInfrom caller viasafeTransferFrom. - Delegates to the low‑level
swap.
- Events – Propagates
Swap,SwapFees,Sync. - Reverts –
Expired(),InvalidPath*, liquidity or price‑related errors.
swapTokensForExactTokens
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
High‑level "exact‑out" variant.
- Checks – Requires
amountIn ≤ amountInMaxor revertsExcessiveInputAmount(). - Mirrors
swapExactTokensForTokensotherwise.
swap
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
Low‑level primitive that can perform flash‑swaps if data is non‑empty.
- Who / Role –
APPROVED_SWAPPER - Rules – Exactly one of
amount0Out,amount1Outmust be non‑zero; elseInvalidSwapAmounts(). - Callback – If
datais supplied,tomust implement UniswapV2CallbackInterface. - Invariant – Ensures required tokens plus fees are returned, else reverts
InsufficientInputAmount(). - Events –
SwapFees→Sync→Swap(in that order).
Errors
| Error | Trigger |
|---|---|
InvalidTokenAddress() | Token not in pair for collectFees/removeTokens |
InvalidPath() / InvalidPathLength() | Path must be [token0,token1] or reverse |
InvalidSwapAmounts() | Both amount0Out and amount1Out non‑zero or zero |
Expired() | Deadline passed in router helpers |
InsufficientLiquidity() | Reserves too low |
InvalidToken0PurchaseFee() / InvalidToken1PurchaseFee() | Fee outside configured bounds |
ExcessiveInputAmount() | amountIn exceeds caller‑supplied max |
InsufficientOutputAmount() | Quoted out < amountOutMin |
InsufficientInputAmount() | Fewer tokens in than required by pricing |
PairIsPaused() | Swaps disabled |
BasePriceOutOfBounds() / AnnualizedInterestRateOutOfBounds() | Oracle update outside guardrails |
MinBasePriceGreaterThanMaxBasePrice() | Invalid bounds |
MinAnnualizedInterestRateGreaterThanMax() | — |
MinToken0PurchaseFeeGreaterThanMax() / MinToken1PurchaseFeeGreaterThanMax() | Fee bounds inverted |
InsufficientTokens() | Treasury withdrawal exceeds balance |
IncorrectDecimals() | Init decimals mismatch with token contracts |
Interface
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
library AgoraStableSwapPair {
struct Version {
uint256 major;
uint256 minor;
uint256 patch;
}
}
interface IAgoraStableSwapPair {
struct InitializeParams {
address token0;
uint8 token0Decimals;
address token1;
uint8 token1Decimals;
uint256 minToken0PurchaseFee;
uint256 maxToken0PurchaseFee;
uint256 minToken1PurchaseFee;
uint256 maxToken1PurchaseFee;
uint256 token0PurchaseFee;
uint256 token1PurchaseFee;
address initialAdminAddress;
address initialWhitelister;
address initialFeeSetter;
address initialTokenRemover;
address initialPauser;
address initialPriceSetter;
address initialTokenReceiver;
address initialFeeReceiver;
uint256 minBasePrice;
uint256 maxBasePrice;
int256 minAnnualizedInterestRate;
int256 maxAnnualizedInterestRate;
uint256 basePrice;
int256 annualizedInterestRate;
}
error AddressIsNotRole(string role);
error AnnualizedInterestRateOutOfBounds();
error BasePriceOutOfBounds();
error CannotRemoveLastManager();
error ExcessiveInputAmount();
error Expired();
error IncorrectDecimals();
error InsufficientInputAmount();
error InsufficientLiquidity();
error InsufficientOutputAmount();
error InsufficientTokens();
error InvalidInitialization();
error InvalidPath();
error InvalidPathLength();
error InvalidSwapAmounts();
error InvalidToken0PurchaseFee();
error InvalidToken1PurchaseFee();
error InvalidTokenAddress();
error MinAnnualizedInterestRateGreaterThanMax();
error MinBasePriceGreaterThanMaxBasePrice();
error MinToken0PurchaseFeeGreaterThanMax();
error MinToken1PurchaseFeeGreaterThanMax();
error NotInitializing();
error PairIsPaused();
error ReentrancyGuardReentrantCall();
error RoleNameTooLong();
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
error SafeERC20FailedOperation(address token);
event ConfigureOraclePrice(uint256 basePrice, int256 annualizedInterestRate);
event Initialized(uint64 version);
event RemoveTokens(address indexed tokenAddress, uint256 amount);
event RoleAssigned(string indexed role, address indexed address_);
event RoleRevoked(string indexed role, address indexed address_);
event SetApprovedSwapper(address indexed approvedSwapper, bool isApproved);
event SetFeeBounds(
uint256 minToken0PurchaseFee,
uint256 maxToken0PurchaseFee,
uint256 minToken1PurchaseFee,
uint256 maxToken1PurchaseFee
);
event SetFeeReceiver(address indexed feeReceiver);
event SetOraclePriceBounds(
uint256 minBasePrice,
uint256 maxBasePrice,
int256 minAnnualizedInterestRate,
int256 maxAnnualizedInterestRate
);
event SetPaused(bool isPaused);
event SetTokenPurchaseFees(uint256 token0PurchaseFee, uint256 token1PurchaseFee);
event SetTokenReceiver(address indexed tokenReceiver);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event SwapFees(uint256 token0FeesAccumulated, uint256 token1FeesAccumulated);
event Sync(uint256 reserve0, uint256 reserve1);
function ACCESS_CONTROL_MANAGER_ROLE() external view returns (string memory);
function AGORA_ACCESS_CONTROL_STORAGE_SLOT() external view returns (bytes32);
function AGORA_STABLE_SWAP_STORAGE_SLOT() external view returns (bytes32);
function APPROVED_SWAPPER() external view returns (string memory);
function FEE_PRECISION() external view returns (uint256);
function FEE_SETTER_ROLE() external view returns (string memory);
function PAUSER_ROLE() external view returns (string memory);
function PRICE_PRECISION() external view returns (uint256);
function PRICE_SETTER_ROLE() external view returns (string memory);
function TOKEN_REMOVER_ROLE() external view returns (string memory);
function WHITELISTER_ROLE() external view returns (string memory);
function assignRole(string memory _role, address _newAddress, bool _addRole) external;
function basePrice() external view returns (uint256);
function calculatePrice(
uint256 _priceLastUpdated,
uint256 _timestamp,
int256 _perSecondInterestRate,
uint256 _basePrice
) external pure returns (uint256 _price);
function collectFees(address _tokenAddress, uint256 _amount) external;
function configureOraclePrice(uint256 _basePrice, int256 _annualizedInterestRate) external;
function feeReceiverAddress() external view returns (address);
function getAllRoles() external view returns (string[] memory _roles);
function getAmount0In(
uint256 _amount1Out,
uint256 _token0OverToken1Price,
uint256 _token1PurchaseFee
) external pure returns (uint256 _amount0In, uint256 _token1PurchaseFeeAmount);
function getAmount0Out(
uint256 _amount1In,
uint256 _token0OverToken1Price,
uint256 _token0PurchaseFee
) external pure returns (uint256 _amount0Out, uint256 _token0PurchaseFeeAmount);
function getAmount1In(
uint256 _amount0Out,
uint256 _token0OverToken1Price,
uint256 _token0PurchaseFee
) external pure returns (uint256 _amount1In, uint256 _token0FeeAmount);
function getAmount1Out(
uint256 _amount0In,
uint256 _token0OverToken1Price,
uint256 _token1PurchaseFee
) external pure returns (uint256 _amount1Out, uint256 _token1PurchaseFeeAmount);
function getAmountsIn(uint256 _amountOut, address[] memory _path) external view returns (uint256[] memory _amounts);
function getAmountsOut(uint256 _amountIn, address[] memory _path) external view returns (uint256[] memory _amounts);
function getPrice() external view returns (uint256 _currentPrice);
function getPrice(uint256 _timestamp) external view returns (uint256 _price);
function getPriceNormalized() external view returns (uint256 _normalizedPrice);
function getRoleMembers(string memory _role) external view returns (address[] memory _members);
function hasRole(string memory _role, address _address) external view returns (bool);
function implementationAddress() external view returns (address);
function initialize(InitializeParams memory _params) external;
function isPaused() external view returns (bool);
function maxAnnualizedInterestRate() external view returns (int256);
function maxBasePrice() external view returns (uint256);
function maxToken0PurchaseFee() external view returns (uint256);
function maxToken1PurchaseFee() external view returns (uint256);
function minAnnualizedInterestRate() external view returns (int256);
function minBasePrice() external view returns (uint256);
function minToken0PurchaseFee() external view returns (uint256);
function minToken1PurchaseFee() external view returns (uint256);
function name() external view returns (string memory);
function perSecondInterestRate() external view returns (int256);
function priceLastUpdated() external view returns (uint256);
function proxyAdminAddress() external view returns (address);
function removeTokens(address _tokenAddress, uint256 _amount) external;
function requireValidPath(address[] memory _path, address _token0, address _token1) external pure;
function reserve0() external view returns (uint256);
function reserve1() external view returns (uint256);
function setApprovedSwappers(address[] memory _approvedSwappers, bool _setApproved) external;
function setFeeBounds(
uint256 _minToken0PurchaseFee,
uint256 _maxToken0PurchaseFee,
uint256 _minToken1PurchaseFee,
uint256 _maxToken1PurchaseFee
) external;
function setFeeReceiver(address _feeReceiver) external;
function setOraclePriceBounds(
uint256 _minBasePrice,
uint256 _maxBasePrice,
int256 _minAnnualizedInterestRate,
int256 _maxAnnualizedInterestRate
) external;
function setPaused(bool _setPaused) external;
function setTokenPurchaseFees(uint256 _token0PurchaseFee, uint256 _token1PurchaseFee) external;
function setTokenReceiver(address _tokenReceiver) external;
function swap(uint256 _amount0Out, uint256 _amount1Out, address _to, bytes memory _data) external;
function swapExactTokensForTokens(
uint256 _amountIn,
uint256 _amountOutMin,
address[] memory _path,
address _to,
uint256 _deadline
) external returns (uint256[] memory _amounts);
function swapTokensForExactTokens(
uint256 _amountOut,
uint256 _amountInMax,
address[] memory _path,
address _to,
uint256 _deadline
) external returns (uint256[] memory _amounts);
function sync() external;
function token0() external view returns (address);
function token0Decimals() external view returns (uint8);
function token0FeesAccumulated() external view returns (uint256);
function token0PurchaseFee() external view returns (uint256);
function token1() external view returns (address);
function token1Decimals() external view returns (uint8);
function token1FeesAccumulated() external view returns (uint256);
function token1PurchaseFee() external view returns (uint256);
function tokenReceiverAddress() external view returns (address);
function version() external pure returns (AgoraStableSwapPair.Version memory _version);
}
ABI
The ABI of the AgorastableSwapPair is available here