# FyToken

## Introduction

An fyToken is an instantiation of a Hifi zero-coupon bond, with a specific configuration. It is pegged to a unique Erc20 collateral type, Erc20 underlying type and expiration time. The FyToken contract is itself an Erc20.

## Gas Costs

Gas usage is not deterministic due to requisite calls to third-party Erc20 tokens. We provide the table below for guidance only:

| Action              | Typical Gas Cost |
| ------------------- | ---------------- |
| Borrow              | <130K            |
| Liquidate Borrow    | <100K            |
| Repay Borrow        | <80K             |
| Repay Borrow Behalf | <80K             |

## Erc20 Functions <a href="#non-constant-functions" id="non-constant-functions"></a>

All [Erc20](https://eips.ethereum.org/EIPS/eip-20) functions are implemented: `allowance()`, `balanceOf()`, `decimals()`, `name()`, `symbol()`, `totalSupply()`, `approve()`, `transfer()`and `transferFrom().`

## Erc2612 Functions

All [Erc2612](https://eips.ethereum.org/EIPS/eip-2612) functions are implemented: `permit()`, `nonces()` and `DOMAIN_SEPARATOR()`. Erc2612 allows users to permit another account (or contract) to use their funds using a signed message. This enables gas-less transactions and single approval/transfer transactions.

## Storage Properties

### Balance Sheet

The address of the BalanceSheet contract, the global debt registry of Hifi.

```javascript
BalanceSheetInterface public balanceSheet;
```

### Collateral

The Erc20 asset that backs the borrows of this fyToken.

```javascript
Erc20Interface public collateral;
```

### Collateral Precision Scalar

The ratio between mantissa precision (1e18) and the collateral precision.

```javascript
uint256 public collateralPrecisionScalar;
```

### Expiration Time

Unix timestamp in seconds for when this token expires.

```javascript
uint256 public expirationTime;
```

### Fintroller

The unique Fintroller associated with this contract.

```javascript
FintrollerInterface public fintroller;
```

### Redemption Pool

The unique Redemption Pool associated with this contract.

```javascript
RedemptionPoolInterface public redemptionPool;
```

### Underlying

The Erc20 underlying, or target, asset for this fyToken.

```javascript
Erc20Interface public underlying;
```

### Underlying Precision Scalar

The ratio between mantissa precision (1e18) and the underlying precision.

```javascript
uint256 public underlyingPrecisionScalar;
```

## Constant Functions <a href="#non-constant-functions" id="non-constant-functions"></a>

### Is Matured <a href="#getstream" id="getstream"></a>

Checks if the bond matured.

```javascript
function isMatured() public view returns (bool)
```

* `RETURN`: true = bond matured, otherwise it didn't.

#### Solidity

```javascript
FyTokenInterface fyToken = FyTokenInterface(0xabcd...);
uint256 isMatured = fyToken.isMatured();
```

#### Ethers.js

```javascript
const fyToken = new ethers.Contract(0xabcd..., fyTokenInterfaceABI, signer);
const isMatured = await fyToken.isMatured();
```

## Non-Constant Functions <a href="#non-constant-functions" id="non-constant-functions"></a>

### Borrow <a href="#getstream" id="getstream"></a>

Increases the debt of the caller and mints new fyToken. Emits a "Borrow" event.

```javascript
function borrow(uint256 borrowAmount) public returns (bool)
```

* `borrowAmount`: The amount of fyTokens to borrow and print into existence.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* The vault must be open.
* Must be called prior to maturation.
* The amount to borrow cannot be zero.
* The "Fintroller" must allow this action to be performed.
* The locked collateral cannot be zero.
* The total supply of fyTokens cannot exceed the debt ceiling.
* The caller must not fall below the threshold collateralization ratio.
  {% endhint %}

#### Solidity <a href="#solidity-5" id="solidity-5"></a>

```javascript
FyTokenInterface fyToken = FyTokenInterface(0xabcd...);
uint256 borrowAmount = 10000000000000000000;
bool success = fyToken.borrow(borrowAmount);
require(success, "something went wrong");
```

#### Ethers.js <a href="#ethers-js-5" id="ethers-js-5"></a>

```javascript
const fyToken = new ethers.Contract(0xabcd..., fyTokenInterfaceABI, signer);
const borrowAmount = "10000000000000000000";
const borrowTx = await fyToken.borrow(borrowAmount);
await borrowTx.wait();
```

### Burn <a href="#getstream" id="getstream"></a>

Destroys `burnAmount` tokens from `holder`, reducing the token supply. Emits a "Burn" event.

```javascript
function burn(address holder, uint256 burnAmount) external returns (bool)
```

* `holder`: The account whose fyTokens to burn.
* `burnAmount`: The amount of fyTokens to burn.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* Must be called prior to maturation.
* Can only be called by the "RedemptionPool".
* The amount to burn cannot be zero.
  {% endhint %}

{% hint style="info" %}
We didn't write code snippets for this function because it is not meant to be called directly. Only the "RedemptionPool" contract is allowed to call "burn".
{% endhint %}

### Liquidate Borrow <a href="#getstream" id="getstream"></a>

Repays the debt of the borrower and rewards the liquidator with a surplus of collateral. Emits a "LiquidateBorrow" event.

```javascript
function liquidateBorrow(address borrower, uint256 repayAmount) external override returns (bool)
```

* `borrower`: The account to liquidate.
* `repayAmount`: The amount of fyTokens to repay.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* The vault must be open.
* The liquidator cannot liquidate themselves.
* The amount to repay cannot be zero.
* The "Fintroller" must allow this action to be performed.
* The borrower must be underwater if the bond didn't mature.
* The caller must have at least "repayAmount" fyTokens.
* The borrower must have at least "repayAmount" debt.
* The amount of clutched collateral cannot be more than what the borrower has in the vault.
  {% endhint %}

#### Solidity <a href="#solidity-5" id="solidity-5"></a>

```javascript
FyTokenInterface fyToken = FyTokenInterface(0xabcd...);
address borrower = 0xbcde...;
uint256 repayAmount = 10000000000000000000;
bool success = fyToken.liquidateBorrow(borrower, repayAmount);
require(success, "something went wrong");
```

#### Ethers.js <a href="#ethers-js-5" id="ethers-js-5"></a>

```javascript
const fyToken = new ethers.Contract(0xabcd..., fyTokenInterfaceABI, signer);
const borrower = 0xbcde...;
const repayAmount = "10000000000000000000";
const liquidateBorrowTx = await fyToken.liquidateBorrow(borrower, repayAmount);
await liquidateBorrowTx.wait();
```

### Mint <a href="#getstream" id="getstream"></a>

Prints new tokens into existence and assigns them to `beneficiary`, increasing the total supply. Emits a "Mint" event.

```javascript
function mint(address beneficiary, uint256 mintAmount) external override returns (bool)
```

* `beneficiary`: The borrower account for which to mint the tokens.
* `mintAmount`: The amount of fyTokens to print into existence.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* Can only be called by the Redemption Pool.
* The amount to mint cannot be zero.
  {% endhint %}

{% hint style="info" %}
We didn't write code snippets for this function because it is not meant to be called directly. Only the "RedemptionPool" contract is allowed to call "burn".
{% endhint %}

### Repay Borrow <a href="#getstream" id="getstream"></a>

Deletes the borrower account's debt from the registry and take the fyTokens out of circulation. Emits a "RepayBorrow" event.

```javascript
function repayBorrow(uint256 repayAmount) external override returns (bool)
```

* `repayAmount` : The amount of fyTokens to repay.
* `RETURN`
  * true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* The vault must be open.
* The amount to repay cannot be zero.
* The Fintroller must allow this action to be performed.
* The caller must have at least `repayAmount` fyTokens.
* The caller must have at least `repayAmount` debt.
  {% endhint %}

#### Solidity <a href="#solidity-5" id="solidity-5"></a>

```javascript
FyTokenInterface fyToken = FyTokenInterface(0xabcd...);
uint256 repayAmount = 10000000000000000000;
bool success = fyToken.repayBorrow(repayAmount);
require(success, "something went wrong");
```

#### Ethers.js

```javascript
const fyToken = new ethers.Contract(0xabcd..., fyTokenInterfaceABI, signer);
const repayAmount = "10000000000000000000";
const repayBorrowTx = await fyToken.repayBorrow(repayAmount);
await repayBorrowTx.wait();
```

### Repay Borrow Behalf <a href="#getstream" id="getstream"></a>

Clears the borrower account's debt from the registry and take the fyTokens out of circulation. Emits a "RepayBorrow" event.

```javascript
function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (bool)
```

* `borrower`: The borrower account for which to repay the borrow.
* `repayAmount`: The amount of fyTokens to repay.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* same as the "repayBorrow" function, but here "msg.sender" is the account that must have at least "repayAmount" fyTokens to repay the borrow for the "borrower".
  {% endhint %}

#### Solidity <a href="#solidity-5" id="solidity-5"></a>

```javascript
FyTokenInterface fyToken = FyTokenInterface(0xabcd...);
address borrower = 0xbcde...;
uint256 repayAmount = 10000000000000000000;
bool success = fyToken.repayBorrowBehalf(borrower, repayAmount);
require(success, "something went wrong");
```

#### Ethers.js <a href="#ethers-js-5" id="ethers-js-5"></a>

```javascript
const fyToken = new ethers.Contract(0xabcd..., fyTokenInterfaceABI, signer);
const borrower = 0xbcde...;
const repayAmount = "10000000000000000000";
const repayBorrowBehalfTx = await fyToken.repayBorrowBehalf(borrower, repayAmount);
await repayBorrowBehalfTx.wait();
```

### Set Fintroller <a href="#getstream" id="getstream"></a>

Updates the Fintroller contract's address saved in storage. Emits a "SetFintroller" event.

```javascript
function _setFintroller(FintrollerInterface newFintroller) external returns (bool)
```

* `newFintroller`: The address of the new Fintroller contract.
* `RETURN`: true = success, otherwise it reverts.

{% hint style="warning" %}
Requirements

* The caller must be the admin.
* The new Fintroller must pass the inspection.
  {% endhint %}

{% hint style="info" %}
We didn't write code snippets for this function because it is not meant to be called by end users. Only the protocol admin is allowed to call "setFintroller".
{% endhint %}
