Skip to content

Commit

Permalink
Merge the develop branch to the master branch, preparation to v6.0.0-rc0
Browse files Browse the repository at this point in the history
This set of changes includes the following improvements and fixes:
  * [Improvement] AMB Home-to-Foreign async calls (#570), closes #492
  * [Improvement] Add GSN support for erc20-to-native bridge mode (#571)
  * [Fix] Fix issues with packages versions and linter (#600)
  * [Other] Bump package version before 6.0.0-rc0 (#598)
  • Loading branch information
akolotov authored May 7, 2021
2 parents 7edfe77 + de37e25 commit 004d466
Show file tree
Hide file tree
Showing 31 changed files with 18,383 additions and 47,919 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ deploy/*.env*
!deploy/.env.example
upgrade/*.env*
!upgrade/.env.example
.vscode
72 changes: 72 additions & 0 deletions contracts/gsn/BasePaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier:MIT
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;

import "../upgradeable_contracts/Ownable.sol";

import "./interfaces/GsnTypes.sol";
import "./interfaces/IPaymaster.sol";
import "./interfaces/IRelayHub.sol";
import "./utils/GsnEip712Library.sol";
import "./forwarder/IForwarder.sol";

/**
* Abstract base class to be inherited by a concrete Paymaster
* A subclass must implement:
* - preRelayedCall
* - postRelayedCall
*/
contract BasePaymaster is IPaymaster, Ownable {
IRelayHub internal relayHub;
IForwarder public trustedForwarder;

function getHubAddr() public view returns (address) {
return address(relayHub);
}

//overhead of forwarder verify+signature, plus hub overhead.
uint256 public constant FORWARDER_HUB_OVERHEAD = 50000;

//These parameters are documented in IPaymaster.GasLimits
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;

function getGasLimits() external view returns (IPaymaster.GasLimits) {
return
IPaymaster.GasLimits(PAYMASTER_ACCEPTANCE_BUDGET, PRE_RELAYED_CALL_GAS_LIMIT, POST_RELAYED_CALL_GAS_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");
GsnEip712Library.verifyForwarderTrusted(relayRequest);
}

/*
* 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");
_;
}

function setRelayHub(IRelayHub hub) public onlyOwner {
relayHub = hub;
}

function setTrustedForwarder(IForwarder forwarder) public onlyOwner {
trustedForwarder = forwarder;
}

// check current deposit on relay hub.
function getRelayHubDeposit() public view returns (uint256) {
return relayHub.balanceOf(address(this));
}

// withdraw deposit from relayHub
function withdrawRelayHubDepositTo(uint256 amount, address target) public onlyOwner {
relayHub.withdraw(amount, target);
}
}
57 changes: 57 additions & 0 deletions contracts/gsn/BaseRelayRecipient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier:MIT
// solhint-disable no-inline-assembly
pragma solidity 0.4.24;

import "./interfaces/IRelayRecipient.sol";

/**
* A base contract to be inherited by any contract that want to receive relayed transactions
* A subclass must use "_msgSender()" instead of "msg.sender"
*/
contract BaseRelayRecipient is IRelayRecipient {
/**
* return the sender of this call.
* if the call came through our trusted forwarder, return the original sender.
* otherwise, return `msg.sender`.
* should be used in the contract anywhere instead of msg.sender
*/
function _msgSender() internal view returns (address ret) {
if (msg.data.length >= 24 && isTrustedForwarder(msg.sender)) {
// At this point we know that the sender is a trusted forwarder,
// so we trust that the last bytes of msg.data are the verified sender address.
// extract sender address from the end of msg.data
assembly {
ret := shr(96, calldataload(sub(calldatasize, 20)))
}
} else {
return msg.sender;
}
}

/**
* return the msg.data of this call.
* if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
* of the msg.data - so this method will strip those 20 bytes off.
* otherwise, return `msg.data`
* should be used in the contract instead of msg.data, where the difference matters (e.g. when explicitly
* signing or hashing the
*/
function _msgData() internal view returns (bytes memory ret) {
if (msg.data.length >= 24 && isTrustedForwarder(msg.sender)) {
// At this point we know that the sender is a trusted forwarder,
// we copy the msg.data , except the last 20 bytes (and update the total length)
assembly {
let ptr := mload(0x40)
// copy only size-20 bytes
let size := sub(calldatasize, 20)
// structure RLP data as <offset> <length> <bytes>
mstore(ptr, 0x20)
mstore(add(ptr, 32), size)
calldatacopy(add(ptr, 64), 0, size)
return(ptr, add(size, 64))
}
} else {
return msg.data;
}
}
}
68 changes: 68 additions & 0 deletions contracts/gsn/forwarder/IForwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier:MIT
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;

contract IForwarder {
struct ForwardRequest {
address from;
address to;
uint256 value;
uint256 gas;
uint256 nonce;
bytes data;
}

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.
*/
function verify(
ForwardRequest forwardRequest,
bytes32 domainSeparator,
bytes32 requestTypeHash,
bytes suffixData,
bytes signature
) external view;

/**
* execute a transaction
* @param forwardRequest - all transaction parameters
* @param domainSeparator - domain used when signing this request
* @param requestTypeHash - request type used when signing this request.
* @param suffixData - the extension data used when signing this request.
* @param signature - signature to validate.
*
* the transaction is verified, and then executed.
* the success and ret of "call" are returned.
* This method would revert only verification errors. target errors
* are reported using the returned "success" and ret string
*/
function execute(
ForwardRequest forwardRequest,
bytes32 domainSeparator,
bytes32 requestTypeHash,
bytes suffixData,
bytes signature
) external payable returns (bool success, bytes memory ret);

/**
* 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.
*/
function registerRequestType(string typeName, string typeSuffix) external;

/**
* Register a new domain separator.
* The domain separator must have the following fields: name,version,chainId, verifyingContract.
* the chainId is the current network's chainId, and the verifyingContract is this forwarder.
* This method is given the domain name and version to create and register the domain separator value.
* @param name the domain's display name
* @param version the domain/protocol version
*/
function registerDomainSeparator(string name, string version) external;
}
23 changes: 23 additions & 0 deletions contracts/gsn/interfaces/GsnTypes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier:MIT
pragma solidity 0.4.24;

import "../forwarder/IForwarder.sol";

contract GsnTypes {
struct RelayData {
uint256 gasPrice;
uint256 pctRelayFee;
uint256 baseRelayFee;
address relayWorker;
address paymaster;
bytes paymasterData;
uint256 clientId;
address forwarder;
}

//note: must start with the ForwardRequest to be an extension of the generic forwarder
struct RelayRequest {
IForwarder.ForwardRequest request;
RelayData relayData;
}
}
11 changes: 11 additions & 0 deletions contracts/gsn/interfaces/IKnowForwarderAddress.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier:MIT
pragma solidity 0.4.24;

interface IKnowForwarderAddress {
/**
* return the forwarder we trust to forward relayed transactions to us.
* the forwarder is required to verify the sender's signature, and verify
* the call is not a replay.
*/
function getTrustedForwarder() external view returns (address);
}
112 changes: 112 additions & 0 deletions contracts/gsn/interfaces/IPaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier:MIT
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;

import "./GsnTypes.sol";

contract IPaymaster {
/**
* @param acceptanceBudget -
* Paymaster expected gas budget to accept (or reject) a request
* This a gas required by any calculations that might need to reject the
* transaction, by preRelayedCall, forwarder and recipient.
* See value in BasePaymaster.PAYMASTER_ACCEPTANCE_BUDGET
* Transaction that gets rejected above that gas usage is on the paymaster's expense.
* As long this value is above preRelayedCallGasLimit (see defaults in BasePaymaster), the
* Paymaster is guaranteed it will never pay for rejected transactions.
* If this value is below preRelayedCallGasLimt, it might might make Paymaster open to a "griefing" attack.
*
* Specifying value too high might make the call rejected by some relayers.
*
* From a Relay's point of view, this is the highest gas value a paymaster might "grief" the relay,
* since the paymaster will pay anything above that (regardless if the tx reverts)
*
* @param preRelayedCallGasLimit - the max gas usage of preRelayedCall. any revert (including OOG)
* of preRelayedCall is a reject by the paymaster.
* as long as acceptanceBudget is above preRelayedCallGasLimit, any such revert (including OOG)
* is not payed by the paymaster.
* @param postRelayedCallGasLimit - the max gas usage of postRelayedCall.
* 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 {
uint256 acceptanceBudget;
uint256 preRelayedCallGasLimit;
uint256 postRelayedCallGasLimit;
}

/**
* Return the GasLimits constants used by the Paymaster.
*/
function getGasLimits() external view returns (GasLimits memory limits);

/**
* return the relayHub of this contract.
*/
function getHubAddr() public view returns (address);

/**
* 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);

/**
* Called by Relay (and RelayHub), to validate if the paymaster agrees to pay for this call.
*
* MUST be protected with relayHubOnly() in case it modifies state.
*
* The Paymaster rejects by the following "revert" operations
* - preRelayedCall() method reverts
* - the forwarder reverts because of nonce or signature error
* - the paymaster returned "rejectOnRecipientRevert", and the recipient contract reverted.
* In any of the above cases, all paymaster calls (and recipient call) are reverted.
* In any other case, the paymaster agrees to pay for the gas cost of the transaction (note
* that this includes also postRelayedCall revert)
*
* The rejectOnRecipientRevert flag means the Paymaster "delegate" the rejection to the recipient
* code. It also means the Paymaster trust the recipient to reject fast: both preRelayedCall,
* forwarder check and receipient checks must fit into the GasLimits.acceptanceBudget,
* otherwise the TX is paid by the Paymaster.
*
* @param relayRequest - the full relay request structure
* @param signature - user's EIP712-compatible signature of the {@link relayRequest}.
* 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},
* 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:
* a context to be passed to postRelayedCall
* rejectOnRecipientRevert - TRUE if paymaster want to reject the TX if the recipient reverts.
* FALSE means that rejects by the recipient will be completed on chain, and paid by the paymaster.
* (note that in the latter case, the preRelayedCall and postRelayedCall are not reverted).
*/
function preRelayedCall(
GsnTypes.RelayRequest relayRequest,
bytes signature,
bytes approvalData,
uint256 maxPossibleGas
) public returns (bytes memory context, bool rejectOnRecipientRevert);

/**
* This method is called after the actual relayed function call.
* It may be used to record the transaction (e.g. charge the caller by some contract logic) for this call.
*
* MUST be protected with relayHubOnly() in case it modifies state.
*
* @param context - the call context, as returned by the preRelayedCall
* @param success - true if the relayed call succeeded, false if it reverted
* @param gasUseWithoutPost - the actual amount of gas used by the entire transaction, EXCEPT
* the gas used by the postRelayedCall itself.
* @param relayData - the relay params of the request. can be used by relayHub.calculateCharge()
*
* Revert in this functions causes a revert of the client's relayed call (and preRelayedCall(), but the Paymaster
* is still committed to pay the relay for the entire transaction.
*/
function postRelayedCall(bytes context, bool success, uint256 gasUseWithoutPost, GsnTypes.RelayData relayData)
public;

function versionPaymaster() external view returns (string memory);
}
Loading

0 comments on commit 004d466

Please sign in to comment.