Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VEN-1687]: feat: deploy rewards distributors #273

Merged
merged 9 commits into from
Jul 12, 2023
1 change: 1 addition & 0 deletions deploy/001-deploy-mock-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { getConfig } from "../helpers/deploymentConfig";

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts }: any = hre;

Check warning on line 7 in deploy/001-deploy-mock-tokens.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();

Expand All @@ -19,6 +19,7 @@
args: [token.name, token.symbol, token.decimals],
log: true,
autoMine: true, // speed up deployment on local network (ganache, hardhat), no effect on live networks
skipIfAlreadyDeployed: true,
});
}
}
Expand Down
9 changes: 9 additions & 0 deletions deploy/010-deploy-reward-distributors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const proxyOwnerAddress = await toAddress(preconfiguredAddresses.NormalTimelock || "account:deployer", hre);

const pools = await getUnregisteredRewardsDistributors(poolConfig, hre);

await deploy("RewardsDistributorImpl", {
contract: "RewardsDistributor",
from: deployer,
autoMine: true,
log: true,
});

for (const pool of pools) {
const rewards = pool.rewards;
if (!rewards) continue;
Expand All @@ -34,6 +42,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
from: deployer,
contract: "RewardsDistributor",
proxy: {
implementationName: `RewardsDistributorImpl`,
owner: proxyOwnerAddress,
proxyContract: "OpenZeppelinTransparentProxy",
execute: {
Expand Down
57 changes: 41 additions & 16 deletions deploy/011-initial-liquidity.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import { BigNumber } from "ethers";
import { DeployFunction } from "hardhat-deploy/types";
import { HardhatRuntimeEnvironment } from "hardhat/types";

import { DeploymentConfig, PoolConfig, getConfig, getTokenConfig } from "../helpers/deploymentConfig";
import {
DeploymentConfig,
PoolConfig,
TokenConfig,
VTokenConfig,
getConfig,
getTokenConfig,
} from "../helpers/deploymentConfig";
import { getUnderlyingMock, getUnderlyingToken, getUnregisteredVTokens } from "../helpers/deploymentUtils";

const sumAmounts = async (tokens: { symbol: string; amount: BigNumber }[]) => {
const amounts: { [symbol: string]: BigNumber } = {};
for (const { symbol, amount } of tokens) {
amounts[symbol] = amount.add(amounts[symbol] || 0);
}
return amounts;
};

const faucetTokens = async (deploymentConfig: DeploymentConfig, hre: HardhatRuntimeEnvironment) => {
const { poolConfig, tokensConfig } = deploymentConfig;
const unregisteredVTokens = await getUnregisteredVTokens(poolConfig, hre);
const vTokenConfigs = unregisteredVTokens.map((p: PoolConfig) => p.vtokens).flat();
const assetsToFaucet = vTokenConfigs
.map((v: { asset: string }) => getTokenConfig(v.asset, tokensConfig))
.filter((token: TokenConfig) => token.isMock || token.faucetInitialLiquidity)
.map((token: TokenConfig) => token.symbol);

for (const vTokenConfig of vTokenConfigs) {
const token = getTokenConfig(vTokenConfig.asset, tokensConfig);
if (!token.isMock && !token.faucetInitialLiquidity) {
continue;
}
const tokenContract = await getUnderlyingMock(token.symbol);
console.log(`Minting ${vTokenConfig.initialSupply} mock ${token.symbol} to owner`);
const tx = await tokenContract.faucet(vTokenConfig.initialSupply);
const vTokensToFaucet = vTokenConfigs.filter((v: { asset: string }) => assetsToFaucet.includes(v.asset));

const amounts = vTokensToFaucet.map((token: VTokenConfig) => ({
symbol: token.asset,
amount: BigNumber.from(token.initialSupply),
}));
const totalAmounts = await sumAmounts(amounts);
for (const [symbol, amount] of Object.entries(totalAmounts)) {
const tokenContract = await getUnderlyingMock(symbol);
console.log(`Minting ${amount} mock ${symbol} to owner`);
const tx = await tokenContract.faucet(amount, { gasLimit: 5000000 });
await tx.wait(1);
}
};
Expand All @@ -29,13 +52,15 @@ const approveTimelock = async (deploymentConfig: DeploymentConfig, hre: HardhatR
const unregisteredVTokens = await getUnregisteredVTokens(poolConfig, hre);
const vTokenConfigs = unregisteredVTokens.map((p: PoolConfig) => p.vtokens).flat();

for (const vTokenConfig of vTokenConfigs) {
const { asset, initialSupply } = vTokenConfig;
const token = getTokenConfig(asset, tokensConfig);

const tokenContract = await getUnderlyingToken(token.symbol, tokensConfig);
console.log(`Approving ${initialSupply} ${token.symbol} to Timelock`);
const tx = await tokenContract.approve(preconfiguredAddresses.NormalTimelock, initialSupply);
const amounts = vTokenConfigs.map((token: VTokenConfig) => ({
symbol: token.asset,
amount: BigNumber.from(token.initialSupply),
}));
const totalAmounts = await sumAmounts(amounts);
for (const [symbol, amount] of Object.entries(totalAmounts)) {
const tokenContract = await getUnderlyingToken(symbol, tokensConfig);
console.log(`Approving ${amount} ${symbol} to Timelock`);
const tx = await tokenContract.approve(preconfiguredAddresses.NormalTimelock, amount, { gasLimit: 5000000 });
await tx.wait(1);
}
};
Expand Down
143 changes: 79 additions & 64 deletions deploy/013-vip-based-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,16 @@
getUnregisteredVTokens,
toAddress,
} from "../helpers/deploymentUtils";
import { Comptroller, PoolRegistry, RewardsDistributor } from "../typechain";
import { AccessControlManager, Comptroller, PoolRegistry, RewardsDistributor } from "../typechain";

interface GovernanceCommand {
contract: string;
signature: string;
argTypes: string[];
parameters: any[];

Check warning on line 29 in deploy/013-vip-based-config.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
value: BigNumberish;
}

interface ProposalActions {
targets: string[];
values: BigNumberish[];
signatures: string[];
calldatas: string[];
}

const toProposalActions = (commands: GovernanceCommand[]): ProposalActions => {
const targets = commands.map(c => c.contract);
const values = commands.map(c => c.value);
const signatures = commands.map(c => c.signature);
const calldatas = commands.map(c => ethers.utils.defaultAbiCoder.encode(c.argTypes, c.parameters));
return { targets, values, signatures, calldatas };
};

const addRewardsDistributor = async (
rewardsDistributor: RewardsDistributor,
pool: PoolConfig,
Expand Down Expand Up @@ -90,6 +75,7 @@

const configureRewards = async (
unregisteredRewardDistributors: PoolConfig[],
owner: string,
hre: HardhatRuntimeEnvironment,
): Promise<GovernanceCommand[]> => {
const commands = await Promise.all(
Expand All @@ -100,7 +86,7 @@
const contractName = `RewardsDistributor_${rewardConfig.asset}_${pool.id}`;
const rewardsDistributor = await ethers.getContract<RewardsDistributor>(contractName);
return [
...(await acceptOwnership(contractName, hre)),
...(await acceptOwnership(contractName, owner, hre)),
await addRewardsDistributor(rewardsDistributor, pool, rewardConfig),
await setRewardSpeed(pool, rewardsDistributor, rewardConfig),
];
Expand All @@ -112,15 +98,24 @@
return commands.flat();
};

const acceptOwnership = async (contractName: string, hre: HardhatRuntimeEnvironment): Promise<GovernanceCommand[]> => {
const acceptOwnership = async (
contractName: string,
targetOwner: string,
hre: HardhatRuntimeEnvironment,
): Promise<GovernanceCommand[]> => {
if (!hre.network.live) {
return [];
}
const abi = ["function owner() view returns (address)"];
const deployment = await hre.deployments.get(contractName);
const contract = await ethers.getContractAt(abi, deployment.address);
if ((await contract.owner()) === targetOwner) {
return [];
}
console.log(`Adding a command to accept the admin rights over ${contractName}`);
const contract = await ethers.getContract(contractName);
return [
{
contract: contract.address,
contract: deployment.address,
signature: "acceptOwnership()",
argTypes: [],
parameters: [],
Expand Down Expand Up @@ -160,14 +155,15 @@

const addPools = async (
unregisteredPools: PoolConfig[],
poolsOwner: string,
hre: HardhatRuntimeEnvironment,
): Promise<GovernanceCommand[]> => {
const poolRegistry = await ethers.getContract<PoolRegistry>("PoolRegistry");
const commands = await Promise.all(
unregisteredPools.map(async (pool: PoolConfig) => {
const comptroller = await ethers.getContract<Comptroller>(`Comptroller_${pool.id}`);
return [
...(await acceptOwnership(`Comptroller_${pool.id}`, hre)),
...(await acceptOwnership(`Comptroller_${pool.id}`, poolsOwner, hre)),
await setOracle(comptroller, pool),
addPool(poolRegistry, comptroller, pool),
];
Expand Down Expand Up @@ -203,7 +199,7 @@
return [
{
contract: preconfiguredAddresses.VTreasury,
signature: "withdrawTreasuryBep20(address,uint256,address)",
signature: "withdrawTreasuryBEP20(address,uint256,address)",
argTypes: ["address", "uint256", "address"],
parameters: [tokenContract.address, initialSupply, preconfiguredAddresses.NormalTimelock],
value: 0,
Expand All @@ -223,8 +219,15 @@
const token = getTokenConfig(asset, tokensConfig);
const tokenContract = await getUnderlyingToken(token.symbol, tokensConfig);

console.log(`Adding a command to approve ${initialSupply} ${token.symbol} to PoolRegistry`);
console.log(`Adding commands to approve ${initialSupply} ${token.symbol} to PoolRegistry`);
return [
{
contract: tokenContract.address,
signature: "approve(address,uint256)",
argTypes: ["address", "uint256"],
parameters: [poolRegistry.address, 0],
value: 0,
},
{
contract: tokenContract.address,
signature: "approve(address,uint256)",
Expand Down Expand Up @@ -283,32 +286,65 @@
return poolCommands.flat();
};

const configureAccessControls = async (deploymentConfig: DeploymentConfig, hre: HardhatRuntimeEnvironment) => {
const makeRole = (mainnetBehavior: boolean, targetContract: string, method: string): string => {
if (mainnetBehavior && targetContract === ethers.constants.AddressZero) {
return ethers.utils.keccak256(
ethers.utils.solidityPack(["bytes32", "string"], [ethers.constants.HashZero, method]),
);
}
return ethers.utils.keccak256(ethers.utils.solidityPack(["address", "string"], [targetContract, method]));
};

const hasPermission = async (
accessControl: AccessControlManager,
targetContract: string,
method: string,
caller: string,
hre: HardhatRuntimeEnvironment,
): Promise<boolean> => {
const role = makeRole(hre.network.name === "bscmainnet", targetContract, method);
return accessControl.hasRole(role, caller);
};

const configureAccessControls = async (
deploymentConfig: DeploymentConfig,
hre: HardhatRuntimeEnvironment,
): Promise<GovernanceCommand[]> => {
const { accessControlConfig, preconfiguredAddresses } = deploymentConfig;
const accessControlManager = await toAddress(
const accessControlManagerAddress = await toAddress(
preconfiguredAddresses.AccessControlManager || "AccessControlManager",
hre,
);
return await Promise.all(
const accessControlManager = await ethers.getContractAt<AccessControlManager>(
"AccessControlManager",
accessControlManagerAddress,
);
const commands = await Promise.all(
accessControlConfig.map(async (entry: AccessControlEntry) => {
const { caller, target, method } = entry;
const callerAddress = await toAddress(caller, hre);
const targetAddress = await toAddress(target, hre);
return {
contract: accessControlManager,
signature: "giveCallPermission(address,string,address)",
argTypes: ["address", "string", "address"],
parameters: [targetAddress, method, callerAddress],
value: 0,
};
if (await hasPermission(accessControlManager, targetAddress, method, callerAddress, hre)) {
return [];
}
return [
{
contract: accessControlManagerAddress,
signature: "giveCallPermission(address,string,address)",
argTypes: ["address", "string", "address"],
parameters: [targetAddress, method, callerAddress],
value: 0,
},
];
}),
);
return commands.flat();
};

const logCommand = (prefix: string, command: GovernanceCommand) => {
const valueStr = command.value == 0 ? "" : "{ value: " + parseEther(command.value.toString()) + " }";
console.log(`${prefix} ${command.contract}.${command.signature}${valueStr} (`);
command.parameters.forEach((param: any) => console.log(` ${param},`));

Check warning on line 347 in deploy/013-vip-based-config.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
console.log(")");
};

Expand Down Expand Up @@ -338,43 +374,22 @@
const unregisteredPools = await getUnregisteredPools(poolConfig, hre);
const unregisteredVTokens = await getUnregisteredVTokens(poolConfig, hre);
const unregisteredRewardsDistributors = await getUnregisteredRewardsDistributors(poolConfig, hre);
const owner = preconfiguredAddresses.NormalTimelock || deployer;
const commands = [
...(await configureAccessControls(deploymentConfig, hre)),
...(await acceptOwnership("PoolRegistry", hre)),
...(await addPools(unregisteredPools, hre)),
...(await acceptOwnership("PoolRegistry", owner, hre)),
...(await addPools(unregisteredPools, owner, hre)),
...(await addMarkets(unregisteredVTokens, deploymentConfig, hre)),
...(await configureRewards(unregisteredRewardsDistributors, hre)),
...(await configureRewards(unregisteredRewardsDistributors, owner, hre)),
];

const proposalActions = toProposalActions(commands);
console.log("targets", proposalActions.targets);
console.log("signatures", proposalActions.signatures);
console.log("calldatas", proposalActions.calldatas);
console.log("values", proposalActions.values);

if (hre.network.live) {
const governorBravo = await ethers.getContractAt("GovernorBravoDelegate", preconfiguredAddresses.GovernorBravo);
const NORMAL_VIP = 0;
const meta = {
version: "v2",
title: "Isolated lending, phase 1",
description: ``,
forDescription: "I agree that Venus Protocol should proceed with IL Phase 1",
againstDescription: "I do not think that Venus Protocol should proceed with IL Phase 1",
abstainDescription: "I am indifferent to whether Venus Protocol proceeds with IL Phase 1",
};
const signer = await ethers.getSigner(deployer);
const tx = await governorBravo
.connect(signer)
.propose(
proposalActions.targets,
proposalActions.values,
proposalActions.signatures,
proposalActions.calldatas,
JSON.stringify(meta),
NORMAL_VIP,
);
await tx.wait();
console.log("Please propose a VIP with the following commands:");
console.log(
JSON.stringify(
commands.map(c => ({ target: c.contract, signature: c.signature, params: c.parameters, value: c.value })),
),
);
} else {
await executeCommands(commands, hre);
}
Expand Down
Loading
Loading