Update GSN interface (#628)
This commit is contained in:
parent
7579b5249e
commit
93b1afbe67
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,13 +3,14 @@ require('array-flat-polyfill')
|
|||
|
||||
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')
|
||||
|
||||
// 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 {
|
||||
|
@ -32,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
|
||||
|
||||
|
@ -75,7 +77,7 @@ contract('ForeignBridge_ERC20_to_Native_GSN', async accounts => {
|
|||
await GsnTestEnvironment.stopGsn()
|
||||
})
|
||||
describe('#initialize', async () => {
|
||||
it('should initialize', async () => {
|
||||
it('should initialize paymaster', async () => {
|
||||
router = await UniswapRouterMock.new()
|
||||
foreignBridge = await XDaiForeignBridgeMock.new()
|
||||
|
||||
|
@ -284,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