Separate XDaiForeignBridge contract with compound and GSN support (#626)
This commit is contained in:
parent
4b0eb1e2fc
commit
7579b5249e
|
@ -1,8 +1,8 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol";
|
||||
import "../upgradeable_contracts/erc20_to_native/XDaiForeignBridge.sol";
|
||||
|
||||
contract ForeignBridgeErcToNativeMock is ForeignBridgeErcToNative {
|
||||
contract XDaiForeignBridgeMock is XDaiForeignBridge {
|
||||
/**
|
||||
* @dev Tells the address of the DAI token in the Ganache Testchain.
|
||||
*/
|
|
@ -59,12 +59,9 @@ contract GSNForeignERC20Bridge is BasicForeignBridge, ERC20Bridge, BaseRelayReci
|
|||
function onExecuteMessageGSN(address recipient, uint256 amount, uint256 fee) internal returns (bool) {
|
||||
addTotalExecutedPerDay(getCurrentDay(), amount);
|
||||
// Send maxTokensFee to paymaster
|
||||
uint256 unshiftMaxFee = _unshiftValue(fee);
|
||||
bool first = erc20token().transfer(addressStorage[PAYMASTER], unshiftMaxFee);
|
||||
|
||||
// Send rest of tokens to user
|
||||
uint256 unshiftLeft = _unshiftValue(amount - fee);
|
||||
bool second = erc20token().transfer(recipient, unshiftLeft);
|
||||
ERC20 token = erc20token();
|
||||
bool first = token.transfer(addressStorage[PAYMASTER], fee);
|
||||
bool second = token.transfer(recipient, amount - fee);
|
||||
|
||||
return first && second;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../BasicForeignBridge.sol";
|
||||
import "../ERC20Bridge.sol";
|
||||
import "../OtherSideBridgeStorage.sol";
|
||||
import "./CompoundConnector.sol";
|
||||
import "../GSNForeignERC20Bridge.sol";
|
||||
|
||||
contract ForeignBridgeErcToNative is
|
||||
BasicForeignBridge,
|
||||
ERC20Bridge,
|
||||
OtherSideBridgeStorage,
|
||||
CompoundConnector,
|
||||
GSNForeignERC20Bridge
|
||||
{
|
||||
contract ForeignBridgeErcToNative is ERC20Bridge, OtherSideBridgeStorage {
|
||||
function initialize(
|
||||
address _validatorContract,
|
||||
address _erc20token,
|
||||
|
@ -46,26 +37,6 @@ contract ForeignBridgeErcToNative is
|
|||
return 0x18762d46; // bytes4(keccak256(abi.encodePacked("erc-to-native-core")))
|
||||
}
|
||||
|
||||
function upgradeTo530(address _interestReceiver) external {
|
||||
require(msg.sender == address(this));
|
||||
|
||||
address dai = address(daiToken());
|
||||
address comp = address(compToken());
|
||||
_setInterestEnabled(dai, true);
|
||||
_setMinCashThreshold(dai, 1000000 ether);
|
||||
_setMinInterestPaid(dai, 1000 ether);
|
||||
_setInterestReceiver(dai, _interestReceiver);
|
||||
|
||||
_setMinInterestPaid(comp, 1 ether);
|
||||
_setInterestReceiver(comp, _interestReceiver);
|
||||
|
||||
invest(dai);
|
||||
}
|
||||
|
||||
function investDai() external {
|
||||
invest(address(daiToken()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraws the erc20 tokens or native coins from this contract.
|
||||
* @param _token address of the claimed token or address(0) for native coins.
|
||||
|
@ -73,51 +44,17 @@ contract ForeignBridgeErcToNative is
|
|||
*/
|
||||
function claimTokens(address _token, address _to) external onlyIfUpgradeabilityOwner {
|
||||
// Since bridged tokens are locked at this contract, it is not allowed to claim them with the use of claimTokens function
|
||||
address bridgedToken = address(erc20token());
|
||||
require(_token != address(bridgedToken));
|
||||
require(_token != address(cDaiToken()) || !isInterestEnabled(bridgedToken));
|
||||
require(_token != address(compToken()) || !isInterestEnabled(bridgedToken));
|
||||
require(_token != address(erc20token()));
|
||||
claimValues(_token, _to);
|
||||
}
|
||||
|
||||
function onExecuteMessageGSN(address recipient, uint256 amount, uint256 fee) internal returns (bool) {
|
||||
addTotalExecutedPerDay(getCurrentDay(), amount);
|
||||
uint256 unshiftMaxFee = _unshiftValue(fee);
|
||||
uint256 unshiftLeft = _unshiftValue(amount - fee);
|
||||
|
||||
ERC20 token = erc20token();
|
||||
ensureEnoughTokens(token, unshiftMaxFee + unshiftLeft);
|
||||
|
||||
// Send maxTokensFee to paymaster
|
||||
bool first = token.transfer(addressStorage[PAYMASTER], unshiftMaxFee);
|
||||
|
||||
// Send rest of tokens to user
|
||||
bool second = token.transfer(recipient, unshiftLeft);
|
||||
|
||||
return first && second;
|
||||
}
|
||||
|
||||
function onExecuteMessage(
|
||||
address _recipient,
|
||||
uint256 _amount,
|
||||
bytes32 /*_txHash*/
|
||||
) internal returns (bool) {
|
||||
addTotalExecutedPerDay(getCurrentDay(), _amount);
|
||||
uint256 amount = _unshiftValue(_amount);
|
||||
|
||||
ERC20 token = erc20token();
|
||||
ensureEnoughTokens(token, amount);
|
||||
|
||||
return token.transfer(_recipient, amount);
|
||||
}
|
||||
|
||||
function ensureEnoughTokens(ERC20 token, uint256 amount) internal {
|
||||
uint256 currentBalance = token.balanceOf(address(this));
|
||||
|
||||
if (currentBalance < amount) {
|
||||
uint256 withdrawAmount = (amount - currentBalance).add(minCashThreshold(address(token)));
|
||||
_withdraw(address(token), withdrawAmount);
|
||||
}
|
||||
return erc20token().transfer(_recipient, _unshiftValue(_amount));
|
||||
}
|
||||
|
||||
function onFailedMessage(address, uint256, bytes32) internal {
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "./ForeignBridgeErcToNative.sol";
|
||||
import "./CompoundConnector.sol";
|
||||
import "../GSNForeignERC20Bridge.sol";
|
||||
|
||||
contract XDaiForeignBridge is ForeignBridgeErcToNative, CompoundConnector, GSNForeignERC20Bridge {
|
||||
function initialize(
|
||||
address _validatorContract,
|
||||
address _erc20token,
|
||||
uint256 _requiredBlockConfirmations,
|
||||
uint256 _gasPrice,
|
||||
uint256[3] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
|
||||
uint256[2] _homeDailyLimitHomeMaxPerTxArray, //[ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
|
||||
address _owner,
|
||||
int256 _decimalShift,
|
||||
address _bridgeOnOtherSide
|
||||
) external onlyRelevantSender returns (bool) {
|
||||
require(!isInitialized());
|
||||
require(AddressUtils.isContract(_validatorContract));
|
||||
require(_erc20token == address(daiToken()));
|
||||
require(_decimalShift == 0);
|
||||
|
||||
addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
|
||||
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
|
||||
_setRequiredBlockConfirmations(_requiredBlockConfirmations);
|
||||
_setGasPrice(_gasPrice);
|
||||
_setLimits(_dailyLimitMaxPerTxMinPerTxArray);
|
||||
_setExecutionLimits(_homeDailyLimitHomeMaxPerTxArray);
|
||||
_setOwner(_owner);
|
||||
_setBridgeContractOnOtherSide(_bridgeOnOtherSide);
|
||||
setInitialize();
|
||||
|
||||
return isInitialized();
|
||||
}
|
||||
|
||||
function erc20token() public view returns (ERC20) {
|
||||
return daiToken();
|
||||
}
|
||||
|
||||
function upgradeTo530(address _interestReceiver) external {
|
||||
require(msg.sender == address(this));
|
||||
|
||||
address dai = address(daiToken());
|
||||
address comp = address(compToken());
|
||||
_setInterestEnabled(dai, true);
|
||||
_setMinCashThreshold(dai, 1000000 ether);
|
||||
_setMinInterestPaid(dai, 1000 ether);
|
||||
_setInterestReceiver(dai, _interestReceiver);
|
||||
|
||||
_setMinInterestPaid(comp, 1 ether);
|
||||
_setInterestReceiver(comp, _interestReceiver);
|
||||
|
||||
invest(dai);
|
||||
}
|
||||
|
||||
function investDai() external {
|
||||
invest(address(daiToken()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraws the erc20 tokens or native coins from this contract.
|
||||
* @param _token address of the claimed token or address(0) for native coins.
|
||||
* @param _to address of the tokens/coins receiver.
|
||||
*/
|
||||
function claimTokens(address _token, address _to) external onlyIfUpgradeabilityOwner {
|
||||
// Since bridged tokens are locked at this contract, it is not allowed to claim them with the use of claimTokens function
|
||||
address bridgedToken = address(daiToken());
|
||||
require(_token != address(bridgedToken));
|
||||
require(_token != address(cDaiToken()) || !isInterestEnabled(bridgedToken));
|
||||
require(_token != address(compToken()) || !isInterestEnabled(bridgedToken));
|
||||
claimValues(_token, _to);
|
||||
}
|
||||
|
||||
function onExecuteMessage(
|
||||
address _recipient,
|
||||
uint256 _amount,
|
||||
bytes32 /*_txHash*/
|
||||
) internal returns (bool) {
|
||||
addTotalExecutedPerDay(getCurrentDay(), _amount);
|
||||
|
||||
ERC20 token = daiToken();
|
||||
ensureEnoughTokens(token, _amount);
|
||||
|
||||
return token.transfer(_recipient, _amount);
|
||||
}
|
||||
|
||||
function onExecuteMessageGSN(address recipient, uint256 amount, uint256 fee) internal returns (bool) {
|
||||
ensureEnoughTokens(daiToken(), amount);
|
||||
|
||||
return super.onExecuteMessageGSN(recipient, amount, fee);
|
||||
}
|
||||
|
||||
function ensureEnoughTokens(ERC20 token, uint256 amount) internal {
|
||||
uint256 currentBalance = token.balanceOf(address(this));
|
||||
|
||||
if (currentBalance < amount) {
|
||||
uint256 withdrawAmount = (amount - currentBalance).add(minCashThreshold(address(token)));
|
||||
_withdraw(address(token), withdrawAmount);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ const {
|
|||
} = require('../deploymentUtils')
|
||||
const { web3Foreign, deploymentPrivateKey, FOREIGN_RPC_URL } = require('../web3')
|
||||
const {
|
||||
foreignContracts: { EternalStorageProxy, BridgeValidators, ForeignBridgeErcToNative: ForeignBridge }
|
||||
foreignContracts: { EternalStorageProxy, BridgeValidators, XDaiForeignBridge: ForeignBridge }
|
||||
} = require('../loadContracts')
|
||||
|
||||
const VALIDATORS = env.VALIDATORS.split(' ')
|
||||
|
|
|
@ -11,7 +11,7 @@ function getContracts() {
|
|||
ERC677BridgeToken: require(`../../build/${buildPath}/ERC677BridgeToken.json`),
|
||||
ERC677BridgeTokenRewardable: require(`../../build/${buildPath}/ERC677BridgeTokenRewardable.json`),
|
||||
ERC677BridgeTokenPermittable: require(`../../build/${buildPath}/PermittableToken.json`),
|
||||
ForeignBridgeErcToNative: require(`../../build/${buildPath}/ForeignBridgeErcToNative.json`),
|
||||
XDaiForeignBridge: require(`../../build/${buildPath}/XDaiForeignBridge.json`),
|
||||
FeeManagerErcToNative: require(`../../build/${buildPath}/FeeManagerErcToNative.json`),
|
||||
FeeManagerErcToNativePOSDAO: require(`../../build/${buildPath}/FeeManagerErcToNativePOSDAO.json`),
|
||||
HomeBridgeErcToNative: require(`../../build/${buildPath}/HomeBridgeErcToNative.json`),
|
||||
|
|
|
@ -3,7 +3,7 @@ const BridgeValidators = artifacts.require('BridgeValidators.sol')
|
|||
const EternalStorageProxy = artifacts.require('EternalStorageProxy.sol')
|
||||
const ERC677BridgeToken = artifacts.require('ERC677BridgeToken.sol')
|
||||
const ERC20Mock = artifacts.require('ERC20Mock.sol')
|
||||
const ForeignBridgeErcToNativeMock = artifacts.require('ForeignBridgeErcToNativeMock.sol')
|
||||
const XDaiForeignBridgeMock = artifacts.require('XDaiForeignBridgeMock.sol')
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { ERROR_MSG, ZERO_ADDRESS, toBN } = require('../setup')
|
||||
|
@ -212,7 +212,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
const value = ether('0.25')
|
||||
let foreignBridge
|
||||
beforeEach(async () => {
|
||||
foreignBridge = await ForeignBridgeErcToNativeMock.new()
|
||||
foreignBridge = await ForeignBridge.new()
|
||||
token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
await foreignBridge.initialize(
|
||||
validatorContract.address,
|
||||
|
@ -367,7 +367,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
await multisigValidatorContract.initialize(2, twoAuthorities, ownerOfValidatorContract, {
|
||||
from: ownerOfValidatorContract
|
||||
})
|
||||
foreignBridgeWithMultiSignatures = await ForeignBridgeErcToNativeMock.new()
|
||||
foreignBridgeWithMultiSignatures = await ForeignBridge.new()
|
||||
await foreignBridgeWithMultiSignatures.initialize(
|
||||
multisigValidatorContract.address,
|
||||
token.address,
|
||||
|
@ -429,7 +429,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
await validatorContractWith3Signatures.initialize(3, authoritiesFiveAccs, ownerOfValidators)
|
||||
const erc20Token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
const value = halfEther
|
||||
const foreignBridgeWithThreeSigs = await ForeignBridgeErcToNativeMock.new()
|
||||
const foreignBridgeWithThreeSigs = await ForeignBridge.new()
|
||||
|
||||
await foreignBridgeWithThreeSigs.initialize(
|
||||
validatorContractWith3Signatures.address,
|
||||
|
@ -477,7 +477,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
|
||||
await validatorContract.initialize(MAX_SIGNATURES, addresses, ownerOfValidators)
|
||||
const erc20Token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
const foreignBridgeWithMaxSigs = await ForeignBridgeErcToNativeMock.new()
|
||||
const foreignBridgeWithMaxSigs = await ForeignBridge.new()
|
||||
|
||||
await foreignBridgeWithMaxSigs.initialize(
|
||||
validatorContract.address,
|
||||
|
@ -622,7 +622,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
const valueOnHome = toBN(valueOnForeign * 10 ** decimalShift)
|
||||
|
||||
const owner = accounts[0]
|
||||
const foreignBridgeImpl = await ForeignBridgeErcToNativeMock.new()
|
||||
const foreignBridgeImpl = await ForeignBridge.new()
|
||||
const storageProxy = await EternalStorageProxy.new().should.be.fulfilled
|
||||
await storageProxy.upgradeTo('1', foreignBridgeImpl.address).should.be.fulfilled
|
||||
const foreignBridge = await ForeignBridge.at(storageProxy.address)
|
||||
|
@ -673,7 +673,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
await multisigValidatorContract.initialize(2, twoAuthorities, ownerOfValidatorContract, {
|
||||
from: ownerOfValidatorContract
|
||||
})
|
||||
const foreignBridgeWithMultiSignatures = await ForeignBridgeErcToNativeMock.new()
|
||||
const foreignBridgeWithMultiSignatures = await ForeignBridge.new()
|
||||
await foreignBridgeWithMultiSignatures.initialize(
|
||||
multisigValidatorContract.address,
|
||||
token.address,
|
||||
|
@ -862,7 +862,7 @@ contract('ForeignBridge_ERC20_to_Native', async accounts => {
|
|||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
foreignBridge = await ForeignBridgeErcToNativeMock.new()
|
||||
foreignBridge = await XDaiForeignBridgeMock.new()
|
||||
await foreignBridge.initialize(
|
||||
validatorContract.address,
|
||||
dai.address,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// Required for opengsn proper work
|
||||
require('array-flat-polyfill')
|
||||
|
||||
const ForeignBridge = artifacts.require('ForeignBridgeErcToNative.sol')
|
||||
const ForeignBridgeErcToNativeMock = artifacts.require('ForeignBridgeErcToNativeMock.sol')
|
||||
const XDaiForeignBridgeMock = artifacts.require('XDaiForeignBridgeMock.sol')
|
||||
const BridgeValidators = artifacts.require('BridgeValidators.sol')
|
||||
const ERC677BridgeToken = artifacts.require('ERC677BridgeToken.sol')
|
||||
|
||||
const UniswapRouterMock = artifacts.require('UniswapRouterMock.sol')
|
||||
const TokenPaymaster = artifacts.require('TokenPaymaster.sol')
|
||||
|
@ -23,6 +21,7 @@ const {
|
|||
evalMetrics,
|
||||
paymasterError
|
||||
} = require('../helpers/helpers')
|
||||
const getCompoundContracts = require('../compound/contracts')
|
||||
|
||||
const requireBlockConfirmations = 8
|
||||
const gasPrice = web3.utils.toWei('1', 'gwei')
|
||||
|
@ -43,6 +42,8 @@ function createEmptyAccount(relayer) {
|
|||
}
|
||||
|
||||
contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
||||
const faucet = accounts[6] // account where all Compound-related DAIs where minted
|
||||
|
||||
let validatorContract
|
||||
let authorities
|
||||
let owner
|
||||
|
@ -65,16 +66,18 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
authorities = [accounts[1], accounts[2]]
|
||||
owner = accounts[0]
|
||||
await validatorContract.initialize(1, authorities, owner)
|
||||
otherSideBridge = await ForeignBridge.new()
|
||||
otherSideBridge = await XDaiForeignBridgeMock.new()
|
||||
|
||||
const contracts = await getCompoundContracts()
|
||||
token = contracts.dai
|
||||
})
|
||||
after(async () => {
|
||||
await GsnTestEnvironment.stopGsn()
|
||||
})
|
||||
describe('#initialize', async () => {
|
||||
it('should initialize', async () => {
|
||||
token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
router = await UniswapRouterMock.new()
|
||||
foreignBridge = await ForeignBridgeErcToNativeMock.new()
|
||||
foreignBridge = await XDaiForeignBridgeMock.new()
|
||||
|
||||
paymaster = await TokenPaymaster.new(
|
||||
RelayHubAddress,
|
||||
|
@ -100,9 +103,8 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
let GSNRelayer
|
||||
let GSNSigner
|
||||
beforeEach(async () => {
|
||||
token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
ForeignBridgeErcToNativeMock.web3.setProvider(web3.currentProvider)
|
||||
foreignBridge = await ForeignBridgeErcToNativeMock.new()
|
||||
XDaiForeignBridgeMock.web3.setProvider(web3.currentProvider)
|
||||
foreignBridge = await XDaiForeignBridgeMock.new()
|
||||
await foreignBridge.initialize(
|
||||
validatorContract.address,
|
||||
token.address,
|
||||
|
@ -128,7 +130,7 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
await foreignBridge.setTrustedForwarder(ForwarderAddress)
|
||||
await foreignBridge.setPayMaster(paymaster.address)
|
||||
|
||||
await token.mint(foreignBridge.address, BRIDGE_TOKENS)
|
||||
await token.transfer(foreignBridge.address, BRIDGE_TOKENS, { from: faucet })
|
||||
|
||||
// Give Router 1 ether
|
||||
await web3.eth.sendTransaction({
|
||||
|
@ -158,7 +160,7 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
// From now on all calls will be relayed through GSN.
|
||||
// If you want to omit GSN specify
|
||||
// { useGSN: false } in transaction details
|
||||
ForeignBridgeErcToNativeMock.web3.setProvider(GSNRelayer)
|
||||
XDaiForeignBridgeMock.web3.setProvider(GSNRelayer)
|
||||
})
|
||||
it('should allow to executeSignaturesGSN', async () => {
|
||||
const recipientAccount = GSNSigner
|
||||
|
@ -243,7 +245,7 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
.fulfilled
|
||||
|
||||
// tx 2
|
||||
await token.mint(foreignBridge.address, BRIDGE_TOKENS)
|
||||
await token.transfer(foreignBridge.address, BRIDGE_TOKENS, { from: faucet })
|
||||
const from2 = createEmptyAccount(GSNRelayer)
|
||||
const message2 = createMessage(from2, REQUESTED_TOKENS, transactionHash, foreignBridge.address)
|
||||
const signature2 = await sign(authorities[0], message2)
|
||||
|
|
Loading…
Reference in New Issue