From 5c8c84ef995d529b8f91cb3d38d9f5c6bb5cd2be Mon Sep 17 00:00:00 2001 From: AugustoL Date: Tue, 21 Sep 2021 10:52:27 -0300 Subject: [PATCH] feat(contracts): Add DATonlyERC20 with upgrade tests --- .nvmrc | 1 + .../DecentralizedAutonomousTrustOnlyERC20.sol | 85 +++++++++++ scripts/DAT.js | 9 +- test/dat/upgrade.js | 138 ++++++++++++++++++ 4 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 .nvmrc create mode 100644 contracts/DecentralizedAutonomousTrustOnlyERC20.sol create mode 100644 test/dat/upgrade.js diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..5595ae1aa --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14.17.6 diff --git a/contracts/DecentralizedAutonomousTrustOnlyERC20.sol b/contracts/DecentralizedAutonomousTrustOnlyERC20.sol new file mode 100644 index 000000000..8c7159777 --- /dev/null +++ b/contracts/DecentralizedAutonomousTrustOnlyERC20.sol @@ -0,0 +1,85 @@ +pragma solidity 0.5.17; + +import "./DecentralizedAutonomousTrust.sol"; + +/** + * @title DecentralizedAutonomousTrustOnlyERC20 + */ +contract DecentralizedAutonomousTrustOnlyERC20 is DecentralizedAutonomousTrust { + + function recover(address _token) public { + if (_token == address(0)) + beneficiary.transfer(address(this).balance); + else + IERC20(_token).transfer(beneficiary, IERC20(_token).balanceOf(address(this))); + } + + function transferBeneficiary(address payable newBeneficiary) public { + require(msg.sender == beneficiary, "DecentralizedAutonomousTrustOnlyERC20: Not called by beneficiary"); + beneficiary = newBeneficiary; + } + + function estimateBuyValue( + uint _currencyValue + ) public view + returns (uint) + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function buy( + address _to, + uint _currencyValue, + uint _minTokensBought + ) public payable + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function estimateSellValue( + uint _quantityToSell + ) public view + returns(uint) + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function sell( + address payable _to, + uint _quantityToSell, + uint _minCurrencyReturned + ) public + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function estimatePayValue( + uint _currencyValue + ) public view + returns (uint) + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function pay( + address _to, + uint _currencyValue + ) public payable + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function estimateExitFee( + uint _msgValue + ) public view + returns(uint) + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + + function close() public payable + { + revert("DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now"); + } + +} diff --git a/scripts/DAT.js b/scripts/DAT.js index 77af2d63b..a6146d831 100644 --- a/scripts/DAT.js +++ b/scripts/DAT.js @@ -2,7 +2,7 @@ const { Contracts, ZWeb3 } = require('@openzeppelin/upgrades'); const zeroAddress = '0x0000000000000000000000000000000000000000'; const fs = require('fs'); -async function deployDAT(web3, options = {}, useProxy, network) { +async function deployDAT(web3, options = {}) { ZWeb3.initialize(web3.currentProvider); Contracts.setLocalBuildDir('contracts/build/'); @@ -12,6 +12,7 @@ async function deployDAT(web3, options = {}, useProxy, network) { const ProxyContract = Contracts.getFromLocal('AdminUpgradeabilityProxy'); const ProxyAdminContract = Contracts.getFromLocal('ProxyAdmin'); const Multicall = Contracts.getFromLocal('Multicall'); + const DecentralizedAutonomousTrustOnlyERC20 = Contracts.getFromLocal('DecentralizedAutonomousTrustOnlyERC20'); const contracts = {}; const callOptions = Object.assign( @@ -47,7 +48,7 @@ async function deployDAT(web3, options = {}, useProxy, network) { if (options.log) console.log(`DAT template deployed ${datContract.address}`); - const datProxy = await ProxyContract.new( + contracts.datProxy = await ProxyContract.new( datContract.address, // logic contracts.proxyAdmin.address, // admin [], // data @@ -56,9 +57,9 @@ async function deployDAT(web3, options = {}, useProxy, network) { } ); if (options.log) - console.log(`DAT proxy deployed ${datProxy.address}`); + console.log(`DAT proxy deployed ${contracts.datProxy.address}`); - contracts.dat = await DATContract.at(datProxy.address); + contracts.dat = await DATContract.at(contracts.datProxy.address); contracts.dat.implementation = datContract.address; await contracts.dat.methods.initialize( diff --git a/test/dat/upgrade.js b/test/dat/upgrade.js new file mode 100644 index 000000000..1be388f11 --- /dev/null +++ b/test/dat/upgrade.js @@ -0,0 +1,138 @@ +const { deployDAT } = require("../../scripts/DAT"); + +const { constants } = require("../helpers"); +const { reverts } = require("truffle-assertions"); + +const { Contracts, ZWeb3 } = require('@openzeppelin/upgrades'); +ZWeb3.initialize(web3.currentProvider); +Contracts.setLocalBuildDir('contracts/build/'); +const DecentralizedAutonomousTrustOnlyERC20 = Contracts.getFromLocal('DecentralizedAutonomousTrustOnlyERC20'); +const ERC20Mintable = Contracts.getFromLocal('ERC20Mintable'); + + +contract("dat / upgrade", ([_, controller, buyer, investor, other, beneficiary]) => { + let contracts; + + it("should upgrade to DecentralizedAutonomousTrustOnlyERC20", async () => { + contracts = await deployDAT(web3); + + await contracts.dat.methods.buy(buyer, "100000000000000000000", "1").send({ + value: "100000000000000000000", + from: buyer, + gas: 9000000 + }); + assert.equal( + (await contracts.dat.methods.balanceOf(buyer).call()).toString(), + "141421356237309504880" + ); + + await contracts.dat.methods.sell(buyer, "666666666666", 1) + .send({ from: buyer, gas: 9000000 }); + assert.equal( + (await contracts.dat.methods.balanceOf(buyer).call()).toString(), + "141421355570642838214" + ); + + await contracts.dat.methods.pay(constants.ZERO_ADDRESS, "666").send({ + from: investor, + value: "666", + }); + + const testToken = await ERC20Mintable.new({ + from: controller, gas: 9000000 + }); + await testToken.methods.initialize(controller).send({from: controller}); + await testToken.methods.mint(controller, "1000000").send({from: controller}); + await testToken.methods.transfer(contracts.datProxy.address, "1000000").send({from: controller}); + + const datOnlyERC20Implementation = await DecentralizedAutonomousTrustOnlyERC20.new({ + from: controller, gas: 9000000 + }); + + // Upgrade implementation + await contracts.proxyAdmin.methods + .upgrade(contracts.datProxy.address, datOnlyERC20Implementation.address) + .send({ from: controller }) + + assert.equal( + await contracts.proxyAdmin.methods.getProxyImplementation(contracts.datProxy.address).call(), + datOnlyERC20Implementation.address + ); + + // Check that DAT functions are not allowed anymore + await reverts( + contracts.dat.methods.estimateBuyValue("100000000000000000000").call({ + from: buyer, + gas: 9000000 + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + await reverts( + contracts.dat.methods.buy(buyer, "100000000000000000000", "1").send({ + value: "100000000000000000000", + from: buyer, + gas: 9000000 + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + + await reverts( + contracts.dat.methods.estimateSellValue("666666666666").call({ + from: buyer, + gas: 9000000 + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + await reverts( + contracts.dat.methods.sell(buyer, "666666666666", 1) + .send({ from: buyer, gas: 9000000 }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + + await reverts( + contracts.dat.methods.estimatePayValue("666").call({ + from: investor, + gas: 9000000 + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + await reverts( + contracts.dat.methods.pay(constants.ZERO_ADDRESS, "666").send({ + from: investor, + value: "666", + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + + await reverts( + contracts.dat.methods.estimateExitFee("1").call({ + from: controller, + gas: 9000000 + }), + "DecentralizedAutonomousTrustOnlyERC20: Old DAT function not allowed anymore. Only ERC20 functions allowed now" + ); + + // Recover ERC20 and ETH from DAT + assert.equal( await testToken.methods.balanceOf(contracts.datProxy.address).call(), 1000000 ); + assert.equal( await web3.eth.getBalance(contracts.datProxy.address), 9999999927307628892 ); + assert.equal( + await web3.eth.getBalance(beneficiary), + 100000090000000000000000600 + ); + + const datOnlyERC20 = await DecentralizedAutonomousTrustOnlyERC20.at(contracts.datProxy.address); + + await datOnlyERC20.methods.recover(testToken.address).send(); + await datOnlyERC20.methods.recover(constants.ZERO_ADDRESS).send(); + + assert.equal(await testToken.methods.balanceOf(contracts.datProxy.address).call(), 0); + assert.equal(await web3.eth.getBalance(contracts.datProxy.address), 0); + assert.equal(await testToken.methods.balanceOf(beneficiary).call(), 1000000); + assert.equal( + await web3.eth.getBalance(beneficiary), + 100000090000000000000000600 + 9999999927307628892 + ); + + }); + +});