Merge the develop branch to the master branch, preparation to v6.0.0
This update for the `master` branch contains the changes made to address findings discovered during a security audit: * [Fix] Stricter preconditions for payInterest (#623) * [Fix] Fix offset in comments (#624) * [Fix] Use fixed lower call gas limit (#627) * [Fix] Separate XDaiForeignBridge contract with compound and GSN support (#626) * [Fix] Update GSN interface (#628) * [Fix] Block ERC20 selectors in AMB requests (#630) * [Other] Bump package and contracts interfaces version prior to 6.0.0 (#629)
This commit is contained in:
commit
908a481079
|
@ -4,7 +4,6 @@ pragma experimental ABIEncoderV2;
|
|||
|
||||
import "../upgradeable_contracts/Ownable.sol";
|
||||
|
||||
import "./interfaces/GsnTypes.sol";
|
||||
import "./interfaces/IPaymaster.sol";
|
||||
import "./interfaces/IRelayHub.sol";
|
||||
import "./utils/GsnEip712Library.sol";
|
||||
|
@ -18,7 +17,7 @@ import "./forwarder/IForwarder.sol";
|
|||
*/
|
||||
contract BasePaymaster is IPaymaster, Ownable {
|
||||
IRelayHub internal relayHub;
|
||||
IForwarder public trustedForwarder;
|
||||
address private _trustedForwarder;
|
||||
|
||||
function getHubAddr() public view returns (address) {
|
||||
return address(relayHub);
|
||||
|
@ -27,20 +26,26 @@ contract BasePaymaster is IPaymaster, Ownable {
|
|||
//overhead of forwarder verify+signature, plus hub overhead.
|
||||
uint256 public constant FORWARDER_HUB_OVERHEAD = 50000;
|
||||
|
||||
//These parameters are documented in IPaymaster.GasLimits
|
||||
//These parameters are documented in IPaymaster.GasAndDataLimits
|
||||
uint256 public constant PRE_RELAYED_CALL_GAS_LIMIT = 100000;
|
||||
uint256 public constant POST_RELAYED_CALL_GAS_LIMIT = 110000;
|
||||
uint256 public constant PAYMASTER_ACCEPTANCE_BUDGET = PRE_RELAYED_CALL_GAS_LIMIT + FORWARDER_HUB_OVERHEAD;
|
||||
uint256 public constant CALLDATA_SIZE_LIMIT = 10500;
|
||||
|
||||
function getGasLimits() external view returns (IPaymaster.GasLimits) {
|
||||
function getGasAndDataLimits() external view returns (IPaymaster.GasAndDataLimits limits) {
|
||||
return
|
||||
IPaymaster.GasLimits(PAYMASTER_ACCEPTANCE_BUDGET, PRE_RELAYED_CALL_GAS_LIMIT, POST_RELAYED_CALL_GAS_LIMIT);
|
||||
IPaymaster.GasAndDataLimits(
|
||||
PAYMASTER_ACCEPTANCE_BUDGET,
|
||||
PRE_RELAYED_CALL_GAS_LIMIT,
|
||||
POST_RELAYED_CALL_GAS_LIMIT,
|
||||
CALLDATA_SIZE_LIMIT
|
||||
);
|
||||
}
|
||||
|
||||
// this method must be called from preRelayedCall to validate that the forwarder
|
||||
// is approved by the paymaster as well as by the recipient contract.
|
||||
function _verifyForwarder(GsnTypes.RelayRequest relayRequest) public view {
|
||||
require(address(trustedForwarder) == relayRequest.relayData.forwarder, "Forwarder is not trusted");
|
||||
require(address(_trustedForwarder) == relayRequest.relayData.forwarder, "Forwarder is not trusted");
|
||||
GsnEip712Library.verifyForwarderTrusted(relayRequest);
|
||||
}
|
||||
|
||||
|
@ -48,7 +53,7 @@ contract BasePaymaster is IPaymaster, Ownable {
|
|||
* modifier to be used by recipients as access control protection for preRelayedCall & postRelayedCall
|
||||
*/
|
||||
modifier relayHubOnly() {
|
||||
require(msg.sender == getHubAddr(), "Function can only be called by RelayHub");
|
||||
require(msg.sender == getHubAddr(), "can only be called by RelayHub");
|
||||
_;
|
||||
}
|
||||
|
||||
|
@ -56,16 +61,28 @@ contract BasePaymaster is IPaymaster, Ownable {
|
|||
relayHub = hub;
|
||||
}
|
||||
|
||||
function setTrustedForwarder(IForwarder forwarder) public onlyOwner {
|
||||
trustedForwarder = forwarder;
|
||||
function setTrustedForwarder(address forwarder) public onlyOwner {
|
||||
_trustedForwarder = forwarder;
|
||||
}
|
||||
|
||||
// check current deposit on relay hub.
|
||||
function getRelayHubDeposit() public view returns (uint256) {
|
||||
function trustedForwarder() external view returns (address) {
|
||||
return _trustedForwarder;
|
||||
}
|
||||
|
||||
/// check current deposit on relay hub.
|
||||
function getRelayHubDeposit() external view returns (uint256) {
|
||||
return relayHub.balanceOf(address(this));
|
||||
}
|
||||
|
||||
// withdraw deposit from relayHub
|
||||
// any eth moved into the paymaster is transferred as a deposit.
|
||||
// This way, we don't need to understand the RelayHub API in order to replenish
|
||||
// the paymaster.
|
||||
function() external payable {
|
||||
require(address(relayHub) != address(0), "relay hub address not set");
|
||||
relayHub.depositFor.value(msg.value)(address(this));
|
||||
}
|
||||
|
||||
/// withdraw deposit from relayHub
|
||||
function withdrawRelayHubDepositTo(uint256 amount, address target) public onlyOwner {
|
||||
relayHub.withdraw(amount, target);
|
||||
}
|
||||
|
|
|
@ -10,14 +10,20 @@ contract IForwarder {
|
|||
uint256 gas;
|
||||
uint256 nonce;
|
||||
bytes data;
|
||||
uint256 validUntil;
|
||||
}
|
||||
|
||||
event DomainRegistered(bytes32 indexed domainSeparator, bytes domainValue);
|
||||
|
||||
event RequestTypeRegistered(bytes32 indexed typeHash, string typeStr);
|
||||
|
||||
function getNonce(address from) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* verify the transaction would execute.
|
||||
* validate the signature and the nonce of the request.
|
||||
* revert if either signature or nonce are incorrect.
|
||||
* also revert if domainSeparator or requestTypeHash are not registered.
|
||||
*/
|
||||
function verify(
|
||||
ForwardRequest forwardRequest,
|
||||
|
@ -51,8 +57,8 @@ contract IForwarder {
|
|||
/**
|
||||
* Register a new Request typehash.
|
||||
* @param typeName - the name of the request type.
|
||||
* @param typeSuffix - anything after the generic params can be empty string (if no extra fields are needed)
|
||||
* if it does contain a value, then a comma is added first.
|
||||
* @param typeSuffix - any extra data after the generic params.
|
||||
* (must add at least one param. The generic ForwardRequest type is always registered by the constructor)
|
||||
*/
|
||||
function registerRequestType(string typeName, string typeSuffix) external;
|
||||
|
||||
|
|
|
@ -4,15 +4,16 @@ pragma solidity 0.4.24;
|
|||
import "../forwarder/IForwarder.sol";
|
||||
|
||||
contract GsnTypes {
|
||||
/// @notice gasPrice, pctRelayFee and baseRelayFee must be validated inside of the paymaster's preRelayedCall in order not to overpay
|
||||
struct RelayData {
|
||||
uint256 gasPrice;
|
||||
uint256 pctRelayFee;
|
||||
uint256 baseRelayFee;
|
||||
address relayWorker;
|
||||
address paymaster;
|
||||
address forwarder;
|
||||
bytes paymasterData;
|
||||
uint256 clientId;
|
||||
address forwarder;
|
||||
}
|
||||
|
||||
//note: must start with the ForwardRequest to be an extension of the generic forwarder
|
||||
|
|
|
@ -29,16 +29,19 @@ contract IPaymaster {
|
|||
* note that an OOG will revert the transaction, but the paymaster already committed to pay,
|
||||
* so the relay will get compensated, at the expense of the paymaster
|
||||
*/
|
||||
struct GasLimits {
|
||||
struct GasAndDataLimits {
|
||||
uint256 acceptanceBudget;
|
||||
uint256 preRelayedCallGasLimit;
|
||||
uint256 postRelayedCallGasLimit;
|
||||
uint256 calldataSizeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GasLimits constants used by the Paymaster.
|
||||
* Return the Gas Limits and msg.data max size constants used by the Paymaster.
|
||||
*/
|
||||
function getGasLimits() external view returns (GasLimits memory limits);
|
||||
function getGasAndDataLimits() external view returns (GasAndDataLimits memory limits);
|
||||
|
||||
function trustedForwarder() external view returns (address);
|
||||
|
||||
/**
|
||||
* return the relayHub of this contract.
|
||||
|
@ -49,7 +52,7 @@ contract IPaymaster {
|
|||
* Can be used to determine if the contract can pay for incoming calls before making any.
|
||||
* @return the paymaster's deposit in the RelayHub.
|
||||
*/
|
||||
function getRelayHubDeposit() public view returns (uint256);
|
||||
function getRelayHubDeposit() external view returns (uint256);
|
||||
|
||||
/**
|
||||
* Called by Relay (and RelayHub), to validate if the paymaster agrees to pay for this call.
|
||||
|
@ -74,7 +77,7 @@ contract IPaymaster {
|
|||
* Note that in most cases the paymaster shouldn't try use it at all. It is always checked
|
||||
* by the forwarder immediately after preRelayedCall returns.
|
||||
* @param approvalData - extra dapp-specific data (e.g. signature from trusted party)
|
||||
* @param maxPossibleGas - based on values returned from {@link getGasLimits},
|
||||
* @param maxPossibleGas - based on values returned from {@link getGasAndDataLimits},
|
||||
* the RelayHub will calculate the maximum possible amount of gas the user may be charged for.
|
||||
* In order to convert this value to wei, the Paymaster has to call "relayHub.calculateCharge()"
|
||||
* return:
|
||||
|
|
|
@ -6,21 +6,25 @@ import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
|
|||
import "../BasePaymaster.sol";
|
||||
import "./IUniswapV2Router02.sol";
|
||||
import "../../upgradeable_contracts/GSNForeignERC20Bridge.sol";
|
||||
import "../../upgradeable_contracts/Claimable.sol";
|
||||
|
||||
contract TokenPaymaster is BasePaymaster {
|
||||
address private token;
|
||||
contract TokenPaymaster is BasePaymaster, Claimable {
|
||||
ERC20 private token;
|
||||
IUniswapV2Router02 private router;
|
||||
address private bridge;
|
||||
|
||||
address[] private tokenWethPair = new address[](2);
|
||||
uint256 public postGasUsage = 300000;
|
||||
// Default value from BasePaymaster, may be changed later
|
||||
// if we need to increase number of signatures for bridge contract
|
||||
uint256 public calldataSizeLimit = 10500;
|
||||
|
||||
constructor(address _relayHub, address _forwarder, address _token, address _router, address _bridge) public {
|
||||
_setOwner(msg.sender);
|
||||
relayHub = IRelayHub(_relayHub);
|
||||
trustedForwarder = IForwarder(_forwarder);
|
||||
setRelayHub(IRelayHub(_relayHub));
|
||||
setTrustedForwarder(_forwarder);
|
||||
|
||||
token = _token;
|
||||
token = ERC20(_token);
|
||||
router = IUniswapV2Router02(_router);
|
||||
bridge = _bridge;
|
||||
|
||||
|
@ -29,7 +33,7 @@ contract TokenPaymaster is BasePaymaster {
|
|||
}
|
||||
|
||||
function setToken(address t) external onlyOwner {
|
||||
token = t;
|
||||
token = ERC20(t);
|
||||
}
|
||||
|
||||
function setRouter(IUniswapV2Router02 r) external onlyOwner {
|
||||
|
@ -40,13 +44,16 @@ contract TokenPaymaster is BasePaymaster {
|
|||
bridge = b;
|
||||
}
|
||||
|
||||
function versionPaymaster() external view returns (string memory) {
|
||||
return "2.0.0+opengsn.tokengsn.ipaymaster";
|
||||
function setPostGasUsage(uint256 gasUsage) external onlyOwner {
|
||||
postGasUsage = gasUsage;
|
||||
}
|
||||
|
||||
function() external payable {
|
||||
require(address(relayHub) != address(0), "relay hub address not set");
|
||||
relayHub.depositFor.value(msg.value)(address(this));
|
||||
function setCalldataSizeLimit(uint256 sizeLimit) external onlyOwner {
|
||||
calldataSizeLimit = sizeLimit;
|
||||
}
|
||||
|
||||
function versionPaymaster() external view returns (string memory) {
|
||||
return "2.2.0+opengsn.bridgetokengsn.ipaymaster";
|
||||
}
|
||||
|
||||
function deposit() external payable {
|
||||
|
@ -54,19 +61,16 @@ contract TokenPaymaster is BasePaymaster {
|
|||
relayHub.depositFor.value(msg.value)(address(this));
|
||||
}
|
||||
|
||||
function getGasLimits() external view returns (IPaymaster.GasLimits memory limits) {
|
||||
function getGasAndDataLimits() external view returns (IPaymaster.GasAndDataLimits memory limits) {
|
||||
return
|
||||
IPaymaster.GasLimits(
|
||||
IPaymaster.GasAndDataLimits(
|
||||
PAYMASTER_ACCEPTANCE_BUDGET,
|
||||
PRE_RELAYED_CALL_GAS_LIMIT,
|
||||
postGasUsage // maximum postRelayedCall gasLimit
|
||||
postGasUsage, // maximum postRelayedCall gasLimit
|
||||
calldataSizeLimit
|
||||
);
|
||||
}
|
||||
|
||||
function erc20() internal view returns (ERC20) {
|
||||
return ERC20(token);
|
||||
}
|
||||
|
||||
function readBytes32(bytes memory b, uint256 index) internal pure returns (bytes32 res) {
|
||||
require(b.length >= index + 32, "data too short");
|
||||
assembly {
|
||||
|
@ -79,7 +83,7 @@ contract TokenPaymaster is BasePaymaster {
|
|||
bytes signature,
|
||||
bytes approvalData,
|
||||
uint256 maxPossibleGas
|
||||
) public returns (bytes memory context, bool revertOnRecipientRevert) {
|
||||
) public relayHubOnly returns (bytes memory context, bool revertOnRecipientRevert) {
|
||||
(signature, approvalData);
|
||||
_verifyForwarder(relayRequest);
|
||||
bytes memory reqData = relayRequest.request.data;
|
||||
|
@ -108,12 +112,9 @@ contract TokenPaymaster is BasePaymaster {
|
|||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function setPostGasUsage(uint256 gasUsage) external onlyOwner {
|
||||
postGasUsage = gasUsage;
|
||||
}
|
||||
|
||||
function postRelayedCall(bytes context, bool success, uint256 gasUseWithoutPost, GsnTypes.RelayData relayData)
|
||||
public
|
||||
relayHubOnly
|
||||
{
|
||||
(success);
|
||||
// Extract data from context
|
||||
|
@ -137,7 +138,7 @@ contract TokenPaymaster is BasePaymaster {
|
|||
uint256 chargeWei = relayHub.calculateCharge(min(gasUseWithoutPost + postGasUsage, maxPossibleGas), relayData);
|
||||
|
||||
// Uniswap
|
||||
require(erc20().approve(address(router), maxTokensFee), "approve failed");
|
||||
require(token.approve(address(router), maxTokensFee), "approve failed");
|
||||
// NOTE: Received eth automatically converts to relayhub deposit
|
||||
uint256 spentTokens = router.swapTokensForExactETH(
|
||||
chargeWei,
|
||||
|
@ -149,7 +150,11 @@ contract TokenPaymaster is BasePaymaster {
|
|||
|
||||
// Send rest of tokens to user
|
||||
if (spentTokens < maxTokensFee) {
|
||||
require(erc20().transfer(to, maxTokensFee - spentTokens));
|
||||
require(token.transfer(to, maxTokensFee - spentTokens));
|
||||
}
|
||||
}
|
||||
|
||||
function claimTokens(address _token, address _to) external onlyOwner {
|
||||
claimValues(_token, _to);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ library Message {
|
|||
// offset 32: 20 bytes :: address - recipient address
|
||||
// offset 52: 32 bytes :: uint256 - value
|
||||
// offset 84: 32 bytes :: bytes32 - transaction hash
|
||||
// offset 104: 20 bytes :: address - contract address to prevent double spending
|
||||
// offset 116: 20 bytes :: address - contract address to prevent double spending
|
||||
|
||||
// mload always reads 32 bytes.
|
||||
// so we can and have to start reading recipient at offset 20 instead of 32.
|
||||
|
|
|
@ -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.
|
||||
*/
|
|
@ -36,7 +36,8 @@ contract GSNForeignERC20Bridge is BasicForeignBridge, ERC20Bridge, BaseRelayReci
|
|||
* as a commission
|
||||
*/
|
||||
function executeSignaturesGSN(bytes message, bytes signatures, uint256 maxTokensFee) external {
|
||||
require(msg.sender == addressStorage[TRUSTED_FORWARDER], "invalid forwarder");
|
||||
// Allow only forwarder calls
|
||||
require(isTrustedForwarder(msg.sender), "invalid forwarder");
|
||||
Message.hasEnoughValidSignatures(message, signatures, validatorContract(), false);
|
||||
|
||||
address recipient;
|
||||
|
@ -59,12 +60,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;
|
||||
}
|
||||
|
|
|
@ -26,14 +26,6 @@ contract InterestReceiverStakeBuyback is InterestReceiverBase {
|
|||
// (min received %) * (amount / 1 DAI) * (STAKE per 1 DAI)
|
||||
uint256 minAmount = (minReceivedFraction * amount * uniswapRouterV2.getAmountsOut(1 ether, path)[2]) / 10**36;
|
||||
|
||||
bytes memory data = abi.encodeWithSelector(
|
||||
uniswapRouterV2.swapExactTokensForTokens.selector,
|
||||
amount,
|
||||
minAmount,
|
||||
path,
|
||||
burnAddress,
|
||||
now
|
||||
);
|
||||
address(uniswapRouterV2).call(data);
|
||||
uniswapRouterV2.swapExactTokensForTokens(amount, minAmount, path, burnAddress, now);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,7 @@ contract InterestReceiverSwapToETH is InterestReceiverBase {
|
|||
// (min received %) * (amount / 1 DAI) * (ETH per 1 DAI)
|
||||
uint256 minAmount = (minReceivedFraction * amount * uniswapRouterV2.getAmountsOut(1 ether, path)[1]) / 10**36;
|
||||
|
||||
bytes memory data = abi.encodeWithSelector(
|
||||
uniswapRouterV2.swapExactTokensForETH.selector,
|
||||
amount,
|
||||
minAmount,
|
||||
path,
|
||||
address(this),
|
||||
now
|
||||
);
|
||||
address(uniswapRouterV2).call(data);
|
||||
uniswapRouterV2.swapExactTokensForETH(amount, minAmount, path, address(this), now);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@ pragma solidity 0.4.24;
|
|||
|
||||
contract VersionableBridge {
|
||||
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
|
||||
return (6, 0, 0);
|
||||
return (6, 1, 0);
|
||||
}
|
||||
|
||||
/* solcov ignore next */
|
||||
|
|
|
@ -101,6 +101,15 @@ contract BasicAMB is BasicBridge, VersionableAMB {
|
|||
return boolStorage[ALLOW_REENTRANT_REQUESTS];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
claimValues(_token, _to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function for retrieving current nonce value
|
||||
* @return nonce value
|
||||
|
|
|
@ -45,7 +45,7 @@ contract BasicHomeAMB is BasicAMB, MessageDelivery {
|
|||
* @param _data calldata passed to the executor on the other side.
|
||||
* @param _gas gas limit used on the other network for executing a message.
|
||||
*/
|
||||
function requireToConfirmMessage(address _contract, bytes _data, uint256 _gas) external returns (bytes32) {
|
||||
function requireToConfirmMessage(address _contract, bytes memory _data, uint256 _gas) public returns (bytes32) {
|
||||
return _sendMessage(_contract, _data, _gas, SEND_TO_MANUAL_LANE);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ contract MessageDelivery is BasicAMB, MessageProcessor {
|
|||
using SafeMath for uint256;
|
||||
|
||||
uint256 internal constant SEND_TO_ORACLE_DRIVEN_LANE = 0x00;
|
||||
// after EIP2929, call to warmed contract address costs 100 instead of 2600
|
||||
uint256 internal constant MIN_GAS_PER_CALL = 100;
|
||||
|
||||
/**
|
||||
* @dev Requests message relay to the opposite network
|
||||
|
@ -17,7 +19,7 @@ contract MessageDelivery is BasicAMB, MessageProcessor {
|
|||
* @param _data calldata passed to the executor on the other side
|
||||
* @param _gas gas limit used on the other network for executing a message
|
||||
*/
|
||||
function requireToPassMessage(address _contract, bytes _data, uint256 _gas) public returns (bytes32) {
|
||||
function requireToPassMessage(address _contract, bytes memory _data, uint256 _gas) public returns (bytes32) {
|
||||
return _sendMessage(_contract, _data, _gas, SEND_TO_ORACLE_DRIVEN_LANE);
|
||||
}
|
||||
|
||||
|
@ -28,11 +30,33 @@ contract MessageDelivery is BasicAMB, MessageProcessor {
|
|||
* @param _gas gas limit used on the other network for executing a message
|
||||
* @param _dataType AMB message dataType to be included as a part of the header
|
||||
*/
|
||||
function _sendMessage(address _contract, bytes _data, uint256 _gas, uint256 _dataType) internal returns (bytes32) {
|
||||
function _sendMessage(address _contract, bytes memory _data, uint256 _gas, uint256 _dataType)
|
||||
internal
|
||||
returns (bytes32)
|
||||
{
|
||||
// it is not allowed to pass messages while other messages are processed
|
||||
// if other is not explicitly configured
|
||||
require(messageId() == bytes32(0) || allowReentrantRequests());
|
||||
require(_gas >= getMinimumGasUsage(_data) && _gas <= maxGasPerTx());
|
||||
require(_gas >= MIN_GAS_PER_CALL && _gas <= maxGasPerTx());
|
||||
|
||||
uint256 selector;
|
||||
assembly {
|
||||
selector := and(mload(add(_data, 4)), 0xffffffff)
|
||||
}
|
||||
// In order to prevent possible unauthorized ERC20 withdrawals, the following function signatures are prohibited:
|
||||
// * transfer(address,uint256)
|
||||
// * approve(address,uint256)
|
||||
// * transferFrom(address,address,uint256)
|
||||
// * approveAndCall(address,uint256,bytes)
|
||||
// * transferAndCall(address,uint256,bytes)
|
||||
// See https://medium.com/immunefi/xdai-stake-arbitrary-call-method-bug-postmortem-f80a90ac56e3 for more details
|
||||
require(
|
||||
selector != 0xa9059cbb &&
|
||||
selector != 0x095ea7b3 &&
|
||||
selector != 0x23b872dd &&
|
||||
selector != 0x4000aea0 &&
|
||||
selector != 0xcae9ca51
|
||||
);
|
||||
|
||||
(bytes32 _messageId, bytes memory header) = _packHeader(_contract, _gas, _dataType);
|
||||
|
||||
|
@ -42,17 +66,6 @@ contract MessageDelivery is BasicAMB, MessageProcessor {
|
|||
return _messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns a lower limit on gas limit for the particular message data
|
||||
* @param _data calldata passed to the executor on the other side
|
||||
*/
|
||||
function getMinimumGasUsage(bytes _data) public pure returns (uint256 gas) {
|
||||
// From Ethereum Yellow Paper
|
||||
// 68 gas is paid for every non-zero byte of data or code for a transaction
|
||||
// Starting from Istanbul hardfork, 16 gas is paid (EIP-2028)
|
||||
return _data.length.mul(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Packs message header into a single bytes blob
|
||||
* @param _contract executor address on the other side
|
||||
|
|
|
@ -17,6 +17,6 @@ contract VersionableAMB is VersionableBridge {
|
|||
* @return (major, minor, patch) version triple
|
||||
*/
|
||||
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
|
||||
return (6, 1, 0);
|
||||
return (6, 2, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ contract CompoundConnector is InterestConnector {
|
|||
/**
|
||||
* @dev Claims Comp token and transfers it to the associated interest receiver.
|
||||
*/
|
||||
function claimCompAndPay() external {
|
||||
function claimCompAndPay() external onlyEOA {
|
||||
address[] memory holders = new address[](1);
|
||||
holders[0] = address(this);
|
||||
address[] memory markets = new address[](1);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -21,6 +21,17 @@ contract InterestConnector is Ownable, ERC20Bridge {
|
|||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Ensures that caller is an EOA.
|
||||
* Functions with such modifier cannot be called from other contract (as well as from GSN-like approaches)
|
||||
*/
|
||||
modifier onlyEOA {
|
||||
// solhint-disable-next-line avoid-tx-origin
|
||||
require(msg.sender == tx.origin);
|
||||
/* solcov ignore next */
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tells if interest earning was enabled for particular token.
|
||||
* @return true, if interest bearing is enabled.
|
||||
|
@ -123,7 +134,7 @@ contract InterestConnector is Ownable, ERC20Bridge {
|
|||
* Requires interest for the given token to be enabled.
|
||||
* @param _token address of the token contract.
|
||||
*/
|
||||
function payInterest(address _token) external interestEnabled(_token) {
|
||||
function payInterest(address _token) external onlyEOA interestEnabled(_token) {
|
||||
uint256 interest = interestAmount(_token);
|
||||
require(interest >= minInterestPaid(_token));
|
||||
|
||||
|
|
|
@ -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`),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tokenbridge-contracts",
|
||||
"version": "6.0.0-rc1",
|
||||
"version": "6.0.0",
|
||||
"description": "Bridge",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -20,12 +20,15 @@
|
|||
"author": "POA network",
|
||||
"license": "GPLv3",
|
||||
"dependencies": {
|
||||
"@opengsn/gsn": "^2.1.0",
|
||||
"@opengsn/cli": "^2.2.2",
|
||||
"@opengsn/contracts": "^2.2.2",
|
||||
"@opengsn/provider": "^2.2.2",
|
||||
"array-flat-polyfill": "^1.0.1",
|
||||
"openzeppelin-solidity": "1.12.0",
|
||||
"truffle": "^5.3.2",
|
||||
"truffle-flattener": "^1.4.2",
|
||||
"web3-provider-engine": "^14.0.6"
|
||||
"web3-provider-engine": "^14.0.6",
|
||||
"web3-utils": "^1.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codechecks/client": "^0.1.9",
|
||||
|
|
|
@ -3,6 +3,7 @@ const HomeBridge = artifacts.require('HomeAMB.sol')
|
|||
const GasToken = artifacts.require('GasTokenMock.sol')
|
||||
const BridgeValidators = artifacts.require('BridgeValidators.sol')
|
||||
const Box = artifacts.require('Box.sol')
|
||||
const ERC20Mock = artifacts.require('ERC20Mock.sol')
|
||||
const ERC677ReceiverTest = artifacts.require('ERC677ReceiverTest.sol')
|
||||
const EternalStorageProxy = artifacts.require('EternalStorageProxy.sol')
|
||||
|
||||
|
@ -334,6 +335,49 @@ contract('ForeignAMB', async accounts => {
|
|||
expect(messageId2).to.include(`${bridgeId}0000000000000001`)
|
||||
expect(messageId3).to.include(`${bridgeId}0000000000000002`)
|
||||
})
|
||||
it('should fail to send message with blocked signatures', async () => {
|
||||
const blockedFunctions = [
|
||||
'transfer(address,uint256)',
|
||||
'approve(address,uint256)',
|
||||
'transferFrom(address,address,uint256)',
|
||||
'approveAndCall(address,uint256,bytes)',
|
||||
'transferAndCall(address,uint256,bytes)'
|
||||
].map(web3.eth.abi.encodeFunctionSignature)
|
||||
for (const signature of blockedFunctions) {
|
||||
await foreignBridge.requireToPassMessage(accounts[7], signature, 10000).should.be.rejected
|
||||
}
|
||||
await foreignBridge.requireToPassMessage(accounts[7], '0x11223344', 10000).should.be.fulfilled
|
||||
})
|
||||
})
|
||||
describe('claimTokens', () => {
|
||||
let foreignBridge
|
||||
let token
|
||||
beforeEach(async () => {
|
||||
const foreignBridgeV1 = await ForeignBridge.new()
|
||||
token = await ERC20Mock.new('Test', 'TST', 18)
|
||||
|
||||
// create proxy
|
||||
const proxy = await EternalStorageProxy.new()
|
||||
await proxy.upgradeTo('1', foreignBridgeV1.address).should.be.fulfilled
|
||||
|
||||
foreignBridge = await ForeignBridge.at(proxy.address)
|
||||
await foreignBridge.initialize(
|
||||
FOREIGN_CHAIN_ID_HEX,
|
||||
HOME_CHAIN_ID_HEX,
|
||||
validatorContract.address,
|
||||
oneEther,
|
||||
gasPrice,
|
||||
requiredBlockConfirmations,
|
||||
owner
|
||||
)
|
||||
})
|
||||
it('should claim mistakenly locked tokens', async () => {
|
||||
await token.mint(foreignBridge.address, oneEther)
|
||||
await foreignBridge.claimTokens(token.address, owner, { from: accounts[2] }).should.be.rejected
|
||||
await foreignBridge.claimTokens(token.address, owner, { from: owner }).should.be.fulfilled
|
||||
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(oneEther)
|
||||
})
|
||||
})
|
||||
describe('executeSignatures', () => {
|
||||
let foreignBridge
|
||||
|
|
|
@ -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,8 +1,7 @@
|
|||
// 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')
|
||||
|
||||
|
@ -10,8 +9,8 @@ const UniswapRouterMock = artifacts.require('UniswapRouterMock.sol')
|
|||
const TokenPaymaster = artifacts.require('TokenPaymaster.sol')
|
||||
|
||||
// GSN
|
||||
const { RelayProvider } = require('@opengsn/gsn')
|
||||
const { GsnTestEnvironment } = require('@opengsn/gsn/dist/GsnTestEnvironment')
|
||||
const { RelayProvider } = require('@opengsn/provider')
|
||||
const { GsnTestEnvironment } = require('@opengsn/cli/dist/GsnTestEnvironment')
|
||||
|
||||
const { toBN, ERROR_MSG, ZERO_ADDRESS } = require('../setup')
|
||||
const {
|
||||
|
@ -23,6 +22,7 @@ const {
|
|||
evalMetrics,
|
||||
paymasterError
|
||||
} = require('../helpers/helpers')
|
||||
const getCompoundContracts = require('../compound/contracts')
|
||||
|
||||
const requireBlockConfirmations = 8
|
||||
const gasPrice = web3.utils.toWei('1', 'gwei')
|
||||
|
@ -33,6 +33,7 @@ const minPerTx = ether('0.01')
|
|||
const dailyLimit = homeDailyLimit
|
||||
const ZERO = toBN(0)
|
||||
const decimalShiftZero = 0
|
||||
const HALF_ETHER = ether('0.5')
|
||||
const FIVE_ETHER = ether('5')
|
||||
const GSNGasLimit = 500000
|
||||
|
||||
|
@ -43,6 +44,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 +68,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)
|
||||
it('should initialize paymaster', async () => {
|
||||
router = await UniswapRouterMock.new()
|
||||
foreignBridge = await ForeignBridgeErcToNativeMock.new()
|
||||
foreignBridge = await XDaiForeignBridgeMock.new()
|
||||
|
||||
paymaster = await TokenPaymaster.new(
|
||||
RelayHubAddress,
|
||||
|
@ -100,9 +105,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 +132,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 +162,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 +247,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)
|
||||
|
@ -282,4 +286,29 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
.should.be.rejectedWith(`${ERROR_MSG} invalid forwarder`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#claimTokens', async () => {
|
||||
it('can send erc20', async () => {
|
||||
router = await UniswapRouterMock.new()
|
||||
paymaster = await TokenPaymaster.new(
|
||||
RelayHubAddress,
|
||||
ForwarderAddress,
|
||||
ZERO_ADDRESS,
|
||||
router.address,
|
||||
ZERO_ADDRESS,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
const token = await ERC677BridgeToken.new('Some ERC20', 'RSZT', 18)
|
||||
await token.mint(accounts[1], HALF_ETHER).should.be.fulfilled
|
||||
await token.transfer(paymaster.address, HALF_ETHER, { from: accounts[1] })
|
||||
|
||||
await paymaster.claimTokens(token.address, accounts[3], { from: accounts[3] }).should.be.rejectedWith(ERROR_MSG)
|
||||
await paymaster.claimTokens(token.address, accounts[3], { from: owner })
|
||||
const pmBalance = await token.balanceOf(paymaster.address)
|
||||
const accBalance = await token.balanceOf(accounts[3])
|
||||
pmBalance.should.be.bignumber.equal(ZERO)
|
||||
accBalance.should.be.bignumber.equal(HALF_ETHER)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue