diff --git a/multisig/proposals/ethereum/vip-071/index.ts b/multisig/proposals/ethereum/vip-071/index.ts new file mode 100644 index 000000000..33366bec5 --- /dev/null +++ b/multisig/proposals/ethereum/vip-071/index.ts @@ -0,0 +1,28 @@ +import { parseUnits } from "ethers/lib/utils"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { makeProposal } from "src/utils"; + +const { VTREASURY, NORMAL_TIMELOCK } = NETWORK_ADDRESSES.ethereum; + +export const LBTC = "0x8236a87084f8B84306f72007F36F2618A5634494"; +export const WBTC = "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"; +export const WBTC_RECEIVER = "0xCb09Ab3F6254437d225Ed3CABEBe0949782E2372"; +const INITIAL_SUPPLY = parseUnits("0.106", 8); +const WBTC_TO_TRANSFER = parseUnits("0.006", 8); + +export const vip071 = () => { + return makeProposal([ + { + target: VTREASURY, + signature: "withdrawTreasuryToken(address,uint256,address)", + params: [LBTC, INITIAL_SUPPLY, NORMAL_TIMELOCK], + }, + { + target: VTREASURY, + signature: "withdrawTreasuryToken(address,uint256,address)", + params: [WBTC, WBTC_TO_TRANSFER, WBTC_RECEIVER], + }, + ]); +}; + +export default vip071; diff --git a/multisig/simulations/ethereum/vip-071/abi/erc20.json b/multisig/simulations/ethereum/vip-071/abi/erc20.json new file mode 100644 index 000000000..374b04c75 --- /dev/null +++ b/multisig/simulations/ethereum/vip-071/abi/erc20.json @@ -0,0 +1,295 @@ +[ + { + "inputs": [ + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "userAddress", "type": "address" }, + { "indexed": false, "internalType": "address payable", "name": "relayerAddress", "type": "address" }, + { "indexed": false, "internalType": "bytes", "name": "functionSignature", "type": "bytes" } + ], + "name": "MetaTransactionExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "previousAdminRole", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ERC712_VERSION", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PREDICATE_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "userAddress", "type": "address" }, + { "internalType": "bytes", "name": "functionSignature", "type": "bytes" }, + { "internalType": "bytes32", "name": "sigR", "type": "bytes32" }, + { "internalType": "bytes32", "name": "sigS", "type": "bytes32" }, + { "internalType": "uint8", "name": "sigV", "type": "uint8" } + ], + "name": "executeMetaTransaction", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeperator", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "user", "type": "address" }], + "name": "getNonce", + "outputs": [{ "internalType": "uint256", "name": "nonce", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "getRoleMember", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleMemberCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/multisig/simulations/ethereum/vip-071/index.ts b/multisig/simulations/ethereum/vip-071/index.ts new file mode 100644 index 000000000..b619f7e98 --- /dev/null +++ b/multisig/simulations/ethereum/vip-071/index.ts @@ -0,0 +1,37 @@ +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { forking, pretendExecutingVip } from "src/vip-framework"; + +import vip071, { LBTC, WBTC, WBTC_RECEIVER } from "../../../proposals/ethereum/vip-071"; +import ERC20_ABI from "./abi/erc20.json"; + +const { NORMAL_TIMELOCK, VTREASURY } = NETWORK_ADDRESSES.ethereum; + +forking(21285800, async () => { + const lbtc = new ethers.Contract(LBTC, ERC20_ABI, ethers.provider); + const wbtc = new ethers.Contract(WBTC, ERC20_ABI, ethers.provider); + let wbtcReceiverBalanceBefore: BigNumber; + + before(async () => { + wbtcReceiverBalanceBefore = await wbtc.balanceOf(WBTC_RECEIVER); + }); + + describe("Post-VIP behavior", async () => { + before(async () => { + await pretendExecutingVip(await vip071()); + }); + + it("transfers initial supply from treasury to normal timelock", async () => { + expect(await lbtc.balanceOf(NORMAL_TIMELOCK)).to.equal(parseUnits("0.106", 8)); + expect(await lbtc.balanceOf(VTREASURY)).to.equal(0); + }); + + it(`transfers 0.006 WBTC to ${WBTC_RECEIVER}`, async () => { + const wbtcReceiverBalanceAfter = await wbtc.balanceOf(WBTC_RECEIVER); + expect(wbtcReceiverBalanceAfter.sub(wbtcReceiverBalanceBefore)).to.equal(parseUnits("0.006", 8)); + }); + }); +}); diff --git a/simulations/vip-401/bscmainnet.ts b/simulations/vip-401/bscmainnet.ts new file mode 100644 index 000000000..47c695fd2 --- /dev/null +++ b/simulations/vip-401/bscmainnet.ts @@ -0,0 +1,17 @@ +import { expectEvents } from "../../src/utils"; +import { forking, testVip } from "../../src/vip-framework"; +import vip401 from "../../vips/vip-401/bscmainnet"; +import OMNICHAIN_PROPOSAL_SENDER_ABI from "./abi/OmnichainProposalSender.json"; + +forking(43771100, async () => { + testVip("LBTC", await vip401(), { + callbackAfterExecution: async txResponse => { + await expectEvents( + txResponse, + [OMNICHAIN_PROPOSAL_SENDER_ABI], + ["ExecuteRemoteProposal", "StorePayload"], + [1, 0], + ); + }, + }); +}); diff --git a/simulations/vip-401/ethereum.ts b/simulations/vip-401/ethereum.ts new file mode 100644 index 000000000..1a1cb3248 --- /dev/null +++ b/simulations/vip-401/ethereum.ts @@ -0,0 +1,150 @@ +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { setMaxStalePeriodInChainlinkOracle, setRedstonePrice } from "src/utils"; +import { checkIsolatedPoolsComptrollers } from "src/vip-framework/checks/checkIsolatedPoolsComptrollers"; +import { checkRiskParameters } from "src/vip-framework/checks/checkRiskParameters"; +import { checkVToken } from "src/vip-framework/checks/checkVToken"; +import { checkInterestRate } from "src/vip-framework/checks/interestRateModel"; +import { forking, pretendExecutingVip, testForkedNetworkVipCommands } from "src/vip-framework/index"; + +import vip071 from "../../multisig/proposals/ethereum/vip-071"; +import vip401, { + CONVERSION_INCENTIVE, + LBTC_REDSTONE_FEED, + converterBaseAssets, + marketSpec, +} from "../../vips/vip-401/bscmainnet"; +import POOL_REGISTRY_ABI from "./abi/PoolRegistry.json"; +import RESILIENT_ORACLE_ABI from "./abi/ResilientOracle.json"; +import SINGLE_TOKEN_CONVERTER_ABI from "./abi/SingleTokenConverter.json"; +import COMPTROLLER_ABI from "./abi/comptroller.json"; +import VTOKEN_ABI from "./abi/vToken.json"; + +const { ethereum } = NETWORK_ADDRESSES; +const PROTOCOL_SHARE_RESERVE = "0x8c8c8530464f7D95552A11eC31Adbd4dC4AC4d3E"; +const BLOCKS_PER_YEAR = BigNumber.from(2628000); +const WBTC = "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"; +const WBTC_CHAINLINK_FEED = "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c"; +const LBTC_HOLDER = "0x468c34703F6c648CCf39DBaB11305D17C70ba011"; + +const { POOL_REGISTRY, RESILIENT_ORACLE, REDSTONE_ORACLE, NORMAL_TIMELOCK, GUARDIAN, CHAINLINK_ORACLE, VTREASURY } = + ethereum; + +forking(21285800, async () => { + const resilientOracle = new ethers.Contract(RESILIENT_ORACLE, RESILIENT_ORACLE_ABI, ethers.provider); + const rsOracle = new ethers.Contract(REDSTONE_ORACLE, RESILIENT_ORACLE_ABI, ethers.provider); + const poolRegistry = new ethers.Contract(POOL_REGISTRY, POOL_REGISTRY_ABI, ethers.provider); + const vToken = new ethers.Contract(marketSpec.vToken.address, VTOKEN_ABI, ethers.provider); + const comptroller = new ethers.Contract(marketSpec.vToken.comptroller, COMPTROLLER_ABI, ethers.provider); + + before(async () => { + const ONE_YEAR = 31536000; + await setRedstonePrice( + REDSTONE_ORACLE, + marketSpec.vToken.underlying.address, + LBTC_REDSTONE_FEED, + NORMAL_TIMELOCK, + ONE_YEAR, + { tokenDecimals: marketSpec.vToken.underlying.decimals }, + ); + await setMaxStalePeriodInChainlinkOracle(CHAINLINK_ORACLE, WBTC, WBTC_CHAINLINK_FEED, GUARDIAN); + + await pretendExecutingVip(await vip071()); + }); + + describe("Pre-VIP behavior", () => { + it("check price", async () => { + await expect(resilientOracle.getPrice(marketSpec.vToken.underlying.address)).to.be.reverted; + }); + + it("should have 11 markets in core pool", async () => { + const poolVTokens = await comptroller.getAllMarkets(); + expect(poolVTokens).to.have.lengthOf(11); + }); + }); + + testForkedNetworkVipCommands("LBTC", await vip401()); + + describe("Post-VIP behavior", async () => { + before(async () => { + await pretendExecutingVip(await vip401()); + }); + + it("check price", async () => { + console.log("correlated price", await rsOracle.getPrice(marketSpec.vToken.underlying.address)); + console.log("base price", await resilientOracle.getPrice(WBTC)); + expect(await resilientOracle.getPrice(marketSpec.vToken.underlying.address)).to.be.closeTo( + parseUnits("94993.81299751", 28), + parseUnits("1", 18), + ); + expect(await resilientOracle.getUnderlyingPrice(marketSpec.vToken.address)).to.be.closeTo( + parseUnits("94993.81299751", 28), + parseUnits("1", 18), + ); + }); + + it("should have 12 markets in core pool", async () => { + const poolVTokens = await comptroller.getAllMarkets(); + expect(poolVTokens).to.have.lengthOf(12); + }); + + it(`should add ${marketSpec.vToken.symbol} to the pool`, async () => { + const registeredVToken = await poolRegistry.getVTokenForAsset( + comptroller.address, + marketSpec.vToken.underlying.address, + ); + expect(registeredVToken).to.equal(marketSpec.vToken.address); + }); + + it("has correct owner", async () => { + expect(await vToken.owner()).to.equal(GUARDIAN); + }); + + it("should send 0.006 vLBTC_Core to treasury", async () => { + const treasuryShare = parseUnits("0.006", 8); + expect(await vToken.balanceOf(VTREASURY)).to.equal(treasuryShare); + }); + + it("should send 0.1 vLBTC_Core to receiver", async () => { + const projectShare = parseUnits("0.1", 8); + expect(await vToken.balanceOf(marketSpec.initialSupply.vTokenReceiver)).to.equal(projectShare); + }); + + it("should not keep any vTokens in the timelock", async () => { + expect(await vToken.balanceOf(NORMAL_TIMELOCK)).to.equal(0); + }); + + it("has correct protocol share reserve", async () => { + expect(await vToken.protocolShareReserve()).equals(PROTOCOL_SHARE_RESERVE); + }); + + checkRiskParameters(marketSpec.vToken.address, marketSpec.vToken, marketSpec.riskParameters); + checkVToken(marketSpec.vToken.address, marketSpec.vToken); + checkInterestRate( + marketSpec.interestRateModel.address, + marketSpec.vToken.symbol, + marketSpec.interestRateModel, + BLOCKS_PER_YEAR, + ); + + it("check Pool", async () => { + checkIsolatedPoolsComptrollers({ + [marketSpec.vToken.comptroller]: LBTC_HOLDER, + }); + }); + + describe("Converters", () => { + for (const [converterAddress, baseAsset] of Object.entries(converterBaseAssets)) { + const converterContract = new ethers.Contract(converterAddress, SINGLE_TOKEN_CONVERTER_ABI, ethers.provider); + const asset = marketSpec.vToken.underlying.address; + it(`should set ${CONVERSION_INCENTIVE} as incentive in converter ${converterAddress}, for asset ${asset}`, async () => { + const result = await converterContract.conversionConfigurations(baseAsset, asset); + expect(result.incentive).to.equal(CONVERSION_INCENTIVE); + }); + } + }); + }); +}); diff --git a/vips/vip-401/bscmainnet.ts b/vips/vip-401/bscmainnet.ts new file mode 100644 index 000000000..f999a86b5 --- /dev/null +++ b/vips/vip-401/bscmainnet.ts @@ -0,0 +1,186 @@ +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { LzChainId, ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { ethereum } = NETWORK_ADDRESSES; + +const LBTC = "0x8236a87084f8B84306f72007F36F2618A5634494"; +const LBTC_VTOKEN = "0x25C20e6e110A1cE3FEbaCC8b7E48368c7b2F0C91"; +const CORE_POOL_COMPTROLLER = "0x687a01ecF6d3907658f7A7c714749fAC32336D1B"; +const REDUCE_RESERVES_BLOCK_DELTA = "7200"; + +const { POOL_REGISTRY, REDSTONE_ORACLE, RESILIENT_ORACLE, VTREASURY, NORMAL_TIMELOCK } = ethereum; + +export const marketSpec = { + vToken: { + address: LBTC_VTOKEN, + name: "Venus LBTC (Core)", + symbol: "vLBTC_Core", + underlying: { + address: LBTC, + decimals: 8, + symbol: "LBTC", + }, + decimals: 8, + exchangeRate: parseUnits("1", 18), + comptroller: CORE_POOL_COMPTROLLER, + }, + interestRateModel: { + address: "0xD9a049512ABaA7073D02a398ceD1B92371bff622", // JumpRateModelV2_base0bps_slope900bps_jump20000bps_kink4500bps + base: "0", + multiplier: "0.09", + jump: "2", + kink: "0.45", + }, + initialSupply: { + amount: parseUnits("0.106", 8), + vTokenReceiver: "0xCb09Ab3F6254437d225Ed3CABEBe0949782E2372", + }, + riskParameters: { + supplyCap: parseUnits("450", 8), + borrowCap: parseUnits("45", 8), + collateralFactor: parseUnits("0.735", 18), + liquidationThreshold: parseUnits("0.785", 18), + reserveFactor: parseUnits("0.2", 18), + protocolSeizeShare: parseUnits("0.05", 18), + }, +}; + +const LBTC_ONE_JUMP_REDSTONE_ORACLE = "0x3c8C488d65F2AFDe15F285eAAF4B153C4d35A944"; +export const LBTC_REDSTONE_FEED = "0xb415eAA355D8440ac7eCB602D3fb67ccC1f0bc81"; +const MAX_STALE_PERIOD = 26 * 3600; // heartbeat of 24 hours + 2 hours margin + +export const CONVERSION_INCENTIVE = 1e14; + +export const USDT_PRIME_CONVERTER = "0x4f55cb0a24D5542a3478B0E284259A6B850B06BD"; +export const USDC_PRIME_CONVERTER = "0xcEB9503f10B781E30213c0b320bCf3b3cE54216E"; +export const WBTC_PRIME_CONVERTER = "0xDcCDE673Cd8988745dA384A7083B0bd22085dEA0"; +export const WETH_PRIME_CONVERTER = "0xb8fD67f215117FADeF06447Af31590309750529D"; +export const XVS_VAULT_CONVERTER = "0x1FD30e761C3296fE36D9067b1e398FD97B4C0407"; +const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; +const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; +const WBTC = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"; +const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; +const XVS = "0xd3CC9d8f3689B83c91b7B59cAB4946B063EB894A"; +export const converterBaseAssets = { + [USDT_PRIME_CONVERTER]: USDT, + [USDC_PRIME_CONVERTER]: USDC, + [WBTC_PRIME_CONVERTER]: WBTC, + [WETH_PRIME_CONVERTER]: WETH, + [XVS_VAULT_CONVERTER]: XVS, +}; + +enum ConversionAccessibility { + NONE = 0, + ALL = 1, + ONLY_FOR_CONVERTERS = 2, + ONLY_FOR_USERS = 3, +} + +export const vip401 = () => { + const meta = { + version: "v2", + title: "VIP-401", + description: `LBTC`, + forDescription: "Process to configure and launch the new market", + againstDescription: "Defer configuration and launch of the new market", + abstainDescription: "No opinion on the matter", + }; + + return makeProposal( + [ + // Configure Oracle + { + target: REDSTONE_ORACLE, + signature: "setTokenConfig((address,address,uint256))", + params: [[marketSpec.vToken.underlying.address, LBTC_REDSTONE_FEED, MAX_STALE_PERIOD]], + dstChainId: LzChainId.ethereum, + }, + { + target: RESILIENT_ORACLE, + signature: "setTokenConfig((address,address[3],bool[3]))", + params: [ + [ + marketSpec.vToken.underlying.address, + [LBTC_ONE_JUMP_REDSTONE_ORACLE, ethers.constants.AddressZero, ethers.constants.AddressZero], + [true, false, false], + ], + ], + dstChainId: LzChainId.ethereum, + }, + + // Add Market + { + target: marketSpec.vToken.underlying.address, + signature: "approve(address,uint256)", + params: [POOL_REGISTRY, marketSpec.initialSupply.amount], + dstChainId: LzChainId.ethereum, + }, + { + target: marketSpec.vToken.address, + signature: "setReduceReservesBlockDelta(uint256)", + params: [REDUCE_RESERVES_BLOCK_DELTA], + dstChainId: LzChainId.ethereum, + }, + { + target: POOL_REGISTRY, + signature: "addMarket((address,uint256,uint256,uint256,address,uint256,uint256))", + params: [ + [ + marketSpec.vToken.address, + marketSpec.riskParameters.collateralFactor, + marketSpec.riskParameters.liquidationThreshold, + marketSpec.initialSupply.amount, + NORMAL_TIMELOCK, + marketSpec.riskParameters.supplyCap, + marketSpec.riskParameters.borrowCap, + ], + ], + dstChainId: LzChainId.ethereum, + }, + { + target: marketSpec.vToken.underlying.address, + signature: "approve(address,uint256)", + params: [POOL_REGISTRY, 0], + dstChainId: LzChainId.ethereum, + }, + { + target: marketSpec.vToken.address, + signature: "setProtocolSeizeShare(uint256)", + params: [marketSpec.riskParameters.protocolSeizeShare], + dstChainId: LzChainId.ethereum, + }, + + { + target: marketSpec.vToken.address, + signature: "transfer(address,uint256)", + params: [marketSpec.initialSupply.vTokenReceiver, parseUnits("0.1", 8)], + dstChainId: LzChainId.ethereum, + }, + { + target: marketSpec.vToken.address, + signature: "transfer(address,uint256)", + params: [VTREASURY, parseUnits("0.006", 8)], + dstChainId: LzChainId.ethereum, + }, + + // Configure converters + ...Object.entries(converterBaseAssets).map(([converter, baseAsset]: [string, string]) => ({ + target: converter, + signature: "setConversionConfigs(address,address[],(uint256,uint8)[])", + params: [ + baseAsset, + [marketSpec.vToken.underlying.address], + [[CONVERSION_INCENTIVE, ConversionAccessibility.ALL]], + ], + dstChainId: LzChainId.ethereum, + })), + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip401;