diff --git a/.env.example b/.env.example index e0101e39..30ef154e 100644 --- a/.env.example +++ b/.env.example @@ -9,4 +9,4 @@ COINMARKETCAP_KEY = "COINMARKETCAP API KEY" TYPECHAIN_TARGET = "TYPECHAIN TARGET" # Environment -ENVIRONMENT = "PROD|STAGE|DEV|DEV_SEPOLIA|DEV_MUMBAI" +ENVIRONMENT = "PROD|STAGE|DEV|DEV_SEPOLIA" diff --git a/contracts/core/network-properties/BSCProperties.sol b/contracts/core/network-properties/BSCProperties.sol index 600e935e..19e4dab3 100644 --- a/contracts/core/network-properties/BSCProperties.sol +++ b/contracts/core/network-properties/BSCProperties.sol @@ -1,12 +1,36 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; +import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import "@solarity/solidity-lib/access-control/MultiOwnable.sol"; + +import "@uniswap/v2-periphery/contracts/interfaces/IWETH.sol"; + import "../../interfaces/core/INetworkProperties.sol"; -contract BSCProperties is INetworkProperties { +contract BSCProperties is INetworkProperties, MultiOwnable, UUPSUpgradeable { uint256 private constant BNB_SUPPLY = 150_000_000 * 10 ** 18; + IWETH public weth; + + function __NetworkProperties_init(address weth_) external initializer { + __MultiOwnable_init(); + + weth = IWETH(weth_); + } + + function unwrapWeth(uint256 amount) external override { + weth.withdraw(amount); + + (bool ok, ) = payable(msg.sender).call{value: amount}(""); + assert(ok); + } + + receive() external payable {} + function getNativeSupply() external view override returns (uint256) { return BNB_SUPPLY; } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } diff --git a/contracts/gov/user-keeper/GovUserKeeper.sol b/contracts/gov/user-keeper/GovUserKeeper.sol index b7e50460..b6e40f6b 100644 --- a/contracts/gov/user-keeper/GovUserKeeper.sol +++ b/contracts/gov/user-keeper/GovUserKeeper.sol @@ -847,7 +847,8 @@ contract GovUserKeeper is IGovUserKeeper, OwnableUpgradeable, ERC721HolderUpgrad if (wrapping) { IWETH(wethAddress).deposit{value: value}(); } else { - IWETH(wethAddress).withdraw(value); + IWETH(wethAddress).transfer(networkPropertiesAddress, value); + INetworkProperties(networkPropertiesAddress).unwrapWeth(value); } } diff --git a/contracts/interfaces/core/INetworkProperties.sol b/contracts/interfaces/core/INetworkProperties.sol index 5e88f060..ff114f9c 100644 --- a/contracts/interfaces/core/INetworkProperties.sol +++ b/contracts/interfaces/core/INetworkProperties.sol @@ -2,6 +2,10 @@ pragma solidity ^0.8.20; interface INetworkProperties { + /// @notice Used to unwrap WEth out of SphereX-controlled beacon and return to the sender + /// @param amount The amount to unwrap + function unwrapWeth(uint256 amount) external; + /// @notice Used in native coin governance mechanism /// @return The current full supply of native coin function getNativeSupply() external view returns (uint256); diff --git a/contracts/mock/gov/NetworkPropertiesMock.sol b/contracts/mock/gov/NetworkPropertiesMock.sol new file mode 100644 index 00000000..71cfe0a4 --- /dev/null +++ b/contracts/mock/gov/NetworkPropertiesMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "../../core/network-properties/BSCProperties.sol"; + +contract NetworkPropertiesMock is BSCProperties { + function changeWeth(address newAddress) external { + weth = IWETH(newAddress); + } +} diff --git a/deploy/config/configs/dev-mumbai.conf.js b/deploy/config/configs/dev-mumbai.conf.js deleted file mode 100644 index cc35c5a3..00000000 --- a/deploy/config/configs/dev-mumbai.conf.js +++ /dev/null @@ -1,193 +0,0 @@ -const { ZERO_ADDR, PRECISION } = require("../../../scripts/utils/constants.js"); -const { wei } = require("../../../scripts/utils/utils.js"); - -const { getBytesPolynomialPowerInit } = require("../utils.js"); - -const owners = ["0xEd498E75d471C3b874461a87Bb7146453CC8175A", "0xCa543e570e4A1F6DA7cf9C4C7211692Bc105a00A"]; - -const tokens = { - DEXE: "0x9a1AF9996458FDe28dD33EDC199f707ca9Ec2bA6", - BUSD: "0xe11A86849d99F524cAC3E7A0Ec1241828e332C62", - USDT: "0x3813e82e6f7098b9583FC0F33a962D02018B6803", - BABT: "0x0000000000000000000000000000000000000000", - WBNB: "0x9c3c9283d3e44854697cd22d3faa240cfb032889", -}; - -const uniswap = { - router: "0x8954afa98594b838bda56fe4c12a09d7739d179b", - quoter: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", -}; - -const NETWORK_PROPERTIES = ""; - -const DEXE_DAO_NAME = "DeXe Protocol"; - -const DOCUMENT_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000"; - -const DEFAULT_CORE_PROPERTIES = { - govVotesLimit: 50, - govCommissionPercentage: PRECISION.times(30).toFixed(), - tokenSaleProposalCommissionPercentage: PRECISION.toFixed(), - micropoolVoteRewardsPercentage: PRECISION.times(20).toFixed(), - treasuryVoteRewardsPercentage: PRECISION.times(1.618).toFixed(), -}; - -const DEFAULT_POOL_TYPES = [ - ["0", uniswap.router, "0"], - ["1", uniswap.quoter, "500"], - ["1", uniswap.quoter, "2500"], - ["1", uniswap.quoter, "10000"], -]; - -const POOL_PARAMETERS = { - settingsParams: { - proposalSettings: [ - { - earlyCompletion: true, - delegatedVotingAllowed: false, - validatorsVote: false, - duration: 600, - durationValidators: 600, - quorum: PRECISION.times("0.00001").toFixed(), - quorumValidators: PRECISION.times("0.00001").toFixed(), - minVotesForVoting: wei("10"), - minVotesForCreating: wei("1"), - executionDelay: 0, - rewardsInfo: { - rewardToken: ZERO_ADDR, - creationReward: 0, - executionReward: 0, - voteRewardsCoefficient: 0, - }, - executorDescription: "default", - }, - { - earlyCompletion: true, - delegatedVotingAllowed: false, - validatorsVote: false, - duration: 600, - durationValidators: 600, - quorum: PRECISION.times("0.00001").toFixed(), - quorumValidators: PRECISION.times("0.00001").toFixed(), - minVotesForVoting: wei("10"), - minVotesForCreating: wei("1"), - executionDelay: 0, - rewardsInfo: { - rewardToken: ZERO_ADDR, - creationReward: 0, - executionReward: 0, - voteRewardsCoefficient: 0, - }, - executorDescription: "internal", - }, - { - earlyCompletion: true, - delegatedVotingAllowed: false, - validatorsVote: false, - duration: 600, - durationValidators: 600, - quorum: PRECISION.times("0.00001").toFixed(), - quorumValidators: PRECISION.times("0.00001").toFixed(), - minVotesForVoting: wei("10"), - minVotesForCreating: wei("1"), - executionDelay: 0, - rewardsInfo: { - rewardToken: ZERO_ADDR, - creationReward: 0, - executionReward: 0, - voteRewardsCoefficient: 0, - }, - executorDescription: "validators", - }, - ], - additionalProposalExecutors: [], - }, - validatorsParams: { - name: "DEXE Validator Token", - symbol: "DEXEVT", - proposalSettings: { - duration: 600, - executionDelay: 0, - quorum: PRECISION.times("0.00001").toFixed(), - }, - validators: [], - balances: [], - }, - userKeeperParams: { - tokenAddress: tokens.DEXE, - nftAddress: ZERO_ADDR, - individualPower: 0, - nftsTotalSupply: 0, - }, - tokenParams: { - name: "", - symbol: "", - users: [], - cap: 0, - mintedTotal: 0, - amounts: [], - }, - votePowerParams: { - voteType: 1, - initData: getBytesPolynomialPowerInit(PRECISION.times("1.08"), PRECISION.times("0.92"), PRECISION.times("0.97")), - presetAddress: ZERO_ADDR, - }, - verifier: ZERO_ADDR, - onlyBABTHolders: false, - descriptionURL: "", - name: DEXE_DAO_NAME, -}; - -const DP_SETTINGS = { - earlyCompletion: true, - delegatedVotingAllowed: true, - validatorsVote: false, - duration: 600, - durationValidators: 600, - quorum: PRECISION.times("0.00001").toFixed(), - quorumValidators: PRECISION.times("0.00001").toFixed(), - minVotesForVoting: wei("10"), - minVotesForCreating: wei("1"), - executionDelay: 0, - rewardsInfo: { - rewardToken: ZERO_ADDR, - creationReward: 0, - executionReward: 0, - voteRewardsCoefficient: 0, - }, - executorDescription: "distribution-proposal", -}; - -const TOKENSALE_SETTINGS = { - earlyCompletion: true, - delegatedVotingAllowed: false, - validatorsVote: false, - duration: 600, - durationValidators: 600, - quorum: PRECISION.times("0.00001").toFixed(), - quorumValidators: PRECISION.times("0.00001").toFixed(), - minVotesForVoting: wei("10"), - minVotesForCreating: wei("1"), - executionDelay: 0, - rewardsInfo: { - rewardToken: ZERO_ADDR, - creationReward: 0, - executionReward: 0, - voteRewardsCoefficient: 0, - }, - executorDescription: "tokensale-proposal", -}; - -module.exports = { - owners, - tokens, - uniswap, - NETWORK_PROPERTIES, - DEXE_DAO_NAME, - DOCUMENT_HASH, - DEFAULT_CORE_PROPERTIES, - DEFAULT_POOL_TYPES, - POOL_PARAMETERS, - DP_SETTINGS, - TOKENSALE_SETTINGS, -}; diff --git a/deploy/config/configs/dev-sepolia.conf.js b/deploy/config/configs/dev-sepolia.conf.js index c0213a2f..6618f5d2 100644 --- a/deploy/config/configs/dev-sepolia.conf.js +++ b/deploy/config/configs/dev-sepolia.conf.js @@ -18,7 +18,7 @@ const uniswap = { quoter: "0xed1f6473345f45b75f8179591dd5ba1888cf2fb3", }; -const NETWORK_PROPERTIES = "0x7Dc3f3bEAeC9dAbC1Ae17508d96335E228a3c2a8"; +const NETWORK_PROPERTIES = "0xeEf4962269b8c8693e910D1e5B3BCf52bA4Ca80A"; const DEXE_DAO_NAME = "DeXe Protocol"; diff --git a/deploy/config/configs/dev.conf.js b/deploy/config/configs/dev.conf.js index 4406f5ba..fc921ecc 100644 --- a/deploy/config/configs/dev.conf.js +++ b/deploy/config/configs/dev.conf.js @@ -18,7 +18,7 @@ const uniswap = { quoter: "0xbC203d7f83677c7ed3F7acEc959963E7F4ECC5C2", }; -const NETWORK_PROPERTIES = "0x35b3978fa2fA3cCC754e47cbD1D9956485A83736"; +const NETWORK_PROPERTIES = "0xfC0b23b0F841Ce81eC8Fa7Ba80A1096f05cDD7Eb"; const DEXE_DAO_NAME = "DeXe Protocol"; diff --git a/deploy/config/configs/prod.conf.js b/deploy/config/configs/prod.conf.js index 6a4a316a..bc1a02e2 100644 --- a/deploy/config/configs/prod.conf.js +++ b/deploy/config/configs/prod.conf.js @@ -18,7 +18,7 @@ const uniswap = { quoter: "0xb048bbc1ee6b733fffcfb9e9cef7375518e25997", }; -const NETWORK_PROPERTIES = "0x9a1AF9996458FDe28dD33EDC199f707ca9Ec2bA6"; +const NETWORK_PROPERTIES = ""; const DEXE_DAO_NAME = "DeXe Protocol"; diff --git a/deploy/config/configs/stage.conf.js b/deploy/config/configs/stage.conf.js index d93fe954..b20e0d7f 100644 --- a/deploy/config/configs/stage.conf.js +++ b/deploy/config/configs/stage.conf.js @@ -18,7 +18,7 @@ const uniswap = { quoter: "0xbC203d7f83677c7ed3F7acEc959963E7F4ECC5C2", }; -const NETWORK_PROPERTIES = "0x35b3978fa2fA3cCC754e47cbD1D9956485A83736"; +const NETWORK_PROPERTIES = ""; const DEXE_DAO_NAME = "DeXe Protocol"; diff --git a/deploy/config/utils.js b/deploy/config/utils.js index 4c0c230e..70a7d1a2 100644 --- a/deploy/config/utils.js +++ b/deploy/config/utils.js @@ -15,10 +15,6 @@ const getConfig = () => { return require("./configs/dev-sepolia.conf.js"); } - if (process.env.ENVIRONMENT == "DEV_MUMBAI") { - return require("./configs/dev-mumbai.conf.js"); - } - throw Error("No environment config specified"); }; @@ -77,9 +73,26 @@ const getBytesDexeMultiplierInit = (multiplierName, multiplierSymbol) => { ); }; +const getBytesNetworkPropertiesInit = (wethAddress) => { + return web3.eth.abi.encodeFunctionCall( + { + name: "__NetworkProperties_init", + type: "function", + inputs: [ + { + type: "address", + name: "weth_", + }, + ], + }, + [wethAddress], + ); +}; + module.exports = { getConfig, getBytesPolynomialPowerInit, getBytesContractsRegistryInit, getBytesDexeMultiplierInit, + getBytesNetworkPropertiesInit, }; diff --git a/hardhat.config.js b/hardhat.config.js index d7c7a952..25183d05 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -40,12 +40,6 @@ module.exports = { accounts: privateKey(), gasMultiplier: 1.2, }, - mumbai: { - url: `https://polygon-testnet.public.blastapi.io`, - accounts: privateKey(), - gasPrice: 2000000000, - gasMultiplier: 1.2, - }, chapel: { url: "https://data-seed-prebsc-1-s1.binance.org:8545", accounts: privateKey(), @@ -79,7 +73,6 @@ module.exports = { mainnet: `${process.env.ETHERSCAN_KEY}`, goerli: `${process.env.ETHERSCAN_KEY}`, sepolia: `${process.env.ETHERSCAN_KEY}`, - polygonMumbai: `${process.env.POLYGONSCAN_KEY}`, bsc: `${process.env.BSCSCAN_KEY}`, bscTestnet: `${process.env.BSCSCAN_KEY}`, }, diff --git a/package.json b/package.json index df10d112..466eee92 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "private-network-fork": "npx hardhat node --fork https://mainnet.infura.io/v3/$(grep INFURA_KEY .env | cut -d '\"' -f2)", "deploy-dev": "npx hardhat migrate --network localhost", "deploy-sepolia": "npx hardhat migrate --network sepolia --verify", - "deploy-mumbai": "npx hardhat migrate --network mumbai --verify", "deploy-chapel": "npx hardhat migrate --network chapel --verify", "deploy-eth": "npx hardhat migrate --network ethereum --verify", "deploy-bsc": "npx hardhat migrate --network bsc --verify", diff --git a/test/core/NetworkProperties.test.js b/test/core/NetworkProperties.test.js new file mode 100644 index 00000000..a6e7c2c5 --- /dev/null +++ b/test/core/NetworkProperties.test.js @@ -0,0 +1,59 @@ +const { assert } = require("chai"); +const { accounts } = require("../../scripts/utils/utils"); +const Reverter = require("../helpers/reverter"); +const truffleAssert = require("truffle-assertions"); + +const ERC1967Proxy = artifacts.require("ERC1967Proxy"); +const NetworkProperties = artifacts.require("BSCProperties"); +const NetworkPropertiesMock = artifacts.require("NetworkPropertiesMock"); +const WethMock = artifacts.require("WETHMock"); + +NetworkProperties.numberFormat = "BigNumber"; +WethMock.numberFormat = "BigNumber"; + +describe("Network Properties", () => { + let OWNER; + let networkProperties; + let weth; + + const reverter = new Reverter(); + + before("setup", async () => { + OWNER = await accounts(0); + SECOND = await accounts(1); + + let implementation = await NetworkProperties.new(); + networkProperties = await NetworkProperties.at((await ERC1967Proxy.new(implementation.address, "0x")).address); + weth = await WethMock.new(); + await networkProperties.__NetworkProperties_init(weth.address); + + await reverter.snapshot(); + }); + + afterEach(reverter.revert); + + describe("upgradeability", () => { + it("should not initialize twice", async () => { + await truffleAssert.reverts( + networkProperties.__NetworkProperties_init(weth.address), + "Initializable: contract is already initialized", + ); + }); + + it("could not upgrade if not owner", async () => { + await truffleAssert.reverts( + networkProperties.upgradeTo(networkProperties.address, { from: SECOND }), + "MultiOwnable: caller is not the owner", + ); + }); + + it("could upgrade if owner", async () => { + let networkPropertiesMock = await NetworkPropertiesMock.new(); + + await networkProperties.upgradeTo(networkPropertiesMock.address); + + networkPropertiesMock = await NetworkPropertiesMock.at(networkProperties.address); + await networkPropertiesMock.changeWeth(weth.address); + }); + }); +}); diff --git a/test/core/PriceFeed.test.js b/test/core/PriceFeed.test.js index 28b7e463..9293a30f 100644 --- a/test/core/PriceFeed.test.js +++ b/test/core/PriceFeed.test.js @@ -61,6 +61,7 @@ describe("PriceFeed", () => { const _sphereXEngine = await SphereXEngineMock.new(); await contractsRegistry.__MultiOwnableContractsRegistry_init(); + await networkProperties.__NetworkProperties_init(WETH.address); await contractsRegistry.addContract(await contractsRegistry.DEXE_NAME(), DEXE.address); await contractsRegistry.addContract(await contractsRegistry.USD_NAME(), USD.address); diff --git a/test/factory/PoolFactory.test.js b/test/factory/PoolFactory.test.js index e3ec9c0e..6d130452 100644 --- a/test/factory/PoolFactory.test.js +++ b/test/factory/PoolFactory.test.js @@ -151,6 +151,7 @@ describe("PoolFactory", () => { sphereXEngine = await SphereXEngine.new(0, OWNER); await contractsRegistry.__MultiOwnableContractsRegistry_init(); + await networkProperties.__NetworkProperties_init(WETH.address); await sphereXEngine.grantRole(await sphereXEngine.SENDER_ADDER_ROLE(), contractsRegistry.address); diff --git a/test/gov/GovPool.test.js b/test/gov/GovPool.test.js index 8ac4ae44..f4460a7c 100644 --- a/test/gov/GovPool.test.js +++ b/test/gov/GovPool.test.js @@ -67,7 +67,7 @@ const ERC721RawPower = artifacts.require("ERC721RawPower"); const ERC721Expert = artifacts.require("ERC721Expert"); const ERC20Mock = artifacts.require("ERC20Mock"); const WethMock = artifacts.require("WETHMock"); -const BscProperties = artifacts.require("BSCProperties"); +const BscProperties = artifacts.require("NetworkPropertiesMock"); const ERC20 = artifacts.require("ERC20"); const BABTMock = artifacts.require("BABTMock"); const ExecutorTransferMock = artifacts.require("ExecutorTransferMock"); @@ -145,6 +145,7 @@ describe("GovPool", () => { async function switchWeth(poolOffset, newWethAddress) { await contractsRegistry.addContract(await contractsRegistry.WETH_NAME(), newWethAddress); + await networkProperties.changeWeth(newWethAddress); await poolRegistry.injectDependenciesToExistingPools(await poolRegistry.GOV_POOL_NAME(), poolOffset, 1); } @@ -254,6 +255,7 @@ describe("GovPool", () => { rewardToken = await ERC20Mock.new("REWARD", "RWD", 18); await contractsRegistry.__MultiOwnableContractsRegistry_init(); + await networkProperties.__NetworkProperties_init(weth.address); await contractsRegistry.addContract(await contractsRegistry.SPHEREX_ENGINE_NAME(), _sphereXEngine.address); await contractsRegistry.addContract(await contractsRegistry.POOL_SPHEREX_ENGINE_NAME(), _sphereXEngine.address); diff --git a/test/gov/proposals/DistributionProposal.test.js b/test/gov/proposals/DistributionProposal.test.js index ae8942c9..c4a70e20 100644 --- a/test/gov/proposals/DistributionProposal.test.js +++ b/test/gov/proposals/DistributionProposal.test.js @@ -126,6 +126,7 @@ describe("DistributionProposal", () => { const _sphereXEngine = await SphereXEngineMock.new(); await contractsRegistry.__MultiOwnableContractsRegistry_init(); + await networkProperties.__NetworkProperties_init(WETH.address); await contractsRegistry.addContract(await contractsRegistry.SPHEREX_ENGINE_NAME(), _sphereXEngine.address); await contractsRegistry.addContract(await contractsRegistry.POOL_SPHEREX_ENGINE_NAME(), _sphereXEngine.address); diff --git a/test/gov/proposals/TokenSaleProposal.test.js b/test/gov/proposals/TokenSaleProposal.test.js index 25475425..68e6d895 100644 --- a/test/gov/proposals/TokenSaleProposal.test.js +++ b/test/gov/proposals/TokenSaleProposal.test.js @@ -185,6 +185,7 @@ describe("TokenSaleProposal", () => { attacker = await GovTokenSaleAttackerMock.new(); await contractsRegistry.__MultiOwnableContractsRegistry_init(); + await networkProperties.__NetworkProperties_init(weth.address); await contractsRegistry.addContract(await contractsRegistry.SPHEREX_ENGINE_NAME(), _sphereXEngine.address); await contractsRegistry.addContract(await contractsRegistry.POOL_SPHEREX_ENGINE_NAME(), _sphereXEngine.address);