Skip to content

Commit

Permalink
Merge pull request #2 from liquity/feat/deployment-script
Browse files Browse the repository at this point in the history
Feat/deployment script
  • Loading branch information
jltqy authored Oct 3, 2024
2 parents 1add9a4 + 149d962 commit c0aa023
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 19 deletions.
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
broadcast/

# Docs
docs/
Expand Down
6 changes: 5 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ number_underscore = "preserve"

[profile.default.rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
tenderly = "${TENDERLY_FORK_URL}"
sepolia = "${SEPOLIA_RPC_URL}"
tenderly = "${TENDERLY_FORK_URL}"

# forge script --chain mainnet script/DeployLocal.s.sol:DeployLocalScript --rpc-url https://eth-mainnet.alchemyapi.io/v2/B4w2ueJLjihQPuf868vthxg7FvfND5i5
# forge script --chain sepolia script/DeploySepolia.s.sol:DeploySepoliaScript --rpc-url https://eth-sepolia.g.alchemy.com/v2/6U2Z1L4BEW2VkZeHS5NQAWrvciif1DDI
12 changes: 0 additions & 12 deletions script/Counter.s.sol

This file was deleted.

181 changes: 181 additions & 0 deletions script/DeploySepolia.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script} from "forge-std/Script.sol";
import {MockERC20} from "forge-std/mocks/MockERC20.sol";

import {PoolManager, Deployers, Hooks} from "v4-core/test/utils/Deployers.sol";
import {ICurveStableswapFactoryNG} from "../src/interfaces/ICurveStableswapFactoryNG.sol";
import {ICurveStableswapNG} from "../src/interfaces/ICurveStableswapNG.sol";
import {ILiquidityGauge} from "./../src/interfaces/ILiquidityGauge.sol";

import {IGovernance} from "../src/interfaces/IGovernance.sol";

import {Governance} from "../src/Governance.sol";
import {UniV4Donations} from "../src/UniV4Donations.sol";
import {CurveV2GaugeRewards} from "../src/CurveV2GaugeRewards.sol";
import {Hooks} from "../src/utils/BaseHook.sol";

import {MockStakingV1} from "../test/mocks/MockStakingV1.sol";
import {HookMiner} from "./utils/HookMiner.sol";

contract DeploySepoliaScript is Script, Deployers {
// Environment Constants
MockERC20 private lqty;
MockERC20 private bold;
address private stakingV1;
MockERC20 private usdc;

PoolManager private constant poolManager = PoolManager(0xE8E23e97Fa135823143d6b9Cba9c699040D51F70);
ICurveStableswapFactoryNG private constant curveFactory =
ICurveStableswapFactoryNG(address(0xfb37b8D939FFa77114005e61CFc2e543d6F49A81));

// Governance Constants
uint128 private constant REGISTRATION_FEE = 100e18;
uint128 private constant REGISTRATION_THRESHOLD_FACTOR = 0.001e18;
uint128 private constant UNREGISTRATION_THRESHOLD_FACTOR = 3e18;
uint16 private constant REGISTRATION_WARM_UP_PERIOD = 4;
uint16 private constant UNREGISTRATION_AFTER_EPOCHS = 4;
uint128 private constant VOTING_THRESHOLD_FACTOR = 0.03e18;
uint88 private constant MIN_CLAIM = 500e18;
uint88 private constant MIN_ACCRUAL = 1000e18;
uint32 private constant EPOCH_DURATION = 604800;
uint32 private constant EPOCH_VOTING_CUTOFF = 518400;

// UniV4Donations Constants
uint256 private immutable VESTING_EPOCH_START = block.timestamp;
uint256 private constant VESTING_EPOCH_DURATION = 7 days;
uint24 private constant FEE = 400;
int24 constant MAX_TICK_SPACING = 32767;

// CurveV2GaugeRewards Constants
uint256 private constant DURATION = 7 days;

// Contracts
Governance private governance;
address[] private initialInitiatives;
UniV4Donations private uniV4Donations;
CurveV2GaugeRewards private curveV2GaugeRewards;
ICurveStableswapNG private curvePool;
ILiquidityGauge private gauge;

// Deployer
address private deployer;
uint256 private privateKey;
uint256 private nonce;

function setUp() public {
privateKey = vm.envUint("PRIVATE_KEY");
deployer = vm.createWallet(privateKey).addr;
nonce = vm.getNonce(deployer);
}

function deployEnvironment() private {
lqty = deployMockERC20("Liquity", "LQTY", 18);
bold = deployMockERC20("Bold", "BOLD", 18);
usdc = deployMockERC20("USD Coin", "USDC", 6);
stakingV1 = address(new MockStakingV1(address(lqty)));
}

function deployGovernance() private {
governance = new Governance(
address(lqty),
address(bold),
stakingV1,
address(bold),
IGovernance.Configuration({
registrationFee: REGISTRATION_FEE,
registrationThresholdFactor: REGISTRATION_THRESHOLD_FACTOR,
unregistrationThresholdFactor: UNREGISTRATION_THRESHOLD_FACTOR,
registrationWarmUpPeriod: REGISTRATION_WARM_UP_PERIOD,
unregistrationAfterEpochs: UNREGISTRATION_AFTER_EPOCHS,
votingThresholdFactor: VOTING_THRESHOLD_FACTOR,
minClaim: MIN_CLAIM,
minAccrual: MIN_ACCRUAL,
epochStart: uint32(block.timestamp),
epochDuration: EPOCH_DURATION,
epochVotingCutoff: EPOCH_VOTING_CUTOFF
}),
initialInitiatives
);
assert(governance == uniV4Donations.governance());
}

function deployUniV4Donations(uint256 _nonce) private {
address gov = address(vm.computeCreateAddress(deployer, _nonce));
uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_ADD_LIQUIDITY_FLAG);

(, bytes32 salt) = HookMiner.find(
0x4e59b44847b379578588920cA78FbF26c0B4956C,
// address(this),
flags,
type(UniV4Donations).creationCode,
abi.encode(
gov,
address(bold),
address(lqty),
block.timestamp,
EPOCH_DURATION,
address(poolManager),
address(usdc),
FEE,
MAX_TICK_SPACING
)
);

uniV4Donations = new UniV4Donations{salt: salt}(
gov,
address(bold),
address(lqty),
block.timestamp,
EPOCH_DURATION,
address(poolManager),
address(usdc),
FEE,
MAX_TICK_SPACING
);

initialInitiatives.push(address(uniV4Donations));
}

function deployCurveV2GaugeRewards(uint256 _nonce) private {
address[] memory _coins = new address[](2);
_coins[0] = address(bold);
_coins[1] = address(usdc);
uint8[] memory _asset_types = new uint8[](2);
_asset_types[0] = 0;
_asset_types[1] = 0;
bytes4[] memory _method_ids = new bytes4[](2);
_method_ids[0] = 0x0;
_method_ids[1] = 0x0;
address[] memory _oracles = new address[](2);
_oracles[0] = address(0x0);
_oracles[1] = address(0x0);

curvePool = ICurveStableswapNG(
curveFactory.deploy_plain_pool(
"BOLD-USDC", "BOLDUSDC", _coins, 200, 1000000, 50000000000, 866, 0, _asset_types, _method_ids, _oracles
)
);

gauge = ILiquidityGauge(curveFactory.deploy_gauge(address(curvePool)));

curveV2GaugeRewards = new CurveV2GaugeRewards(
address(vm.computeCreateAddress(address(this), _nonce)),
address(bold),
address(lqty),
address(gauge),
DURATION
);

initialInitiatives.push(address(curveV2GaugeRewards));
}

function run() public {
vm.startBroadcast(privateKey);
deployEnvironment();
deployUniV4Donations(nonce + 8);
deployGovernance();
vm.stopBroadcast();
}
}
52 changes: 52 additions & 0 deletions script/utils/HookMiner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.21;

/// @title HookMiner - a library for mining hook addresses
/// @dev This library is intended for `forge test` environments. There may be gotchas when using salts in `forge script` or `forge create`
library HookMiner {
// mask to slice out the bottom 14 bit of the address
uint160 constant FLAG_MASK = 0x3FFF;

// Maximum number of iterations to find a salt, avoid infinite loops
uint256 constant MAX_LOOP = 100_000;

/// @notice Find a salt that produces a hook address with the desired `flags`
/// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address
/// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy)
/// @param flags The desired flags for the hook address
/// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode`
/// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))`
/// @return hookAddress salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}(<constructor arguments>)`
function find(address deployer, uint160 flags, bytes memory creationCode, bytes memory constructorArgs)
internal
view
returns (address, bytes32)
{
address hookAddress;
bytes memory creationCodeWithArgs = abi.encodePacked(creationCode, constructorArgs);

uint256 salt;
for (salt; salt < MAX_LOOP; salt++) {
hookAddress = computeAddress(deployer, salt, creationCodeWithArgs);
if (uint160(hookAddress) & FLAG_MASK == flags && hookAddress.code.length == 0) {
return (hookAddress, bytes32(salt));
}
}
revert("HookMiner: could not find salt");
}

/// @notice Precompute a contract address deployed via CREATE2
/// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address
/// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy)
/// @param salt The salt used to deploy the hook
/// @param creationCode The creation code of a hook contract
function computeAddress(address deployer, uint256 salt, bytes memory creationCode)
internal
pure
returns (address hookAddress)
{
return address(
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xFF), deployer, salt, keccak256(creationCode)))))
);
}
}
2 changes: 0 additions & 2 deletions test/Governance.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,6 @@ contract GovernanceTest is Test {
address userProxy = governance.deployUserProxy();

vm.store(address(lqty), keccak256(abi.encode(user, 0)), bytes32(abi.encode(uint256(_deltaLQTYVotes))));
console.log(lqty.balanceOf(user));
lqty.approve(address(userProxy), _deltaLQTYVotes);
governance.depositLQTY(_deltaLQTYVotes);

Expand All @@ -948,7 +947,6 @@ contract GovernanceTest is Test {
address userProxy = governance.deployUserProxy();

vm.store(address(lqty), keccak256(abi.encode(user, 0)), bytes32(abi.encode(uint256(_deltaLQTYVetos))));
console.log(lqty.balanceOf(user));
lqty.approve(address(userProxy), _deltaLQTYVetos);
governance.depositLQTY(_deltaLQTYVetos);

Expand Down
2 changes: 1 addition & 1 deletion test/UniV4Donations.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol";

import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";

import {IPoolManager, PoolManager, Deployers, TickMath, Hooks} from "v4-core/test/utils/Deployers.sol";
import {IPoolManager, PoolManager, Deployers, TickMath, Hooks, IHooks} from "v4-core/test/utils/Deployers.sol";
import {PoolModifyLiquidityTest} from "v4-core/src/test/PoolModifyLiquidityTest.sol";

import {IGovernance} from "../src/interfaces/IGovernance.sol";
Expand Down

0 comments on commit c0aa023

Please sign in to comment.