diff --git a/contracts/identity/IdentityV3.sol b/contracts/identity/IdentityV3.sol index c572de3c..3c37186f 100644 --- a/contracts/identity/IdentityV3.sol +++ b/contracts/identity/IdentityV3.sol @@ -35,6 +35,7 @@ contract IdentityV3 is string public constant TYPED_STRUCTURE = "ConnectIdentity(address whitelisted,address connected,uint256 deadline)"; + /* @dev rough estimate of number of whitelisted addresses */ uint256 public whitelistedCount; uint256 public whitelistedContracts; uint256 public authenticationPeriod; @@ -236,6 +237,7 @@ contract IdentityV3 is function addBlacklisted( address account ) public onlyRole(IDENTITY_ADMIN_ROLE) whenNotPaused { + _removeWhitelisted(account); identities[account].status = 255; emit BlacklistAdded(account); } diff --git a/contracts/reserve/GenericDistributionHelper.sol b/contracts/reserve/GenericDistributionHelper.sol index 8fd62ed2..c9d193ab 100644 --- a/contracts/reserve/GenericDistributionHelper.sol +++ b/contracts/reserve/GenericDistributionHelper.sol @@ -58,6 +58,8 @@ contract GenericDistributionHelper is address public reserveToken; + uint256 private _status; //for reentrancy guard + event Distribution( uint256 distributed, uint256 startingBalance, @@ -76,6 +78,18 @@ contract GenericDistributionHelper is receive() external payable {} + modifier nonReentrant() { + // On the first call to nonReentrant, _status will be _NOT_ENTERED + require(_status != 1, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = 1; + _; + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = 0; + } + function initialize( INameService _ns, IStaticOracle _oracle, @@ -129,7 +143,7 @@ contract GenericDistributionHelper is * @notice this is usually called by reserve, but can be called by anyone anytime to trigger distribution * @param _amount how much was sent, informational only */ - function onDistribution(uint256 _amount) external virtual { + function onDistribution(uint256 _amount) external virtual nonReentrant { //we consider the actual balance and not _amount uint256 toDistribute = nativeToken().balanceOf(address(this)); if (toDistribute == 0) return; @@ -300,7 +314,7 @@ contract GenericDistributionHelper is } for (uint i = 1; i < gdPools.length; i++) { uint24 fee = IUniswapV3Pool(gdPools[i]).fee(); - gdFee = gasFee < fee ? gasFee : fee; + gdFee = gdFee < fee ? gdFee : fee; } ERC20(nativeToken()).approve(address(ROUTER), amountToSell); uint256 amountOutMinimum = (minReceived * (100 - feeSettings.maxSlippage)) / diff --git a/hardhat.config.ts b/hardhat.config.ts index 6aeaf12d..d733e784 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -41,7 +41,7 @@ const MAINNET_URL = "https://mainnet.infura.io/v3/" + infura_api; const xdc = { accounts: { mnemonic }, - url: "https://rpc.ankr.com/xdc/ef07ba6590dc46db9275bba237aed203ed6d5fb3e3203ff237a82a841f75b2ce", + url: "https://rpc.ankr.com/xdc", gas: 3000000, gasPrice: 12.5e9, chainId: 50 @@ -65,15 +65,7 @@ const hhconfig: HardhatUserConfig = { enabled: true }, etherscan: { - apiKey: { - mainnet: etherscan_key, - txdc: etherscan_key, - xdc: etherscan_key, - celo: etherscan_key, - alfajores: celoscan_key, - base: basescan_key, - fuse: etherscan_key - }, + apiKey: etherscan_key, customChains: [ { network: "fuse", diff --git a/releases/deployment.json b/releases/deployment.json index 68256243..5c8d930b 100644 --- a/releases/deployment.json +++ b/releases/deployment.json @@ -684,6 +684,8 @@ "AdminWallet": "0x66fc1bE551f752706130b6f54d84141F8c2Ae8Bb", "Faucet": "0x7344Da1Be296f03fbb8082aDaC5696058B5a9bd9", "Invites": "0x6bd698566632bf2e81e2278f1656CB24aAF06D2e", - "UBIScheme": "0x22867567E2D80f2049200E25C6F31CB6Ec2F0faf" + "UBIScheme": "0x22867567E2D80f2049200E25C6F31CB6Ec2F0faf", + "MpbBridge": "0xa3247276DbCC76Dd7705273f766eB3E8a5ecF4a5", + "BulkWhitelist": "0xe8861A20452Db53dF685F1d9e6B7017de3DB0E46" } } diff --git a/scripts/multichain-deploy/helpers.ts b/scripts/multichain-deploy/helpers.ts index 948144c1..32f8796e 100644 --- a/scripts/multichain-deploy/helpers.ts +++ b/scripts/multichain-deploy/helpers.ts @@ -5,7 +5,7 @@ import { TransactionResponse, TransactionReceipt } from "@ethersproject/provider import { getImplementationAddress } from "@openzeppelin/upgrades-core"; import SafeApiKit from "@safe-global/api-kit"; -import Safe from "@safe-global/protocol-kit"; +import Safe, { SafeTransactionOptionalProps } from "@safe-global/protocol-kit"; import { MetaTransactionData } from "@safe-global/types-kit"; import util from "util"; @@ -357,6 +357,7 @@ export const executeViaSafe = async ( functionInputs, safeAddress: string, safeSignerOrNetwork?: Signer | string, + safeOptions: SafeTransactionOptionalProps = {}, isSimulation = false ) => { if (typeof safeSignerOrNetwork !== "object" && !process.env.SAFEOWNER_PRIVATE_KEY) { @@ -387,6 +388,10 @@ export const executeViaSafe = async ( // new ethers.providers.JsonRpcProvider("https://rpc.fuse.io") // ); break; + case "xdc": + chainId = 50; + provider = "https://rpc.xdcrpc.com"; + break; } } else if (safeSignerOrNetwork) { // safeSigner = safeSignerOrNetwork as any; diff --git a/scripts/proposals/gip-25-xdc-upgrade-ubi.ts b/scripts/proposals/gip-25-xdc-upgrade-ubi.ts index 96ed9031..69e96f4f 100644 --- a/scripts/proposals/gip-25-xdc-upgrade-ubi.ts +++ b/scripts/proposals/gip-25-xdc-upgrade-ubi.ts @@ -15,6 +15,7 @@ // deploy distribution helper // transfer usdc to xdc reserve // update celo reserve parameters accordingly +// upgrade celo's distribution helper to genericdistributionhelper (simulate it is working) import { network, ethers, upgrades } from "hardhat"; import { reset } from "@nomicfoundation/hardhat-network-helpers"; @@ -30,12 +31,14 @@ import { import dao from "../../releases/deployment.json"; import { Controller, IdentityV3, IGoodDollar, IMessagePassingBridge, UBISchemeV2 } from "../../types"; +import { upgrade } from "./gip-15"; let { name: networkName } = network; const isSimulation = network.name === "hardhat" || network.name === "fork" || network.name === "localhost"; const bridgeUpgradeImpl = { - "production-celo": "0x7bDaF2Fb332761b2a6A565a43ccB0ACfC36d2C3D", - production: "0x6f252280eB53df085eAD27BBe55d615741A8268D", - "production-mainnet": "0x7baFe060A37E31E707b8B28a90a36731ee99aFBa" + "production-celo": "0xF3eAB7018d74E7Df95A5d8dC70987C0539bDF48f", + production: "0xFB62aA509a7B260b6697B671C969a184d6c39E90", + "production-mainnet": "0x12ab702f015D3302f3cc0c4AbA0626A127D06A07", + "production-xdc": "0xe4CFA18A3d0a7d77fAA42961ee943c9221d61937" }; export const upgradeCeloStep1 = async (network, checksOnly) => { let [root] = await ethers.getSigners(); @@ -177,6 +180,297 @@ export const upgradeCeloStep1 = async (network, checksOnly) => { console.log("UBI claim from connected account tx:", claimTx.events); } }; +export const upgradeCeloFix = async (network, checksOnly) => { + let [root] = await ethers.getSigners(); + + const isProduction = networkName.includes("production"); + + if (isProduction) verifyProductionSigner(root); + + let networkEnv = networkName; + let guardian = root; + if (isSimulation) { + networkEnv = network; + } + + let release: { [key: string]: any } = dao[networkEnv]; + + console.log("signer:", root.address, { networkEnv, isSimulation, isProduction, release }); + + if (isSimulation) { + networkEnv = network; + guardian = await ethers.getImpersonatedSigner(release.GuardiansSafe); + + await root.sendTransaction({ + value: ethers.utils.parseEther("1"), + to: release.GuardiansSafe + }); + } + + const bridgeImpl = bridgeUpgradeImpl[networkEnv]; + const toBurn = "5515965554075495700269228267"; + const proposalActions = [ + [release.MpbBridge, "upgradeTo(address)", ethers.utils.defaultAbiCoder.encode(["address"], [bridgeImpl]), "0"], //upgrade bridge + [release.GoodDollar, "burn(uint256)", ethers.utils.defaultAbiCoder.encode(["uint256"], [toBurn.toString()]), "0"] //burn locked supply on celo bridge + ]; + + console.log({ + networkEnv, + guardian: guardian.address, + isSimulation, + isProduction, + release + }); + + const proposalContracts = proposalActions.map(a => a[0]); + const proposalFunctionSignatures = proposalActions.map(a => a[1]); + const proposalFunctionInputs = proposalActions.map(a => a[2]); + const proposalEthValues = proposalActions.map(a => a[3]); + if (isProduction && !checksOnly) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + release.GuardiansSafe, + "celo", + { nonce: 5 } + ); + } else if (!checksOnly) { + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkEnv + ); + } +}; +export const upgradeXdcFix = async (network, checksOnly) => { + let [root] = await ethers.getSigners(); + + const isProduction = networkName.includes("production"); + + if (isProduction) verifyProductionSigner(root); + + let networkEnv = networkName; + let guardian = root; + if (isSimulation) { + networkEnv = network; + } + + let release: { [key: string]: any } = dao[networkEnv]; + + console.log("signer:", root.address, { networkEnv, isSimulation, isProduction, release }); + + if (isSimulation) { + networkEnv = network; + guardian = await ethers.getImpersonatedSigner(release.GuardiansSafe); + + await root.sendTransaction({ + value: ethers.utils.parseEther("1"), + to: release.GuardiansSafe + }); + } + + const bridgeImpl = bridgeUpgradeImpl[networkEnv]; + const proposalActions = [ + [release.MpbBridge, "upgradeTo(address)", ethers.utils.defaultAbiCoder.encode(["address"], [bridgeImpl]), "0"] //upgrade bridge + ]; + + console.log({ + networkEnv, + guardian: guardian.address, + isSimulation, + isProduction, + release + }); + + const proposalContracts = proposalActions.map(a => a[0]); + const proposalFunctionSignatures = proposalActions.map(a => a[1]); + const proposalFunctionInputs = proposalActions.map(a => a[2]); + const proposalEthValues = proposalActions.map(a => a[3]); + if (isProduction && !checksOnly) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + release.GuardiansSafe, + "xdc" + ); + } else if (!checksOnly) { + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkEnv + ); + } +}; +export const upgradeFuseFix = async (network, checksOnly) => { + let [root] = await ethers.getSigners(); + + const isProduction = networkName.includes("production"); + + if (isProduction) verifyProductionSigner(root); + + let networkEnv = networkName; + let guardian = root; + if (isSimulation) { + networkEnv = network; + } + + let release: { [key: string]: any } = dao[networkEnv]; + + console.log("signer:", root.address, { networkEnv, isSimulation, isProduction, release }); + + if (isSimulation) { + networkEnv = network; + guardian = await ethers.getImpersonatedSigner(release.GuardiansSafe); + + await root.sendTransaction({ + value: ethers.utils.parseEther("1"), + to: release.GuardiansSafe + }); + } + + const bridgeImpl = bridgeUpgradeImpl[networkEnv]; + const proposalActions = [ + [release.MpbBridge, "upgradeTo(address)", ethers.utils.defaultAbiCoder.encode(["address"], [bridgeImpl]), "0"] //upgrade bridge + ]; + + console.log({ + networkEnv, + guardian: guardian.address, + isSimulation, + isProduction, + release + }); + + const proposalContracts = proposalActions.map(a => a[0]); + const proposalFunctionSignatures = proposalActions.map(a => a[1]); + const proposalFunctionInputs = proposalActions.map(a => a[2]); + const proposalEthValues = proposalActions.map(a => a[3]); + if (isProduction && !checksOnly) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + release.GuardiansSafe, + "fuse" + ); + } else if (!checksOnly) { + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkEnv + ); + } +}; +export const upgradeEthFix = async (network, checksOnly) => { + let [root] = await ethers.getSigners(); + + const isProduction = networkName.includes("production"); + + if (isProduction) verifyProductionSigner(root); + + let networkEnv = networkName; + let guardian = root; + if (isSimulation) { + networkEnv = network; + } + + let release: { [key: string]: any } = dao[networkEnv]; + + console.log("signer:", root.address, { networkEnv, isSimulation, isProduction, release }); + + if (isSimulation) { + networkEnv = network; + guardian = await ethers.getImpersonatedSigner(release.GuardiansSafe); + + await root.sendTransaction({ + value: ethers.utils.parseEther("1"), + to: release.GuardiansSafe + }); + } + + const bridgeImpl = bridgeUpgradeImpl[networkEnv]; + const upgradeCall = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("upgrade()")).substring(0, 10); + + const proposalActions = [ + [ + release.MpbBridge, + "upgradeToAndCall(address,bytes)", + ethers.utils.defaultAbiCoder.encode(["address", "bytes"], [bridgeImpl, upgradeCall]), + "0" + ] //upgrade bridge + ]; + + console.log({ + networkEnv, + guardian: guardian.address, + isSimulation, + isProduction, + release + }); + + const proposalContracts = proposalActions.map(a => a[0]); + const proposalFunctionSignatures = proposalActions.map(a => a[1]); + const proposalFunctionInputs = proposalActions.map(a => a[2]); + const proposalEthValues = proposalActions.map(a => a[3]); + if (isProduction && !checksOnly) { + await executeViaSafe( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + release.GuardiansSafe, + "mainnet", + { nonce: 15 } + ); + } else if (!checksOnly) { + await executeViaGuardian( + proposalContracts, + proposalEthValues, + proposalFunctionSignatures, + proposalFunctionInputs, + guardian, + networkEnv + ); + } + + if (isSimulation || !isProduction) { + const ctrl = (await ethers.getContractAt("Controller", release.Controller)) as Controller; + const isMinterScheme = await ctrl.getSchemePermissions(release.MpbBridge, release.Avatar); + console.log("Bridge minter permissions on avatar:", isMinterScheme); + // check xdc chainid in bridge + const mpb = (await ethers.getContractAt("IMessagePassingBridge", release.MpbBridge)) as IMessagePassingBridge; + console.log("xdc lz chainid:", await mpb.toLzChainId(50)); + } +}; + +export const verifyUpgradeCeloStep1 = async networkEnv => { + let release: { [key: string]: any } = dao[networkEnv]; + const ctrl = (await ethers.getContractAt("Controller", release.Controller)) as Controller; + const isMinterScheme = await ctrl.getSchemePermissions(release.MpbBridge, release.Avatar); + console.log("Bridge minter permissions on avatar:", isMinterScheme); + // check xdc chainid in bridge + const mpb = (await ethers.getContractAt("IMessagePassingBridge", release.MpbBridge)) as IMessagePassingBridge; + console.log("xdc lz chainid:", await mpb.toLzChainId(50)); + const gd = await ethers.getContractAt("IGoodDollar", release.GoodDollar); + const supplyAfter = await gd.totalSupply(); + const bridgeBalanceAfter = await gd.balanceOf(release.MpbBridge); + const avatarBalanceAfter = await gd.balanceOf(release.Avatar); + console.log({ bridgeBalanceAfter, avatarBalanceAfter, supplyAfter }); +}; export const upgradeFuseStep1 = async (network, checksOnly) => { let [root] = await ethers.getSigners(); @@ -444,4 +738,9 @@ export const main = async () => { } }; -main().catch(console.log); +// upgradeCeloFix("production-celo", false).catch(console.log); +// upgradeEthFix("production-mainnet", false).catch(console.log); +upgradeFuseFix("production", false).catch(console.log); +// upgradeXdcFix("production-xdc", false).catch(console.log); +// verifyUpgradeCeloStep1("production-celo"); +// main().catch(console.log); diff --git a/yarn.lock b/yarn.lock index 185f1433..e7c11c39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3773,46 +3773,25 @@ __metadata: linkType: hard "@safe-global/api-kit@npm:^4.0.0": - version: 4.0.0 - resolution: "@safe-global/api-kit@npm:4.0.0" + version: 4.0.1 + resolution: "@safe-global/api-kit@npm:4.0.1" dependencies: - "@safe-global/protocol-kit": ^6.1.0 + "@safe-global/protocol-kit": ^6.1.2 "@safe-global/types-kit": ^3.0.0 node-fetch: ^2.7.0 viem: ^2.21.8 - checksum: 187ce7a1ddba432cdc95c05064366128dd21e9948f5b14ac304c7aef230a3d3fc8e91861303f8ecfedeaf3805fc258184511062bdfd1150b42d65a3afb145adc - languageName: node - linkType: hard - -"@safe-global/protocol-kit@npm:^6.0.2": - version: 6.0.2 - resolution: "@safe-global/protocol-kit@npm:6.0.2" - dependencies: - "@noble/curves": ^1.6.0 - "@peculiar/asn1-schema": ^2.3.13 - "@safe-global/safe-deployments": ^1.37.31 - "@safe-global/safe-modules-deployments": ^2.2.7 - "@safe-global/types-kit": ^2.0.1 - abitype: ^1.0.2 - semver: ^7.7.1 - viem: ^2.21.8 - dependenciesMeta: - "@noble/curves": - optional: true - "@peculiar/asn1-schema": - optional: true - checksum: c3ebedb87b5ee76750cf6e7b63bf55e87f1a76bb1925f01773297dcf769845eb767a037b9182032067a524229475e88994015e467144ec96bfe870906b0caac1 + checksum: f90d8939fbac4e4887576d66b61c3b24656b51816092c5a5c3b914b0d4ed6bed5475f1f2cf284696dfcfbb44740b93a23a4d8e1ccf4dc6a6f78779ea382ed351 languageName: node linkType: hard -"@safe-global/protocol-kit@npm:^6.1.0": - version: 6.1.1 - resolution: "@safe-global/protocol-kit@npm:6.1.1" +"@safe-global/protocol-kit@npm:^6.0.2, @safe-global/protocol-kit@npm:^6.1.2": + version: 6.1.2 + resolution: "@safe-global/protocol-kit@npm:6.1.2" dependencies: "@noble/curves": ^1.6.0 "@peculiar/asn1-schema": ^2.3.13 - "@safe-global/safe-deployments": ^1.37.42 - "@safe-global/safe-modules-deployments": ^2.2.14 + "@safe-global/safe-deployments": ^1.37.49 + "@safe-global/safe-modules-deployments": ^2.2.21 "@safe-global/types-kit": ^3.0.0 abitype: ^1.0.2 semver: ^7.7.2 @@ -3822,39 +3801,23 @@ __metadata: optional: true "@peculiar/asn1-schema": optional: true - checksum: 3999727d68a0504f5fd0e4ffa64fe83ff3d4ae80797106c52070cbd541f0bbaf6758f76ebf1161cce7c323d87d9e989e4aaaf8d9a3135101c1329c62443992ff + checksum: d6dfa653676e9486ac104c95b8b69fdc7d66c53a875263d0181136767e34c0e4c40500b093a2ad77672b1e2497e36f44da334eccbfd81cb12fd1a7b39ed7ee3d languageName: node linkType: hard -"@safe-global/safe-deployments@npm:^1.37.31": - version: 1.37.31 - resolution: "@safe-global/safe-deployments@npm:1.37.31" +"@safe-global/safe-deployments@npm:^1.37.49": + version: 1.37.49 + resolution: "@safe-global/safe-deployments@npm:1.37.49" dependencies: semver: ^7.6.2 - checksum: 8f209f5acf4fcffed46f2489b45fdbc58178cdafb2bc081f62442d5aeb60de581a63748e18f43d990fa1ee536ff75b6c64adacbc8264360aa8435697bfed9240 + checksum: b004057e84ae1ca68f0829bdb9f4a303e592a87cadc7688110d85d218de2f7a79b790449235e3d96b11827028b774f5becbfe1d02f9b46f4bbe50bbfe3246c78 languageName: node linkType: hard -"@safe-global/safe-deployments@npm:^1.37.42": - version: 1.37.47 - resolution: "@safe-global/safe-deployments@npm:1.37.47" - dependencies: - semver: ^7.6.2 - checksum: 952ff6e6272622d2fcde1c5a5542bdb89613f6ddf9852f829f5961096b64fffe7d8f6924ad8ca1beaf970036242d11e4027b68c202b7d205dc9c4556750983d0 - languageName: node - linkType: hard - -"@safe-global/safe-modules-deployments@npm:^2.2.14": - version: 2.2.18 - resolution: "@safe-global/safe-modules-deployments@npm:2.2.18" - checksum: 4713a9deb5d39ffa1af273c427e7647cb8e81b6212281b9f6cb83fb69701114b2c3b0bb2e0e889ea8bc71860daa8dacd1082a20df5617677d235ffdfb4ab226b - languageName: node - linkType: hard - -"@safe-global/safe-modules-deployments@npm:^2.2.7": - version: 2.2.7 - resolution: "@safe-global/safe-modules-deployments@npm:2.2.7" - checksum: 73fadef510c172f282bf73ff99d650fad7b9b70da79f09abd3d5e5907b8a33e42bf85021ceca8f345d9c57ddddffd562d2e90d4bc89b95b5dc1faf256d65cd49 +"@safe-global/safe-modules-deployments@npm:^2.2.21": + version: 2.2.21 + resolution: "@safe-global/safe-modules-deployments@npm:2.2.21" + checksum: 967fd772a27242bea089c07ab98f963551474967ff087d1a9e385909e05db572515969fd21a6965ddd50ee1018f9229d26516069809400eb11c7c4663293f9db languageName: node linkType: hard @@ -19637,7 +19600,7 @@ patch-package@latest: languageName: node linkType: hard -"semver@npm:^7.6.2, semver@npm:^7.7.1": +"semver@npm:^7.6.2": version: 7.7.1 resolution: "semver@npm:7.7.1" bin: