diff --git a/contracts/deploy/013_trustee.js b/contracts/deploy/013_trustee.js new file mode 100644 index 0000000000..446c5ab56e --- /dev/null +++ b/contracts/deploy/013_trustee.js @@ -0,0 +1,117 @@ +const { + isMainnet, + isFork, + isRinkeby, + isSmokeTest, +} = require("../test/helpers.js"); +const { + log, + deployWithConfirmation, + withConfirmation, + executeProposal, + sendProposal, +} = require("../utils/deploy"); +const { proposeArgs } = require("../utils/governor"); +const { getTxOpts } = require("../utils/tx"); + +const deployName = "013_upgrades"; + +/** + * Deploys the vault trustee feature: + * - upgrade VaultCore + * - set trusteeAdress and trusteeFeeBps + * @returns {Promise} + */ +const trustee = async (hre) => { + console.log(`Running ${deployName} deployment...`); + + const { governorAddr } = await hre.getNamedAccounts(); + + // Signers + const sGovernor = await ethers.provider.getSigner(governorAddr); + + const cVaultProxy = await ethers.getContract("VaultProxy"); + const cvaultAdmin = await ethers.getContractAt( + "VaultAdmin", + cVaultProxy.address + ); + + // Deploy a new VaultCore contract. + const dVaultCore = await deployWithConfirmation("VaultCore"); + + // Proposal for the governor to do the upgrades. + const propDescription = "Trustee deploy and config"; + const trusteeAddress = "0xF14BBdf064E3F67f51cd9BD646aE3716aD938FDC"; // Strategist multi-sig + const trusteeFeeBps = 1000; // 1000 bps = 10% + const propArgs = await proposeArgs([ + { + contract: cVaultProxy, + signature: "upgradeTo(address)", + args: [dVaultCore.address], + }, + { + contract: cvaultAdmin, + signature: "setTrusteeAddress(address)", + args: [trusteeAddress], + }, + { + contract: cvaultAdmin, + signature: "setTrusteeFeeBps(uint256)", + args: [trusteeFeeBps], + }, + ]); + + if (isMainnet) { + // On Mainnet, only propose. The enqueue and execution are handled manually via multi-sig. + log("Sending proposal to governor..."); + await sendProposal(propArgs, propDescription); + log("Proposal sent."); + } else if (isFork) { + // On Fork we can send the proposal then impersonate the guardian to execute it. + log("Sending and executing proposal..."); + await executeProposal(propArgs, propDescription); + log("Proposal executed."); + } else { + // Hardcoding gas estimate on Rinkeby since it fails for an undetermined reason... + const gasLimit = isRinkeby ? 1000000 : null; + + await withConfirmation( + cVaultProxy + .connect(sGovernor) + .upgradeTo(dVaultCore.address, await getTxOpts(gasLimit)) + ); + log("Upgraded VaultCore to new implementation"); + + await withConfirmation( + cvaultAdmin + .connect(sGovernor) + .setTrusteeAddress(trusteeAddress, await getTxOpts(gasLimit)) + ); + log("Trustee address set"); + + await withConfirmation( + cvaultAdmin + .connect(sGovernor) + .setTrusteeFeeBps(trusteeFeeBps, await getTxOpts(gasLimit)) + ); + log("Trustee fee bps set"); + } + + return true; +}; + +const main = async (hre) => { + console.log(`Running ${deployName} deployment...`); + if (!hre) { + hre = require("hardhat"); + } + await trustee(hre); + console.log(`${deployName} deploy done.`); + return true; +}; + +main.id = deployName; +main.dependencies = ["012_upgrades"]; +main.skip = () => !(isMainnet || isRinkeby || isFork) || isSmokeTest; + +module.exports = main; diff --git a/contracts/hardhat.config.js b/contracts/hardhat.config.js index 1293d1a5b0..34231d5f5c 100644 --- a/contracts/hardhat.config.js +++ b/contracts/hardhat.config.js @@ -32,6 +32,7 @@ const { harvest, reallocate, rebase, + yield, } = require("./tasks/vault"); const MAINNET_DEPLOYER = "0x71F78361537A6f7B6818e7A760c8bC0146D93f50"; @@ -87,6 +88,7 @@ task("allocate", "Call allocate() on the Vault", allocate); task("capital", "Set the Vault's pauseCapital flag", capital); task("harvest", "Call harvest() on Vault", harvest); task("rebase", "Call rebase() on the Vault", rebase); +task("yield", "Artificially generate yield on the Vault", yield); task("reallocate", "Allocate assets from one Strategy to another") .addParam("from", "Address to withdraw asset from") .addParam("to", "Address to deposit asset to") diff --git a/contracts/scripts/etherscan/etherscanVerify.js b/contracts/scripts/etherscan/etherscanVerify.js index 4bb4beda3f..2159076bad 100644 --- a/contracts/scripts/etherscan/etherscanVerify.js +++ b/contracts/scripts/etherscan/etherscanVerify.js @@ -48,6 +48,7 @@ const deprecatedContractNames = [ "MinuteTimelock", "OpenUniswapOracle", "RebaseHooks", + "OUSDReset", ]; function logError(...args) { diff --git a/contracts/tasks/debug.js b/contracts/tasks/debug.js index d536022db7..f7b0619c19 100644 --- a/contracts/tasks/debug.js +++ b/contracts/tasks/debug.js @@ -224,7 +224,8 @@ async function debug(taskArguments, hre) { // const rebasePaused = await vault.rebasePaused(); const capitalPaused = await vault.capitalPaused(); - const redeemFeeBps = await vault.redeemFeeBps(); + const redeemFeeBps = Number(await vault.redeemFeeBps()); + const trusteeFeeBps = Number(await vault.trusteeFeeBps()); const vaultBuffer = await vault.vaultBuffer(); const autoAllocateThreshold = await vault.autoAllocateThreshold(); const rebaseThreshold = await vault.rebaseThreshold(); @@ -233,12 +234,14 @@ async function debug(taskArguments, hre) { const strategyCount = await vault.getStrategyCount(); const assetCount = await vault.getAssetCount(); const strategistAddress = await vault.strategistAddr(); + const trusteeAddress = await vault.trusteeAddress(); console.log("\nVault Settings"); console.log("================"); console.log("rebasePaused:\t\t\t", rebasePaused); console.log("capitalPaused:\t\t\t", capitalPaused); - console.log("redeemFeeBps:\t\t\t", redeemFeeBps.toString()); + console.log(`redeemFeeBps:\t\t\t ${redeemFeeBps} (${redeemFeeBps / 100}%)`); + console.log(`trusteeFeeBps:\t\t\t ${trusteeFeeBps} (${trusteeFeeBps / 100}%)`); console.log("vaultBuffer:\t\t\t", formatUnits(vaultBuffer.toString(), 18)); console.log( "autoAllocateThreshold (USD):\t", @@ -254,6 +257,7 @@ async function debug(taskArguments, hre) { console.log("Strategy count:\t\t\t", Number(strategyCount)); console.log("Asset count:\t\t\t", Number(assetCount)); console.log("Strategist address:\t\t", strategistAddress); + console.log("Trustee address:\t\t", trusteeAddress) const assets = [ { diff --git a/contracts/tasks/vault.js b/contracts/tasks/vault.js index 93e0256287..ac827acb76 100644 --- a/contracts/tasks/vault.js +++ b/contracts/tasks/vault.js @@ -69,6 +69,63 @@ async function rebase(taskArguments, hre) { console.log("Rebase transaction confirmed"); } +/** + * Artificially generate yield on the vault by sending it USDT. + */ +async function yield(taskArguments, hre) { + const usdtAbi = require("../test/abi/usdt.json").abi; + const { + ousdUnitsFormat, + usdtUnits, + usdtUnitsFormat, + isFork, + isLocalhost + } = require("../test/helpers"); + if (!isFork && !isLocalhost) { + throw new Error('Task can only be used on local or fork') + } + + let richSigner, usdt; + if (isFork) { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [addresses.mainnet.Binance], + }); + richSigner = await hre.ethers.provider.getSigner( + addresses.mainnet.Binance + ); + usdt = await hre.ethers.getContractAt(usdtAbi, addresses.mainnet.USDT); + } else { + const signers = await hre.ethers.getSigners(); + richSigner = signers + usdt = await hre.ethers.getContract("MockUSDT"); + } + + const vaultProxy = await ethers.getContract("VaultProxy"); + const vault = await ethers.getContractAt("IVault", vaultProxy.address); + + const ousdProxy = await ethers.getContract("OUSDProxy"); + const ousd = await ethers.getContractAt("OUSD", ousdProxy.address); + + console.log("Sending yield to vault") + let usdtBalance = await usdt.balanceOf(vaultProxy.address) + console.log("USDT vault balance", usdtUnitsFormat(usdtBalance)) + let vaultValue = await vault.totalValue() + console.log("Vault value", ousdUnitsFormat(vaultValue)) + let supply = await ousd.totalSupply(); + console.log("OUSD supply", ousdUnitsFormat(supply)) + + // Transfer 100k USDT to the vault. + await usdt.connect(richSigner).transfer(vaultProxy.address, usdtUnits("100000")); + + usdtBalance = await usdt.balanceOf(vaultProxy.address) + console.log("USDT vault balance", usdtUnitsFormat(usdtBalance)) + vaultValue = await vault.totalValue() + console.log("Vault value", ousdUnitsFormat(vaultValue)) + supply = await ousd.totalSupply(); + console.log("OUSD supply", ousdUnitsFormat(supply)) +} + /** * Call the Vault's admin pauseCapital method. */ @@ -231,4 +288,5 @@ module.exports = { harvest, reallocate, rebase, + yield, } \ No newline at end of file