From 6443c0ec2378ad4a5a3faf006b4fab5a43f64994 Mon Sep 17 00:00:00 2001 From: Jaro Date: Thu, 13 Aug 2020 14:42:16 +0300 Subject: [PATCH 1/3] No registration fee is needed Blockchain tx fee is enough protection agains face identity registrations --- contracts/Registry.sol | 12 +++--------- migrations/2_deploy_contracts.js | 2 +- test/beneficiary.js | 2 +- test/fundsRecovery.js | 2 +- test/fundsRecoveryByCheque.js | 2 +- test/greenpaths.js | 2 +- test/hermes.js | 2 +- test/hermesChannelSettlement.js | 2 +- test/hermesClosing.js | 2 +- test/hermesFee.js | 2 +- test/hermesPunishment.js | 2 +- test/hermesStake.js | 2 +- test/multiHermes.js | 2 +- test/parentRegistry.js | 2 +- test/registry.js | 30 ++++++++---------------------- 15 files changed, 24 insertions(+), 44 deletions(-) diff --git a/contracts/Registry.sol b/contracts/Registry.sol index 5166314..30ad212 100644 --- a/contracts/Registry.sol +++ b/contracts/Registry.sol @@ -29,7 +29,6 @@ contract Registry is FundsRecovery { using SafeMath for uint256; address public dex; - uint256 public registrationFee; uint256 public minimalHermesStake; address internal channelImplementationAddress; address internal hermesImplementationAddress; @@ -49,8 +48,7 @@ contract Registry is FundsRecovery { event HermesURLUpdated(address indexed hermesId, bytes newURL); event ConsumerChannelCreated(address indexed identityHash, address indexed hermesId, address channelAddress); - constructor (address _tokenAddress, address _dexAddress, uint256 _regFee, uint256 _minimalHermesStake, address _channelImplementation, address _hermesImplementation, address _parentAddress) public { - registrationFee = _regFee; + constructor (address _tokenAddress, address _dexAddress, uint256 _minimalHermesStake, address _channelImplementation, address _hermesImplementation, address _parentAddress) public { minimalHermesStake = _minimalHermesStake; require(_tokenAddress != address(0)); @@ -79,8 +77,8 @@ contract Registry is FundsRecovery { address _identityHash = keccak256(abi.encodePacked(address(this), _hermesId, _stakeAmount, _transactorFee, _beneficiary)).recover(_signature); require(_identityHash != address(0), "wrong signature"); - // Tokens amount to get from channel to cover tx fee, registration fee and provider's stake - uint256 _totalFee = registrationFee.add(_stakeAmount).add(_transactorFee); + // Tokens amount to get from channel to cover tx fee and provider's stake + uint256 _totalFee = _stakeAmount.add(_transactorFee); require(_totalFee <= token.balanceOf(getChannelAddress(_identityHash, _hermesId)), "not enought funds in channel to cover fees"); // Deploy channel contract for given identity (mini proxy which is pointing to implementation) @@ -246,10 +244,6 @@ contract Registry is FundsRecovery { return status == HermesContract.Status.Active; } - function changeRegistrationFee(uint256 _newFee) public onlyOwner { - registrationFee = _newFee; - } - function transferCollectedFeeTo(address _beneficiary) public onlyOwner{ uint256 _collectedFee = token.balanceOf(address(this)); require(_collectedFee > 0, "collected fee cannot be less than zero"); diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 7da79b8..3fa8784 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -18,7 +18,7 @@ module.exports = async function (deployer, network, accounts) { await deployer.deploy(DEXImplementation) await deployer.deploy(ChannelImplementation) await deployer.deploy(HermesImplementation) - await deployer.deploy(Registry, MystToken.address, DEXImplementation.address, 0, 0, ChannelImplementation.address, HermesImplementation.address, zeroAddress) + await deployer.deploy(Registry, MystToken.address, DEXImplementation.address, 0, ChannelImplementation.address, HermesImplementation.address, zeroAddress) } else { await deployer.deploy(SafeMathLib) await deployer.link(SafeMathLib, [OldMystToken]) diff --git a/test/beneficiary.js b/test/beneficiary.js index 1c0a3c8..0173d81 100644 --- a/test/beneficiary.js +++ b/test/beneficiary.js @@ -39,7 +39,7 @@ contract("Setting beneficiary tests", ([txMaker, operatorAddress, beneficiaryA, const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, operator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 1, channelImplementation.address, hermesImplementation.address, zeroAddress) + registry = await Registry.new(token.address, dex.address, 1, channelImplementation.address, hermesImplementation.address, zeroAddress) // Give some ethers for gas for operator await topUpEthers(txMaker, operator.address, OneEther) diff --git a/test/fundsRecovery.js b/test/fundsRecovery.js index 742f716..c20d775 100644 --- a/test/fundsRecovery.js +++ b/test/fundsRecovery.js @@ -191,7 +191,7 @@ contract('Registry funds recovery', ([_, txMaker, identity, account, fundsDestin // Deploy registry smart contract const nativeToken = await Token.new() // Native token is used as main unit of value in channels. We're recovering any other tokens but not this. - registry = await Registry.new(nativeToken.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress, { from: txMaker }) + registry = await Registry.new(nativeToken.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress, { from: txMaker }) expect(registry.address.toLowerCase()).to.be.equal(registryAddress.toLowerCase()) // Set funds destination diff --git a/test/fundsRecoveryByCheque.js b/test/fundsRecoveryByCheque.js index 7160c2d..9a6db1e 100644 --- a/test/fundsRecoveryByCheque.js +++ b/test/fundsRecoveryByCheque.js @@ -49,7 +49,7 @@ contract('Full path (in channel using cheque) test for funds recovery', ([txMake const dex = await MystDex.new() const channelImplementation = await ChannelImplementation.new() const hermesImplementation = await HermesImplementation.new() - registry = await Registry.new(nativeToken.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(nativeToken.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) hermesId = await registry.getHermesAddress(hermesOperator) expectedAddress = await genCreate2Address(identityHash, hermesId, registry, channelImplementation.address) diff --git a/test/greenpaths.js b/test/greenpaths.js index bb1cfb9..f5b4552 100644 --- a/test/greenpaths.js +++ b/test/greenpaths.js @@ -56,7 +56,7 @@ contract('Green path tests', ([txMaker, ...beneficiaries]) => { const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new() const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 1, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 1, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Give some ethers for gas for operator await topUpEthers(txMaker, operator.address, OneEther) diff --git a/test/hermes.js b/test/hermes.js index eca187b..2fe9c9c 100644 --- a/test/hermes.js +++ b/test/hermes.js @@ -45,7 +45,7 @@ contract('Hermes Contract Implementation tests', ([txMaker, operatorAddress, ben const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, operator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 1, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 1, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Give some ethers for gas for operator await topUpEthers(txMaker, operator.address, OneEther) diff --git a/test/hermesChannelSettlement.js b/test/hermesChannelSettlement.js index 3947050..27e85b0 100644 --- a/test/hermesChannelSettlement.js +++ b/test/hermesChannelSettlement.js @@ -42,7 +42,7 @@ contract("Channel openinig via settlement tests", ([txMaker, beneficiaryA, benef const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, operator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 100, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 100, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Give some ethers for gas for operator await topUpEthers(txMaker, operator.address, OneEther) diff --git a/test/hermesClosing.js b/test/hermesClosing.js index 210cda8..d0e981f 100644 --- a/test/hermesClosing.js +++ b/test/hermesClosing.js @@ -35,7 +35,7 @@ contract('Hermes closing', ([txMaker, operatorAddress, ...beneficiaries]) => { const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, hermesOperator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, Zero, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, OneToken) diff --git a/test/hermesFee.js b/test/hermesFee.js index 82111a4..660700b 100644 --- a/test/hermesFee.js +++ b/test/hermesFee.js @@ -32,7 +32,7 @@ contract('Hermes fee', ([txMaker, operatorAddress, ...beneficiaries]) => { dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, hermesOperator.address, 0, OneToken) channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, 1000) diff --git a/test/hermesPunishment.js b/test/hermesPunishment.js index 054e46f..b5f45d5 100644 --- a/test/hermesPunishment.js +++ b/test/hermesPunishment.js @@ -37,7 +37,7 @@ contract('Hermes punishment', ([txMaker, operatorAddress, ...beneficiaries]) => const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, hermesOperator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, Zero, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, OneToken) diff --git a/test/hermesStake.js b/test/hermesStake.js index da63a3d..afbb931 100644 --- a/test/hermesStake.js +++ b/test/hermesStake.js @@ -35,7 +35,7 @@ contract('Hermes stake management', ([txMaker, operatorAddress, ...beneficiaries const dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new(token.address, hermesOperator.address, 0, OneToken) const channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, Zero, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, stake, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, OneToken) diff --git a/test/multiHermes.js b/test/multiHermes.js index 8127f8b..3e04e83 100644 --- a/test/multiHermes.js +++ b/test/multiHermes.js @@ -33,7 +33,7 @@ contract('Multi hermeses', ([txMaker, ...beneficiaries]) => { dex = await MystDex.new() const hermesImplementation = await HermesImplementation.new() channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermeses await topUpTokens(token, txMaker, 1000) diff --git a/test/parentRegistry.js b/test/parentRegistry.js index 6c0b795..0bb2e21 100644 --- a/test/parentRegistry.js +++ b/test/parentRegistry.js @@ -31,7 +31,7 @@ contract('Parent registry', ([txMaker, minter, hermesOperator, hermesOperator2, hermesImplementation = await HermesImplementation.new() channelImplementation = await ChannelImplementation.new() parentRegistry = await ParentRegistry.new(token.address) - registry = await Registry.new(token.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, parentRegistry.address) + registry = await Registry.new(token.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, parentRegistry.address) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, 100) diff --git a/test/registry.js b/test/registry.js index 76f7032..df31683 100644 --- a/test/registry.js +++ b/test/registry.js @@ -34,18 +34,13 @@ contract('Registry', ([txMaker, minter, fundsDestination, ...otherAccounts]) => dex = await MystDex.new() hermesImplementation = await HermesImplementation.new() channelImplementation = await ChannelImplementation.new() - registry = await Registry.new(token.address, dex.address, 0, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) + registry = await Registry.new(token.address, dex.address, 0, channelImplementation.address, hermesImplementation.address, ZeroAddress) // Topup some tokens into txMaker address so it could register hermes await topUpTokens(token, txMaker, 10) await token.approve(registry.address, 10) }) - it('should have zero registration fee', async () => { - const registrationFee = await registry.registrationFee() - expect(Number(registrationFee)).to.be.equal(0) - }) - it('should register hermes', async () => { const hermesURL = Buffer.from('http://test.hermes') await registry.registerHermes(hermesOperator, 10, 0, 25, OneToken, hermesURL) @@ -116,30 +111,24 @@ contract('Registry', ([txMaker, minter, fundsDestination, ...otherAccounts]) => // ==================== Paid registration ====================== - it('should allow to change fee', async () => { - const newFee = 100 - await registry.changeRegistrationFee(newFee) - const registrationFee = await registry.registrationFee() - expect(Number(registrationFee)).to.be.equal(newFee) - }) - it('should fail registering identity having 0 balance', async () => { + const txFee = 1 const secondIdentity = identities[1] const secondIdentityHash = secondIdentity.address const channelAddress = await genCreate2Address(secondIdentityHash, hermesId, registry, channelImplementation.address) expect(Number(await token.balanceOf(channelAddress))).to.be.equal(0) - const signature = signIdentityRegistration(registry.address, hermesId, Zero, Zero, fundsDestination, secondIdentity) - await registry.registerIdentity(hermesId, Zero, Zero, fundsDestination, signature).should.be.rejected + const signature = signIdentityRegistration(registry.address, hermesId, Zero, txFee, fundsDestination, secondIdentity) + await registry.registerIdentity(hermesId, Zero, txFee, fundsDestination, signature).should.be.rejected expect(await registry.isRegistered(secondIdentityHash)).to.be.false }) it('should register identity which has coins', async () => { + const txFee = 100 const secondIdentity = identities[1] const secondIdentityHash = secondIdentity.address const channelAddress = await genCreate2Address(secondIdentityHash, hermesId, registry, channelImplementation.address) - const registratinoFee = 100 - const balanceBefore = Number(await token.balanceOf(registry.address)) + expect(Number(await token.balanceOf(channelAddress))).to.be.equal(0) // TopUp channel -> send or mint tokens into channel address const topUpAmount = 1000000 @@ -147,12 +136,9 @@ contract('Registry', ([txMaker, minter, fundsDestination, ...otherAccounts]) => expect(Number(await token.balanceOf(channelAddress))).to.be.equal(topUpAmount) // Register identity - const signature = signIdentityRegistration(registry.address, hermesId, Zero, Zero, fundsDestination, secondIdentity) - await registry.registerIdentity(hermesId, Zero, Zero, fundsDestination, signature) + const signature = signIdentityRegistration(registry.address, hermesId, Zero, txFee, fundsDestination, secondIdentity) + await registry.registerIdentity(hermesId, Zero, txFee, fundsDestination, signature) expect(await registry.isRegistered(secondIdentityHash)).to.be.true - - // Registry should own some tokens - expect(Number(await token.balanceOf(registry.address))).to.be.equal(balanceBefore + registratinoFee) }) it("should send transaction fee for txMaker", async () => { From 9c0e7424b9f1874afca05856e46682183bda9330 Mon Sep 17 00:00:00 2001 From: Jaro Date: Thu, 13 Aug 2020 15:30:21 +0300 Subject: [PATCH 2/3] Tests to prove that second same identity reigstration will fail --- test/registry.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/registry.js b/test/registry.js index df31683..66d18e5 100644 --- a/test/registry.js +++ b/test/registry.js @@ -77,6 +77,16 @@ contract('Registry', ([txMaker, minter, fundsDestination, ...otherAccounts]) => await registry.registerIdentity(hermesId, Zero, Zero, fundsDestination, signature).should.be.rejected }) + it('should reject registration with different beneficiary for already registered identity', async () => { + const identity = identities[0] + const beneficiary = otherAccounts[0] + + expect(await registry.isRegistered(identity.address)).to.be.true + + const signature = signIdentityRegistration(registry.address, hermesId, Zero, Zero, beneficiary, identity) + await registry.registerIdentity(hermesId, Zero, Zero, beneficiary, signature).should.be.rejected + }) + it('registry should have proper channel address calculations', async () => { const identityHash = identities[0].address expect( From 3b4a04fe75c0749d1bcce412b7668a2b8d737ac3 Mon Sep 17 00:00:00 2001 From: Jaro Date: Fri, 14 Aug 2020 08:45:48 +0300 Subject: [PATCH 3/3] Deployed latest version of smart contracts --- README.md | 106 +- contracts/MystToken.sol | 7 + .../ChannelImplementation.sol.flattened | 87 +- .../flattened/DEXImplementation.sol.flattened | 89 +- .../HermesImplementation.sol.flattened | 87 +- contracts/flattened/Mysttoken.sol.flattened | 1234 +++-------------- contracts/flattened/Registry.sol.flattened | 61 +- migrations/2_deploy_contracts.js | 4 +- 8 files changed, 304 insertions(+), 1371 deletions(-) diff --git a/README.md b/README.md index 67a13a1..9a4c557 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,13 @@ npm run migrate MYSTT ERC20 Token (Mintable a la myst token): [0x7753cfAD258eFbC52A9A1452e42fFbce9bE486cb](https://goerli.etherscan.io/address/0x7753cfAD258eFbC52A9A1452e42fFbce9bE486cb) -MYSTTv2 ERC777 Token (migratable from MYSTTv1): [0x8EA3F639e98da04708520C63b34AfBAa1594bC82](https://goerli.etherscan.io/address/0x8EA3F639e98da04708520C63b34AfBAa1594bC82) +MYSTTv2 Token: [0xf74a5ca65E4552CfF0f13b116113cCb493c580C5](https://goerli.etherscan.io/address/0xf74a5ca65E4552CfF0f13b116113cCb493c580C5) Registry smart contract: -[0x2FD2AbE2fF222b84db9B3fF8D37532A9417f244A](https://goerli.etherscan.io/address/0x2FD2AbE2fF222b84db9B3fF8D37532A9417f244A) +[0xc82Cc5B0bAe95F443e33FF053aAa70F1Eb7d312A](https://goerli.etherscan.io/address/0xc82Cc5B0bAe95F443e33FF053aAa70F1Eb7d312A) Hermes smart contract: -[0xF20e4068Aecb427481Dd35B36506d8AAcD5763E9](https://goerli.etherscan.io/address/0xF20e4068Aecb427481Dd35B36506d8AAcD5763E9) +[0x42a537D649d6853C0a866470f2d084DA0f73b5E4](https://goerli.etherscan.io/address/0x42a537D649d6853C0a866470f2d084DA0f73b5E4) ### Deloyment log: @@ -54,73 +54,73 @@ Hermes smart contract: Deploying 'MystToken' --------------------- - > transaction hash: 0xfb91b38d2ceebe01e70242c33c512fd61643efac882bad6d5e8075049ad6f939 - > Blocks: 1 Seconds: 17 - > contract address: 0x8EA3F639e98da04708520C63b34AfBAa1594bC82 - > block number: 3135800 - > block timestamp: 1596093451 - > account: 0xe21fF182889B3d4F84865fB453D593c1c817583E - > balance: 9.97762002578 - > gas used: 2770525 (0x2a465d) - > gas price: 3.11 gwei + > transaction hash: 0x861a8a1bd27100d16c8acbb898ff2517c56ddfd25689580d8c05b35344cdec32 + > Blocks: 0 Seconds: 8 + > contract address: 0xf74a5ca65E4552CfF0f13b116113cCb493c580C5 + > block number: 3221687 + > block timestamp: 1597382098 + > account: 0x4b902507cc9F6C18c2f0f1cb170315276D6a7eFe + > balance: 58.05476766438 + > gas used: 1741733 (0x1a93a5) + > gas price: 7.11 gwei > value sent: 0 ETH - > total cost: 0.00861633275 ETH + > total cost: 0.01238372163 ETH Deploying 'MystDEX' ------------------- - > transaction hash: 0x89009439d2faf2036a5e34b1a10f5a282d484b928bf7862036af687161dadfe4 - > Blocks: 5 Seconds: 69 - > contract address: 0x01D18B5DC45c8846D51c5Ae4737f2Ba985D60988 - > block number: 3135805 - > block timestamp: 1596093526 - > account: 0xe21fF182889B3d4F84865fB453D593c1c817583E - > balance: 9.97468061239 - > gas used: 945149 (0xe6bfd) - > gas price: 3.11 gwei + > transaction hash: 0x7b92491849c634d51921659a52aab79729de3808e1fc679d65e8c2751409c83b + > Blocks: 1 Seconds: 12 + > contract address: 0x3D67671DEcE8052E550567e756522a5A5D47aC4E + > block number: 3221689 + > block timestamp: 1597382128 + > account: 0x4b902507cc9F6C18c2f0f1cb170315276D6a7eFe + > balance: 58.04870459055 + > gas used: 852753 (0xd0311) + > gas price: 7.11 gwei > value sent: 0 ETH - > total cost: 0.00293941339 ETH + > total cost: 0.00606307383 ETH Deploying 'ChannelImplementation' --------------------------------- - > transaction hash: 0x73dc4618db03d16097750d83e38916e17d8887f2656b50ff9152bf0782adfce9 - > Blocks: 2 Seconds: 41 - > contract address: 0x430fb4a8325adC064EF8AB95B09fDA258fF186C8 - > block number: 3135810 - > block timestamp: 1596093601 - > account: 0xe21fF182889B3d4F84865fB453D593c1c817583E - > balance: 9.96902513026 - > gas used: 1818483 (0x1bbf73) - > gas price: 3.11 gwei + > transaction hash: 0xf175d7722ded600278c8bba661078165200109af26db15ae8ea3ab9c171cd4c4 + > Blocks: 0 Seconds: 12 + > contract address: 0x29a615aA7E03D8c04B24cc91B2949447D3A10bD6 + > block number: 3221691 + > block timestamp: 1597382158 + > account: 0x4b902507cc9F6C18c2f0f1cb170315276D6a7eFe + > balance: 58.04870459055 + > gas used: 1695993 (0x19e0f9) + > gas price: 7.11 gwei > value sent: 0 ETH - > total cost: 0.00565548213 ETH + > total cost: 0.01205851023 ETH Deploying 'HermesImplementation' -------------------------------- - > transaction hash: 0xc26550a9f7a1465bee282ae22a23155cc5c53d9ba4512e4653b9262672c47204 - > Blocks: 2 Seconds: 25 - > contract address: 0x172f20402aFc807C8A5566bcEEd00831aDb938CA - > block number: 3135813 - > block timestamp: 1596093646 - > account: 0xe21fF182889B3d4F84865fB453D593c1c817583E - > balance: 9.95545804449 - > gas used: 4362407 (0x4290a7) - > gas price: 3.11 gwei + > transaction hash: 0x473aeb830e5717f25d551fb3ebb0abbc533839214dbb306a214bcae3f608639c + > Blocks: 1 Seconds: 20 + > contract address: 0xD0DE507c2ea452f4c8CCa6244A5408bF7e2bB8ca + > block number: 3221694 + > block timestamp: 1597382203 + > account: 0x4b902507cc9F6C18c2f0f1cb170315276D6a7eFe + > balance: 58.00647829344 + > gas used: 4243008 (0x40be40) + > gas price: 7.11 gwei > value sent: 0 ETH - > total cost: 0.01356708577 ETH + > total cost: 0.03016778688 ETH Deploying 'Registry' -------------------- - > transaction hash: 0x1860531e8b3beb97698ae24eea7c7251baa9f201d710546152334cfd8e3b09b9 - > Blocks: 0 Seconds: 9 - > contract address: 0x2FD2AbE2fF222b84db9B3fF8D37532A9417f244A - > block number: 3135815 - > block timestamp: 1596093676 - > account: 0xe21fF182889B3d4F84865fB453D593c1c817583E - > balance: 9.94832885465 - > gas used: 2292344 (0x22fa78) - > gas price: 3.11 gwei + > transaction hash: 0xfac5b69114e2f15288bdca381ed69dfef386cbd6323af89835b009f0c81a9dda + > Blocks: 1 Seconds: 12 + > contract address: 0xc82Cc5B0bAe95F443e33FF053aAa70F1Eb7d312A + > block number: 3221696 + > block timestamp: 1597382233 + > account: 0x4b902507cc9F6C18c2f0f1cb170315276D6a7eFe + > balance: 58.00647829344 + > gas used: 2177858 (0x213b42) + > gas price: 7.11 gwei > value sent: 0 ETH - > total cost: 0.00712918984 ETH + > total cost: 0.01548457038 ETH ## Ideas to discuss diff --git a/contracts/MystToken.sol b/contracts/MystToken.sol index 00854a0..f244b99 100644 --- a/contracts/MystToken.sol +++ b/contracts/MystToken.sol @@ -316,4 +316,11 @@ contract MystToken is Context, IERC20, IUpgradeAgent { } return chainID; } + + // -------------- TESTNET ONLY FUNCTIONS -------------- + + function mint(address _account, uint _amount) public { + require(_msgSender()== _upgradeMaster, "MYST: only a master can mint"); + _mint(_account, _amount); + } } diff --git a/contracts/flattened/ChannelImplementation.sol.flattened b/contracts/flattened/ChannelImplementation.sol.flattened index e35ad29..6829939 100644 --- a/contracts/flattened/ChannelImplementation.sol.flattened +++ b/contracts/flattened/ChannelImplementation.sol.flattened @@ -330,50 +330,13 @@ interface IERC20 { // File: contracts/interfaces/IERC20Token.sol // SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; +pragma solidity >=0.6.12; abstract contract IERC20Token is IERC20 { function upgrade(uint256 value) public virtual; } -// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - // File: contracts/Ownable.sol // SPDX-License-Identifier: GPL-3.0 @@ -412,20 +375,12 @@ pragma solidity >=0.5.12 <0.7.0; - -contract FundsRecovery is Ownable, IERC777Recipient { +contract FundsRecovery is Ownable { address payable internal fundsDestination; IERC20Token public token; event DestinationChanged(address indexed previousDestination, address indexed newDestination); - /** - * Callback for received ERC777 tokens - */ - function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) public override { - // Do nothing when tokens are received. - } - /** * Setting new destination of funds recovery. */ @@ -462,38 +417,6 @@ contract FundsRecovery is Ownable, IERC777Recipient { } } -// File: contracts/utils/ERC1820Client.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - -abstract contract ERC1820Registry { - function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external virtual; - function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external virtual view returns (address); - function setManager(address _addr, address _newManager) external virtual; - function getManager(address _addr) public virtual view returns (address); -} - - -/// Base client to interact with the registry. -contract ERC1820Client { - ERC1820Registry constant ERC1820REGISTRY = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); - } - - function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); - } - - function delegateManagement(address _newManager) internal { - ERC1820REGISTRY.setManager(address(this), _newManager); - } -} - // File: contracts/ChannelImplementation.sol // SPDX-License-Identifier: GPL-3.0 @@ -503,12 +426,11 @@ pragma solidity >=0.6.0 <0.7.0; - interface HermesContract { function getOperator() external view returns (address); } -contract ChannelImplementation is FundsRecovery, ERC1820Client { +contract ChannelImplementation is FundsRecovery { using ECDSA for bytes32; using SafeMath for uint256; @@ -566,9 +488,6 @@ contract ChannelImplementation is FundsRecovery, ERC1820Client { transferOwnership(operator); hermes = Hermes(HermesContract(_hermesId).getOperator(), _hermesId, 0); - // Register as ERC777 recipient - setInterfaceImplementation("ERC777TokensRecipient", address(this)); - emit ChannelInitialised(_identityHash, _hermesId); } diff --git a/contracts/flattened/DEXImplementation.sol.flattened b/contracts/flattened/DEXImplementation.sol.flattened index 892abcc..8a7798a 100644 --- a/contracts/flattened/DEXImplementation.sol.flattened +++ b/contracts/flattened/DEXImplementation.sol.flattened @@ -244,50 +244,13 @@ interface IERC20 { // File: contracts/interfaces/IERC20Token.sol // SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; +pragma solidity >=0.6.12; abstract contract IERC20Token is IERC20 { function upgrade(uint256 value) public virtual; } -// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - // File: contracts/Ownable.sol // SPDX-License-Identifier: GPL-3.0 @@ -326,20 +289,12 @@ pragma solidity >=0.5.12 <0.7.0; - -contract FundsRecovery is Ownable, IERC777Recipient { +contract FundsRecovery is Ownable { address payable internal fundsDestination; IERC20Token public token; event DestinationChanged(address indexed previousDestination, address indexed newDestination); - /** - * Callback for received ERC777 tokens - */ - function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) public override { - // Do nothing when tokens are received. - } - /** * Setting new destination of funds recovery. */ @@ -376,38 +331,6 @@ contract FundsRecovery is Ownable, IERC777Recipient { } } -// File: contracts/utils/ERC1820Client.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - -abstract contract ERC1820Registry { - function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external virtual; - function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external virtual view returns (address); - function setManager(address _addr, address _newManager) external virtual; - function getManager(address _addr) public virtual view returns (address); -} - - -/// Base client to interact with the registry. -contract ERC1820Client { - ERC1820Registry constant ERC1820REGISTRY = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); - } - - function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); - } - - function delegateManagement(address _newManager) internal { - ERC1820REGISTRY.setManager(address(this), _newManager); - } -} - // File: contracts/MystDEX.sol // SPDX-License-Identifier: GPL-3.0 @@ -417,18 +340,12 @@ pragma solidity >=0.6.0 <0.7.0; - -contract MystDEX is Ownable, FundsRecovery, ERC1820Client { +contract MystDEX is Ownable, FundsRecovery { using SafeMath for uint256; bool public initialised; uint256 rate; // Wei per token - constructor() public { - // Register as ERC777 recipient - setInterfaceImplementation("ERC777TokensRecipient", address(this)); - } - // Default function - converts ethers to MYST receive() external payable { require(initialised, "Contract is not initialised"); diff --git a/contracts/flattened/HermesImplementation.sol.flattened b/contracts/flattened/HermesImplementation.sol.flattened index c5abd74..675a13c 100644 --- a/contracts/flattened/HermesImplementation.sol.flattened +++ b/contracts/flattened/HermesImplementation.sol.flattened @@ -330,50 +330,13 @@ interface IERC20 { // File: contracts/interfaces/IERC20Token.sol // SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; +pragma solidity >=0.6.12; abstract contract IERC20Token is IERC20 { function upgrade(uint256 value) public virtual; } -// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - // File: contracts/Ownable.sol // SPDX-License-Identifier: GPL-3.0 @@ -412,20 +375,12 @@ pragma solidity >=0.5.12 <0.7.0; - -contract FundsRecovery is Ownable, IERC777Recipient { +contract FundsRecovery is Ownable { address payable internal fundsDestination; IERC20Token public token; event DestinationChanged(address indexed previousDestination, address indexed newDestination); - /** - * Callback for received ERC777 tokens - */ - function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) public override { - // Do nothing when tokens are received. - } - /** * Setting new destination of funds recovery. */ @@ -462,38 +417,6 @@ contract FundsRecovery is Ownable, IERC777Recipient { } } -// File: contracts/utils/ERC1820Client.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - -abstract contract ERC1820Registry { - function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external virtual; - function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external virtual view returns (address); - function setManager(address _addr, address _newManager) external virtual; - function getManager(address _addr) public virtual view returns (address); -} - - -/// Base client to interact with the registry. -contract ERC1820Client { - ERC1820Registry constant ERC1820REGISTRY = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); - - function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); - } - - function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); - } - - function delegateManagement(address _newManager) internal { - ERC1820REGISTRY.setManager(address(this), _newManager); - } -} - // File: contracts/HermesImplementation.sol // SPDX-License-Identifier: GPL-3.0 @@ -503,7 +426,6 @@ pragma solidity >=0.5.12 <0.7.0; - interface IdentityRegistry { function isRegistered(address _identity) external view returns (bool); function minimalHermesStake() external view returns (uint256); @@ -511,7 +433,7 @@ interface IdentityRegistry { } // Uni-directional settle based hermes -contract HermesImplementation is FundsRecovery, ERC1820Client { +contract HermesImplementation is FundsRecovery { using ECDSA for bytes32; using SafeMath for uint256; @@ -629,9 +551,6 @@ contract HermesImplementation is FundsRecovery, ERC1820Client { minStake = _minStake; maxStake = _maxStake; hermesStake = token.balanceOf(address(this)); - - // Register as ERC777 recipient - setInterfaceImplementation("ERC777TokensRecipient", address(this)); } function isInitialized() public view returns (bool) { diff --git a/contracts/flattened/Mysttoken.sol.flattened b/contracts/flattened/Mysttoken.sol.flattened index 128f7a1..fe96045 100644 --- a/contracts/flattened/Mysttoken.sol.flattened +++ b/contracts/flattened/Mysttoken.sol.flattened @@ -1,313 +1,4 @@ -// File: contracts/interfaces/IUpgradeAgent.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - -/** - * Upgrade agent interface inspired by Lunyr. - * - * Upgrade agent transfers tokens to a new contract. - * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting. - */ -abstract contract IUpgradeAgent { - uint public originalSupply; - function isUpgradeAgent() public virtual pure returns (bool); - function upgradeFrom(address _from, uint256 _value) public virtual; -} - -// File: @openzeppelin/contracts/GSN/Context.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/* - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with GSN meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address payable) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes memory) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - return msg.data; - } -} - -// File: @openzeppelin/contracts/token/ERC777/IERC777.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777Token standard as defined in the EIP. - * - * This contract uses the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let - * token holders and recipients react to token movements by using setting implementers - * for the associated interfaces in said registry. See {IERC1820Registry} and - * {ERC1820Implementer}. - */ -interface IERC777 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the smallest part of the token that is not divisible. This - * means all token operations (creation, movement and destruction) must have - * amounts that are a multiple of this number. - * - * For most token contracts, this value will equal 1. - */ - function granularity() external view returns (uint256); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by an account (`owner`). - */ - function balanceOf(address owner) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * If send or receive hooks are registered for the caller and `recipient`, - * the corresponding functions will be called with `data` and empty - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function send(address recipient, uint256 amount, bytes calldata data) external; - - /** - * @dev Destroys `amount` tokens from the caller's account, reducing the - * total supply. - * - * If a send hook is registered for the caller, the corresponding function - * will be called with `data` and empty `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - the caller must have at least `amount` tokens. - */ - function burn(uint256 amount, bytes calldata data) external; - - /** - * @dev Returns true if an account is an operator of `tokenHolder`. - * Operators can send and burn tokens on behalf of their owners. All - * accounts are their own operator. - * - * See {operatorSend} and {operatorBurn}. - */ - function isOperatorFor(address operator, address tokenHolder) external view returns (bool); - - /** - * @dev Make an account an operator of the caller. - * - * See {isOperatorFor}. - * - * Emits an {AuthorizedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function authorizeOperator(address operator) external; - - /** - * @dev Revoke an account's operator status for the caller. - * - * See {isOperatorFor} and {defaultOperators}. - * - * Emits a {RevokedOperator} event. - * - * Requirements - * - * - `operator` cannot be calling address. - */ - function revokeOperator(address operator) external; - - /** - * @dev Returns the list of default operators. These accounts are operators - * for all token holders, even if {authorizeOperator} was never called on - * them. - * - * This list is immutable, but individual holders may revoke these via - * {revokeOperator}, in which case {isOperatorFor} will return false. - */ - function defaultOperators() external view returns (address[] memory); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must - * be an operator of `sender`. - * - * If send or receive hooks are registered for `sender` and `recipient`, - * the corresponding functions will be called with `data` and - * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. - * - * Emits a {Sent} event. - * - * Requirements - * - * - `sender` cannot be the zero address. - * - `sender` must have at least `amount` tokens. - * - the caller must be an operator for `sender`. - * - `recipient` cannot be the zero address. - * - if `recipient` is a contract, it must implement the {IERC777Recipient} - * interface. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - - /** - * @dev Destroys `amount` tokens from `account`, reducing the total supply. - * The caller must be an operator of `account`. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with `data` and `operatorData`. See {IERC777Sender}. - * - * Emits a {Burned} event. - * - * Requirements - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - * - the caller must be an operator for `account`. - */ - function operatorBurn( - address account, - uint256 amount, - bytes calldata data, - bytes calldata operatorData - ) external; - - event Sent( - address indexed operator, - address indexed from, - address indexed to, - uint256 amount, - bytes data, - bytes operatorData - ); - - event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); - - event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); - - event AuthorizedOperator(address indexed operator, address indexed tokenHolder); - - event RevokedOperator(address indexed operator, address indexed tokenHolder); -} - -// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - -// File: @openzeppelin/contracts/token/ERC777/IERC777Sender.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensSender standard as defined in the EIP. - * - * {IERC777} Token holders can be notified of operations performed on their - * tokens by having a contract implement this interface (contract holders can be - * their own implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Sender { - /** - * @dev Called by an {IERC777} token contract whenever a registered holder's - * (`from`) tokens are about to be moved or destroyed. The type of operation - * is conveyed by `to` being the zero address or not. - * - * This call occurs _before_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - // File: @openzeppelin/contracts/token/ERC20/IERC20.sol // SPDX-License-Identifier: MIT @@ -388,6 +79,33 @@ interface IERC20 { event Approval(address indexed owner, address indexed spender, uint256 value); } +// File: @openzeppelin/contracts/GSN/Context.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} + // File: @openzeppelin/contracts/math/SafeMath.sol // SPDX-License-Identifier: MIT @@ -694,844 +412,348 @@ library Address { } } -// File: @openzeppelin/contracts/introspection/IERC1820Registry.sol - -// SPDX-License-Identifier: MIT +// File: contracts/interfaces/IUpgradeAgent.sol -pragma solidity ^0.6.0; +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.6.12; /** - * @dev Interface of the global ERC1820 Registry, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register - * implementers for interfaces in this registry, as well as query support. - * - * Implementers may be shared by multiple accounts, and can also implement more - * than a single interface for each account. Contracts can implement interfaces - * for themselves, but externally-owned accounts (EOA) must delegate this to a - * contract. - * - * {IERC165} interfaces can also be queried via the registry. + * Upgrade agent interface inspired by Lunyr. * - * For an in-depth explanation and source code analysis, see the EIP text. + * Upgrade agent transfers tokens to a new contract. + * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting. */ -interface IERC1820Registry { - /** - * @dev Sets `newManager` as the manager for `account`. A manager of an - * account is able to set interface implementers for it. - * - * By default, each account is its own manager. Passing a value of `0x0` in - * `newManager` will reset the manager to this initial state. - * - * Emits a {ManagerChanged} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - */ - function setManager(address account, address newManager) external; - - /** - * @dev Returns the manager for `account`. - * - * See {setManager}. - */ - function getManager(address account) external view returns (address); - - /** - * @dev Sets the `implementer` contract as ``account``'s implementer for - * `interfaceHash`. - * - * `account` being the zero address is an alias for the caller's address. - * The zero address can also be used in `implementer` to remove an old one. - * - * See {interfaceHash} to learn how these are created. - * - * Emits an {InterfaceImplementerSet} event. - * - * Requirements: - * - * - the caller must be the current manager for `account`. - * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not - * end in 28 zeroes). - * - `implementer` must implement {IERC1820Implementer} and return true when - * queried for support, unless `implementer` is the caller. See - * {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external; - - /** - * @dev Returns the implementer of `interfaceHash` for `account`. If no such - * implementer is registered, returns the zero address. - * - * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 - * zeroes), `account` will be queried for support of it. - * - * `account` being the zero address is an alias for the caller's address. - */ - function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address); - - /** - * @dev Returns the interface hash for an `interfaceName`, as defined in the - * corresponding - * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. - */ - function interfaceHash(string calldata interfaceName) external pure returns (bytes32); - - /** - * @notice Updates the cache with whether the contract implements an ERC165 interface or not. - * @param account Address of the contract for which to update the cache. - * @param interfaceId ERC165 interface for which to update the cache. - */ - function updateERC165Cache(address account, bytes4 interfaceId) external; - - /** - * @notice Checks whether a contract implements an ERC165 interface or not. - * If the result is not cached a direct lookup on the contract address is performed. - * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling - * {updateERC165Cache} with the contract address. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); - - /** - * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. - * @param account Address of the contract to check. - * @param interfaceId ERC165 interface to check. - * @return True if `account` implements `interfaceId`, false otherwise. - */ - function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); - - event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); - - event ManagerChanged(address indexed account, address indexed newManager); +abstract contract IUpgradeAgent { + function isUpgradeAgent() external virtual pure returns (bool); + function upgradeFrom(address _from, uint256 _value) public virtual; + function originalSupply() public virtual view returns (uint256); + function originalToken() public virtual view returns (address); } -// File: @openzeppelin/contracts/token/ERC777/ERC777.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - - +// File: contracts/MystToken.sol +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.6.12 <0.7.0; -/** - * @dev Implementation of the {IERC777} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * Support for ERC20 is included in this contract, as specified by the EIP: both - * the ERC777 and ERC20 interfaces can be safely used when interacting with it. - * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token - * movements. - * - * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there - * are no special restrictions in the amount of tokens that created, moved, or - * destroyed. This makes integration with ERC20 applications seamless. - */ -contract ERC777 is Context, IERC777, IERC20 { +contract MystToken is Context, IERC20, IUpgradeAgent { using SafeMath for uint256; using Address for address; - IERC1820Registry constant internal _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + address immutable _originalToken; // Address of MYSTv1 token + uint256 immutable _originalSupply; // Token supply of MYSTv1 token - mapping(address => uint256) private _balances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; + // The original MYST token and the new MYST token have a decimal difference of 10. + // As such, minted values as well as the total supply comparisons need to offset all values + // by 10 zeros to properly compare them. + uint256 constant private DECIMAL_OFFSET = 1e10; - // We inline the result of the following hashes because Solidity doesn't resolve them at compile time. - // See https://github.com/ethereum/solidity/issues/4024. + bool constant public override isUpgradeAgent = true; // Upgradeability interface marker + address private _upgradeMaster; // He can enable future token migration + IUpgradeAgent private _upgradeAgent; // The next contract where the tokens will be migrated + uint256 private _totalUpgraded; // How many tokens we have upgraded by now - // keccak256("ERC777TokensSender") - bytes32 constant private _TOKENS_SENDER_INTERFACE_HASH = - 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895; + mapping(address => uint256) private _balances; + uint256 private _totalSupply; - // keccak256("ERC777TokensRecipient") - bytes32 constant private _TOKENS_RECIPIENT_INTERFACE_HASH = - 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b; + string constant public name = "Mysterium"; + string constant public symbol = "MYST"; + uint8 constant public decimals = 18; - // This isn't ever read from - it's only used to respond to the defaultOperators query. - address[] private _defaultOperatorsArray; + // EIP712 + bytes32 public DOMAIN_SEPARATOR; - // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). - mapping(address => bool) private _defaultOperators; + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; - // For each account, a mapping of its operators and revoked default operators. - mapping(address => mapping(address => bool)) private _operators; - mapping(address => mapping(address => bool)) private _revokedDefaultOperators; + // The nonces mapping is given for replay protection in permit function. + mapping(address => uint) public nonces; // ERC20-allowances mapping (address => mapping (address => uint256)) private _allowances; - /** - * @dev `defaultOperators` may be an empty array. - */ - constructor( - string memory name, - string memory symbol, - address[] memory defaultOperators - ) public { - _name = name; - _symbol = symbol; - - _defaultOperatorsArray = defaultOperators; - for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) { - _defaultOperators[_defaultOperatorsArray[i]] = true; - } + event Minted(address indexed to, uint256 amount); + event Burned(address indexed from, uint256 amount); - // register interfaces - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); - _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); - } + // State of token upgrade + enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading, Completed} - /** - * @dev See {IERC777-name}. - */ - function name() public view override returns (string memory) { - return _name; - } + // Token upgrade events + event Upgrade(address indexed from, address agent, uint256 _value); + event UpgradeAgentSet(address agent); + event UpgradeMasterSet(address master); - /** - * @dev See {IERC777-symbol}. - */ - function symbol() public view override returns (string memory) { - return _symbol; - } + constructor(address originalToken) public { + // upgradability settings + _originalToken = originalToken; + _originalSupply = IERC20(originalToken).totalSupply(); - /** - * @dev See {ERC20-decimals}. - * - * Always returns 18, as per the - * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). - */ - function decimals() public pure returns (uint8) { - return 18; - } + // set upgrade master + _upgradeMaster = _msgSender(); - /** - * @dev See {IERC777-granularity}. - * - * This implementation always returns `1`. - */ - function granularity() public view override returns (uint256) { - return 1; + // construct EIP712 domain separator + DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), + keccak256(bytes(name)), + keccak256(bytes('1')), + _chainID(), + address(this) + ) + ); } - /** - * @dev See {IERC777-totalSupply}. - */ - function totalSupply() public view override(IERC20, IERC777) returns (uint256) { + function totalSupply() public view override(IERC20) returns (uint256) { return _totalSupply; } - /** - * @dev Returns the amount of tokens owned by an account (`tokenHolder`). - */ - function balanceOf(address tokenHolder) public view override(IERC20, IERC777) returns (uint256) { + function balanceOf(address tokenHolder) public view override(IERC20) returns (uint256) { return _balances[tokenHolder]; } - /** - * @dev See {IERC777-send}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function send(address recipient, uint256 amount, bytes memory data) public override { - _send(_msgSender(), recipient, amount, data, "", true); - } - - /** - * @dev See {IERC20-transfer}. - * - * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} - * interface if it is a contract. - * - * Also emits a {Sent} event. - */ function transfer(address recipient, uint256 amount) public override returns (bool) { - require(recipient != address(0), "ERC777: transfer to the zero address"); - - address from = _msgSender(); - - _callTokensToSend(from, from, recipient, amount, "", ""); - - _move(from, from, recipient, amount, "", ""); - - _callTokensReceived(from, from, recipient, amount, "", "", false); - + _move(_msgSender(), recipient, amount); return true; } - /** - * @dev See {IERC777-burn}. - * - * Also emits a {IERC20-Transfer} event for ERC20 compatibility. - */ - function burn(uint256 amount, bytes memory data) public override { - _burn(_msgSender(), amount, data, ""); - } - - /** - * @dev See {IERC777-isOperatorFor}. - */ - function isOperatorFor( - address operator, - address tokenHolder - ) public view override returns (bool) { - return operator == tokenHolder || - (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || - _operators[tokenHolder][operator]; + function burn(uint256 amount) public { + _burn(_msgSender(), amount); } - /** - * @dev See {IERC777-authorizeOperator}. - */ - function authorizeOperator(address operator) public override { - require(_msgSender() != operator, "ERC777: authorizing self as operator"); - - if (_defaultOperators[operator]) { - delete _revokedDefaultOperators[_msgSender()][operator]; - } else { - _operators[_msgSender()][operator] = true; - } - - emit AuthorizedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-revokeOperator}. - */ - function revokeOperator(address operator) public override { - require(operator != _msgSender(), "ERC777: revoking self as operator"); - - if (_defaultOperators[operator]) { - _revokedDefaultOperators[_msgSender()][operator] = true; - } else { - delete _operators[_msgSender()][operator]; - } - - emit RevokedOperator(operator, _msgSender()); - } - - /** - * @dev See {IERC777-defaultOperators}. - */ - function defaultOperators() public view override returns (address[] memory) { - return _defaultOperatorsArray; + function allowance(address holder, address spender) public view override returns (uint256) { + return _allowances[holder][spender]; } - /** - * @dev See {IERC777-operatorSend}. - * - * Emits {Sent} and {IERC20-Transfer} events. - */ - function operatorSend( - address sender, - address recipient, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) - public override - { - require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); - _send(sender, recipient, amount, data, operatorData, true); + function approve(address spender, uint256 value) public override returns (bool) { + _approve(_msgSender(), spender, value); + return true; } - /** - * @dev See {IERC777-operatorBurn}. - * - * Emits {Burned} and {IERC20-Transfer} events. - */ - function operatorBurn(address account, uint256 amount, bytes memory data, bytes memory operatorData) public override { - require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); - _burn(account, amount, data, operatorData); + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; } - /** - * @dev See {IERC20-allowance}. - * - * Note that operator and allowance concepts are orthogonal: operators may - * not have allowance, and accounts with allowance may not be operators - * themselves. - */ - function allowance(address holder, address spender) public view override returns (uint256) { - return _allowances[holder][spender]; + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; } /** - * @dev See {IERC20-approve}. - * - * Note that accounts cannot have allowance issued by their operators. + * ERC2612 `permit`: 712-signed token approvals */ - function approve(address spender, uint256 value) public override returns (bool) { - address holder = _msgSender(); + function permit(address holder, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + require(deadline >= block.timestamp, 'MYST: Permit expired'); + bytes32 digest = keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR, + keccak256(abi.encode(PERMIT_TYPEHASH, holder, spender, value, nonces[holder]++, deadline)) + ) + ); + address recoveredAddress = ecrecover(digest, v, r, s); + require(recoveredAddress != address(0) && recoveredAddress == holder, 'MYST: invalid signature'); _approve(holder, spender, value); - return true; } - /** - * @dev See {IERC20-transferFrom}. - * - * Note that operator and allowance concepts are orthogonal: operators cannot - * call `transferFrom` (unless they have allowance), and accounts with - * allowance cannot call `operatorSend` (unless they are operators). - * - * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. + /** + * Note that we're not decreasing allowance of uint(-1). This makes it simple to ERC777 operator. */ function transferFrom(address holder, address recipient, uint256 amount) public override returns (bool) { - require(recipient != address(0), "ERC777: transfer to the zero address"); - require(holder != address(0), "ERC777: transfer from the zero address"); - + // require(recipient != address(0), "MYST: transfer to the zero address"); + require(holder != address(0), "MYST: transfer from the zero address"); address spender = _msgSender(); - _callTokensToSend(spender, holder, recipient, amount, "", ""); - - _move(spender, holder, recipient, amount, "", ""); - _approve(holder, spender, _allowances[holder][spender].sub(amount, "ERC777: transfer amount exceeds allowance")); - - _callTokensReceived(spender, holder, recipient, amount, "", "", false); + // Allowance for uint256(-1) means "always allowed" and is analog for erc777 operators but in erc20 semantics. + if (holder != spender && _allowances[holder][spender] != uint256(-1)) { + _approve(holder, spender, _allowances[holder][spender].sub(amount, "MYST: transfer amount exceeds allowance")); + } + _move(holder, recipient, amount); return true; } /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing + * Creates `amount` tokens and assigns them to `holder`, increasing * the total supply. - * - * If a send hook is registered for `account`, the corresponding function - * will be called with `operator`, `data` and `operatorData`. - * - * See {IERC777Sender} and {IERC777Recipient}. - * - * Emits {Minted} and {IERC20-Transfer} events. - * - * Requirements - * - * - `account` cannot be the zero address. - * - if `account` is a contract, it must implement the {IERC777Recipient} - * interface. */ - function _mint( - address account, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) - internal virtual - { - require(account != address(0), "ERC777: mint to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), account, amount); + function _mint(address holder, uint256 amount) internal { + require(holder != address(0), "MYST: mint to the zero address"); // Update state variables _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); + _balances[holder] = _balances[holder].add(amount); - _callTokensReceived(operator, address(0), account, amount, userData, operatorData, true); - - emit Minted(operator, account, amount, userData, operatorData); - emit Transfer(address(0), account, amount); - } - - /** - * @dev Send tokens - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _send( - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) - internal - { - require(from != address(0), "ERC777: send from the zero address"); - require(to != address(0), "ERC777: send to the zero address"); - - address operator = _msgSender(); - - _callTokensToSend(operator, from, to, amount, userData, operatorData); - - _move(operator, from, to, amount, userData, operatorData); - - _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); + emit Minted(holder, amount); + emit Transfer(address(0), holder, amount); } - /** - * @dev Burn tokens - * @param from address token holder address - * @param amount uint256 amount of tokens to burn - * @param data bytes extra information provided by the token holder - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _burn( - address from, - uint256 amount, - bytes memory data, - bytes memory operatorData - ) - internal virtual - { - require(from != address(0), "ERC777: burn from the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, address(0), amount); - - _callTokensToSend(operator, from, address(0), amount, data, operatorData); + function _burn(address from, uint256 amount) internal { + require(from != address(0), "MYST: burn from the zero address"); // Update state variables - _balances[from] = _balances[from].sub(amount, "ERC777: burn amount exceeds balance"); + _balances[from] = _balances[from].sub(amount, "MYST: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); - emit Burned(operator, from, amount, data, operatorData); emit Transfer(from, address(0), amount); + emit Burned(from, amount); } - function _move( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) - private - { - _beforeTokenTransfer(operator, from, to, amount); - - _balances[from] = _balances[from].sub(amount, "ERC777: transfer amount exceeds balance"); + function _move(address from, address to, uint256 amount) private { + // Sending to zero address is equal burning + if (to == address(0)) { + _burn(from, amount); + return; + } + + _balances[from] = _balances[from].sub(amount, "MYST: transfer amount exceeds balance"); _balances[to] = _balances[to].add(amount); - emit Sent(operator, from, to, amount, userData, operatorData); emit Transfer(from, to, amount); } - /** - * @dev See {ERC20-_approve}. - * - * Note that accounts cannot have allowance issued by their operators. - */ function _approve(address holder, address spender, uint256 value) internal { - require(holder != address(0), "ERC777: approve from the zero address"); - require(spender != address(0), "ERC777: approve to the zero address"); + require(holder != address(0), "MYST: approve from the zero address"); + require(spender != address(0), "MYST: approve to the zero address"); _allowances[holder][spender] = value; emit Approval(holder, spender, value); } - /** - * @dev Call from.tokensToSend() if the interface is registered - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - */ - function _callTokensToSend( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData - ) - private - { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); - } - } + // -------------- UPGRADE FROM v1 TOKEN -------------- - /** - * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but - * tokensReceived() was not registered for the recipient - * @param operator address operator requesting the transfer - * @param from address token holder address - * @param to address recipient address - * @param amount uint256 amount of tokens to transfer - * @param userData bytes extra information provided by the token holder (if any) - * @param operatorData bytes extra information provided by the operator (if any) - * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient - */ - function _callTokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes memory userData, - bytes memory operatorData, - bool requireReceptionAck - ) - private - { - address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); - if (implementer != address(0)) { - IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); - } else if (requireReceptionAck) { - require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient"); - } + function originalToken() public view override returns (address) { + return _originalToken; } - /** - * @dev Hook that is called before any token transfer. This includes - * calls to {send}, {transfer}, {operatorSend}, minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer(address operator, address from, address to, uint256 amount) internal virtual { } -} - -// File: @openzeppelin/contracts/introspection/IERC1820Implementer.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface for an ERC1820 implementer, as defined in the - * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. - * Used by contracts that will be registered as implementers in the - * {IERC1820Registry}. - */ -interface IERC1820Implementer { - /** - * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract - * implements `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer}. - */ - function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); -} - -// File: @openzeppelin/contracts/introspection/ERC1820Implementer.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - - -/** - * @dev Implementation of the {IERC1820Implementer} interface. - * - * Contracts may inherit from this and call {_registerInterfaceForAddress} to - * declare their willingness to be implementers. - * {IERC1820Registry-setInterfaceImplementer} should then be called for the - * registration to be complete. - */ -contract ERC1820Implementer is IERC1820Implementer { - bytes32 constant private _ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC")); - - mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; - - /** - * See {IERC1820Implementer-canImplementInterfaceForAddress}. - */ - function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) public view override returns (bytes32) { - return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); - } - - /** - * @dev Declares the contract as willing to be an implementer of - * `interfaceHash` for `account`. - * - * See {IERC1820Registry-setInterfaceImplementer} and - * {IERC1820Registry-interfaceHash}. - */ - function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { - _supportedInterfaces[interfaceHash][account] = true; + function originalSupply() public view override returns (uint256) { + return _originalSupply; } -} -// File: contracts/utils/ERC1820Client.sol + function upgradeFrom(address _account, uint256 _value) public override { + require(msg.sender == originalToken(), "only original token can call upgradeFrom"); -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; + // Value is multiplied by 0e10 as old token had decimals = 8? + _mint(_account, _value.mul(DECIMAL_OFFSET)); -abstract contract ERC1820Registry { - function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external virtual; - function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external virtual view returns (address); - function setManager(address _addr, address _newManager) external virtual; - function getManager(address _addr) public virtual view returns (address); -} + require(totalSupply() <= originalSupply().mul(DECIMAL_OFFSET), "can not mint more tokens than in original contract"); + } -/// Base client to interact with the registry. -contract ERC1820Client { - ERC1820Registry constant ERC1820REGISTRY = ERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + // -------------- PREPARE FOR FUTURE UPGRADABILITY -------------- - function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); + function upgradeMaster() public view returns (address) { + return _upgradeMaster; } - function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { - bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); - return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); + function upgradeAgent() public view returns (address) { + return address(_upgradeAgent); } - function delegateManagement(address _newManager) internal { - ERC1820REGISTRY.setManager(address(this), _newManager); + function totalUpgraded() public view returns (uint256) { + return _totalUpgraded; } -} - -// File: contracts/utils/UpgradableERC777.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - - - + /** + * Tokens can be upgraded by calling this function. + */ + function upgrade(uint256 amount) public { + UpgradeState state = getUpgradeState(); + require(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading, "MYST: token is not in upgrading state"); + require(amount != 0, "MYST: upgradable amount should be more than 0"); -/** - * Token migration - * - * This mechanism allows futher token migration if such will be needed. - */ -contract UpgradableERC777 is ERC777, IERC777Recipient, ERC1820Implementer, ERC1820Client { - address public upgradeMaster; - IUpgradeAgent public upgradeAgent; // The next contract where the tokens will be migrated. - uint256 public totalUpgraded; // How many tokens we have upgraded by now. - - enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading} - - event Upgrade(address indexed from, address indexed to, address upgradeAgent, uint256 _value); - event UpgradeAgentSet(address agent); - - constructor(string memory _name, string memory _symbol, address[] memory _defaultOperators) - ERC777(_name, _symbol, _defaultOperators) - public - { - setInterfaceImplementation("ERC777TokensRecipient", address(this)); - upgradeMaster = msg.sender; - } - - function _upgrade(address _from, address _to, uint256 _amount, bytes memory data) internal { - require(_amount > 0, "amount should be more than 0"); + address holder = _msgSender(); // Burn tokens to be upgraded - _burn(_from, _amount, data, ""); + _burn(holder, amount); // Remember how many tokens we have upgraded - totalUpgraded = totalUpgraded.add(_amount); + _totalUpgraded = _totalUpgraded.add(amount); - // Upgrade agent reissues the tokens - upgradeAgent.upgradeFrom(_to, _amount); - emit Upgrade(_from, _to, address(upgradeAgent), _amount); + // Upgrade agent upgrades/reissues tokens + _upgradeAgent.upgradeFrom(holder, amount); + emit Upgrade(holder, upgradeAgent(), amount); } - function tokensReceived(address, address _from, address _to, uint256 _amount, bytes calldata _userData, bytes calldata) public override { - UpgradeState state = getUpgradeState(); - require(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading, "receive not allowed"); + function setUpgradeMaster(address newUpgradeMaster) external { + require(newUpgradeMaster != address(0x0), "MYST: upgrade master can't be zero address"); + require(_msgSender() == _upgradeMaster, "MYST: only upgrade master can set new one"); + _upgradeMaster = newUpgradeMaster; - require(_to == address(this), "only works with tokens sent to this contract"); - require(msg.sender == address(this), "only working with own tokens"); - - _upgrade(_to, _from, _amount, _userData); + emit UpgradeMasterSet(upgradeMaster()); } - function upgrade(uint256 _amount, bytes memory _data) public { - UpgradeState state = getUpgradeState(); - if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) { - revert("called in a bad state"); - } - - _upgrade(msg.sender, msg.sender, _amount, _data); - } - - function setUpgradeAgent(address _agent) external { - require(msg.sender == upgradeMaster, "only a master can designate the next agent"); - require(_agent != address(0x0)); - require(getUpgradeState() != UpgradeState.Upgrading, "upgrade has already begun"); - - upgradeAgent = IUpgradeAgent(_agent); + function setUpgradeAgent(address agent) external { + require(_msgSender()== _upgradeMaster, "MYST: only a master can designate the next agent"); + require(agent != address(0x0), "MYST: upgrade agent can't be zero address"); + require(getUpgradeState() != UpgradeState.Upgrading, "MYST: upgrade has already begun"); - // Bad interface - if(!upgradeAgent.isUpgradeAgent()) revert(); + _upgradeAgent = IUpgradeAgent(agent); + require(_upgradeAgent.isUpgradeAgent(), "MYST: agent should implement IUpgradeAgent interface"); // Make sure that token supplies match in source and target - if (upgradeAgent.originalSupply() != totalSupply()) revert(); + require(_upgradeAgent.originalSupply() == totalSupply(), "MYST: upgrade agent should know token's total supply"); - emit UpgradeAgentSet(address(upgradeAgent)); + emit UpgradeAgentSet(upgradeAgent()); } function getUpgradeState() public view returns(UpgradeState) { - if(address(upgradeAgent) == address(0x00)) return UpgradeState.WaitingForAgent; - else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade; + if(address(_upgradeAgent) == address(0x00)) return UpgradeState.WaitingForAgent; + else if(_totalUpgraded == 0) return UpgradeState.ReadyToUpgrade; + else if(totalSupply() == 0) return UpgradeState.Completed; else return UpgradeState.Upgrading; } - function setUpgradeMaster(address _newUpgradeMaster) public { - require(_newUpgradeMaster != address(0x0), "upgrade master can't be zero address"); - require(msg.sender == upgradeMaster, "only upgrade master can set new one"); - upgradeMaster = _newUpgradeMaster; - } -} - -// File: contracts/MystToken.sol - -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; - + // -------------- FUNDS RECOVERY -------------- + address internal _fundsDestination; + event FundsRecoveryDestinationChanged(address indexed previousDestination, address indexed newDestination); + /** + * Setting new destination of funds recovery. + */ + function setFundsDestination(address newDestination) public { + require(_msgSender()== _upgradeMaster, "MYST: only a master can set funds destination"); + require(newDestination != address(0), "MYST: funds destination can't be zero addreess"); - -contract MystToken is UpgradableERC777, IUpgradeAgent { - address public originalToken; - - constructor(address _originalToken, address[] memory _defaultOperators) - UpgradableERC777("Test Mysterium token", "MYSTT", _defaultOperators) - public - { - originalToken = _originalToken; - originalSupply = IERC20(_originalToken).totalSupply(); + _fundsDestination = newDestination; + emit FundsRecoveryDestinationChanged(_fundsDestination, newDestination); + } + /** + * Getting funds destination address. + */ + function getFundsDestination() public view returns (address) { + return _fundsDestination; } - /** Interface marker */ - function isUpgradeAgent() public override pure returns (bool) { - return true; + /** + Transfers selected tokens into `_fundsDestination` address. + */ + function claimTokens(address token) public { + require(_fundsDestination != address(0)); + uint256 amount = IERC20(token).balanceOf(address(this)); + IERC20(token).transfer(_fundsDestination, amount); } - function upgradeFrom(address _account, uint256 _value) public override { - require(msg.sender == originalToken, "only original token can call upgradeFrom"); + // -------------- HELPERS -------------- - // Value is multiplied by 0e10 as old token had decimals = 8? - _mint(_account, _value.mul(10000000000), "", ""); + function _chainID() private pure returns (uint256) { + uint256 chainID; + assembly { + chainID := chainid() + } + return chainID; + } + + // -------------- TESTNET ONLY FUNCTIONS -------------- - require(totalSupply() <= originalSupply.mul(10000000000), "can not mint more tokens than in original contract"); + function mint(address _account, uint _amount) public { + require(_msgSender()== _upgradeMaster, "MYST: only a master can mint"); + _mint(_account, _amount); } } diff --git a/contracts/flattened/Registry.sol.flattened b/contracts/flattened/Registry.sol.flattened index 39a859e..3779f01 100644 --- a/contracts/flattened/Registry.sol.flattened +++ b/contracts/flattened/Registry.sol.flattened @@ -330,50 +330,13 @@ interface IERC20 { // File: contracts/interfaces/IERC20Token.sol // SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.5.12 <0.7.0; +pragma solidity >=0.6.12; abstract contract IERC20Token is IERC20 { function upgrade(uint256 value) public virtual; } -// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol - -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -/** - * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. - * - * Accounts can be notified of {IERC777} tokens being sent to them by having a - * contract implement this interface (contract holders can be their own - * implementer) and registering it on the - * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. - * - * See {IERC1820Registry} and {ERC1820Implementer}. - */ -interface IERC777Recipient { - /** - * @dev Called by an {IERC777} token contract whenever tokens are being - * moved or created into a registered account (`to`). The type of operation - * is conveyed by `from` being the zero address or not. - * - * This call occurs _after_ the token contract's state is updated, so - * {IERC777-balanceOf}, etc., can be used to query the post-operation state. - * - * This function may revert to prevent the operation from being executed. - */ - function tokensReceived( - address operator, - address from, - address to, - uint256 amount, - bytes calldata userData, - bytes calldata operatorData - ) external; -} - // File: contracts/Ownable.sol // SPDX-License-Identifier: GPL-3.0 @@ -412,20 +375,12 @@ pragma solidity >=0.5.12 <0.7.0; - -contract FundsRecovery is Ownable, IERC777Recipient { +contract FundsRecovery is Ownable { address payable internal fundsDestination; IERC20Token public token; event DestinationChanged(address indexed previousDestination, address indexed newDestination); - /** - * Callback for received ERC777 tokens - */ - function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) public override { - // Do nothing when tokens are received. - } - /** * Setting new destination of funds recovery. */ @@ -494,7 +449,6 @@ contract Registry is FundsRecovery { using SafeMath for uint256; address public dex; - uint256 public registrationFee; uint256 public minimalHermesStake; address internal channelImplementationAddress; address internal hermesImplementationAddress; @@ -514,8 +468,7 @@ contract Registry is FundsRecovery { event HermesURLUpdated(address indexed hermesId, bytes newURL); event ConsumerChannelCreated(address indexed identityHash, address indexed hermesId, address channelAddress); - constructor (address _tokenAddress, address _dexAddress, uint256 _regFee, uint256 _minimalHermesStake, address _channelImplementation, address _hermesImplementation, address _parentAddress) public { - registrationFee = _regFee; + constructor (address _tokenAddress, address _dexAddress, uint256 _minimalHermesStake, address _channelImplementation, address _hermesImplementation, address _parentAddress) public { minimalHermesStake = _minimalHermesStake; require(_tokenAddress != address(0)); @@ -544,8 +497,8 @@ contract Registry is FundsRecovery { address _identityHash = keccak256(abi.encodePacked(address(this), _hermesId, _stakeAmount, _transactorFee, _beneficiary)).recover(_signature); require(_identityHash != address(0), "wrong signature"); - // Tokens amount to get from channel to cover tx fee, registration fee and provider's stake - uint256 _totalFee = registrationFee.add(_stakeAmount).add(_transactorFee); + // Tokens amount to get from channel to cover tx fee and provider's stake + uint256 _totalFee = _stakeAmount.add(_transactorFee); require(_totalFee <= token.balanceOf(getChannelAddress(_identityHash, _hermesId)), "not enought funds in channel to cover fees"); // Deploy channel contract for given identity (mini proxy which is pointing to implementation) @@ -711,10 +664,6 @@ contract Registry is FundsRecovery { return status == HermesContract.Status.Active; } - function changeRegistrationFee(uint256 _newFee) public onlyOwner { - registrationFee = _newFee; - } - function transferCollectedFeeTo(address _beneficiary) public onlyOwner{ uint256 _collectedFee = token.balanceOf(address(this)); require(_collectedFee > 0, "collected fee cannot be less than zero"); diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 3fa8784..0450d6a 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -13,8 +13,8 @@ const zeroAddress = '0x0000000000000000000000000000000000000000' module.exports = async function (deployer, network, accounts) { // We do have MYSTTv1 deployed on Görli already if (network === 'goerli') { - const originalToken = '0x7753cfAD258eFbC52A9A1452e42fFbce9bE486cb' - await deployer.deploy(MystToken, originalTokens) + const originalToken = '0x8EA3F639e98da04708520C63b34AfBAa1594bC82' + await deployer.deploy(MystToken, originalToken) await deployer.deploy(DEXImplementation) await deployer.deploy(ChannelImplementation) await deployer.deploy(HermesImplementation)