diff --git a/simulations/vip-600/abi/SwapRouter.json b/simulations/vip-600/abi/SwapRouter.json new file mode 100644 index 000000000..ab208ac75 --- /dev/null +++ b/simulations/vip-600/abi/SwapRouter.json @@ -0,0 +1,618 @@ +[ + { + "inputs": [ + { + "internalType": "contract IComptroller", + "name": "_comptroller", + "type": "address" + }, + { + "internalType": "contract SwapHelper", + "name": "_swapHelper", + "type": "address" + }, + { + "internalType": "contract IWBNB", + "name": "_wrappedNative", + "type": "address" + }, + { + "internalType": "address", + "name": "_nativeVToken", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + } + ], + "name": "InsufficientAmountOut", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "NativeTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "NoTokensReceived", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RepayFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "SupplyFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SwapFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "UnauthorizedNativeSender", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRepaid", + "type": "uint256" + } + ], + "name": "SwapAndRepay", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountSupplied", + "type": "uint256" + } + ], + "name": "SwapAndSupply", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SweepNative", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SweepToken", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE_TOKEN_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE_VTOKEN", + "outputs": [ + { + "internalType": "contract IVBNB", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SWAP_HELPER", + "outputs": [ + { + "internalType": "contract SwapHelper", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WRAPPED_NATIVE", + "outputs": [ + { + "internalType": "contract IWBNB", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapAndRepay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxAmountIn", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapAndRepayFull", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapAndSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapNativeAndRepay", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapNativeAndRepayFull", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minAmountOut", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "name": "swapNativeAndSupply", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "sweepNative", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/simulations/vip-600/bscmainnet.ts b/simulations/vip-600/bscmainnet.ts new file mode 100644 index 000000000..82dea5108 --- /dev/null +++ b/simulations/vip-600/bscmainnet.ts @@ -0,0 +1,46 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { forking, testVip } from "src/vip-framework"; + +import { SWAP_HELPER, SWAP_ROUTER, UNITROLLER, vip600 } from "../../vips/vip-600/bscmainnet"; +import SWAP_ROUTER_ABI from "./abi/SwapRouter.json"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +forking(76556273, async () => { + let swapRouter: Contract; + + before(async () => { + swapRouter = await ethers.getContractAt(SWAP_ROUTER_ABI, SWAP_ROUTER); + }); + + describe("Pre-VIP behavior", () => { + it("SwapRouter should have NORMAL_TIMELOCK as pending owner", async () => { + expect(await swapRouter.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + describe("SwapRouter configuration", () => { + it("should have correct COMPTROLLER", async () => { + expect(await swapRouter.COMPTROLLER()).to.equal(UNITROLLER); + }); + + it("should have correct swapHelper", async () => { + expect(await swapRouter.SWAP_HELPER()).to.equal(SWAP_HELPER); + }); + }); + }); + + testVip("VIP-600", await vip600(), {}); + + describe("Post-VIP behavior", () => { + it("SwapRouter should have NORMAL_TIMELOCK as owner", async () => { + expect(await swapRouter.owner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("SwapRouter should have zero address as pending owner", async () => { + expect(await swapRouter.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + }); +}); diff --git a/simulations/vip-600/bsctestnet.ts b/simulations/vip-600/bsctestnet.ts new file mode 100644 index 000000000..72911634a --- /dev/null +++ b/simulations/vip-600/bsctestnet.ts @@ -0,0 +1,46 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { forking, testVip } from "src/vip-framework"; + +import { SWAP_HELPER, SWAP_ROUTER, UNITROLLER, vip600 } from "../../vips/vip-600/bsctestnet"; +import SWAP_ROUTER_ABI from "./abi/SwapRouter.json"; + +const { bsctestnet } = NETWORK_ADDRESSES; + +forking(85710269, async () => { + let swapRouter: Contract; + + before(async () => { + swapRouter = await ethers.getContractAt(SWAP_ROUTER_ABI, SWAP_ROUTER); + }); + + describe("Pre-VIP behavior", () => { + it("SwapRouter should have NORMAL_TIMELOCK as pending owner", async () => { + expect(await swapRouter.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + describe("SwapRouter configuration", () => { + it("should have correct COMPTROLLER", async () => { + expect(await swapRouter.COMPTROLLER()).to.equal(UNITROLLER); + }); + + it("should have correct swapHelper", async () => { + expect(await swapRouter.SWAP_HELPER()).to.equal(SWAP_HELPER); + }); + }); + }); + + testVip("VIP-600", await vip600(), {}); + + describe("Post-VIP behavior", () => { + it("SwapRouter should have NORMAL_TIMELOCK as owner", async () => { + expect(await swapRouter.owner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + it("SwapRouter should have zero address as pending owner", async () => { + expect(await swapRouter.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + }); +}); diff --git a/src/vip-framework/index.ts b/src/vip-framework/index.ts index de2aa7f1f..5176b2df4 100644 --- a/src/vip-framework/index.ts +++ b/src/vip-framework/index.ts @@ -30,7 +30,7 @@ const OMNICHAIN_PROPOSAL_SENDER = getOmnichainProposalSenderAddress(); const OMNICHAIN_GOVERNANCE_EXECUTOR = NETWORK_ADDRESSES[FORKED_NETWORK as REMOTE_NETWORKS].OMNICHAIN_GOVERNANCE_EXECUTOR; -const VOTING_PERIOD = 115200; +const VOTING_PERIOD = 192384; export const { DEFAULT_PROPOSER_ADDRESS, diff --git a/vips/vip-600/bscmainnet.ts b/vips/vip-600/bscmainnet.ts new file mode 100644 index 000000000..82b2dcf5e --- /dev/null +++ b/vips/vip-600/bscmainnet.ts @@ -0,0 +1,31 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +export const UNITROLLER = "0xfD36E2c2a6789Db23113685031d7F16329158384"; +export const SWAP_ROUTER = "0xde7E4f67Af577F29e5F3B995f9e67FD425F73621"; +export const SWAP_HELPER = "0xD79be25aEe798Aa34A9Ba1230003d7499be29A24"; + +export const vip600 = () => { + const meta = { + version: "v2", + title: "VIP-600 [BNB Chain] Implementation of SwapRouter", + description: `Implements SwapRouter contract and transfers its ownership to Venus Protocol on BNB Chain.`, + forDescription: "Execute", + againstDescription: "Do not execute", + abstainDescription: "Abstain", + }; + + return makeProposal( + [ + { + target: SWAP_ROUTER, + signature: "acceptOwnership()", + params: [], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip600; diff --git a/vips/vip-600/bsctestnet.ts b/vips/vip-600/bsctestnet.ts new file mode 100644 index 000000000..52f927d30 --- /dev/null +++ b/vips/vip-600/bsctestnet.ts @@ -0,0 +1,31 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +export const UNITROLLER = "0x94d1820b2D1c7c7452A163983Dc888CEC546b77D"; +export const SWAP_ROUTER = "0xd3F226acA3210990DBA3f410b74E36b08F31FCf2"; +export const SWAP_HELPER = "0x55Fa097cA59BAc70C1ba488BEb11A5F6bf7019Eb"; + +export const vip600 = () => { + const meta = { + version: "v2", + title: "VIP-600 [BNB Chain] Implementation of SwapRouter", + description: `Implements SwapRouter contract and transfers its ownership to Venus Protocol on BNB Chain.`, + forDescription: "Execute", + againstDescription: "Do not execute", + abstainDescription: "Abstain", + }; + + return makeProposal( + [ + { + target: SWAP_ROUTER, + signature: "acceptOwnership()", + params: [], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip600;