From d1158ea68c597075a5aec4a77a9c16f061beffd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Wed, 14 Aug 2019 09:21:38 -0300 Subject: [PATCH] GSN compatibility (#1880) * switch to using Context internally * add context import * Add smoke test to make sure enabling GSN support works * Update test/GSN/ERC721GSNRecipientMock.test.js Co-Authored-By: Francisco Giordano * Upgrade truffle * add missing awaits * Revert "Upgrade truffle" This reverts commit f9b0ba9019650fdbf8919a33e6fba834689e6b04. --- .editorconfig | 1 + contracts/access/roles/CapperRole.sol | 9 +- contracts/access/roles/MinterRole.sol | 9 +- contracts/access/roles/PauserRole.sol | 9 +- contracts/access/roles/SignerRole.sol | 9 +- contracts/access/roles/WhitelistAdminRole.sol | 9 +- contracts/access/roles/WhitelistedRole.sol | 7 +- contracts/crowdsale/Crowdsale.sol | 7 +- .../distribution/RefundableCrowdsale.sol | 5 +- contracts/drafts/SignatureBouncer.sol | 19 +- contracts/examples/SimpleToken.sol | 7 +- contracts/lifecycle/Pausable.sol | 7 +- contracts/mocks/ERC721GSNRecipientMock.sol | 18 + contracts/mocks/ERC777Mock.sol | 5 +- contracts/mocks/ERC777SenderRecipientMock.sol | 7 +- contracts/mocks/ReentrancyAttack.sol | 5 +- contracts/mocks/SafeERC20Helper.sol | 13 +- contracts/ownership/Ownable.sol | 7 +- contracts/ownership/Secondary.sol | 7 +- contracts/payment/PaymentSplitter.sol | 5 +- contracts/token/ERC20/ERC20.sol | 15 +- contracts/token/ERC20/ERC20Burnable.sol | 5 +- contracts/token/ERC721/ERC721.sol | 19 +- contracts/token/ERC721/ERC721Burnable.sol | 5 +- contracts/token/ERC721/ERC721Enumerable.sol | 3 +- contracts/token/ERC721/ERC721Metadata.sol | 3 +- contracts/token/ERC777/ERC777.sol | 37 +- package-lock.json | 453 +++++++++++++++++- package.json | 2 +- test/GSN/ERC721GSNRecipientMock.test.js | 46 ++ 30 files changed, 645 insertions(+), 108 deletions(-) create mode 100644 contracts/mocks/ERC721GSNRecipientMock.sol create mode 100644 test/GSN/ERC721GSNRecipientMock.test.js diff --git a/.editorconfig b/.editorconfig index e885a65c4..fe678a6cb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,7 @@ root = true [*] charset = utf-8 end_of_line = lf +indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true diff --git a/contracts/access/roles/CapperRole.sol b/contracts/access/roles/CapperRole.sol index 7c1af8a8c..2b239c1f8 100644 --- a/contracts/access/roles/CapperRole.sol +++ b/contracts/access/roles/CapperRole.sol @@ -1,8 +1,9 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; -contract CapperRole { +contract CapperRole is Context { using Roles for Roles.Role; event CapperAdded(address indexed account); @@ -11,11 +12,11 @@ contract CapperRole { Roles.Role private _cappers; constructor () internal { - _addCapper(msg.sender); + _addCapper(_msgSender()); } modifier onlyCapper() { - require(isCapper(msg.sender), "CapperRole: caller does not have the Capper role"); + require(isCapper(_msgSender()), "CapperRole: caller does not have the Capper role"); _; } @@ -28,7 +29,7 @@ contract CapperRole { } function renounceCapper() public { - _removeCapper(msg.sender); + _removeCapper(_msgSender()); } function _addCapper(address account) internal { diff --git a/contracts/access/roles/MinterRole.sol b/contracts/access/roles/MinterRole.sol index a84367cf3..f881e3a73 100644 --- a/contracts/access/roles/MinterRole.sol +++ b/contracts/access/roles/MinterRole.sol @@ -1,8 +1,9 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; -contract MinterRole { +contract MinterRole is Context { using Roles for Roles.Role; event MinterAdded(address indexed account); @@ -11,11 +12,11 @@ contract MinterRole { Roles.Role private _minters; constructor () internal { - _addMinter(msg.sender); + _addMinter(_msgSender()); } modifier onlyMinter() { - require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role"); + require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role"); _; } @@ -28,7 +29,7 @@ contract MinterRole { } function renounceMinter() public { - _removeMinter(msg.sender); + _removeMinter(_msgSender()); } function _addMinter(address account) internal { diff --git a/contracts/access/roles/PauserRole.sol b/contracts/access/roles/PauserRole.sol index f149139aa..6ad238402 100644 --- a/contracts/access/roles/PauserRole.sol +++ b/contracts/access/roles/PauserRole.sol @@ -1,8 +1,9 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; -contract PauserRole { +contract PauserRole is Context { using Roles for Roles.Role; event PauserAdded(address indexed account); @@ -11,11 +12,11 @@ contract PauserRole { Roles.Role private _pausers; constructor () internal { - _addPauser(msg.sender); + _addPauser(_msgSender()); } modifier onlyPauser() { - require(isPauser(msg.sender), "PauserRole: caller does not have the Pauser role"); + require(isPauser(_msgSender()), "PauserRole: caller does not have the Pauser role"); _; } @@ -28,7 +29,7 @@ contract PauserRole { } function renouncePauser() public { - _removePauser(msg.sender); + _removePauser(_msgSender()); } function _addPauser(address account) internal { diff --git a/contracts/access/roles/SignerRole.sol b/contracts/access/roles/SignerRole.sol index 468d0aef1..50535bead 100644 --- a/contracts/access/roles/SignerRole.sol +++ b/contracts/access/roles/SignerRole.sol @@ -1,8 +1,9 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; -contract SignerRole { +contract SignerRole is Context { using Roles for Roles.Role; event SignerAdded(address indexed account); @@ -11,11 +12,11 @@ contract SignerRole { Roles.Role private _signers; constructor () internal { - _addSigner(msg.sender); + _addSigner(_msgSender()); } modifier onlySigner() { - require(isSigner(msg.sender), "SignerRole: caller does not have the Signer role"); + require(isSigner(_msgSender()), "SignerRole: caller does not have the Signer role"); _; } @@ -28,7 +29,7 @@ contract SignerRole { } function renounceSigner() public { - _removeSigner(msg.sender); + _removeSigner(_msgSender()); } function _addSigner(address account) internal { diff --git a/contracts/access/roles/WhitelistAdminRole.sol b/contracts/access/roles/WhitelistAdminRole.sol index 8c16928fe..b26f4ed6b 100644 --- a/contracts/access/roles/WhitelistAdminRole.sol +++ b/contracts/access/roles/WhitelistAdminRole.sol @@ -1,12 +1,13 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; /** * @title WhitelistAdminRole * @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts. */ -contract WhitelistAdminRole { +contract WhitelistAdminRole is Context { using Roles for Roles.Role; event WhitelistAdminAdded(address indexed account); @@ -15,11 +16,11 @@ contract WhitelistAdminRole { Roles.Role private _whitelistAdmins; constructor () internal { - _addWhitelistAdmin(msg.sender); + _addWhitelistAdmin(_msgSender()); } modifier onlyWhitelistAdmin() { - require(isWhitelistAdmin(msg.sender), "WhitelistAdminRole: caller does not have the WhitelistAdmin role"); + require(isWhitelistAdmin(_msgSender()), "WhitelistAdminRole: caller does not have the WhitelistAdmin role"); _; } @@ -32,7 +33,7 @@ contract WhitelistAdminRole { } function renounceWhitelistAdmin() public { - _removeWhitelistAdmin(msg.sender); + _removeWhitelistAdmin(_msgSender()); } function _addWhitelistAdmin(address account) internal { diff --git a/contracts/access/roles/WhitelistedRole.sol b/contracts/access/roles/WhitelistedRole.sol index 326d3343a..5d749bfd3 100644 --- a/contracts/access/roles/WhitelistedRole.sol +++ b/contracts/access/roles/WhitelistedRole.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../Roles.sol"; import "./WhitelistAdminRole.sol"; @@ -9,7 +10,7 @@ import "./WhitelistAdminRole.sol"; * crowdsale). This role is special in that the only accounts that can add it are WhitelistAdmins (who can also remove * it), and not Whitelisteds themselves. */ -contract WhitelistedRole is WhitelistAdminRole { +contract WhitelistedRole is Context, WhitelistAdminRole { using Roles for Roles.Role; event WhitelistedAdded(address indexed account); @@ -18,7 +19,7 @@ contract WhitelistedRole is WhitelistAdminRole { Roles.Role private _whitelisteds; modifier onlyWhitelisted() { - require(isWhitelisted(msg.sender), "WhitelistedRole: caller does not have the Whitelisted role"); + require(isWhitelisted(_msgSender()), "WhitelistedRole: caller does not have the Whitelisted role"); _; } @@ -35,7 +36,7 @@ contract WhitelistedRole is WhitelistAdminRole { } function renounceWhitelisted() public { - _removeWhitelisted(msg.sender); + _removeWhitelisted(_msgSender()); } function _addWhitelisted(address account) internal { diff --git a/contracts/crowdsale/Crowdsale.sol b/contracts/crowdsale/Crowdsale.sol index 03fda95a5..5aec96193 100644 --- a/contracts/crowdsale/Crowdsale.sol +++ b/contracts/crowdsale/Crowdsale.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../token/ERC20/IERC20.sol"; import "../math/SafeMath.sol"; import "../token/ERC20/SafeERC20.sol"; @@ -17,7 +18,7 @@ import "../utils/ReentrancyGuard.sol"; * the methods to add functionality. Consider using 'super' where appropriate to concatenate * behavior. */ -contract Crowdsale is ReentrancyGuard { +contract Crowdsale is Context, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -70,7 +71,7 @@ contract Crowdsale is ReentrancyGuard { * buyTokens directly when purchasing tokens from a contract. */ function () external payable { - buyTokens(msg.sender); + buyTokens(_msgSender()); } /** @@ -118,7 +119,7 @@ contract Crowdsale is ReentrancyGuard { _weiRaised = _weiRaised.add(weiAmount); _processPurchase(beneficiary, tokens); - emit TokensPurchased(msg.sender, beneficiary, weiAmount, tokens); + emit TokensPurchased(_msgSender(), beneficiary, weiAmount, tokens); _updatePurchasingState(beneficiary, weiAmount); diff --git a/contracts/crowdsale/distribution/RefundableCrowdsale.sol b/contracts/crowdsale/distribution/RefundableCrowdsale.sol index d91044131..9c42606fe 100644 --- a/contracts/crowdsale/distribution/RefundableCrowdsale.sol +++ b/contracts/crowdsale/distribution/RefundableCrowdsale.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "../../math/SafeMath.sol"; import "./FinalizableCrowdsale.sol"; import "../../payment/escrow/RefundEscrow.sol"; @@ -14,7 +15,7 @@ import "../../payment/escrow/RefundEscrow.sol"; * the goal is unlikely to be met, they sell their tokens (possibly at a discount). The attacker will be refunded when * the crowdsale is finalized, and the users that purchased from them will be left with worthless tokens. */ -contract RefundableCrowdsale is FinalizableCrowdsale { +contract RefundableCrowdsale is Context, FinalizableCrowdsale { using SafeMath for uint256; // minimum amount of funds to be raised in weis @@ -77,6 +78,6 @@ contract RefundableCrowdsale is FinalizableCrowdsale { * @dev Overrides Crowdsale fund forwarding, sending funds to escrow. */ function _forwardFunds() internal { - _escrow.deposit.value(msg.value)(msg.sender); + _escrow.deposit.value(msg.value)(_msgSender()); } } diff --git a/contracts/drafts/SignatureBouncer.sol b/contracts/drafts/SignatureBouncer.sol index 48d3cc493..239a79248 100644 --- a/contracts/drafts/SignatureBouncer.sol +++ b/contracts/drafts/SignatureBouncer.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../access/roles/SignerRole.sol"; import "../cryptography/ECDSA.sol"; @@ -34,7 +35,7 @@ import "../cryptography/ECDSA.sol"; * the data in the signature much more complex. * See https://ethereum.stackexchange.com/a/50616 for more details. */ -contract SignatureBouncer is SignerRole { +contract SignatureBouncer is Context, SignerRole { using ECDSA for bytes32; // Function selectors are 4 bytes long, as documented in @@ -51,7 +52,7 @@ contract SignatureBouncer is SignerRole { * @dev Requires that a valid signature of a signer was provided. */ modifier onlyValidSignature(bytes memory signature) { - require(_isValidSignature(msg.sender, signature), "SignatureBouncer: invalid signature for caller"); + require(_isValidSignature(_msgSender(), signature), "SignatureBouncer: invalid signature for caller"); _; } @@ -60,7 +61,7 @@ contract SignatureBouncer is SignerRole { */ modifier onlyValidSignatureAndMethod(bytes memory signature) { // solhint-disable-next-line max-line-length - require(_isValidSignatureAndMethod(msg.sender, signature), "SignatureBouncer: invalid signature for caller and method"); + require(_isValidSignatureAndMethod(_msgSender(), signature), "SignatureBouncer: invalid signature for caller and method"); _; } @@ -69,7 +70,7 @@ contract SignatureBouncer is SignerRole { */ modifier onlyValidSignatureAndData(bytes memory signature) { // solhint-disable-next-line max-line-length - require(_isValidSignatureAndData(msg.sender, signature), "SignatureBouncer: invalid signature for caller and data"); + require(_isValidSignatureAndData(_msgSender(), signature), "SignatureBouncer: invalid signature for caller and data"); _; } @@ -86,9 +87,10 @@ contract SignatureBouncer is SignerRole { * @return bool */ function _isValidSignatureAndMethod(address account, bytes memory signature) internal view returns (bool) { + bytes memory msgData = _msgData(); bytes memory data = new bytes(_METHOD_ID_SIZE); for (uint256 i = 0; i < data.length; i++) { - data[i] = msg.data[i]; + data[i] = msgData[i]; } return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature); } @@ -99,11 +101,12 @@ contract SignatureBouncer is SignerRole { * @return bool */ function _isValidSignatureAndData(address account, bytes memory signature) internal view returns (bool) { - require(msg.data.length > _SIGNATURE_SIZE, "SignatureBouncer: data is too short"); + bytes memory msgData = _msgData(); + require(msgData.length > _SIGNATURE_SIZE, "SignatureBouncer: data is too short"); - bytes memory data = new bytes(msg.data.length - _SIGNATURE_SIZE); + bytes memory data = new bytes(msgData.length - _SIGNATURE_SIZE); for (uint256 i = 0; i < data.length; i++) { - data[i] = msg.data[i]; + data[i] = msgData[i]; } return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature); diff --git a/contracts/examples/SimpleToken.sol b/contracts/examples/SimpleToken.sol index 355d959bf..7c0e4778c 100644 --- a/contracts/examples/SimpleToken.sol +++ b/contracts/examples/SimpleToken.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../token/ERC20/ERC20.sol"; import "../token/ERC20/ERC20Detailed.sol"; @@ -9,12 +10,12 @@ import "../token/ERC20/ERC20Detailed.sol"; * Note they can later distribute these tokens as they wish using `transfer` and other * `ERC20` functions. */ -contract SimpleToken is ERC20, ERC20Detailed { +contract SimpleToken is Context, ERC20, ERC20Detailed { /** - * @dev Constructor that gives msg.sender all of existing tokens. + * @dev Constructor that gives _msgSender() all of existing tokens. */ constructor () public ERC20Detailed("SimpleToken", "SIM", 18) { - _mint(msg.sender, 10000 * (10 ** uint256(decimals()))); + _mint(_msgSender(), 10000 * (10 ** uint256(decimals()))); } } diff --git a/contracts/lifecycle/Pausable.sol b/contracts/lifecycle/Pausable.sol index ab74d47fd..77d11e33b 100644 --- a/contracts/lifecycle/Pausable.sol +++ b/contracts/lifecycle/Pausable.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../access/roles/PauserRole.sol"; /** @@ -11,7 +12,7 @@ import "../access/roles/PauserRole.sol"; * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ -contract Pausable is PauserRole { +contract Pausable is Context, PauserRole { /** * @dev Emitted when the pause is triggered by a pauser (`account`). */ @@ -60,7 +61,7 @@ contract Pausable is PauserRole { */ function pause() public onlyPauser whenNotPaused { _paused = true; - emit Paused(msg.sender); + emit Paused(_msgSender()); } /** @@ -68,6 +69,6 @@ contract Pausable is PauserRole { */ function unpause() public onlyPauser whenPaused { _paused = false; - emit Unpaused(msg.sender); + emit Unpaused(_msgSender()); } } diff --git a/contracts/mocks/ERC721GSNRecipientMock.sol b/contracts/mocks/ERC721GSNRecipientMock.sol new file mode 100644 index 000000000..23406573f --- /dev/null +++ b/contracts/mocks/ERC721GSNRecipientMock.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.5.0; + +import "../token/ERC721/ERC721.sol"; +import "../GSN/GSNRecipient.sol"; +import "../GSN/bouncers/GSNBouncerSignature.sol"; + +/** + * @title ERC721GSNRecipientMock + * A simple ERC721 mock that has GSN support enabled + */ +contract ERC721GSNRecipientMock is ERC721, GSNRecipient, GSNBouncerSignature { + constructor(address trustedSigner) public GSNBouncerSignature(trustedSigner) { } + // solhint-disable-previous-line no-empty-blocks + + function mint(uint256 tokenId) public { + _mint(_msgSender(), tokenId); + } +} diff --git a/contracts/mocks/ERC777Mock.sol b/contracts/mocks/ERC777Mock.sol index 5df02dd46..314be2ea7 100644 --- a/contracts/mocks/ERC777Mock.sol +++ b/contracts/mocks/ERC777Mock.sol @@ -1,8 +1,9 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../token/ERC777/ERC777.sol"; -contract ERC777Mock is ERC777 { +contract ERC777Mock is Context, ERC777 { constructor( address initialHolder, uint256 initialBalance, @@ -10,7 +11,7 @@ contract ERC777Mock is ERC777 { string memory symbol, address[] memory defaultOperators ) public ERC777(name, symbol, defaultOperators) { - _mint(msg.sender, initialHolder, initialBalance, "", ""); + _mint(_msgSender(), initialHolder, initialBalance, "", ""); } function mintInternal ( diff --git a/contracts/mocks/ERC777SenderRecipientMock.sol b/contracts/mocks/ERC777SenderRecipientMock.sol index 7ce0513f7..a1db31c55 100644 --- a/contracts/mocks/ERC777SenderRecipientMock.sol +++ b/contracts/mocks/ERC777SenderRecipientMock.sol @@ -1,12 +1,13 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../token/ERC777/IERC777.sol"; import "../token/ERC777/IERC777Sender.sol"; import "../token/ERC777/IERC777Recipient.sol"; import "../introspection/IERC1820Registry.sol"; import "../introspection/ERC1820Implementer.sol"; -contract ERC777SenderRecipientMock is IERC777Sender, IERC777Recipient, ERC1820Implementer { +contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { event TokensToSendCalled( address operator, address from, @@ -51,7 +52,7 @@ contract ERC777SenderRecipientMock is IERC777Sender, IERC777Recipient, ERC1820Im revert(); } - IERC777 token = IERC777(msg.sender); + IERC777 token = IERC777(_msgSender()); uint256 fromBalance = token.balanceOf(from); // when called due to burn, to will be the zero address, which will have a balance of 0 @@ -82,7 +83,7 @@ contract ERC777SenderRecipientMock is IERC777Sender, IERC777Recipient, ERC1820Im revert(); } - IERC777 token = IERC777(msg.sender); + IERC777 token = IERC777(_msgSender()); uint256 fromBalance = token.balanceOf(from); // when called due to burn, to will be the zero address, which will have a balance of 0 diff --git a/contracts/mocks/ReentrancyAttack.sol b/contracts/mocks/ReentrancyAttack.sol index 09d52a260..2b8b6be36 100644 --- a/contracts/mocks/ReentrancyAttack.sol +++ b/contracts/mocks/ReentrancyAttack.sol @@ -1,9 +1,10 @@ pragma solidity ^0.5.0; -contract ReentrancyAttack { +import "../GSN/Context.sol"; +contract ReentrancyAttack is Context { function callSender(bytes4 data) public { // solhint-disable-next-line avoid-low-level-calls - (bool success,) = msg.sender.call(abi.encodeWithSelector(data)); + (bool success,) = _msgSender().call(abi.encodeWithSelector(data)); require(success, "ReentrancyAttack: failed call"); } } diff --git a/contracts/mocks/SafeERC20Helper.sol b/contracts/mocks/SafeERC20Helper.sol index b36dc35a7..8a1590262 100644 --- a/contracts/mocks/SafeERC20Helper.sol +++ b/contracts/mocks/SafeERC20Helper.sol @@ -1,9 +1,10 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../token/ERC20/IERC20.sol"; import "../token/ERC20/SafeERC20.sol"; -contract ERC20ReturnFalseMock { +contract ERC20ReturnFalseMock is Context { uint256 private _allowance; // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, @@ -31,7 +32,7 @@ contract ERC20ReturnFalseMock { } } -contract ERC20ReturnTrueMock { +contract ERC20ReturnTrueMock is Context { mapping (address => uint256) private _allowances; // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, @@ -54,7 +55,7 @@ contract ERC20ReturnTrueMock { } function setAllowance(uint256 allowance_) public { - _allowances[msg.sender] = allowance_; + _allowances[_msgSender()] = allowance_; } function allowance(address owner, address) public view returns (uint256) { @@ -62,7 +63,7 @@ contract ERC20ReturnTrueMock { } } -contract ERC20NoReturnMock { +contract ERC20NoReturnMock is Context { mapping (address => uint256) private _allowances; // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, @@ -82,7 +83,7 @@ contract ERC20NoReturnMock { } function setAllowance(uint256 allowance_) public { - _allowances[msg.sender] = allowance_; + _allowances[_msgSender()] = allowance_; } function allowance(address owner, address) public view returns (uint256) { @@ -90,7 +91,7 @@ contract ERC20NoReturnMock { } } -contract SafeERC20Wrapper { +contract SafeERC20Wrapper is Context { using SafeERC20 for IERC20; IERC20 private _token; diff --git a/contracts/ownership/Ownable.sol b/contracts/ownership/Ownable.sol index 407646dc7..a1a84931b 100644 --- a/contracts/ownership/Ownable.sol +++ b/contracts/ownership/Ownable.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to @@ -9,7 +10,7 @@ pragma solidity ^0.5.0; * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ -contract Ownable { +contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); @@ -18,7 +19,7 @@ contract Ownable { * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { - _owner = msg.sender; + _owner = _msgSender(); emit OwnershipTransferred(address(0), _owner); } @@ -41,7 +42,7 @@ contract Ownable { * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { - return msg.sender == _owner; + return _msgSender() == _owner; } /** diff --git a/contracts/ownership/Secondary.sol b/contracts/ownership/Secondary.sol index b34be41fd..5cb1dda05 100644 --- a/contracts/ownership/Secondary.sol +++ b/contracts/ownership/Secondary.sol @@ -1,9 +1,10 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; /** * @dev A Secondary contract can only be used by its primary account (the one that created it). */ -contract Secondary { +contract Secondary is Context { address private _primary; /** @@ -17,7 +18,7 @@ contract Secondary { * @dev Sets the primary account to the one that is creating the Secondary contract. */ constructor () internal { - _primary = msg.sender; + _primary = _msgSender(); emit PrimaryTransferred(_primary); } @@ -25,7 +26,7 @@ contract Secondary { * @dev Reverts if called from any account other than the primary. */ modifier onlyPrimary() { - require(msg.sender == _primary, "Secondary: caller is not the primary account"); + require(_msgSender() == _primary, "Secondary: caller is not the primary account"); _; } diff --git a/contracts/payment/PaymentSplitter.sol b/contracts/payment/PaymentSplitter.sol index 58889826c..561af437c 100644 --- a/contracts/payment/PaymentSplitter.sol +++ b/contracts/payment/PaymentSplitter.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../GSN/Context.sol"; import "../math/SafeMath.sol"; /** @@ -15,7 +16,7 @@ import "../math/SafeMath.sol"; * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release} * function. */ -contract PaymentSplitter { +contract PaymentSplitter is Context { using SafeMath for uint256; event PayeeAdded(address account, uint256 shares); @@ -56,7 +57,7 @@ contract PaymentSplitter { * functions]. */ function () external payable { - emit PaymentReceived(msg.sender, msg.value); + emit PaymentReceived(_msgSender(), msg.value); } /** diff --git a/contracts/token/ERC20/ERC20.sol b/contracts/token/ERC20/ERC20.sol index 81f434925..b987e572a 100644 --- a/contracts/token/ERC20/ERC20.sol +++ b/contracts/token/ERC20/ERC20.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; @@ -27,7 +28,7 @@ import "../../math/SafeMath.sol"; * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ -contract ERC20 is IERC20 { +contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; @@ -59,7 +60,7 @@ contract ERC20 is IERC20 { * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public returns (bool) { - _transfer(msg.sender, recipient, amount); + _transfer(_msgSender(), recipient, amount); return true; } @@ -78,7 +79,7 @@ contract ERC20 is IERC20 { * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public returns (bool) { - _approve(msg.sender, spender, value); + _approve(_msgSender(), spender, value); return true; } @@ -96,7 +97,7 @@ contract ERC20 is IERC20 { */ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); - _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } @@ -113,7 +114,7 @@ contract ERC20 is IERC20 { * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } @@ -132,7 +133,7 @@ contract ERC20 is IERC20 { * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } @@ -224,6 +225,6 @@ contract ERC20 is IERC20 { */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); - _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: burn amount exceeds allowance")); + _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } } diff --git a/contracts/token/ERC20/ERC20Burnable.sol b/contracts/token/ERC20/ERC20Burnable.sol index 6226d46cd..e58b72b25 100644 --- a/contracts/token/ERC20/ERC20Burnable.sol +++ b/contracts/token/ERC20/ERC20Burnable.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./ERC20.sol"; /** @@ -7,14 +8,14 @@ import "./ERC20.sol"; * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ -contract ERC20Burnable is ERC20 { +contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public { - _burn(msg.sender, amount); + _burn(_msgSender(), amount); } /** diff --git a/contracts/token/ERC721/ERC721.sol b/contracts/token/ERC721/ERC721.sol index e4459ecad..e59187408 100644 --- a/contracts/token/ERC721/ERC721.sol +++ b/contracts/token/ERC721/ERC721.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "../../math/SafeMath.sol"; @@ -11,7 +12,7 @@ import "../../introspection/ERC165.sol"; * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ -contract ERC721 is ERC165, IERC721 { +contract ERC721 is Context, ERC165, IERC721 { using SafeMath for uint256; using Address for address; using Counters for Counters.Counter; @@ -88,7 +89,7 @@ contract ERC721 is ERC165, IERC721 { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); - require(msg.sender == owner || isApprovedForAll(owner, msg.sender), + require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); @@ -115,10 +116,10 @@ contract ERC721 is ERC165, IERC721 { * @param approved representing the status of the approval to be set */ function setApprovalForAll(address to, bool approved) public { - require(to != msg.sender, "ERC721: approve to caller"); + require(to != _msgSender(), "ERC721: approve to caller"); - _operatorApprovals[msg.sender][to] = approved; - emit ApprovalForAll(msg.sender, to, approved); + _operatorApprovals[_msgSender()][to] = approved; + emit ApprovalForAll(_msgSender(), to, approved); } /** @@ -141,7 +142,7 @@ contract ERC721 is ERC165, IERC721 { */ function transferFrom(address from, address to, uint256 tokenId) public { //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transferFrom(from, to, tokenId); } @@ -167,14 +168,14 @@ contract ERC721 is ERC165, IERC721 { * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. - * Requires the msg.sender to be the owner, approved, or operator + * Requires the _msgSender() to be the owner, approved, or operator * @param from current owner of the token * @param to address to receive the ownership of the given token ID * @param tokenId uint256 ID of the token to be transferred * @param _data bytes data to send along with a safe transfer check */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { - require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransferFrom(from, to, tokenId, _data); } @@ -330,7 +331,7 @@ contract ERC721 is ERC165, IERC721 { return true; } - bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); + bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data); return (retval == _ERC721_RECEIVED); } diff --git a/contracts/token/ERC721/ERC721Burnable.sol b/contracts/token/ERC721/ERC721Burnable.sol index 562a17e8e..e35796b9e 100644 --- a/contracts/token/ERC721/ERC721Burnable.sol +++ b/contracts/token/ERC721/ERC721Burnable.sol @@ -1,19 +1,20 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./ERC721.sol"; /** * @title ERC721 Burnable Token * @dev ERC721 Token that can be irreversibly burned (destroyed). */ -contract ERC721Burnable is ERC721 { +contract ERC721Burnable is Context, ERC721 { /** * @dev Burns a specific ERC721 token. * @param tokenId uint256 id of the ERC721 token to be burned. */ function burn(uint256 tokenId) public { //solhint-disable-next-line max-line-length - require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721Burnable: caller is not owner nor approved"); + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved"); _burn(tokenId); } } diff --git a/contracts/token/ERC721/ERC721Enumerable.sol b/contracts/token/ERC721/ERC721Enumerable.sol index eb25f8715..ff7a15843 100644 --- a/contracts/token/ERC721/ERC721Enumerable.sol +++ b/contracts/token/ERC721/ERC721Enumerable.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./IERC721Enumerable.sol"; import "./ERC721.sol"; import "../../introspection/ERC165.sol"; @@ -8,7 +9,7 @@ import "../../introspection/ERC165.sol"; * @title ERC-721 Non-Fungible Token with optional enumeration extension logic * @dev See https://eips.ethereum.org/EIPS/eip-721 */ -contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable { +contract ERC721Enumerable is Context, ERC165, ERC721, IERC721Enumerable { // Mapping from owner to list of owned token IDs mapping(address => uint256[]) private _ownedTokens; diff --git a/contracts/token/ERC721/ERC721Metadata.sol b/contracts/token/ERC721/ERC721Metadata.sol index ce263ddc7..26f4706bd 100644 --- a/contracts/token/ERC721/ERC721Metadata.sol +++ b/contracts/token/ERC721/ERC721Metadata.sol @@ -1,10 +1,11 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./ERC721.sol"; import "./IERC721Metadata.sol"; import "../../introspection/ERC165.sol"; -contract ERC721Metadata is ERC165, ERC721, IERC721Metadata { +contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata { // Token name string private _name; diff --git a/contracts/token/ERC777/ERC777.sol b/contracts/token/ERC777/ERC777.sol index feb511b8f..4adb0f847 100644 --- a/contracts/token/ERC777/ERC777.sol +++ b/contracts/token/ERC777/ERC777.sol @@ -1,5 +1,6 @@ pragma solidity ^0.5.0; +import "../../GSN/Context.sol"; import "./IERC777.sol"; import "./IERC777Recipient.sol"; import "./IERC777Sender.sol"; @@ -23,7 +24,7 @@ import "../../introspection/IERC1820Registry.sol"; * are no special restrictions in the amount of tokens that created, moved, or * destroyed. This makes integration with ERC20 applications seamless. */ -contract ERC777 is IERC777, IERC20 { +contract ERC777 is Context, IERC777, IERC20 { using SafeMath for uint256; using Address for address; @@ -134,7 +135,7 @@ contract ERC777 is IERC777, IERC20 { * Also emits a {Transfer} event for ERC20 compatibility. */ function send(address recipient, uint256 amount, bytes calldata data) external { - _send(msg.sender, msg.sender, recipient, amount, data, "", true); + _send(_msgSender(), _msgSender(), recipient, amount, data, "", true); } /** @@ -148,7 +149,7 @@ contract ERC777 is IERC777, IERC20 { function transfer(address recipient, uint256 amount) external returns (bool) { require(recipient != address(0), "ERC777: transfer to the zero address"); - address from = msg.sender; + address from = _msgSender(); _callTokensToSend(from, from, recipient, amount, "", ""); @@ -165,7 +166,7 @@ contract ERC777 is IERC777, IERC20 { * Also emits a {Transfer} event for ERC20 compatibility. */ function burn(uint256 amount, bytes calldata data) external { - _burn(msg.sender, msg.sender, amount, data, ""); + _burn(_msgSender(), _msgSender(), amount, data, ""); } /** @@ -184,30 +185,30 @@ contract ERC777 is IERC777, IERC20 { * @dev See {IERC777-authorizeOperator}. */ function authorizeOperator(address operator) external { - require(msg.sender != operator, "ERC777: authorizing self as operator"); + require(_msgSender() != operator, "ERC777: authorizing self as operator"); if (_defaultOperators[operator]) { - delete _revokedDefaultOperators[msg.sender][operator]; + delete _revokedDefaultOperators[_msgSender()][operator]; } else { - _operators[msg.sender][operator] = true; + _operators[_msgSender()][operator] = true; } - emit AuthorizedOperator(operator, msg.sender); + emit AuthorizedOperator(operator, _msgSender()); } /** * @dev See {IERC777-revokeOperator}. */ function revokeOperator(address operator) external { - require(operator != msg.sender, "ERC777: revoking self as operator"); + require(operator != _msgSender(), "ERC777: revoking self as operator"); if (_defaultOperators[operator]) { - _revokedDefaultOperators[msg.sender][operator] = true; + _revokedDefaultOperators[_msgSender()][operator] = true; } else { - delete _operators[msg.sender][operator]; + delete _operators[_msgSender()][operator]; } - emit RevokedOperator(operator, msg.sender); + emit RevokedOperator(operator, _msgSender()); } /** @@ -231,8 +232,8 @@ contract ERC777 is IERC777, IERC20 { ) external { - require(isOperatorFor(msg.sender, sender), "ERC777: caller is not an operator for holder"); - _send(msg.sender, sender, recipient, amount, data, operatorData, true); + require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); + _send(_msgSender(), sender, recipient, amount, data, operatorData, true); } /** @@ -241,8 +242,8 @@ contract ERC777 is IERC777, IERC20 { * Emits {Burned} and {Transfer} events. */ function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external { - require(isOperatorFor(msg.sender, account), "ERC777: caller is not an operator for holder"); - _burn(msg.sender, account, amount, data, operatorData); + require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); + _burn(_msgSender(), account, amount, data, operatorData); } /** @@ -262,7 +263,7 @@ contract ERC777 is IERC777, IERC20 { * Note that accounts cannot have allowance issued by their operators. */ function approve(address spender, uint256 value) external returns (bool) { - address holder = msg.sender; + address holder = _msgSender(); _approve(holder, spender, value); return true; } @@ -280,7 +281,7 @@ contract ERC777 is IERC777, IERC20 { require(recipient != address(0), "ERC777: transfer to the zero address"); require(holder != address(0), "ERC777: transfer from the zero address"); - address spender = msg.sender; + address spender = _msgSender(); _callTokensToSend(spender, holder, recipient, amount, "", ""); diff --git a/package-lock.json b/package-lock.json index dfb1158b6..d7f1dce3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -434,30 +434,469 @@ } }, "@openzeppelin/gsn-helpers": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@openzeppelin/gsn-helpers/-/gsn-helpers-0.1.4.tgz", - "integrity": "sha512-RELmLAI5FZMkmszBcryBLAvgi6YRTKjOyeLVZVNP+Cg0ZqfDGSGKG85yuplMgRyMtqh5wPDf5Ah5YiCFjrjVfw==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@openzeppelin/gsn-helpers/-/gsn-helpers-0.1.5.tgz", + "integrity": "sha512-KxrTHB36KRPPZs6DiVy4RxbZ39BfEFm+8ZicC51o1rJRHCV5kyjZ8uPa32oXsHt2Bl2Ve7iw1I26xwn/QClEzQ==", "dev": true, "requires": { "axios": "^0.19.0", "chai": "^4.2.0", "commander": "^2.20.0", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", "lodash": "^4.17.15", "sleep-promise": "^8.0.1", - "web3": "^1.2.0" + "tmp": "^0.1.0", + "web3": "^1.2.1" }, "dependencies": { + "@types/node": { + "version": "10.14.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.15.tgz", + "integrity": "sha512-CBR5avlLcu0YCILJiDIXeU2pTw7UK/NIxfC63m7d7CVamho1qDEzXKkOtEauQRPMy6MI8mLozth+JJkas7HY6g==", + "dev": true + }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "dev": true, + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + } + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz", + "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==", + "dev": true + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=", + "dev": true + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==", + "dev": true + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", + "dev": true + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=", + "dev": true + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "dev": true, + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "dev": true, + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "dev": true, + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-promievent": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz", + "integrity": "sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw==", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + } + }, + "web3-core-requestmanager": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz", + "integrity": "sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-providers-http": "1.2.1", + "web3-providers-ipc": "1.2.1", + "web3-providers-ws": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "dev": true, + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "dev": true, + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "dev": true, + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "dev": true, + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "dev": true, + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-providers-http": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.1.tgz", + "integrity": "sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ==", + "dev": true, + "requires": { + "web3-core-helpers": "1.2.1", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz", + "integrity": "sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA==", + "dev": true, + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-providers-ws": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz", + "integrity": "sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA==", + "dev": true, + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "dev": true, + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + }, + "websocket": { + "version": "github:web3-js/WebSocket-Node#b134a75541b5db59668df81c03e926cd5f325077", + "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "dev": true, + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "gulp": "^4.0.2", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } } } }, @@ -3847,6 +4286,12 @@ "once": "^1.4.0" } }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", diff --git a/package.json b/package.json index 2e3fe874c..a49a866e1 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "homepage": "https://github.com/OpenZeppelin/openzeppelin-contracts", "devDependencies": { - "@openzeppelin/gsn-helpers": "^0.1.4", + "@openzeppelin/gsn-helpers": "^0.1.5", "@openzeppelin/gsn-provider": "^0.1.4", "chai": "^4.2.0", "concurrently": "^4.1.0", diff --git a/test/GSN/ERC721GSNRecipientMock.test.js b/test/GSN/ERC721GSNRecipientMock.test.js new file mode 100644 index 000000000..867753341 --- /dev/null +++ b/test/GSN/ERC721GSNRecipientMock.test.js @@ -0,0 +1,46 @@ +const { constants, expectEvent } = require('openzeppelin-test-helpers'); +const { ZERO_ADDRESS } = constants; +const gsn = require('@openzeppelin/gsn-helpers'); +const { fixSignature } = require('../helpers/sign'); +const { utils: { toBN } } = require('web3'); + +const ERC721GSNRecipientMock = artifacts.require('ERC721GSNRecipientMock'); + +contract('ERC721GSNRecipient (integration)', function ([_, signer, sender]) { + const tokenId = '42'; + + beforeEach(async function () { + this.token = await ERC721GSNRecipientMock.new(signer); + }); + + async function testMintToken (token, from, tokenId, options = {}) { + const { tx } = await token.mint(tokenId, { from, ...options }); + await expectEvent.inTransaction(tx, ERC721GSNRecipientMock, 'Transfer', { from: ZERO_ADDRESS, to: from, tokenId }); + } + + context('when called directly', function () { + it('sender can mint tokens', async function () { + await testMintToken(this.token, sender, tokenId); + }); + }); + + context('when relay-called', function () { + beforeEach(async function () { + await gsn.fundRecipient(web3, { recipient: this.token.address }); + }); + + it('sender can mint tokens', async function () { + const approveFunction = async (data) => + fixSignature( + await web3.eth.sign( + web3.utils.soliditySha3( + // eslint-disable-next-line max-len + data.relayerAddress, data.from, data.encodedFunctionCall, toBN(data.txFee), toBN(data.gasPrice), toBN(data.gas), toBN(data.nonce), data.relayHubAddress, this.token.address + ), signer + ) + ); + + await testMintToken(this.token, sender, tokenId, { useGSN: true, approveFunction }); + }); + }); +});