# Smart Contract Integration

## 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).

```solidity
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, 2023
address 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.
enum Principals {
   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).

```solidity
address lender = 0x8dF84b03F73a680E04b0A59EE173219026333107;

// This example is for USDC-MAR23 market, lending on Notional
uint8 principal = 8; // Notional's enum value from the MarketPlace contract
uint256 maturity = 1680393600; // ~April 02, 2023
address underlying = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC
uint256 amount = 100000000; // $100 USDC
uint256 minReceived = 90000000; // Slippage control: receive at least 90 iPTs

// First, approve the Lender to spend the user's underlying
IERC20(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.

```solidity
address lender = 0x8dF84b03F73a680E04b0A59EE173219026333107;

// In this example, let's assume the user has Swivel's zcToken for this market
uint8 principal = 1; // Swivel's enum value from MarketPlace contract
uint256 maturity = 1680393600; // ~April 02, 2023
address underlying = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC
uint256 amount = 100000000; // $100 zcTokens
address zcToken = 0x3476303e9038833AeC9ccCd12747BD0E0d026a8B; // Swivel's PT

// First, approve the Lender to spend the user's principal token
IERC20(zcToken).approve(lender, amount);

// Wrap the zcToken in an iPT. The user will receive iPTs 1:1 for each PT sent 
// to the Lender
ILender(lender).mint(principal, underlying, maturity, amount);

```

## Redeeming

Once a market matures, users can `redeem` the underlying asset via the Redeemer contract.

{% hint style="warning" %}
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.
{% endhint %}

```solidity
address redeemer = 0x7690e18b7c7BE861976fCBb8E5053D2a2ebaB1AD;

// Get the market that has matured
uint256 maturity = 1680393600; // ~April 02, 2023
address underlying = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC

// Execute the redemption
IRedeemer(redeemer).redeem(underlying, maturity);
```

## Swapping

Prior to maturity, users may swap their iPTs for the underlying, and vise versa via a Yield Space Pool.

```solidity
address marketPlace = 0x9A74762723685c5EEE2f94b80a427dE1bf029426;

// Get the market that will be used to swap on
address iPT = 0x49494b3CB41829011471A059a72A16652D95aD6f; // Illuminate's principal token
uint256 maturity = IERC5095(iPT).maturity(); // e.g. 1680393600 (~April 02, 2023)
address underlying = IERC5095(iPT).underlying(); // e.g. USDC
uint256 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 underlying
IERC20(underlying).approve(marketPlace, amount);

// Conduct the swap
IMarketPlace(marketPlace).buyPrincipalToken(underlying, maturity, amount, slippage);

-----------------------------------------

// Second example: swap iPTs for underlying
// First, approve the MarketPlace contract to spend the iPT
IERC20(iPT).approve(marketPlace, amount);

// Conduct the swap
IMarketPlace(marketPlace).sellPrincipalToken(underlying, maturity, amount, slippage);
```

{% hint style="info" %}
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.
{% endhint %}

## Swapping with EIP4626 (EIP5095) Interfaces

Prior to maturity, users may swap their iPTs for the underlying, and vise versa via a Yield Space Pool.

```solidity
// Get the market that will be used to swap on
address iPT = 0x49494b3CB41829011471A059a72A16652D95aD6f; // Illuminate's principal token
uint256 maturity = IERC5095(iPT).maturity(); // e.g. 1680393600 (~April 02, 2023)
address underlying = IERC5095(iPT).underlying(); // e.g. USDC
uint256 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 underlying
IERC20(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.

```solidity
// The Lender contract stores the halted variable, which stops the
// entire Illuminate protocol.
address lender = 0x8dF84b03F73a680E04b0A59EE173219026333107;

// Fetch the halted flag
bool 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 paused
uint256 maturity = 1680393600; // ~April 02, 2023
address underlying = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // USDC
address 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);

```
