Blueberry Bank Contract

aboutBlueberryBank.sol is a smart contract that implements a bank in which users can borrow and lend different tokens, as well as keep track of their positions. The contract includes the following functionalities:

  • Allowing borrowing, lending, and repaying different tokens

  • Tracking the state of each position and storing position data

  • Implement a borrowing limit for each user's position

  • Use an oracle to determine token prices

  • Implement a fee system for borrowing, lending, and deployment inside of the protocol

Functions

initialize

function initialize(ICoreOracle oracle_, IProtocolConfig config_) external initializer 

This function initializes the bank smart contract by setting the Oracle smart contract, the Protocol config address, and the Fee manager address. It also initializes some internal state variables. The function can only be called by the contract owner.

Parameters:

EXECUTOR

function EXECUTOR() external view override returns (address) 

This function returns the current executor of the smart contract, which is the owner of the current position. If there is no position under execution, it reverts with an error.

Parameters:

  • address - the address of the current position owner

setAllowContractCalls

function setAllowContractCalls(bool ok) external onlyOwner 

This function sets the allowContractCalls flag to allow or disallow contract calls. If allowContractCalls is set to true, then only externally-owned accounts (EOAs) can call the contract. This function can only be called by the contract owner.

Parameters

  • ok - The status to set allowContractCalls to. If false, only EOA can call the contract.

whitelistContract

whitelistContracts(address[] calldata contracts, bool[] calldata statuses) external onlyOwner

This function sets the status of the given contracts in the whitelist. The function can only be called by the contract owner.

Parameters:

whitelistSpells

whitelistSpells(address[] calldata spells, bool[] calldata statuses) external onlyOwner

This function sets the status of the given spells in the whitelist. The function can only be called by the contract owner.

Parameters:

whitelistTokens

whitelistTokens(address[] calldata tokens, bool[] calldata statuses) external onlyOwner

This function sets the status of the given tokens in the whitelist. It also checks whether the given tokens are supported by the Oracle smart contract. The function can only be called by the contract owner.

Parameters:

whitelistERC1155

whitelistERC1155(address[] memory tokens, bool ok) external onlyOwner

This function sets the status of the given ERC1155(wrapped tokens) in the whitelist. The function can only be called by the contract owner.

Parameters:

addBank

addBank(address token, address softVault, address hardVault, uint256 liqThreshold) external onlyOwner onlyWhitelistedToken(token)

This function adds a new bank to the ecosystem. It creates a new Bank struct and sets its properties, such as the underlying token for the bank and the address of the soft and hard vaults. It also checks whether the bToken for the soft vault has already been added to any other bank. If the bToken has already been added to a bank, it reverts with an error. The function can only be called by the contract owner.

Parameters:

setBankStatus

setBankStatus(uint256 _bankStatus) external onlyOwner

This function is used to set the bank status to a new value. Only callable by the owner

Parameters:

Modifiers

  • onlyOwner : Only the contract owner can call this function

Return

  • None

isBorrowerAllowed

function isBorrowAllowed() public view returns (bool) 

This function is used to check whether borrowing is allowed for the bank or not

Parameters:

  • None

Modifiers:

  • None

Return:

  • bool: Returns true if borrowing is allowed, false otherwise.

isRepayAllowed

function isRepayAllowed() public view returns (bool) 

This function is used to check whether repaying is allowed for the bank or not

Parameters:

  • None

Modifiers:

  • None

Return:

  • bool: Returns true if repaying is allowed, false otherwise.

isLendAllowed

function isLendAllowed() public view returns (bool) 

This function is used to check whether lending is allowed for the bank or not

Parameters:

  • None

Modifiers:

  • None

Return:

  • bool: Returns true if lending is allowed, false otherwise.

isWithdrawLendAllowed

function isWithdrawLendAllowed() public view returns (bool) 

This function is used to check whether withdrawing lending is allowed for the bank or not

Parameters:

  • None

Modifiers:

  • None

Return:

  • bool: Returns true if withdrawing lend is allowed, false otherwise.

accrue

function accrue(address token) public override 

This function is used to trigger interest accrual for the given bank.

Parameters:

accrueAll

function accrueAll(address[] memory token) external 

This function is used to trigger interest accrual for a list of banks.

Parameters:

Modifiers:

  • None

Return:

  • None

_borrowBalanceStored

function _borrowBalanceStored(address token) internal view returns (uint256)

This internal function is used to get the stored borrow balance for the given token

Parameters:

Modifiers:

  • None

Return:

  • uint256: The stored borrow balance for the given token.

currentPositionDebt

function currentPositionDebt(uint256 positionId) public view returns (uint256 debt)

This function returns the debt of a given position considering the debt interest stored.

Parameters:

Return:

  • debt uint256: The current debt balance of the specified position.

getPositionDebt

function getPositionDebt(uint256 positionId) public view returns (uint256 debt)

This function returns the debt of the specified position considering the debt interest stored. The function should be called after calling the accrue function to get the current debt.

Parameters:

Modifiers:

  • None

Returns:

  • debt (uint256): The debt balance of the specified position.

getBankInfo

function getBankInfo(address token) external view override returns (bool isListed, address bToken, uint256 totalShare)

This function returns the bank information for the specified token.

Parameters:

Modifiers:

  • None

Returns:

  • isListed (bool): True if the specified token is listed in the banks, otherwise false

  • bToken (address): The address of the associated bToken contract for the specified token

  • totalShare (uin256): The total share of the specified token in the bank

getPositionInfo

function getPositionInfo(uint256 positionId) external view override returns (Position memory)

This function returns the information of the specified position.

Parameters:

Modifiers:

  • None

Returns:

  • Position (struct): The information about the specified position

getCurrentPositionInfo

function getCurrenPositionInfo() external view override returns (Position memory)

This function returns the information about the current position

Returns:

  • Position (struct): The information about the current position

Modifiers:

  • if (POSITION_ID == _NO_ID): MODIFIES THE FUNCTION TO CHECK IF POSITION_ID is not equal to _NO_ID before executing the function.

getPositionValue

function getPositionValue(uint256 positionId) public view override returns (uint256 positionValue)

This function returns the USD value of the total collateral of the specified position considering yields generated from the collaterals.

Parameters

Return:

  • positionValue (uin256): The USD value of the total collateral of the specified position.

getDebtValue

function getDebtValue(uint256 positionId) public view override returns (uint256 debtValue)

This function should be called to get the current USD value of a position's debt. This function should be called after calling accrue() to get the current debt balance.

Parameters:

Return:

  • debtValue (uint256): The USD value of the position's debt

getIsolatedCollateralValue

function getIsolatedCollateralValue(uint256 positionId) public view override returns (uint256 icollValue)

This function returns the USD value of the isolated collateral of a given position. It takes into consideration the stored lending interest in the position. This function should be called after calling accrue() to get the current debt.

Parameters:

Return:

  • icollValue (uint256): The USD value of the position's collateral.

getPositionRisk

function getPositionRisk(uint256 positionId) public view returns (uint256 risk)

This function calculates the risk ratio of a given position, which represents the degree of risk associated with the position. The higher the risk ratio, the higher the risk. By taking into consideration the following factors:

  • pv (uint256): The position value

  • ov (uint256): The debt value

  • cv (uint256): The isolated collateral value

If the position is closed or overcollateralized, the risk is set to 0. If there is no isolated collateral or there is an error with the isolated underlying token, the risk is set to the maximum value of Constants.DENOMINATOR. Otherwise, the risk is calculated as (ov - pv) * Constants.DENOMINATOR / cv

Parameters:

Returns:

  • risk (uint256): The calculated risk ratio

isLiquidatable

function isLiquidatable(uint256 positionId) public view returns (bool)

This function checks whether a given position can be liquidated by first calculating its risk ratio using the getPositionRisk function. It then compares the risk ratio to the liquidation threshold defined in the oracle for the underlying token of the position. If the risk ratio is higher than or equal to the liquidation threshold, the position is considered liquidatable and the function returns true. Otherwise, the function returns false.

Parameters:

Returns:

  • bool: A boolean value indicating whether the position is liquidatable or not.

liquidate

function liquidate(uint256 positionId, address debtToken, uint256 amountCall) external override lock poke(debtToken) 

This function liquidates a position, paying off its debt for the original owner and taking the collateral of the position.

Parameters:

Details:

  1. Check if the repay operation is allowed

  2. Check if the amountCall parameter is non-zero

  3. Check if the position is liquidatable using the isLiquidatable function

  4. Retrieve the position and bank information

  5. Calculate the amount paid and the share of the debt to repay using the _repay function

  6. Calculate the liquidation size and the share of the underlying vault using the oldShare value, the collateralSize, and the underlyingValueShare fields of this position

  7. Update the collateralSize and underlyingVaultShare fields of the position

  8. Transfer the position (wrapped LP tokens) to the liquidator using the safeTransferFrom function of the ERC1155 token

  9. Transfer the underlying collateral (vault share tokens) to the liquidator using the safeTransfer function of the ERC20 token for a soft vault or the safeTransferFrom function of the ERC1155 token for a hard vault.

  10. Emit the positionId of the position liquidated, msg.sender the address of the liquidator, debtToken The token address that got repaid, amountPaid How much did the liquidator pay to the bank, share the share of the vault tokens, liqSize how large the liquidation was, and uVaultShare the underlying vault token share.

execute

function execute(uint256 positionId, address spell, bytes memory data) external lock onlyEOAEx returns (uint256)

This function allows the called to execute a spell with supplied data. The spell must be whitelisted before execution. If the positionID is zero, a new position will be created. If it is not zero, the function will check if the position exists, and is owned by the caller, and if not, it will revert. The function will set the global variables POSITION_ID and SPELL to the supplied positionID and spell, respectively. It will then execute the spell with the supplied data and handle any errors that may occur. Finally, it will check if the position is liquidatable and emit an event.

Modifiers

  • onlyEOAEx: The function can only be called by an externally-owned account that is executing a spell

Parameters

Return:

  • (uint256): The position ID that was executed and created.

lend

function lend(address token, uint256 amount) external override inExec poke(token) onlyWhitelistedToken(token)

This function allows the called to lend tokens to the bank as isolated collateral. The function must be called while under execution. The token must be whitelisted before lending.

Modifiers:

  • inExec: The function can only be called while under execution

  • poke(token): The function will poke the token before execution to ensure its balance and allowance is up-to-date

  • onlyWhitelistedToken(token): The function can only be called with a whitelisted token

Parameters

withdrawLend

function withdrawLend(address token, uint256 shareAmount) external override inExec poke(token)

This function withdraws isolated collateral tokens that were lent to the bank. It first checks if the withdrawLend function is allowed to execute. It then retrieves the Position storage and Bank memory objects from their respective mappings. If the token address does not match the underlying token address in the Position storage, the function throws an INVALID_UTOKEN error.

If shareAmount is equal to type(uint256).max, the function sets shareAmount to the underlyingVaultShare stored in the Position storage.

The function then calculates the amount to withdraw using either the softVault or hardVault. If _isSoftVault(token) returns true, the function calls the approve and withdraw functions of the ISoftVault interface. If it returns false, the function calls the withdraw function of the IHardVault interface.

After the tokens are withdrawn, the underlyingVaultShare stored in the Position storage is updated, the tokens are approved for transfer to the feeManager contract, and the withdrawal fee is taken by calling the doCutWithdrawFee function of the feeManager contract. Finally, the tokens are transferred to the caller.

Modifiers:

  • inExec: This modifier checks if the function is being called from the spell contract while under execution

  • poke: This modifier updates the state of the contract by calling the poke method with the given token address

Parameters:

borrow

function borrow(address token, uint256 amount) external override inExec poke(token) onlyWhitelistedToken(token) returns (uint256 borrowedAmount)

This function allows the caller to borrow tokens from the bank. It first checks if the borrow function is allowed to execute. It then retrieves the Bank storage and Position storage objects from their respective mappings. If the Position storage has no debt token set, it sets the token. This function can only be called from the spell contract while under execution

Modifiers:

  • inExec: This modifier checks if the function is being called from the spell contract while under execution

  • poke: This modifier updates the state of the contract by calling the poke method with the given token address

  • onlyWhitelistedToken: This modifier checks if the given token address is whitelisted

Parameters:

Return:

  • borrowedAmount (uint256): The number of tokens borrowed.

repay

function repay(address token, uint256 amountCall) external override inExec poke(token) onlyWhitelistedToken(token)

This function allows a user to repay a specific token to the bank. It should only be called during execution. Before executing the function, it checks whether the repay is allowed. If the repay is not allowed, it will revert with an error message.

The function then calls the internal function _repay() with the position ID, token, and amountCall as parameters. _repay() calculates the amount to be repaid and the corresponding debt share reduced. The function then emits a Repay event with the relevant parameters.

Modifiers:

  • inExec: This modifier checks if the function is being called from the spell contract while under execution

  • poke: This modifier updates the state of the contract by calling the poke method with the given token address

  • onlyWhitelistedToken: This modifier checks if the given token address is whitelisted

Parameters:

_repay

function _repay(uint256 positionId, address token, uint256 amountCall) internal returns (uint256, uint256)

This function is used to perform a repayment action on a given position ID, using the specified token. It returns the amount actually taken and the debt share reduced. The debt token of the position must match the specified token, otherwise, the function will revert. The amount to repay must not exceed the old debt, otherwise, the function will revert. The function must be called while under execution.

Parameters:

Returns:

putCollateral

function putCollateral(address collToken, uint256 collId, uint256 amountCall) external override inExec

This function allows users to put more collateral into their position. The function first checks if the collateral token being used is the same as the one already specified in the position. If not, it checks if the oracle supports the wrapped token of the LP address. If the collateral size of the position is greater than zero, it reverts. If both checks pass, the collateral token and ID are updated in the position.

The function then calls the internal _doERC1155TransferIn() function to transfer the ERC1155 tokens from the user's wallet to the contract. The amount of tokens transferred is added to the position's collateral size. An event PutCollateral() is then emitted to signify that collateral has been added to the position.

Modifiers:

  • inExec Can only be called during execution

Parameters:

takeCollateral

function takeCollateral(uint256 amount) external override inExec returns (uint256)

This function allows users to take some of their collateral back. It must only be called during execution. The function first retrieves the position from the positions mapping. If the amount to be taken back is set to the maximum uint256 value, it sets the amount to the full collateral size of the position. Then, it reduces the collateral size of the position by the amount being taken back.

The function then calls the safeTransferFrom() function of the IERC1155Upgradeable interface to transfer the ERC1155 tokens from the contract back to the user's wallet. An event TakeCollateral() is emitted to signify that collateral has been taken back from the position. The amount of tokens taken back is returned.

Modifiers:

  • inExec Can only be called during execution

Parameters:

  • amount (uint256): The amount of ERC1155 tokens to be taken back by transfer

_doBorrow

function _doBorrow(address token, uint256 amountCall) internal returns (uint256 borrowAmount)

This function is used to perform a borrow from a bank and returns the amount borrowed.

Parameters:

Returns:

  • borrowAmount (uint256): The amount actually borrowed from the bank

_doRepay

function _doRepay(address token, uint256 amountCall) internal returns (uint256 repaidAmount)

This function is used to perform a repay to the bank and returns the amount actually repaid.

Parameters:

Returns:

  • repaidAmount (uint256): The amount actually repaid to the bank

_doERC20TransferIn

function _doERC20TransferIn(address token, uint256 amountCall) internal returns (uint256)

This function is used to perform an ERC20 transfer-in and return the amount actually received.

Parameters:

Returns:

  • (uint256): The amount actually received from the transfer

_doERC1155TransferIn

function _doERC1155TransferIn(address token, uint256 id, uint256 amountCall) internal returns (uint256)

This function is used to perform an ERC1155 transfer-in and return the amount actually received.

Parameters:

Returns:

  • (uint256): The amount actually received from the transfer

_isSoftVault

function _isSoftVault(address token) internal view returns (bool)

This function checks whether the given token address corresponds to a soft vault or hard vault. The function accesses the banks mapping to retrieve the softVault address for the given token. It then calls the uToken() function of the ISoftVault interface to get the underlying token address of the soft vault. If the retrieved underlying token address is equal to the given token, then the vault is a soft vault and the function returns true. Otherwise, it returns false.

Parameters:

  • token (address): The token address to check

Returns:

  • bool : true if the vault is a soft vault, false if it is a hard vault

_ensureApprove

function _ensureApprove(address token, address spender, uint256 amount) internal

This function is used to reset the approval for spender to zero and set it again to amount. The function calls the approve function of the IERC20Upgradeable interface to reset the approval to zero and then calls it again to set the approval to amount

Parameters:

Last updated