diff --git a/contracts/exchange2.sol b/contracts/exchange2.sol index a447d9f..e017cc4 100644 --- a/contracts/exchange2.sol +++ b/contracts/exchange2.sol @@ -5,47 +5,70 @@ import "./dmc2.sol"; import "./gwt2.sol"; import "./dividend.sol"; -contract Exchange2 { +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract Exchange2 is Initializable, UUPSUpgradeable, OwnableUpgradeable { address dmcToken; address gwtToken; address fundationIncome; // 当前周期,也可以看做周期总数 - uint256 current_circle = 0; + uint256 current_circle; uint256 current_mine_circle_start; // 这个周期内还能mint多少DMC - uint256 remain_dmc_balance = 0; + uint256 remain_dmc_balance; // 这个周期的能mint的DMC总量 - uint256 current_circle_dmc_balance = 0; - uint256 current_finish_time = 0; - uint256 public dmc2gwt_rate = 210; + uint256 current_circle_dmc_balance; + uint256 current_finish_time; + uint256 public dmc2gwt_rate; // 总共未mint的DMC总量,这个值会慢慢释放掉 - uint256 total_addtion_dmc_balance = 0; + uint256 total_addtion_dmc_balance; // 没有挖完的周期总数 - uint256 addtion_circle_count = 0; + uint256 addtion_circle_count; // 周期的最小时长 - uint256 min_circle_time = 100; + uint256 min_circle_time; //uint256 total_mine_period = 420; - uint256 adjust_period = 21; - uint256 initial_dmc_balance=768242.27 ether; + uint256 adjust_period; + uint256 initial_dmc_balance; + + // DMC1到2的兑换 + mapping(bytes32 => uint256) public dmc1_to_dmc2; + event newCycle(uint256 cycle_number, uint256 dmc_balance, uint256 start_time); + event gwtRateChanged(uint256 new_rate, uint256 old_rate); + event DMCMinted(uint256 amount, uint256 remain); + + function initialize(address _dmcToken, address _gwtToken, address _fundationIncome, uint256 _min_circle_time) public initializer { + __UUPSUpgradeable_init(); + __Ownable_init(msg.sender); + __Exchange2Upgradable_init(_dmcToken, _gwtToken, _fundationIncome, _min_circle_time); + } - constructor(address _dmcToken, address _gwtToken, address _fundationIncome, uint256 _min_circle_time) { + function __Exchange2Upgradable_init(address _dmcToken, address _gwtToken, address _fundationIncome, uint256 _min_circle_time) public onlyInitializing { + require(_min_circle_time > 0); dmcToken = _dmcToken; gwtToken = _gwtToken; fundationIncome = _fundationIncome; min_circle_time = _min_circle_time; + dmc2gwt_rate = 210; + adjust_period = 21; + initial_dmc_balance = 768242.27 ether; + _newCycle(); } function getCircleBalance(uint256 circle) public view returns (uint256) { + uint256 adjust_times = (circle-1) / adjust_period; uint256 balance = initial_dmc_balance; for (uint i = 0; i < adjust_times; i++) { @@ -70,13 +93,14 @@ contract Exchange2 { } current_circle_dmc_balance = remain_dmc_balance; - console.log("new cycle %d start at %d, dmc balance %d", current_circle, current_mine_circle_start, current_circle_dmc_balance); + emit newCycle(current_circle, current_circle_dmc_balance, current_mine_circle_start); + //console.log("new cycle %d start at %d, dmc balance %d", current_circle, current_mine_circle_start, current_circle_dmc_balance); } function adjustExchangeRate() internal { if(block.timestamp >= current_mine_circle_start + min_circle_time) { //结束当前挖矿周期 - + uint256 old_rate = dmc2gwt_rate; if(remain_dmc_balance > 0) { total_addtion_dmc_balance += remain_dmc_balance; addtion_circle_count += 1; @@ -84,12 +108,12 @@ contract Exchange2 { console.log("prev cycle dmc balance left %d, total left %d, total left cycle %d", remain_dmc_balance, total_addtion_dmc_balance, addtion_circle_count); //本周期未挖完,降低dmc2gwt_rate + uint256 old_rate = dmc2gwt_rate; dmc2gwt_rate = dmc2gwt_rate * (1-remain_dmc_balance/current_circle_dmc_balance); if(dmc2gwt_rate < 210) { // 最低值为210 dmc2gwt_rate = 210; } - console.log("decrease dmc2gwt_rate to %d", dmc2gwt_rate); } else { if (addtion_circle_count > 0) { @@ -104,6 +128,8 @@ contract Exchange2 { console.log("increase dmc2gwt_rate to %d", dmc2gwt_rate); } + emit gwtRateChanged(dmc2gwt_rate, old_rate); + _newCycle(); } else { console.log("keep cycle."); @@ -111,6 +137,43 @@ contract Exchange2 { } } + function _decreaseDMCBalance(uint256 amount) internal returns (uint256, bool) { + bool is_empty = false; + uint256 real_amount = amount; + if(remain_dmc_balance > amount) { + remain_dmc_balance -= amount; + } else { + // 待确认:我将current_finish_time看作是一轮DMC释放完毕的时间,但这会导致gwt的汇率计算可能与GWT的实际兑换情况无关 + current_finish_time = block.timestamp; + + real_amount = remain_dmc_balance; + remain_dmc_balance = 0; + is_empty = true; + } + + emit DMCMinted(real_amount, remain_dmc_balance); + + return (real_amount, is_empty); + } + + function registerDMC1(address recvAddress, string calldata cookie, uint256 dmc1Amount) onlyOwner public { + dmc1_to_dmc2[keccak256(abi.encodePacked(recvAddress, cookie))] = dmc1Amount; + } + + function claimDMC2(string calldata cookie) public { + bytes32 key = keccak256(abi.encodePacked(msg.sender, cookie)); + require(dmc1_to_dmc2[key] > 0, "no dmc1 amount"); + uint256 dmc2Amount = dmc1_to_dmc2[key] * 4 / 5; + (uint256 claimAmount, bool is_empty) = _decreaseDMCBalance(dmc2Amount); + if (is_empty) { + dmc1_to_dmc2[key] -= claimAmount * 5 / 4; + } else { + dmc1_to_dmc2[key] = 0; + } + + DMC2(dmcToken).mint(msg.sender, claimAmount); + } + function DMCtoGWT(uint256 amount) public { DMC2(dmcToken).burnFrom(msg.sender, amount); GWTToken2(gwtToken).mint(msg.sender, amount * dmc2gwt_rate * 6 / 5); @@ -118,23 +181,19 @@ contract Exchange2 { function GWTtoDMC(uint256 amount) public { adjustExchangeRate(); - uint256 real_dmc_count = 0; uint256 dmc_count = amount / dmc2gwt_rate; console.log("exchange dmc %d from amount %d, rate %d", dmc_count, amount, dmc2gwt_rate); - if(dmc_count >= remain_dmc_balance) { - current_finish_time = block.timestamp; - - real_dmc_count = remain_dmc_balance; - remain_dmc_balance = 0; - //不用立刻转给分红合约,而是等积累一下 - GWTToken2(gwtToken).transferFrom(msg.sender, address(this), real_dmc_count * dmc2gwt_rate); - DMC2(dmcToken).mint(msg.sender, real_dmc_count); - } else { - remain_dmc_balance -= dmc_count; - //不用立刻转给分红合约,而是等积累一下 - GWTToken2(gwtToken).transferFrom(msg.sender, address(this), amount); - DMC2(dmcToken).mint(msg.sender, dmc_count); + + (uint256 real_dmc_amount, bool is_empty) = _decreaseDMCBalance(dmc_count); + uint256 real_gwt_amount = amount; + if (is_empty) { + //current_finish_time = block.timestamp; + real_gwt_amount = real_dmc_amount * dmc2gwt_rate; } + + //不用立刻转给分红合约,而是等积累一下 + GWTToken2(gwtToken).transferFrom(msg.sender, address(this), real_gwt_amount); + DMC2(dmcToken).mint(msg.sender, real_dmc_amount); } // 手工将累积的收入打给分红合约 @@ -146,4 +205,6 @@ contract Exchange2 { function getCycleInfo() public view returns (uint256, uint256, uint256) { return (current_circle, remain_dmc_balance, current_circle_dmc_balance); } + + function _authorizeUpgrade(address) internal override onlyOwner {} } \ No newline at end of file diff --git a/test/test_exchange.ts b/test/test_exchange.ts index ad47018..c2f6ae4 100644 --- a/test/test_exchange.ts +++ b/test/test_exchange.ts @@ -4,6 +4,8 @@ import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers'; import { expect } from "chai"; import { mine } from "@nomicfoundation/hardhat-network-helpers"; +// 此测试运行的前提:每轮的释放DMC固定为210,汇率固定为210 + describe("Exchange", function () { let dmc: DMC2 let gwt: GWTToken2 @@ -17,17 +19,16 @@ describe("Exchange", function () { dmc = await (await ethers.deployContract("DMC2", [ethers.parseEther("1000000000"), [signers[0].address], [ethers.parseEther("1000")]])).waitForDeployment() gwt = await (await ethers.deployContract("GWTToken2")).waitForDeployment() - exchange = await(await ethers.deployContract("Exchange2", [await dmc.getAddress(), await gwt.getAddress(), ethers.ZeroAddress, 1000])).waitForDeployment() - console.log("exchange:", await exchange.getAddress()) - /* - exchange = await (await upgrades.deployProxy(await ethers.getContractFactory("Exchange"), - [await dmc.getAddress(), await gwt.getAddress()], + //exchange = await(await ethers.deployContract("Exchange2", [await dmc.getAddress(), await gwt.getAddress(), ethers.ZeroAddress, 1000])).waitForDeployment(); + exchange = await (await upgrades.deployProxy(await ethers.getContractFactory("Exchange2"), + [await dmc.getAddress(), await gwt.getAddress(), ethers.ZeroAddress, 1000], { initializer: "initialize", kind: "uups", timeout: 0 - })).waitForDeployment() as unknown as Exchange; - */ + })).waitForDeployment() as unknown as Exchange2; + console.log("exchange:", await exchange.getAddress()); + await (await gwt.enableMinter([await exchange.getAddress(), signers[0].address])).wait(); await (await dmc.enableMinter([await exchange.getAddress()])).wait();