Integrate Magic-Fi as a liquidity source on EVM
🎇Contracts
Two main contracts of Magic-Fi's swap function are as follows:
MarblePPV2_1.sol
: the main swap contract that handles the logic operation, including setting the token info, calculating slippage, calculating the exchange amount and executing trades. it stores all tokens that are supported for trading. In this contract, it defines quote token and base token. Quote token is the reference token (i.e. stablecoins) in the contract and there is only one quote token. The contract can have multiple base tokens and they can be added by the strategist.MarbleRouterV2.sol
: the router contract that frontend users interact with. It interacts with the lower layerMarblePPV2.sol
to execute the sell logic and send back users the desired amount of tokens. This contract also implements the logic to route user orders to 3rd party aggregator (e.g. 1inch) whenMarblePPV2.sol
does not have sufficient liquidity.
🎇Supported assets
Adding IntegrationHelper.sol
MarblePPV2_1.sol
has one quote token which is typically the dominant stablecoin of each chain and multiple base tokens. A IntegrationHelper.sol
contract is deployed on each supported chain which contains the address of the quote token and the list of tradable base token addresses in MarblePPV2_1.sol
.
The latest list of supported tokens can be retrieved via the getSupportTokens()
function which will return two values:
first value: quoteToken address
second value: array of baseToken addresses
🎇Integrate Magic-Fi as a liquidity source
When integrating Magic-Fi as a liquidity source, you can either interact with MagRouterV2.sol
or MarblePPV2_1.sol
.
Integrating MarblePPV2_1.sol
MarblePPV2_1.sol
Another way is to integrate directly with MarblePPV2_1.sol
. This approach is slightly more gas efficient, but it requires writing the smart contract code and manually send the fromToken
to MarblePPV2_1.sol
, which a better choice for apps that already have an aggregation logic.
Contract addresses:
Same address across Sonic, Arbitrum, Avalanche, BSC, Optimism, Polygon PoS, Linea, Base, Mantle:
0xEd9e3f98bBed560e66B89AaC922E29D4596A9642
zkSync Era:
0xE656d70bc3550e3EEE9dE7dC79367A44Fd13d975
Interface
Copy
/// @notice The quote token address (immutable).
/// @return address of quote token
function quoteToken() external view returns (address);
/// @notice Gets the pool size of the specified token (swap liquidity).
/// @param token the token address
/// @return the pool size
function poolSize(address token) external view returns (uint256);
/// @notice Query the amount to swap `fromToken` to `toToken`, without checking the pool reserve balance.
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of `fromToken` to swap
/// @return toAmount the swapped amount of `toToken`
function tryQuery(
address fromToken,
address toToken,
uint256 fromAmount
) external view returns (uint256 toAmount);
/// @notice Query the amount to swap `fromToken` to `toToken`, with checking the pool reserve balance.
/// @dev tx reverts when 'toToken' balance is insufficient.
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of `fromToken` to swap
/// @return toAmount the swapped amount of `toToken`
function query(
address fromToken,
address toToken,
uint256 fromAmount
) external view returns (uint256 toAmount);
/// @notice Swap `fromToken` to `toToken`.
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of `fromToken` to swap
/// @param minToAmount the minimum amount of `toToken` to receive
/// @param to the destination address
/// @param rebateTo the rebate address (optional, can be address ZERO)
/// @return realToAmount the amount of toToken to receive
function swap(
address fromToken,
address toToken,
uint256 fromAmount,
uint256 minToAmount,
address to,
address rebateTo
) external returns (uint256 realToAmount);
Sample code
Copy
IMagPPV2 public magPool; // IMagPPV2 smart contract
function sampleFunc(...) external {
... your business code ..
// query the trade
address fromToken = WBTC_ADDR;
address toToken = USDT_ADDR;
uint256 amountIn = 10000000; // 0.1 btc
uint256 amountOut = magPool.query(fromToken, toToken, amountIn);
// or magPool.tryQuery if you don't need the swap.
// swap
IERC20(toToken).safeTransfer(address(magPool), amountIn);
uint256 realToAmount = magPool.swap(
fromToken,
toToken,
amountIn,
amountOut * 99 / 100,
address(this),
address(0)
);
... other business code to deal with `toToken` ...
}
🎇Price check with Magoracle
Magic-Fi's oracle has a few price check implemented, find the addresses of the magoracle contract in On-chain price feeds.
checking against the 3rd party oracle price e.g. ChainLink and Pyth
set the price range a swap can be executed against the current price
Integrating MarbleRouterV2.sol
MarbleRouterV2.sol
The straightforward way is interacting with MarbleRouterV2.sol
contract which provides the query and swap for any two specified tokens. It streamlined the logic of swapping native blockchain coin and swapping between any of the two supported assets, which simplifies the integration for apps that do not have an existing aggregation logic.
Contract addresses:
Same address across Sonic, Arbitrum, Avalanche, BSC, Optimism, Polygon PoS, Linea, Base, Mantle :
0x4c4AF8DBc524681930a27b2F1Af5bcC8062E6fB7
zkSync:
0x09873bfECA34F1Acd0a7e55cDA591f05d8a75369
Interface
Copy
/// @dev query the amount to swap fromToken -> toToken
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of fromToken to swap
/// @return toAmount the predicted amount to receive
function querySwap(
address fromToken,
address toToken,
uint256 fromAmount
) external view returns (uint256 toAmount);
/// @dev query the amount to swap fromToken -> toToken,
/// WITHOUT checking the reserve balance; so it
/// always returns the quoted amount (for reference).
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of fromToken to swap
/// @return toAmount the predicted amount to receive
function tryQuerySwap(
address fromToken,
address toToken,
uint256 fromAmount
) external view returns (uint256 toAmount);
/// @notice Swap `fromToken` to `toToken`.
/// @param fromToken the from token
/// @param toToken the to token
/// @param fromAmount the amount of `fromToken` to swap
/// @param minToAmount the minimum amount of `toToken` to receive
/// @param to the destination address
/// @param rebateTo the rebate address (optional, can be 0)
/// @return realToAmount the amount of toToken to receive
function swap(
address fromToken,
address toToken,
uint256 fromAmount,
uint256 minToAmount,
address payable to,
address rebateTo
) external payable returns (uint256 realToAmount);
Sample code
Copy
// Query Part
address fromToken = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f; // wbtc
address toToken = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; // usdc
uint256 amount1 = magRouter.querySwap(
fromToken,
toToken,
100000000 // 1 wbtc amount in decimal 8
);
uint256 amount2 = magRouter.tryQuerySwap(
fromToken,
toToken,
100000000); // 1 wbtc amount in decimal 8
// NOTE: when the balance reserve is enough for swap, amount1 = amount2;
// otherwise, amount2 is still calculated based on magoracle price and magic market-making
// formula, but the amount1 qeury simply failed with insufficient balance error.
// Swap Part
uint256 amountIn = 100000000;
IERC20(fromToken).safeApprove(address(magRouter), amountIn);
uint256 realToAmount = magRouter.swap(
fromToken, // wbtc token address
toToken, // usdc token address
amountIn, // wbtc amount to swap in decimal 8
19000000000, // min amount = 19000 usdc (price: 19500)
address(this), // the address to receive the swap fund
address(0) // the rebateTo address
);
Last updated