From 27111ca6207f1c90d6f6f7d4fed05e1ba9912f30 Mon Sep 17 00:00:00 2001 From: cwsnt Date: Fri, 19 Sep 2025 09:07:35 +0700 Subject: [PATCH] SOV-5107: sip for LiquityBaseParamsAddressChanges event arg fix --- hardhat/tasks/sips/args/sipArgs.js | 38 +++++++ tests-onchain/sip0087.test.js | 167 +++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 tests-onchain/sip0087.test.js diff --git a/hardhat/tasks/sips/args/sipArgs.js b/hardhat/tasks/sips/args/sipArgs.js index f095f6719..685d83f6a 100644 --- a/hardhat/tasks/sips/args/sipArgs.js +++ b/hardhat/tasks/sips/args/sipArgs.js @@ -1180,6 +1180,43 @@ const getArgsSip0084Part2 = async (hre) => { return { args, governor: "GovernorOwner" }; }; +const getArgsSip0087 = async (hre) => { + const { + ethers, + deployments: { get }, + } = hre; + const abiCoder = new ethers.utils.AbiCoder(); + + const newTroveManagerImplementation = await get("TroveManager_Implementation"); + const newTroveManagerRedeemOps = await get("TroveManagerRedeemOps"); + + const troveManagerProxy = await get("TroveManager_Proxy"); + const troveManagerContract = await ethers.getContract("TroveManager"); + const troveManagerOwner = await troveManagerContract.getOwner(); + + //validate + if (!network.tags.mainnet) { + logger.error("Unknown network"); + process.exit(1); + } + + const args = { + targets: [troveManagerProxy.address, troveManagerProxy.address], + targetOwnerValidationAddresses: [troveManagerOwner, troveManagerOwner], + values: [0, 0], + signatures: ["setImplementation(address)", "setTroveManagerRedeemOps(address)"], + data: [ + abiCoder.encode(["address"], [newTroveManagerImplementation.address]), + abiCoder.encode(["address"], [newTroveManagerRedeemOps.address]), + ], + // @todo update sip description & sha256 + description: + "SIP-0087: Fix LiquityBaseParamsAddressChanges event argument, Details: https://github.com/DistributedCollective/SIPS/blob/a86ac0e/SIP-0087.md, sha256: ", + }; + + return { args, governor: "GovernorOwner" }; +}; + module.exports = { sampleGovernorAdminSIP, sampleGovernorOwnerSIP, @@ -1202,4 +1239,5 @@ module.exports = { getArgsSip0079, getArgsSip0084Part1, getArgsSip0084Part2, + getArgsSip0087, }; diff --git a/tests-onchain/sip0087.test.js b/tests-onchain/sip0087.test.js new file mode 100644 index 000000000..1cee4fbe2 --- /dev/null +++ b/tests-onchain/sip0087.test.js @@ -0,0 +1,167 @@ +// first run a local forked mainnet node in a separate terminal window: +// npx hardhat node --fork https://mainnet-dev.sovryn.app/rpc --no-deploy +// now run the test: +// npx hardhat test tests-onchain/sip0087.test.js --network rskForkedMainnet + +const { + impersonateAccount, + mine, + time, + setBalance, +} = require("@nomicfoundation/hardhat-network-helpers"); +const hre = require("hardhat"); +const { getProtocolModules } = require("../deployment/helpers/helpers"); + +const { + ethers, + deployments: { createFixture, get }, +} = hre; + +const MAX_DURATION = ethers.BigNumber.from(24 * 60 * 60).mul(1092); + +const ONE_RBTC = ethers.utils.parseEther("1.0"); + +const getImpersonatedSigner = async (addressToImpersonate) => { + await impersonateAccount(addressToImpersonate); + return await ethers.getSigner(addressToImpersonate); +}; + +describe("Protocol Modules Deployments and Upgrades via Governance", () => { + const getImpersonatedSignerFromJsonRpcProvider = async (addressToImpersonate) => { + const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); + await provider.send("hardhat_impersonateAccount", [addressToImpersonate]); + return provider.getSigner(addressToImpersonate); + }; + + const setupTest = createFixture(async ({ deployments }) => { + const deployer = (await ethers.getSigners())[0].address; + const deployerSigner = await ethers.getSigner(deployer); + + const multisigAddress = (await get("MultiSigWallet")).address; + const multisigSigner = await getImpersonatedSignerFromJsonRpcProvider(multisigAddress); + + await setBalance(deployer, ONE_RBTC.mul(10)); + await deployments.fixture(["ProtocolModules"], { + keepExistingDeployments: true, + }); // start from a fresh deployments + + const staking = await ethers.getContract("Staking", deployerSigner); + const sovrynProtocol = await ethers.getContract("SovrynProtocol", deployerSigner); + + const god = await deployments.get("GovernorOwner"); + const governorOwner = await ethers.getContractAt( + "GovernorAlpha", + god.address, + deployerSigner + ); + const governorOwnerSigner = await getImpersonatedSigner(god.address); + + await setBalance(governorOwnerSigner.address, ONE_RBTC); + const timelockOwner = await ethers.getContract("TimelockOwner", governorOwnerSigner); + + const timelockOwnerSigner = await getImpersonatedSignerFromJsonRpcProvider( + timelockOwner.address + ); + await setBalance(timelockOwnerSigner._address, ONE_RBTC); + + // + return { + deployer, + deployerSigner, + staking, + sovrynProtocol, + governorOwner, + governorOwnerSigner, + timelockOwner, + timelockOwnerSigner, + multisigAddress, + multisigSigner, + }; + }); + + /// @todo change the SIP name + describe("SIP-0087 Test creation and execution", () => { + it("SIP-0087 is executable and valid", async () => { + if (!hre.network.tags["forked"]) { + console.error("ERROR: Must run on a forked net"); + return; + } + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: "https://mainnet-dev.sovryn.app/rpc", + blockNumber: 6037998, + }, + }, + ], + }); + + const { + deployer, + deployerSigner, + staking, + sovrynProtocol, + governorOwner, + timelockOwnerSigner, + multisigAddress, + multisigSigner, + } = await setupTest(); + + // CREATE PROPOSAL + const sov = await ethers.getContract("SOV", timelockOwnerSigner); + const whaleAmount = (await sov.totalSupply()).mul(ethers.BigNumber.from(5)); + await sov.mint(deployer, whaleAmount); + + await sov.connect(deployerSigner).approve(staking.address, whaleAmount); + + if (await staking.paused()) await staking.connect(multisigSigner).pauseUnpause(false); + const kickoffTS = await staking.kickoffTS(); + await staking.stake(whaleAmount, kickoffTS.add(MAX_DURATION), deployer, deployer); + await mine(); + + // CREATE PROPOSAL AND VERIFY + const proposalIdBeforeSIP = await governorOwner.latestProposalIds(deployer); + await hre.run("sips:create", { argsFunc: "getArgsSip0087" }); + const proposalId = await governorOwner.latestProposalIds(deployer); + expect( + proposalId, + "Proposal was not created. Check the SIP creation is not commented out." + ).is.gt(proposalIdBeforeSIP); + + // VOTE FOR PROPOSAL + + await mine(); + await governorOwner.connect(deployerSigner).castVote(proposalId, true); + + // QUEUE PROPOSAL + let proposal = await governorOwner.proposals(proposalId); + await mine(proposal.endBlock); + await governorOwner.queue(proposalId); + + // EXECUTE PROPOSAL + proposal = await governorOwner.proposals(proposalId); + await time.increaseTo(proposal.eta); + await expect(governorOwner.execute(proposalId)) + .to.emit(governorOwner, "ProposalExecuted") + .withArgs(proposalId); + + // VERIFY execution + expect((await governorOwner.proposals(proposalId)).executed).to.be.true; + + // Validate trove manager contracs upgrade + const troveManagerProxy = await ethers.getContract("TroveManager_Proxy"); + const troveManagerImpl = await get("TroveManager_Implementation"); + + const troveManager = await ethers.getContract("TroveManager"); + const troveManagerRedeemOps = await get("TroveManagerRedeemOps"); + + expect(await troveManagerProxy.getImplementation()).to.equal(troveManagerImpl.address); + + expect(await troveManager.troveManagerRedeemOps()).to.equal( + troveManagerRedeemOps.address + ); + }); + }); +});