diff --git a/contracts/Deployer.sol b/contracts/Deployer.sol new file mode 100644 index 00000000..5e550654 --- /dev/null +++ b/contracts/Deployer.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.8.12; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { CREATE3 } from "solmate/src/utils/CREATE3.sol"; +import { Vault } from "./Vault.sol"; +import { Staker } from "./Staker.sol"; +import { Liquidator } from "./Liquidator.sol"; + +/// @title Deployer contract +/// @author Ithil +/// @notice Used to deploy Ithil smart contracts to deterministic addresses +/// on multiple chains irrespective of contracts' bytecode +contract Deployer is Ownable { + bytes32 internal constant salt = keccak256(bytes("ithil")); + + function getDeployed() public view returns (address) { + return CREATE3.getDeployed(salt); + } +} + +contract VaultDeployer is Deployer { + address public vault; + + function deploy(address weth) external onlyOwner { + vault = CREATE3.deploy(salt, abi.encodePacked(type(Vault).creationCode, abi.encode(weth)), 0); + } +} + +contract StakerDeployer is Deployer { + address public staker; + + function deploy(address token) external onlyOwner { + staker = CREATE3.deploy(salt, abi.encodePacked(type(Staker).creationCode, abi.encode(token)), 0); + } +} + +contract LiquidatorDeployer is Deployer { + address public liquidator; + + function deploy(address staker) external onlyOwner { + liquidator = CREATE3.deploy(salt, abi.encodePacked(type(Liquidator).creationCode, abi.encode(staker)), 0); + } +} diff --git a/contracts/liquidation/Liquidator.sol b/contracts/Liquidator.sol similarity index 97% rename from contracts/liquidation/Liquidator.sol rename to contracts/Liquidator.sol index 17d45011..53247e7a 100644 --- a/contracts/liquidation/Liquidator.sol +++ b/contracts/Liquidator.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.12; import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import { VaultMath } from "../libraries/VaultMath.sol"; -import { GeneralMath } from "../libraries/GeneralMath.sol"; -import { IVault } from "../interfaces/IVault.sol"; -import { IStrategy } from "../interfaces/IStrategy.sol"; -import { IStaker } from "../interfaces/IStaker.sol"; +import { VaultMath } from "./libraries/VaultMath.sol"; +import { GeneralMath } from "./libraries/GeneralMath.sol"; +import { IVault } from "./interfaces/IVault.sol"; +import { IStrategy } from "./interfaces/IStrategy.sol"; +import { IStaker } from "./interfaces/IStaker.sol"; /// @title Liquidator contract /// @author Ithil @@ -144,7 +144,7 @@ contract Liquidator is Ownable { IERC20(position.owedToken).safeTransferFrom(liquidatorUser, address(strategy.vault()), price); // slither-disable-next-line arbitrary-send-erc20 IERC20(position.heldToken).safeTransferFrom(address(strategy), liquidatorUser, position.allowance); - + // The following is necessary to avoid residual transfers during the repay // It means that everything "extra" from principal is fees dueFees = price.positiveSub(position.principal); diff --git a/package.json b/package.json index 41413aa8..2f661b31 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,8 @@ "dotenv": "^15.0.0", "eslint": "^8.8.0", "eslint-config-prettier": "^8.3.0", - "ethereumjs-util": "^7.1.5", "ethereum-waffle": "^3.4.0", + "ethereumjs-util": "^7.1.5", "ethers": "^5.5.4", "fs-extra": "^10.0.0", "hardhat": "^2.8.3", @@ -65,6 +65,7 @@ "solhint": "^3.3.6", "solhint-plugin-prettier": "^0.0.5", "solidity-coverage": "^0.7.18", + "solmate": "^6.6.1", "ts-generator": "^0.1.1", "ts-node": "^10.9.1", "typechain": "^7.0.0", @@ -91,7 +92,7 @@ "prettier:check": "prettier --check --config ./.prettierrc.yaml \"**/*.{js,json,md,sol,ts}\"", "publish": "hardhat publish", "size": "hardhat size-contracts", - "test": "yarn clean && yarn compile && hardhat test", + "test": "hardhat test", "typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain", "verify": "hardhat verify" } diff --git a/test/integration/Deployer.test.ts b/test/integration/Deployer.test.ts new file mode 100644 index 00000000..77828b79 --- /dev/null +++ b/test/integration/Deployer.test.ts @@ -0,0 +1,29 @@ +import { expect } from "chai"; +import { artifacts, ethers, waffle } from "hardhat"; + +import { tokens } from "../common/mainnet"; + +describe("Create3 deployer tests", function () { + it("deploy", async function () { + const VaultDeployer = await ethers.getContractFactory("VaultDeployer"); + const vaultDeployer = await VaultDeployer.deploy(); + await vaultDeployer.deploy(tokens.WETH.address); + const vault = await vaultDeployer.vault(); + expect(vault).not.to.equal("0x0000000000000000000000000000000000000000"); + expect(vault).to.equal(await vaultDeployer.getDeployed()); + + const StakerDeployer = await ethers.getContractFactory("StakerDeployer"); + const stakerDeployer = await StakerDeployer.deploy(); + await stakerDeployer.deploy(tokens.WETH.address); + const staker = await stakerDeployer.staker(); + expect(staker).not.to.equal("0x0000000000000000000000000000000000000000"); + expect(staker).to.equal(await stakerDeployer.getDeployed()); + + const LiquidatorDeployer = await ethers.getContractFactory("LiquidatorDeployer"); + const liquidatorDeployer = await LiquidatorDeployer.deploy(); + await liquidatorDeployer.deploy(staker); + const liquidator = await liquidatorDeployer.liquidator(); + expect(liquidator).not.to.equal("0x0000000000000000000000000000000000000000"); + expect(liquidator).to.equal(await liquidatorDeployer.getDeployed()); + }); +}); diff --git a/yarn.lock b/yarn.lock index 33f996c6..74c86f72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9395,6 +9395,11 @@ solidity-coverage@^0.7.18: shelljs "^0.8.3" web3-utils "^1.3.0" +solmate@^6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/solmate/-/solmate-6.6.1.tgz#f0907e1cdada6dd5fbfe11811ab545162b713197" + integrity sha512-WHvRXQvGtgR6R9nmkDTz/d+oULMqf/D33rlzQyadTX2SbuTmaW7ToEjGjGtWUVCQwZsZ/JP3vbOEVv7fB50btg== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"