const HomeBridge = artifacts.require('HomeBridgeErcToNative.sol') const EternalStorageProxy = artifacts.require('EternalStorageProxy.sol') const BridgeValidators = artifacts.require('BridgeValidators.sol') const BlockReward = artifacts.require('BlockRewardMock.sol') const OldBlockReward = artifacts.require('OldBlockReward') const RewardableValidators = artifacts.require('RewardableValidators.sol') const FeeManagerErcToNative = artifacts.require('FeeManagerErcToNative.sol') const FeeManagerErcToNativePOSDAO = artifacts.require('FeeManagerErcToNativePOSDAO') const FeeManagerMock = artifacts.require('FeeManagerMock') const { expect } = require('chai') const { ERROR_MSG, ZERO_ADDRESS, toBN } = require('../setup') const { createMessage, sign, ether, expectEventInLogs, createAccounts, deployProxy } = require('../helpers/helpers') const minPerTx = ether('0.01') const requireBlockConfirmations = 8 const gasPrice = web3.utils.toWei('1', 'gwei') const quarterEther = ether('0.25') const oneEther = ether('1') const halfEther = ether('0.5') const foreignDailyLimit = oneEther const foreignMaxPerTx = halfEther const ZERO = toBN(0) const MAX_GAS = 8000000 const MAX_VALIDATORS = 50 const decimalShiftZero = 0 contract('HomeBridge_ERC20_to_Native', async accounts => { let homeContract let validatorContract let blockRewardContract let authorities let owner before(async () => { validatorContract = await deployProxy(BridgeValidators) blockRewardContract = await BlockReward.new() authorities = [accounts[1]] owner = accounts[0] await validatorContract.initialize(1, authorities, owner) }) describe('#initialize', async () => { beforeEach(async () => { homeContract = await deployProxy(HomeBridge) }) it('sets variables', async () => { expect(await homeContract.validatorContract()).to.be.equal(ZERO_ADDRESS) expect(await homeContract.deployedAtBlock()).to.be.bignumber.equal(ZERO) expect(await homeContract.dailyLimit()).to.be.bignumber.equal(ZERO) expect(await homeContract.maxPerTx()).to.be.bignumber.equal(ZERO) expect(await homeContract.decimalShift()).to.be.bignumber.equal(ZERO) expect(await homeContract.isInitialized()).to.be.equal(false) expect(await homeContract.blockRewardContract()).to.be.equal(ZERO_ADDRESS) const { logs } = await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, '9' ).should.be.fulfilled expect(await homeContract.isInitialized()).to.be.equal(true) expect(await homeContract.validatorContract()).to.be.equal(validatorContract.address) expect(await homeContract.deployedAtBlock()).to.be.bignumber.above(ZERO) expect(await homeContract.dailyLimit()).to.be.bignumber.equal('3') expect(await homeContract.maxPerTx()).to.be.bignumber.equal('2') expect(await homeContract.minPerTx()).to.be.bignumber.equal('1') expect(await homeContract.decimalShift()).to.be.bignumber.equal('9') expect(await homeContract.blockRewardContract()).to.be.equal(blockRewardContract.address) expect(await homeContract.gasPrice()).to.be.bignumber.equal(gasPrice) const bridgeMode = '0x18762d46' // 4 bytes of keccak256('erc-to-native-core') expect(await homeContract.getBridgeMode()).to.be.equal(bridgeMode) const { major, minor, patch } = await homeContract.getBridgeInterfacesVersion() expect(major).to.be.bignumber.gte(ZERO) expect(minor).to.be.bignumber.gte(ZERO) expect(patch).to.be.bignumber.gte(ZERO) expectEventInLogs(logs, 'RequiredBlockConfirmationChanged', { requiredBlockConfirmations: toBN(requireBlockConfirmations) }) expectEventInLogs(logs, 'GasPriceChanged', { gasPrice }) expectEventInLogs(logs, 'ExecutionDailyLimitChanged', { newLimit: foreignDailyLimit }) expectEventInLogs(logs, 'DailyLimitChanged', { newLimit: '3' }) }) it('can update block reward contract', async () => { ZERO_ADDRESS.should.be.equal(await homeContract.blockRewardContract()) await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled blockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) const secondBlockRewardContract = await BlockReward.new() await homeContract.setBlockRewardContract(secondBlockRewardContract.address) secondBlockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) const thirdBlockRewardContract = await BlockReward.new() await homeContract .setBlockRewardContract(thirdBlockRewardContract.address, { from: accounts[4] }) .should.be.rejectedWith(ERROR_MSG) secondBlockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) const notAContract = accounts[5] await homeContract.setBlockRewardContract(notAContract).should.be.rejectedWith(ERROR_MSG) secondBlockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) await homeContract.setBlockRewardContract(validatorContract.address).should.be.rejectedWith(ERROR_MSG) secondBlockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) const oldBlockRewardContract = await OldBlockReward.new() await homeContract.setBlockRewardContract(oldBlockRewardContract.address).should.be.fulfilled oldBlockRewardContract.address.should.be.equal(await homeContract.blockRewardContract()) }) it('cant set maxPerTx > dailyLimit', async () => { false.should.be.equal(await homeContract.isInitialized()) await homeContract .initialize( validatorContract.address, ['1', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .initialize( validatorContract.address, ['3', '2', '2'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) false.should.be.equal(await homeContract.isInitialized()) }) it('can be deployed via upgradeToAndCall', async () => { const storageProxy = await EternalStorageProxy.new().should.be.fulfilled const data = homeContract.contract.methods .initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, ['3', '2'], owner, decimalShiftZero ) .encodeABI() const impl = await HomeBridge.new() await storageProxy.upgradeToAndCall('1', impl.address, data).should.be.fulfilled const finalContract = await HomeBridge.at(storageProxy.address) expect(await finalContract.isInitialized()).to.be.equal(true) expect(await finalContract.validatorContract()).to.be.equal(validatorContract.address) expect(await finalContract.dailyLimit()).to.be.bignumber.equal('3') expect(await finalContract.maxPerTx()).to.be.bignumber.equal('2') expect(await finalContract.minPerTx()).to.be.bignumber.equal('1') expect(await finalContract.blockRewardContract()).to.be.equal(blockRewardContract.address) }) it('can be upgraded keeping the state', async () => { const homeOwner = accounts[8] const storageProxy = await EternalStorageProxy.new().should.be.fulfilled const data = homeContract.contract.methods .initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, ['3', '2'], homeOwner, decimalShiftZero ) .encodeABI() const homeContractV1 = await HomeBridge.new() await storageProxy.upgradeToAndCall('1', homeContractV1.address, data).should.be.fulfilled const finalContract = await HomeBridge.at(storageProxy.address) expect(await finalContract.isInitialized()).to.be.equal(true) expect(await finalContract.validatorContract()).to.be.equal(validatorContract.address) expect(await finalContract.dailyLimit()).to.be.bignumber.equal('3') expect(await finalContract.maxPerTx()).to.be.bignumber.equal('2') expect(await finalContract.minPerTx()).to.be.bignumber.equal('1') expect(await finalContract.blockRewardContract()).to.be.equal(blockRewardContract.address) const homeContractV2 = await HomeBridge.new() await storageProxy.upgradeTo('2', homeContractV2.address).should.be.fulfilled const finalContractV2 = await HomeBridge.at(storageProxy.address) expect(await finalContractV2.isInitialized()).to.be.equal(true) expect(await finalContractV2.validatorContract()).to.be.equal(validatorContract.address) expect(await finalContractV2.dailyLimit()).to.be.bignumber.equal('3') expect(await finalContractV2.maxPerTx()).to.be.bignumber.equal('2') expect(await finalContractV2.minPerTx()).to.be.bignumber.equal('1') expect(await finalContractV2.blockRewardContract()).to.be.equal(blockRewardContract.address) }) it('cant initialize with invalid arguments', async () => { false.should.be.equal(await homeContract.isInitialized()) await homeContract .initialize( validatorContract.address, ['3', '2', '1'], gasPrice, 0, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .initialize( owner, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .initialize( ZERO_ADDRESS, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, owner, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [halfEther, oneEther], owner, decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) // not valid decimal shift await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, '100' ).should.be.rejected await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled true.should.be.equal(await homeContract.isInitialized()) }) }) describe('#rewardableInitialize', async () => { let feeManager let homeFee let foreignFee beforeEach(async () => { feeManager = await FeeManagerErcToNative.new() homeContract = await deployProxy(HomeBridge) homeFee = ether('0.001') foreignFee = ether('0.002') }) it('sets variables', async () => { expect(await homeContract.validatorContract()).to.be.equal(ZERO_ADDRESS) expect(await homeContract.deployedAtBlock()).to.be.bignumber.equal(ZERO) expect(await homeContract.dailyLimit()).to.be.bignumber.equal(ZERO) expect(await homeContract.maxPerTx()).to.be.bignumber.equal(ZERO) expect(await homeContract.decimalShift()).to.be.bignumber.equal(ZERO) expect(await homeContract.isInitialized()).to.be.equal(false) expect(await homeContract.blockRewardContract()).to.be.equal(ZERO_ADDRESS) await homeContract.rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], '9' ).should.be.fulfilled expect(await homeContract.isInitialized()).to.be.equal(true) expect(await homeContract.validatorContract()).to.be.equal(validatorContract.address) expect(await homeContract.deployedAtBlock()).to.be.bignumber.above(ZERO) expect(await homeContract.dailyLimit()).to.be.bignumber.equal('3') expect(await homeContract.maxPerTx()).to.be.bignumber.equal('2') expect(await homeContract.minPerTx()).to.be.bignumber.equal('1') expect(await homeContract.decimalShift()).to.be.bignumber.equal('9') expect(await homeContract.blockRewardContract()).to.be.equal(blockRewardContract.address) expect(await homeContract.gasPrice()).to.be.bignumber.equal(gasPrice) const bridgeMode = '0x18762d46' // 4 bytes of keccak256('erc-to-native-core') expect(await homeContract.getBridgeMode()).to.be.equal(bridgeMode) const { major, minor, patch } = await homeContract.getBridgeInterfacesVersion() expect(major).to.be.bignumber.gte(ZERO) expect(minor).to.be.bignumber.gte(ZERO) expect(patch).to.be.bignumber.gte(ZERO) const feeManagerContract = await homeContract.feeManagerContract() feeManagerContract.should.be.equals(feeManager.address) const bridgeHomeFee = await homeContract.getHomeFee() bridgeHomeFee.should.be.bignumber.equal(homeFee) const bridgeForeignFee = await homeContract.getForeignFee() bridgeForeignFee.should.be.bignumber.equal(foreignFee) }) it('cant initialize with invalid arguments', async () => { false.should.be.equal(await homeContract.isInitialized()) await homeContract .rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, 0, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .rewardableInitialize( owner, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .rewardableInitialize( ZERO_ADDRESS, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, owner, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [halfEther, oneEther], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract .rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, ZERO_ADDRESS, [homeFee, foreignFee], decimalShiftZero ) .should.be.rejectedWith(ERROR_MSG) await homeContract.rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ).should.be.fulfilled true.should.be.equal(await homeContract.isInitialized()) }) it('can update fee contract', async () => { await homeContract.rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ).should.be.fulfilled // Given const newFeeManager = await FeeManagerErcToNative.new() // When await homeContract.setFeeManagerContract(newFeeManager.address, { from: owner }).should.be.fulfilled // Then const feeManagerContract = await homeContract.feeManagerContract() feeManagerContract.should.be.equals(newFeeManager.address) }) it('can update fee', async () => { await homeContract.rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ).should.be.fulfilled // Given const newHomeFee = ether('0.1') const newForeignFee = ether('0.2') // When await homeContract.setHomeFee(newHomeFee, { from: owner }).should.be.fulfilled await homeContract.setForeignFee(newForeignFee, { from: owner }).should.be.fulfilled // Then const bridgeHomeFee = await homeContract.getHomeFee() bridgeHomeFee.should.be.bignumber.equal(newHomeFee) const bridgeForeignFee = await homeContract.getForeignFee() bridgeForeignFee.should.be.bignumber.equal(newForeignFee) }) it('fee should be less than 100%', async () => { await homeContract.rewardableInitialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, feeManager.address, [homeFee, foreignFee], decimalShiftZero ).should.be.fulfilled const invalidFee = ether('1') const invalidBigFee = ether('2') await homeContract.setHomeFee(invalidFee, { from: owner }).should.be.rejectedWith(ERROR_MSG) await homeContract.setForeignFee(invalidFee, { from: owner }).should.be.rejectedWith(ERROR_MSG) await homeContract.setHomeFee(invalidBigFee, { from: owner }).should.be.rejectedWith(ERROR_MSG) await homeContract.setForeignFee(invalidBigFee, { from: owner }).should.be.rejectedWith(ERROR_MSG) const newHomeFee = ether('0.99') const newForeignFee = ether('0.99') await homeContract.setHomeFee(newHomeFee, { from: owner }).should.be.fulfilled await homeContract.setForeignFee(newForeignFee, { from: owner }).should.be.fulfilled expect(await homeContract.getHomeFee()).to.be.bignumber.equals(newHomeFee) expect(await homeContract.getForeignFee()).to.be.bignumber.equals(newForeignFee) }) }) describe('#fallback', async () => { beforeEach(async () => { homeContract = await deployProxy(HomeBridge) await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) }) it('should accept native coins', async () => { const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const minted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) minted.should.be.bignumber.equal('10') const { logs } = await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expectEventInLogs(logs, 'UserRequestForSignature', { recipient: accounts[1], value: toBN(1) }) expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('1') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') const homeContractBalance = toBN(await web3.eth.getBalance(homeContract.address)) homeContractBalance.should.be.bignumber.equal(ZERO) }) it('should accumulate burnt coins', async () => { await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('2') await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('3') const homeContractBalance = toBN(await web3.eth.getBalance(homeContract.address)) homeContractBalance.should.be.bignumber.equal(ZERO) }) it('doesnt let you send more than daily limit', async () => { await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('1') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('2') await homeContract.sendTransaction({ from: accounts[1], value: 2 }).should.be.rejectedWith(ERROR_MSG) await homeContract.setDailyLimit(4).should.be.fulfilled await homeContract.sendTransaction({ from: accounts[1], value: 2 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('4') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('4') }) it('doesnt let you send more than max amount per tx', async () => { await blockRewardContract.addMintedTotallyByBridge(200, homeContract.address) await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled await homeContract .sendTransaction({ from: accounts[1], value: 3 }) .should.be.rejectedWith(ERROR_MSG) await homeContract.setMaxPerTx(100).should.be.rejectedWith(ERROR_MSG) await homeContract.setDailyLimit(100).should.be.fulfilled await homeContract.setMaxPerTx(99).should.be.fulfilled // meets max per tx and daily limit await homeContract.sendTransaction({ from: accounts[1], value: 99 }).should.be.fulfilled // above daily limit await homeContract .sendTransaction({ from: accounts[1], value: 1 }) .should.be.rejectedWith(ERROR_MSG) }) it('should not let to deposit less than minPerTx', async () => { const newDailyLimit = 100 const newMaxPerTx = 50 const newMinPerTx = 20 await blockRewardContract.addMintedTotallyByBridge(200, homeContract.address) await homeContract.setDailyLimit(newDailyLimit).should.be.fulfilled await homeContract.setMaxPerTx(newMaxPerTx).should.be.fulfilled await homeContract.setMinPerTx(newMinPerTx).should.be.fulfilled await homeContract.sendTransaction({ from: accounts[1], value: newMinPerTx }).should.be.fulfilled await homeContract .sendTransaction({ from: accounts[1], value: newMinPerTx - 1 }) .should.be.rejectedWith(ERROR_MSG) }) it('should fail if not enough bridged tokens', async () => { const initiallyMinted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) initiallyMinted.should.be.bignumber.equal(ZERO) await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.rejectedWith(ERROR_MSG) await blockRewardContract.addMintedTotallyByBridge(2, homeContract.address) await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.fulfilled await homeContract.sendTransaction({ from: accounts[1], value: 1 }).should.be.rejectedWith(ERROR_MSG) const minted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) const burnt = await homeContract.totalBurntCoins() minted.should.be.bignumber.equal('2') burnt.should.be.bignumber.equal('2') }) }) describe('#relayTokens', () => { const recipient = accounts[7] beforeEach(async () => { homeContract = await deployProxy(HomeBridge) await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) }) it('should accept native coins and alternative receiver', async () => { const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const minted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) minted.should.be.bignumber.equal('10') const { logs } = await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: toBN(1) }) expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('1') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') const homeContractBalance = toBN(await web3.eth.getBalance(homeContract.address)) homeContractBalance.should.be.bignumber.equal(ZERO) }) it('should accumulate burnt coins', async () => { await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('2') await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('3') const homeContractBalance = toBN(await web3.eth.getBalance(homeContract.address)) homeContractBalance.should.be.bignumber.equal(ZERO) }) it('doesnt let you send more than daily limit', async () => { await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('1') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('2') await homeContract.relayTokens(recipient, { from: accounts[1], value: 2 }).should.be.rejectedWith(ERROR_MSG) await homeContract.setDailyLimit(4).should.be.fulfilled await homeContract.relayTokens(recipient, { from: accounts[1], value: 2 }).should.be.fulfilled expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('4') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('4') }) it('doesnt let you send more than max amount per tx', async () => { await blockRewardContract.addMintedTotallyByBridge(200, homeContract.address) await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled await homeContract .relayTokens(recipient, { from: accounts[1], value: 3 }) .should.be.rejectedWith(ERROR_MSG) await homeContract.setMaxPerTx(100).should.be.rejectedWith(ERROR_MSG) await homeContract.setDailyLimit(100).should.be.fulfilled await homeContract.setMaxPerTx(99).should.be.fulfilled // meets max per tx and daily limit await homeContract.relayTokens(recipient, { from: accounts[1], value: 99 }).should.be.fulfilled // above daily limit await homeContract .relayTokens(recipient, { from: accounts[1], value: 1 }) .should.be.rejectedWith(ERROR_MSG) }) it('should not let to deposit less than minPerTx', async () => { const newDailyLimit = 100 const newMaxPerTx = 50 const newMinPerTx = 20 await blockRewardContract.addMintedTotallyByBridge(200, homeContract.address) await homeContract.setDailyLimit(newDailyLimit).should.be.fulfilled await homeContract.setMaxPerTx(newMaxPerTx).should.be.fulfilled await homeContract.setMinPerTx(newMinPerTx).should.be.fulfilled await homeContract.relayTokens(recipient, { from: accounts[1], value: newMinPerTx }).should.be.fulfilled await homeContract .relayTokens(recipient, { from: accounts[1], value: newMinPerTx - 1 }) .should.be.rejectedWith(ERROR_MSG) }) it('should fail if not enough bridged tokens', async () => { const initiallyMinted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) initiallyMinted.should.be.bignumber.equal(ZERO) await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.rejectedWith(ERROR_MSG) await blockRewardContract.addMintedTotallyByBridge(2, homeContract.address) await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.fulfilled await homeContract.relayTokens(recipient, { from: accounts[1], value: 1 }).should.be.rejectedWith(ERROR_MSG) const minted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) const burnt = await homeContract.totalBurntCoins() minted.should.be.bignumber.equal('2') burnt.should.be.bignumber.equal('2') }) }) describe('#setting limits', async () => { let homeContract beforeEach(async () => { homeContract = await deployProxy(HomeBridge) await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) }) it('setMaxPerTx allows to set only to owner and cannot be more than daily limit', async () => { await homeContract.setMaxPerTx(2, { from: authorities[0] }).should.be.rejectedWith(ERROR_MSG) await homeContract.setMaxPerTx(2, { from: owner }).should.be.fulfilled await homeContract.setMaxPerTx(3, { from: owner }).should.be.rejectedWith(ERROR_MSG) const maxPerTx = await homeContract.maxPerTx() maxPerTx.should.be.bignumber.equal(toBN(2)) }) it('setMinPerTx allows to set only to owner and cannot be more than daily limit and should be less than maxPerTx', async () => { await homeContract.setMinPerTx(1, { from: authorities[0] }).should.be.rejectedWith(ERROR_MSG) await homeContract.setMinPerTx(1, { from: owner }).should.be.fulfilled await homeContract.setMinPerTx(2, { from: owner }).should.be.rejectedWith(ERROR_MSG) const minPerTx = await homeContract.minPerTx() minPerTx.should.be.bignumber.equal(toBN(1)) }) it('setMaxPerTx allows to set limit to zero', async () => { await homeContract.setMaxPerTx(0, { from: owner }).should.be.fulfilled const maxPerTx = await homeContract.maxPerTx() maxPerTx.should.be.bignumber.equal(ZERO) }) it('setExecutionMaxPerTx allows to set only to owner and cannot be more than execution daily limit', async () => { const newValue = ether('0.3') const initialExecutionMaxPerTx = await homeContract.executionMaxPerTx() initialExecutionMaxPerTx.should.be.bignumber.not.equal(newValue) await homeContract.setExecutionMaxPerTx(newValue, { from: authorities[0] }).should.be.rejectedWith(ERROR_MSG) await homeContract.setExecutionMaxPerTx(newValue, { from: owner }).should.be.fulfilled await homeContract.setExecutionMaxPerTx(oneEther, { from: owner }).should.be.rejectedWith(ERROR_MSG) const executionMaxPerTx = await homeContract.executionMaxPerTx() executionMaxPerTx.should.be.bignumber.equal(newValue) }) it('executionDailyLimit allows to set only to owner', async () => { const newValue = ether('1.5') const initialExecutionDailyLimit = await homeContract.executionDailyLimit() initialExecutionDailyLimit.should.be.bignumber.not.equal(newValue) await homeContract.setExecutionDailyLimit(newValue, { from: authorities[0] }).should.be.rejectedWith(ERROR_MSG) await homeContract.setExecutionDailyLimit('2', { from: owner }).should.be.rejectedWith(ERROR_MSG) await homeContract.setExecutionDailyLimit(newValue, { from: owner }).should.be.fulfilled expect(await homeContract.executionDailyLimit()).to.be.bignumber.equal(newValue) await homeContract.setExecutionDailyLimit(0, { from: owner }).should.be.fulfilled expect(await homeContract.executionDailyLimit()).to.be.bignumber.equal(ZERO) await homeContract.setExecutionDailyLimit(newValue, { from: owner }).should.be.fulfilled expect(await homeContract.executionDailyLimit()).to.be.bignumber.equal(newValue) }) }) describe('#executeAffirmation', async () => { let homeBridge beforeEach(async () => { homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( validatorContract.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled }) it('should allow validator to executeAffirmation', async () => { const recipient = accounts[5] const value = halfEther const balanceBefore = await web3.eth.getBalance(recipient) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }) expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(toBN(balanceBefore).add(value)) const msgHash = web3.utils.soliditySha3(recipient, value, transactionHash) const senderHash = web3.utils.soliditySha3(authorities[0], msgHash) true.should.be.equal(await homeBridge.affirmationsSigned(senderHash)) }) it('should allow validator to executeAffirmation with zero value', async () => { const recipient = accounts[5] const value = ZERO const balanceBefore = await web3.eth.getBalance(recipient) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }) expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(toBN(balanceBefore).add(value)) const msgHash = web3.utils.soliditySha3(recipient, value, transactionHash) const senderHash = web3.utils.soliditySha3(authorities[0], msgHash) true.should.be.equal(await homeBridge.affirmationsSigned(senderHash)) }) it('test with 2 signatures required', async () => { const validatorContractWith2Signatures = await deployProxy(BridgeValidators) const authoritiesThreeAccs = [accounts[1], accounts[2], accounts[3]] const ownerOfValidators = accounts[0] await validatorContractWith2Signatures.initialize(2, authoritiesThreeAccs, ownerOfValidators) const homeBridgeWithTwoSigs = await deployProxy(HomeBridge) await homeBridgeWithTwoSigs.initialize( validatorContractWith2Signatures.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) const recipient = accounts[5] const value = halfEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const balanceBefore = toBN(await web3.eth.getBalance(recipient)) const msgHash = web3.utils.soliditySha3(recipient, value, transactionHash) const { logs } = await homeBridgeWithTwoSigs.executeAffirmation(recipient, value, transactionHash, { from: authoritiesThreeAccs[0] }).should.be.fulfilled expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) const notProcessed = await homeBridgeWithTwoSigs.numAffirmationsSigned(msgHash) notProcessed.should.be.bignumber.equal('1') await homeBridgeWithTwoSigs .executeAffirmation(recipient, value, transactionHash, { from: authoritiesThreeAccs[0] }) .should.be.rejectedWith(ERROR_MSG) const secondSignature = await homeBridgeWithTwoSigs.executeAffirmation(recipient, value, transactionHash, { from: authoritiesThreeAccs[1] }).should.be.fulfilled const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(balanceBefore.add(value)) expectEventInLogs(secondSignature.logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const senderHash = web3.utils.soliditySha3(authoritiesThreeAccs[0], msgHash) true.should.be.equal(await homeBridgeWithTwoSigs.affirmationsSigned(senderHash)) const senderHash2 = web3.utils.soliditySha3(authoritiesThreeAccs[1], msgHash) true.should.be.equal(await homeBridgeWithTwoSigs.affirmationsSigned(senderHash2)) const markedAsProcessed = await homeBridgeWithTwoSigs.numAffirmationsSigned(msgHash) const processed = toBN(2) .pow(toBN(255)) .add(toBN(2)) markedAsProcessed.should.be.bignumber.equal(processed) }) it('should not allow non-validator to execute affirmation', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' await homeBridge .executeAffirmation(recipient, value, transactionHash, { from: accounts[7] }) .should.be.rejectedWith(ERROR_MSG) }) it('should fail if the block reward contract is not set', async () => { homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( validatorContract.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, ZERO_ADDRESS, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) const recipient = accounts[5] const value = halfEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' await homeBridge .executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }) .should.be.rejectedWith(ERROR_MSG) }) it('works with 5 validators and 3 required signatures', async () => { const recipient = accounts[8] const authoritiesFiveAccs = [accounts[1], accounts[2], accounts[3], accounts[4], accounts[5]] const ownerOfValidators = accounts[0] const validatorContractWith3Signatures = await deployProxy(BridgeValidators) await validatorContractWith3Signatures.initialize(3, authoritiesFiveAccs, ownerOfValidators) const homeBridgeWithThreeSigs = await deployProxy(HomeBridge) await homeBridgeWithThreeSigs.initialize( validatorContractWith3Signatures.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) const value = halfEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridgeWithThreeSigs.executeAffirmation(recipient, value, transactionHash, { from: authoritiesFiveAccs[0] }).should.be.fulfilled expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) await homeBridgeWithThreeSigs.executeAffirmation(recipient, value, transactionHash, { from: authoritiesFiveAccs[1] }).should.be.fulfilled const thirdSignature = await homeBridgeWithThreeSigs.executeAffirmation(recipient, value, transactionHash, { from: authoritiesFiveAccs[2] }).should.be.fulfilled expectEventInLogs(thirdSignature.logs, 'AffirmationCompleted', { recipient, value, transactionHash }) }) it('should not allow execute affirmation over foreign max tx limit', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs, 'AmountLimitExceeded', { recipient, value, transactionHash }) }) it('should fail if txHash already set as above of limits', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs, 'AmountLimitExceeded', { recipient, value, transactionHash }) // should fail for the same message await homeBridge .executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }) .should.be.rejectedWith(ERROR_MSG) // should succeed for different transfer in the same message await homeBridge.executeAffirmation(accounts[6], value, transactionHash, { from: authorities[0] }).should.be .fulfilled }) it('should not allow execute affirmation over daily foreign limit', async () => { await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled const recipient = accounts[5] const value = halfEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const transactionHash2 = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121' const { logs: logs2 } = await homeBridge.executeAffirmation(recipient, value, transactionHash2, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs2, 'SignedForAffirmation', { signer: authorities[0], transactionHash: transactionHash2 }) expectEventInLogs(logs2, 'AffirmationCompleted', { recipient, value, transactionHash: transactionHash2 }) const transactionHash3 = '0x69debd8fd1923c9cb3cd8ef6461e2740b2d037943b941729d5a47671a2bb8712' const { logs: logs3 } = await homeBridge.executeAffirmation(recipient, value, transactionHash3, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs3, 'AmountLimitExceeded', { recipient, value, transactionHash: transactionHash3 }) const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(halfEther) const transactionHash4 = '0xc9ffe298d85ec5c515153608924b7bdcf1835539813dcc82cdbcc071170c3196' const { logs: logs4 } = await homeBridge.executeAffirmation(recipient, value, transactionHash4, { from: authorities[0] }).should.be.fulfilled expectEventInLogs(logs4, 'AmountLimitExceeded', { recipient, value, transactionHash: transactionHash4 }) const newOutOfLimitAmount = await homeBridge.outOfLimitAmount() newOutOfLimitAmount.should.be.bignumber.equal(oneEther) }) }) describe('#submitSignature', async () => { let validatorContractWith2Signatures let authoritiesThreeAccs let ownerOfValidators let homeBridgeWithTwoSigs beforeEach(async () => { validatorContractWith2Signatures = await deployProxy(BridgeValidators) authoritiesThreeAccs = [accounts[1], accounts[2], accounts[3]] ownerOfValidators = accounts[0] await validatorContractWith2Signatures.initialize(2, authoritiesThreeAccs, ownerOfValidators) homeBridgeWithTwoSigs = await deployProxy(HomeBridge) await homeBridgeWithTwoSigs.initialize( validatorContractWith2Signatures.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) }) it('allows a validator to submit a signature', async () => { const recipientAccount = accounts[8] const value = halfEther const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, value, transactionHash, homeBridgeWithTwoSigs.address) const signature = await sign(authoritiesThreeAccs[0], message) const { logs } = await homeBridgeWithTwoSigs.submitSignature(signature, message, { from: authorities[0] }).should.be.fulfilled logs[0].event.should.be.equal('SignedForUserRequest') const { messageHash } = logs[0].args const signatureFromContract = await homeBridgeWithTwoSigs.signature(messageHash, 0) const messageFromContract = await homeBridgeWithTwoSigs.message(messageHash) signature.should.be.equal(signatureFromContract) messageFromContract.should.be.equal(messageFromContract) const hashMsg = web3.utils.soliditySha3(message) const hashSenderMsg = web3.utils.soliditySha3(authorities[0], hashMsg) true.should.be.equal(await homeBridgeWithTwoSigs.messagesSigned(hashSenderMsg)) }) it('when enough requiredSignatures are collected, CollectedSignatures event is emitted', async () => { const recipientAccount = accounts[8] const value = halfEther const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, value, transactionHash, homeBridgeWithTwoSigs.address) const signature = await sign(authoritiesThreeAccs[0], message) const signature2 = await sign(authoritiesThreeAccs[1], message) expect(await validatorContractWith2Signatures.requiredSignatures()).to.be.bignumber.equal('2') await homeBridgeWithTwoSigs.submitSignature(signature, message, { from: authoritiesThreeAccs[0] }).should.be.fulfilled await homeBridgeWithTwoSigs .submitSignature(signature, message, { from: authoritiesThreeAccs[0] }) .should.be.rejectedWith(ERROR_MSG) await homeBridgeWithTwoSigs .submitSignature(signature, message, { from: authoritiesThreeAccs[1] }) .should.be.rejectedWith(ERROR_MSG) const { logs } = await homeBridgeWithTwoSigs.submitSignature(signature2, message, { from: authoritiesThreeAccs[1] }).should.be.fulfilled logs.length.should.be.equal(2) logs[1].event.should.be.equal('CollectedSignatures') logs[1].args.authorityResponsibleForRelay.should.be.equal(authoritiesThreeAccs[1]) }) it('works with 5 validators and 3 required signatures', async () => { const recipientAccount = accounts[8] const authoritiesFiveAccs = [accounts[1], accounts[2], accounts[3], accounts[4], accounts[5]] const validatorContractWith3Signatures = await deployProxy(BridgeValidators) await validatorContractWith3Signatures.initialize(3, authoritiesFiveAccs, ownerOfValidators) const homeBridgeWithThreeSigs = await deployProxy(HomeBridge) await homeBridgeWithThreeSigs.initialize( validatorContractWith3Signatures.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) const value = halfEther const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, value, transactionHash, homeBridgeWithThreeSigs.address) const signature = await sign(authoritiesFiveAccs[0], message) const signature2 = await sign(authoritiesFiveAccs[1], message) const signature3 = await sign(authoritiesFiveAccs[2], message) expect(await validatorContractWith3Signatures.requiredSignatures()).to.be.bignumber.equal('3') await homeBridgeWithThreeSigs.submitSignature(signature, message, { from: authoritiesFiveAccs[0] }).should.be.fulfilled await homeBridgeWithThreeSigs.submitSignature(signature2, message, { from: authoritiesFiveAccs[1] }).should.be.fulfilled const { logs } = await homeBridgeWithThreeSigs.submitSignature(signature3, message, { from: authoritiesFiveAccs[2] }).should.be.fulfilled logs.length.should.be.equal(2) logs[1].event.should.be.equal('CollectedSignatures') logs[1].args.authorityResponsibleForRelay.should.be.equal(authoritiesFiveAccs[2]) }) it('attack when increasing requiredSignatures', async () => { const recipientAccount = accounts[8] const value = halfEther const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, value, transactionHash, homeBridgeWithTwoSigs.address) const signature = await sign(authoritiesThreeAccs[0], message) const signature2 = await sign(authoritiesThreeAccs[1], message) const signature3 = await sign(authoritiesThreeAccs[2], message) expect(await validatorContractWith2Signatures.requiredSignatures()).to.be.bignumber.equal('2') await homeBridgeWithTwoSigs.submitSignature(signature, message, { from: authoritiesThreeAccs[0] }).should.be.fulfilled await homeBridgeWithTwoSigs .submitSignature(signature, message, { from: authoritiesThreeAccs[0] }) .should.be.rejectedWith(ERROR_MSG) await homeBridgeWithTwoSigs .submitSignature(signature, message, { from: authoritiesThreeAccs[1] }) .should.be.rejectedWith(ERROR_MSG) const { logs } = await homeBridgeWithTwoSigs.submitSignature(signature2, message, { from: authoritiesThreeAccs[1] }).should.be.fulfilled logs.length.should.be.equal(2) logs[1].event.should.be.equal('CollectedSignatures') logs[1].args.authorityResponsibleForRelay.should.be.equal(authoritiesThreeAccs[1]) await validatorContractWith2Signatures.setRequiredSignatures(3).should.be.fulfilled expect(await validatorContractWith2Signatures.requiredSignatures()).to.be.bignumber.equal('3') await homeBridgeWithTwoSigs .submitSignature(signature3, message, { from: authoritiesThreeAccs[2] }) .should.be.rejectedWith(ERROR_MSG) }) it('attack when decreasing requiredSignatures', async () => { const recipientAccount = accounts[8] const value = halfEther const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, value, transactionHash, homeBridgeWithTwoSigs.address) const signature = await sign(authoritiesThreeAccs[0], message) const signature2 = await sign(authoritiesThreeAccs[1], message) expect(await validatorContractWith2Signatures.requiredSignatures()).to.be.bignumber.equal('2') await homeBridgeWithTwoSigs.submitSignature(signature, message, { from: authoritiesThreeAccs[0] }).should.be.fulfilled await validatorContractWith2Signatures.setRequiredSignatures(1).should.be.fulfilled expect(await validatorContractWith2Signatures.requiredSignatures()).to.be.bignumber.equal('1') const { logs } = await homeBridgeWithTwoSigs.submitSignature(signature2, message, { from: authoritiesThreeAccs[1] }).should.be.fulfilled logs.length.should.be.equal(2) logs[1].event.should.be.equal('CollectedSignatures') logs[1].args.authorityResponsibleForRelay.should.be.equal(authoritiesThreeAccs[1]) }) }) describe('#requiredMessageLength', async () => { beforeEach(async () => { homeContract = await deployProxy(HomeBridge) }) it('should return the required message length', async () => { expect(await homeContract.requiredMessageLength()).to.be.bignumber.equal('104') }) }) describe('#fixAssetsAboveLimits', async () => { let homeBridge beforeEach(async () => { homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( validatorContract.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ) }) it('Should revert if value to unlock is bigger than max per transaction', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value) await homeBridge.fixAssetsAboveLimits(messageId, true, value).should.be.rejectedWith(ERROR_MSG) await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.fulfilled }) it('Should allow to partially reduce outOfLimitAmount and not emit UserRequestForSignature', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value) const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled logs.length.should.be.equal(1) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: halfEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther) const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be .fulfilled logsSecondTx.length.should.be.equal(1) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: ZERO }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO) }) it('Should allow to partially reduce outOfLimitAmount and emit UserRequestForSignature', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value) const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: halfEther }) expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: halfEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther) const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be .fulfilled logsSecondTx.length.should.be.equal(2) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: ZERO }) expectEventInLogs(logsSecondTx, 'UserRequestForSignature', { recipient, value: halfEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO) }) it('Should revert if try to unlock more than available', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value) const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: halfEther }) expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: halfEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther) const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be .fulfilled logsSecondTx.length.should.be.equal(2) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId, value: quarterEther, remaining: quarterEther }) expectEventInLogs(logsSecondTx, 'UserRequestForSignature', { recipient, value: quarterEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(quarterEther) await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.rejectedWith(ERROR_MSG) const { logs: logsThirdTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be .fulfilled expectEventInLogs(logsThirdTx, 'AssetAboveLimitsFixed', { messageId, value: quarterEther, remaining: ZERO }) expectEventInLogs(logsThirdTx, 'UserRequestForSignature', { recipient, value: quarterEther }) expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO) }) it('Should not be allow to be called by an already fixed txHash', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const transactionHash2 = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121' const { logs: logs1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled const { logs: logs2 } = await homeBridge.executeAffirmation(recipient, value, transactionHash2, { from: authorities[0] }).should.be.fulfilled const messageId1 = logs1[0].args.messageId const messageId2 = logs2[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value.add(value)) await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled const newOutOfLimitAmount = await homeBridge.outOfLimitAmount() newOutOfLimitAmount.should.be.bignumber.equal(value) await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.rejectedWith(ERROR_MSG) await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO) await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.rejectedWith(ERROR_MSG) }) it('Should fail if txHash didnt increase out of limit amount', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const invalidMessageId = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') await homeBridge.fixAssetsAboveLimits(invalidMessageId, true, halfEther).should.be.rejectedWith(ERROR_MSG) }) it('Should fail if not called by proxyOwner', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId await homeBridge .fixAssetsAboveLimits(messageId, true, halfEther, { from: recipient }) .should.be.rejectedWith(ERROR_MSG) await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther, { from: owner }).should.be.fulfilled }) it('Should emit UserRequestForSignature with value reduced by fee', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[5] const value = oneEther const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled expectEventInLogs(affirmationLogs, 'AmountLimitExceeded', { recipient, value, transactionHash }) const messageId = affirmationLogs[0].args.messageId const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled // Then logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId, value: halfEther, remaining: halfEther }) expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: finalValue }) }) it('should not allow to fix assets after they were already processed', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be .fulfilled const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(ZERO) await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.rejectedWith(ERROR_MSG) }) it('should not allow to executeAffirmation after assets were fixed', async () => { const recipient = accounts[5] const value = oneEther const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be.fulfilled affirmationLogs[0].event.should.be.equal('AmountLimitExceeded') const messageId = affirmationLogs[0].args.messageId const outOfLimitAmount = await homeBridge.outOfLimitAmount() outOfLimitAmount.should.be.bignumber.equal(value) await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be .rejected await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be .rejected }) }) describe('#feeManager', async () => { let homeBridge let rewardableValidators const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 beforeEach(async () => { rewardableValidators = await deployProxy(RewardableValidators) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled }) it('should be able to set and get fee manager contract', async () => { // Given const feeManager = await FeeManagerErcToNative.new() // When await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled // Then const feeManagerContract = await homeBridge.feeManagerContract() feeManagerContract.should.be.equals(feeManager.address) }) it('should be able to set and get fees', async () => { // Given // 10% fee const homeFee = ether('0.1') const foreignFee = ether('0.2') const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled // When await homeBridge.setHomeFee(homeFee, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(foreignFee, { from: owner }).should.be.fulfilled // Then const bridgeHomeFee = await homeBridge.getHomeFee() bridgeHomeFee.should.be.bignumber.equal(homeFee) const bridgeForeignFee = await homeBridge.getForeignFee() bridgeForeignFee.should.be.bignumber.equal(foreignFee) }) it('should return zero parameters for zero fee manager', async () => { // When await homeBridge.setFeeManagerContract(ZERO_ADDRESS, { from: owner }).should.be.fulfilled // Then expect(await homeBridge.getFeeManagerMode()).to.be.equal('0x00000000') expect(await homeBridge.getHomeFee()).to.be.bignumber.equal(ZERO) expect(await homeBridge.getForeignFee()).to.be.bignumber.equal(ZERO) }) it('should be able to get fee manager mode', async () => { // Given const feeManager = await FeeManagerErcToNative.new() const bothDirectionsModeHash = '0xd7de965f' // When await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled // Then const feeManagerMode = await homeBridge.getFeeManagerMode() feeManagerMode.should.be.equals(bothDirectionsModeHash) }) it('should be able to get fee manager mode from POSDAO fee manager', async () => { // Given const feeManager = await FeeManagerErcToNativePOSDAO.new() const bothDirectionsModeHash = '0xd7de965f' // When await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled // Then const feeManagerMode = await homeBridge.getFeeManagerMode() feeManagerMode.should.be.equals(bothDirectionsModeHash) }) }) describe('#feeManager_ExecuteAffirmation', async () => { it('should distribute fee to validator', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[5] const value = halfEther const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const balanceBefore = toBN(await web3.eth.getBalance(recipient)) const rewardAddressBalanceBefore = toBN(await web3.eth.getBalance(rewards[0])) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled // Then expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[0], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = toBN(await web3.eth.getBalance(recipient)) const rewardAddressBalanceAfter = toBN(await web3.eth.getBalance(rewards[0])) rewardAddressBalanceAfter.should.be.bignumber.equal(rewardAddressBalanceBefore.add(feeAmount)) balanceAfter.should.be.bignumber.equal(balanceBefore.add(finalValue)) }) it('should distribute fee to 3 validators', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1], accounts[2], accounts[3]] const rewards = [accounts[4], accounts[5], accounts[6]] const requiredSignatures = 2 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) const blockRewardContract = await BlockReward.new() await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[0], value: halfEther }).should.be.fulfilled // Given const initialBlockRewardBalance = toBN(await web3.eth.getBalance(blockRewardContract.address)) initialBlockRewardBalance.should.be.bignumber.equal(halfEther) const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) // totalFee / 3 const feePerValidator = toBN(166666666666666) const feePerValidatorPlusDiff = toBN(166666666666668) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[8] const balanceBefore = toBN(await web3.eth.getBalance(recipient)) const initialBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const initialBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const initialBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs: logsValidator1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[1] }).should.be.fulfilled // Then logsValidator1.length.should.be.equals(1) expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[1], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(balanceBefore.add(finalValue)) const updatedBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const updatedBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const updatedBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) expect( updatedBalanceRewardAddress1.eq(initialBalanceRewardAddress1.add(feePerValidator)) || updatedBalanceRewardAddress1.eq(initialBalanceRewardAddress1.add(feePerValidatorPlusDiff)) ).to.equal(true) expect( updatedBalanceRewardAddress2.eq(initialBalanceRewardAddress2.add(feePerValidator)) || updatedBalanceRewardAddress2.eq(initialBalanceRewardAddress2.add(feePerValidatorPlusDiff)) ).to.equal(true) expect( updatedBalanceRewardAddress3.eq(initialBalanceRewardAddress3.add(feePerValidator)) || updatedBalanceRewardAddress3.eq(initialBalanceRewardAddress3.add(feePerValidatorPlusDiff)) ).to.equal(true) const blockRewardBalance = toBN(await web3.eth.getBalance(blockRewardContract.address)) blockRewardBalance.should.be.bignumber.equal(ZERO) }) it('should distribute fee to 5 validators', async () => { // Initialize const owner = accounts[0] const validators = [accounts[0], accounts[1], accounts[2], accounts[3], accounts[4]] const rewards = [accounts[5], accounts[6], accounts[7], accounts[8], accounts[9]] const requiredSignatures = 5 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[0], value: oneEther }).should.be.fulfilled // Given const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const feePerValidator = feeAmount.div(toBN(5)) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = '0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955' const balanceBefore = toBN(await web3.eth.getBalance(recipient)) const initialBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const initialBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const initialBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const initialBalanceRewardAddress4 = toBN(await web3.eth.getBalance(rewards[3])) const initialBalanceRewardAddress5 = toBN(await web3.eth.getBalance(rewards[4])) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs: logsValidator1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[1] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[2] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[3] }).should.be.fulfilled const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[4] }).should.be.fulfilled // Then logsValidator1.length.should.be.equals(1) expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[4], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(balanceBefore.add(value.sub(feeAmount))) const updatedBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const updatedBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const updatedBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const updatedBalanceRewardAddress4 = toBN(await web3.eth.getBalance(rewards[3])) const updatedBalanceRewardAddress5 = toBN(await web3.eth.getBalance(rewards[4])) updatedBalanceRewardAddress1.should.be.bignumber.equal(initialBalanceRewardAddress1.add(feePerValidator)) updatedBalanceRewardAddress2.should.be.bignumber.equal(initialBalanceRewardAddress2.add(feePerValidator)) updatedBalanceRewardAddress3.should.be.bignumber.equal(initialBalanceRewardAddress3.add(feePerValidator)) updatedBalanceRewardAddress4.should.be.bignumber.equal(initialBalanceRewardAddress4.add(feePerValidator)) updatedBalanceRewardAddress5.should.be.bignumber.equal(initialBalanceRewardAddress5.add(feePerValidator)) }) it('should distribute fee to max allowed number of validators', async () => { // Initialize const owner = accounts[0] const validators = createAccounts(web3, MAX_VALIDATORS) validators[0] = accounts[2] const rewards = createAccounts(web3, MAX_VALIDATORS) const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: owner, value: oneEther }).should.be.fulfilled // Given const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = '0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955' const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { receipt } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }) expect(receipt.gasUsed).to.be.lte(MAX_GAS) }) }) describe('#feeManager_fallback', async () => { let homeBridge let rewardableValidators const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 beforeEach(async () => { rewardableValidators = await deployProxy(RewardableValidators) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) }) it('should subtract fee from value', async () => { // Given // 0.1% fee const value = halfEther const recipient = accounts[8] const fee = 0.001 const feeInWei = ether(fee.toString()) const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled // When const { logs } = await homeBridge.sendTransaction({ from: recipient, value }).should.be.fulfilled // Then expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: finalValue }) const currentDay = await homeBridge.getCurrentDay() value.should.be.bignumber.equal(await homeBridge.totalSpentPerDay(currentDay)) finalValue.should.be.bignumber.equal(await homeBridge.totalBurntCoins()) const homeBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(homeBridgeBalance)).to.be.bignumber.equal(value.sub(finalValue)) }) }) describe('#feeManager_relayTokens', async () => { let homeBridge let rewardableValidators const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 beforeEach(async () => { rewardableValidators = await deployProxy(RewardableValidators) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled homeBridge = await deployProxy(HomeBridge) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) }) it('should subtract fee from value', async () => { // Given // 0.1% fee const value = halfEther const recipient = accounts[8] const sender = accounts[7] const fee = 0.001 const feeInWei = ether(fee.toString()) const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled // When const { logs } = await homeBridge.relayTokens(recipient, { from: sender, value }).should.be.fulfilled // Then expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: finalValue }) const currentDay = await homeBridge.getCurrentDay() value.should.be.bignumber.equal(await homeBridge.totalSpentPerDay(currentDay)) finalValue.should.be.bignumber.equal(await homeBridge.totalBurntCoins()) const homeBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(homeBridgeBalance)).to.be.bignumber.equal(value.sub(finalValue)) }) }) describe('#feeManager_submitSignature', async () => { it('should distribute fee to validator', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[5] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const rewardAddressBalanceBefore = toBN(await web3.eth.getBalance(rewards[0])) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(initialBridgeBalance)).to.be.bignumber.equal(ZERO) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) afterTransferBridgeBalance.should.be.bignumber.equal(feeAmount) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const { logs } = await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') logs[2].event.should.be.equal('FeeDistributedFromSignatures') const finalBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(finalBridgeBalance)).to.be.bignumber.equal(ZERO) const rewardAddressBalanceAfter = await web3.eth.getBalance(rewards[0]) expect(toBN(rewardAddressBalanceAfter)).to.be.bignumber.equal(toBN(rewardAddressBalanceBefore).add(feeAmount)) }) it('should distribute fee to 3 validators', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1], accounts[2], accounts[3]] const rewards = [accounts[4], accounts[5], accounts[6]] const requiredSignatures = 3 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() const feePerValidator = toBN(166666666666666) const feePerValidatorPlusDiff = toBN(166666666666668) await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[7] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) initialBridgeBalance.should.be.bignumber.equal(ZERO) const initialBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const initialBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const initialBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) afterTransferBridgeBalance.should.be.bignumber.equal(feeAmount) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const signature2 = await sign(validators[1], message) const signature3 = await sign(validators[2], message) await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled await homeBridge.submitSignature(signature2, message, { from: validators[1] }).should.be.fulfilled const { logs } = await homeBridge.submitSignature(signature3, message, { from: validators[2] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') logs[2].event.should.be.equal('FeeDistributedFromSignatures') const updatedBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const updatedBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const updatedBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const bridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) bridgeBalance.should.be.bignumber.equal(ZERO) expect( updatedBalanceRewardAddress1.eq(initialBalanceRewardAddress1.add(feePerValidator)) || updatedBalanceRewardAddress1.eq(initialBalanceRewardAddress1.add(feePerValidatorPlusDiff)) ).to.equal(true) expect( updatedBalanceRewardAddress2.eq(initialBalanceRewardAddress2.add(feePerValidator)) || updatedBalanceRewardAddress2.eq(initialBalanceRewardAddress2.add(feePerValidatorPlusDiff)) ).to.equal(true) expect( updatedBalanceRewardAddress3.eq(initialBalanceRewardAddress3.add(feePerValidator)) || updatedBalanceRewardAddress3.eq(initialBalanceRewardAddress3.add(feePerValidatorPlusDiff)) ).to.equal(true) }) it('should distribute fee to 5 validators', async () => { // Initialize const owner = accounts[0] const validators = [accounts[0], accounts[1], accounts[2], accounts[3], accounts[4]] const rewards = [accounts[5], accounts[6], accounts[7], accounts[8], accounts[9]] const requiredSignatures = 5 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[0] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const feePerValidator = feeAmount.div(toBN(5)) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) initialBridgeBalance.should.be.bignumber.equal(ZERO) const initialBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const initialBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const initialBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const initialBalanceRewardAddress4 = toBN(await web3.eth.getBalance(rewards[3])) const initialBalanceRewardAddress5 = toBN(await web3.eth.getBalance(rewards[4])) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = toBN(await web3.eth.getBalance(homeBridge.address)) afterTransferBridgeBalance.should.be.bignumber.equal(feeAmount) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const signature2 = await sign(validators[1], message) const signature3 = await sign(validators[2], message) const signature4 = await sign(validators[3], message) const signature5 = await sign(validators[4], message) await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled await homeBridge.submitSignature(signature2, message, { from: validators[1] }).should.be.fulfilled await homeBridge.submitSignature(signature3, message, { from: validators[2] }).should.be.fulfilled await homeBridge.submitSignature(signature4, message, { from: validators[3] }).should.be.fulfilled const { logs } = await homeBridge.submitSignature(signature5, message, { from: validators[4] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') logs[2].event.should.be.equal('FeeDistributedFromSignatures') const updatedBalanceRewardAddress1 = toBN(await web3.eth.getBalance(rewards[0])) const updatedBalanceRewardAddress2 = toBN(await web3.eth.getBalance(rewards[1])) const updatedBalanceRewardAddress3 = toBN(await web3.eth.getBalance(rewards[2])) const updatedBalanceRewardAddress4 = toBN(await web3.eth.getBalance(rewards[3])) const updatedBalanceRewardAddress5 = toBN(await web3.eth.getBalance(rewards[4])) updatedBalanceRewardAddress1.should.be.bignumber.equal(initialBalanceRewardAddress1.add(feePerValidator)) updatedBalanceRewardAddress2.should.be.bignumber.equal(initialBalanceRewardAddress2.add(feePerValidator)) updatedBalanceRewardAddress3.should.be.bignumber.equal(initialBalanceRewardAddress3.add(feePerValidator)) updatedBalanceRewardAddress4.should.be.bignumber.equal(initialBalanceRewardAddress4.add(feePerValidator)) updatedBalanceRewardAddress5.should.be.bignumber.equal(initialBalanceRewardAddress5.add(feePerValidator)) }) it('should distribute fee to max allowed number of validators', async () => { // Initialize const owner = accounts[0] const validators = createAccounts(web3, MAX_VALIDATORS) validators[0] = accounts[2] const rewards = createAccounts(web3, MAX_VALIDATORS) const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNative.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[0] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const { receipt } = await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled expect(receipt.gasUsed).to.be.lte(MAX_GAS) }) }) describe('#FeeManager_random', async () => { it('should return value between 0 and 3', async () => { // Given const feeManager = await FeeManagerMock.new() for (let i = 0; i < 10; i++) { // send Tx to generate new blocks await feeManager.setHomeFee(0).should.be.fulfilled // When const result = await feeManager.randomTest(3) // Then expect(result).to.be.bignumber.gte(ZERO) expect(result).to.be.bignumber.lt('3') } }) }) describe('#feeManager_ExecuteAffirmation_POSDAO', async () => { it('should distribute fee to validator', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[5] const value = halfEther const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const balanceBefore = await web3.eth.getBalance(recipient) const rewardAddressBalanceBefore = await web3.eth.getBalance(rewards[0]) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled // Then expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[0], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = await web3.eth.getBalance(recipient) const rewardAddressBalanceAfter = await web3.eth.getBalance(rewards[0]) expect(toBN(rewardAddressBalanceAfter)).to.be.bignumber.equal(toBN(rewardAddressBalanceBefore).add(feeAmount)) expect(toBN(balanceAfter)).to.be.bignumber.equal(toBN(balanceBefore).add(finalValue)) const feeAmountBlockReward = await blockRewardContract.feeAmount() feeAmountBlockReward.should.be.bignumber.equal(feeAmount) }) it('should distribute fee to 3 validators', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1], accounts[2], accounts[3]] const rewards = [accounts[4], accounts[5], accounts[6]] const requiredSignatures = 2 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) const blockRewardContract = await BlockReward.new() await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[0], value: halfEther }).should.be.fulfilled // Given const initialBlockRewardBalance = await web3.eth.getBalance(blockRewardContract.address) expect(toBN(initialBlockRewardBalance)).to.be.bignumber.equal(halfEther) const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) // totalFee / 3 const feePerValidator = toBN(166666666666666) const feePerValidatorPlusDiff = toBN(166666666666668) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[8] const balanceBefore = await web3.eth.getBalance(recipient) const initialBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const initialBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const initialBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs: logsValidator1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[1] }).should.be.fulfilled // Then logsValidator1.length.should.be.equals(1) expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[1], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = await web3.eth.getBalance(recipient) expect(toBN(balanceAfter)).to.be.bignumber.equal(toBN(balanceBefore).add(finalValue)) const updatedBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const updatedBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const updatedBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) expect( toBN(updatedBalanceRewardAddress1).eq(toBN(initialBalanceRewardAddress1).add(feePerValidator)) || toBN(updatedBalanceRewardAddress1).eq(toBN(initialBalanceRewardAddress1).add(feePerValidatorPlusDiff)) ).to.equal(true) expect( toBN(updatedBalanceRewardAddress2).eq(toBN(initialBalanceRewardAddress2).add(feePerValidator)) || toBN(updatedBalanceRewardAddress2).eq(toBN(initialBalanceRewardAddress2).add(feePerValidatorPlusDiff)) ).to.equal(true) expect( toBN(updatedBalanceRewardAddress3).eq(toBN(initialBalanceRewardAddress3).add(feePerValidator)) || toBN(updatedBalanceRewardAddress3).eq(toBN(initialBalanceRewardAddress3).add(feePerValidatorPlusDiff)) ).to.equal(true) const feeAmountBlockReward = await blockRewardContract.feeAmount() expect(toBN(feeAmountBlockReward)).to.be.bignumber.equal(feeAmount) const blockRewardBalance = await web3.eth.getBalance(blockRewardContract.address) expect(toBN(blockRewardBalance)).to.be.bignumber.equal(ZERO) }) it('should distribute fee to 5 validators', async () => { // Initialize const owner = accounts[0] const validators = [accounts[0], accounts[1], accounts[2], accounts[3], accounts[4]] const rewards = [accounts[5], accounts[6], accounts[7], accounts[8], accounts[9]] const requiredSignatures = 5 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[0], value: oneEther }).should.be.fulfilled // Given const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const feePerValidator = feeAmount.div(toBN(5)) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = '0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955' const balanceBefore = await web3.eth.getBalance(recipient) const initialBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const initialBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const initialBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const initialBalanceRewardAddress4 = await web3.eth.getBalance(rewards[3]) const initialBalanceRewardAddress5 = await web3.eth.getBalance(rewards[4]) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { logs: logsValidator1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[1] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[2] }).should.be.fulfilled await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[3] }).should.be.fulfilled const { logs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[4] }).should.be.fulfilled // Then logsValidator1.length.should.be.equals(1) expectEventInLogs(logs, 'SignedForAffirmation', { signer: validators[4], transactionHash }) expectEventInLogs(logs, 'FeeDistributedFromAffirmation', { feeAmount, transactionHash }) expectEventInLogs(logs, 'AffirmationCompleted', { recipient, value, transactionHash }) const balanceAfter = await web3.eth.getBalance(recipient) expect(toBN(balanceAfter)).to.be.bignumber.equal(toBN(balanceBefore).add(value.sub(feeAmount))) const updatedBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const updatedBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const updatedBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const updatedBalanceRewardAddress4 = await web3.eth.getBalance(rewards[3]) const updatedBalanceRewardAddress5 = await web3.eth.getBalance(rewards[4]) expect(toBN(updatedBalanceRewardAddress1)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress1).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress2)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress2).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress3)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress3).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress4)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress4).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress5)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress5).add(feePerValidator) ) const feeAmountBlockReward = await blockRewardContract.feeAmount() expect(toBN(feeAmountBlockReward)).to.be.bignumber.equal(feeAmount) }) it('should distribute fee to max allowed number of validators', async () => { // Initialize const owner = accounts[0] const validators = createAccounts(web3, MAX_VALIDATORS) validators[0] = accounts[2] const rewards = createAccounts(web3, MAX_VALIDATORS) const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.sendTransaction({ from: accounts[0], value: oneEther }).should.be.fulfilled // Given const value = halfEther // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setForeignFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = '0xf4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955' const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When const { receipt } = await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: validators[0] }).should.be.fulfilled expect(receipt.gasUsed).to.be.lte(MAX_GAS) }) }) describe('#feeManager_fallback_POSDAO', async () => { let homeBridge let rewardableValidators const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 beforeEach(async () => { rewardableValidators = await deployProxy(RewardableValidators) homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) }) it('should subtract fee from value', async () => { // Given // 0.1% fee const value = halfEther const recipient = accounts[8] const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled // When const { logs } = await homeBridge.sendTransaction({ from: recipient, value }).should.be.fulfilled // Then const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: finalValue }) const currentDay = await homeBridge.getCurrentDay() value.should.be.bignumber.equal(await homeBridge.totalSpentPerDay(currentDay)) value.should.be.bignumber.equal(await homeBridge.totalBurntCoins()) const homeBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(homeBridgeBalance)).to.be.bignumber.equal(ZERO) }) }) describe('#feeManager_relayTokens_POSDAO', async () => { let homeBridge let rewardableValidators const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 beforeEach(async () => { rewardableValidators = await deployProxy(RewardableValidators) homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) }) it('should subtract fee from value', async () => { // Given // 0.1% fee const value = halfEther const recipient = accounts[8] const sender = accounts[7] const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled // When const { logs } = await homeBridge.relayTokens(recipient, { from: sender, value }).should.be.fulfilled // Then const valueCalc = 0.5 * (1 - fee) const finalValue = ether(valueCalc.toString()) expectEventInLogs(logs, 'UserRequestForSignature', { recipient, value: finalValue }) const currentDay = await homeBridge.getCurrentDay() value.should.be.bignumber.equal(await homeBridge.totalSpentPerDay(currentDay)) value.should.be.bignumber.equal(await homeBridge.totalBurntCoins()) const homeBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(homeBridgeBalance)).to.be.bignumber.equal(ZERO) }) }) describe('#feeManager_submitSignature_POSDAO', async () => { it('should distribute fee to validator', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1]] const rewards = [accounts[2]] const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[5] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const rewardAddressBalanceBefore = await web3.eth.getBalance(rewards[0]) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(initialBridgeBalance)).to.be.bignumber.equal(ZERO) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(afterTransferBridgeBalance)).to.be.bignumber.equal(ZERO) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const { logs } = await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') expectEventInLogs(logs, 'FeeDistributedFromSignatures', { feeAmount, transactionHash }) const finalBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(finalBridgeBalance)).to.be.bignumber.equal(ZERO) const rewardAddressBalanceAfter = await web3.eth.getBalance(rewards[0]) expect(toBN(rewardAddressBalanceAfter)).to.be.bignumber.equal(toBN(rewardAddressBalanceBefore).add(feeAmount)) const feeAmountBlockReward = await blockRewardContract.feeAmount() expect(toBN(feeAmountBlockReward)).to.be.bignumber.equal(feeAmount) }) it('should distribute fee to 3 validators', async () => { // Initialize const owner = accounts[9] const validators = [accounts[1], accounts[2], accounts[3]] const rewards = [accounts[4], accounts[5], accounts[6]] const requiredSignatures = 3 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() const feePerValidator = toBN(166666666666666) const feePerValidatorPlusDiff = toBN(166666666666668) await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[7] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(initialBridgeBalance)).to.be.bignumber.equal(ZERO) const initialBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const initialBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const initialBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(afterTransferBridgeBalance)).to.be.bignumber.equal(ZERO) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const signature2 = await sign(validators[1], message) const signature3 = await sign(validators[2], message) await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled await homeBridge.submitSignature(signature2, message, { from: validators[1] }).should.be.fulfilled const { logs } = await homeBridge.submitSignature(signature3, message, { from: validators[2] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') expectEventInLogs(logs, 'FeeDistributedFromSignatures', { feeAmount, transactionHash }) const updatedBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const updatedBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const updatedBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const bridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(bridgeBalance)).to.be.bignumber.equal(ZERO) expect( toBN(updatedBalanceRewardAddress1).eq(toBN(initialBalanceRewardAddress1).add(feePerValidator)) || toBN(updatedBalanceRewardAddress1).eq(toBN(initialBalanceRewardAddress1).add(feePerValidatorPlusDiff)) ).to.equal(true) expect( toBN(updatedBalanceRewardAddress2).eq(toBN(initialBalanceRewardAddress2).add(feePerValidator)) || toBN(updatedBalanceRewardAddress2).eq(toBN(initialBalanceRewardAddress2).add(feePerValidatorPlusDiff)) ).to.equal(true) expect( toBN(updatedBalanceRewardAddress3).eq(toBN(initialBalanceRewardAddress3).add(feePerValidator)) || toBN(updatedBalanceRewardAddress3).eq(toBN(initialBalanceRewardAddress3).add(feePerValidatorPlusDiff)) ).to.equal(true) const feeAmountBlockReward = await blockRewardContract.feeAmount() expect(toBN(feeAmountBlockReward)).to.be.bignumber.equal(feeAmount) }) it('should distribute fee to 5 validators', async () => { // Initialize const owner = accounts[0] const validators = [accounts[0], accounts[1], accounts[2], accounts[3], accounts[4]] const rewards = [accounts[5], accounts[6], accounts[7], accounts[8], accounts[9]] const requiredSignatures = 5 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[0] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const feeAmountCalc = 0.5 * fee const feeAmount = ether(feeAmountCalc.toString()) const feePerValidator = feeAmount.div(toBN(5)) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const initialBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(initialBridgeBalance)).to.be.bignumber.equal(ZERO) const initialBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const initialBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const initialBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const initialBalanceRewardAddress4 = await web3.eth.getBalance(rewards[3]) const initialBalanceRewardAddress5 = await web3.eth.getBalance(rewards[4]) // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const afterTransferBridgeBalance = await web3.eth.getBalance(homeBridge.address) expect(toBN(afterTransferBridgeBalance)).to.be.bignumber.equal(ZERO) const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const signature2 = await sign(validators[1], message) const signature3 = await sign(validators[2], message) const signature4 = await sign(validators[3], message) const signature5 = await sign(validators[4], message) await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled await homeBridge.submitSignature(signature2, message, { from: validators[1] }).should.be.fulfilled await homeBridge.submitSignature(signature3, message, { from: validators[2] }).should.be.fulfilled await homeBridge.submitSignature(signature4, message, { from: validators[3] }).should.be.fulfilled const { logs } = await homeBridge.submitSignature(signature5, message, { from: validators[4] }).should.be.fulfilled // Then logs.length.should.be.equal(3) logs[1].event.should.be.equal('CollectedSignatures') expectEventInLogs(logs, 'FeeDistributedFromSignatures', { feeAmount, transactionHash }) const updatedBalanceRewardAddress1 = await web3.eth.getBalance(rewards[0]) const updatedBalanceRewardAddress2 = await web3.eth.getBalance(rewards[1]) const updatedBalanceRewardAddress3 = await web3.eth.getBalance(rewards[2]) const updatedBalanceRewardAddress4 = await web3.eth.getBalance(rewards[3]) const updatedBalanceRewardAddress5 = await web3.eth.getBalance(rewards[4]) expect(toBN(updatedBalanceRewardAddress1)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress1).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress2)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress2).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress3)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress3).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress4)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress4).add(feePerValidator) ) expect(toBN(updatedBalanceRewardAddress5)).to.be.bignumber.equal( toBN(initialBalanceRewardAddress5).add(feePerValidator) ) const feeAmountBlockReward = await blockRewardContract.feeAmount() feeAmountBlockReward.should.be.bignumber.equal(feeAmount) }) it('should distribute fee to max allowed number of validators', async () => { // Initialize const owner = accounts[0] const validators = createAccounts(web3, MAX_VALIDATORS) validators[0] = accounts[2] const rewards = createAccounts(web3, MAX_VALIDATORS) const requiredSignatures = 1 const rewardableValidators = await deployProxy(RewardableValidators) const homeBridge = await deployProxy(HomeBridge) await rewardableValidators.initialize(requiredSignatures, validators, rewards, owner).should.be.fulfilled await blockRewardContract.setValidatorsRewards(rewards) await homeBridge.initialize( rewardableValidators.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShiftZero ).should.be.fulfilled await blockRewardContract.addMintedTotallyByBridge(oneEther, homeBridge.address) // Given // 0.1% fee const fee = 0.001 const feeInWei = ether(fee.toString()) const feeManager = await FeeManagerErcToNativePOSDAO.new() await homeBridge.setFeeManagerContract(feeManager.address, { from: owner }).should.be.fulfilled await homeBridge.setHomeFee(feeInWei, { from: owner }).should.be.fulfilled const recipient = accounts[0] const initialValue = halfEther const valueCalc = 0.5 * (1 - fee) const value = ether(valueCalc.toString()) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' // When await homeBridge.sendTransaction({ from: recipient, value: initialValue }).should.be.fulfilled const message = createMessage(recipient, value, transactionHash, homeBridge.address) const signature = await sign(validators[0], message) const { receipt } = await homeBridge.submitSignature(signature, message, { from: validators[0] }).should.be.fulfilled expect(receipt.gasUsed).to.be.lte(MAX_GAS) }) }) describe('#decimals Shift', async () => { for (const decimalShift of [2, -1]) { it(`Foreign to Home: test with 2 signatures required and decimal shift ${decimalShift}`, async () => { await blockRewardContract.sendTransaction({ from: accounts[2], value: oneEther }).should.be.fulfilled const recipient = accounts[5] const valueOnHome = halfEther const valueOnForeign = toBN(valueOnHome / 10 ** decimalShift) const validatorContractWith2Signatures = await deployProxy(BridgeValidators) const authoritiesThreeAccs = [accounts[1], accounts[2], accounts[3]] const ownerOfValidators = accounts[0] await validatorContractWith2Signatures.initialize(2, authoritiesThreeAccs, ownerOfValidators) const homeBridgeWithTwoSigs = await deployProxy(HomeBridge) const currentDay = await homeBridgeWithTwoSigs.getCurrentDay() await homeBridgeWithTwoSigs.initialize( validatorContractWith2Signatures.address, [oneEther, halfEther, minPerTx], gasPrice, requireBlockConfirmations, blockRewardContract.address, [ether('100'), ether('10')], owner, decimalShift ) const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415' const balanceBefore = toBN(await web3.eth.getBalance(recipient)) const totalExecutedPerDayBefore = await homeBridgeWithTwoSigs.totalExecutedPerDay(currentDay) const msgHash = web3.utils.soliditySha3(recipient, valueOnForeign, transactionHash) const { logs } = await homeBridgeWithTwoSigs.executeAffirmation(recipient, valueOnForeign, transactionHash, { from: authoritiesThreeAccs[0] }).should.be.fulfilled expectEventInLogs(logs, 'SignedForAffirmation', { signer: authorities[0], transactionHash }) const notProcessed = await homeBridgeWithTwoSigs.numAffirmationsSigned(msgHash) notProcessed.should.be.bignumber.equal('1') await homeBridgeWithTwoSigs .executeAffirmation(recipient, valueOnForeign, transactionHash, { from: authoritiesThreeAccs[0] }) .should.be.rejectedWith(ERROR_MSG) const secondSignature = await homeBridgeWithTwoSigs.executeAffirmation( recipient, valueOnForeign, transactionHash, { from: authoritiesThreeAccs[1] } ).should.be.fulfilled const balanceAfter = toBN(await web3.eth.getBalance(recipient)) balanceAfter.should.be.bignumber.equal(balanceBefore.add(valueOnHome)) const totalExecutedPerDayAfter = await homeBridgeWithTwoSigs.totalExecutedPerDay(currentDay) totalExecutedPerDayAfter.should.be.bignumber.equal(totalExecutedPerDayBefore.add(valueOnForeign)) expectEventInLogs(secondSignature.logs, 'AffirmationCompleted', { recipient, value: valueOnForeign, transactionHash }) const senderHash = web3.utils.soliditySha3(authoritiesThreeAccs[0], msgHash) true.should.be.equal(await homeBridgeWithTwoSigs.affirmationsSigned(senderHash)) const senderHash2 = web3.utils.soliditySha3(authoritiesThreeAccs[1], msgHash) true.should.be.equal(await homeBridgeWithTwoSigs.affirmationsSigned(senderHash2)) const markedAsProcessed = await homeBridgeWithTwoSigs.numAffirmationsSigned(msgHash) const processed = toBN(2) .pow(toBN(255)) .add(toBN(2)) markedAsProcessed.should.be.bignumber.equal(processed) }) it(`Home to Foreign: test decimal shift ${decimalShift}, no impact on UserRequestForSignature value`, async () => { homeContract = await deployProxy(HomeBridge) await homeContract.initialize( validatorContract.address, ['3', '2', '1'], gasPrice, requireBlockConfirmations, blockRewardContract.address, [foreignDailyLimit, foreignMaxPerTx], owner, decimalShift ) const currentDay = await homeContract.getCurrentDay() expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal(ZERO) await blockRewardContract.addMintedTotallyByBridge(10, homeContract.address) const minted = await blockRewardContract.mintedTotallyByBridge(homeContract.address) minted.should.be.bignumber.equal('10') const recipientAccount = accounts[1] const { logs } = await homeContract.sendTransaction({ from: recipientAccount, value: 1 }).should.be.fulfilled expectEventInLogs(logs, 'UserRequestForSignature', { recipient: recipientAccount, value: toBN(1) }) expect(await homeContract.totalSpentPerDay(currentDay)).to.be.bignumber.equal('1') expect(await homeContract.totalBurntCoins()).to.be.bignumber.equal('1') const homeContractBalance = toBN(await web3.eth.getBalance(homeContract.address)) homeContractBalance.should.be.bignumber.equal(ZERO) const transactionHash = '0x1045bfe274b88120a6b1e5d01b5ec00ab5d01098346e90e7c7a3c9b8f0181c80' const message = createMessage(recipientAccount, 1, transactionHash, homeContract.address) const signature = await sign(authorities[0], message) expect(await validatorContract.requiredSignatures()).to.be.bignumber.equal('1') const { logs: logsSubmitSignature } = await homeContract.submitSignature(signature, message, { from: authorities[0] }).should.be.fulfilled logsSubmitSignature.length.should.be.equal(2) logsSubmitSignature[1].event.should.be.equal('CollectedSignatures') }) } }) })