Merge tag 'v2.1.1' of github.com:OpenZeppelin/openzeppelin-solidity
v2.1.1
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
/**
|
||||
* @title Counter
|
||||
@ -14,15 +13,11 @@ pragma solidity ^0.4.24;
|
||||
* so it's not something you have to worry about.)
|
||||
*/
|
||||
library Counter {
|
||||
|
||||
struct Counter {
|
||||
uint256 current; // default: 0
|
||||
}
|
||||
|
||||
function next(Counter storage index)
|
||||
internal
|
||||
returns (uint256)
|
||||
{
|
||||
function next(Counter storage index) internal returns (uint256) {
|
||||
index.current += 1;
|
||||
return index.current;
|
||||
}
|
||||
|
||||
@ -1,33 +1,27 @@
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "zos-lib/contracts/Initializable.sol";
|
||||
import "../../token/ERC20/IERC20.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC-1047 Token Metadata
|
||||
* @dev See https://eips.ethereum.org/EIPS/eip-1046
|
||||
* @dev tokenURI must respond with a URI that implements https://eips.ethereum.org/EIPS/eip-1047
|
||||
* @dev TODO - update https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC721/IERC721.sol#L17 when 1046 is finalized
|
||||
*/
|
||||
contract ERC20TokenMetadata is Initializable, IERC20 {
|
||||
function tokenURI() external view returns (string);
|
||||
function tokenURI() external view returns (string memory);
|
||||
|
||||
uint256[50] private ______gap;
|
||||
}
|
||||
|
||||
|
||||
contract ERC20WithMetadata is Initializable, ERC20TokenMetadata {
|
||||
string private _tokenURI = "";
|
||||
string private _tokenURI;
|
||||
|
||||
function initialize(string tokenURI)
|
||||
public
|
||||
initializer
|
||||
{
|
||||
function initialize(string memory tokenURI) public {
|
||||
_tokenURI = tokenURI;
|
||||
}
|
||||
|
||||
function tokenURI() external view returns (string) {
|
||||
function tokenURI() external view returns (string memory) {
|
||||
return _tokenURI;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "zos-lib/contracts/Initializable.sol";
|
||||
import "../token/ERC20/IERC20.sol";
|
||||
@ -6,7 +6,6 @@ import "../token/ERC20/ERC20Mintable.sol";
|
||||
import "../token/ERC20/SafeERC20.sol";
|
||||
import "../math/Math.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title ERC20Migrator
|
||||
* @dev This contract can be used to migrate an ERC20 token from one
|
||||
@ -46,7 +45,7 @@ contract ERC20Migrator is Initializable {
|
||||
* @param legacyToken address of the old token contract
|
||||
*/
|
||||
function initialize(IERC20 legacyToken) public initializer {
|
||||
require(legacyToken != address(0));
|
||||
require(address(legacyToken) != address(0));
|
||||
_legacyToken = legacyToken;
|
||||
}
|
||||
|
||||
@ -70,9 +69,9 @@ contract ERC20Migrator is Initializable {
|
||||
* @param newToken the token that will be minted
|
||||
*/
|
||||
function beginMigration(ERC20Mintable newToken) public {
|
||||
require(_newToken == address(0));
|
||||
require(newToken != address(0));
|
||||
require(newToken.isMinter(this));
|
||||
require(address(_newToken) == address(0));
|
||||
require(address(newToken) != address(0));
|
||||
require(newToken.isMinter(address(this)));
|
||||
|
||||
_newToken = newToken;
|
||||
}
|
||||
@ -84,7 +83,7 @@ contract ERC20Migrator is Initializable {
|
||||
* @param amount amount of tokens to be migrated
|
||||
*/
|
||||
function migrate(address account, uint256 amount) public {
|
||||
_legacyToken.safeTransferFrom(account, this, amount);
|
||||
_legacyToken.safeTransferFrom(account, address(this), amount);
|
||||
_newToken.mint(account, amount);
|
||||
}
|
||||
|
||||
@ -95,7 +94,7 @@ contract ERC20Migrator is Initializable {
|
||||
*/
|
||||
function migrateAll(address account) public {
|
||||
uint256 balance = _legacyToken.balanceOf(account);
|
||||
uint256 allowance = _legacyToken.allowance(account, this);
|
||||
uint256 allowance = _legacyToken.allowance(account, address(this));
|
||||
uint256 amount = Math.min(balance, allowance);
|
||||
migrate(account, amount);
|
||||
}
|
||||
|
||||
@ -1,33 +1,39 @@
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "zos-lib/contracts/Initializable.sol";
|
||||
import "../access/roles/SignerRole.sol";
|
||||
import "../cryptography/ECDSA.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title SignatureBouncer
|
||||
* @author PhABC, Shrugs and aflesher
|
||||
* @dev SignatureBouncer allows users to submit a signature as a permission to do an action.
|
||||
* If the signature is from one of the authorized signer addresses, the signature
|
||||
* is valid.
|
||||
* @dev SignatureBouncer allows users to submit a signature as a permission to
|
||||
* do an action.
|
||||
* If the signature is from one of the authorized signer addresses, the
|
||||
* signature is valid.
|
||||
* Note that SignatureBouncer offers no protection against replay attacks, users
|
||||
* must add this themselves!
|
||||
*
|
||||
* Signer addresses can be individual servers signing grants or different
|
||||
* users within a decentralized club that have permission to invite other members.
|
||||
* This technique is useful for whitelists and airdrops; instead of putting all
|
||||
* valid addresses on-chain, simply sign a grant of the form
|
||||
* keccak256(abi.encodePacked(`:contractAddress` + `:granteeAddress`)) using a valid signer address.
|
||||
* users within a decentralized club that have permission to invite other
|
||||
* members. This technique is useful for whitelists and airdrops; instead of
|
||||
* putting all valid addresses on-chain, simply sign a grant of the form
|
||||
* keccak256(abi.encodePacked(`:contractAddress` + `:granteeAddress`)) using a
|
||||
* valid signer address.
|
||||
* Then restrict access to your crowdsale/whitelist/airdrop using the
|
||||
* `onlyValidSignature` modifier (or implement your own using _isValidSignature).
|
||||
* In addition to `onlyValidSignature`, `onlyValidSignatureAndMethod` and
|
||||
* `onlyValidSignatureAndData` can be used to restrict access to only a given method
|
||||
* or a given method with given parameters respectively.
|
||||
* `onlyValidSignatureAndData` can be used to restrict access to only a given
|
||||
* method or a given method with given parameters respectively.
|
||||
* See the tests in SignatureBouncer.test.js for specific usage examples.
|
||||
* @notice A method that uses the `onlyValidSignatureAndData` modifier must make the _signature
|
||||
* parameter the "last" parameter. You cannot sign a message that has its own
|
||||
* signature in it so the last 128 bytes of msg.data (which represents the
|
||||
* length of the _signature data and the _signaature data itself) is ignored when validating.
|
||||
* Also non fixed sized parameters make constructing the data in the signature
|
||||
* much more complex. See https://ethereum.stackexchange.com/a/50616 for more details.
|
||||
*
|
||||
* @notice A method that uses the `onlyValidSignatureAndData` modifier must make
|
||||
* the _signature parameter the "last" parameter. You cannot sign a message that
|
||||
* has its own signature in it so the last 128 bytes of msg.data (which
|
||||
* represents the length of the _signature data and the _signaature data itself)
|
||||
* is ignored when validating. Also non fixed sized parameters make constructing
|
||||
* the data in the signature much more complex.
|
||||
* See https://ethereum.stackexchange.com/a/50616 for more details.
|
||||
*/
|
||||
contract SignatureBouncer is Initializable, SignerRole {
|
||||
using ECDSA for bytes32;
|
||||
@ -38,11 +44,14 @@ contract SignatureBouncer is Initializable, SignerRole {
|
||||
// Signature size is 65 bytes (tightly packed v + r + s), but gets padded to 96 bytes
|
||||
uint256 private constant _SIGNATURE_SIZE = 96;
|
||||
|
||||
function initialize(address sender) internal initializer {
|
||||
SignerRole.initialize(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev requires that a valid signature of a signer was provided
|
||||
*/
|
||||
modifier onlyValidSignature(bytes signature)
|
||||
{
|
||||
modifier onlyValidSignature(bytes memory signature) {
|
||||
require(_isValidSignature(msg.sender, signature));
|
||||
_;
|
||||
}
|
||||
@ -50,8 +59,7 @@ contract SignatureBouncer is Initializable, SignerRole {
|
||||
/**
|
||||
* @dev requires that a valid signature with a specifed method of a signer was provided
|
||||
*/
|
||||
modifier onlyValidSignatureAndMethod(bytes signature)
|
||||
{
|
||||
modifier onlyValidSignatureAndMethod(bytes memory signature) {
|
||||
require(_isValidSignatureAndMethod(msg.sender, signature));
|
||||
_;
|
||||
}
|
||||
@ -59,48 +67,29 @@ contract SignatureBouncer is Initializable, SignerRole {
|
||||
/**
|
||||
* @dev requires that a valid signature with a specifed method and params of a signer was provided
|
||||
*/
|
||||
modifier onlyValidSignatureAndData(bytes signature)
|
||||
{
|
||||
modifier onlyValidSignatureAndData(bytes memory signature) {
|
||||
require(_isValidSignatureAndData(msg.sender, signature));
|
||||
_;
|
||||
}
|
||||
|
||||
function initialize(address sender) public initializer {
|
||||
SignerRole.initialize(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev is the signature of `this + sender` from a signer?
|
||||
* @return bool
|
||||
*/
|
||||
function _isValidSignature(address account, bytes signature)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return _isValidDataHash(
|
||||
keccak256(abi.encodePacked(address(this), account)),
|
||||
signature
|
||||
);
|
||||
function _isValidSignature(address account, bytes memory signature) internal view returns (bool) {
|
||||
return _isValidDataHash(keccak256(abi.encodePacked(address(this), account)), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev is the signature of `this + sender + methodId` from a signer?
|
||||
* @return bool
|
||||
*/
|
||||
function _isValidSignatureAndMethod(address account, bytes signature)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
function _isValidSignatureAndMethod(address account, bytes memory signature) internal view returns (bool) {
|
||||
bytes memory data = new bytes(_METHOD_ID_SIZE);
|
||||
for (uint i = 0; i < data.length; i++) {
|
||||
data[i] = msg.data[i];
|
||||
}
|
||||
return _isValidDataHash(
|
||||
keccak256(abi.encodePacked(address(this), account, data)),
|
||||
signature
|
||||
);
|
||||
return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,20 +97,15 @@ contract SignatureBouncer is Initializable, SignerRole {
|
||||
* @notice the signature parameter of the method being validated must be the "last" parameter
|
||||
* @return bool
|
||||
*/
|
||||
function _isValidSignatureAndData(address account, bytes signature)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
function _isValidSignatureAndData(address account, bytes memory signature) internal view returns (bool) {
|
||||
require(msg.data.length > _SIGNATURE_SIZE);
|
||||
|
||||
bytes memory data = new bytes(msg.data.length - _SIGNATURE_SIZE);
|
||||
for (uint i = 0; i < data.length; i++) {
|
||||
data[i] = msg.data[i];
|
||||
}
|
||||
return _isValidDataHash(
|
||||
keccak256(abi.encodePacked(address(this), account, data)),
|
||||
signature
|
||||
);
|
||||
|
||||
return _isValidDataHash(keccak256(abi.encodePacked(address(this), account, data)), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,14 +113,8 @@ contract SignatureBouncer is Initializable, SignerRole {
|
||||
* and then recover the signature and check it against the signer role
|
||||
* @return bool
|
||||
*/
|
||||
function _isValidDataHash(bytes32 hash, bytes signature)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
address signer = hash
|
||||
.toEthSignedMessageHash()
|
||||
.recover(signature);
|
||||
function _isValidDataHash(bytes32 hash, bytes memory signature) internal view returns (bool) {
|
||||
address signer = hash.toEthSignedMessageHash().recover(signature);
|
||||
|
||||
return signer != address(0) && isSigner(signer);
|
||||
}
|
||||
|
||||
60
contracts/drafts/SignedSafeMath.sol
Normal file
60
contracts/drafts/SignedSafeMath.sol
Normal file
@ -0,0 +1,60 @@
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
/**
|
||||
* @title SignedSafeMath
|
||||
* @dev Signed math operations with safety checks that revert on error
|
||||
*/
|
||||
library SignedSafeMath {
|
||||
int256 constant private INT256_MIN = -2**255;
|
||||
|
||||
/**
|
||||
* @dev Multiplies two signed integers, reverts on overflow.
|
||||
*/
|
||||
function mul(int256 a, int256 b) internal pure returns (int256) {
|
||||
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
|
||||
// benefit is lost if 'b' is also tested.
|
||||
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
|
||||
if (a == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
require(!(a == -1 && b == INT256_MIN)); // This is the only case of overflow not detected by the check below
|
||||
|
||||
int256 c = a * b;
|
||||
require(c / a == b);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
|
||||
*/
|
||||
function div(int256 a, int256 b) internal pure returns (int256) {
|
||||
require(b != 0); // Solidity only automatically asserts when dividing by 0
|
||||
require(!(b == -1 && a == INT256_MIN)); // This is the only case of overflow
|
||||
|
||||
int256 c = a / b;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Subtracts two signed integers, reverts on overflow.
|
||||
*/
|
||||
function sub(int256 a, int256 b) internal pure returns (int256) {
|
||||
int256 c = a - b;
|
||||
require((b >= 0 && c <= a) || (b < 0 && c > a));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Adds two signed integers, reverts on overflow.
|
||||
*/
|
||||
function add(int256 a, int256 b) internal pure returns (int256) {
|
||||
int256 c = a + b;
|
||||
require((b >= 0 && c >= a) || (b < 0 && c < a));
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,10 @@
|
||||
/* solium-disable security/no-block-members */
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "zos-lib/contracts/Initializable.sol";
|
||||
import "../token/ERC20/SafeERC20.sol";
|
||||
import "../ownership/Ownable.sol";
|
||||
import "../math/SafeMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @title TokenVesting
|
||||
* @dev A token holder contract that can release its token balance gradually like a
|
||||
@ -15,15 +12,22 @@ import "../math/SafeMath.sol";
|
||||
* owner.
|
||||
*/
|
||||
contract TokenVesting is Initializable, Ownable {
|
||||
// The vesting schedule is time-based (i.e. using block timestamps as opposed to e.g. block numbers), and is
|
||||
// therefore sensitive to timestamp manipulation (which is something miners can do, to a certain degree). Therefore,
|
||||
// it is recommended to avoid using short time durations (less than a minute). Typical vesting schemes, with a cliff
|
||||
// period of a year and a duration of four years, are safe to use.
|
||||
// solhint-disable not-rely-on-time
|
||||
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
event Released(uint256 amount);
|
||||
event Revoked();
|
||||
event TokensReleased(address token, uint256 amount);
|
||||
event TokenVestingRevoked(address token);
|
||||
|
||||
// beneficiary of tokens after they are released
|
||||
address private _beneficiary;
|
||||
|
||||
// Durations and timestamps are expressed in UNIX time, the same units as block.timestamp.
|
||||
uint256 private _cliff;
|
||||
uint256 private _start;
|
||||
uint256 private _duration;
|
||||
@ -43,21 +47,13 @@ contract TokenVesting is Initializable, Ownable {
|
||||
* @param duration duration in seconds of the period in which the tokens will vest
|
||||
* @param revocable whether the vesting is revocable or not
|
||||
*/
|
||||
function initialize(
|
||||
address beneficiary,
|
||||
uint256 start,
|
||||
uint256 cliffDuration,
|
||||
uint256 duration,
|
||||
bool revocable,
|
||||
address sender
|
||||
)
|
||||
public
|
||||
initializer
|
||||
{
|
||||
constructor (address beneficiary, uint256 start, uint256 cliffDuration, uint256 duration, bool revocable, address sender) public initializer {
|
||||
Ownable.initialize(sender);
|
||||
|
||||
require(beneficiary != address(0));
|
||||
require(cliffDuration <= duration);
|
||||
require(duration > 0);
|
||||
require(start.add(duration) > block.timestamp);
|
||||
|
||||
_beneficiary = beneficiary;
|
||||
_revocable = revocable;
|
||||
@ -69,49 +65,49 @@ contract TokenVesting is Initializable, Ownable {
|
||||
/**
|
||||
* @return the beneficiary of the tokens.
|
||||
*/
|
||||
function beneficiary() public view returns(address) {
|
||||
function beneficiary() public view returns (address) {
|
||||
return _beneficiary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cliff time of the token vesting.
|
||||
*/
|
||||
function cliff() public view returns(uint256) {
|
||||
function cliff() public view returns (uint256) {
|
||||
return _cliff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the start time of the token vesting.
|
||||
*/
|
||||
function start() public view returns(uint256) {
|
||||
function start() public view returns (uint256) {
|
||||
return _start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the duration of the token vesting.
|
||||
*/
|
||||
function duration() public view returns(uint256) {
|
||||
function duration() public view returns (uint256) {
|
||||
return _duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the vesting is revocable.
|
||||
*/
|
||||
function revocable() public view returns(bool) {
|
||||
function revocable() public view returns (bool) {
|
||||
return _revocable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of the token released.
|
||||
*/
|
||||
function released(address token) public view returns(uint256) {
|
||||
function released(address token) public view returns (uint256) {
|
||||
return _released[token];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the token is revoked.
|
||||
*/
|
||||
function revoked(address token) public view returns(bool) {
|
||||
function revoked(address token) public view returns (bool) {
|
||||
return _revoked[token];
|
||||
}
|
||||
|
||||
@ -120,15 +116,15 @@ contract TokenVesting is Initializable, Ownable {
|
||||
* @param token ERC20 token which is being vested
|
||||
*/
|
||||
function release(IERC20 token) public {
|
||||
uint256 unreleased = releasableAmount(token);
|
||||
uint256 unreleased = _releasableAmount(token);
|
||||
|
||||
require(unreleased > 0);
|
||||
|
||||
_released[token] = _released[token].add(unreleased);
|
||||
_released[address(token)] = _released[address(token)].add(unreleased);
|
||||
|
||||
token.safeTransfer(_beneficiary, unreleased);
|
||||
|
||||
emit Released(unreleased);
|
||||
emit TokensReleased(address(token), unreleased);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,39 +134,39 @@ contract TokenVesting is Initializable, Ownable {
|
||||
*/
|
||||
function revoke(IERC20 token) public onlyOwner {
|
||||
require(_revocable);
|
||||
require(!_revoked[token]);
|
||||
require(!_revoked[address(token)]);
|
||||
|
||||
uint256 balance = token.balanceOf(address(this));
|
||||
|
||||
uint256 unreleased = releasableAmount(token);
|
||||
uint256 unreleased = _releasableAmount(token);
|
||||
uint256 refund = balance.sub(unreleased);
|
||||
|
||||
_revoked[token] = true;
|
||||
_revoked[address(token)] = true;
|
||||
|
||||
token.safeTransfer(owner(), refund);
|
||||
|
||||
emit Revoked();
|
||||
emit TokenVestingRevoked(address(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates the amount that has already vested but hasn't been released yet.
|
||||
* @param token ERC20 token which is being vested
|
||||
*/
|
||||
function releasableAmount(IERC20 token) public view returns (uint256) {
|
||||
return vestedAmount(token).sub(_released[token]);
|
||||
function _releasableAmount(IERC20 token) private view returns (uint256) {
|
||||
return _vestedAmount(token).sub(_released[address(token)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates the amount that has already vested.
|
||||
* @param token ERC20 token which is being vested
|
||||
*/
|
||||
function vestedAmount(IERC20 token) public view returns (uint256) {
|
||||
uint256 currentBalance = token.balanceOf(this);
|
||||
uint256 totalBalance = currentBalance.add(_released[token]);
|
||||
function _vestedAmount(IERC20 token) private view returns (uint256) {
|
||||
uint256 currentBalance = token.balanceOf(address(this));
|
||||
uint256 totalBalance = currentBalance.add(_released[address(token)]);
|
||||
|
||||
if (block.timestamp < _cliff) {
|
||||
return 0;
|
||||
} else if (block.timestamp >= _start.add(_duration) || _revoked[token]) {
|
||||
} else if (block.timestamp >= _start.add(_duration) || _revoked[address(token)]) {
|
||||
return totalBalance;
|
||||
} else {
|
||||
return totalBalance.mul(block.timestamp.sub(_start)).div(_duration);
|
||||
|
||||
Reference in New Issue
Block a user