This page describes how to integrate Illuminate into your smart contracts with examples.
Looking up markets
We can begin with finding supported tokens and pools within an Illuminate market. A market is defined by a tuple of underlying (address) and maturity (uint256).
address marketPlace =0x9A74762723685c5EEE2f94b80a427dE1bf029426;// To look up a market, you will need the maturity and underlying for a given market.// In this example, we'll look up information about the USDC-MAR23 market.uint256 maturity =1680393600; // ~April 02, 2023address underlying =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC// First, we'll look up the supported principal tokens for the market.// To do so, call the markets method on the MarketPlace contract. This method takes// in an underlying, maturity and principal enum. The enum value, defined below, // returns the PT for a given market. A null address is returned if there is no PT// for that particular market.enumPrincipals { Illuminate,// 0 Swivel,// 1 Yield,// 2 Element,// 3 Pendle,// 4 Tempus,// 5 Sense,// 6 Apwine,// 7 Notional // 8}// To look up Pendle's PT for the USDC-MAR23 market:address pendlePT =IMarketPlace(marketPlace).markets(underlying, maturity,4);// Additionally, we can get the Yield Space Pool for the Illuminate principal token// by calling the pools method:address iptPool =IMarketPlace(marketPlace).pools(underlying, maturity);
Lending
The Lender contract provides convenience lend methods that swap between the underlying and supported PTs. Each protocol has a lend method that results in users receiving Illuminate principal tokens (iPTs).
address lender =0x8dF84b03F73a680E04b0A59EE173219026333107;// This example is for USDC-MAR23 market, lending on Notionaluint8 principal =8; // Notional's enum value from the MarketPlace contractuint256 maturity =1680393600; // ~April 02, 2023address underlying =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDCuint256 amount =100000000; // $100 USDCuint256 minReceived =90000000; // Slippage control: receive at least 90 iPTs// First, approve the Lender to spend the user's underlyingIERC20(underlying).approve(lender, amount);// Second, lend the underlying via Illuminate. // The amount of iPTs is returned by the lend method uint256 received =ILender(lender).lend(principal, maturity, underlying, amount, minReceived);
Minting
If a user already has PTs, they can wrap them into iPTs via the Lender contract's mint method.
address lender =0x8dF84b03F73a680E04b0A59EE173219026333107;// In this example, let's assume the user has Swivel's zcToken for this marketuint8 principal =1; // Swivel's enum value from MarketPlace contractuint256 maturity =1680393600; // ~April 02, 2023address underlying =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDCuint256 amount =100000000; // $100 zcTokensaddress zcToken =0x3476303e9038833AeC9ccCd12747BD0E0d026a8B; // Swivel's PT// First, approve the Lender to spend the user's principal tokenIERC20(zcToken).approve(lender, amount);// Wrap the zcToken in an iPT. The user will receive iPTs 1:1 for each PT sent // to the LenderILender(lender).mint(principal, underlying, maturity, amount);
Redeeming
Once a market matures, users can redeem the underlying asset via the Redeemer contract.
Execution of the redemption should only be done after the Redeemer has redeemed the supported principal tokens from the Lender contract. Redeeming prior to this will result in lost funds.
address redeemer =0x7690e18b7c7BE861976fCBb8E5053D2a2ebaB1AD;// Get the market that has matureduint256 maturity =1680393600; // ~April 02, 2023address underlying =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC// Execute the redemptionIRedeemer(redeemer).redeem(underlying, maturity);
Swapping
Prior to maturity, users may swap their iPTs for the underlying, and vise versa via a Yield Space Pool.
address marketPlace =0x9A74762723685c5EEE2f94b80a427dE1bf029426;// Get the market that will be used to swap onaddress iPT =0x49494b3CB41829011471A059a72A16652D95aD6f; // Illuminate's principal tokenuint256 maturity =IERC5095(iPT).maturity(); // e.g. 1680393600 (~April 02, 2023)address underlying =IERC5095(iPT).underlying(); // e.g. USDCuint256 amount =100000000; // $100 USDC (or iPTs, which have the same # of decimals)uint256 slippage =101000000; // Minimum number of tokens to receive in the swap// First example: swap underlying for iPTs// First, approve the MarketPlace contract to spend the underlyingIERC20(underlying).approve(marketPlace, amount);// Conduct the swapIMarketPlace(marketPlace).buyPrincipalToken(underlying, maturity, amount, slippage);-----------------------------------------// Second example: swap iPTs for underlying// First, approve the MarketPlace contract to spend the iPTIERC20(iPT).approve(marketPlace, amount);// Conduct the swapIMarketPlace(marketPlace).sellPrincipalToken(underlying, maturity, amount, slippage);
Note that there are two other methods that can be used to faciliate swaps: buyUnderlying and sellUnderlying. These methods provide different slippage configurations, and require a similar flow to execute.
Swapping with EIP4626 (EIP5095) Interfaces
Prior to maturity, users may swap their iPTs for the underlying, and vise versa via a Yield Space Pool.
// Get the market that will be used to swap onaddress iPT =0x49494b3CB41829011471A059a72A16652D95aD6f; // Illuminate's principal tokenuint256 maturity =IERC5095(iPT).maturity(); // e.g. 1680393600 (~April 02, 2023)address underlying =IERC5095(iPT).underlying(); // e.g. USDCuint256 amount =100000000; // $100 USDC (or iPTs, which have the same # of decimals)uint256 slippage =101000000; // Minimum number of tokens to receive in the swap// First example: swap underlying for iPTs// First, approve the iPT contract to spend the underlyingIERC20(underlying).approve(iPT, amount);// Conduct the swap through EIP5095 interfaces -- // Spends `amount` on iPTs through a YieldSpace pool, receiving at minimum `slippage`IERC5095(iPT).deposit(address(this), amount, slippage);
Checking for Paused States
Integrated protocols are subject to being paused on a principal or market basis by the admin of each respective contract. Below, we demonstrate how to check the paused state of the contracts.
// The Lender contract stores the halted variable, which stops the// entire Illuminate protocol.address lender =0x8dF84b03F73a680E04b0A59EE173219026333107;// Fetch the halted flagbool isIlluminateHalted =ILender(lender).halted();// To determine if a particular protocol is stopped, check the paused mapping.// This mapping uses the enum mapping from the MarketPlace to define the protocol.// In this example, we check if Sense is paused on Illuminate.bool isSensePaused =ILender(lender).paused(6);// The MarketPlace may also pause a market, by setting the iPT address to 0 via the// setPrincipal call. To check if a market is paused, call markets.address marketPlace =0x9A74762723685c5EEE2f94b80a427dE1bf029426;// In this example, we'll check if the USDC-MAR23 market has been pauseduint256 maturity =1680393600; // ~April 02, 2023address underlying =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDCaddress isUSDCMAR23Paused =IMarketPlace(marketPlace).markets(underlying, maturity,0);requre(isUSDCMAR23Paused !=address(0),'market paused');// In addition, the Redeemer contract can pause the redemption of iPT on a market// basis. This can be looked up via the maturity and underlying pair.address redeemer =0x7690e18b7c7BE861976fCBb8E5053D2a2ebaB1AD;// In this example, we'll look up the USDC-MAR23 market from above.bool isPTRedemptionPaused =IRedeemer(redeemer).paused(underlying, maturity);