Skip to content

Commit d98bb99

Browse files
lastpersonviatrix
andauthored
feat: use CreateX to have deterministic deployment addresses across all chains. (#20)
* Use CreateX for deployments * Fix lint * Cleanup and add scripts test * Refactor and simplify tasks usage. * Transfer admin rights after deployment and cleanup * Fix lint * Add comments, WIP * Add Rebalancer comments * Add extra pool config * Add extra pool admin renounce * Update rebalancer comments * Add comments for liquidity pools * Move config from env to network config * Update config for liquidity pool * Update addresses in config * Add Hub config * Fix typos --------- Co-authored-by: viatrix <[email protected]>
1 parent e3694ee commit d98bb99

29 files changed

+977
-332
lines changed

.env.example

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,27 @@
1-
# Contracts admin.
2-
ADMIN=
3-
# Address that can increase/decerease LP conversion rate.
4-
ADJUSTER=
5-
# Deposits to Liquidity Hub are only allowed till this limit is reached.
6-
ASSETS_LIMIT=
7-
# Liquidity mining tiers. Multiplier will be divided by 1000,000,000. So 1750000000 will result in 1.75x.
8-
# There is no limit to the number of tiers, but has to be atleast one.
9-
TIER_1_SECONDS=7776000
10-
TIER_1_MULTIPLIER=300000000
11-
TIER_2_SECONDS=15552000
12-
TIER_2_MULTIPLIER=800000000
13-
TIER_3_SECONDS=31104000
14-
TIER_3_MULTIPLIER=1666666667
15-
# Rebalance caller.
16-
REBALANCE_CALLER=
17-
# Liquidity Pool parameters.
18-
# Value 500 will result in health factor 5.
19-
MIN_HEALTH_FACTOR=500
20-
# Value 20 will result in LTV 20%.
21-
DEFAULT_LTV=20
22-
MPC_ADDRESS=
23-
WITHDRAW_PROFIT=
24-
PAUSER=
251
# General deployment parameters.
26-
DEPLOYER_PRIVATE_KEY=
2+
# Deploy id + the deployer address acts as a key to determine deployed contract addresses.
3+
# It should only be set once and never change, unless you are planning to redeploy everything.
4+
DEPLOY_ID=MVP
5+
6+
# Upgrade id + the deployer address acts as a key to determine address of new versions of implementations
7+
# or instances. It should be set for a particular release, when upgrade scripts are executed across all chains.
8+
UPGRADE_ID=V1
9+
10+
# Deployer address is used to determine deployed contract addresses when the script/task is executed
11+
# with a key that is different from original deployer (eg. admin).
12+
DEPLOYER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
13+
14+
# Private key that is going to sign transactions be that deployment or other scripts/tasks.
15+
PRIVATE_KEY=
16+
2717
VERIFY=false
2818
BASE_SEPOLIA_RPC=
2919
ETHEREUM_SEPOLIA_RPC=
3020
ARBITRUM_SEPOLIA_RPC=
3121
ETHERSCAN_BASE_SEPOLIA=
3222
ETHERSCAN_ETHEREUM_SEPOLIA=
3323
ETHERSCAN_ARBITRUM_SEPOLIA=
34-
# Upgrade and configuration update parameters.
35-
LIQUIDITY_POOL=
36-
REBALANCER=
24+
3725
# Testing parameters.
3826
FORK_PROVIDER=https://eth-mainnet.public.blastapi.io
3927
USDC_OWNER_ADDRESS=0x7713974908Be4BEd47172370115e8b1219F4A5f0

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ jobs:
2424
run: npm run compile
2525
- name: Hardhat Tests
2626
run: npm run test
27-
- name: Deploy Tests
28-
run: npm run test:deploy
27+
- name: Script Tests
28+
run: npm run test:scripts

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ Local deployment wallet private key is: 0xac0974bec39a17e36ba4a6b4d238ff944bacb4
2424

2525
To deploy to live networks, create a `.env` file using the `.env.example` and fill in the relevant variables (only the ones needed for your deployment).
2626
You need to have a private key specified.
27+
Inspect and modify if needed the `network.config.ts`.
2728
To deploy to Base Testnet do:
2829

2930
npm run deploy-basetest
3031

3132
Make sure to save the output of the deployment. You can use those later in the `.env` file to run other scripts on the already deployed system.
3233

33-
You could optionally set VERIFY to `true` in order to publish the source code after deployemnt to sourcify.dev.
34+
You could optionally set VERIFY to `true` in order to publish the source code after deployment to Etherscan.

contracts/LiquidityHub.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ import {IManagedToken} from "./interfaces/IManagedToken.sol";
1616
import {ILiquidityPool} from "./interfaces/ILiquidityPool.sol";
1717
import {ILiquidityHub} from "./interfaces/ILiquidityHub.sol";
1818

19+
/// @title A modified version of the ERC4626 vault with the following key differences:
20+
/// 1. The shares token functionality is delegated to a dedicated token contract.
21+
/// 2. The total assets variable cannot be increased by a donation, making inflation by users impossible.
22+
/// 3. The total assets could be increased or decreased by an Adjuster role to modify the conversion rate.
23+
/// 4. Has an admin controlled maximum total assets limit.
24+
/// 5. Supports deposit with permit if the underlying asset supports permit as well.
25+
/// 6. Underlying assets are deposited/withdrawn to/from a connected ILiquidityPool contract.
26+
/// 7. To withdraw/redeem on behalf, owner has to approve spender on the shares contract instead of this one.
27+
/// @notice Upgradeable.
28+
/// @author Oleksii Matiiasevych <[email protected]>
1929
contract LiquidityHub is ILiquidityHub, ERC4626Upgradeable, AccessControlUpgradeable {
2030
using Math for uint256;
2131

contracts/LiquidityMining.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
66
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
77
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
88

9+
/// @title Staking contract locks the stake according to the configured/specified tier parameters.
10+
/// Each tier specifies a lock period in seconds and a multiplier that is used to mint score to the staker.
11+
/// The score is represented as an ERC20 token which this contract is itself.
12+
/// Each stake, even by the same user, is treated independently.
13+
/// Rewards are minted immediately upon staking because user cannot unstake early.
14+
/// The multiplier is specified in units of 0.01, eg. 150 will result in a 1.5x of staked amount as score.
15+
/// The owner can disable the future staking, without the ability to enable it again, which does not affect
16+
/// existing stakes.
17+
/// @author Oleksii Matiiasevych <[email protected]>
918
contract LiquidityMining is ERC20, Ownable {
1019
using SafeERC20 for IERC20;
1120

contracts/LiquidityPool.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
1111
import {ILiquidityPool} from "./interfaces/ILiquidityPool.sol";
1212
import {IBorrower} from "./interfaces/IBorrower.sol";
1313

14+
/// @title Liquidity pool contract holds the liquidity asset and allows solvers to borrow
15+
/// the asset from the pool and to perform an external function call upon providing the MPC signature.
16+
/// It's possible to perform borrowing with swap by the solver (the solver gets the borrowed
17+
/// assets from the pool, swaps them to fill tokens, and then the pool performs the target call).
18+
/// Repayment is done by transferring the assets to the contract without calling any function.
19+
/// Rebalancing is done by depositing and withdrawing assets from this pool by the LIQUIDITY_ADMIN_ROLE.
20+
/// Profit from borrowing is accounted for and can be withdrawn by the WITHDRAW_PROFIT_ROLE.
21+
/// Borrowing can be paused by the WITHDRAW_PROFIT_ROLE before withdrawing the profit.
22+
/// The contract is pausable by the PAUSER_ROLE.
23+
/// @author Tanya Bushenyova <[email protected]>
1424
contract LiquidityPool is ILiquidityPool, AccessControl, EIP712, Pausable {
1525
using SafeERC20 for IERC20;
1626
using ECDSA for bytes32;

contracts/LiquidityPoolAave.sol

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ import {IAaveOracle} from "./interfaces/IAaveOracle.sol";
1010
import {IAavePoolDataProvider} from "./interfaces/IAavePoolDataProvider.sol";
1111
import {LiquidityPool} from "./LiquidityPool.sol";
1212

13+
/// @title A version of the liquidity pool contract that uses Aave pool.
14+
/// Deposits of the liquidity token are supplied to Aave as collateral.
15+
/// It's possible to borrow other tokens from Aave pool upon providing the MPC signature.
16+
/// The contract verifies that the borrowing won't put it at risk of liquidation
17+
/// by checking the custom LTV and health factor that should be configured with a safety margin.
18+
/// Repayment to Aave is done by transferring the assets to the contract and calling the repay function.
19+
/// Rebalancing is done by depositing and withdrawing assets from Aave pool by the liquidity admin role.
20+
/// Profit from borrowing and accrued interest from supplying liquidity is accounted for
21+
/// and can be withdrawn by the WITHDRAW_PROFIT_ROLE.
22+
/// @author Tanya Bushenyova <[email protected]>
1323
contract LiquidityPoolAave is LiquidityPool {
1424
using SafeERC20 for IERC20;
1525

@@ -23,14 +33,15 @@ contract LiquidityPoolAave is LiquidityPool {
2333
uint256 public minHealthFactor;
2434
uint256 public defaultLTV;
2535

26-
mapping(address token => uint256 ltv) public _borrowTokenLTV;
36+
mapping(address token => uint256 ltv) public borrowTokenLTV;
2737

2838
error TokenLtvExceeded();
2939
error NoCollateral();
3040
error HealthFactorTooLow();
3141
error NothingToRepay();
3242
error CollateralNotSupported();
3343
error CannotWithdrawAToken();
44+
error InvalidLength();
3445

3546
event SuppliedToAave(uint256 amount);
3647
event BorrowTokenLTVSet(address token, uint256 oldLTV, uint256 newLTV);
@@ -73,10 +84,18 @@ contract LiquidityPoolAave is LiquidityPool {
7384

7485
// Admin functions
7586

76-
function setBorrowTokenLTV(address token, uint256 ltv) external onlyRole(DEFAULT_ADMIN_ROLE) {
77-
uint256 oldLTV = _borrowTokenLTV[token];
78-
_borrowTokenLTV[token] = ltv;
79-
emit BorrowTokenLTVSet(token, oldLTV, ltv);
87+
function setBorrowTokenLTVs(
88+
address[] calldata tokens,
89+
uint256[] calldata ltvs
90+
) external onlyRole(DEFAULT_ADMIN_ROLE) {
91+
require(tokens.length == ltvs.length, InvalidLength());
92+
for (uint256 i = 0; i < tokens.length; ++i) {
93+
address token = tokens[i];
94+
uint256 ltv = ltvs[i];
95+
uint256 oldLTV = borrowTokenLTV[token];
96+
borrowTokenLTV[token] = ltv;
97+
emit BorrowTokenLTVSet(token, oldLTV, ltv);
98+
}
8099
}
81100

82101
function setDefaultLTV(uint256 defaultLTV_) external onlyRole(DEFAULT_ADMIN_ROLE) {
@@ -94,7 +113,7 @@ contract LiquidityPoolAave is LiquidityPool {
94113
// Internal functions
95114

96115
function _checkTokenLTV(address borrowToken) private view {
97-
uint256 ltv = _borrowTokenLTV[borrowToken];
116+
uint256 ltv = borrowTokenLTV[borrowToken];
98117
if (ltv == 0) ltv = defaultLTV;
99118

100119
uint256 totalCollateral = ATOKEN.balanceOf(address(this));

contracts/ManagedToken.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ pragma solidity 0.8.28;
44
import {ERC20, ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
55
import {IManagedToken} from "./interfaces/IManagedToken.sol";
66

7+
/// @title An ERC20 token with enabled Permit and mint/burn/spendAllowance functions exposed to
8+
/// an immutable manager which is expected to be a contract too. This is meant to keep the token
9+
/// logic simple enough so that it does not need to be upgradeable.
10+
/// @author Oleksii Matiiasevych <[email protected]>
711
contract ManagedToken is IManagedToken, ERC20Permit {
812
address immutable public MANAGER;
913

contracts/Rebalancer.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import {ILiquidityPool} from "./interfaces/ILiquidityPool.sol";
1010
import {IRebalancer} from "./interfaces/IRebalancer.sol";
1111
import {ICCTPTokenMessenger, ICCTPMessageTransmitter} from "./interfaces/ICCTP.sol";
1212

13+
/// @title Facilitates liquidity movement between Liquidity Pools on same/different chains.
14+
/// Routes, which is a destination pool/domain and a bridging provider, have to be approved by admin.
15+
/// Rebalancing only takes arbitrary input on the amount, then contract enforces correct processing.
16+
/// REBALANCER_ROLE is needed to finalize/init rebalancing process.
17+
/// @notice Upgradeable.
18+
/// @author Oleksii Matiiasevych <[email protected]>
1319
contract Rebalancer is IRebalancer, AccessControlUpgradeable {
1420
using SafeERC20 for IERC20;
1521
using BitMaps for BitMaps.BitMap;

contracts/SprinterLiquidityMining.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import {
1010
import {ILiquidityHub} from "./interfaces/ILiquidityHub.sol";
1111
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
1212

13+
/// @title Modified version of the LiquidityMining contract, with the following differences:
14+
/// 1. The score tokens can only be minted. No transfers or burns. Used for convinient show on explorers.
15+
/// 2. Supports deposit into the connected liquidity hub with a subsequent stake in the same transaction.
16+
/// 3. Supports above scenario with permit of liquidity hub's underlying asset.
17+
/// 4. Supports unstake with a subsequent withdraw from the connected liquidity hub.
18+
/// @author Oleksii Matiiasevych <[email protected]>
1319
contract SprinterLiquidityMining is LiquidityMining {
1420
using SafeERC20 for IERC20;
1521

0 commit comments

Comments
 (0)