Merge ERC20 charge recipients into a single one

pull/1844/head
Nicolás Venturo 6 years ago committed by Santiago Palladino
parent 6871bb7cfe
commit 108c998faa
  1. 59
      contracts/gsn/GSNRecipientERC20BuiltInCharge.sol
  2. 83
      contracts/gsn/GSNRecipientERC20Charge.sol

@ -1,59 +0,0 @@
pragma solidity ^0.5.0;
import "./GSNRecipientERC20Charge.sol";
import "../token/ERC20/ERC20.sol";
import "../token/ERC20/ERC20Detailed.sol";
import "../ownership/Secondary.sol";
contract GSNRecipientERC20BuiltInCharge is GSNRecipientERC20Charge {
constructor(string memory name, string memory symbol, uint8 decimals) public {
_setToken(new __unstable__ERC20PrimaryAdmin(name, symbol, decimals));
}
function _getEquivalentTokens(uint256 weiAmount) internal view returns (uint256) {
return weiAmount;
}
function _mintBuiltIn(address account, uint256 amount) internal {
// Solidity doesn't allow converting contract types, so we need to go through the intermediate address type
__unstable__ERC20PrimaryAdmin(address(token())).mint(account, amount);
}
}
/**
* @title __unstable__ERC20PrimaryAdmin
* @dev An ERC20 token owned by another contract, which has minting permissions and can use transferFrom to receive
* anyone's tokens. This contract is an internal helper for GSNRecipientERC20BuiltInCharge, and should not be used
* outside of this context.
*/
// solhint-disable-next-line contract-name-camelcase
contract __unstable__ERC20PrimaryAdmin is ERC20, ERC20Detailed, Secondary {
uint256 private constant UINT256_MAX = 2**256 - 1;
constructor(string memory name, string memory symbol, uint8 decimals) public ERC20Detailed(name, symbol, decimals) {
// solhint-disable-previous-line no-empty-blocks
}
// The primary account (GSNRecipientERC20BuiltInCharge) can mint tokens
function mint(address account, uint256 amount) public onlyPrimary {
_mint(account, amount);
}
// The primary account has 'infinite' allowance for all token holders
function allowance(address owner, address spender) public view returns (uint256) {
if (spender == primary()) {
return UINT256_MAX;
} else {
return super.allowance(owner, spender);
}
}
// Allowance for the primary account cannot be changed (it is always 'infinite')
function _approve(address owner, address spender, uint256 value) internal {
if (spender == primary()) {
return;
} else {
super._approve(owner, spender, value);
}
}
}

@ -2,31 +2,32 @@ pragma solidity ^0.5.0;
import "./GSNRecipient.sol"; import "./GSNRecipient.sol";
import "../math/SafeMath.sol"; import "../math/SafeMath.sol";
import "../token/ERC20/IERC20.sol"; import "../ownership/Secondary.sol";
import "../token/ERC20/SafeERC20.sol"; import "../token/ERC20/SafeERC20.sol";
import "../token/ERC20/ERC20.sol";
import "../token/ERC20/ERC20Detailed.sol";
contract GSNRecipientERC20Charge is GSNRecipient { contract GSNRecipientERC20Charge is GSNRecipient {
using SafeERC20 for IERC20; using SafeERC20 for __unstable__ERC20PrimaryAdmin;
using SafeMath for uint256; using SafeMath for uint256;
IERC20 private _token;
enum GSNRecipientERC20ChargeErrorCodes { enum GSNRecipientERC20ChargeErrorCodes {
INSUFFICIENT_BALANCE, INSUFFICIENT_BALANCE,
INSUFFICIENT_ALLOWANCE INSUFFICIENT_ALLOWANCE
} }
function token() public view returns (IERC20) { __unstable__ERC20PrimaryAdmin private _token;
return _token;
constructor(string memory name, string memory symbol, uint8 decimals) public {
_token = new __unstable__ERC20PrimaryAdmin(name, symbol, decimals);
} }
// We provide this as a function instead of a constructor to give more flexibility to derived contracts, which may function token() public view returns (IERC20) {
// not receive the token address as an argument, or even deploy it themselves. return IERC20(_token);
function _setToken(IERC20 token_) internal { }
require(address(_token) == address(0), "ERC20Migrator: token already set");
require(address(token_) != address(0), "ERC20Migrator: token is the zero address");
_token = token_; function _mintBuiltIn(address account, uint256 amount) internal {
_token.mint(account, amount);
} }
function acceptRelayedCall( function acceptRelayedCall(
@ -44,34 +45,64 @@ contract GSNRecipientERC20Charge is GSNRecipient {
view view
returns (uint256, bytes memory) returns (uint256, bytes memory)
{ {
uint256 maxTokenCharge = _getEquivalentTokens(maxPossibleCharge); if (_token.balanceOf(from) < maxPossibleCharge) {
if (_token.balanceOf(from) < maxTokenCharge) {
return (_declineRelayedCall(uint256(GSNRecipientERC20ChargeErrorCodes.INSUFFICIENT_BALANCE)), ""); return (_declineRelayedCall(uint256(GSNRecipientERC20ChargeErrorCodes.INSUFFICIENT_BALANCE)), "");
} else if (_token.allowance(from, address(this)) < maxTokenCharge) { } else if (_token.allowance(from, address(this)) < maxPossibleCharge) {
return (_declineRelayedCall(uint256(GSNRecipientERC20ChargeErrorCodes.INSUFFICIENT_ALLOWANCE)), ""); return (_declineRelayedCall(uint256(GSNRecipientERC20ChargeErrorCodes.INSUFFICIENT_ALLOWANCE)), "");
} }
return (_acceptRelayedCall(), abi.encode(from, maxTokenCharge)); return (_acceptRelayedCall(), abi.encode(from, maxPossibleCharge));
} }
function preRelayedCall(bytes calldata context) external returns (bytes32) { function preRelayedCall(bytes calldata context) external returns (bytes32) {
(address from, uint256 maxTokenCharge) = abi.decode(context, (address, uint256)); (address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
// The maximum token charge is pre-charged from the user // The maximum token charge is pre-charged from the user
_token.safeTransferFrom(from, address(this), maxTokenCharge); _token.safeTransferFrom(from, address(this), maxPossibleCharge);
} }
function postRelayedCall(bytes calldata context, bool, uint256 actualCharge, bytes32) external { function postRelayedCall(bytes calldata context, bool, uint256 actualCharge, bytes32) external {
(address from, uint256 maxTokenCharge) = abi.decode(context, (address, uint256)); (address from, uint256 maxPossibleCharge) = abi.decode(context, (address, uint256));
// After the relayed call has been executed and the actual charge estimated, the excess pre-charge is returned // After the relayed call has been executed and the actual charge estimated, the excess pre-charge is returned
_token.safeTransfer(from, maxTokenCharge.sub(actualCharge)); _token.safeTransfer(from, maxPossibleCharge.sub(actualCharge));
} }
}
/** /**
* @dev Converts an amount in wei to the equivalent number of tokens. This fucntion must be implemented by derived * @title __unstable__ERC20PrimaryAdmin
* contracts by specifying an exchange rate, which may e.g. be consulted from an oracle. * @dev An ERC20 token owned by another contract, which has minting permissions and can use transferFrom to receive
*/ * anyone's tokens. This contract is an internal helper for GSNRecipientERC20Charge, and should not be used
function _getEquivalentTokens(uint256 weiAmount) internal view returns (uint256); * outside of this context.
*/
// solhint-disable-next-line contract-name-camelcase
contract __unstable__ERC20PrimaryAdmin is ERC20, ERC20Detailed, Secondary {
uint256 private constant UINT256_MAX = 2**256 - 1;
constructor(string memory name, string memory symbol, uint8 decimals) public ERC20Detailed(name, symbol, decimals) {
// solhint-disable-previous-line no-empty-blocks
}
// The primary account (GSNRecipientERC20Charge) can mint tokens
function mint(address account, uint256 amount) public onlyPrimary {
_mint(account, amount);
}
// The primary account has 'infinite' allowance for all token holders
function allowance(address owner, address spender) public view returns (uint256) {
if (spender == primary()) {
return UINT256_MAX;
} else {
return super.allowance(owner, spender);
}
}
// Allowance for the primary account cannot be changed (it is always 'infinite')
function _approve(address owner, address spender, uint256 value) internal {
if (spender == primary()) {
return;
} else {
super._approve(owner, spender, value);
}
}
} }

Loading…
Cancel
Save