Skip to content

Instantly share code, notes, and snippets.

@ngyam
Created February 17, 2021 16:34
Show Gist options
  • Save ngyam/a1f022c43e343c4957d80bf02789ed5e to your computer and use it in GitHub Desktop.
Save ngyam/a1f022c43e343c4957d80bf02789ed5e to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.17+commit.d19bba13.js&optimize=false&runs=200&gist=
pragma solidity 0.5.8;
/// @title BlockReward contract interface
/// @dev This interface must be implemented by a system contract that implements the
/// reward logic
interface IBlockReward {
/// @notice Produces rewards for the given benefactors,
/// with corresponding reward codes
/// @dev Should only be callable by `SYSTEM_ADDRESS`
/// @param benefactors The list of addresses who can be rewarded
/// @param kind The corresponding list of benefactor types
/// @return The list of addresses to be rewarded and the corresponding list of reward amounts
function reward(address[] calldata benefactors, uint16[] calldata kind)
external
returns (address[] memory, uint256[] memory);
}
/// @title S Curve Reward provider
/// @notice Provides the appropriate block author rewards
/// based on points of a discrete, inverse S curve
contract SCurveProvider {
/// Required length of the discrete S curve
uint256 public constant REQUIRED_CURVE_LENGTH = 120;
/// Discrete points of the curve
uint256[] public sCurve;
/// Discrete step size
uint256 public blockStepSize;
/// End of the reward period (both block- and community reward)
/// expressed in block number
uint256 public rewardPeriodEnd;
constructor()
public
{
_initCurve();
// This check is for protecting against some tampering
// for chainspec deployment
require(sCurve.length == REQUIRED_CURVE_LENGTH, "Reward curve is not the required length");
}
/// @notice Returns the block reward amount based on the block number
/// and points of the S curve
/// @param _currentBlock The block number to calculate the reward to
/// @return The block reward amount in wei
function getBlockReward(uint256 _currentBlock)
public
view
returns (uint256)
{
if (_checkRewardPeriodEnded(_currentBlock)) {
return 0;
}
return sCurve[_currentBlock / blockStepSize];
}
/// @notice Checks whether the reward period is over or not (block and community)
/// @return True if the reward period has ended, false otherwise
function checkRewardPeriodEnded()
public
view
returns (bool)
{
return _checkRewardPeriodEnded(block.number);
}
/// @notice Checks whether the block reward period is over or not
/// @param _currentBlock The block number to check on
/// @return True if the block reward period has ended, false otherwise
function _checkRewardPeriodEnded(uint256 _currentBlock)
internal
view
returns (bool)
{
return (_currentBlock >= rewardPeriodEnd);
}
// solhint-disable function-max-lines
/// @dev Inits the S curve. Values are hardcoded,
/// everyhting is calculated beforehand
function _initCurve()
private
{
sCurve = [
uint256(304418979390926464),
304418979390926464,
304418979390926464,
304418979390926464,
304418979390926464,
304418979390926464,
304418979390926464,
304369560376526464,
304221303333326464,
303974208261326464,
303628275160526464,
303183504030926464,
302639894872526464,
301997447685326464,
301256162469326464,
300416039224526464,
299477077950926464,
298439278648526464,
297302641317326464,
296067165957326464,
294732852568526464,
293299701150926464,
291767711704526464,
290136884229326464,
288407218725326464,
286578715192526464,
284651373630926464,
282625194040526464,
280500176421326464,
278276320773326464,
275953627096526464,
273532095390926464,
271011725656526464,
268392517893326464,
265674472101326464,
262857588280526464,
259941866430926464,
256927306552526464,
253813908645326464,
250601672709326464,
247290598744526464,
243880686750926464,
240371936728526464,
236764348677326464,
233057922597326464,
229252658488526464,
225348556350926464,
221345616184526464,
217243837989326464,
213043221765326464,
208743767512526464,
204345475230926464,
199848344920526464,
195252376581326464,
190557570213326464,
185763925816526464,
180871443390926464,
175880122936526464,
170789964453326464,
165600967941326464,
160117606656000000,
154824830213760000,
149621007997440000,
144506140007040000,
139480226242560000,
134543266704000000,
129695261391360000,
124936210304640000,
120266113443840000,
115684970808960000,
111192782400000000,
106789548216960000,
102475268259840000,
98249942528640000,
94113571023360000,
90066153744000000,
86107690690560000,
82238181863040000,
78457627261440000,
74766026885760000,
71163380736000000,
67649688812160000,
64224951114240000,
60889167642240000,
57642338396160000,
54484463376000000,
51415542581760000,
48435576013440000,
45544563671040000,
42742505554560000,
40029401664000000,
37405251999360000,
34870056560640000,
32423815347840000,
30066528360960000,
27798195600000000,
25618817064960000,
23528392755840000,
21526922672640000,
19614406815360000,
17790845184000000,
16056237778560000,
14410584599040000,
12853885645440000,
11386140917760000,
10007350416000000,
8717514140160000,
7516632090240000,
6404704266240000,
5381730668160000,
4447711296000000,
3602646149760000,
2846535229440000,
2179378535040000,
1601176066560000,
1111927824000000,
711633807360000,
400294016640000,
177908451840000,
44477112960000
];
// roughly 1 month with a 5 sec step size
blockStepSize = 525600;
//roughly 10 years with a 5 sec step size
rewardPeriodEnd = blockStepSize * REQUIRED_CURVE_LENGTH;
}
// solhint-enable function-max-lines
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b, "Multiplication error");
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "Division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "Overflow error");
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "Overflow error");
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "Division by zero");
return a % b;
}
}
/// @title Block reward contract
/// @notice Performs payouts at each new created block. Block authors
/// are rewarded according to an S-curve, while there is a constant payout for
/// a community fund for a certain period of time.
/// @dev Contract is used by the Parity client and its address is
/// specified in the chainspec
contract BlockReward is SCurveProvider, IBlockReward {
using SafeMath for uint256;
// storage variables for logging reward statistics
uint256 public mintedTotally;
uint256 public mintedForCommunity;
mapping(address => uint256) public mintedForCommunityForAccount;
mapping(address => uint256) public mintedForAccount;
mapping(uint256 => uint256) public mintedInBlock;
mapping(address => mapping(uint256 => uint256)) public mintedForAccountInBlock;
/// Parity client SYSTEM_ADDRESS: 2^160 - 2
address internal systemAddress = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
/// The constant amount that gets sent to the
/// community fund with each new block
uint256 public communityFundAmount;
/// Address of the community fund. Preferably a multisig wallet
address public communityFund;
/// Mapping of addresses and their payout addresses where rewards are minted
mapping(address => address) public payoutAddresses;
constructor(address _communityFundAddress, uint256 _communityFundAmount)
public
{
communityFund = _communityFundAddress;
communityFundAmount = _communityFundAmount;
}
/// @notice Sets community fund address. Ideally
/// it is a multisig wallet address
/// @param _newFund New community fund address
function setCommunityFund(address _newFund)
external
{
require(
msg.sender == communityFund,
"Caller is not the community fund"
);
communityFund = _newFund;
}
/// @notice Sets payout address. Every sender can only set its own
/// payout address. The contract only rewards block authors, but it
/// is not checking who sets an address for itself. The community fund
/// can set a payout address too, if desired.
/// @param _newPayoutAddress The payout address belonging to the sender
function setPayoutAddress(address _newPayoutAddress)
external
{
payoutAddresses[msg.sender] = _newPayoutAddress;
}
/// @notice Resets the payout address. If a payout address is reseted/not set,
/// the minted amounts get sent to the original one. The sender resets its own
/// payout address
function resetPayoutAddress()
external
{
delete payoutAddresses[msg.sender];
}
/// @notice The function that is called by the client to issue rewards at a new block. The rewards are
/// minted: the balances of the corresponing addresses are simply increased with the amount
/// @dev It is a service transaction invoked by system, which doesn't cost anyhting but still can
/// modify state. Cannot emit events.
/// @param benefactors List of addresses that can be rewarded
/// @param kind List of type codes belonging to the benefactors. They determine the category
/// an address belongs to. 0 is for block authors which we are only interested in
/// @return List of addreses to be rewarded, and list of corresponding reward amounts in wei
function reward(address[] calldata benefactors, uint16[] calldata kind)
external
returns (address[] memory, uint256[] memory)
{
require(msg.sender == systemAddress, "Caller is not the system");
require(benefactors.length == kind.length, "Benefactors/types list length differs");
require(benefactors.length == 1, "Benefactors list length is not 1");
require(kind[0] == 0, "Benefactor is not the block author");
if (benefactors[0] == address(0) || _checkRewardPeriodEnded(block.number)) {
return (new address[](0), new uint256[](0));
}
address[] memory receivers = new address[](2);
uint256[] memory rewards = new uint256[](receivers.length);
receivers[0] = _getPayoutAddress(benefactors[0]);
rewards[0] = getBlockReward(block.number);
receivers[1] = _getPayoutAddress(communityFund);
rewards[1] = communityFundAmount;
_logMinted(receivers[0], rewards[0]);
_logCommunityMinted(receivers[1], rewards[1]);
return (receivers, rewards);
}
/// @dev Retrieves the payout address of an account if there is any. If not specified
/// or resetted, returns the original account
/// @param _somebody An account address we retrieve the payout address for
/// @return The payout address
function _getPayoutAddress(address _somebody)
private
view
returns (address)
{
address _payoutAddress = payoutAddresses[_somebody];
if (_payoutAddress == address(0)) {
return _somebody;
}
return _payoutAddress;
}
/// @dev Logs a community-mint event. Calls `_logMinted` after setting the
/// community specific metrics
/// @param _account The account address where the tokens are minted
/// @param _amount The minted amount in wei
function _logCommunityMinted(address _account, uint256 _amount)
private
{
mintedForCommunity = mintedForCommunity.add(_amount);
mintedForCommunityForAccount[_account] = mintedForCommunityForAccount[_account].add(_amount);
_logMinted(_account, _amount);
}
/// @dev Logs a mint event, and stores related metrics (counters).
/// @param _account The account address where the tokens are minted
/// @param _amount The minted amount in wei
function _logMinted(address _account, uint256 _amount)
private
{
mintedForAccountInBlock[_account][block.number] = mintedForAccountInBlock[_account][block.number].add(_amount);
mintedForAccount[_account] = mintedForAccount[_account].add(_amount);
mintedInBlock[block.number] = mintedInBlock[block.number].add(_amount);
mintedTotally = mintedTotally.add(_amount);
}
}
pragma solidity ^0.6.0;
contract Ownable {
address public owner;
modifier onlyOwner {
require(isOwner(msg.sender));
_;
}
constructor() public {
owner = msg.sender;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
function isOwner(address addr) public view returns (bool) {
return owner == addr;
}
}
interface IBridge {
function relayTokens(address _receiver) external payable;
}
contract BridgeHead is Ownable{
IBridge public bridge;
function setBridge(address _bridge)
public
onlyOwner
{
bridge = IBridge(_bridge);
}
function relay() external payable {
bridge.relayTokens{value: msg.value}(msg.sender);
}
receive()
external
payable
{
bridge.relayTokens{value: msg.value}(msg.sender);
}
}
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
}
contract BridgeUtil {
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 bytes (most of them zeros) followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
public
pure
returns (address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := mload(add(message, 20))
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function recoverAddressFromSignedMessage(bytes signature, bytes message, bool isAMBMessage)
public
pure
returns (address)
{
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
}
function hashMessage(bytes message, bool isAMBMessage) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
if (isAMBMessage) {
return keccak256(abi.encodePacked(prefix, uintToString(message.length), message));
} else {
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
}
function isMessageValid(bytes _msg) public pure returns (bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() public pure returns (uint256) {
return 104;
}
function hasEnoughValidSignatures(
bytes _message,
bytes _signatures,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
uint256 amount;
assembly {
amount := and(mload(add(_signatures, 1)), 0xff)
}
require(amount >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
uint8 v;
bytes32 r;
bytes32 s;
uint256 posr = 33 + amount + 32 * i;
uint256 poss = posr + 32 * amount;
assembly {
v := mload(add(_signatures, add(2, i)))
r := mload(add(_signatures, posr))
s := mload(add(_signatures, poss))
}
address recoveredAddress = ecrecover(hash, v, r, s);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function uintToString(uint256 i) public pure returns (string) {
if (i == 0) return "0";
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
bstr[k--] = bytes1(48 + (i % 10));
i /= 10;
}
return string(bstr);
}
function hashMsg(bytes _message) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_message));
}
function hashSender(bytes _hashMsg, address _sender) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_sender, _hashMsg));
}
function signIdx(bytes _hashMsg, uint256 _signed) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_hashMsg, _signed));
}
function isAlreadyProcessed(uint256 _number) public pure returns (bool) {
return _number & (2**255) == 2**255;
}
function markAsProcessed(uint256 _v) internal pure returns (uint256) {
return _v | (2**255);
}
bytes4 public CALCULATE_FEE = 0x9862f26f; // calculateFee(uint256,bool,bytes32)
bytes32 public HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 public FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
bytes4 public DISTRIBUTE_FEE_FROM_SIGNATURES = 0x59d78464; // distributeFeeFromSignatures(uint256)
bytes4 public DISTRIBUTE_FEE_FROM_AFFIRMATION = 0x054d46ec;
function calculateFeeCalldata(uint256 _value, bool _recover, bytes32 _feeType) public view returns (bytes) {
return abi.encodeWithSelector(CALCULATE_FEE, _value, _recover, _feeType);
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/interfaces/IUpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
/* solcov ignore next */
_;
}
/**
* @dev Throws if called by any account other than contract itself or owner.
*/
modifier onlyRelevantSender() {
// proxy owner if used through proxy, address(0) otherwise
require(
!address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
/* solcov ignore next */
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
// File: contracts/upgradeable_contracts/Initializable.sol
pragma solidity 0.4.24;
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}
// File: contracts/upgradeable_contracts/InitializableBridge.sol
pragma solidity 0.4.24;
contract InitializableBridge is Initializable {
bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
function deployedAtBlock() external view returns (uint256) {
return uintStorage[DEPLOYED_AT_BLOCK];
}
}
// File: contracts/upgradeable_contracts/BaseBridgeValidators.sol
pragma solidity 0.4.24;
contract BaseBridgeValidators is InitializableBridge, Ownable {
using SafeMath for uint256;
address public constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 internal constant MAX_VALIDATORS = 50;
bytes32 internal constant REQUIRED_SIGNATURES = 0xd18ea17c351d6834a0e568067fb71804d2a588d5e26d60f792b1c724b1bd53b1; // keccak256(abi.encodePacked("requiredSignatures"))
bytes32 internal constant VALIDATOR_COUNT = 0x8656d603d9f985c3483946a92789d52202f49736384ba131cb92f62c4c1aa082; // keccak256(abi.encodePacked("validatorCount"))
event ValidatorAdded(address indexed validator);
event ValidatorRemoved(address indexed validator);
event RequiredSignaturesChanged(uint256 requiredSignatures);
function setRequiredSignatures(uint256 _requiredSignatures) external onlyOwner {
require(validatorCount() >= _requiredSignatures);
require(_requiredSignatures != 0);
uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
emit RequiredSignaturesChanged(_requiredSignatures);
}
function getBridgeValidatorsInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (2, 3, 0);
}
function validatorList() external view returns (address[]) {
address[] memory list = new address[](validatorCount());
uint256 counter = 0;
address nextValidator = getNextValidator(F_ADDR);
require(nextValidator != address(0));
while (nextValidator != F_ADDR) {
list[counter] = nextValidator;
nextValidator = getNextValidator(nextValidator);
counter++;
require(nextValidator != address(0));
}
return list;
}
function _addValidator(address _validator) internal {
require(_validator != address(0) && _validator != F_ADDR);
require(!isValidator(_validator));
address firstValidator = getNextValidator(F_ADDR);
require(firstValidator != address(0));
setNextValidator(_validator, firstValidator);
setNextValidator(F_ADDR, _validator);
setValidatorCount(validatorCount().add(1));
}
function _removeValidator(address _validator) internal {
require(validatorCount() > requiredSignatures());
require(isValidator(_validator));
address validatorsNext = getNextValidator(_validator);
address index = F_ADDR;
address next = getNextValidator(index);
require(next != address(0));
while (next != _validator) {
index = next;
next = getNextValidator(index);
require(next != F_ADDR && next != address(0));
}
setNextValidator(index, validatorsNext);
deleteItemFromAddressStorage("validatorsList", _validator);
setValidatorCount(validatorCount().sub(1));
}
function requiredSignatures() public view returns (uint256) {
return uintStorage[REQUIRED_SIGNATURES];
}
function validatorCount() public view returns (uint256) {
return uintStorage[VALIDATOR_COUNT];
}
function isValidator(address _validator) public view returns (bool) {
return _validator != F_ADDR && getNextValidator(_validator) != address(0);
}
function getNextValidator(address _address) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("validatorsList", _address))];
}
function deleteItemFromAddressStorage(string _mapName, address _address) internal {
delete addressStorage[keccak256(abi.encodePacked(_mapName, _address))];
}
function setValidatorCount(uint256 _validatorCount) internal {
require(_validatorCount <= MAX_VALIDATORS);
uintStorage[VALIDATOR_COUNT] = _validatorCount;
}
function setNextValidator(address _prevValidator, address _validator) internal {
addressStorage[keccak256(abi.encodePacked("validatorsList", _prevValidator))] = _validator;
}
function isValidatorDuty(address _validator) external view returns (bool) {
uint256 counter = 0;
address next = getNextValidator(F_ADDR);
require(next != address(0));
while (next != F_ADDR) {
if (next == _validator) {
return (block.number % validatorCount() == counter);
}
next = getNextValidator(next);
counter++;
require(next != address(0));
}
return false;
}
}
// File: contracts/upgradeable_contracts/BridgeValidators.sol
pragma solidity 0.4.24;
contract BridgeValidators is BaseBridgeValidators {
function initialize(uint256 _requiredSignatures, address[] _initialValidators, address _owner)
external
onlyRelevantSender
returns (bool)
{
require(!isInitialized());
require(_owner != address(0));
setOwner(_owner);
require(_requiredSignatures != 0);
require(_initialValidators.length >= _requiredSignatures);
for (uint256 i = 0; i < _initialValidators.length; i++) {
require(_initialValidators[i] != address(0) && _initialValidators[i] != F_ADDR);
require(!isValidator(_initialValidators[i]));
if (i == 0) {
setNextValidator(F_ADDR, _initialValidators[i]);
if (_initialValidators.length == 1) {
setNextValidator(_initialValidators[i], F_ADDR);
}
} else if (i == _initialValidators.length - 1) {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
setNextValidator(_initialValidators[i], F_ADDR);
} else {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
}
emit ValidatorAdded(_initialValidators[i]);
}
setValidatorCount(_initialValidators.length);
uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
setInitialize();
emit RequiredSignaturesChanged(_requiredSignatures);
return isInitialized();
}
function addValidator(address _validator) external onlyOwner {
_addValidator(_validator);
emit ValidatorAdded(_validator);
}
function removeValidator(address _validator) external onlyOwner {
_removeValidator(_validator);
emit ValidatorRemoved(_validator);
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
_;
}
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("owner"))];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[keccak256(abi.encodePacked("owner"))] = newOwner;
}
}
// File: contracts/libraries/SafeMath.sol
pragma solidity 0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/upgradeable_contracts/BaseBridgeValidators.sol
pragma solidity 0.4.24;
contract BaseBridgeValidators is EternalStorage, Ownable {
using SafeMath for uint256;
address public constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
event ValidatorAdded (address indexed validator);
event ValidatorRemoved (address indexed validator);
event RequiredSignaturesChanged (uint256 requiredSignatures);
function setRequiredSignatures(uint256 _requiredSignatures)
external
onlyOwner
{
require(validatorCount() >= _requiredSignatures);
require(_requiredSignatures != 0);
uintStorage[keccak256(abi.encodePacked("requiredSignatures"))] = _requiredSignatures;
emit RequiredSignaturesChanged(_requiredSignatures);
}
function getBridgeValidatorsInterfacesVersion()
public
pure
returns (uint64 major, uint64 minor, uint64 patch)
{
return (2, 2, 0);
}
function validatorList() public view returns (address[]) {
address [] memory list = new address[](validatorCount());
uint256 counter = 0;
address nextValidator = getNextValidator(F_ADDR);
require(nextValidator != address(0));
while (nextValidator != F_ADDR) {
list[counter] = nextValidator;
nextValidator = getNextValidator(nextValidator);
counter++;
if (nextValidator == address(0) ) {
revert();
}
}
return list;
}
function _addValidator(address _validator) internal {
require(_validator != address(0) && _validator != F_ADDR);
require(!isValidator(_validator));
address firstValidator = getNextValidator(F_ADDR);
require(firstValidator != address(0));
setNextValidator(_validator, firstValidator);
setNextValidator(F_ADDR, _validator);
setValidatorCount(validatorCount().add(1));
}
function _removeValidator(address _validator) internal {
require(validatorCount() > requiredSignatures());
require(isValidator(_validator));
address validatorsNext = getNextValidator(_validator);
address index = F_ADDR;
address next = getNextValidator(index);
require(next != address(0));
while (next != _validator) {
index = next;
next = getNextValidator(index);
if (next == F_ADDR || next == address(0) ) {
revert();
}
}
setNextValidator(index, validatorsNext);
deleteItemFromAddressStorage("validatorsList", _validator);
setValidatorCount(validatorCount().sub(1));
}
function requiredSignatures() public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("requiredSignatures"))];
}
function validatorCount() public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("validatorCount"))];
}
function isValidator(address _validator) public view returns (bool) {
return _validator != F_ADDR && getNextValidator(_validator) != address(0);
}
function isInitialized() public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("isInitialized"))];
}
function deployedAtBlock() public view returns (uint256) {
return uintStorage[keccak256("deployedAtBlock")];
}
function getNextValidator(address _address) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("validatorsList", _address))];
}
function deleteItemFromAddressStorage(string _mapName, address _address) internal {
delete addressStorage[keccak256(abi.encodePacked(_mapName, _address))];
}
function setValidatorCount(uint256 _validatorCount) internal {
uintStorage[keccak256(abi.encodePacked("validatorCount"))] = _validatorCount;
}
function setNextValidator(address _prevValidator, address _validator) internal {
addressStorage[keccak256(abi.encodePacked("validatorsList", _prevValidator))] = _validator;
}
function setInitialize(bool _status) internal {
boolStorage[keccak256(abi.encodePacked("isInitialized"))] = _status;
}
}
// File: contracts/upgradeable_contracts/BridgeValidators.sol
pragma solidity 0.4.24;
contract BridgeValidators is BaseBridgeValidators {
function initialize(
uint256 _requiredSignatures,
address[] _initialValidators,
address _owner
)
public
returns (bool)
{
require(!isInitialized());
require(_owner != address(0));
setOwner(_owner);
require(_requiredSignatures != 0);
require(_initialValidators.length >= _requiredSignatures);
for (uint256 i = 0; i < _initialValidators.length; i++) {
require(_initialValidators[i] != address(0) && _initialValidators[i] != F_ADDR);
require(!isValidator(_initialValidators[i]));
if (i == 0) {
setNextValidator(F_ADDR, _initialValidators[i]);
if (_initialValidators.length == 1) {
setNextValidator(_initialValidators[i], F_ADDR);
}
} else if (i == _initialValidators.length - 1) {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
setNextValidator(_initialValidators[i], F_ADDR);
} else {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
}
setValidatorCount(validatorCount().add(1));
emit ValidatorAdded(_initialValidators[i]);
}
uintStorage[keccak256(abi.encodePacked("requiredSignatures"))] = _requiredSignatures;
uintStorage[keccak256("deployedAtBlock")] = block.number;
setInitialize(true);
emit RequiredSignaturesChanged(_requiredSignatures);
return isInitialized();
}
function addValidator(address _validator) external onlyOwner {
_addValidator(_validator);
emit ValidatorAdded(_validator);
}
function removeValidator(address _validator) external onlyOwner {
_removeValidator(_validator);
emit ValidatorRemoved(_validator);
}
}
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
}
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
contract FeeTester is EternalStorage, FeeTypes {
bytes4 internal constant CALCULATE_FEE = 0x9862f26f; // calculateFee(uint256,bool,bytes32)
constructor(uint256 _fee) public {
setHomeFee(_fee);
setForeignFee(_fee);
}
function setHomeFee(uint256 _fee) public {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
}
function setForeignFee(uint256 _fee) public {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
}
function calculateFee(uint256 _value, bool _recover, address _impl, bytes32 _feeType)
external
view
returns (uint256)
{
uint256 fee;
bytes memory callData = abi.encodeWithSelector(CALCULATE_FEE, _value, _recover, _feeType);
assembly {
let result := callcode(gas, _impl, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
}
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*
* This contract has been modified to remove the revokeOwnership function
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
library UintMedian {
using SafeMath for uint256;
/**
* @dev Returns the sorted middle, or the average of the two middle indexed
* items if the array has an even number of elements
* @param _list The list of elements to compare
*/
function calculate(uint256[] memory _list)
internal
pure
returns (uint256)
{
uint256 answerLength = _list.length;
uint256 middleIndex = answerLength.div(2);
if (answerLength % 2 == 0) {
uint256 median1 = quickselect(copy(_list), middleIndex);
uint256 median2 = quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed
uint256 remainder = (median1 % 2 + median2 % 2) / 2;
return (median1 / 2).add(median2 / 2).add(remainder); // signed integers are not supported by SafeMath
} else {
return quickselect(_list, middleIndex.add(1)); // quickselect is 1 indexed
}
}
/**
* @dev Returns the kth value of the ordered array
* See: http://www.cs.yale.edu/homes/aspnes/pinewiki/QuickSelect.html
* @param _a The list of elements to pull from
* @param _k The index, 1 based, of the elements you want to pull from when ordered
*/
function quickselect(uint256[] memory _a, uint256 _k)
private
pure
returns (uint256)
{
uint256[] memory a = _a;
uint256 k = _k;
uint256 aLen = a.length;
uint256[] memory a1 = new uint256[](aLen);
uint256[] memory a2 = new uint256[](aLen);
uint256 a1Len;
uint256 a2Len;
uint256 pivot;
uint256 i;
while (true) {
pivot = a[aLen.div(2)];
a1Len = 0;
a2Len = 0;
for (i = 0; i < aLen; i++) {
if (a[i] < pivot) {
a1[a1Len] = a[i];
a1Len++;
} else if (a[i] > pivot) {
a2[a2Len] = a[i];
a2Len++;
}
}
if (k <= a1Len) {
aLen = a1Len;
(a, a1) = swap(a, a1);
} else if (k > (aLen.sub(a2Len))) {
k = k.sub(aLen.sub(a2Len));
aLen = a2Len;
(a, a2) = swap(a, a2);
} else {
return pivot;
}
}
}
/**
* @dev Swaps the pointers to two uint256 arrays in memory
* @param _a The pointer to the first in memory array
* @param _b The pointer to the second in memory array
*/
function swap(uint256[] memory _a, uint256[] memory _b)
private
pure
returns(uint256[] memory, uint256[] memory)
{
return (_b, _a);
}
/**
* @dev Makes an in memory copy of the array passed in
* @param _list The pointer to the array to be copied
*/
function copy(uint256[] memory _list)
private
pure
returns(uint256[] memory)
{
uint256[] memory list2 = new uint256[](_list.length);
for (uint256 i = 0; i < _list.length; i++) {
list2[i] = _list[i];
}
return list2;
}
}
contract CronAggregator is Ownable {
using SafeMath for uint256;
enum AggregationStatus {
DEFAULT,
OPEN_IDLE,
OPEN_ONGOING,
CLOSED_SUCCESS,
CLOSED_FAILIURE
}
struct Aggregation {
uint256 minContribution;
uint256 maxContribution;
uint256[] contributions;
address[] contributors;
}
uint256 public minContribution;
uint256 public fixedTimeBase;
uint256 public fixedTimeFrequency;
uint256 public contributionTimeWindow;
uint256 constant internal MAX_CONTRIBUTOR_COUNT = 45;
uint256 public currentValue;
uint256 public latestCompletedAggregation;
uint256 public latestAggregationValue;
uint256 public latestAggregationHeight;
uint256 public latestAggregationTime;
uint256 public aggregationCounter = 0;
bytes32 internal aggregationSession;
mapping(uint256 => Aggregation) internal aggregation;
mapping(address => bool) public authorizedToContribute;
address[] public authorizedContributors;
bool public onlyUniqueContributors;
event ContributionReceived(uint256 indexed response, uint256 indexed aggregationId, address indexed sender);
event CurrentValueUpdated(uint256 indexed current, uint256 indexed aggregationId);
constructor (
uint256 _currentValue,
uint256 _fixedTimeBase,
uint256 _fixedTimeFrequency,
uint256 _contributionTimeWindow,
uint256 _minContribution,
bool _onlyUniqueContributors,
address[] memory _authorizedContributors
)
public
onlyValidSettings(
_fixedTimeBase,
_fixedTimeFrequency,
_contributionTimeWindow,
_minContribution,
_authorizedContributors
)
{
currentValue = _currentValue;
_updateTimeSettings(
_fixedTimeBase,
_fixedTimeFrequency,
_contributionTimeWindow
);
_updateContributionSettings(
_minContribution,
_onlyUniqueContributors,
_authorizedContributors
);
}
function cronCallback(uint256 _value)
external
onlyContributionPeriod
onlyAuthorizedContributor(msg.sender)
{
uint256 epoch = block.timestamp.sub(fixedTimeBase).div(fixedTimeFrequency);
bytes32 assumedSession = keccak256(
abi.encodePacked(
epoch,
fixedTimeBase,
fixedTimeFrequency,
aggregationCounter
)
);
if (assumedSession != aggregationSession) {
delete aggregation[aggregationCounter];
aggregationCounter = aggregationCounter.add(1);
aggregationSession = keccak256(
abi.encodePacked(
epoch,
fixedTimeBase,
fixedTimeFrequency,
aggregationCounter
)
);
aggregation[aggregationCounter].minContribution = minContribution;
aggregation[aggregationCounter].maxContribution = authorizedContributors.length;
} else {
require(!_isFull(aggregationCounter), "Maximum contributions reached");
}
if (onlyUniqueContributors) {
require(!_alreadyContributed(aggregationCounter), "Address already contributed");
aggregation[aggregationCounter].contributors.push(msg.sender);
}
aggregation[aggregationCounter].contributions.push(_value);
emit ContributionReceived(_value, aggregationCounter, msg.sender);
_updateLatestAnswer(aggregationCounter);
}
function setCurrentValue(uint256 _value)
external
onlyOwner
{
currentValue = _value;
}
function getStatus()
public
view
returns(AggregationStatus)
{
uint256 epoch = block.timestamp.sub(fixedTimeBase).div(fixedTimeFrequency);
bytes32 assumedSession = keccak256(
abi.encodePacked(
epoch,
fixedTimeBase,
fixedTimeFrequency,
aggregationCounter
)
);
if (isContributionPeriod()) {
if (assumedSession == aggregationSession) {
return AggregationStatus.OPEN_ONGOING;
} else {
return AggregationStatus.OPEN_IDLE;
}
} else {
if (aggregationCounter == 0) {
return AggregationStatus.DEFAULT;
}
if (assumedSession == aggregationSession) {
if (latestCompletedAggregation == aggregationCounter) {
return AggregationStatus.CLOSED_SUCCESS;
} else {
return AggregationStatus.CLOSED_FAILIURE;
}
} else {
return AggregationStatus.CLOSED_FAILIURE;
}
}
}
function updateContributionSettings(
uint256 _minContribution,
bool _onlyUniqueContributors,
address[] memory _authorizedContributors
)
public
notContributionPeriod
onlyOwner
onlyValidSettings(
fixedTimeBase,
fixedTimeFrequency,
contributionTimeWindow,
_minContribution,
_authorizedContributors
)
{
for (uint8 i = 0; i < authorizedContributors.length; i++) {
delete authorizedToContribute[authorizedContributors[i]];
}
delete authorizedContributors;
_updateContributionSettings(
_minContribution,
_onlyUniqueContributors,
_authorizedContributors
);
}
function updateTimeSettings(
uint256 _fixedTimeBase,
uint256 _fixedTimeFrequency,
uint256 _contributionTimeWindow
)
public
notContributionPeriod
onlyOwner
onlyValidSettings(
_fixedTimeBase,
_fixedTimeFrequency,
_contributionTimeWindow,
minContribution,
authorizedContributors
)
{
_updateTimeSettings(
_fixedTimeBase,
_fixedTimeFrequency,
_contributionTimeWindow
);
}
function isAggregated()
public
view
returns (bool)
{
return currentValue == latestAggregationValue;
}
function getAuthorizedContributors()
public
view
returns (address[] memory)
{
return authorizedContributors;
}
function isContributionPeriod()
internal
view
returns (bool)
{
uint256 _rem = block.timestamp.sub(fixedTimeBase) % fixedTimeFrequency;
return _rem <= contributionTimeWindow;
}
function _updateContributionSettings(
uint256 _minContribution,
bool _onlyUniqueContributors,
address[] memory _authorizedContributors
)
internal
{
minContribution = _minContribution;
onlyUniqueContributors = _onlyUniqueContributors;
authorizedContributors = _authorizedContributors;
for (uint8 i = 0; i < _authorizedContributors.length; i++) {
authorizedToContribute[_authorizedContributors[i]] = true;
}
}
function _updateTimeSettings(
uint256 _fixedTimeBase,
uint256 _fixedTimeFrequency,
uint256 _contributionTimeWindow
)
internal
{
fixedTimeBase = _fixedTimeBase;
fixedTimeFrequency = _fixedTimeFrequency;
contributionTimeWindow = _contributionTimeWindow;
}
function _isFull(uint256 _aggregationId)
internal
view
returns (bool)
{
return aggregation[_aggregationId].contributions.length >= aggregation[_aggregationId].maxContribution;
}
function _alreadyContributed(uint256 _aggregationId)
private
view
returns (bool)
{
for (uint8 i = 0; i < aggregation[_aggregationId].contributors.length; i++) {
if (aggregation[_aggregationId].contributors[i] == msg.sender) {
return true;
}
}
return false;
}
function _updateLatestAnswer(uint256 _aggregationId)
private
ensureMinContributionsReceived(_aggregationId)
ensureOnlyLatestAnswer(_aggregationId)
{
currentValue = UintMedian.calculate(aggregation[_aggregationId].contributions);
latestAggregationValue = currentValue;
latestCompletedAggregation = _aggregationId;
latestAggregationHeight = block.number;
latestAggregationTime = block.timestamp;
emit CurrentValueUpdated(currentValue, _aggregationId);
}
/**
* @dev Prevents taking an action if the minimum number of contributions has not
* been received for an answer.
* @param _aggregationId The the identifier that keeps track of the contributions.
*/
modifier ensureMinContributionsReceived(uint256 _aggregationId) {
if (aggregation[_aggregationId].contributions.length >= aggregation[_aggregationId].minContribution) {
_;
}
}
modifier ensureOnlyLatestAnswer(uint256 _aggregationId) {
if (latestCompletedAggregation <= _aggregationId) {
_;
}
}
modifier onlyAuthorizedContributor(address _address) {
require(
authorizedToContribute[_address],
"Not an authorized address"
);
_;
}
modifier onlyContributionPeriod {
require(isContributionPeriod(), "Not contribution period");
_;
}
modifier notContributionPeriod {
require(!isContributionPeriod(), "Contribution period");
_;
}
modifier onlyValidSettings (
uint256 _fixedTimeBase,
uint256 _fixedTimeFrequency,
uint256 _contributionTimeWindow,
uint256 _minContribution,
address[] memory _authorizedContributors
) {
require(_authorizedContributors.length <= MAX_CONTRIBUTOR_COUNT, "Cannot have more than 45 contributors");
require(
_authorizedContributors.length >= _minContribution,
"Must have at least as many contributors as required contributions"
);
require(_fixedTimeBase <= block.timestamp, "Fixed-time base must be in the past");
//require(_contributionTimeWindow > 0, "Contribution window cannot be 0");
require(_fixedTimeFrequency > 0, "Time frequency cannot be 0");
require(_fixedTimeFrequency > _contributionTimeWindow, "Freq must be > than window");
_;
}
}
pragma solidity >= 0.4.0;
library BytesUtils {
/*
* @dev Returns the keccak-256 hash of a byte range.
* @param self The byte string to hash.
* @param offset The position to start hashing at.
* @param len The number of bytes to hash.
* @return The hash of the byte range.
*/
function keccak(bytes memory self, uint offset, uint len) internal pure returns (bytes32 ret) {
require(offset + len <= self.length);
assembly {
ret := keccak256(add(add(self, 32), offset), len)
}
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal.
* @param self The first bytes to compare.
* @param other The second bytes to compare.
* @return The result of the comparison.
*/
function compare(bytes memory self, bytes memory other) internal pure returns (int) {
return compare(self, 0, self.length, other, 0, other.length);
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal. Comparison is done per-rune,
* on unicode codepoints.
* @param self The first bytes to compare.
* @param offset The offset of self.
* @param len The length of self.
* @param other The second bytes to compare.
* @param otheroffset The offset of the other string.
* @param otherlen The length of the other string.
* @return The result of the comparison.
*/
function compare(bytes memory self, uint offset, uint len, bytes memory other, uint otheroffset, uint otherlen) internal pure returns (int) {
uint shortest = len;
if (otherlen < len)
shortest = otherlen;
uint selfptr;
uint otherptr;
assembly {
selfptr := add(self, add(offset, 32))
otherptr := add(other, add(otheroffset, 32))
}
for (uint idx = 0; idx < shortest; idx += 32) {
uint a;
uint b;
assembly {
a := mload(selfptr)
b := mload(otherptr)
}
if (a != b) {
// Mask out irrelevant bytes and check again
uint mask;
if (shortest > 32) {
mask = uint256(- 1); // aka 0xffffff....
} else {
mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
}
uint diff = (a & mask) - (b & mask);
if (diff != 0)
return int(diff);
}
selfptr += 32;
otherptr += 32;
}
return int(len) - int(otherlen);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @param len The number of bytes to compare
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset, uint len) internal pure returns (bool) {
return keccak(self, offset, len) == keccak(other, otherOffset, len);
}
/*
* @dev Returns true if the two byte ranges are equal with offsets.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset) internal pure returns (bool) {
return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
}
/*
* @dev Compares a range of 'self' to all of 'other' and returns True iff
* they are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other) internal pure returns (bool) {
return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, bytes memory other) internal pure returns(bool) {
return self.length == other.length && equals(self, 0, other, 0, self.length);
}
/*
* @dev Returns the 8-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 8 bits of the string, interpreted as an integer.
*/
function readUint8(bytes memory self, uint idx) internal pure returns (uint8 ret) {
return uint8(self[idx]);
}
/*
* @dev Returns the 16-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 16 bits of the string, interpreted as an integer.
*/
function readUint16(bytes memory self, uint idx) internal pure returns (uint16 ret) {
require(idx + 2 <= self.length);
assembly {
ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
}
}
/*
* @dev Returns the 32-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bits of the string, interpreted as an integer.
*/
function readUint32(bytes memory self, uint idx) internal pure returns (uint32 ret) {
require(idx + 4 <= self.length);
assembly {
ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes32(bytes memory self, uint idx) internal pure returns (bytes32 ret) {
require(idx + 32 <= self.length);
assembly {
ret := mload(add(add(self, 32), idx))
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes20(bytes memory self, uint idx) internal pure returns (bytes20 ret) {
require(idx + 20 <= self.length);
assembly {
ret := and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
}
}
/*
* @dev Returns the n byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes.
* @param len The number of bytes.
* @return The specified 32 bytes of the string.
*/
function readBytesN(bytes memory self, uint idx, uint len) internal pure returns (bytes32 ret) {
require(len <= 32);
require(idx + len <= self.length);
assembly {
let mask := not(sub(exp(256, sub(32, len)), 1))
ret := and(mload(add(add(self, 32), idx)), mask)
}
}
function memcpy(uint dest, uint src, uint len) private pure {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/*
* @dev Copies a substring into a new byte string.
* @param self The byte string to copy from.
* @param offset The offset to start copying at.
* @param len The number of bytes to copy.
*/
function substring(bytes memory self, uint offset, uint len) internal pure returns(bytes memory) {
require(offset + len <= self.length);
bytes memory ret = new bytes(len);
uint dest;
uint src;
assembly {
dest := add(ret, 32)
src := add(add(self, 32), offset)
}
memcpy(dest, src, len);
return ret;
}
// Maps characters from 0x30 to 0x7A to their base32 values.
// 0xFF represents invalid characters in that range.
bytes constant base32HexTable = hex'00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F';
/**
* @dev Decodes unpadded base32 data of up to one word in length.
* @param self The data to decode.
* @param off Offset into the string to start at.
* @param len Number of characters to decode.
* @return The decoded data, left aligned.
*/
function base32HexDecodeWord(bytes memory self, uint off, uint len) internal pure returns(bytes32) {
require(len <= 52);
uint ret = 0;
uint8 decoded;
for(uint i = 0; i < len; i++) {
bytes1 char = self[off + i];
require(char >= 0x30 && char <= 0x7A);
decoded = uint8(base32HexTable[uint(uint8(char)) - 0x30]);
require(decoded <= 0x20);
if(i == len - 1) {
break;
}
ret = (ret << 5) | decoded;
}
uint bitlen = len * 5;
if(len % 8 == 0) {
// Multiple of 8 characters, no padding
ret = (ret << 5) | decoded;
} else if(len % 8 == 2) {
// Two extra characters - 1 byte
ret = (ret << 3) | (decoded >> 2);
bitlen -= 2;
} else if(len % 8 == 4) {
// Four extra characters - 2 bytes
ret = (ret << 1) | (decoded >> 4);
bitlen -= 4;
} else if(len % 8 == 5) {
// Five extra characters - 3 bytes
ret = (ret << 4) | (decoded >> 1);
bitlen -= 1;
} else if(len % 8 == 7) {
// Seven extra characters - 4 bytes
ret = (ret << 2) | (decoded >> 3);
bitlen -= 3;
} else {
revert();
}
return bytes32(ret << (256 - bitlen));
}
}
/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library Buffer {
/**
* @dev Represents a mutable buffer. Buffers have a current value (buf) and
* a capacity. The capacity may be longer than the current value, in
* which case it can be extended without the need to allocate more memory.
*/
struct buffer {
bytes buf;
uint capacity;
}
/**
* @dev Initializes a buffer with an initial capacity.
* @param buf The buffer to initialize.
* @param capacity The number of bytes of space to allocate the buffer.
* @return The buffer, for chaining.
*/
function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
if (capacity % 32 != 0) {
capacity += 32 - (capacity % 32);
}
// Allocate space for the buffer data
buf.capacity = capacity;
assembly {
let ptr := mload(0x40)
mstore(buf, ptr)
mstore(ptr, 0)
mstore(0x40, add(ptr, capacity))
}
return buf;
}
/**
* @dev Initializes a new buffer from an existing bytes object.
* Changes to the buffer may mutate the original value.
* @param b The bytes object to initialize the buffer with.
* @return A new buffer.
*/
function fromBytes(bytes memory b) internal pure returns(buffer memory) {
buffer memory buf;
buf.buf = b;
buf.capacity = b.length;
return buf;
}
function resize(buffer memory buf, uint capacity) private pure {
bytes memory oldbuf = buf.buf;
init(buf, capacity);
append(buf, oldbuf);
}
function max(uint a, uint b) private pure returns(uint) {
if (a > b) {
return a;
}
return b;
}
/**
* @dev Sets buffer length to 0.
* @param buf The buffer to truncate.
* @return The original buffer, for chaining..
*/
function truncate(buffer memory buf) internal pure returns (buffer memory) {
assembly {
let bufptr := mload(buf)
mstore(bufptr, 0)
}
return buf;
}
/**
* @dev Writes a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The start offset to write to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
require(len <= data.length);
if (off + len + buf.buf.length > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint dest;
uint src;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Start address = buffer address + offset + sizeof(buffer length)
dest := add(add(bufptr, 32), off)
// Update buffer length if we're extending it
if gt(add(len, off), buflen) {
mstore(bufptr, add(len, off))
}
src := add(data, 32)
}
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
return buf;
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, len);
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, data.length);
}
/**
* @dev Writes a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write the byte at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
if (off > buf.capacity) {
resize(buf, buf.capacity * 2);
}
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Address = buffer address + sizeof(buffer length) + off
let dest := add(add(bufptr, off), 32)
mstore8(dest, data)
// Update buffer length if we extended it
if eq(off, buflen) {
mstore(bufptr, add(buflen, 1))
}
}
return buf;
}
/**
* @dev Appends a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
return writeUint8(buf, buf.buf.length, data);
}
/**
* @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (left-aligned).
* @return The original buffer, for chaining.
*/
function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
if (len + off > buf.capacity) {
resize(buf, max(buf.capacity, len) * 2);
}
uint mask = 256 ** len - 1;
// Right-align data
data = data >> (8 * (32 - len));
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + sizeof(buffer length) + off + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
/**
* @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
return write(buf, off, bytes32(data), 20);
}
/**
* @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chhaining.
*/
function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, bytes32(data), 20);
}
/**
* @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, 32);
}
/**
* @dev Writes an integer to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (right-aligned).
* @return The original buffer, for chaining.
*/
function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
if (len + off > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint mask = 256 ** len - 1;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + off + sizeof(buffer length) + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
}
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
interface PriceOracle {
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param expires When the name presently expires (0 if this is a new registration).
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint expires, uint duration) external view returns(uint);
}
/**
* A generic resolver interface which includes all the functions including the ones deprecated
*/
interface Resolver{
event AddrChanged(bytes32 indexed node, address a);
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
event NameChanged(bytes32 indexed node, string name);
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
event ContenthashChanged(bytes32 indexed node, bytes hash);
/* Deprecated events */
event ContentChanged(bytes32 indexed node, bytes32 hash);
function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory);
function addr(bytes32 node) external view returns (address);
function addr(bytes32 node, uint coinType) external view returns(bytes memory);
function contenthash(bytes32 node) external view returns (bytes memory);
function dnsrr(bytes32 node) external view returns (bytes memory);
function name(bytes32 node) external view returns (string memory);
function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
function text(bytes32 node, string calldata key) external view returns (string memory);
function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address);
function setABI(bytes32 node, uint256 contentType, bytes calldata data) external;
function setAddr(bytes32 node, address addr) external;
function setAddr(bytes32 node, uint coinType, bytes calldata a) external;
function setContenthash(bytes32 node, bytes calldata hash) external;
function setDnsrr(bytes32 node, bytes calldata data) external;
function setName(bytes32 node, string calldata _name) external;
function setPubkey(bytes32 node, bytes32 x, bytes32 y) external;
function setText(bytes32 node, string calldata key, string calldata value) external;
function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external;
function supportsInterface(bytes4 interfaceID) external pure returns (bool);
/* Deprecated functions */
function content(bytes32 node) external view returns (bytes32);
function multihash(bytes32 node) external view returns (bytes memory);
function setContent(bytes32 node, bytes32 hash) external;
function setMultihash(bytes32 node, bytes calldata hash) external;
}
library StringUtils {
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
uint len;
uint i = 0;
uint bytelength = bytes(s).length;
for(len = 0; i < bytelength; len++) {
byte b = bytes(s)[i];
if(b < 0x80) {
i += 1;
} else if (b < 0xE0) {
i += 2;
} else if (b < 0xF0) {
i += 3;
} else if (b < 0xF8) {
i += 4;
} else if (b < 0xFC) {
i += 5;
} else {
i += 6;
}
}
return len;
}
}
interface DNSSEC {
event AlgorithmUpdated(uint8 id, address addr);
event DigestUpdated(uint8 id, address addr);
event NSEC3DigestUpdated(uint8 id, address addr);
event RRSetUpdated(bytes name, bytes rrset);
function submitRRSets(bytes calldata data, bytes calldata proof) external returns (bytes memory);
function submitRRSet(bytes calldata input, bytes calldata sig, bytes calldata proof) external returns (bytes memory);
function deleteRRSet(uint16 deleteType, bytes calldata deleteName, bytes calldata nsec, bytes calldata sig, bytes calldata proof) external;
function rrdata(uint16 dnstype, bytes calldata name) external view returns (uint32, uint64, bytes20);
}
/**
* The ENS registry contract.
*/
contract ENSRegistry is ENS {
struct Record {
address owner;
address resolver;
uint64 ttl;
}
mapping (bytes32 => Record) records;
// Permits modifications only by the owner of the specified node.
modifier only_owner(bytes32 node) {
require(records[node].owner == msg.sender);
_;
}
/**
* @dev Constructs a new ENS registrar.
*/
constructor() public {
records[0x0].owner = msg.sender;
}
/**
* @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
* @param node The node to transfer ownership of.
* @param owner The address of the new owner.
*/
function setOwner(bytes32 node, address owner) external only_owner(node) {
emit Transfer(node, owner);
records[node].owner = owner;
}
/**
* @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
*/
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external only_owner(node) {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
emit NewOwner(node, label, owner);
records[subnode].owner = owner;
}
/**
* @dev Sets the resolver address for the specified node.
* @param node The node to update.
* @param resolver The address of the resolver.
*/
function setResolver(bytes32 node, address resolver) external only_owner(node) {
emit NewResolver(node, resolver);
records[node].resolver = resolver;
}
/**
* @dev Sets the TTL for the specified node.
* @param node The node to update.
* @param ttl The TTL in seconds.
*/
function setTTL(bytes32 node, uint64 ttl) external only_owner(node) {
emit NewTTL(node, ttl);
records[node].ttl = ttl;
}
/**
* @dev Returns the address that owns the specified node.
* @param node The specified node.
* @return address of the owner.
*/
function owner(bytes32 node) external view returns (address) {
return records[node].owner;
}
/**
* @dev Returns the address of the resolver for the specified node.
* @param node The specified node.
* @return address of the resolver.
*/
function resolver(bytes32 node) external view returns (address) {
return records[node].resolver;
}
/**
* @dev Returns the TTL of a node, and any records associated with it.
* @param node The specified node.
* @return ttl of the node.
*/
function ttl(bytes32 node) external view returns (uint64) {
return records[node].ttl;
}
}
/**
* @dev RRUtils is a library that provides utilities for parsing DNS resource records.
*/
library RRUtils {
using BytesUtils for *;
using Buffer for *;
/**
* @dev Returns the number of bytes in the DNS name at 'offset' in 'self'.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The length of the DNS name at 'offset', in bytes.
*/
function nameLength(bytes memory self, uint offset) internal pure returns(uint) {
uint idx = offset;
while (true) {
assert(idx < self.length);
uint labelLen = self.readUint8(idx);
idx += labelLen + 1;
if (labelLen == 0) {
break;
}
}
return idx - offset;
}
/**
* @dev Returns a DNS format name at the specified offset of self.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The name.
*/
function readName(bytes memory self, uint offset) internal pure returns(bytes memory ret) {
uint len = nameLength(self, offset);
return self.substring(offset, len);
}
/**
* @dev Returns the number of labels in the DNS name at 'offset' in 'self'.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The number of labels in the DNS name at 'offset', in bytes.
*/
function labelCount(bytes memory self, uint offset) internal pure returns(uint) {
uint count = 0;
while (true) {
assert(offset < self.length);
uint labelLen = self.readUint8(offset);
offset += labelLen + 1;
if (labelLen == 0) {
break;
}
count += 1;
}
return count;
}
/**
* @dev An iterator over resource records.
*/
struct RRIterator {
bytes data;
uint offset;
uint16 dnstype;
uint16 class;
uint32 ttl;
uint rdataOffset;
uint nextOffset;
}
/**
* @dev Begins iterating over resource records.
* @param self The byte string to read from.
* @param offset The offset to start reading at.
* @return An iterator object.
*/
function iterateRRs(bytes memory self, uint offset) internal pure returns (RRIterator memory ret) {
ret.data = self;
ret.nextOffset = offset;
next(ret);
}
/**
* @dev Returns true iff there are more RRs to iterate.
* @param iter The iterator to check.
* @return True iff the iterator has finished.
*/
function done(RRIterator memory iter) internal pure returns(bool) {
return iter.offset >= iter.data.length;
}
/**
* @dev Moves the iterator to the next resource record.
* @param iter The iterator to advance.
*/
function next(RRIterator memory iter) internal pure {
iter.offset = iter.nextOffset;
if (iter.offset >= iter.data.length) {
return;
}
// Skip the name
uint off = iter.offset + nameLength(iter.data, iter.offset);
// Read type, class, and ttl
iter.dnstype = iter.data.readUint16(off);
off += 2;
iter.class = iter.data.readUint16(off);
off += 2;
iter.ttl = iter.data.readUint32(off);
off += 4;
// Read the rdata
uint rdataLength = iter.data.readUint16(off);
off += 2;
iter.rdataOffset = off;
iter.nextOffset = off + rdataLength;
}
/**
* @dev Returns the name of the current record.
* @param iter The iterator.
* @return A new bytes object containing the owner name from the RR.
*/
function name(RRIterator memory iter) internal pure returns(bytes memory) {
return iter.data.substring(iter.offset, nameLength(iter.data, iter.offset));
}
/**
* @dev Returns the rdata portion of the current record.
* @param iter The iterator.
* @return A new bytes object containing the RR's RDATA.
*/
function rdata(RRIterator memory iter) internal pure returns(bytes memory) {
return iter.data.substring(iter.rdataOffset, iter.nextOffset - iter.rdataOffset);
}
/**
* @dev Checks if a given RR type exists in a type bitmap.
* @param self The byte string to read the type bitmap from.
* @param offset The offset to start reading at.
* @param rrtype The RR type to check for.
* @return True if the type is found in the bitmap, false otherwise.
*/
function checkTypeBitmap(bytes memory self, uint offset, uint16 rrtype) internal pure returns (bool) {
uint8 typeWindow = uint8(rrtype >> 8);
uint8 windowByte = uint8((rrtype & 0xff) / 8);
uint8 windowBitmask = uint8(uint8(1) << (uint8(7) - uint8(rrtype & 0x7)));
for (uint off = offset; off < self.length;) {
uint8 window = self.readUint8(off);
uint8 len = self.readUint8(off + 1);
if (typeWindow < window) {
// We've gone past our window; it's not here.
return false;
} else if (typeWindow == window) {
// Check this type bitmap
if (len * 8 <= windowByte) {
// Our type is past the end of the bitmap
return false;
}
return (self.readUint8(off + windowByte + 2) & windowBitmask) != 0;
} else {
// Skip this type bitmap
off += len + 2;
}
}
return false;
}
function compareNames(bytes memory self, bytes memory other) internal pure returns (int) {
if (self.equals(other)) {
return 0;
}
uint off;
uint otheroff;
uint prevoff;
uint otherprevoff;
uint counts = labelCount(self, 0);
uint othercounts = labelCount(other, 0);
// Keep removing labels from the front of the name until both names are equal length
while (counts > othercounts) {
prevoff = off;
off = progress(self, off);
counts--;
}
while (othercounts > counts) {
otherprevoff = otheroff;
otheroff = progress(other, otheroff);
othercounts--;
}
// Compare the last nonequal labels to each other
while (counts > 0 && !self.equals(off, other, otheroff)) {
prevoff = off;
off = progress(self, off);
otherprevoff = otheroff;
otheroff = progress(other, otheroff);
counts -= 1;
}
if (off == 0) {
return -1;
}
if(otheroff == 0) {
return 1;
}
return self.compare(prevoff + 1, self.readUint8(prevoff), other, otherprevoff + 1, other.readUint8(otherprevoff));
}
function progress(bytes memory body, uint off) internal pure returns(uint) {
return off + 1 + body.readUint8(off);
}
}
library DNSClaimChecker {
using BytesUtils for bytes;
using RRUtils for *;
using Buffer for Buffer.buffer;
uint16 constant CLASS_INET = 1;
uint16 constant TYPE_TXT = 16;
function getLabels(bytes memory name) internal view returns (bytes32, bytes32) {
uint len = name.readUint8(0);
uint second = name.readUint8(len + 1);
require(name.readUint8(len + second + 2) == 0);
return (name.keccak(1, len), keccak256(abi.encodePacked(bytes32(0), name.keccak(2 + len, second))));
}
function getOwnerAddress(DNSSEC oracle, bytes memory name, bytes memory proof)
internal
view
returns (address, bool)
{
// Add "_ens." to the front of the name.
Buffer.buffer memory buf;
buf.init(name.length + 5);
buf.append("\x04_ens");
buf.append(name);
bytes20 hash;
uint64 inserted;
// Check the provided TXT record has been validated by the oracle
(, inserted, hash) = oracle.rrdata(TYPE_TXT, buf.buf);
if (hash == bytes20(0) && proof.length == 0) return (address(0x0), false);
require(hash == bytes20(keccak256(proof)));
for (RRUtils.RRIterator memory iter = proof.iterateRRs(0); !iter.done(); iter.next()) {
require(inserted + iter.ttl >= now, "DNS record is stale; refresh or delete it before proceeding.");
bool found;
address addr;
(addr, found) = parseRR(proof, iter.rdataOffset);
if (found) {
return (addr, true);
}
}
return (address(0x0), false);
}
function parseRR(bytes memory rdata, uint idx) internal pure returns (address, bool) {
while (idx < rdata.length) {
uint len = rdata.readUint8(idx); idx += 1;
bool found;
address addr;
(addr, found) = parseString(rdata, idx, len);
if (found) return (addr, true);
idx += len;
}
return (address(0x0), false);
}
function parseString(bytes memory str, uint idx, uint len) internal pure returns (address, bool) {
// TODO: More robust parsing that handles whitespace and multiple key/value pairs
if (str.readUint32(idx) != 0x613d3078) return (address(0x0), false); // 0x613d3078 == 'a=0x'
if (len < 44) return (address(0x0), false);
return hexToAddress(str, idx + 4);
}
function hexToAddress(bytes memory str, uint idx) internal pure returns (address, bool) {
if (str.length - idx < 40) return (address(0x0), false);
uint ret = 0;
for (uint i = idx; i < idx + 40; i++) {
ret <<= 4;
uint x = str.readUint8(i);
if (x >= 48 && x < 58) {
ret |= x - 48;
} else if (x >= 65 && x < 71) {
ret |= x - 55;
} else if (x >= 97 && x < 103) {
ret |= x - 87;
} else {
return (address(0x0), false);
}
}
return (address(ret), true);
}
}
/**
* @dev An ENS registrar that allows the owner of a DNS name to claim the
* corresponding name in ENS.
*/
contract DNSRegistrar {
DNSSEC public oracle;
ENS public ens;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private DNSSEC_CLAIM_ID = bytes4(
keccak256("claim(bytes,bytes)") ^
keccak256("proveAndClaim(bytes,bytes,bytes)") ^
keccak256("oracle()")
);
event Claim(bytes32 indexed node, address indexed owner, bytes dnsname);
constructor(DNSSEC _dnssec, ENS _ens) public {
oracle = _dnssec;
ens = _ens;
}
/**
* @dev Claims a name by proving ownership of its DNS equivalent.
* @param name The name to claim, in DNS wire format.
* @param proof A DNS RRSet proving ownership of the name. Must be verified
* in the DNSSEC oracle before calling. This RRSET must contain a TXT
* record for '_ens.' + name, with the value 'a=0x...'. Ownership of
* the name will be transferred to the address specified in the TXT
* record.
*/
function claim(bytes memory name, bytes memory proof) public {
address addr;
(addr,) = DNSClaimChecker.getOwnerAddress(oracle, name, proof);
bytes32 labelHash;
bytes32 rootNode;
(labelHash, rootNode) = DNSClaimChecker.getLabels(name);
ens.setSubnodeOwner(rootNode, labelHash, addr);
emit Claim(keccak256(abi.encodePacked(rootNode, labelHash)), addr, name);
}
/**
* @dev Submits proofs to the DNSSEC oracle, then claims a name using those proofs.
* @param name The name to claim, in DNS wire format.
* @param input The data to be passed to the Oracle's `submitProofs` function. The last
* proof must be the TXT record required by the registrar.
* @param proof The proof record for the first element in input.
*/
function proveAndClaim(bytes memory name, bytes memory input, bytes memory proof) public {
proof = oracle.submitRRSets(input, proof);
claim(name, proof);
}
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == DNSSEC_CLAIM_ID;
}
}
// We need this to persuade Truffle to compile all the code we want to use.
// We need this second file because Solidity treats imports lexically, and
// without it we have conflicts between identically named imports.
/**
* @title IERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract BaseRegistrar is IERC721, Ownable {
uint constant public GRACE_PERIOD = 90 days;
event ControllerAdded(address indexed controller);
event ControllerRemoved(address indexed controller);
event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
event NameRenewed(uint256 indexed id, uint expires);
// The ENS registry
ENS public ens;
// The namehash of the TLD this registrar owns (eg, .eth)
bytes32 public baseNode;
// A map of addresses that are authorised to register and renew names.
mapping(address=>bool) public controllers;
// Authorises a controller, who can register and renew domains.
function addController(address controller) external;
// Revoke controller permission for an address.
function removeController(address controller) external;
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external;
// Returns the expiration timestamp of the specified label hash.
function nameExpires(uint256 id) external view returns(uint);
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool);
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint);
function renew(uint256 id, uint duration) external returns(uint);
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external;
}
/**
* @dev A registrar controller for registering and renewing names at fixed cost.
*/
contract ETHRegistrarController is Ownable {
using StringUtils for *;
uint constant public MIN_REGISTRATION_DURATION = 28 days;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4(
keccak256("rentPrice(string,uint256)") ^
keccak256("available(string)") ^
keccak256("makeCommitment(string,address,bytes32)") ^
keccak256("commit(bytes32)") ^
keccak256("register(string,address,uint256,bytes32)") ^
keccak256("renew(string,uint256)")
);
bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4(
keccak256("registerWithConfig(string,address,uint256,bytes32,address,address)") ^
keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)")
);
BaseRegistrar base;
PriceOracle prices;
uint public minCommitmentAge;
uint public maxCommitmentAge;
mapping(bytes32=>uint) public commitments;
event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
event NewPriceOracle(address indexed oracle);
constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public {
require(_maxCommitmentAge > _minCommitmentAge);
base = _base;
prices = _prices;
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}
function rentPrice(string memory name, uint duration) view public returns(uint) {
bytes32 hash = keccak256(bytes(name));
return prices.price(name, base.nameExpires(uint256(hash)), duration);
}
function valid(string memory name) public pure returns(bool) {
return name.strlen() >= 3;
}
function available(string memory name) public view returns(bool) {
bytes32 label = keccak256(bytes(name));
return valid(name) && base.available(uint256(label));
}
function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) {
return makeCommitmentWithConfig(name, owner, secret, address(0), address(0));
}
function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) {
bytes32 label = keccak256(bytes(name));
if (resolver == address(0) && addr == address(0)) {
return keccak256(abi.encodePacked(label, owner, secret));
}
require(resolver != address(0));
return keccak256(abi.encodePacked(label, owner, resolver, addr, secret));
}
function commit(bytes32 commitment) public {
require(commitments[commitment] + maxCommitmentAge < now);
commitments[commitment] = now;
}
function register(string calldata name, address owner, uint duration, bytes32 secret) external payable {
registerWithConfig(name, owner, duration, secret, address(0), address(0));
}
function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {
bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr);
uint cost = _consumeCommitment(name, duration, commitment);
bytes32 label = keccak256(bytes(name));
uint256 tokenId = uint256(label);
uint expires;
if(resolver != address(0)) {
// Set this contract as the (temporary) owner, giving it
// permission to set up the resolver.
expires = base.register(tokenId, address(this), duration);
// The nodehash of this label
bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label));
// Set the resolver
base.ens().setResolver(nodehash, resolver);
// Configure the resolver
if (addr != address(0)) {
Resolver(resolver).setAddr(nodehash, addr);
}
// Now transfer full ownership to the expeceted owner
base.reclaim(tokenId, owner);
base.transferFrom(address(this), owner, tokenId);
} else {
require(addr == address(0));
expires = base.register(tokenId, owner, duration);
}
emit NameRegistered(name, label, owner, cost, expires);
// Refund any extra payment
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
}
function renew(string calldata name, uint duration) external payable {
uint cost = rentPrice(name, duration);
require(msg.value >= cost);
bytes32 label = keccak256(bytes(name));
uint expires = base.renew(uint256(label), duration);
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
emit NameRenewed(name, label, cost, expires);
}
function setPriceOracle(PriceOracle _prices) public onlyOwner {
prices = _prices;
emit NewPriceOracle(address(prices));
}
function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner {
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}
function withdraw() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == COMMITMENT_CONTROLLER_ID ||
interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID;
}
function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {
// Require a valid commitment
require(commitments[commitment] + minCommitmentAge <= now);
// If the commitment is too old, or the name is registered, stop
require(commitments[commitment] + maxCommitmentAge > now);
require(available(name));
delete(commitments[commitment]);
uint cost = rentPrice(name, duration);
require(duration >= MIN_REGISTRATION_DURATION);
require(msg.value >= cost);
return cost;
}
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
contract SimplePriceOracle is Ownable, PriceOracle {
using SafeMath for *;
// Rent in wei per second
uint public rentPrice;
event RentPriceChanged(uint price);
constructor(uint _rentPrice) public {
setPrice(_rentPrice);
}
function setPrice(uint _rentPrice) public onlyOwner {
rentPrice = _rentPrice;
emit RentPriceChanged(_rentPrice);
}
/**
* @dev Returns the price to register or renew a name.
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata /*name*/, uint /*expires*/, uint duration) external view returns(uint) {
return duration.mul(rentPrice);
}
}
pragma solidity >= 0.4.0;
/**
* @title IERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
// Logged when an operator is added or removed.
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external;
function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external;
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32);
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function setApprovalForAll(address operator, bool approved) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
function recordExists(bytes32 node) external view returns (bool);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract BaseRegistrar is IERC721, Ownable {
uint constant public GRACE_PERIOD = 90 days;
event ControllerAdded(address indexed controller);
event ControllerRemoved(address indexed controller);
event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
event NameRenewed(uint256 indexed id, uint expires);
// The ENS registry
ENS public ens;
// The namehash of the TLD this registrar owns (eg, .eth)
bytes32 public baseNode;
// A map of addresses that are authorised to register and renew names.
mapping(address=>bool) public controllers;
// Authorises a controller, who can register and renew domains.
function addController(address controller) external;
// Revoke controller permission for an address.
function removeController(address controller) external;
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external;
// Returns the expiration timestamp of the specified label hash.
function nameExpires(uint256 id) external view returns(uint);
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool);
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint);
function renew(uint256 id, uint duration) external returns(uint);
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external;
}
/**
* @dev A basic interface for ENS resolvers.
*/
contract Resolver {
function supportsInterface(bytes4 interfaceID) public pure returns (bool);
function addr(bytes32 node) public view returns (address);
function setAddr(bytes32 node, address addr) public;
}
interface Deed {
function setOwner(address payable newOwner) external;
function setRegistrar(address newRegistrar) external;
function setBalance(uint newValue, bool throwOnFailure) external;
function closeDeed(uint refundRatio) external;
function destroyDeed() external;
function owner() external view returns (address);
function previousOwner() external view returns (address);
function value() external view returns (uint);
function creationDate() external view returns (uint);
}
interface Registrar {
enum Mode { Open, Auction, Owned, Forbidden, Reveal, NotYetAvailable }
event AuctionStarted(bytes32 indexed hash, uint registrationDate);
event NewBid(bytes32 indexed hash, address indexed bidder, uint deposit);
event BidRevealed(bytes32 indexed hash, address indexed owner, uint value, uint8 status);
event HashRegistered(bytes32 indexed hash, address indexed owner, uint value, uint registrationDate);
event HashReleased(bytes32 indexed hash, uint value);
event HashInvalidated(bytes32 indexed hash, string indexed name, uint value, uint registrationDate);
function state(bytes32 _hash) external view returns (Mode);
function startAuction(bytes32 _hash) external;
function startAuctions(bytes32[] calldata _hashes) external;
function newBid(bytes32 sealedBid) external payable;
function startAuctionsAndBid(bytes32[] calldata hashes, bytes32 sealedBid) external payable;
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) external;
function cancelBid(address bidder, bytes32 seal) external;
function finalizeAuction(bytes32 _hash) external;
function transfer(bytes32 _hash, address payable newOwner) external;
function releaseDeed(bytes32 _hash) external;
function invalidateName(string calldata unhashedName) external;
function eraseNode(bytes32[] calldata labels) external;
function transferRegistrars(bytes32 _hash) external;
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) external;
function entries(bytes32 _hash) external view returns (Mode, address, uint, uint, uint);
}
contract RegistrarInterface {
event OwnerChanged(bytes32 indexed label, address indexed oldOwner, address indexed newOwner);
event DomainConfigured(bytes32 indexed label);
event DomainUnlisted(bytes32 indexed label);
event NewRegistration(bytes32 indexed label, string subdomain, address indexed owner, address indexed referrer, uint price);
event RentPaid(bytes32 indexed label, string subdomain, uint amount, uint expirationDate);
// InterfaceID of these four methods is 0xc1b15f5a
function query(bytes32 label, string calldata subdomain) external view returns (string memory domain, uint signupFee, uint rent, uint referralFeePPM);
function register(bytes32 label, string calldata subdomain, address owner, address payable referrer, address resolver) external payable;
function rentDue(bytes32 label, string calldata subdomain) external view returns (uint timestamp);
function payRent(bytes32 label, string calldata subdomain) external payable;
}
contract AbstractSubdomainRegistrar is RegistrarInterface {
// namehash('eth')
bytes32 constant public TLD_NODE = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;
bool public stopped = false;
address public registrarOwner;
address public migration;
address public registrar;
ENS public ens;
modifier owner_only(bytes32 label) {
require(owner(label) == msg.sender);
_;
}
modifier not_stopped() {
require(!stopped);
_;
}
modifier registrar_owner_only() {
require(msg.sender == registrarOwner);
_;
}
event DomainTransferred(bytes32 indexed label, string name);
constructor(ENS _ens) public {
ens = _ens;
registrar = ens.owner(TLD_NODE);
registrarOwner = msg.sender;
}
function doRegistration(bytes32 node, bytes32 label, address subdomainOwner, Resolver resolver) internal {
// Get the subdomain so we can configure it
ens.setSubnodeOwner(node, label, address(this));
bytes32 subnode = keccak256(abi.encodePacked(node, label));
// Set the subdomain's resolver
ens.setResolver(subnode, address(resolver));
// Set the address record on the resolver
resolver.setAddr(subnode, subdomainOwner);
// Pass ownership of the new subdomain to the registrant
ens.setOwner(subnode, subdomainOwner);
}
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
return (
(interfaceID == 0x01ffc9a7) // supportsInterface(bytes4)
|| (interfaceID == 0xc1b15f5a) // RegistrarInterface
);
}
function rentDue(bytes32 label, string calldata subdomain) external view returns (uint timestamp) {
return 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
}
/**
* @dev Sets the resolver record for a name in ENS.
* @param name The name to set the resolver for.
* @param resolver The address of the resolver
*/
function setResolver(string memory name, address resolver) public owner_only(keccak256(bytes(name))) {
bytes32 label = keccak256(bytes(name));
bytes32 node = keccak256(abi.encodePacked(TLD_NODE, label));
ens.setResolver(node, resolver);
}
/**
* @dev Configures a domain for sale.
* @param name The name to configure.
* @param price The price in wei to charge for subdomain registrations
* @param referralFeePPM The referral fee to offer, in parts per million
*/
function configureDomain(string memory name, uint price, uint referralFeePPM) public {
configureDomainFor(name, price, referralFeePPM, msg.sender, address(0x0));
}
/**
* @dev Stops the registrar, disabling configuring of new domains.
*/
function stop() public not_stopped registrar_owner_only {
stopped = true;
}
/**
* @dev Sets the address where domains are migrated to.
* @param _migration Address of the new registrar.
*/
function setMigrationAddress(address _migration) public registrar_owner_only {
require(stopped);
migration = _migration;
}
function transferOwnership(address newOwner) public registrar_owner_only {
registrarOwner = newOwner;
}
/**
* @dev Returns information about a subdomain.
* @param label The label hash for the domain.
* @param subdomain The label for the subdomain.
* @return domain The name of the domain, or an empty string if the subdomain
* is unavailable.
* @return price The price to register a subdomain, in wei.
* @return rent The rent to retain a subdomain, in wei per second.
* @return referralFeePPM The referral fee for the dapp, in ppm.
*/
function query(bytes32 label, string calldata subdomain) external view returns (string memory domain, uint price, uint rent, uint referralFeePPM);
function owner(bytes32 label) public view returns (address);
function configureDomainFor(string memory name, uint price, uint referralFeePPM, address payable _owner, address _transfer) public;
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safeTransfer`. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
/**
* @title ERC165
* @author Matt Condon (@shrugs)
* @dev Implements ERC165 using a lookup table.
*/
contract ERC165 is IERC165 {
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself
*/
constructor () internal {
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev internal method for registering an interface
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
_supportedInterfaces[interfaceId] = true;
}
}
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from token ID to owner
mapping (uint256 => address) private _tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => uint256) private _ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
constructor () public {
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
}
/**
* @dev Gets the balance of the specified address
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
return _ownedTokensCount[owner];
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenId uint256 ID of the token to query the owner of
* @return owner address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param to operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible
* Requires the msg sender 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
*/
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* 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
* @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
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* 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
* @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 {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data));
}
/**
* @dev Returns whether the specified token exists
* @param tokenId uint256 ID of the token to query the existence of
* @return whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
address owner = _tokenOwner[tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId));
_tokenOwner[tokenId] = to;
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* Deprecated, use _burn(uint256) instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
require(ownerOf(tokenId) == owner);
_clearApproval(tokenId);
_ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);
_tokenOwner[tokenId] = address(0);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal {
_burn(ownerOf(tokenId), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to transferFrom, this imposes no restrictions on msg.sender.
* @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
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from);
require(to != address(0));
_clearApproval(tokenId);
_ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
_tokenOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
internal returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Private function to clear current approval of a given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(uint256 tokenId) private {
if (_tokenApprovals[tokenId] != address(0)) {
_tokenApprovals[tokenId] = address(0);
}
}
}
contract BaseRegistrarImplementation is BaseRegistrar, ERC721 {
// A map of expiry times
mapping(uint256=>uint) expiries;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(address)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
constructor(ENS _ens, bytes32 _baseNode) public {
ens = _ens;
baseNode = _baseNode;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
/**
* @dev Gets the owner of the specified token ID. Names become unowned
* when their registration expires.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
require(expiries[tokenId] > now);
return super.ownerOf(tokenId);
}
// Authorises a controller, who can register and renew domains.
function addController(address controller) external onlyOwner {
controllers[controller] = true;
emit ControllerAdded(controller);
}
// Revoke controller permission for an address.
function removeController(address controller) external onlyOwner {
controllers[controller] = false;
emit ControllerRemoved(controller);
}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// Returns the expiration timestamp of the specified id.
function nameExpires(uint256 id) external view returns(uint) {
return expiries[id];
}
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool) {
// Not available if it's registered here or in its grace period.
return expiries[id] + GRACE_PERIOD < now;
}
/**
* @dev Register a name.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function register(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, true);
}
/**
* @dev Register a name, without modifying the registry.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, false);
}
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
require(available(id));
require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow
expiries[id] = now + duration;
if(_exists(id)) {
// Name was previously owned, and expired
_burn(id);
}
_mint(owner, id);
if(updateRegistry) {
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
emit NameRegistered(id, owner, now + duration);
return now + duration;
}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external live {
require(_isApprovedOrOwner(msg.sender, id));
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == ERC721_ID ||
interfaceID == RECLAIM_ID;
}
}
/*
Temporary Hash Registrar
========================
This is a simplified version of a hash registrar. It is purporsefully limited:
names cannot be six letters or shorter, new auctions will stop after 4 years.
The plan is to test the basic features and then move to a new contract in at most
2 years, when some sort of renewal mechanism will be enabled.
*/
/**
* @title Deed to hold ether in exchange for ownership of a node
* @dev The deed can be controlled only by the registrar and can only send ether back to the owner.
*/
contract DeedImplementation is Deed {
address payable constant burn = address(0xdead);
address payable private _owner;
address private _previousOwner;
address private _registrar;
uint private _creationDate;
uint private _value;
bool active;
event OwnerChanged(address newOwner);
event DeedClosed();
modifier onlyRegistrar {
require(msg.sender == _registrar);
_;
}
modifier onlyActive {
require(active);
_;
}
constructor(address payable initialOwner) public payable {
_owner = initialOwner;
_registrar = msg.sender;
_creationDate = now;
active = true;
_value = msg.value;
}
function setOwner(address payable newOwner) external onlyRegistrar {
require(newOwner != address(0x0));
_previousOwner = _owner; // This allows contracts to check who sent them the ownership
_owner = newOwner;
emit OwnerChanged(newOwner);
}
function setRegistrar(address newRegistrar) external onlyRegistrar {
_registrar = newRegistrar;
}
function setBalance(uint newValue, bool throwOnFailure) external onlyRegistrar onlyActive {
// Check if it has enough balance to set the value
require(_value >= newValue);
_value = newValue;
// Send the difference to the owner
require(_owner.send(address(this).balance - newValue) || !throwOnFailure);
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
*
* @param refundRatio The amount*1/1000 to refund
*/
function closeDeed(uint refundRatio) external onlyRegistrar onlyActive {
active = false;
require(burn.send(((1000 - refundRatio) * address(this).balance)/1000));
emit DeedClosed();
_destroyDeed();
}
/**
* @dev Close a deed and refund a specified fraction of the bid value
*/
function destroyDeed() external {
_destroyDeed();
}
function owner() external view returns (address) {
return _owner;
}
function previousOwner() external view returns (address) {
return _previousOwner;
}
function value() external view returns (uint) {
return _value;
}
function creationDate() external view returns (uint) {
_creationDate;
}
function _destroyDeed() internal {
require(!active);
// Instead of selfdestruct(owner), invoke owner fallback function to allow
// owner to log an event if desired; but owner should also be aware that
// its fallback function can also be invoked by setBalance
if (_owner.send(address(this).balance)) {
selfdestruct(burn);
}
}
}
/**
* @title Registrar
* @dev The registrar handles the auction process for each subnode of the node it owns.
*/
contract HashRegistrar is Registrar {
ENS public ens;
bytes32 public rootNode;
mapping (bytes32 => Entry) _entries;
mapping (address => mapping (bytes32 => Deed)) public sealedBids;
uint32 constant totalAuctionLength = 5 days;
uint32 constant revealPeriod = 48 hours;
uint32 public constant launchLength = 8 weeks;
uint constant minPrice = 0.01 ether;
uint public registryStarted;
struct Entry {
Deed deed;
uint registrationDate;
uint value;
uint highestBid;
}
modifier inState(bytes32 _hash, Mode _state) {
require(state(_hash) == _state);
_;
}
modifier onlyOwner(bytes32 _hash) {
require(state(_hash) == Mode.Owned && msg.sender == _entries[_hash].deed.owner());
_;
}
modifier registryOpen() {
require(now >= registryStarted && now <= registryStarted + (365 * 4) * 1 days && ens.owner(rootNode) == address(this));
_;
}
/**
* @dev Constructs a new Registrar, with the provided address as the owner of the root node.
*
* @param _ens The address of the ENS
* @param _rootNode The hash of the rootnode.
*/
constructor(ENS _ens, bytes32 _rootNode, uint _startDate) public {
ens = _ens;
rootNode = _rootNode;
registryStarted = _startDate > 0 ? _startDate : now;
}
/**
* @dev Start an auction for an available hash
*
* @param _hash The hash to start an auction on
*/
function startAuction(bytes32 _hash) external {
_startAuction(_hash);
}
/**
* @dev Start multiple auctions for better anonymity
*
* Anyone can start an auction by sending an array of hashes that they want to bid for.
* Arrays are sent so that someone can open up an auction for X dummy hashes when they
* are only really interested in bidding for one. This will increase the cost for an
* attacker to simply bid blindly on all new auctions. Dummy auctions that are
* open but not bid on are closed after a week.
*
* @param _hashes An array of hashes, at least one of which you presumably want to bid on
*/
function startAuctions(bytes32[] calldata _hashes) external {
_startAuctions(_hashes);
}
/**
* @dev Submit a new sealed bid on a desired hash in a blind auction
*
* Bids are sent by sending a message to the main contract with a hash and an amount. The hash
* contains information about the bid, including the bidded hash, the bid amount, and a random
* salt. Bids are not tied to any one auction until they are revealed. The value of the bid
* itself can be masqueraded by sending more than the value of your actual bid. This is
* followed by a 48h reveal period. Bids revealed after this period will be burned and the ether unrecoverable.
* Since this is an auction, it is expected that most public hashes, like known domains and common dictionary
* words, will have multiple bidders pushing the price up.
*
* @param sealedBid A sealedBid, created by the shaBid function
*/
function newBid(bytes32 sealedBid) external payable {
_newBid(sealedBid);
}
/**
* @dev Start a set of auctions and bid on one of them
*
* This method functions identically to calling `startAuctions` followed by `newBid`,
* but all in one transaction.
*
* @param hashes A list of hashes to start auctions on.
* @param sealedBid A sealed bid for one of the auctions.
*/
function startAuctionsAndBid(bytes32[] calldata hashes, bytes32 sealedBid) external payable {
_startAuctions(hashes);
_newBid(sealedBid);
}
/**
* @dev Submit the properties of a bid to reveal them
*
* @param _hash The node in the sealedBid
* @param _value The bid amount in the sealedBid
* @param _salt The sale in the sealedBid
*/
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) external {
bytes32 seal = shaBid(_hash, msg.sender, _value, _salt);
Deed bid = sealedBids[msg.sender][seal];
require(address(bid) != address(0x0));
sealedBids[msg.sender][seal] = Deed(address(0x0));
Entry storage h = _entries[_hash];
uint value = min(_value, bid.value());
bid.setBalance(value, true);
Mode auctionState = state(_hash);
if (auctionState == Mode.Owned) {
// Too late! Bidder loses their bid. Gets 0.5% back.
bid.closeDeed(5);
emit BidRevealed(_hash, msg.sender, value, 1);
} else if (auctionState != Mode.Reveal) {
// Invalid phase
revert();
} else if (value < minPrice || bid.creationDate() > h.registrationDate - revealPeriod) {
// Bid too low or too late, refund 99.5%
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 0);
} else if (value > h.highestBid) {
// New winner
// Cancel the other bid, refund 99.5%
if (address(h.deed) != address(0x0)) {
Deed previousWinner = h.deed;
previousWinner.closeDeed(995);
}
// Set new winner
// Per the rules of a vickery auction, the value becomes the previous highestBid
h.value = h.highestBid; // will be zero if there's only 1 bidder
h.highestBid = value;
h.deed = bid;
emit BidRevealed(_hash, msg.sender, value, 2);
} else if (value > h.value) {
// Not winner, but affects second place
h.value = value;
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 3);
} else {
// Bid doesn't affect auction
bid.closeDeed(995);
emit BidRevealed(_hash, msg.sender, value, 4);
}
}
/**
* @dev Cancel a bid
*
* @param seal The value returned by the shaBid function
*/
function cancelBid(address bidder, bytes32 seal) external {
Deed bid = sealedBids[bidder][seal];
// If a sole bidder does not `unsealBid` in time, they have a few more days
// where they can call `startAuction` (again) and then `unsealBid` during
// the revealPeriod to get back their bid value.
// For simplicity, they should call `startAuction` within
// 9 days (2 weeks - totalAuctionLength), otherwise their bid will be
// cancellable by anyone.
require(address(bid) != address(0x0) && now >= bid.creationDate() + totalAuctionLength + 2 weeks);
// Send the canceller 0.5% of the bid, and burn the rest.
bid.setOwner(msg.sender);
bid.closeDeed(5);
sealedBids[bidder][seal] = Deed(0);
emit BidRevealed(seal, bidder, 0, 5);
}
/**
* @dev Finalize an auction after the registration date has passed
*
* @param _hash The hash of the name the auction is for
*/
function finalizeAuction(bytes32 _hash) external onlyOwner(_hash) {
Entry storage h = _entries[_hash];
// Handles the case when there's only a single bidder (h.value is zero)
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value, true);
trySetSubnodeOwner(_hash, h.deed.owner());
emit HashRegistered(_hash, h.deed.owner(), h.value, h.registrationDate);
}
/**
* @dev The owner of a domain may transfer it to someone else at any time.
*
* @param _hash The node to transfer
* @param newOwner The address to transfer ownership to
*/
function transfer(bytes32 _hash, address payable newOwner) external onlyOwner(_hash) {
require(newOwner != address(0x0));
Entry storage h = _entries[_hash];
h.deed.setOwner(newOwner);
trySetSubnodeOwner(_hash, newOwner);
}
/**
* @dev After some time, or if we're no longer the registrar, the owner can release
* the name and get their ether back.
*
* @param _hash The node to release
*/
function releaseDeed(bytes32 _hash) external onlyOwner(_hash) {
Entry storage h = _entries[_hash];
Deed deedContract = h.deed;
require(now >= h.registrationDate + 365 days || ens.owner(rootNode) != address(this));
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
_tryEraseSingleNode(_hash);
deedContract.closeDeed(1000);
emit HashReleased(_hash, h.value);
}
/**
* @dev Submit a name 6 characters long or less. If it has been registered,
* the submitter will earn 50% of the deed value.
*
* We are purposefully handicapping the simplified registrar as a way
* to force it into being restructured in a few years.
*
* @param unhashedName An invalid name to search for in the registry.
*/
function invalidateName(string calldata unhashedName)
external
inState(keccak256(abi.encode(unhashedName)), Mode.Owned)
{
require(strlen(unhashedName) <= 6);
bytes32 hash = keccak256(abi.encode(unhashedName));
Entry storage h = _entries[hash];
_tryEraseSingleNode(hash);
if (address(h.deed) != address(0x0)) {
// Reward the discoverer with 50% of the deed
// The previous owner gets 50%
h.value = max(h.value, minPrice);
h.deed.setBalance(h.value/2, false);
h.deed.setOwner(msg.sender);
h.deed.closeDeed(1000);
}
emit HashInvalidated(hash, unhashedName, h.value, h.registrationDate);
h.value = 0;
h.highestBid = 0;
h.deed = Deed(0);
}
/**
* @dev Allows anyone to delete the owner and resolver records for a (subdomain of) a
* name that is not currently owned in the registrar. If passing, eg, 'foo.bar.eth',
* the owner and resolver fields on 'foo.bar.eth' and 'bar.eth' will all be cleared.
*
* @param labels A series of label hashes identifying the name to zero out, rooted at the
* registrar's root. Must contain at least one element. For instance, to zero
* 'foo.bar.eth' on a registrar that owns '.eth', pass an array containing
* [keccak256('foo'), keccak256('bar')].
*/
function eraseNode(bytes32[] calldata labels) external {
require(labels.length != 0);
require(state(labels[labels.length - 1]) != Mode.Owned);
_eraseNodeHierarchy(labels.length - 1, labels, rootNode);
}
/**
* @dev Transfers the deed to the current registrar, if different from this one.
*
* Used during the upgrade process to a permanent registrar.
*
* @param _hash The name hash to transfer.
*/
function transferRegistrars(bytes32 _hash) external onlyOwner(_hash) {
address registrar = ens.owner(rootNode);
require(registrar != address(this));
// Migrate the deed
Entry storage h = _entries[_hash];
h.deed.setRegistrar(registrar);
// Call the new registrar to accept the transfer
Registrar(registrar).acceptRegistrarTransfer(_hash, h.deed, h.registrationDate);
// Zero out the Entry
h.deed = Deed(0);
h.registrationDate = 0;
h.value = 0;
h.highestBid = 0;
}
/**
* @dev Accepts a transfer from a previous registrar; stubbed out here since there
* is no previous registrar implementing this interface.
*
* @param hash The sha3 hash of the label to transfer.
* @param deed The Deed object for the name being transferred in.
* @param registrationDate The date at which the name was originally registered.
*/
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) external {
hash; deed; registrationDate; // Don't warn about unused variables
}
function entries(bytes32 _hash) external view returns (Mode, address, uint, uint, uint) {
Entry storage h = _entries[_hash];
return (state(_hash), address(h.deed), h.registrationDate, h.value, h.highestBid);
}
// State transitions for names:
// Open -> Auction (startAuction)
// Auction -> Reveal
// Reveal -> Owned
// Reveal -> Open (if nobody bid)
// Owned -> Open (releaseDeed or invalidateName)
function state(bytes32 _hash) public view returns (Mode) {
Entry storage entry = _entries[_hash];
if (!isAllowed(_hash, now)) {
return Mode.NotYetAvailable;
} else if (now < entry.registrationDate) {
if (now < entry.registrationDate - revealPeriod) {
return Mode.Auction;
} else {
return Mode.Reveal;
}
} else {
if (entry.highestBid == 0) {
return Mode.Open;
} else {
return Mode.Owned;
}
}
}
/**
* @dev Determines if a name is available for registration yet
*
* Each name will be assigned a random date in which its auction
* can be started, from 0 to 8 weeks
*
* @param _hash The hash to start an auction on
* @param _timestamp The timestamp to query about
*/
function isAllowed(bytes32 _hash, uint _timestamp) public view returns (bool allowed) {
return _timestamp > getAllowedTime(_hash);
}
/**
* @dev Returns available date for hash
*
* The available time from the `registryStarted` for a hash is proportional
* to its numeric value.
*
* @param _hash The hash to start an auction on
*/
function getAllowedTime(bytes32 _hash) public view returns (uint) {
return registryStarted + ((launchLength * (uint(_hash) >> 128)) >> 128);
// Right shift operator: a >> b == a / 2**b
}
/**
* @dev Hash the values required for a secret bid
*
* @param hash The node corresponding to the desired namehash
* @param value The bid amount
* @param salt A random value to ensure secrecy of the bid
* @return The hash of the bid values
*/
function shaBid(bytes32 hash, address owner, uint value, bytes32 salt) public pure returns (bytes32) {
return keccak256(abi.encodePacked(hash, owner, value, salt));
}
function _tryEraseSingleNode(bytes32 label) internal {
if (ens.owner(rootNode) == address(this)) {
ens.setSubnodeOwner(rootNode, label, address(this));
bytes32 node = keccak256(abi.encodePacked(rootNode, label));
ens.setResolver(node, address(0x0));
ens.setOwner(node, address(0x0));
}
}
function _startAuction(bytes32 _hash) internal registryOpen() {
Mode mode = state(_hash);
if (mode == Mode.Auction) return;
require(mode == Mode.Open);
Entry storage newAuction = _entries[_hash];
newAuction.registrationDate = now + totalAuctionLength;
newAuction.value = 0;
newAuction.highestBid = 0;
emit AuctionStarted(_hash, newAuction.registrationDate);
}
function _startAuctions(bytes32[] memory _hashes) internal {
for (uint i = 0; i < _hashes.length; i ++) {
_startAuction(_hashes[i]);
}
}
function _newBid(bytes32 sealedBid) internal {
require(address(sealedBids[msg.sender][sealedBid]) == address(0x0));
require(msg.value >= minPrice);
// Creates a new hash contract with the owner
Deed bid = (new DeedImplementation).value(msg.value)(msg.sender);
sealedBids[msg.sender][sealedBid] = bid;
emit NewBid(sealedBid, msg.sender, msg.value);
}
function _eraseNodeHierarchy(uint idx, bytes32[] memory labels, bytes32 node) internal {
// Take ownership of the node
ens.setSubnodeOwner(node, labels[idx], address(this));
node = keccak256(abi.encodePacked(node, labels[idx]));
// Recurse if there are more labels
if (idx > 0) {
_eraseNodeHierarchy(idx - 1, labels, node);
}
// Erase the resolver and owner records
ens.setResolver(node, address(0x0));
ens.setOwner(node, address(0x0));
}
/**
* @dev Assign the owner in ENS, if we're still the registrar
*
* @param _hash hash to change owner
* @param _newOwner new owner to transfer to
*/
function trySetSubnodeOwner(bytes32 _hash, address _newOwner) internal {
if (ens.owner(rootNode) == address(this))
ens.setSubnodeOwner(rootNode, _hash, _newOwner);
}
/**
* @dev Returns the maximum of two unsigned integers
*
* @param a A number to compare
* @param b A number to compare
* @return The maximum of two unsigned integers
*/
function max(uint a, uint b) internal pure returns (uint) {
if (a > b)
return a;
else
return b;
}
/**
* @dev Returns the minimum of two unsigned integers
*
* @param a A number to compare
* @param b A number to compare
* @return The minimum of two unsigned integers
*/
function min(uint a, uint b) internal pure returns (uint) {
if (a < b)
return a;
else
return b;
}
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
s; // Don't warn about unused variables
// Starting here means the LSB will be the byte we care about
uint ptr;
uint end;
assembly {
ptr := add(s, 1)
end := add(mload(s), ptr)
}
uint len = 0;
for (len; ptr < end; len++) {
uint8 b;
assembly { b := and(mload(ptr), 0xFF) }
if (b < 0x80) {
ptr += 1;
} else if (b < 0xE0) {
ptr += 2;
} else if (b < 0xF0) {
ptr += 3;
} else if (b < 0xF8) {
ptr += 4;
} else if (b < 0xFC) {
ptr += 5;
} else {
ptr += 6;
}
}
return len;
}
}
contract OldBaseRegistrarImplementation is BaseRegistrar, ERC721 {
// Expiration timestamp for migrated domains.
uint public transferPeriodEnds;
// The interim registrar
Registrar public previousRegistrar;
// A map of expiry times
mapping(uint256=>uint) expiries;
uint constant public MIGRATION_LOCK_PERIOD = 0;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(uint256)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
constructor(ENS _ens, HashRegistrar _previousRegistrar, bytes32 _baseNode, uint _transferPeriodEnds) public {
// Require that people have time to transfer names over.
require(_transferPeriodEnds > now + 2 * MIGRATION_LOCK_PERIOD);
ens = _ens;
baseNode = _baseNode;
previousRegistrar = _previousRegistrar;
transferPeriodEnds = _transferPeriodEnds;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
/**
* @dev Gets the owner of the specified token ID. Names become unowned
* when their registration expires.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
require(expiries[tokenId] > now);
return super.ownerOf(tokenId);
}
// Authorises a controller, who can register and renew domains.
function addController(address controller) external onlyOwner {
controllers[controller] = true;
emit ControllerAdded(controller);
}
// Revoke controller permission for an address.
function removeController(address controller) external onlyOwner {
controllers[controller] = false;
emit ControllerRemoved(controller);
}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// Returns the expiration timestamp of the specified id.
function nameExpires(uint256 id) external view returns(uint) {
return expiries[id];
}
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool) {
// Not available if it's registered here or in its grace period.
if(expiries[id] + GRACE_PERIOD >= now) {
return false;
}
// Available if we're past the transfer period, or the name isn't
// registered in the legacy registrar.
return now > transferPeriodEnds || previousRegistrar.state(bytes32(id)) == Registrar.Mode.Open;
}
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, true);
}
/**
* @dev Register a name.
*/
function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, false);
}
/**
* @dev Register a name.
*/
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
require(available(id));
require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow
expiries[id] = now + duration;
if(_exists(id)) {
// Name was previously owned, and expired
_burn(id);
}
_mint(owner, id);
if(updateRegistry) {
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
emit NameRegistered(id, owner, now + duration);
return now + duration;
}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external live {
require(_isApprovedOrOwner(msg.sender, id));
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
/**
* @dev Transfers a registration from the initial registrar.
* This function is called by the initial registrar when a user calls `transferRegistrars`.
*/
function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external live {
uint256 id = uint256(label);
require(msg.sender == address(previousRegistrar));
require(expiries[id] == 0);
require(transferPeriodEnds > now);
uint registrationDate;
(,,registrationDate,,) = previousRegistrar.entries(label);
require(registrationDate < now - MIGRATION_LOCK_PERIOD);
address owner = deed.owner();
// Destroy the deed and transfer the funds back to the registrant.
deed.closeDeed(1000);
// Register the name
expiries[id] = transferPeriodEnds;
_mint(owner, id);
ens.setSubnodeOwner(baseNode, label, owner);
emit NameMigrated(id, owner, transferPeriodEnds);
emit NameRegistered(id, owner, transferPeriodEnds);
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == ERC721_ID ||
interfaceID == RECLAIM_ID;
}
}
interface OldENS {
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
contract RegistrarMigration {
using SafeMath for uint;
bytes constant private UNUSED_SUBDOMAIN = hex'ffffffffffffffff';
Registrar public legacyRegistrar;
uint transferPeriodEnds;
OldBaseRegistrarImplementation public oldRegistrar;
BaseRegistrarImplementation public newRegistrar;
OldENS public oldENS;
ENS public newENS;
AbstractSubdomainRegistrar public oldSubdomainRegistrar;
AbstractSubdomainRegistrar public newSubdomainRegistrar;
bytes32 public baseNode;
constructor(OldBaseRegistrarImplementation _old, BaseRegistrarImplementation _new, AbstractSubdomainRegistrar _oldSubdomainRegistrar, AbstractSubdomainRegistrar _newSubdomainRegistrar) public {
oldRegistrar = _old;
oldENS = OldENS(address(_old.ens()));
baseNode = _old.baseNode();
legacyRegistrar = _old.previousRegistrar();
transferPeriodEnds = _old.transferPeriodEnds();
oldSubdomainRegistrar = _oldSubdomainRegistrar;
newRegistrar = _new;
newENS = _new.ens();
require(_new.baseNode() == baseNode);
newSubdomainRegistrar = _newSubdomainRegistrar;
}
function doMigration(uint256 tokenId, address registrant, uint expires) internal {
bytes32 node = keccak256(abi.encodePacked(baseNode, bytes32(tokenId)));
address controller = oldENS.owner(node);
if(address(registrant) != address(oldSubdomainRegistrar) && hasCode(controller)) {
// For names controlled by a contract or not in ENS, only migrate over the registration
newRegistrar.registerOnly(tokenId, registrant, expires.sub(now));
return;
}
// Register the name on the new registry with the same expiry time.
newRegistrar.register(tokenId, address(this), expires.sub(now));
// Copy over resolver, TTL and owner to the new registry.
address resolver = oldENS.resolver(node);
if(resolver != address(0)) {
newENS.setResolver(node, resolver);
}
uint64 ttl = oldENS.ttl(node);
if(ttl != 0) {
newENS.setTTL(node, ttl);
}
if(address(registrant) == address(oldSubdomainRegistrar) && address(registrant) != address(0)) {
// Handle subdomain registrar domains
// Fetch data from the old subdomain registrar
(string memory label, uint price,, uint referralFeePPM) = oldSubdomainRegistrar.query(bytes32(tokenId), string(UNUSED_SUBDOMAIN));
address owner = oldSubdomainRegistrar.owner(bytes32(tokenId));
if(bytes(label).length == 0) {
revert("Unable to migrate domain on subdomain registrar");
}
// Transfer to the new subdomain registrar
newRegistrar.approve(address(newSubdomainRegistrar), tokenId);
newSubdomainRegistrar.configureDomainFor(label, price, referralFeePPM, address(uint160(owner)), address(0));
} else {
newENS.setOwner(node, controller);
// Transfer the registration to the registrant.
newRegistrar.transferFrom(address(this), registrant, tokenId);
}
// Replace ownership on the old registry so it can't be updated any further.
oldENS.setSubnodeOwner(baseNode, bytes32(tokenId), address(this));
}
/**
* @dev Migrate a name from the previous version of the BaseRegistrar
*/
function migrate(uint256 tokenId) public {
address registrant = oldRegistrar.ownerOf(tokenId);
doMigration(tokenId, registrant, oldRegistrar.nameExpires(tokenId));
}
/**
* @dev Migrate a list of names from the previous version of the BaseRegistrar.
*/
function migrateAll(uint256[] calldata tokenIds) external {
for(uint i = 0; i < tokenIds.length; i++) {
migrate(tokenIds[i]);
}
}
/**
* @dev Migrate a name from the legacy (auction-based) registrar.
*/
function migrateLegacy(bytes32 label) public {
(Registrar.Mode mode, address deed, , ,) = legacyRegistrar.entries(label);
require(mode == Registrar.Mode.Owned);
address owner = Deed(deed).owner();
doMigration(uint256(label), owner, transferPeriodEnds);
}
/**
* @dev Migrate a list of names from the legacy (auction-based) registrar.
*/
function migrateAllLegacy(bytes32[] calldata labels) external {
for(uint i = 0; i < labels.length; i++) {
migrateLegacy(labels[i]);
}
}
function hasCode(address addr) private view returns(bool ret) {
assembly {
ret := not(not(extcodesize(addr)))
}
}
}
// We need this to persuade Truffle to compile all the code we want to use.
// We need this *third* file because Solidity treats imports lexically, and
// without it we have conflicts between identically named imports.
/**
* @dev Implements an ENS registrar that sells subdomains on behalf of their owners.
*
* Users may register a subdomain by calling `register` with the name of the domain
* they wish to register under, and the label hash of the subdomain they want to
* register. They must also specify the new owner of the domain, and the referrer,
* who is paid an optional finder's fee. The registrar then configures a simple
* default resolver, which resolves `addr` lookups to the new owner, and sets
* the `owner` account as the owner of the subdomain in ENS.
*
* New domains may be added by calling `configureDomain`, then transferring
* ownership in the ENS registry to this contract. Ownership in the contract
* may be transferred using `transfer`, and a domain may be unlisted for sale
* using `unlistDomain`. There is (deliberately) no way to recover ownership
* in ENS once the name is transferred to this registrar.
*
* Critically, this contract does not check one key property of a listed domain:
*
* - Is the name UTS46 normalised?
*
* User applications MUST check these two elements for each domain before
* offering them to users for registration.
*
* Applications should additionally check that the domains they are offering to
* register are controlled by this registrar, since calls to `register` will
* fail if this is not the case.
*/
contract EthRegistrarSubdomainRegistrar is AbstractSubdomainRegistrar {
struct Domain {
string name;
address payable owner;
uint price;
uint referralFeePPM;
}
mapping (bytes32 => Domain) domains;
constructor(ENS ens) AbstractSubdomainRegistrar(ens) public { }
/**
* @dev owner returns the address of the account that controls a domain.
* Initially this is a null address. If the name has been
* transferred to this contract, then the internal mapping is consulted
* to determine who controls it. If the owner is not set,
* the owner of the domain in the Registrar is returned.
* @param label The label hash of the deed to check.
* @return The address owning the deed.
*/
function owner(bytes32 label) public view returns (address) {
if (domains[label].owner != address(0x0)) {
return domains[label].owner;
}
return BaseRegistrar(registrar).ownerOf(uint256(label));
}
/**
* @dev Transfers internal control of a name to a new account. Does not update
* ENS.
* @param name The name to transfer.
* @param newOwner The address of the new owner.
*/
function transfer(string memory name, address payable newOwner) public owner_only(keccak256(bytes(name))) {
bytes32 label = keccak256(bytes(name));
emit OwnerChanged(label, domains[label].owner, newOwner);
domains[label].owner = newOwner;
}
/**
* @dev Configures a domain, optionally transferring it to a new owner.
* @param name The name to configure.
* @param price The price in wei to charge for subdomain registrations.
* @param referralFeePPM The referral fee to offer, in parts per million.
* @param _owner The address to assign ownership of this domain to.
* @param _transfer The address to set as the transfer address for the name
* when the permanent registrar is replaced. Can only be set to a non-zero
* value once.
*/
function configureDomainFor(string memory name, uint price, uint referralFeePPM, address payable _owner, address _transfer) public owner_only(keccak256(bytes(name))) {
bytes32 label = keccak256(bytes(name));
Domain storage domain = domains[label];
if (BaseRegistrar(registrar).ownerOf(uint256(label)) != address(this)) {
BaseRegistrar(registrar).transferFrom(msg.sender, address(this), uint256(label));
BaseRegistrar(registrar).reclaim(uint256(label), address(this));
}
if (domain.owner != _owner) {
domain.owner = _owner;
}
if (keccak256(bytes(domain.name)) != label) {
// New listing
domain.name = name;
}
domain.price = price;
domain.referralFeePPM = referralFeePPM;
emit DomainConfigured(label);
}
/**
* @dev Unlists a domain
* May only be called by the owner.
* @param name The name of the domain to unlist.
*/
function unlistDomain(string memory name) public owner_only(keccak256(bytes(name))) {
bytes32 label = keccak256(bytes(name));
Domain storage domain = domains[label];
emit DomainUnlisted(label);
domain.name = '';
domain.price = 0;
domain.referralFeePPM = 0;
}
/**
* @dev Returns information about a subdomain.
* @param label The label hash for the domain.
* @param subdomain The label for the subdomain.
* @return domain The name of the domain, or an empty string if the subdomain
* is unavailable.
* @return price The price to register a subdomain, in wei.
* @return rent The rent to retain a subdomain, in wei per second.
* @return referralFeePPM The referral fee for the dapp, in ppm.
*/
function query(bytes32 label, string calldata subdomain) external view returns (string memory domain, uint price, uint rent, uint referralFeePPM) {
bytes32 node = keccak256(abi.encodePacked(TLD_NODE, label));
bytes32 subnode = keccak256(abi.encodePacked(node, keccak256(bytes(subdomain))));
if (ens.owner(subnode) != address(0x0)) {
return ('', 0, 0, 0);
}
Domain storage data = domains[label];
return (data.name, data.price, 0, data.referralFeePPM);
}
/**
* @dev Registers a subdomain.
* @param label The label hash of the domain to register a subdomain of.
* @param subdomain The desired subdomain label.
* @param _subdomainOwner The account that should own the newly configured subdomain.
* @param referrer The address of the account to receive the referral fee.
*/
function register(bytes32 label, string calldata subdomain, address _subdomainOwner, address payable referrer, address resolver) external not_stopped payable {
address subdomainOwner = _subdomainOwner;
bytes32 domainNode = keccak256(abi.encodePacked(TLD_NODE, label));
bytes32 subdomainLabel = keccak256(bytes(subdomain));
// Subdomain must not be registered already.
require(ens.owner(keccak256(abi.encodePacked(domainNode, subdomainLabel))) == address(0));
Domain storage domain = domains[label];
// Domain must be available for registration
require(keccak256(bytes(domain.name)) == label);
// User must have paid enough
require(msg.value >= domain.price);
// Send any extra back
if (msg.value > domain.price) {
msg.sender.transfer(msg.value - domain.price);
}
// Send any referral fee
uint256 total = domain.price;
if (domain.referralFeePPM > 0 && referrer != address(0x0) && referrer != domain.owner) {
uint256 referralFee = (domain.price * domain.referralFeePPM) / 1000000;
referrer.transfer(referralFee);
total -= referralFee;
}
// Send the registration fee
if (total > 0) {
domain.owner.transfer(total);
}
// Register the domain
if (subdomainOwner == address(0x0)) {
subdomainOwner = msg.sender;
}
doRegistration(domainNode, subdomainLabel, subdomainOwner, Resolver(resolver));
emit NewRegistration(label, subdomain, subdomainOwner, referrer, domain.price);
}
function rentDue(bytes32 label, string calldata subdomain) external view returns (uint timestamp) {
return 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
}
/**
* @dev Migrates the domain to a new registrar.
* @param name The name of the domain to migrate.
*/
function migrate(string memory name) public owner_only(keccak256(bytes(name))) {
require(stopped);
require(migration != address(0x0));
bytes32 label = keccak256(bytes(name));
Domain storage domain = domains[label];
BaseRegistrar(registrar).approve(migration, uint256(label));
EthRegistrarSubdomainRegistrar(migration).configureDomainFor(
domain.name,
domain.price,
domain.referralFeePPM,
domain.owner,
address(0x0)
);
delete domains[label];
emit DomainTransferred(label, name);
}
function payRent(bytes32 label, string calldata subdomain) external payable {
revert();
}
}
/**
* @dev Implements an ENS registrar that sells subdomains on behalf of their owners.
*
* Users may register a subdomain by calling `register` with the name of the domain
* they wish to register under, and the label hash of the subdomain they want to
* register. They must also specify the new owner of the domain, and the referrer,
* who is paid an optional finder's fee. The registrar then configures a simple
* default resolver, which resolves `addr` lookups to the new owner, and sets
* the `owner` account as the owner of the subdomain in ENS.
*
* New domains may be added by calling `configureDomain`, then transferring
* ownership in the ENS registry to this contract. Ownership in the contract
* may be transferred using `transfer`, and a domain may be unlisted for sale
* using `unlistDomain`. There is (deliberately) no way to recover ownership
* in ENS once the name is transferred to this registrar.
*
* Critically, this contract does not check one key property of a listed domain:
*
* - Is the name UTS46 normalised?
*
* User applications MUST check these two elements for each domain before
* offering them to users for registration.
*
* Applications should additionally check that the domains they are offering to
* register are controlled by this registrar, since calls to `register` will
* fail if this is not the case.
*/
contract ENSMigrationSubdomainRegistrar is EthRegistrarSubdomainRegistrar {
constructor(ENS ens) EthRegistrarSubdomainRegistrar(ens) public { }
function migrateSubdomain(bytes32 node, bytes32 label) external {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
address previous = ens.owner(subnode);
// only allow a contract to run their own migration
require(!isContract(previous) || msg.sender == previous);
ens.setSubnodeRecord(node, label, previous, ens.resolver(subnode), ens.ttl(subnode));
}
function isContract(address addr) private returns (bool) {
uint size;
assembly { size := extcodesize(addr) }
return size > 0;
}
}
pragma solidity >= 0.4.0;
contract Ownable {
address public owner;
modifier onlyOwner {
require(isOwner(msg.sender));
_;
}
constructor() public {
owner = msg.sender;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
function isOwner(address addr) public view returns (bool) {
return owner == addr;
}
}
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
// We need this to persuade Truffle to compile all the code we want to use.
// We need this *fourth* file because Solidity treats imports lexically, and
// without it we have conflicts between identically named imports.
contract Controllable is Ownable {
mapping(address=>bool) public controllers;
modifier onlyController {
require(controllers[msg.sender]);
_;
}
function setController(address controller, bool enabled) public onlyOwner {
controllers[controller] = enabled;
}
}
contract Root is Ownable, Controllable {
bytes32 constant private ROOT_NODE = bytes32(0);
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
event TLDLocked(bytes32 indexed label);
ENS public ens;
mapping(bytes32=>bool) public locked;
constructor(ENS _ens) public {
ens = _ens;
}
function setSubnodeOwner(bytes32 label, address owner) external onlyController {
require(!locked[label]);
ens.setSubnodeOwner(ROOT_NODE, label, owner);
}
function setResolver(address resolver) external onlyOwner {
ens.setResolver(ROOT_NODE, resolver);
}
function lock(bytes32 label) external onlyOwner {
emit TLDLocked(label);
locked[label] = true;
}
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == INTERFACE_META_ID;
}
}
pragma solidity >=0.4.0;
/**
* @dev A library for working with mutable byte buffers in Solidity.
*
* Byte buffers are mutable and expandable, and provide a variety of primitives
* for writing to them. At any time you can fetch a bytes object containing the
* current contents of the buffer. The bytes object should not be stored between
* operations, as it may change due to resizing of the buffer.
*/
library Buffer {
/**
* @dev Represents a mutable buffer. Buffers have a current value (buf) and
* a capacity. The capacity may be longer than the current value, in
* which case it can be extended without the need to allocate more memory.
*/
struct buffer {
bytes buf;
uint capacity;
}
/**
* @dev Initializes a buffer with an initial capacity.
* @param buf The buffer to initialize.
* @param capacity The number of bytes of space to allocate the buffer.
* @return The buffer, for chaining.
*/
function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
if (capacity % 32 != 0) {
capacity += 32 - (capacity % 32);
}
// Allocate space for the buffer data
buf.capacity = capacity;
assembly {
let ptr := mload(0x40)
mstore(buf, ptr)
mstore(ptr, 0)
mstore(0x40, add(ptr, capacity))
}
return buf;
}
/**
* @dev Initializes a new buffer from an existing bytes object.
* Changes to the buffer may mutate the original value.
* @param b The bytes object to initialize the buffer with.
* @return A new buffer.
*/
function fromBytes(bytes memory b) internal pure returns(buffer memory) {
buffer memory buf;
buf.buf = b;
buf.capacity = b.length;
return buf;
}
function resize(buffer memory buf, uint capacity) private pure {
bytes memory oldbuf = buf.buf;
init(buf, capacity);
append(buf, oldbuf);
}
function max(uint a, uint b) private pure returns(uint) {
if (a > b) {
return a;
}
return b;
}
/**
* @dev Sets buffer length to 0.
* @param buf The buffer to truncate.
* @return The original buffer, for chaining..
*/
function truncate(buffer memory buf) internal pure returns (buffer memory) {
assembly {
let bufptr := mload(buf)
mstore(bufptr, 0)
}
return buf;
}
/**
* @dev Writes a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The start offset to write to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function write(buffer memory buf, uint off, bytes memory data, uint len) internal pure returns(buffer memory) {
require(len <= data.length);
if (off + len + buf.buf.length > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint dest;
uint src;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Start address = buffer address + offset + sizeof(buffer length)
dest := add(add(bufptr, 32), off)
// Update buffer length if we're extending it
if gt(add(len, off), buflen) {
mstore(bufptr, add(len, off))
}
src := add(data, 32)
}
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
return buf;
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @param len The number of bytes to copy.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data, uint len) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, len);
}
/**
* @dev Appends a byte string to a buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, data.length);
}
/**
* @dev Writes a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write the byte at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
if (off > buf.capacity) {
resize(buf, buf.capacity * 2);
}
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Length of existing buffer data
let buflen := mload(bufptr)
// Address = buffer address + sizeof(buffer length) + off
let dest := add(add(bufptr, off), 32)
mstore8(dest, data)
// Update buffer length if we extended it
if eq(off, buflen) {
mstore(bufptr, add(buflen, 1))
}
}
return buf;
}
/**
* @dev Appends a byte to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) {
return writeUint8(buf, buf.buf.length, data);
}
/**
* @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
* exceed the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (left-aligned).
* @return The original buffer, for chaining.
*/
function write(buffer memory buf, uint off, bytes32 data, uint len) private pure returns(buffer memory) {
if (len + off > buf.capacity) {
resize(buf, max(buf.capacity, len) * 2);
}
uint mask = 256 ** len - 1;
// Right-align data
data = data >> (8 * (32 - len));
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + sizeof(buffer length) + off + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
/**
* @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
* capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function writeBytes20(buffer memory buf, uint off, bytes20 data) internal pure returns (buffer memory) {
return write(buf, off, bytes32(data), 20);
}
/**
* @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chhaining.
*/
function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, bytes32(data), 20);
}
/**
* @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param data The data to append.
* @return The original buffer, for chaining.
*/
function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
return write(buf, buf.buf.length, data, 32);
}
/**
* @dev Writes an integer to the buffer. Resizes if doing so would exceed
* the capacity of the buffer.
* @param buf The buffer to append to.
* @param off The offset to write at.
* @param data The data to append.
* @param len The number of bytes to write (right-aligned).
* @return The original buffer, for chaining.
*/
function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
if (len + off > buf.capacity) {
resize(buf, max(buf.capacity, len + off) * 2);
}
uint mask = 256 ** len - 1;
assembly {
// Memory address of the buffer data
let bufptr := mload(buf)
// Address = buffer address + off + sizeof(buffer length) + len
let dest := add(add(bufptr, off), len)
mstore(dest, or(and(mload(dest), not(mask)), data))
// Update buffer length if we extended it
if gt(add(off, len), mload(bufptr)) {
mstore(bufptr, add(off, len))
}
}
return buf;
}
}
contract ResolverBase {
bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == INTERFACE_META_ID;
}
function isAuthorised(bytes32 node) internal view returns(bool);
modifier authorised(bytes32 node) {
require(isAuthorised(node));
_;
}
function bytesToAddress(bytes memory b) internal pure returns(address payable a) {
require(b.length == 20);
assembly {
a := div(mload(add(b, 32)), exp(256, 12))
}
}
function addressToBytes(address a) internal pure returns(bytes memory b) {
b = new bytes(20);
assembly {
mstore(add(b, 32), mul(a, exp(256, 12)))
}
}
}
contract TextResolver is ResolverBase {
bytes4 constant private TEXT_INTERFACE_ID = 0x59d1d43c;
event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
mapping(bytes32=>mapping(string=>string)) texts;
/**
* Sets the text data associated with an ENS node and key.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param key The key to set.
* @param value The text data value to set.
*/
function setText(bytes32 node, string calldata key, string calldata value) external authorised(node) {
texts[node][key] = value;
emit TextChanged(node, key, key);
}
/**
* Returns the text data associated with an ENS node and key.
* @param node The ENS node to query.
* @param key The text data key to query.
* @return The associated text data.
*/
function text(bytes32 node, string calldata key) external view returns (string memory) {
return texts[node][key];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == TEXT_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
contract PubkeyResolver is ResolverBase {
bytes4 constant private PUBKEY_INTERFACE_ID = 0xc8690233;
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
struct PublicKey {
bytes32 x;
bytes32 y;
}
mapping(bytes32=>PublicKey) pubkeys;
/**
* Sets the SECP256k1 public key associated with an ENS node.
* @param node The ENS node to query
* @param x the X coordinate of the curve point for the public key.
* @param y the Y coordinate of the curve point for the public key.
*/
function setPubkey(bytes32 node, bytes32 x, bytes32 y) external authorised(node) {
pubkeys[node] = PublicKey(x, y);
emit PubkeyChanged(node, x, y);
}
/**
* Returns the SECP256k1 public key associated with an ENS node.
* Defined in EIP 619.
* @param node The ENS node to query
* @return x, y the X and Y coordinates of the curve point for the public key.
*/
function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y) {
return (pubkeys[node].x, pubkeys[node].y);
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == PUBKEY_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
contract NameResolver is ResolverBase {
bytes4 constant private NAME_INTERFACE_ID = 0x691f3431;
event NameChanged(bytes32 indexed node, string name);
mapping(bytes32=>string) names;
/**
* Sets the name associated with an ENS node, for reverse records.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param name The name to set.
*/
function setName(bytes32 node, string calldata name) external authorised(node) {
names[node] = name;
emit NameChanged(node, name);
}
/**
* Returns the name associated with an ENS node, for reverse records.
* Defined in EIP181.
* @param node The ENS node to query.
* @return The associated name.
*/
function name(bytes32 node) external view returns (string memory) {
return names[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
contract ContentHashResolver is ResolverBase {
bytes4 constant private CONTENT_HASH_INTERFACE_ID = 0xbc1c58d1;
event ContenthashChanged(bytes32 indexed node, bytes hash);
mapping(bytes32=>bytes) hashes;
/**
* Sets the contenthash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param hash The contenthash to set
*/
function setContenthash(bytes32 node, bytes calldata hash) external authorised(node) {
hashes[node] = hash;
emit ContenthashChanged(node, hash);
}
/**
* Returns the contenthash associated with an ENS node.
* @param node The ENS node to query.
* @return The associated contenthash.
*/
function contenthash(bytes32 node) external view returns (bytes memory) {
return hashes[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == CONTENT_HASH_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
contract AddrResolver is ResolverBase {
bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;
bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06;
uint constant private COIN_TYPE_ETH = 60;
event AddrChanged(bytes32 indexed node, address a);
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
mapping(bytes32=>mapping(uint=>bytes)) _addresses;
/**
* Sets the address associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param a The address to set.
*/
function setAddr(bytes32 node, address a) external authorised(node) {
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
}
/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address payable) {
bytes memory a = addr(node, COIN_TYPE_ETH);
if(a.length == 0) {
return address(0);
}
return bytesToAddress(a);
}
function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) {
emit AddressChanged(node, coinType, a);
if(coinType == COIN_TYPE_ETH) {
emit AddrChanged(node, bytesToAddress(a));
}
_addresses[node][coinType] = a;
}
function addr(bytes32 node, uint coinType) public view returns(bytes memory) {
return _addresses[node][coinType];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
contract ABIResolver is ResolverBase {
bytes4 constant private ABI_INTERFACE_ID = 0x2203ab56;
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
mapping(bytes32=>mapping(uint256=>bytes)) abis;
/**
* Sets the ABI associated with an ENS node.
* Nodes may have one ABI of each content type. To remove an ABI, set it to
* the empty string.
* @param node The node to update.
* @param contentType The content type of the ABI
* @param data The ABI data.
*/
function setABI(bytes32 node, uint256 contentType, bytes calldata data) external authorised(node) {
// Content types must be powers of 2
require(((contentType - 1) & contentType) == 0);
abis[node][contentType] = data;
emit ABIChanged(node, contentType);
}
/**
* Returns the ABI associated with an ENS node.
* Defined in EIP205.
* @param node The ENS node to query
* @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
* @return contentType The content type of the return value
* @return data The ABI data
*/
function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory) {
mapping(uint256=>bytes) storage abiset = abis[node];
for (uint256 contentType = 1; contentType <= contentTypes; contentType <<= 1) {
if ((contentType & contentTypes) != 0 && abiset[contentType].length > 0) {
return (contentType, abiset[contentType]);
}
}
return (0, bytes(""));
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == ABI_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
// Logged when an operator is added or removed.
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external;
function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external;
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external returns(bytes32);
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function setApprovalForAll(address operator, bool approved) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
function recordExists(bytes32 node) external view returns (bool);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
/**
* A registrar that allocates subdomains to the first person to claim them, but
* expires registrations a fixed period after they're initially claimed.
*/
contract TestRegistrar {
uint constant registrationPeriod = 4 weeks;
ENS public ens;
bytes32 public rootNode;
mapping (bytes32 => uint) public expiryTimes;
/**
* Constructor.
* @param ensAddr The address of the ENS registry.
* @param node The node that this registrar administers.
*/
constructor(ENS ensAddr, bytes32 node) public {
ens = ensAddr;
rootNode = node;
}
/**
* Register a name that's not currently registered
* @param label The hash of the label to register.
* @param owner The address of the new owner.
*/
function register(bytes32 label, address owner) public {
require(expiryTimes[label] < now);
expiryTimes[label] = now + registrationPeriod;
ens.setSubnodeOwner(rootNode, label, owner);
}
}
contract Resolver {
function setName(bytes32 node, string memory name) public;
}
contract ReverseRegistrar {
// namehash('addr.reverse')
bytes32 public constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
ENS public ens;
Resolver public defaultResolver;
/**
* @dev Constructor
* @param ensAddr The address of the ENS registry.
* @param resolverAddr The address of the default reverse resolver.
*/
constructor(ENS ensAddr, Resolver resolverAddr) public {
ens = ensAddr;
defaultResolver = resolverAddr;
// Assign ownership of the reverse record to our deployer
ReverseRegistrar oldRegistrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE));
if (address(oldRegistrar) != address(0x0)) {
oldRegistrar.claim(msg.sender);
}
}
/**
* @dev Transfers ownership of the reverse ENS record associated with the
* calling account.
* @param owner The address to set as the owner of the reverse record in ENS.
* @return The ENS node hash of the reverse record.
*/
function claim(address owner) public returns (bytes32) {
return claimWithResolver(owner, address(0x0));
}
/**
* @dev Transfers ownership of the reverse ENS record associated with the
* calling account.
* @param owner The address to set as the owner of the reverse record in ENS.
* @param resolver The address of the resolver to set; 0 to leave unchanged.
* @return The ENS node hash of the reverse record.
*/
function claimWithResolver(address owner, address resolver) public returns (bytes32) {
bytes32 label = sha3HexAddress(msg.sender);
bytes32 node = keccak256(abi.encodePacked(ADDR_REVERSE_NODE, label));
address currentOwner = ens.owner(node);
// Update the resolver if required
if (resolver != address(0x0) && resolver != ens.resolver(node)) {
// Transfer the name to us first if it's not already
if (currentOwner != address(this)) {
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, address(this));
currentOwner = address(this);
}
ens.setResolver(node, resolver);
}
// Update the owner if required
if (currentOwner != owner) {
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner);
}
return node;
}
/**
* @dev Sets the `name()` record for the reverse ENS record associated with
* the calling account. First updates the resolver to the default reverse
* resolver if necessary.
* @param name The name to set for this address.
* @return The ENS node hash of the reverse record.
*/
function setName(string memory name) public returns (bytes32) {
bytes32 node = claimWithResolver(address(this), address(defaultResolver));
defaultResolver.setName(node, name);
return node;
}
/**
* @dev Returns the node hash for a given account's reverse records.
* @param addr The address to hash
* @return The ENS node hash.
*/
function node(address addr) public pure returns (bytes32) {
return keccak256(abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr)));
}
/**
* @dev An optimised function to compute the sha3 of the lower-case
* hexadecimal representation of an Ethereum address.
* @param addr The address to hash
* @return The SHA3 hash of the lower-case hexadecimal encoding of the
* input address.
*/
function sha3HexAddress(address addr) private pure returns (bytes32 ret) {
addr;
ret; // Stop warning us about unused variables
assembly {
let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000
for { let i := 40 } gt(i, 0) { } {
i := sub(i, 1)
mstore8(i, byte(and(addr, 0xf), lookup))
addr := div(addr, 0x10)
i := sub(i, 1)
mstore8(i, byte(and(addr, 0xf), lookup))
addr := div(addr, 0x10)
}
ret := keccak256(0, 40)
}
}
}
// We need this to persuade Truffle to compile all the code we want to use.
/**
* The ENS registry contract.
*/
contract ENSRegistry is ENS {
struct Record {
address owner;
address resolver;
uint64 ttl;
}
mapping (bytes32 => Record) records;
mapping (address => mapping(address => bool)) operators;
// Permits modifications only by the owner of the specified node.
modifier authorised(bytes32 node) {
address owner = records[node].owner;
require(owner == msg.sender || operators[owner][msg.sender]);
_;
}
/**
* @dev Constructs a new ENS registrar.
*/
constructor() public {
records[0x0].owner = msg.sender;
}
/**
* @dev Sets the record for a node.
* @param node The node to update.
* @param owner The address of the new owner.
* @param resolver The address of the resolver.
* @param ttl The TTL in seconds.
*/
function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external {
setOwner(node, owner);
_setResolverAndTTL(node, resolver, ttl);
}
/**
* @dev Sets the record for a subnode.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
* @param resolver The address of the resolver.
* @param ttl The TTL in seconds.
*/
function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external {
bytes32 subnode = setSubnodeOwner(node, label, owner);
_setResolverAndTTL(subnode, resolver, ttl);
}
/**
* @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
* @param node The node to transfer ownership of.
* @param owner The address of the new owner.
*/
function setOwner(bytes32 node, address owner) public authorised(node) {
_setOwner(node, owner);
emit Transfer(node, owner);
}
/**
* @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
*/
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public authorised(node) returns(bytes32) {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
_setOwner(subnode, owner);
emit NewOwner(node, label, owner);
return subnode;
}
/**
* @dev Sets the resolver address for the specified node.
* @param node The node to update.
* @param resolver The address of the resolver.
*/
function setResolver(bytes32 node, address resolver) public authorised(node) {
emit NewResolver(node, resolver);
records[node].resolver = resolver;
}
/**
* @dev Sets the TTL for the specified node.
* @param node The node to update.
* @param ttl The TTL in seconds.
*/
function setTTL(bytes32 node, uint64 ttl) public authorised(node) {
emit NewTTL(node, ttl);
records[node].ttl = ttl;
}
/**
* @dev Enable or disable approval for a third party ("operator") to manage
* all of `msg.sender`'s ENS records. Emits the ApprovalForAll event.
* @param operator Address to add to the set of authorized operators.
* @param approved True if the operator is approved, false to revoke approval.
*/
function setApprovalForAll(address operator, bool approved) external {
operators[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
/**
* @dev Returns the address that owns the specified node.
* @param node The specified node.
* @return address of the owner.
*/
function owner(bytes32 node) public view returns (address) {
address addr = records[node].owner;
if (addr == address(this)) {
return address(0x0);
}
return addr;
}
/**
* @dev Returns the address of the resolver for the specified node.
* @param node The specified node.
* @return address of the resolver.
*/
function resolver(bytes32 node) public view returns (address) {
return records[node].resolver;
}
/**
* @dev Returns the TTL of a node, and any records associated with it.
* @param node The specified node.
* @return ttl of the node.
*/
function ttl(bytes32 node) public view returns (uint64) {
return records[node].ttl;
}
/**
* @dev Returns whether a record has been imported to the registry.
* @param node The specified node.
* @return Bool if record exists
*/
function recordExists(bytes32 node) public view returns (bool) {
return records[node].owner != address(0x0);
}
/**
* @dev Query if an address is an authorized operator for another address.
* @param owner The address that owns the records.
* @param operator The address that acts on behalf of the owner.
* @return True if `operator` is an approved operator for `owner`, false otherwise.
*/
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return operators[owner][operator];
}
function _setOwner(bytes32 node, address owner) internal {
records[node].owner = owner;
}
function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
if(resolver != records[node].resolver) {
records[node].resolver = resolver;
emit NewResolver(node, resolver);
}
if(ttl != records[node].ttl) {
records[node].ttl = ttl;
emit NewTTL(node, ttl);
}
}
}
/**
* The ENS registry contract.
*/
contract ENSRegistryWithFallback is ENSRegistry {
ENS public old;
/**
* @dev Constructs a new ENS registrar.
*/
constructor(ENS _old) public ENSRegistry() {
old = _old;
}
/**
* @dev Returns the address of the resolver for the specified node.
* @param node The specified node.
* @return address of the resolver.
*/
function resolver(bytes32 node) public view returns (address) {
if (!recordExists(node)) {
return old.resolver(node);
}
return super.resolver(node);
}
/**
* @dev Returns the address that owns the specified node.
* @param node The specified node.
* @return address of the owner.
*/
function owner(bytes32 node) public view returns (address) {
if (!recordExists(node)) {
return old.owner(node);
}
return super.owner(node);
}
/**
* @dev Returns the TTL of a node, and any records associated with it.
* @param node The specified node.
* @return ttl of the node.
*/
function ttl(bytes32 node) public view returns (uint64) {
if (!recordExists(node)) {
return old.ttl(node);
}
return super.ttl(node);
}
function _setOwner(bytes32 node, address owner) internal {
address addr = owner;
if (addr == address(0x0)) {
addr = address(this);
}
super._setOwner(node, addr);
}
}
library BytesUtils {
/*
* @dev Returns the keccak-256 hash of a byte range.
* @param self The byte string to hash.
* @param offset The position to start hashing at.
* @param len The number of bytes to hash.
* @return The hash of the byte range.
*/
function keccak(bytes memory self, uint offset, uint len) internal pure returns (bytes32 ret) {
require(offset + len <= self.length);
assembly {
ret := keccak256(add(add(self, 32), offset), len)
}
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal.
* @param self The first bytes to compare.
* @param other The second bytes to compare.
* @return The result of the comparison.
*/
function compare(bytes memory self, bytes memory other) internal pure returns (int) {
return compare(self, 0, self.length, other, 0, other.length);
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two bytes are equal. Comparison is done per-rune,
* on unicode codepoints.
* @param self The first bytes to compare.
* @param offset The offset of self.
* @param len The length of self.
* @param other The second bytes to compare.
* @param otheroffset The offset of the other string.
* @param otherlen The length of the other string.
* @return The result of the comparison.
*/
function compare(bytes memory self, uint offset, uint len, bytes memory other, uint otheroffset, uint otherlen) internal pure returns (int) {
uint shortest = len;
if (otherlen < len)
shortest = otherlen;
uint selfptr;
uint otherptr;
assembly {
selfptr := add(self, add(offset, 32))
otherptr := add(other, add(otheroffset, 32))
}
for (uint idx = 0; idx < shortest; idx += 32) {
uint a;
uint b;
assembly {
a := mload(selfptr)
b := mload(otherptr)
}
if (a != b) {
// Mask out irrelevant bytes and check again
uint mask;
if (shortest > 32) {
mask = uint256(- 1); // aka 0xffffff....
} else {
mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
}
uint diff = (a & mask) - (b & mask);
if (diff != 0)
return int(diff);
}
selfptr += 32;
otherptr += 32;
}
return int(len) - int(otherlen);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @param len The number of bytes to compare
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset, uint len) internal pure returns (bool) {
return keccak(self, offset, len) == keccak(other, otherOffset, len);
}
/*
* @dev Returns true if the two byte ranges are equal with offsets.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @param otherOffset The offset into the second byte range.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other, uint otherOffset) internal pure returns (bool) {
return keccak(self, offset, self.length - offset) == keccak(other, otherOffset, other.length - otherOffset);
}
/*
* @dev Compares a range of 'self' to all of 'other' and returns True iff
* they are equal.
* @param self The first byte range to compare.
* @param offset The offset into the first byte range.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, uint offset, bytes memory other) internal pure returns (bool) {
return self.length >= offset + other.length && equals(self, offset, other, 0, other.length);
}
/*
* @dev Returns true if the two byte ranges are equal.
* @param self The first byte range to compare.
* @param other The second byte range to compare.
* @return True if the byte ranges are equal, false otherwise.
*/
function equals(bytes memory self, bytes memory other) internal pure returns(bool) {
return self.length == other.length && equals(self, 0, other, 0, self.length);
}
/*
* @dev Returns the 8-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 8 bits of the string, interpreted as an integer.
*/
function readUint8(bytes memory self, uint idx) internal pure returns (uint8 ret) {
return uint8(self[idx]);
}
/*
* @dev Returns the 16-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 16 bits of the string, interpreted as an integer.
*/
function readUint16(bytes memory self, uint idx) internal pure returns (uint16 ret) {
require(idx + 2 <= self.length);
assembly {
ret := and(mload(add(add(self, 2), idx)), 0xFFFF)
}
}
/*
* @dev Returns the 32-bit number at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bits of the string, interpreted as an integer.
*/
function readUint32(bytes memory self, uint idx) internal pure returns (uint32 ret) {
require(idx + 4 <= self.length);
assembly {
ret := and(mload(add(add(self, 4), idx)), 0xFFFFFFFF)
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes32(bytes memory self, uint idx) internal pure returns (bytes32 ret) {
require(idx + 32 <= self.length);
assembly {
ret := mload(add(add(self, 32), idx))
}
}
/*
* @dev Returns the 32 byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes
* @return The specified 32 bytes of the string.
*/
function readBytes20(bytes memory self, uint idx) internal pure returns (bytes20 ret) {
require(idx + 20 <= self.length);
assembly {
ret := and(mload(add(add(self, 32), idx)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000)
}
}
/*
* @dev Returns the n byte value at the specified index of self.
* @param self The byte string.
* @param idx The index into the bytes.
* @param len The number of bytes.
* @return The specified 32 bytes of the string.
*/
function readBytesN(bytes memory self, uint idx, uint len) internal pure returns (bytes32 ret) {
require(len <= 32);
require(idx + len <= self.length);
assembly {
let mask := not(sub(exp(256, sub(32, len)), 1))
ret := and(mload(add(add(self, 32), idx)), mask)
}
}
function memcpy(uint dest, uint src, uint len) private pure {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint mask = 256 ** (32 - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/*
* @dev Copies a substring into a new byte string.
* @param self The byte string to copy from.
* @param offset The offset to start copying at.
* @param len The number of bytes to copy.
*/
function substring(bytes memory self, uint offset, uint len) internal pure returns(bytes memory) {
require(offset + len <= self.length);
bytes memory ret = new bytes(len);
uint dest;
uint src;
assembly {
dest := add(ret, 32)
src := add(add(self, 32), offset)
}
memcpy(dest, src, len);
return ret;
}
// Maps characters from 0x30 to 0x7A to their base32 values.
// 0xFF represents invalid characters in that range.
bytes constant base32HexTable = hex'00010203040506070809FFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1FFFFFFFFFFFFFFFFFFFFF0A0B0C0D0E0F101112131415161718191A1B1C1D1E1F';
/**
* @dev Decodes unpadded base32 data of up to one word in length.
* @param self The data to decode.
* @param off Offset into the string to start at.
* @param len Number of characters to decode.
* @return The decoded data, left aligned.
*/
function base32HexDecodeWord(bytes memory self, uint off, uint len) internal pure returns(bytes32) {
require(len <= 52);
uint ret = 0;
uint8 decoded;
for(uint i = 0; i < len; i++) {
bytes1 char = self[off + i];
require(char >= 0x30 && char <= 0x7A);
decoded = uint8(base32HexTable[uint(uint8(char)) - 0x30]);
require(decoded <= 0x20);
if(i == len - 1) {
break;
}
ret = (ret << 5) | decoded;
}
uint bitlen = len * 5;
if(len % 8 == 0) {
// Multiple of 8 characters, no padding
ret = (ret << 5) | decoded;
} else if(len % 8 == 2) {
// Two extra characters - 1 byte
ret = (ret << 3) | (decoded >> 2);
bitlen -= 2;
} else if(len % 8 == 4) {
// Four extra characters - 2 bytes
ret = (ret << 1) | (decoded >> 4);
bitlen -= 4;
} else if(len % 8 == 5) {
// Five extra characters - 3 bytes
ret = (ret << 4) | (decoded >> 1);
bitlen -= 1;
} else if(len % 8 == 7) {
// Seven extra characters - 4 bytes
ret = (ret << 2) | (decoded >> 3);
bitlen -= 3;
} else {
revert();
}
return bytes32(ret << (256 - bitlen));
}
}
/**
* @dev RRUtils is a library that provides utilities for parsing DNS resource records.
*/
library RRUtils {
using BytesUtils for *;
using Buffer for *;
/**
* @dev Returns the number of bytes in the DNS name at 'offset' in 'self'.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The length of the DNS name at 'offset', in bytes.
*/
function nameLength(bytes memory self, uint offset) internal pure returns(uint) {
uint idx = offset;
while (true) {
assert(idx < self.length);
uint labelLen = self.readUint8(idx);
idx += labelLen + 1;
if (labelLen == 0) {
break;
}
}
return idx - offset;
}
/**
* @dev Returns a DNS format name at the specified offset of self.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The name.
*/
function readName(bytes memory self, uint offset) internal pure returns(bytes memory ret) {
uint len = nameLength(self, offset);
return self.substring(offset, len);
}
/**
* @dev Returns the number of labels in the DNS name at 'offset' in 'self'.
* @param self The byte array to read a name from.
* @param offset The offset to start reading at.
* @return The number of labels in the DNS name at 'offset', in bytes.
*/
function labelCount(bytes memory self, uint offset) internal pure returns(uint) {
uint count = 0;
while (true) {
assert(offset < self.length);
uint labelLen = self.readUint8(offset);
offset += labelLen + 1;
if (labelLen == 0) {
break;
}
count += 1;
}
return count;
}
/**
* @dev An iterator over resource records.
*/
struct RRIterator {
bytes data;
uint offset;
uint16 dnstype;
uint16 class;
uint32 ttl;
uint rdataOffset;
uint nextOffset;
}
/**
* @dev Begins iterating over resource records.
* @param self The byte string to read from.
* @param offset The offset to start reading at.
* @return An iterator object.
*/
function iterateRRs(bytes memory self, uint offset) internal pure returns (RRIterator memory ret) {
ret.data = self;
ret.nextOffset = offset;
next(ret);
}
/**
* @dev Returns true iff there are more RRs to iterate.
* @param iter The iterator to check.
* @return True iff the iterator has finished.
*/
function done(RRIterator memory iter) internal pure returns(bool) {
return iter.offset >= iter.data.length;
}
/**
* @dev Moves the iterator to the next resource record.
* @param iter The iterator to advance.
*/
function next(RRIterator memory iter) internal pure {
iter.offset = iter.nextOffset;
if (iter.offset >= iter.data.length) {
return;
}
// Skip the name
uint off = iter.offset + nameLength(iter.data, iter.offset);
// Read type, class, and ttl
iter.dnstype = iter.data.readUint16(off);
off += 2;
iter.class = iter.data.readUint16(off);
off += 2;
iter.ttl = iter.data.readUint32(off);
off += 4;
// Read the rdata
uint rdataLength = iter.data.readUint16(off);
off += 2;
iter.rdataOffset = off;
iter.nextOffset = off + rdataLength;
}
/**
* @dev Returns the name of the current record.
* @param iter The iterator.
* @return A new bytes object containing the owner name from the RR.
*/
function name(RRIterator memory iter) internal pure returns(bytes memory) {
return iter.data.substring(iter.offset, nameLength(iter.data, iter.offset));
}
/**
* @dev Returns the rdata portion of the current record.
* @param iter The iterator.
* @return A new bytes object containing the RR's RDATA.
*/
function rdata(RRIterator memory iter) internal pure returns(bytes memory) {
return iter.data.substring(iter.rdataOffset, iter.nextOffset - iter.rdataOffset);
}
/**
* @dev Checks if a given RR type exists in a type bitmap.
* @param self The byte string to read the type bitmap from.
* @param offset The offset to start reading at.
* @param rrtype The RR type to check for.
* @return True if the type is found in the bitmap, false otherwise.
*/
function checkTypeBitmap(bytes memory self, uint offset, uint16 rrtype) internal pure returns (bool) {
uint8 typeWindow = uint8(rrtype >> 8);
uint8 windowByte = uint8((rrtype & 0xff) / 8);
uint8 windowBitmask = uint8(uint8(1) << (uint8(7) - uint8(rrtype & 0x7)));
for (uint off = offset; off < self.length;) {
uint8 window = self.readUint8(off);
uint8 len = self.readUint8(off + 1);
if (typeWindow < window) {
// We've gone past our window; it's not here.
return false;
} else if (typeWindow == window) {
// Check this type bitmap
if (len * 8 <= windowByte) {
// Our type is past the end of the bitmap
return false;
}
return (self.readUint8(off + windowByte + 2) & windowBitmask) != 0;
} else {
// Skip this type bitmap
off += len + 2;
}
}
return false;
}
function compareNames(bytes memory self, bytes memory other) internal pure returns (int) {
if (self.equals(other)) {
return 0;
}
uint off;
uint otheroff;
uint prevoff;
uint otherprevoff;
uint counts = labelCount(self, 0);
uint othercounts = labelCount(other, 0);
// Keep removing labels from the front of the name until both names are equal length
while (counts > othercounts) {
prevoff = off;
off = progress(self, off);
counts--;
}
while (othercounts > counts) {
otherprevoff = otheroff;
otheroff = progress(other, otheroff);
othercounts--;
}
// Compare the last nonequal labels to each other
while (counts > 0 && !self.equals(off, other, otheroff)) {
prevoff = off;
off = progress(self, off);
otherprevoff = otheroff;
otheroff = progress(other, otheroff);
counts -= 1;
}
if (off == 0) {
return -1;
}
if(otheroff == 0) {
return 1;
}
return self.compare(prevoff + 1, self.readUint8(prevoff), other, otherprevoff + 1, other.readUint8(otherprevoff));
}
function progress(bytes memory body, uint off) internal pure returns(uint) {
return off + 1 + body.readUint8(off);
}
}
contract DNSResolver is ResolverBase {
using RRUtils for *;
using BytesUtils for bytes;
bytes4 constant private DNS_RECORD_INTERFACE_ID = 0xa8fa5682;
// DNSRecordChanged is emitted whenever a given node/name/resource's RRSET is updated.
event DNSRecordChanged(bytes32 indexed node, bytes name, uint16 resource, bytes record);
// DNSRecordDeleted is emitted whenever a given node/name/resource's RRSET is deleted.
event DNSRecordDeleted(bytes32 indexed node, bytes name, uint16 resource);
// DNSZoneCleared is emitted whenever a given node's zone information is cleared.
event DNSZoneCleared(bytes32 indexed node);
// Version the mapping for each zone. This allows users who have lost
// track of their entries to effectively delete an entire zone by bumping
// the version number.
// node => version
mapping(bytes32=>uint256) private versions;
// The records themselves. Stored as binary RRSETs
// node => version => name => resource => data
mapping(bytes32=>mapping(uint256=>mapping(bytes32=>mapping(uint16=>bytes)))) private records;
// Count of number of entries for a given name. Required for DNS resolvers
// when resolving wildcards.
// node => version => name => number of records
mapping(bytes32=>mapping(uint256=>mapping(bytes32=>uint16))) private nameEntriesCount;
/**
* Set one or more DNS records. Records are supplied in wire-format.
* Records with the same node/name/resource must be supplied one after the
* other to ensure the data is updated correctly. For example, if the data
* was supplied:
* a.example.com IN A 1.2.3.4
* a.example.com IN A 5.6.7.8
* www.example.com IN CNAME a.example.com.
* then this would store the two A records for a.example.com correctly as a
* single RRSET, however if the data was supplied:
* a.example.com IN A 1.2.3.4
* www.example.com IN CNAME a.example.com.
* a.example.com IN A 5.6.7.8
* then this would store the first A record, the CNAME, then the second A
* record which would overwrite the first.
*
* @param node the namehash of the node for which to set the records
* @param data the DNS wire format records to set
*/
function setDNSRecords(bytes32 node, bytes calldata data) external authorised(node) {
uint16 resource = 0;
uint256 offset = 0;
bytes memory name;
bytes memory value;
bytes32 nameHash;
// Iterate over the data to add the resource records
for (RRUtils.RRIterator memory iter = data.iterateRRs(0); !iter.done(); iter.next()) {
if (resource == 0) {
resource = iter.dnstype;
name = iter.name();
nameHash = keccak256(abi.encodePacked(name));
value = bytes(iter.rdata());
} else {
bytes memory newName = iter.name();
if (resource != iter.dnstype || !name.equals(newName)) {
setDNSRRSet(node, name, resource, data, offset, iter.offset - offset, value.length == 0);
resource = iter.dnstype;
offset = iter.offset;
name = newName;
nameHash = keccak256(name);
value = bytes(iter.rdata());
}
}
}
if (name.length > 0) {
setDNSRRSet(node, name, resource, data, offset, data.length - offset, value.length == 0);
}
}
/**
* Obtain a DNS record.
* @param node the namehash of the node for which to fetch the record
* @param name the keccak-256 hash of the fully-qualified name for which to fetch the record
* @param resource the ID of the resource as per https://en.wikipedia.org/wiki/List_of_DNS_record_types
* @return the DNS record in wire format if present, otherwise empty
*/
function dnsRecord(bytes32 node, bytes32 name, uint16 resource) public view returns (bytes memory) {
return records[node][versions[node]][name][resource];
}
/**
* Check if a given node has records.
* @param node the namehash of the node for which to check the records
* @param name the namehash of the node for which to check the records
*/
function hasDNSRecords(bytes32 node, bytes32 name) public view returns (bool) {
return (nameEntriesCount[node][versions[node]][name] != 0);
}
/**
* Clear all information for a DNS zone.
* @param node the namehash of the node for which to clear the zone
*/
function clearDNSZone(bytes32 node) public authorised(node) {
versions[node]++;
emit DNSZoneCleared(node);
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == DNS_RECORD_INTERFACE_ID || super.supportsInterface(interfaceID);
}
function setDNSRRSet(
bytes32 node,
bytes memory name,
uint16 resource,
bytes memory data,
uint256 offset,
uint256 size,
bool deleteRecord) private
{
uint256 version = versions[node];
bytes32 nameHash = keccak256(name);
bytes memory rrData = data.substring(offset, size);
if (deleteRecord) {
if (records[node][version][nameHash][resource].length != 0) {
nameEntriesCount[node][version][nameHash]--;
}
delete(records[node][version][nameHash][resource]);
emit DNSRecordDeleted(node, name, resource);
} else {
if (records[node][version][nameHash][resource].length == 0) {
nameEntriesCount[node][version][nameHash]++;
}
records[node][version][nameHash][resource] = rrData;
emit DNSRecordChanged(node, name, resource, rrData);
}
}
}
contract InterfaceResolver is ResolverBase, AddrResolver {
bytes4 constant private INTERFACE_INTERFACE_ID = bytes4(keccak256("interfaceImplementer(bytes32,bytes4)"));
bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
event InterfaceChanged(bytes32 indexed node, bytes4 indexed interfaceID, address implementer);
mapping(bytes32=>mapping(bytes4=>address)) interfaces;
/**
* Sets an interface associated with a name.
* Setting the address to 0 restores the default behaviour of querying the contract at `addr()` for interface support.
* @param node The node to update.
* @param interfaceID The EIP 168 interface ID.
* @param implementer The address of a contract that implements this interface for this node.
*/
function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external authorised(node) {
interfaces[node][interfaceID] = implementer;
emit InterfaceChanged(node, interfaceID, implementer);
}
/**
* Returns the address of a contract that implements the specified interface for this name.
* If an implementer has not been set for this interfaceID and name, the resolver will query
* the contract at `addr()`. If `addr()` is set, a contract exists at that address, and that
* contract implements EIP168 and returns `true` for the specified interfaceID, its address
* will be returned.
* @param node The ENS node to query.
* @param interfaceID The EIP 168 interface ID to check for.
* @return The address that implements this interface, or 0 if the interface is unsupported.
*/
function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address) {
address implementer = interfaces[node][interfaceID];
if(implementer != address(0)) {
return implementer;
}
address a = addr(node);
if(a == address(0)) {
return address(0);
}
(bool success, bytes memory returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", INTERFACE_META_ID));
if(!success || returnData.length < 32 || returnData[31] == 0) {
// EIP 168 not supported by target
return address(0);
}
(success, returnData) = a.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceID));
if(!success || returnData.length < 32 || returnData[31] == 0) {
// Specified interface not supported by target
return address(0);
}
return a;
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == INTERFACE_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
contract PublicResolver is ABIResolver, AddrResolver, ContentHashResolver, DNSResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver {
ENS ens;
/**
* A mapping of authorisations. An address that is authorised for a name
* may make any changes to the name that the owner could, but may not update
* the set of authorisations.
* (node, owner, caller) => isAuthorised
*/
mapping(bytes32=>mapping(address=>mapping(address=>bool))) public authorisations;
event AuthorisationChanged(bytes32 indexed node, address indexed owner, address indexed target, bool isAuthorised);
constructor(ENS _ens) public {
ens = _ens;
}
/**
* @dev Sets or clears an authorisation.
* Authorisations are specific to the caller. Any account can set an authorisation
* for any name, but the authorisation that is checked will be that of the
* current owner of a name. Thus, transferring a name effectively clears any
* existing authorisations, and new authorisations can be set in advance of
* an ownership transfer if desired.
*
* @param node The name to change the authorisation on.
* @param target The address that is to be authorised or deauthorised.
* @param isAuthorised True if the address should be authorised, or false if it should be deauthorised.
*/
function setAuthorisation(bytes32 node, address target, bool isAuthorised) external {
authorisations[node][msg.sender][target] = isAuthorised;
emit AuthorisationChanged(node, msg.sender, target, isAuthorised);
}
function isAuthorised(bytes32 node) internal view returns(bool) {
address owner = ens.owner(node);
return owner == msg.sender || authorisations[node][owner][msg.sender];
}
}
/**
* @title IERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safeTransfer`. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
/**
* @title ERC165
* @author Matt Condon (@shrugs)
* @dev Implements ERC165 using a lookup table.
*/
contract ERC165 is IERC165 {
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself
*/
constructor () internal {
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev internal method for registering an interface
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
_supportedInterfaces[interfaceId] = true;
}
}
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from token ID to owner
mapping (uint256 => address) private _tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => uint256) private _ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
constructor () public {
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
}
/**
* @dev Gets the balance of the specified address
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
return _ownedTokensCount[owner];
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenId uint256 ID of the token to query the owner of
* @return owner address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param to operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible
* Requires the msg sender 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
*/
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* 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
* @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
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* 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
* @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 {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data));
}
/**
* @dev Returns whether the specified token exists
* @param tokenId uint256 ID of the token to query the existence of
* @return whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
address owner = _tokenOwner[tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId));
_tokenOwner[tokenId] = to;
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* Deprecated, use _burn(uint256) instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
require(ownerOf(tokenId) == owner);
_clearApproval(tokenId);
_ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1);
_tokenOwner[tokenId] = address(0);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal {
_burn(ownerOf(tokenId), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to transferFrom, this imposes no restrictions on msg.sender.
* @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
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from);
require(to != address(0));
_clearApproval(tokenId);
_ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
_ownedTokensCount[to] = _ownedTokensCount[to].add(1);
_tokenOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
internal returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Private function to clear current approval of a given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(uint256 tokenId) private {
if (_tokenApprovals[tokenId] != address(0)) {
_tokenApprovals[tokenId] = address(0);
}
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract BaseRegistrar is IERC721, Ownable {
uint constant public GRACE_PERIOD = 90 days;
event ControllerAdded(address indexed controller);
event ControllerRemoved(address indexed controller);
event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
event NameRenewed(uint256 indexed id, uint expires);
// The ENS registry
ENS public ens;
// The namehash of the TLD this registrar owns (eg, .eth)
bytes32 public baseNode;
// A map of addresses that are authorised to register and renew names.
mapping(address=>bool) public controllers;
// Authorises a controller, who can register and renew domains.
function addController(address controller) external;
// Revoke controller permission for an address.
function removeController(address controller) external;
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external;
// Returns the expiration timestamp of the specified label hash.
function nameExpires(uint256 id) external view returns(uint);
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool);
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint);
function renew(uint256 id, uint duration) external returns(uint);
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external;
}
contract BaseRegistrarImplementation is BaseRegistrar, ERC721 {
// A map of expiry times
mapping(uint256=>uint) expiries;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(address)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
constructor(ENS _ens, bytes32 _baseNode) public {
ens = _ens;
baseNode = _baseNode;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
/**
* @dev Gets the owner of the specified token ID. Names become unowned
* when their registration expires.
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
require(expiries[tokenId] > now);
return super.ownerOf(tokenId);
}
// Authorises a controller, who can register and renew domains.
function addController(address controller) external onlyOwner {
controllers[controller] = true;
emit ControllerAdded(controller);
}
// Revoke controller permission for an address.
function removeController(address controller) external onlyOwner {
controllers[controller] = false;
emit ControllerRemoved(controller);
}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// Returns the expiration timestamp of the specified id.
function nameExpires(uint256 id) external view returns(uint) {
return expiries[id];
}
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool) {
// Not available if it's registered here or in its grace period.
return expiries[id] + GRACE_PERIOD < now;
}
/**
* @dev Register a name.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function register(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, true);
}
/**
* @dev Register a name, without modifying the registry.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, false);
}
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
require(available(id));
require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow
expiries[id] = now + duration;
if(_exists(id)) {
// Name was previously owned, and expired
_burn(id);
}
_mint(owner, id);
if(updateRegistry) {
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
emit NameRegistered(id, owner, now + duration);
return now + duration;
}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external live {
require(_isApprovedOrOwner(msg.sender, id));
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == ERC721_ID ||
interfaceID == RECLAIM_ID;
}
}
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
contract OwnedResolver is Ownable, ABIResolver, AddrResolver, ContentHashResolver, InterfaceResolver, NameResolver, PubkeyResolver, TextResolver {
function isAuthorised(bytes32 node) internal view returns(bool) {
return msg.sender == owner();
}
}
/**
* @dev Provides a default implementation of a resolver for reverse records,
* which permits only the owner to update it.
*/
contract DefaultReverseResolver {
// namehash('addr.reverse')
bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
ENS public ens;
mapping (bytes32 => string) public name;
/**
* @dev Only permits calls by the reverse registrar.
* @param node The node permission is required for.
*/
modifier onlyOwner(bytes32 node) {
require(msg.sender == ens.owner(node));
_;
}
/**
* @dev Constructor
* @param ensAddr The address of the ENS registry.
*/
constructor(ENS ensAddr) public {
ens = ensAddr;
// Assign ownership of the reverse record to our deployer
ReverseRegistrar registrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE));
if (address(registrar) != address(0x0)) {
registrar.claim(msg.sender);
}
}
/**
* @dev Sets the name for a node.
* @param node The node to update.
* @param _name The name to set.
*/
function setName(bytes32 node, string memory _name) public onlyOwner(node) {
name[node] = _name;
}
}
pragma solidity ^0.4.24;
contract EncodeTest {
uint256 public nonce = 12;
bytes public newState = "jhfjjhfhtffffffffdghhhdfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffABCDEFGHIJKLMNOPQRSTUVWXYZ012345678999999999999999999999999999999999999997777777777777777777777777778888888888888888888888888888888886666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666";
function encode() public view returns (bytes32) {
return keccak256(abi.encode(keccak256(newState), bytes32(nonce)));
}
function encodePacked() public view returns (bytes32) {
return keccak256(abi.encodePacked(keccak256(newState), bytes32(nonce)));
}
function original() public view returns (bytes32) {
return keccak256([keccak256(newState), bytes32(nonce)]);
}
function storageKey(string _input) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_input));
}
}
pragma solidity ^0.5.0;
import "./CronAggregator_flat.sol";
// StablePriceOracle sets a price in USD, based on an oracle.
contract ENSOracle is CronAggregator {
constructor (
uint256 _currentValue,
uint256 _fixedTimeBase,
uint256 _fixedTimeFrequency,
uint256 _contributionTimeWindow,
uint256 _minContribution,
bool _onlyUniqueContributors,
address[] memory _authorizedContributors
)
public
CronAggregator(
_currentValue,
_fixedTimeBase,
_fixedTimeFrequency,
_contributionTimeWindow,
_minContribution,
_onlyUniqueContributors,
_authorizedContributors
)
{
}
function read()
external
view
returns (bytes32)
{
return bytes32(currentValue);
}
}
pragma solidity ^0.4.18;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public;
function setResolver(bytes32 node, address resolver) public;
function setOwner(bytes32 node, address owner) public;
function setTTL(bytes32 node, uint64 ttl) public;
function owner(bytes32 node) public view returns (address);
function resolver(bytes32 node) public view returns (address);
function ttl(bytes32 node) public view returns (uint64);
}
/**
* The ENS registry contract.
*/
contract ENSRegistry is ENS {
struct Record {
address owner;
address resolver;
uint64 ttl;
}
mapping (bytes32 => Record) records;
// Permits modifications only by the owner of the specified node.
modifier only_owner(bytes32 node) {
require(records[node].owner == msg.sender);
_;
}
/**
* @dev Constructs a new ENS registrar.
*/
function ENSRegistry() public {
records[0x0].owner = msg.sender;
}
/**
* @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node.
* @param node The node to transfer ownership of.
* @param owner The address of the new owner.
*/
function setOwner(bytes32 node, address owner) public only_owner(node) {
Transfer(node, owner);
records[node].owner = owner;
}
/**
* @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
*/
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public only_owner(node) {
var subnode = keccak256(node, label);
NewOwner(node, label, owner);
records[subnode].owner = owner;
}
/**
* @dev Sets the resolver address for the specified node.
* @param node The node to update.
* @param resolver The address of the resolver.
*/
function setResolver(bytes32 node, address resolver) public only_owner(node) {
NewResolver(node, resolver);
records[node].resolver = resolver;
}
/**
* @dev Sets the TTL for the specified node.
* @param node The node to update.
* @param ttl The TTL in seconds.
*/
function setTTL(bytes32 node, uint64 ttl) public only_owner(node) {
NewTTL(node, ttl);
records[node].ttl = ttl;
}
/**
* @dev Returns the address that owns the specified node.
* @param node The specified node.
* @return address of the owner.
*/
function owner(bytes32 node) public view returns (address) {
return records[node].owner;
}
/**
* @dev Returns the address of the resolver for the specified node.
* @param node The specified node.
* @return address of the resolver.
*/
function resolver(bytes32 node) public view returns (address) {
return records[node].resolver;
}
/**
* @dev Returns the TTL of a node, and any records associated with it.
* @param node The specified node.
* @return ttl of the node.
*/
function ttl(bytes32 node) public view returns (uint64) {
return records[node].ttl;
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: openzeppelin-solidity/contracts/AddressUtils.sol
pragma solidity ^0.4.24;
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param _addr address to check
* @return whether the target address is a contract
*/
function isContract(address _addr) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(_addr) }
return size > 0;
}
}
// File: contracts/upgradeability/Proxy.sol
pragma solidity 0.4.24;
/**
* @title Proxy
* @dev Gives the possibility to delegate any call to a foreign implementation.
*/
contract Proxy {
/**
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
/* solcov ignore next */
function implementation() public view returns (address);
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
function() public payable {
// solhint-disable-previous-line no-complex-fallback
address _impl = implementation();
require(_impl != address(0));
assembly {
/*
0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40)
loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty
memory. It's needed because we're going to write the return data of delegatecall to the
free memory slot.
*/
let ptr := mload(0x40)
/*
`calldatacopy` is copy calldatasize bytes from calldata
First argument is the destination to which data is copied(ptr)
Second argument specifies the start position of the copied data.
Since calldata is sort of its own unique location in memory,
0 doesn't refer to 0 in memory or 0 in storage - it just refers to the zeroth byte of calldata.
That's always going to be the zeroth byte of the function selector.
Third argument, calldatasize, specifies how much data will be copied.
calldata is naturally calldatasize bytes long (same thing as msg.data.length)
*/
calldatacopy(ptr, 0, calldatasize)
/*
delegatecall params explained:
gas: the amount of gas to provide for the call. `gas` is an Opcode that gives
us the amount of gas still available to execution
_impl: address of the contract to delegate to
ptr: to pass copied data
calldatasize: loads the size of `bytes memory data`, same as msg.data.length
0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic,
these are set to 0, 0 so the output data will not be written to memory. The output
data will be read using `returndatasize` and `returdatacopy` instead.
result: This will be 0 if the call fails and 1 if it succeeds
*/
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
/*
*/
/*
ptr current points to the value stored at 0x40,
because we assigned it like ptr := mload(0x40).
Because we use 0x40 as a free memory pointer,
we want to make sure that the next time we want to allocate memory,
we aren't overwriting anything important.
So, by adding ptr and returndatasize,
we get a memory location beyond the end of the data we will be copying to ptr.
We place this in at 0x40, and any reads from 0x40 will now read from free memory
*/
mstore(0x40, add(ptr, returndatasize))
/*
`returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the
slot it will copy to, 0 means copy from the beginning of the return data, and size is
the amount of data to copy.
`returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall
*/
returndatacopy(ptr, 0, returndatasize)
/*
if `result` is 0, revert.
if `result` is 1, return `size` amount of data from `ptr`. This is the data that was
copied to `ptr` from the delegatecall return data
*/
switch result
case 0 {
revert(ptr, returndatasize)
}
default {
return(ptr, returndatasize)
}
}
}
}
// File: contracts/upgradeability/UpgradeabilityStorage.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Version name of the current implementation
uint256 internal _version;
// Address of the current implementation
address internal _implementation;
/**
* @dev Tells the version name of the current implementation
* @return uint256 representing the name of the current version
*/
function version() external view returns (uint256) {
return _version;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}
// File: contracts/upgradeability/UpgradeabilityProxy.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityProxy
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
*/
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
/**
* @dev This event will be emitted every time the implementation gets upgraded
* @param version representing the version name of the upgraded implementation
* @param implementation representing the address of the upgraded implementation
*/
event Upgraded(uint256 version, address indexed implementation);
/**
* @dev Upgrades the implementation address
* @param version representing the version name of the new implementation to be set
* @param implementation representing the address of the new implementation to be set
*/
function _upgradeTo(uint256 version, address implementation) internal {
require(_implementation != implementation);
// This additional check verifies that provided implementation is at least a contract
require(AddressUtils.isContract(implementation));
// This additional check guarantees that new version will be at least greater than the privios one,
// so it is impossible to reuse old versions, or use the last version twice
require(version > _version);
_version = version;
_implementation = implementation;
emit Upgraded(version, implementation);
}
}
// File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityOwnerStorage
* @dev This contract keeps track of the upgradeability owner
*/
contract UpgradeabilityOwnerStorage {
// Owner of the contract
address internal _upgradeabilityOwner;
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function upgradeabilityOwner() public view returns (address) {
return _upgradeabilityOwner;
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
_upgradeabilityOwner = newUpgradeabilityOwner;
}
}
// File: contracts/upgradeability/OwnedUpgradeabilityProxy.sol
pragma solidity 0.4.24;
/**
* @title OwnedUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
*/
contract OwnedUpgradeabilityProxy is UpgradeabilityOwnerStorage, UpgradeabilityProxy {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event ProxyOwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev the constructor sets the original owner of the contract to the sender account.
*/
constructor() public {
setUpgradeabilityOwner(msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyUpgradeabilityOwner() {
require(msg.sender == upgradeabilityOwner());
/* solcov ignore next */
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferProxyOwnership(address newOwner) external onlyUpgradeabilityOwner {
require(newOwner != address(0));
emit ProxyOwnershipTransferred(upgradeabilityOwner(), newOwner);
setUpgradeabilityOwner(newOwner);
}
/**
* @dev Allows the upgradeability owner to upgrade the current version of the proxy.
* @param version representing the version name of the new implementation to be set.
* @param implementation representing the address of the new implementation to be set.
*/
function upgradeTo(uint256 version, address implementation) public onlyUpgradeabilityOwner {
_upgradeTo(version, implementation);
}
/**
* @dev Allows the upgradeability owner to upgrade the current version of the proxy and call the new implementation
* to initialize whatever is needed through a low level call.
* @param version representing the version name of the new implementation to be set.
* @param implementation representing the address of the new implementation to be set.
* @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
* signature of the implementation to be called with the needed payload
*/
function upgradeToAndCall(uint256 version, address implementation, bytes data)
external
payable
onlyUpgradeabilityOwner
{
upgradeTo(version, implementation);
// solhint-disable-next-line avoid-call-value
require(address(this).call.value(msg.value)(data));
}
}
// File: contracts/upgradeability/EternalStorageProxy.sol
pragma solidity 0.4.24;
/**
* @title EternalStorageProxy
* @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
* Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic
* authorization control functionalities
*/
// solhint-disable-next-line no-empty-blocks
contract EternalStorageProxy is EternalStorage, OwnedUpgradeabilityProxy {}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeability/Proxy.sol
pragma solidity 0.4.24;
/**
* @title Proxy
* @dev Gives the possibility to delegate any call to a foreign implementation.
*/
contract Proxy {
/**
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
function implementation() public view returns (address);
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
function () payable public {
address _impl = implementation();
require(_impl != address(0));
assembly {
/*
0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40)
loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty
memory. It's needed because we're going to write the return data of delegatecall to the
free memory slot.
*/
let ptr := mload(0x40)
/*
`calldatacopy` is copy calldatasize bytes from calldata
First argument is the destination to which data is copied(ptr)
Second argument specifies the start position of the copied data.
Since calldata is sort of its own unique location in memory,
0 doesn't refer to 0 in memory or 0 in storage - it just refers to the zeroth byte of calldata.
That's always going to be the zeroth byte of the function selector.
Third argument, calldatasize, specifies how much data will be copied.
calldata is naturally calldatasize bytes long (same thing as msg.data.length)
*/
calldatacopy(ptr, 0, calldatasize)
/*
delegatecall params explained:
gas: the amount of gas to provide for the call. `gas` is an Opcode that gives
us the amount of gas still available to execution
_impl: address of the contract to delegate to
ptr: to pass copied data
calldatasize: loads the size of `bytes memory data`, same as msg.data.length
0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic,
these are set to 0, 0 so the output data will not be written to memory. The output
data will be read using `returndatasize` and `returdatacopy` instead.
result: This will be 0 if the call fails and 1 if it succeeds
*/
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
/*
*/
/*
ptr current points to the value stored at 0x40,
because we assigned it like ptr := mload(0x40).
Because we use 0x40 as a free memory pointer,
we want to make sure that the next time we want to allocate memory,
we aren't overwriting anything important.
So, by adding ptr and returndatasize,
we get a memory location beyond the end of the data we will be copying to ptr.
We place this in at 0x40, and any reads from 0x40 will now read from free memory
*/
mstore(0x40, add(ptr, returndatasize))
/*
`returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the
slot it will copy to, 0 means copy from the beginning of the return data, and size is
the amount of data to copy.
`returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall
*/
returndatacopy(ptr, 0, returndatasize)
/*
if `result` is 0, revert.
if `result` is 1, return `size` amount of data from `ptr`. This is the data that was
copied to `ptr` from the delegatecall return data
*/
switch result
case 0 { revert(ptr, returndatasize) }
default { return(ptr, returndatasize) }
}
}
}
// File: contracts/upgradeability/UpgradeabilityStorage.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityStorage
* @dev This contract holds all the necessary state variables to support the upgrade functionality
*/
contract UpgradeabilityStorage {
// Version name of the current implementation
uint256 internal _version;
// Address of the current implementation
address internal _implementation;
/**
* @dev Tells the version name of the current implementation
* @return string representing the name of the current version
*/
function version() public view returns (uint256) {
return _version;
}
/**
* @dev Tells the address of the current implementation
* @return address of the current implementation
*/
function implementation() public view returns (address) {
return _implementation;
}
}
// File: contracts/upgradeability/UpgradeabilityProxy.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityProxy
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
*/
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
/**
* @dev This event will be emitted every time the implementation gets upgraded
* @param version representing the version name of the upgraded implementation
* @param implementation representing the address of the upgraded implementation
*/
event Upgraded(uint256 version, address indexed implementation);
/**
* @dev Upgrades the implementation address
* @param version representing the version name of the new implementation to be set
* @param implementation representing the address of the new implementation to be set
*/
function _upgradeTo(uint256 version, address implementation) internal {
require(_implementation != implementation);
require(version > _version);
_version = version;
_implementation = implementation;
emit Upgraded(version, implementation);
}
}
// File: contracts/upgradeability/UpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
/**
* @title UpgradeabilityOwnerStorage
* @dev This contract keeps track of the upgradeability owner
*/
contract UpgradeabilityOwnerStorage {
// Owner of the contract
address private _upgradeabilityOwner;
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function upgradeabilityOwner() public view returns (address) {
return _upgradeabilityOwner;
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal {
_upgradeabilityOwner = newUpgradeabilityOwner;
}
}
// File: contracts/upgradeability/OwnedUpgradeabilityProxy.sol
pragma solidity 0.4.24;
/**
* @title OwnedUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
*/
contract OwnedUpgradeabilityProxy is UpgradeabilityOwnerStorage, UpgradeabilityProxy {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event ProxyOwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev the constructor sets the original owner of the contract to the sender account.
*/
constructor() public {
setUpgradeabilityOwner(msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyProxyOwner() {
require(msg.sender == proxyOwner());
_;
}
/**
* @dev Tells the address of the proxy owner
* @return the address of the proxy owner
*/
function proxyOwner() public view returns (address) {
return upgradeabilityOwner();
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferProxyOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0));
emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
setUpgradeabilityOwner(newOwner);
}
/**
* @dev Allows the upgradeability owner to upgrade the current version of the proxy.
* @param version representing the version name of the new implementation to be set.
* @param implementation representing the address of the new implementation to be set.
*/
function upgradeTo(uint256 version, address implementation) public onlyProxyOwner {
_upgradeTo(version, implementation);
}
/**
* @dev Allows the upgradeability owner to upgrade the current version of the proxy and call the new implementation
* to initialize whatever is needed through a low level call.
* @param version representing the version name of the new implementation to be set.
* @param implementation representing the address of the new implementation to be set.
* @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
* signature of the implementation to be called with the needed payload
*/
function upgradeToAndCall(uint256 version, address implementation, bytes data) payable public onlyProxyOwner {
upgradeTo(version, implementation);
require(address(this).call.value(msg.value)(data));
}
}
// File: contracts/upgradeability/EternalStorageProxy.sol
pragma solidity 0.4.24;
/**
* @title EternalStorageProxy
* @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
* Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic
* authorization control functionalities
*/
contract EternalStorageProxy is OwnedUpgradeabilityProxy, EternalStorage {}
pragma solidity ^0.5.0;
interface Deed {
function setOwner(address payable newOwner) external;
function setRegistrar(address newRegistrar) external;
function setBalance(uint newValue, bool throwOnFailure) external;
function closeDeed(uint refundRatio) external;
function destroyDeed() external;
function owner() external view returns (address);
function previousOwner() external view returns (address);
function value() external view returns (uint);
function creationDate() external view returns (uint);
}
interface Registrar {
enum Mode { Open, Auction, Owned, Forbidden, Reveal, NotYetAvailable }
event AuctionStarted(bytes32 indexed hash, uint registrationDate);
event NewBid(bytes32 indexed hash, address indexed bidder, uint deposit);
event BidRevealed(bytes32 indexed hash, address indexed owner, uint value, uint8 status);
event HashRegistered(bytes32 indexed hash, address indexed owner, uint value, uint registrationDate);
event HashReleased(bytes32 indexed hash, uint value);
event HashInvalidated(bytes32 indexed hash, string indexed name, uint value, uint registrationDate);
function state(bytes32 _hash) external view returns (Mode);
function startAuction(bytes32 _hash) external;
function startAuctions(bytes32[] calldata _hashes) external;
function newBid(bytes32 sealedBid) external payable;
function startAuctionsAndBid(bytes32[] calldata hashes, bytes32 sealedBid) external payable;
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) external;
function cancelBid(address bidder, bytes32 seal) external;
function finalizeAuction(bytes32 _hash) external;
function transfer(bytes32 _hash, address payable newOwner) external;
function releaseDeed(bytes32 _hash) external;
function invalidateName(string calldata unhashedName) external;
function eraseNode(bytes32[] calldata labels) external;
function transferRegistrars(bytes32 _hash) external;
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) external;
function entries(bytes32 _hash) external view returns (Mode, address, uint, uint, uint);
}
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external;
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
}
/**
* A generic resolver interface which includes all the functions including the ones deprecated
*/
interface Resolver{
event AddrChanged(bytes32 indexed node, address a);
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
event NameChanged(bytes32 indexed node, string name);
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
event TextChanged(bytes32 indexed node, string indexed indexedKey, string key);
event ContenthashChanged(bytes32 indexed node, bytes hash);
/* Deprecated events */
event ContentChanged(bytes32 indexed node, bytes32 hash);
function ABI(bytes32 node, uint256 contentTypes) external view returns (uint256, bytes memory);
function addr(bytes32 node) external view returns (address);
function addr(bytes32 node, uint coinType) external view returns(bytes memory);
function contenthash(bytes32 node) external view returns (bytes memory);
function dnsrr(bytes32 node) external view returns (bytes memory);
function name(bytes32 node) external view returns (string memory);
function pubkey(bytes32 node) external view returns (bytes32 x, bytes32 y);
function text(bytes32 node, string calldata key) external view returns (string memory);
function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address);
function setABI(bytes32 node, uint256 contentType, bytes calldata data) external;
function setAddr(bytes32 node, address addr) external;
function setAddr(bytes32 node, uint coinType, bytes calldata a) external;
function setContenthash(bytes32 node, bytes calldata hash) external;
function setDnsrr(bytes32 node, bytes calldata data) external;
function setName(bytes32 node, string calldata _name) external;
function setPubkey(bytes32 node, bytes32 x, bytes32 y) external;
function setText(bytes32 node, string calldata key, string calldata value) external;
function setInterface(bytes32 node, bytes4 interfaceID, address implementer) external;
function supportsInterface(bytes4 interfaceID) external pure returns (bool);
/* Deprecated functions */
function content(bytes32 node) external view returns (bytes32);
function multihash(bytes32 node) external view returns (bytes memory);
function setContent(bytes32 node, bytes32 hash) external;
function setMultihash(bytes32 node, bytes calldata hash) external;
}
library StringUtils {
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
uint len;
uint i = 0;
uint bytelength = bytes(s).length;
for(len = 0; i < bytelength; len++) {
byte b = bytes(s)[i];
if(b < 0x80) {
i += 1;
} else if (b < 0xE0) {
i += 2;
} else if (b < 0xF0) {
i += 3;
} else if (b < 0xF8) {
i += 4;
} else if (b < 0xFC) {
i += 5;
} else {
i += 6;
}
}
return len;
}
}
interface PriceOracle {
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param expires When the name presently expires (0 if this is a new registration).
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint expires, uint duration) external view returns(uint);
}
/**
* @title IERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract BaseRegistrar is IERC721, Ownable {
uint constant public GRACE_PERIOD = 90 days;
event ControllerAdded(address indexed controller);
event ControllerRemoved(address indexed controller);
event NameMigrated(uint256 indexed id, address indexed owner, uint expires);
event NameRegistered(uint256 indexed id, address indexed owner, uint expires);
event NameRenewed(uint256 indexed id, uint expires);
// Expiration timestamp for migrated domains.
uint public transferPeriodEnds;
// The ENS registry
ENS public ens;
// The namehash of the TLD this registrar owns (eg, .eth)
bytes32 public baseNode;
// The interim registrar
Registrar public previousRegistrar;
// A map of addresses that are authorised to register and renew names.
mapping(address=>bool) public controllers;
// Authorises a controller, who can register and renew domains.
function addController(address controller) external;
// Revoke controller permission for an address.
function removeController(address controller) external;
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external;
// Returns the expiration timestamp of the specified label hash.
function nameExpires(uint256 id) external view returns(uint);
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool);
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint);
function renew(uint256 id, uint duration) external returns(uint);
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external;
/**
* @dev Transfers a registration from the initial registrar.
* This function is called by the initial registrar when a user calls `transferRegistrars`.
*/
function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external;
}
/**
* @dev A registrar controller for registering and renewing names at fixed cost.
*/
contract ETHRegistrarController is Ownable {
using StringUtils for *;
uint constant public MIN_REGISTRATION_DURATION = 28 days;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4(
keccak256("rentPrice(string,uint256)") ^
keccak256("available(string)") ^
keccak256("makeCommitment(string,address,bytes32)") ^
keccak256("commit(bytes32)") ^
keccak256("register(string,address,uint256,bytes32)") ^
keccak256("renew(string,uint256)")
);
bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4(
keccak256("registerWithConfig(string,address,uint256,bytes32,address,address)") ^
keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)")
);
BaseRegistrar base;
PriceOracle prices;
uint public minCommitmentAge;
uint public maxCommitmentAge;
mapping(bytes32=>uint) public commitments;
event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
event NewPriceOracle(address indexed oracle);
constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public {
require(_maxCommitmentAge > _minCommitmentAge);
base = _base;
prices = _prices;
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}
function rentPrice(string memory name, uint duration) view public returns(uint) {
bytes32 hash = keccak256(bytes(name));
return prices.price(name, base.nameExpires(uint256(hash)), duration);
}
function valid(string memory name) public pure returns(bool) {
return name.strlen() >= 3;
}
function available(string memory name) public view returns(bool) {
bytes32 label = keccak256(bytes(name));
return valid(name) && base.available(uint256(label));
}
function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) {
return makeCommitmentWithConfig(name, owner, secret, address(0), address(0));
}
function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) {
bytes32 label = keccak256(bytes(name));
if (resolver == address(0) && addr == address(0)) {
return keccak256(abi.encodePacked(label, owner, secret));
}
require(resolver != address(0));
return keccak256(abi.encodePacked(label, owner, resolver, addr, secret));
}
function commit(bytes32 commitment) public {
require(commitments[commitment] + maxCommitmentAge < now);
commitments[commitment] = now;
}
function register(string calldata name, address owner, uint duration, bytes32 secret) external payable {
registerWithConfig(name, owner, duration, secret, address(0), address(0));
}
function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {
bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr);
uint cost = _consumeCommitment(name, duration, commitment);
bytes32 label = keccak256(bytes(name));
uint256 tokenId = uint256(label);
uint expires;
if(resolver != address(0)) {
// Set this contract as the (temporary) owner, giving it
// permission to set up the resolver.
expires = base.register(tokenId, address(this), duration);
// The nodehash of this label
bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label));
// Set the resolver
base.ens().setResolver(nodehash, resolver);
// Configure the resolver
if (addr != address(0)) {
Resolver(resolver).setAddr(nodehash, addr);
}
// Now transfer full ownership to the expeceted owner
base.reclaim(tokenId, owner);
base.transferFrom(address(this), owner, tokenId);
} else {
require(addr == address(0));
expires = base.register(tokenId, owner, duration);
}
emit NameRegistered(name, label, owner, cost, expires);
// Refund any extra payment
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
}
function renew(string calldata name, uint duration) external payable {
uint cost = rentPrice(name, duration);
require(msg.value >= cost);
bytes32 label = keccak256(bytes(name));
uint expires = base.renew(uint256(label), duration);
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
emit NameRenewed(name, label, cost, expires);
}
function setPriceOracle(PriceOracle _prices) public onlyOwner {
prices = _prices;
emit NewPriceOracle(address(prices));
}
function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner {
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}
function withdraw() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == COMMITMENT_CONTROLLER_ID ||
interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID;
}
function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {
// Require a valid commitment
require(commitments[commitment] + minCommitmentAge <= now);
// If the commitment is too old, or the name is registered, stop
require(commitments[commitment] + maxCommitmentAge > now);
require(available(name));
delete(commitments[commitment]);
uint cost = rentPrice(name, duration);
require(duration >= MIN_REGISTRATION_DURATION);
require(msg.value >= cost);
return cost;
}
}
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
pragma solidity 0.5.17;
library Hasher {
function MiMCSponge(uint256 in_xL, uint256 in_xR) public pure returns (uint256 xL, uint256 xR);
}
contract ReentrancyGuard {
// counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
constructor () internal {
// The counter starts at one to prevent changing it from zero to a non-zero
// value, which is a more expensive operation.
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}
contract MerkleTreeWithHistory {
uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 public constant ZERO_VALUE = 21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE
uint32 public levels;
// the following variables are made public for easier testing and debugging and
// are not supposed to be accessed in regular code
bytes32[] public filledSubtrees;
bytes32[] public zeros;
uint32 public currentRootIndex = 0;
uint32 public nextIndex = 0;
uint32 public constant ROOT_HISTORY_SIZE = 100;
bytes32[ROOT_HISTORY_SIZE] public roots;
constructor(uint32 _treeLevels) public {
require(_treeLevels > 0, "_treeLevels should be greater than zero");
require(_treeLevels < 32, "_treeLevels should be less than 32");
levels = _treeLevels;
bytes32 currentZero = bytes32(ZERO_VALUE);
zeros.push(currentZero);
filledSubtrees.push(currentZero);
for (uint32 i = 1; i < levels; i++) {
currentZero = hashLeftRight(currentZero, currentZero);
zeros.push(currentZero);
filledSubtrees.push(currentZero);
}
roots[0] = hashLeftRight(currentZero, currentZero);
}
/**
@dev Hash 2 tree leaves, returns MiMC(_left, _right)
*/
function hashLeftRight(bytes32 _left, bytes32 _right) public pure returns (bytes32) {
require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
uint256 R = uint256(_left);
uint256 C = 0;
(R, C) = Hasher.MiMCSponge(R, C);
R = addmod(R, uint256(_right), FIELD_SIZE);
(R, C) = Hasher.MiMCSponge(R, C);
return bytes32(R);
}
function _insert(bytes32 _leaf) internal returns(uint32 index) {
uint32 currentIndex = nextIndex;
require(currentIndex != uint32(2)**levels, "Merkle tree is full. No more leafs can be added");
nextIndex += 1;
bytes32 currentLevelHash = _leaf;
bytes32 left;
bytes32 right;
for (uint32 i = 0; i < levels; i++) {
if (currentIndex % 2 == 0) {
left = currentLevelHash;
right = zeros[i];
filledSubtrees[i] = currentLevelHash;
} else {
left = filledSubtrees[i];
right = currentLevelHash;
}
currentLevelHash = hashLeftRight(left, right);
currentIndex /= 2;
}
currentRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
roots[currentRootIndex] = currentLevelHash;
return nextIndex - 1;
}
/**
@dev Whether the root is present in the root history
*/
function isKnownRoot(bytes32 _root) public view returns(bool) {
if (_root == 0) {
return false;
}
uint32 i = currentRootIndex;
do {
if (_root == roots[i]) {
return true;
}
if (i == 0) {
i = ROOT_HISTORY_SIZE;
}
i--;
} while (i != currentRootIndex);
return false;
}
/**
@dev Returns the last root
*/
function getLastRoot() public view returns(bytes32) {
return roots[currentRootIndex];
}
}
contract IVerifier {
function verifyProof(bytes memory _proof, uint256[6] memory _input) public returns(bool);
}
contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
uint256 public denomination;
mapping(bytes32 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment
mapping(bytes32 => bool) public commitments;
IVerifier public verifier;
// operator can update snark verification key
// after the final trusted setup ceremony operator rights are supposed to be transferred to zero address
address public operator;
modifier onlyOperator {
require(msg.sender == operator, "Only operator can call this function.");
_;
}
event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp);
event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);
/**
@dev The constructor
@param _verifier the address of SNARK verifier for this contract
@param _denomination transfer amount for each deposit
@param _merkleTreeHeight the height of deposits' Merkle Tree
@param _operator operator address (see operator comment above)
*/
constructor(
IVerifier _verifier,
uint256 _denomination,
uint32 _merkleTreeHeight,
address _operator
) MerkleTreeWithHistory(_merkleTreeHeight) public {
require(_denomination > 0, "denomination should be greater than 0");
verifier = _verifier;
operator = _operator;
denomination = _denomination;
}
/**
@dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
@param _commitment the note commitment, which is PedersenHash(nullifier + secret)
*/
function deposit(bytes32 _commitment) external payable nonReentrant {
require(!commitments[_commitment], "The commitment has been submitted");
uint32 insertedIndex = _insert(_commitment);
commitments[_commitment] = true;
_processDeposit();
emit Deposit(_commitment, insertedIndex, block.timestamp);
}
/** @dev this function is defined in a child contract */
function _processDeposit() internal;
/**
@dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of:
- merkle root of all deposits in the contract
- hash of unique deposit nullifier to prevent double spends
- the recipient of funds
- optional fee that goes to the transaction sender (usually a relay)
*/
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant {
require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]), "Invalid withdraw proof");
nullifierHashes[_nullifierHash] = true;
_processWithdraw(_recipient, _relayer, _fee, _refund);
emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
}
/** @dev this function is defined in a child contract */
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal;
/** @dev whether a note is already spent */
function isSpent(bytes32 _nullifierHash) public view returns(bool) {
return nullifierHashes[_nullifierHash];
}
/** @dev whether an array of notes is already spent */
function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns(bool[] memory spent) {
spent = new bool[](_nullifierHashes.length);
for(uint i = 0; i < _nullifierHashes.length; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}
/**
@dev allow operator to update SNARK verification keys. This is needed to update keys after the final trusted setup ceremony is held.
After that operator rights are supposed to be transferred to zero address
*/
function updateVerifier(address _newVerifier) external onlyOperator {
verifier = IVerifier(_newVerifier);
}
/** @dev operator can change his address */
function changeOperator(address _newOperator) external onlyOperator {
operator = _newOperator;
}
}
contract ETHTornado is Tornado {
constructor(
IVerifier _verifier,
uint256 _denomination,
uint32 _merkleTreeHeight,
address _operator
) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
}
function _processDeposit() internal {
require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction");
}
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal {
// sanity checks
require(msg.value == 0, "Message value is supposed to be zero for ETH instance");
require(_refund == 0, "Refund value is supposed to be zero for ETH instance");
(bool success, ) = _recipient.call.value(denomination - _fee)("");
require(success, "payment to _recipient did not go thru");
if (_fee > 0) {
(success, ) = _relayer.call.value(_fee)("");
require(success, "payment to _relayer did not go thru");
}
}
}
pragma solidity 0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
interface IRewardableValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
function validatorList() external view returns (address[]);
function getValidatorRewardAddress(address _validator) external view returns (address);
function validatorCount() external view returns (uint256);
function getNextValidator(address _address) external view returns (address);
}
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
contract ERC677Storage {
bytes32 internal constant ERC677_TOKEN = 0xa8b0ade3e2b734f043ce298aca4cc8d19d74270223f34531d0988b7d00cba21d; // keccak256(abi.encodePacked("erc677token"))
}
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address _owner, address _spender)
public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
contract ERC677 is ERC20 {
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
function transferAndCall(address, uint256, bytes) external returns (bool);
function increaseAllowance(address spender, uint256 addedValue) public returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool);
}
contract IBurnableMintableERC677Token is ERC677 {
function mint(address _to, uint256 _amount) public returns (bool);
function burn(uint256 _value) public;
function claimTokens(address _token, address _to) public;
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
contract BaseFeeManager is EternalStorage, FeeTypes {
using SafeMath for uint256;
event HomeFeeUpdated(uint256 fee);
event ForeignFeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage
uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
if (!_recover) {
return _value.mul(fee).div(MAX_FEE);
}
return _value.mul(fee).div(MAX_FEE.sub(fee));
}
modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}
function setHomeFee(uint256 _fee) external validFee(_fee) {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
emit HomeFeeUpdated(_fee);
}
function getHomeFee() public view returns (uint256) {
return uintStorage[HOME_FEE_STORAGE_KEY];
}
function setForeignFee(uint256 _fee) external validFee(_fee) {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
emit ForeignFeeUpdated(_fee);
}
function getForeignFee() public view returns (uint256) {
return uintStorage[FOREIGN_FEE_STORAGE_KEY];
}
/* solcov ignore next */
function distributeFeeFromAffirmation(uint256 _fee) external;
/* solcov ignore next */
function distributeFeeFromSignatures(uint256 _fee) external;
/* solcov ignore next */
function getFeeManagerMode() external pure returns (bytes4);
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
}
contract ValidatorsFeeManager is BaseFeeManager, ValidatorStorage {
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_HOME = 0x2a11db67c480122765825a7e4bc5428e8b7b9eca0d4e62b91aac194f99edd0d7; // keccak256(abi.encodePacked("reward-transferring-from-home"))
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_FOREIGN = 0xb14796d751eb4f2570065a479f9e526eabeb2077c564c8a1c5ea559883ea2fab; // keccak256(abi.encodePacked("reward-transferring-from-foreign"))
function distributeFeeFromAffirmation(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_FOREIGN);
}
function distributeFeeFromSignatures(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_HOME);
}
function rewardableValidatorContract() internal view returns (IRewardableValidators) {
return IRewardableValidators(addressStorage[VALIDATOR_CONTRACT]);
}
function distributeFeeProportionally(uint256 _fee, bytes32 _direction) internal {
IRewardableValidators validators = rewardableValidatorContract();
// solhint-disable-next-line var-name-mixedcase
address F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 numOfValidators = validators.validatorCount();
uint256 feePerValidator = _fee.div(numOfValidators);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfValidators));
if (diff > 0) {
randomValidatorIndex = random(numOfValidators);
}
address nextValidator = validators.getNextValidator(F_ADDR);
require((nextValidator != F_ADDR) && (nextValidator != address(0)));
uint256 i = 0;
while (nextValidator != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
address rewardAddress = validators.getValidatorRewardAddress(nextValidator);
onFeeDistribution(rewardAddress, feeToDistribute, _direction);
nextValidator = validators.getNextValidator(nextValidator);
require(nextValidator != address(0));
i = i + 1;
}
}
function onFeeDistribution(address _rewardAddress, uint256 _fee, bytes32 _direction) internal {
if (_direction == REWARD_FOR_TRANSFERRING_FROM_FOREIGN) {
onAffirmationFeeDistribution(_rewardAddress, _fee);
} else {
onSignatureFeeDistribution(_rewardAddress, _fee);
}
}
/* solcov ignore next */
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal;
/* solcov ignore next */
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}
contract FeeManagerNativeToErc is ValidatorsFeeManager, ERC677Storage {
function getFeeManagerMode() external pure returns (bytes4) {
return 0xf2aed8f7; // bytes4(keccak256(abi.encodePacked("manages-one-direction")))
}
function erc677token() public view returns (IBurnableMintableERC677Token) {
return IBurnableMintableERC677Token(addressStorage[ERC677_TOKEN]);
}
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal {
if (!_rewardAddress.send(_fee)) {
(new Sacrifice).value(_fee)(_rewardAddress);
}
}
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal {
erc677token().mint(_rewardAddress, _fee);
}
}
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
interface IRewardableValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
function validatorList() external view returns (address[]);
function getValidatorRewardAddress(address _validator) external view returns (address);
function validatorCount() external view returns (uint256);
function getNextValidator(address _address) external view returns (address);
}
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
contract BaseFeeManager is EternalStorage, FeeTypes {
using SafeMath for uint256;
event HomeFeeUpdated(uint256 fee);
event ForeignFeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage
uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
if (!_recover) {
return _value.mul(fee).div(MAX_FEE);
}
return _value.mul(fee).div(MAX_FEE.sub(fee));
}
modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}
function setHomeFee(uint256 _fee) external validFee(_fee) {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
emit HomeFeeUpdated(_fee);
}
function getHomeFee() public view returns (uint256) {
return uintStorage[HOME_FEE_STORAGE_KEY];
}
function setForeignFee(uint256 _fee) external validFee(_fee) {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
emit ForeignFeeUpdated(_fee);
}
function getForeignFee() public view returns (uint256) {
return uintStorage[FOREIGN_FEE_STORAGE_KEY];
}
/* solcov ignore next */
function distributeFeeFromAffirmation(uint256 _fee) external;
/* solcov ignore next */
function distributeFeeFromSignatures(uint256 _fee) external;
/* solcov ignore next */
function getFeeManagerMode() external pure returns (bytes4);
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
}
contract ValidatorsFeeManager is BaseFeeManager, ValidatorStorage {
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_HOME = 0x2a11db67c480122765825a7e4bc5428e8b7b9eca0d4e62b91aac194f99edd0d7; // keccak256(abi.encodePacked("reward-transferring-from-home"))
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_FOREIGN = 0xb14796d751eb4f2570065a479f9e526eabeb2077c564c8a1c5ea559883ea2fab; // keccak256(abi.encodePacked("reward-transferring-from-foreign"))
function distributeFeeFromAffirmation(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_FOREIGN);
}
function distributeFeeFromSignatures(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_HOME);
}
function rewardableValidatorContract() internal view returns (IRewardableValidators) {
return IRewardableValidators(addressStorage[VALIDATOR_CONTRACT]);
}
function distributeFeeProportionally(uint256 _fee, bytes32 _direction) internal {
IRewardableValidators validators = rewardableValidatorContract();
// solhint-disable-next-line var-name-mixedcase
address F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 numOfValidators = validators.validatorCount();
uint256 feePerValidator = _fee.div(numOfValidators);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfValidators));
if (diff > 0) {
randomValidatorIndex = random(numOfValidators);
}
address nextValidator = validators.getNextValidator(F_ADDR);
require((nextValidator != F_ADDR) && (nextValidator != address(0)));
uint256 i = 0;
while (nextValidator != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
address rewardAddress = validators.getValidatorRewardAddress(nextValidator);
onFeeDistribution(rewardAddress, feeToDistribute, _direction);
nextValidator = validators.getNextValidator(nextValidator);
require(nextValidator != address(0));
i = i + 1;
}
}
function onFeeDistribution(address _rewardAddress, uint256 _fee, bytes32 _direction) internal {
if (_direction == REWARD_FOR_TRANSFERRING_FROM_FOREIGN) {
onAffirmationFeeDistribution(_rewardAddress, _fee);
} else {
onSignatureFeeDistribution(_rewardAddress, _fee);
}
}
/* solcov ignore next */
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal;
/* solcov ignore next */
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}
contract FeeManagerNativeToErcBothDirections is ValidatorsFeeManager {
function getFeeManagerMode() external pure returns (bytes4) {
return 0xd7de965f; // bytes4(keccak256(abi.encodePacked("manages-both-directions")))
}
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function _sendReward(address _rewardAddress, uint256 _fee) internal {
if (!_rewardAddress.send(_fee)) {
(new Sacrifice).value(_fee)(_rewardAddress);
}
}
}
pragma solidity ^0.5.0;
interface CostOracle {
function cost() external view returns(uint);
function costCalculated(uint _fixedFee) external view returns(uint);
function costRelative(uint _amount) external view returns(uint);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
interface ValueOracle {
function currentValue() external view returns (uint256);
}
// StablePriceOracle sets a price in USD, based on an oracle.
contract FixedCostOracle is Ownable, CostOracle {
using SafeMath for *;
// Oracle address
ValueOracle usdOracle;
// fixed cost in atto dollars
uint public fixedCost;
event OracleChanged(address oracle);
event FixedCostChanged(uint cost);
constructor(ValueOracle _usdOracle, uint _fixedCost) public {
setOracle(_usdOracle);
setFixedCost(_fixedCost);
}
/**
* @dev Sets the price oracle address
* @param _usdOracle The address of the price oracle to use.
*/
function setOracle(ValueOracle _usdOracle) public onlyOwner {
usdOracle = _usdOracle;
emit OracleChanged(address(_usdOracle));
}
function setFixedCost(uint _fixedCost) public onlyOwner {
fixedCost = _fixedCost;
emit FixedCostChanged(_fixedCost);
}
function cost() view external returns(uint) {
// Price of one ether in attodollars
uint ethPrice = usdOracle.currentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return fixedCost.mul(1e18).div(ethPrice);
}
function costCalculated(uint _fixedFee) view external returns(uint) {
// Price of one ether in attodollars
uint ethPrice = usdOracle.currentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return _fixedFee.mul(1e18).div(ethPrice);
}
function costRelative(uint256 _amount) view external returns(uint) {
// Price of one ether in attodollars
uint ethPrice = usdOracle.currentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
uint ethCost = fixedCost.mul(1e18).div(ethPrice);
return ethCost.mul(1e18).div(_amount);
}
}
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
interface IRewardableValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
function validatorList() external view returns (address[]);
function getValidatorRewardAddress(address _validator) external view returns (address);
function validatorCount() external view returns (uint256);
function getNextValidator(address _address) external view returns (address);
}
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
contract BaseFeeManager is EternalStorage, FeeTypes {
using SafeMath for uint256;
event HomeFeeUpdated(uint256 fee);
event ForeignFeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage
uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
if (!_recover) {
return _value.mul(fee).div(MAX_FEE);
}
return _value.mul(fee).div(MAX_FEE.sub(fee));
}
modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}
function setHomeFee(uint256 _fee) external {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
emit HomeFeeUpdated(_fee);
}
function getHomeFee() public view returns (uint256) {
return uintStorage[HOME_FEE_STORAGE_KEY];
}
function setForeignFee(uint256 _fee) external {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
emit ForeignFeeUpdated(_fee);
}
function getForeignFee() public view returns (uint256) {
return uintStorage[FOREIGN_FEE_STORAGE_KEY];
}
/* solcov ignore next */
function distributeFeeFromAffirmation(uint256 _fee) external;
/* solcov ignore next */
function distributeFeeFromSignatures(uint256 _fee) external;
/* solcov ignore next */
function getFeeManagerMode() external pure returns (bytes4);
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
}
contract ValidatorsFeeManager is BaseFeeManager, ValidatorStorage {
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_HOME = 0x2a11db67c480122765825a7e4bc5428e8b7b9eca0d4e62b91aac194f99edd0d7; // keccak256(abi.encodePacked("reward-transferring-from-home"))
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_FOREIGN = 0xb14796d751eb4f2570065a479f9e526eabeb2077c564c8a1c5ea559883ea2fab; // keccak256(abi.encodePacked("reward-transferring-from-foreign"))
function distributeFeeFromAffirmation(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_FOREIGN);
}
function distributeFeeFromSignatures(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_HOME);
}
function rewardableValidatorContract() internal view returns (IRewardableValidators) {
return IRewardableValidators(addressStorage[VALIDATOR_CONTRACT]);
}
function distributeFeeProportionally(uint256 _fee, bytes32 _direction) internal {
IRewardableValidators validators = rewardableValidatorContract();
// solhint-disable-next-line var-name-mixedcase
address F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 numOfValidators = validators.validatorCount();
uint256 feePerValidator = _fee.div(numOfValidators);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfValidators));
if (diff > 0) {
randomValidatorIndex = random(numOfValidators);
}
address nextValidator = validators.getNextValidator(F_ADDR);
require((nextValidator != F_ADDR) && (nextValidator != address(0)));
uint256 i = 0;
while (nextValidator != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
address rewardAddress = validators.getValidatorRewardAddress(nextValidator);
onFeeDistribution(rewardAddress, feeToDistribute, _direction);
nextValidator = validators.getNextValidator(nextValidator);
require(nextValidator != address(0));
i = i + 1;
}
}
function onFeeDistribution(address _rewardAddress, uint256 _fee, bytes32 _direction) internal {
if (_direction == REWARD_FOR_TRANSFERRING_FROM_FOREIGN) {
onAffirmationFeeDistribution(_rewardAddress, _fee);
} else {
onSignatureFeeDistribution(_rewardAddress, _fee);
}
}
/* solcov ignore next */
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal;
/* solcov ignore next */
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}
contract FeeManagerNativeToErcBothDirections is ValidatorsFeeManager {
function getFeeManagerMode() external pure returns (bytes4) {
return 0xd7de965f; // bytes4(keccak256(abi.encodePacked("manages-both-directions")))
}
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function _sendReward(address _rewardAddress, uint256 _fee) internal {
if (!_rewardAddress.send(_fee)) {
(new Sacrifice).value(_fee)(_rewardAddress);
}
}
}
contract FixedFeeManagerNativeToErcBothDirections is FeeManagerNativeToErcBothDirections {
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 _fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
//require(_value >= _fee);
return _fee;
}
}
// File: contracts/IBridgeValidators.sol
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint256);
function owner() public view returns(address);
}
// File: contracts/IOwnedUpgradeabilityProxy.sol
pragma solidity 0.4.24;
interface IOwnedUpgradeabilityProxy {
function proxyOwner() public view returns (address);
}
// File: contracts/upgradeable_contracts/OwnedUpgradeability.sol
pragma solidity 0.4.24;
contract OwnedUpgradeability {
function upgradeabilityAdmin() public view returns (address) {
return IOwnedUpgradeabilityProxy(this).proxyOwner();
}
// Avoid using onlyProxyOwner name to prevent issues with implementation from proxy contract
modifier onlyIfOwnerOfProxy() {
require(msg.sender == upgradeabilityAdmin());
_;
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/libraries/SafeMath.sol
pragma solidity 0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/upgradeable_contracts/Validatable.sol
pragma solidity 0.4.24;
contract Validatable is EternalStorage {
function validatorContract() public view returns(IBridgeValidators) {
return IBridgeValidators(addressStorage[keccak256(abi.encodePacked("validatorContract"))]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
_;
}
function requiredSignatures() public view returns(uint256) {
return validatorContract().requiredSignatures();
}
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
_;
}
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("owner"))];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[keccak256(abi.encodePacked("owner"))] = newOwner;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: contracts/upgradeable_contracts/BasicBridge.sol
pragma solidity 0.4.24;
contract BasicBridge is EternalStorage, Validatable, Ownable, OwnedUpgradeability {
using SafeMath for uint256;
event GasPriceChanged(uint256 gasPrice);
event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
event DailyLimitChanged(uint256 newLimit);
event ExecutionDailyLimitChanged(uint256 newLimit);
function getBridgeInterfacesVersion() public pure returns(uint64 major, uint64 minor, uint64 patch) {
return (2, 2, 0);
}
function setGasPrice(uint256 _gasPrice) public onlyOwner {
require(_gasPrice > 0);
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
emit GasPriceChanged(_gasPrice);
}
function gasPrice() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("gasPrice"))];
}
function setRequiredBlockConfirmations(uint256 _blockConfirmations) public onlyOwner {
require(_blockConfirmations > 0);
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _blockConfirmations;
emit RequiredBlockConfirmationChanged(_blockConfirmations);
}
function requiredBlockConfirmations() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))];
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = _value;
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
}
function setTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = _value;
}
function totalExecutedPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
}
function minPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("minPerTx"))];
}
function maxPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("maxPerTx"))];
}
function executionMaxPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))];
}
function setInitialize(bool _status) internal {
boolStorage[keccak256(abi.encodePacked("isInitialized"))] = _status;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("isInitialized"))];
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setDailyLimit(uint256 _dailyLimit) public onlyOwner {
uintStorage[keccak256(abi.encodePacked("dailyLimit"))] = _dailyLimit;
emit DailyLimitChanged(_dailyLimit);
}
function dailyLimit() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("dailyLimit"))];
}
function setExecutionDailyLimit(uint256 _dailyLimit) public onlyOwner {
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _dailyLimit;
emit ExecutionDailyLimitChanged(_dailyLimit);
}
function executionDailyLimit() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))];
}
function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < executionDailyLimit());
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _maxPerTx;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < dailyLimit());
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx < dailyLimit() && _minPerTx < maxPerTx());
uintStorage[keccak256(abi.encodePacked("minPerTx"))] = _minPerTx;
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function withinExecutionLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
}
function claimTokens(address _token, address _to) public onlyIfOwnerOfProxy {
require(_to != address(0));
if (_token == address(0)) {
_to.transfer(address(this).balance);
return;
}
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
require(token.transfer(_to, balance));
}
function isContract(address _addr) internal view returns (bool)
{
uint length;
assembly { length := extcodesize(_addr) }
return length > 0;
}
}
// File: contracts/libraries/Message.sol
pragma solidity 0.4.24;
library Message {
// function uintToString(uint256 inputValue) internal pure returns (string) {
// // figure out the length of the resulting string
// uint256 length = 0;
// uint256 currentValue = inputValue;
// do {
// length++;
// currentValue /= 10;
// } while (currentValue != 0);
// // allocate enough memory
// bytes memory result = new bytes(length);
// // construct the string backwards
// uint256 i = length - 1;
// currentValue = inputValue;
// do {
// result[i--] = byte(48 + currentValue % 10);
// currentValue /= 10;
// } while (currentValue != 0);
// return string(result);
// }
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// bytes 1 to 32 are 0 because message length is stored as little endian.
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 zero bytes followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
internal
pure
returns(address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := and(mload(add(message, 20)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function isMessageValid(bytes _msg) internal pure returns(bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() internal pure returns(uint256) {
return 104;
}
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
// message is always 84 length
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(isMessageValid(_message));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
}
}
// File: contracts/upgradeable_contracts/BasicForeignBridge.sol
pragma solidity 0.4.24;
contract BasicForeignBridge is EternalStorage, Validatable {
using SafeMath for uint256;
/// triggered when relay of deposit from HomeBridge is complete
event RelayedMessage(address recipient, uint value, bytes32 transactionHash);
function executeSignatures(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) external {
Message.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract());
address recipient;
uint256 amount;
bytes32 txHash;
address contractAddress;
(recipient, amount, txHash, contractAddress) = Message.parseMessage(message);
if (messageWithinLimits(amount)) {
require(contractAddress == address(this));
require(!relayedMessages(txHash));
setRelayedMessages(txHash, true);
require(onExecuteMessage(recipient, amount, txHash));
emit RelayedMessage(recipient, amount, txHash);
} else {
onFailedMessage(recipient, amount, txHash);
}
}
function onExecuteMessage(address, uint256, bytes32) internal returns(bool);
function setRelayedMessages(bytes32 _txHash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))] = _status;
}
function relayedMessages(bytes32 _txHash) public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))];
}
function messageWithinLimits(uint256) internal view returns(bool);
function onFailedMessage(address, uint256, bytes32) internal;
}
// File: contracts/upgradeable_contracts/erc20_to_erc20/BasicForeignBridgeErcToErc.sol
pragma solidity 0.4.24;
contract BasicForeignBridgeErcToErc is BasicBridge, BasicForeignBridge {
function _initialize(
address _validatorContract,
address _erc20token,
uint256 _requiredBlockConfirmations,
uint256 _gasPrice,
uint256 _maxPerTx,
uint256 _homeDailyLimit,
uint256 _homeMaxPerTx,
address _owner
) internal {
require(!isInitialized());
require(_validatorContract != address(0) && isContract(_validatorContract));
require(_requiredBlockConfirmations != 0);
require(_gasPrice > 0);
require(_homeMaxPerTx < _homeDailyLimit);
require(_owner != address(0));
addressStorage[keccak256(abi.encodePacked("validatorContract"))] = _validatorContract;
setErc20token(_erc20token);
uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))] = block.number;
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _requiredBlockConfirmations;
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _homeDailyLimit;
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _homeMaxPerTx;
setOwner(_owner);
setInitialize(true);
}
function getBridgeMode() public pure returns(bytes4 _data) {
return bytes4(keccak256(abi.encodePacked("erc-to-erc-core")));
}
function claimTokens(address _token, address _to) public onlyIfOwnerOfProxy {
require(_token != address(erc20token()));
super.claimTokens(_token, _to);
}
function onExecuteMessage(address _recipient, uint256 _amount, bytes32 _txHash) internal returns(bool){
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_amount));
return erc20token().transfer(_recipient, _amount);
}
function messageWithinLimits(uint256 _amount) internal view returns(bool) {
return withinExecutionLimit(_amount);
}
function onFailedMessage(address, uint256, bytes32) internal {
revert();
}
function erc20token() public view returns(ERC20Basic);
function setErc20token(address _token) internal;
}
// File: contracts/upgradeable_contracts/erc20_to_erc20/ForeignBridgeErcToErc.sol
pragma solidity 0.4.24;
contract ForeignBridgeErcToErc is BasicForeignBridgeErcToErc {
function initialize(
address _validatorContract,
address _erc20token,
uint256 _requiredBlockConfirmations,
uint256 _gasPrice,
uint256 _maxPerTx,
uint256 _homeDailyLimit,
uint256 _homeMaxPerTx,
address _owner
) public returns(bool) {
_initialize(
_validatorContract,
_erc20token,
_requiredBlockConfirmations,
_gasPrice,
_maxPerTx,
_homeDailyLimit,
_homeMaxPerTx,
_owner
);
return isInitialized();
}
function erc20token() public view returns(ERC20Basic) {
return ERC20Basic(addressStorage[keccak256(abi.encodePacked("erc20token"))]);
}
function setErc20token(address _token) internal {
require(_token != address(0) && isContract(_token));
addressStorage[keccak256(abi.encodePacked("erc20token"))] = _token;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address _owner, address _spender)
public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// File: contracts/interfaces/ERC677.sol
pragma solidity 0.4.24;
contract ERC677 is ERC20 {
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
function transferAndCall(address, uint256, bytes) external returns (bool);
function increaseAllowance(address spender, uint256 addedValue) public returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool);
}
// File: contracts/interfaces/IBurnableMintableERC677Token.sol
pragma solidity 0.4.24;
contract IBurnableMintableERC677Token is ERC677 {
function mint(address _to, uint256 _amount) public returns (bool);
function burn(uint256 _value) public;
function claimTokens(address _token, address _to) public;
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/interfaces/IBridgeValidators.sol
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
}
// File: contracts/upgradeable_contracts/ValidatorStorage.sol
pragma solidity 0.4.24;
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
// File: contracts/upgradeable_contracts/Validatable.sol
pragma solidity 0.4.24;
contract Validatable is EternalStorage, ValidatorStorage {
function validatorContract() public view returns (IBridgeValidators) {
return IBridgeValidators(addressStorage[VALIDATOR_CONTRACT]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
/* solcov ignore next */
_;
}
function requiredSignatures() public view returns (uint256) {
return validatorContract().requiredSignatures();
}
}
// File: contracts/libraries/Message.sol
pragma solidity 0.4.24;
library Message {
// function uintToString(uint256 inputValue) internal pure returns (string) {
// // figure out the length of the resulting string
// uint256 length = 0;
// uint256 currentValue = inputValue;
// do {
// length++;
// currentValue /= 10;
// } while (currentValue != 0);
// // allocate enough memory
// bytes memory result = new bytes(length);
// // construct the string backwards
// uint256 i = length - 1;
// currentValue = inputValue;
// do {
// result[i--] = byte(48 + currentValue % 10);
// currentValue /= 10;
// } while (currentValue != 0);
// return string(result);
// }
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 bytes (most of them zeros) followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
internal
pure
returns (address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := mload(add(message, 20))
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function isMessageValid(bytes _msg) internal pure returns (bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() internal pure returns (uint256) {
return 104;
}
function recoverAddressFromSignedMessage(bytes signature, bytes message, bool isAMBMessage)
internal
pure
returns (address)
{
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
}
function hashMessage(bytes message, bool isAMBMessage) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
if (isAMBMessage) {
return keccak256(abi.encodePacked(prefix, uintToString(message.length), message));
} else {
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
// It is not necessary to check that arrays have the same length since it will be handled
// during attempt to access to the corresponding elements in the loop and the call will be reverted.
// It will save gas for the rational validators actions and still be safe enough from security point of view
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function hasEnoughValidSignatures(
bytes _message,
bytes _signatures,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
uint256 amount;
assembly {
amount := and(mload(add(_signatures, 1)), 0xff)
}
require(amount >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
uint8 v;
bytes32 r;
bytes32 s;
uint256 posr = 33 + amount + 32 * i;
uint256 poss = posr + 32 * amount;
assembly {
v := mload(add(_signatures, add(2, i)))
r := mload(add(_signatures, posr))
s := mload(add(_signatures, poss))
}
address recoveredAddress = ecrecover(hash, v, r, s);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function uintToString(uint256 i) internal pure returns (string) {
if (i == 0) return "0";
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
bstr[k--] = bytes1(48 + (i % 10));
i /= 10;
}
return string(bstr);
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
// File: contracts/interfaces/IUpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
/* solcov ignore next */
_;
}
/**
* @dev Throws if called by any account other than contract itself or owner.
*/
modifier onlyRelevantSender() {
// proxy owner if used through proxy, address(0) otherwise
require(
!address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
/* solcov ignore next */
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}
// File: contracts/upgradeable_contracts/BasicTokenBridge.sol
pragma solidity 0.4.24;
contract BasicTokenBridge is EternalStorage, Ownable {
using SafeMath for uint256;
event DailyLimitChanged(uint256 newLimit);
event ExecutionDailyLimitChanged(uint256 newLimit);
bytes32 internal constant MIN_PER_TX = 0xbbb088c505d18e049d114c7c91f11724e69c55ad6c5397e2b929e68b41fa05d1; // keccak256(abi.encodePacked("minPerTx"))
bytes32 internal constant MAX_PER_TX = 0x0f8803acad17c63ee38bf2de71e1888bc7a079a6f73658e274b08018bea4e29c; // keccak256(abi.encodePacked("maxPerTx"))
bytes32 internal constant DAILY_LIMIT = 0x4a6a899679f26b73530d8cf1001e83b6f7702e04b6fdb98f3c62dc7e47e041a5; // keccak256(abi.encodePacked("dailyLimit"))
bytes32 internal constant EXECUTION_MAX_PER_TX = 0xc0ed44c192c86d1cc1ba51340b032c2766b4a2b0041031de13c46dd7104888d5; // keccak256(abi.encodePacked("executionMaxPerTx"))
bytes32 internal constant EXECUTION_DAILY_LIMIT = 0x21dbcab260e413c20dc13c28b7db95e2b423d1135f42bb8b7d5214a92270d237; // keccak256(abi.encodePacked("executionDailyLimit"))
bytes32 internal constant DECIMAL_SHIFT = 0x1e8ecaafaddea96ed9ac6d2642dcdfe1bebe58a930b1085842d8fc122b371ee5; // keccak256(abi.encodePacked("decimalShift"))
function totalSpentPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
}
function totalExecutedPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
}
function dailyLimit() public view returns (uint256) {
return uintStorage[DAILY_LIMIT];
}
function executionDailyLimit() public view returns (uint256) {
return uintStorage[EXECUTION_DAILY_LIMIT];
}
function maxPerTx() public view returns (uint256) {
return uintStorage[MAX_PER_TX];
}
function executionMaxPerTx() public view returns (uint256) {
return uintStorage[EXECUTION_MAX_PER_TX];
}
function minPerTx() public view returns (uint256) {
return uintStorage[MIN_PER_TX];
}
function decimalShift() public view returns (uint256) {
return uintStorage[DECIMAL_SHIFT];
}
function withinLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function withinExecutionLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
}
function getCurrentDay() public view returns (uint256) {
// solhint-disable-next-line not-rely-on-time
return now / 1 days;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = _value;
}
function setTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = _value;
}
function setDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > maxPerTx() || _dailyLimit == 0);
uintStorage[DAILY_LIMIT] = _dailyLimit;
emit DailyLimitChanged(_dailyLimit);
}
function setExecutionDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > executionMaxPerTx() || _dailyLimit == 0);
uintStorage[EXECUTION_DAILY_LIMIT] = _dailyLimit;
emit ExecutionDailyLimitChanged(_dailyLimit);
}
function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < executionDailyLimit());
uintStorage[EXECUTION_MAX_PER_TX] = _maxPerTx;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx == 0 || (_maxPerTx > minPerTx() && _maxPerTx < dailyLimit()));
uintStorage[MAX_PER_TX] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx > 0 && _minPerTx < dailyLimit() && _minPerTx < maxPerTx());
uintStorage[MIN_PER_TX] = _minPerTx;
}
}
// File: contracts/upgradeable_contracts/MessageRelay.sol
pragma solidity 0.4.24;
contract MessageRelay is EternalStorage {
function relayedMessages(bytes32 _txHash) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))];
}
function setRelayedMessages(bytes32 _txHash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("relayedMessages", _txHash))] = _status;
}
}
// File: contracts/upgradeable_contracts/Upgradeable.sol
pragma solidity 0.4.24;
contract Upgradeable {
// Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract
modifier onlyIfUpgradeabilityOwner() {
require(msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner());
/* solcov ignore next */
_;
}
}
// File: contracts/upgradeable_contracts/Initializable.sol
pragma solidity 0.4.24;
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}
// File: contracts/upgradeable_contracts/InitializableBridge.sol
pragma solidity 0.4.24;
contract InitializableBridge is Initializable {
bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
function deployedAtBlock() external view returns (uint256) {
return uintStorage[DEPLOYED_AT_BLOCK];
}
}
// File: openzeppelin-solidity/contracts/AddressUtils.sol
pragma solidity ^0.4.24;
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param _addr address to check
* @return whether the target address is a contract
*/
function isContract(address _addr) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(_addr) }
return size > 0;
}
}
// File: contracts/upgradeable_contracts/Sacrifice.sol
pragma solidity 0.4.24;
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
// File: contracts/upgradeable_contracts/Claimable.sol
pragma solidity 0.4.24;
contract Claimable {
bytes4 internal constant TRANSFER = 0xa9059cbb; // transfer(address,uint256)
modifier validAddress(address _to) {
require(_to != address(0));
/* solcov ignore next */
_;
}
function claimValues(address _token, address _to) internal {
if (_token == address(0)) {
claimNativeCoins(_to);
} else {
claimErc20Tokens(_token, _to);
}
}
function claimNativeCoins(address _to) internal {
uint256 value = address(this).balance;
if (!_to.send(value)) {
(new Sacrifice).value(value)(_to);
}
}
function claimErc20Tokens(address _token, address _to) internal {
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
safeTransfer(_token, _to, balance);
}
function safeTransfer(address _token, address _to, uint256 _value) internal {
bytes memory returnData;
bool returnDataResult;
bytes memory callData = abi.encodeWithSelector(TRANSFER, _to, _value);
assembly {
let result := call(gas, _token, 0x0, add(callData, 0x20), mload(callData), 0, 32)
returnData := mload(0)
returnDataResult := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
// Return data is optional
if (returnData.length > 0) {
require(returnDataResult);
}
}
}
// File: contracts/upgradeable_contracts/VersionableBridge.sol
pragma solidity 0.4.24;
contract VersionableBridge {
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (2, 5, 0);
}
/* solcov ignore next */
function getBridgeMode() external pure returns (bytes4);
}
// File: contracts/upgradeable_contracts/BasicBridge.sol
pragma solidity 0.4.24;
contract BasicBridge is InitializableBridge, Validatable, Ownable, Upgradeable, Claimable, VersionableBridge {
event GasPriceChanged(uint256 gasPrice);
event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
bytes32 internal constant GAS_PRICE = 0x55b3774520b5993024893d303890baa4e84b1244a43c60034d1ced2d3cf2b04b; // keccak256(abi.encodePacked("gasPrice"))
bytes32 internal constant REQUIRED_BLOCK_CONFIRMATIONS = 0x916daedf6915000ff68ced2f0b6773fe6f2582237f92c3c95bb4d79407230071; // keccak256(abi.encodePacked("requiredBlockConfirmations"))
function setGasPrice(uint256 _gasPrice) external onlyOwner {
require(_gasPrice > 0);
uintStorage[GAS_PRICE] = _gasPrice;
emit GasPriceChanged(_gasPrice);
}
function gasPrice() external view returns (uint256) {
return uintStorage[GAS_PRICE];
}
function setRequiredBlockConfirmations(uint256 _blockConfirmations) external onlyOwner {
require(_blockConfirmations > 0);
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _blockConfirmations;
emit RequiredBlockConfirmationChanged(_blockConfirmations);
}
function requiredBlockConfirmations() external view returns (uint256) {
return uintStorage[REQUIRED_BLOCK_CONFIRMATIONS];
}
function claimTokens(address _token, address _to) public onlyIfUpgradeabilityOwner validAddress(_to) {
claimValues(_token, _to);
}
}
// File: contracts/upgradeable_contracts/BasicForeignBridge.sol
pragma solidity 0.4.24;
contract BasicForeignBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge, MessageRelay {
/// triggered when relay of deposit from HomeBridge is complete
event RelayedMessage(address recipient, uint256 value, bytes32 transactionHash);
event UserRequestForAffirmation(address recipient, uint256 value);
function executeSignatures(uint8[] vs, bytes32[] rs, bytes32[] ss, bytes message) external {
Message.hasEnoughValidSignatures(message, vs, rs, ss, validatorContract(), false);
address recipient;
uint256 amount;
bytes32 txHash;
address contractAddress;
(recipient, amount, txHash, contractAddress) = Message.parseMessage(message);
if (withinExecutionLimit(amount)) {
require(contractAddress == address(this));
require(!relayedMessages(txHash));
setRelayedMessages(txHash, true);
require(onExecuteMessage(recipient, amount, txHash));
emit RelayedMessage(recipient, amount, txHash);
} else {
onFailedMessage(recipient, amount, txHash);
}
}
/* solcov ignore next */
function onExecuteMessage(address, uint256, bytes32) internal returns (bool);
/* solcov ignore next */
function onFailedMessage(address, uint256, bytes32) internal;
}
// File: contracts/interfaces/ERC677Receiver.sol
pragma solidity 0.4.24;
contract ERC677Receiver {
function onTokenTransfer(address _from, uint256 _value, bytes _data) external returns (bool);
}
// File: contracts/upgradeable_contracts/ERC677Storage.sol
pragma solidity 0.4.24;
contract ERC677Storage {
bytes32 internal constant ERC677_TOKEN = 0xa8b0ade3e2b734f043ce298aca4cc8d19d74270223f34531d0988b7d00cba21d; // keccak256(abi.encodePacked("erc677token"))
}
// File: contracts/libraries/Bytes.sol
pragma solidity 0.4.24;
/**
* @title Bytes
* @dev Helper methods to transform bytes to other solidity types.
*/
library Bytes {
/**
* @dev Truncate bytes array if its size is more than 32 bytes
* and pads the array with zeros from the right side if its size is less than 32.
* @param _bytes to be converted to bytes32 type
* @return bytes32 type of the firsts 32 bytes array in parameter.
*/
function bytesToBytes32(bytes _bytes) internal pure returns (bytes32 result) {
assembly {
result := mload(add(_bytes, 32))
}
}
/**
* @dev Truncate bytes array if its size is more than 20 bytes
* and pads the array with zeros from the right side if its size is less than 20.
* @param _bytes to be converted to address type
* @return address included in the firsts 20 bytes of the bytes array in parameter.
*/
function bytesToAddress(bytes _bytes) internal pure returns (address addr) {
assembly {
addr := mload(add(_bytes, 20))
}
}
}
// File: contracts/upgradeable_contracts/BaseERC677Bridge.sol
pragma solidity 0.4.24;
contract BaseERC677Bridge is BasicTokenBridge, ERC677Receiver, ERC677Storage {
function erc677token() public view returns (ERC677) {
return ERC677(addressStorage[ERC677_TOKEN]);
}
function setErc677token(address _token) internal {
require(AddressUtils.isContract(_token));
addressStorage[ERC677_TOKEN] = _token;
}
function onTokenTransfer(address _from, uint256 _value, bytes _data) external returns (bool) {
ERC677 token = erc677token();
require(msg.sender == address(token));
require(withinLimit(_value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
bridgeSpecificActionsOnTokenTransfer(token, _from, _value, _data);
return true;
}
function chooseReceiver(address _from, bytes _data) internal view returns (address recipient) {
recipient = _from;
if (_data.length > 0) {
require(_data.length == 20);
recipient = Bytes.bytesToAddress(_data);
require(recipient != address(0));
require(recipient != bridgeContractOnOtherSide());
}
}
/* solcov ignore next */
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal;
/* solcov ignore next */
function bridgeContractOnOtherSide() internal view returns (address);
}
// File: contracts/upgradeable_contracts/OtherSideBridgeStorage.sol
pragma solidity 0.4.24;
contract OtherSideBridgeStorage is EternalStorage {
bytes32 internal constant BRIDGE_CONTRACT = 0x71483949fe7a14d16644d63320f24d10cf1d60abecc30cc677a340e82b699dd2; // keccak256(abi.encodePacked("bridgeOnOtherSide"))
function _setBridgeContractOnOtherSide(address _bridgeContract) internal {
addressStorage[BRIDGE_CONTRACT] = _bridgeContract;
}
function bridgeContractOnOtherSide() internal view returns (address) {
return addressStorage[BRIDGE_CONTRACT];
}
}
// File: contracts/upgradeable_contracts/ERC677Bridge.sol
pragma solidity 0.4.24;
contract ERC677Bridge is BaseERC677Bridge, OtherSideBridgeStorage {
function bridgeSpecificActionsOnTokenTransfer(
ERC677, /*_token*/
address _from,
uint256 _value,
bytes _data
) internal {
fireEventOnTokenTransfer(chooseReceiver(_from, _data), _value);
}
/* solcov ignore next */
function fireEventOnTokenTransfer(address _from, uint256 _value) internal;
}
// File: contracts/upgradeable_contracts/ERC677BridgeForBurnableMintableToken.sol
pragma solidity 0.4.24;
contract ERC677BridgeForBurnableMintableToken is ERC677Bridge {
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal {
IBurnableMintableERC677Token(_token).burn(_value);
fireEventOnTokenTransfer(chooseReceiver(_from, _data), _value);
}
}
// File: contracts/upgradeable_contracts/FeeTypes.sol
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
// File: contracts/upgradeable_contracts/RewardableBridge.sol
pragma solidity 0.4.24;
contract RewardableBridge is Ownable, FeeTypes {
event FeeDistributedFromAffirmation(uint256 feeAmount, bytes32 indexed transactionHash);
event FeeDistributedFromSignatures(uint256 feeAmount, bytes32 indexed transactionHash);
bytes32 internal constant FEE_MANAGER_CONTRACT = 0x779a349c5bee7817f04c960f525ee3e2f2516078c38c68a3149787976ee837e5; // keccak256(abi.encodePacked("feeManagerContract"))
bytes4 internal constant GET_HOME_FEE = 0x94da17cd; // getHomeFee()
bytes4 internal constant GET_FOREIGN_FEE = 0xffd66196; // getForeignFee()
bytes4 internal constant GET_FEE_MANAGER_MODE = 0xf2ba9561; // getFeeManagerMode()
bytes4 internal constant SET_HOME_FEE = 0x34a9e148; // setHomeFee(uint256)
bytes4 internal constant SET_FOREIGN_FEE = 0x286c4066; // setForeignFee(uint256)
bytes4 internal constant CALCULATE_FEE = 0x9862f26f; // calculateFee(uint256,bool,bytes32)
bytes4 internal constant DISTRIBUTE_FEE_FROM_SIGNATURES = 0x59d78464; // distributeFeeFromSignatures(uint256)
bytes4 internal constant DISTRIBUTE_FEE_FROM_AFFIRMATION = 0x054d46ec; // distributeFeeFromAffirmation(uint256)
function _getFee(bytes32 _feeType) internal view returns (uint256) {
uint256 fee;
address feeManager = feeManagerContract();
bytes4 method = _feeType == HOME_FEE ? GET_HOME_FEE : GET_FOREIGN_FEE;
bytes memory callData = abi.encodeWithSelector(method);
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function getFeeManagerMode() external view returns (bytes4) {
bytes4 mode;
bytes memory callData = abi.encodeWithSelector(GET_FEE_MANAGER_MODE);
address feeManager = feeManagerContract();
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 4)
mode := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return mode;
}
function feeManagerContract() public view returns (address) {
return addressStorage[FEE_MANAGER_CONTRACT];
}
function setFeeManagerContract(address _feeManager) external onlyOwner {
require(_feeManager == address(0) || AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
}
function _setFee(address _feeManager, uint256 _fee, bytes32 _feeType) internal {
bytes4 method = _feeType == HOME_FEE ? SET_HOME_FEE : SET_FOREIGN_FEE;
require(_feeManager.delegatecall(abi.encodeWithSelector(method, _fee)));
}
function calculateFee(uint256 _value, bool _recover, address _impl, bytes32 _feeType)
internal
view
returns (uint256)
{
uint256 fee;
bytes memory callData = abi.encodeWithSelector(CALCULATE_FEE, _value, _recover, _feeType);
assembly {
let result := callcode(gas, _impl, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function distributeFeeFromSignatures(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_SIGNATURES, _fee)));
emit FeeDistributedFromSignatures(_fee, _txHash);
}
function distributeFeeFromAffirmation(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_AFFIRMATION, _fee)));
emit FeeDistributedFromAffirmation(_fee, _txHash);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/RewardableForeignBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract RewardableForeignBridgeNativeToErc is RewardableBridge {
function setHomeFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, HOME_FEE);
}
function getHomeFee() public view returns (uint256) {
return _getFee(HOME_FEE);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/ForeignBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract ForeignBridgeNativeToErc is
BasicForeignBridge,
ERC677BridgeForBurnableMintableToken,
RewardableForeignBridgeNativeToErc
{
function initialize(
address _validatorContract,
address _erc677token,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _foreignGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _homeDailyLimitHomeMaxPerTxArray, // [ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
address _owner,
uint256 _decimalShift,
address _bridgeOnOtherSide
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_erc677token,
_dailyLimitMaxPerTxMinPerTxArray,
_foreignGasPrice,
_requiredBlockConfirmations,
_homeDailyLimitHomeMaxPerTxArray,
_owner,
_decimalShift,
_bridgeOnOtherSide
);
setInitialize();
return isInitialized();
}
function rewardableInitialize(
address _validatorContract,
address _erc677token,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _foreignGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _homeDailyLimitHomeMaxPerTxArray, // [ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
address _owner,
address _feeManager,
uint256 _homeFee,
uint256 _decimalShift,
address _bridgeOnOtherSide
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_erc677token,
_dailyLimitMaxPerTxMinPerTxArray,
_foreignGasPrice,
_requiredBlockConfirmations,
_homeDailyLimitHomeMaxPerTxArray,
_owner,
_decimalShift,
_bridgeOnOtherSide
);
require(AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
_setFee(_feeManager, _homeFee, HOME_FEE);
setInitialize();
return isInitialized();
}
function getBridgeMode() external pure returns (bytes4 _data) {
return 0x92a8d7fe; // bytes4(keccak256(abi.encodePacked("native-to-erc-core")))
}
function claimTokensFromErc677(address _token, address _to) external onlyIfUpgradeabilityOwner {
IBurnableMintableERC677Token(erc677token()).claimTokens(_token, _to);
}
function _initialize(
address _validatorContract,
address _erc677token,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _foreignGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _homeDailyLimitHomeMaxPerTxArray, // [ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ]
address _owner,
uint256 _decimalShift,
address _bridgeOnOtherSide
) internal {
require(!isInitialized());
require(AddressUtils.isContract(_validatorContract));
require(
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0
_dailyLimitMaxPerTxMinPerTxArray[1] > _dailyLimitMaxPerTxMinPerTxArray[2] && // _maxPerTx > _minPerTx
_dailyLimitMaxPerTxMinPerTxArray[0] > _dailyLimitMaxPerTxMinPerTxArray[1] // _dailyLimit > _maxPerTx
);
require(_requiredBlockConfirmations > 0);
require(_foreignGasPrice > 0);
require(_homeDailyLimitHomeMaxPerTxArray[1] < _homeDailyLimitHomeMaxPerTxArray[0]); // _homeMaxPerTx < _homeDailyLimit
require(_owner != address(0));
addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
setErc677token(_erc677token);
uintStorage[DAILY_LIMIT] = _dailyLimitMaxPerTxMinPerTxArray[0];
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
uintStorage[MAX_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[1];
uintStorage[MIN_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[2];
uintStorage[GAS_PRICE] = _foreignGasPrice;
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _requiredBlockConfirmations;
uintStorage[EXECUTION_DAILY_LIMIT] = _homeDailyLimitHomeMaxPerTxArray[0];
uintStorage[EXECUTION_MAX_PER_TX] = _homeDailyLimitHomeMaxPerTxArray[1];
uintStorage[DECIMAL_SHIFT] = _decimalShift;
setOwner(_owner);
_setBridgeContractOnOtherSide(_bridgeOnOtherSide);
emit RequiredBlockConfirmationChanged(_requiredBlockConfirmations);
emit GasPriceChanged(_foreignGasPrice);
emit DailyLimitChanged(_dailyLimitMaxPerTxMinPerTxArray[0]);
emit ExecutionDailyLimitChanged(_homeDailyLimitHomeMaxPerTxArray[0]);
}
function onExecuteMessage(address _recipient, uint256 _amount, bytes32 _txHash) internal returns (bool) {
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_amount));
uint256 valueToMint = _amount.div(10**decimalShift());
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToMint, false, feeManager, HOME_FEE);
if (fee != 0) {
distributeFeeFromSignatures(fee, feeManager, _txHash);
valueToMint = valueToMint.sub(fee);
}
}
return IBurnableMintableERC677Token(erc677token()).mint(_recipient, valueToMint);
}
function fireEventOnTokenTransfer(address _from, uint256 _value) internal {
emit UserRequestForAffirmation(_from, _value);
}
function onFailedMessage(address, uint256, bytes32) internal {
revert();
}
}
// File: contracts/libraries/SafeMath.sol
pragma solidity 0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/IBridgeValidators.sol
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) public view returns(bool);
function requiredSignatures() public view returns(uint256);
function owner() public view returns(address);
}
// File: contracts/libraries/Message.sol
pragma solidity 0.4.24;
library Message {
// function uintToString(uint256 inputValue) internal pure returns (string) {
// // figure out the length of the resulting string
// uint256 length = 0;
// uint256 currentValue = inputValue;
// do {
// length++;
// currentValue /= 10;
// } while (currentValue != 0);
// // allocate enough memory
// bytes memory result = new bytes(length);
// // construct the string backwards
// uint256 i = length - 1;
// currentValue = inputValue;
// do {
// result[i--] = byte(48 + currentValue % 10);
// currentValue /= 10;
// } while (currentValue != 0);
// return string(result);
// }
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// bytes 1 to 32 are 0 because message length is stored as little endian.
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 zero bytes followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
internal
pure
returns(address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := and(mload(add(message, 20)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function isMessageValid(bytes _msg) internal pure returns(bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() internal pure returns(uint256) {
return 104;
}
function recoverAddressFromSignedMessage(bytes signature, bytes message) internal pure returns (address) {
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message), uint8(v), r, s);
}
function hashMessage(bytes message) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
// message is always 84 length
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract) internal view {
require(isMessageValid(_message));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
if (addressArrayContains(encounteredAddresses, recoveredAddress)) {
revert();
}
encounteredAddresses[i] = recoveredAddress;
}
}
}
// File: contracts/IOwnedUpgradeabilityProxy.sol
pragma solidity 0.4.24;
interface IOwnedUpgradeabilityProxy {
function proxyOwner() public view returns (address);
}
// File: contracts/upgradeable_contracts/OwnedUpgradeability.sol
pragma solidity 0.4.24;
contract OwnedUpgradeability {
function upgradeabilityAdmin() public view returns (address) {
return IOwnedUpgradeabilityProxy(this).proxyOwner();
}
// Avoid using onlyProxyOwner name to prevent issues with implementation from proxy contract
modifier onlyIfOwnerOfProxy() {
require(msg.sender == upgradeabilityAdmin());
_;
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: contracts/upgradeable_contracts/Validatable.sol
pragma solidity 0.4.24;
contract Validatable is EternalStorage {
function validatorContract() public view returns(IBridgeValidators) {
return IBridgeValidators(addressStorage[keccak256(abi.encodePacked("validatorContract"))]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
_;
}
function requiredSignatures() public view returns(uint256) {
return validatorContract().requiredSignatures();
}
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
_;
}
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("owner"))];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[keccak256(abi.encodePacked("owner"))] = newOwner;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: contracts/upgradeable_contracts/BasicBridge.sol
pragma solidity 0.4.24;
contract BasicBridge is EternalStorage, Validatable, Ownable, OwnedUpgradeability {
using SafeMath for uint256;
event GasPriceChanged(uint256 gasPrice);
event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
event DailyLimitChanged(uint256 newLimit);
event ExecutionDailyLimitChanged(uint256 newLimit);
function getBridgeInterfacesVersion() public pure returns(uint64 major, uint64 minor, uint64 patch) {
return (2, 2, 0);
}
function setGasPrice(uint256 _gasPrice) public onlyOwner {
require(_gasPrice > 0);
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
emit GasPriceChanged(_gasPrice);
}
function gasPrice() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("gasPrice"))];
}
function setRequiredBlockConfirmations(uint256 _blockConfirmations) public onlyOwner {
require(_blockConfirmations > 0);
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _blockConfirmations;
emit RequiredBlockConfirmationChanged(_blockConfirmations);
}
function requiredBlockConfirmations() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))];
}
function deployedAtBlock() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))];
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = _value;
}
function totalSpentPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
}
function setTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = _value;
}
function totalExecutedPerDay(uint256 _day) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
}
function minPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("minPerTx"))];
}
function maxPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("maxPerTx"))];
}
function executionMaxPerTx() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))];
}
function setInitialize(bool _status) internal {
boolStorage[keccak256(abi.encodePacked("isInitialized"))] = _status;
}
function isInitialized() public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("isInitialized"))];
}
function getCurrentDay() public view returns(uint256) {
return now / 1 days;
}
function setDailyLimit(uint256 _dailyLimit) public onlyOwner {
uintStorage[keccak256(abi.encodePacked("dailyLimit"))] = _dailyLimit;
emit DailyLimitChanged(_dailyLimit);
}
function dailyLimit() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("dailyLimit"))];
}
function setExecutionDailyLimit(uint256 _dailyLimit) public onlyOwner {
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _dailyLimit;
emit ExecutionDailyLimitChanged(_dailyLimit);
}
function executionDailyLimit() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))];
}
function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < executionDailyLimit());
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _maxPerTx;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < dailyLimit());
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx < dailyLimit() && _minPerTx < maxPerTx());
uintStorage[keccak256(abi.encodePacked("minPerTx"))] = _minPerTx;
}
function withinLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function withinExecutionLimit(uint256 _amount) public view returns(bool) {
uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
}
function claimTokens(address _token, address _to) public onlyIfOwnerOfProxy {
require(_to != address(0));
if (_token == address(0)) {
_to.transfer(address(this).balance);
return;
}
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
require(token.transfer(_to, balance));
}
function isContract(address _addr) internal view returns (bool)
{
uint length;
assembly { length := extcodesize(_addr) }
return length > 0;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address _owner, address _spender)
public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// File: contracts/ERC677.sol
pragma solidity 0.4.24;
contract ERC677 is ERC20 {
event Transfer(address indexed from, address indexed to, uint value, bytes data);
function transferAndCall(address, uint, bytes) external returns (bool);
}
// File: contracts/IBurnableMintableERC677Token.sol
pragma solidity 0.4.24;
contract IBurnableMintableERC677Token is ERC677 {
function mint(address, uint256) public returns (bool);
function burn(uint256 _value) public;
function claimTokens(address _token, address _to) public;
}
// File: contracts/ERC677Receiver.sol
pragma solidity 0.4.24;
contract ERC677Receiver {
function onTokenTransfer(address _from, uint _value, bytes _data) external returns(bool);
}
// File: contracts/upgradeable_contracts/BasicHomeBridge.sol
pragma solidity 0.4.24;
contract BasicHomeBridge is EternalStorage, Validatable {
using SafeMath for uint256;
event UserRequestForSignature(address recipient, uint256 value);
event AffirmationCompleted (address recipient, uint256 value, bytes32 transactionHash);
event SignedForUserRequest(address indexed signer, bytes32 messageHash);
event SignedForAffirmation(address indexed signer, bytes32 transactionHash);
event CollectedSignatures(address authorityResponsibleForRelay, bytes32 messageHash, uint256 NumberOfCollectedSignatures);
function executeAffirmation(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
if (affirmationWithinLimits(value)) {
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
// Duplicated affirmations
require(!affirmationsSigned(hashSender));
setAffirmationsSigned(hashSender, true);
uint256 signed = numAffirmationsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
setNumAffirmationsSigned(hashMsg, signed);
emit SignedForAffirmation(msg.sender, transactionHash);
if (signed >= requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
if (value > 0) {
require(onExecuteAffirmation(recipient, value, transactionHash));
}
emit AffirmationCompleted(recipient, value, transactionHash);
}
} else {
onFailedAffirmation(recipient, value, transactionHash);
}
}
function submitSignature(bytes signature, bytes message) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message));
bytes32 hashMsg = keccak256(abi.encodePacked(message));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
uint256 signed = numMessagesSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
} else {
setMessages(hashMsg, message);
}
setMessagesSigned(hashSender, true);
bytes32 signIdx = keccak256(abi.encodePacked(hashMsg, (signed-1)));
setSignatures(signIdx, signature);
setNumMessagesSigned(hashMsg, signed);
emit SignedForUserRequest(msg.sender, hashMsg);
uint256 reqSigs = requiredSignatures();
if (signed >= reqSigs) {
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg, reqSigs);
onSignaturesCollected(message);
}
}
function setMessagesSigned(bytes32 _hash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("messagesSigned", _hash))] = _status;
}
function onExecuteAffirmation(address, uint256, bytes32) internal returns(bool) {
}
function onSignaturesCollected(bytes) internal {
}
function numAffirmationsSigned(bytes32 _withdrawal) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))];
}
function setAffirmationsSigned(bytes32 _withdrawal, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))] = _status;
}
function setNumAffirmationsSigned(bytes32 _withdrawal, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))] = _number;
}
function affirmationsSigned(bytes32 _withdrawal) public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))];
}
function signature(bytes32 _hash, uint256 _index) public view returns (bytes) {
bytes32 signIdx = keccak256(abi.encodePacked(_hash, _index));
return signatures(signIdx);
}
function messagesSigned(bytes32 _message) public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("messagesSigned", _message))];
}
function messages(bytes32 _hash) internal view returns(bytes) {
return bytesStorage[keccak256(abi.encodePacked("messages", _hash))];
}
function signatures(bytes32 _hash) internal view returns(bytes) {
return bytesStorage[keccak256(abi.encodePacked("signatures", _hash))];
}
function setSignatures(bytes32 _hash, bytes _signature) internal {
bytesStorage[keccak256(abi.encodePacked("signatures", _hash))] = _signature;
}
function setMessages(bytes32 _hash, bytes _message) internal {
bytesStorage[keccak256(abi.encodePacked("messages", _hash))] = _message;
}
function message(bytes32 _hash) public view returns (bytes) {
return messages(_hash);
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))] = _number;
}
function markAsProcessed(uint256 _v) internal pure returns(uint256) {
return _v | 2 ** 255;
}
function isAlreadyProcessed(uint256 _number) public pure returns(bool) {
return _number & 2**255 == 2**255;
}
function numMessagesSigned(bytes32 _message) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))];
}
function requiredMessageLength() public pure returns(uint256) {
return Message.requiredMessageLength();
}
function affirmationWithinLimits(uint256) internal view returns(bool) {
return true;
}
function onFailedAffirmation(address, uint256, bytes32) internal {
}
}
// File: contracts/upgradeable_contracts/OverdrawManagement.sol
pragma solidity 0.4.24;
contract OverdrawManagement is EternalStorage, OwnedUpgradeability {
using SafeMath for uint256;
event UserRequestForSignature(address recipient, uint256 value);
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign) external onlyIfOwnerOfProxy {
require(!fixedAssets(txHash));
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(txHash);
require(recipient != address(0) && value > 0);
setOutOfLimitAmount(outOfLimitAmount().sub(value));
if (unlockOnForeign) {
emit UserRequestForSignature(recipient, value);
}
setFixedAssets(txHash, true);
}
function outOfLimitAmount() public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("outOfLimitAmount"))];
}
function fixedAssets(bytes32 _txHash) public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))];
}
function setOutOfLimitAmount(uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("outOfLimitAmount"))] = _value;
}
function txAboveLimits(bytes32 _txHash) internal view returns(address recipient, uint256 value) {
recipient = addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _txHash))];
value = uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _txHash))];
}
function setTxAboveLimits(address _recipient, uint256 _value, bytes32 _txHash) internal {
addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _txHash))] = _recipient;
uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _txHash))] = _value;
}
function setFixedAssets(bytes32 _txHash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))] = _status;
}
}
// File: contracts/upgradeable_contracts/FeeTypes.sol
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = keccak256(abi.encodePacked("home-fee"));
bytes32 internal constant FOREIGN_FEE = keccak256(abi.encodePacked("foreign-fee"));
}
// File: contracts/upgradeable_contracts/RewardableBridge.sol
pragma solidity 0.4.24;
contract RewardableBridge is Ownable, FeeTypes {
event FeeDistributedFromAffirmation(uint256 feeAmount, bytes32 indexed transactionHash);
event FeeDistributedFromSignatures(uint256 feeAmount, bytes32 indexed transactionHash);
function _getFee(bytes32 _feeType) internal view returns(uint256) {
uint256 fee;
address feeManager = feeManagerContract();
string memory method = _feeType == HOME_FEE ? "getHomeFee()" : "getForeignFee()";
bytes memory callData = abi.encodeWithSignature(method);
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 { revert(0, 0) }
}
return fee;
}
function getFeeManagerMode() public view returns(bytes4) {
bytes4 mode;
bytes memory callData = abi.encodeWithSignature("getFeeManagerMode()");
address feeManager = feeManagerContract();
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 4)
mode := mload(0)
switch result
case 0 { revert(0, 0) }
}
return mode;
}
function feeManagerContract() public view returns(address) {
return addressStorage[keccak256(abi.encodePacked("feeManagerContract"))];
}
function setFeeManagerContract(address _feeManager) public onlyOwner {
require(_feeManager == address(0) || isContract(_feeManager));
addressStorage[keccak256(abi.encodePacked("feeManagerContract"))] = _feeManager;
}
function _setFee(address _feeManager, uint256 _fee, bytes32 _feeType) internal {
string memory method = _feeType == HOME_FEE ? "setHomeFee(uint256)" : "setForeignFee(uint256)";
require(_feeManager.delegatecall(abi.encodeWithSignature(method, _fee)));
}
function isContract(address _addr) internal view returns (bool)
{
uint length;
assembly { length := extcodesize(_addr) }
return length > 0;
}
function calculateFee(uint256 _value, bool _recover, address _impl, bytes32 _feeType) internal view returns(uint256) {
uint256 fee;
bytes memory callData = abi.encodeWithSignature("calculateFee(uint256,bool,bytes32)", _value, _recover, _feeType);
assembly {
let result := callcode(gas, _impl, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 { revert(0, 0) }
}
return fee;
}
function distributeFeeFromSignatures(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSignature("distributeFeeFromSignatures(uint256)", _fee)));
emit FeeDistributedFromSignatures(_fee, _txHash);
}
function distributeFeeFromAffirmation(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSignature("distributeFeeFromAffirmation(uint256)", _fee)));
emit FeeDistributedFromAffirmation(_fee, _txHash);
}
}
// File: contracts/upgradeable_contracts/erc20_to_erc20/RewardableHomeBridgeErcToErc.sol
pragma solidity 0.4.24;
contract RewardableHomeBridgeErcToErc is RewardableBridge {
function setHomeFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, HOME_FEE);
}
function setForeignFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, FOREIGN_FEE);
}
function getHomeFee() public view returns(uint256) {
return _getFee(HOME_FEE);
}
function getForeignFee() public view returns(uint256) {
return _getFee(FOREIGN_FEE);
}
}
// File: contracts/upgradeable_contracts/ERC677Bridge.sol
pragma solidity 0.4.24;
contract ERC677Bridge is BasicBridge {
function erc677token() public view returns(ERC677) {
return ERC677(addressStorage[keccak256(abi.encodePacked("erc677token"))]);
}
function setErc677token(address _token) internal {
require(_token != address(0) && isContract(_token));
addressStorage[keccak256(abi.encodePacked("erc677token"))] = _token;
}
function onTokenTransfer(address _from, uint256 _value, bytes /*_data*/) external returns(bool) {
ERC677 token = erc677token();
require(msg.sender == address(token));
require(withinLimit(_value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
bridgeSpecificActionsOnTokenTransfer(token, _from, _value);
return true;
}
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value) internal {
fireEventOnTokenTransfer(_from, _value);
}
function fireEventOnTokenTransfer(address _from, uint256 _value) internal;
}
// File: contracts/upgradeable_contracts/ERC677BridgeForBurnableMintableToken.sol
pragma solidity 0.4.24;
contract ERC677BridgeForBurnableMintableToken is ERC677Bridge {
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value) internal {
IBurnableMintableERC677Token(_token).burn(_value);
fireEventOnTokenTransfer(_from, _value);
}
}
// File: contracts/upgradeable_contracts/erc20_to_erc20/HomeBridgeErcToErc.sol
pragma solidity 0.4.24;
contract HomeBridgeErcToErc is ERC677Receiver, EternalStorage, BasicBridge, BasicHomeBridge, ERC677BridgeForBurnableMintableToken, OverdrawManagement, RewardableHomeBridgeErcToErc {
event AmountLimitExceeded(address recipient, uint256 value, bytes32 transactionHash);
function initialize (
address _validatorContract,
uint256 _dailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
address _erc677token,
uint256 _foreignDailyLimit,
uint256 _foreignMaxPerTx,
address _owner
) public
returns(bool)
{
_initialize (
_validatorContract,
_dailyLimit,
_maxPerTx,
_minPerTx,
_homeGasPrice,
_requiredBlockConfirmations,
_erc677token,
_foreignDailyLimit,
_foreignMaxPerTx,
_owner
);
setInitialize(true);
return isInitialized();
}
function rewardableInitialize (
address _validatorContract,
uint256 _dailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
address _erc677token,
uint256 _foreignDailyLimit,
uint256 _foreignMaxPerTx,
address _owner,
address _feeManager,
uint256 _homeFee,
uint256 _foreignFee
) public
returns(bool)
{
_rewardableInitialize (
_validatorContract,
_dailyLimit,
_maxPerTx,
_minPerTx,
_homeGasPrice,
_requiredBlockConfirmations,
_erc677token,
_foreignDailyLimit,
_foreignMaxPerTx,
_owner,
_feeManager,
_homeFee,
_foreignFee
);
setInitialize(true);
return isInitialized();
}
function _rewardableInitialize (
address _validatorContract,
uint256 _dailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
address _erc677token,
uint256 _foreignDailyLimit,
uint256 _foreignMaxPerTx,
address _owner,
address _feeManager,
uint256 _homeFee,
uint256 _foreignFee
) internal
returns(bool)
{
_initialize (
_validatorContract,
_dailyLimit,
_maxPerTx,
_minPerTx,
_homeGasPrice,
_requiredBlockConfirmations,
_erc677token,
_foreignDailyLimit,
_foreignMaxPerTx,
_owner
);
require(isContract(_feeManager));
addressStorage[keccak256(abi.encodePacked("feeManagerContract"))] = _feeManager;
_setFee(_feeManager, _homeFee, HOME_FEE);
_setFee(_feeManager, _foreignFee, FOREIGN_FEE);
}
function _initialize (
address _validatorContract,
uint256 _dailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
address _erc677token,
uint256 _foreignDailyLimit,
uint256 _foreignMaxPerTx,
address _owner
) internal
returns(bool)
{
require(!isInitialized());
require(_validatorContract != address(0) && isContract(_validatorContract));
require(_homeGasPrice > 0);
require(_requiredBlockConfirmations > 0);
require(_minPerTx > 0 && _maxPerTx > _minPerTx && _dailyLimit > _maxPerTx);
require(_foreignMaxPerTx < _foreignDailyLimit);
require(_owner != address(0));
addressStorage[keccak256(abi.encodePacked("validatorContract"))] = _validatorContract;
uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))] = block.number;
uintStorage[keccak256(abi.encodePacked("dailyLimit"))] = _dailyLimit;
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
uintStorage[keccak256(abi.encodePacked("minPerTx"))] = _minPerTx;
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _homeGasPrice;
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _requiredBlockConfirmations;
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _foreignDailyLimit;
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _foreignMaxPerTx;
setOwner(_owner);
setErc677token(_erc677token);
}
function getBridgeMode() public pure returns(bytes4 _data) {
return bytes4(keccak256(abi.encodePacked("erc-to-erc-core")));
}
function () payable public {
revert();
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns(bool) {
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_value));
uint256 valueToMint = _value;
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToMint, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
valueToMint = valueToMint.sub(fee);
}
return IBurnableMintableERC677Token(erc677token()).mint(_recipient, valueToMint);
}
function fireEventOnTokenTransfer(address _from, uint256 _value) internal {
uint256 valueToTransfer = _value;
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, HOME_FEE);
valueToTransfer = valueToTransfer.sub(fee);
}
emit UserRequestForSignature(_from, valueToTransfer);
}
function onSignaturesCollected(bytes _message) internal {
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
address recipient;
uint256 amount;
bytes32 txHash;
address contractAddress;
(recipient, amount, txHash, contractAddress) = Message.parseMessage(_message);
uint256 fee = calculateFee(amount, true, feeManager, HOME_FEE);
distributeFeeFromSignatures(fee, feeManager, txHash);
}
}
function affirmationWithinLimits(uint256 _amount) internal view returns(bool) {
return withinExecutionLimit(_amount);
}
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash) internal {
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(_txHash);
require(recipient == address(0) && value == 0);
setOutOfLimitAmount(outOfLimitAmount().add(_value));
setTxAboveLimits(_recipient, _value, _txHash);
emit AmountLimitExceeded(_recipient, _value, _txHash);
}
}
// File: contracts/interfaces/IBridgeValidators.sol
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
}
// File: contracts/libraries/Message.sol
pragma solidity 0.4.24;
library Message {
// function uintToString(uint256 inputValue) internal pure returns (string) {
// // figure out the length of the resulting string
// uint256 length = 0;
// uint256 currentValue = inputValue;
// do {
// length++;
// currentValue /= 10;
// } while (currentValue != 0);
// // allocate enough memory
// bytes memory result = new bytes(length);
// // construct the string backwards
// uint256 i = length - 1;
// currentValue = inputValue;
// do {
// result[i--] = byte(48 + currentValue % 10);
// currentValue /= 10;
// } while (currentValue != 0);
// return string(result);
// }
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 bytes (most of them zeros) followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
internal
pure
returns (address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := mload(add(message, 20))
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function isMessageValid(bytes _msg) internal pure returns (bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() internal pure returns (uint256) {
return 104;
}
function recoverAddressFromSignedMessage(bytes signature, bytes message, bool isAMBMessage)
internal
pure
returns (address)
{
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
}
function hashMessage(bytes message, bool isAMBMessage) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
if (isAMBMessage) {
return keccak256(abi.encodePacked(prefix, uintToString(message.length), message));
} else {
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
// It is not necessary to check that arrays have the same length since it will be handled
// during attempt to access to the corresponding elements in the loop and the call will be reverted.
// It will save gas for the rational validators actions and still be safe enough from security point of view
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function hasEnoughValidSignatures(
bytes _message,
bytes _signatures,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
uint256 amount;
assembly {
amount := and(mload(add(_signatures, 1)), 0xff)
}
require(amount >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
uint8 v;
bytes32 r;
bytes32 s;
uint256 posr = 33 + amount + 32 * i;
uint256 poss = posr + 32 * amount;
assembly {
v := mload(add(_signatures, add(2, i)))
r := mload(add(_signatures, posr))
s := mload(add(_signatures, poss))
}
address recoveredAddress = ecrecover(hash, v, r, s);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function uintToString(uint256 i) internal pure returns (string) {
if (i == 0) return "0";
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
bstr[k--] = bytes1(48 + (i % 10));
i /= 10;
}
return string(bstr);
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address _owner, address _spender)
public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// File: contracts/upgradeable_contracts/ValidatorStorage.sol
pragma solidity 0.4.24;
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
// File: contracts/upgradeable_contracts/Validatable.sol
pragma solidity 0.4.24;
contract Validatable is EternalStorage, ValidatorStorage {
function validatorContract() public view returns (IBridgeValidators) {
return IBridgeValidators(addressStorage[VALIDATOR_CONTRACT]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
/* solcov ignore next */
_;
}
function requiredSignatures() public view returns (uint256) {
return validatorContract().requiredSignatures();
}
}
// File: contracts/interfaces/IUpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}
// File: contracts/upgradeable_contracts/Upgradeable.sol
pragma solidity 0.4.24;
contract Upgradeable {
// Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract
modifier onlyIfUpgradeabilityOwner() {
require(msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner());
/* solcov ignore next */
_;
}
}
// File: contracts/upgradeable_contracts/Initializable.sol
pragma solidity 0.4.24;
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}
// File: contracts/upgradeable_contracts/InitializableBridge.sol
pragma solidity 0.4.24;
contract InitializableBridge is Initializable {
bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
function deployedAtBlock() external view returns (uint256) {
return uintStorage[DEPLOYED_AT_BLOCK];
}
}
// File: openzeppelin-solidity/contracts/AddressUtils.sol
pragma solidity ^0.4.24;
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param _addr address to check
* @return whether the target address is a contract
*/
function isContract(address _addr) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(_addr) }
return size > 0;
}
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
/* solcov ignore next */
_;
}
/**
* @dev Throws if called by any account other than contract itself or owner.
*/
modifier onlyRelevantSender() {
// proxy owner if used through proxy, address(0) otherwise
require(
!address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
/* solcov ignore next */
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}
// File: contracts/upgradeable_contracts/Sacrifice.sol
pragma solidity 0.4.24;
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
// File: contracts/upgradeable_contracts/Claimable.sol
pragma solidity 0.4.24;
contract Claimable {
bytes4 internal constant TRANSFER = 0xa9059cbb; // transfer(address,uint256)
modifier validAddress(address _to) {
require(_to != address(0));
/* solcov ignore next */
_;
}
function claimValues(address _token, address _to) internal {
if (_token == address(0)) {
claimNativeCoins(_to);
} else {
claimErc20Tokens(_token, _to);
}
}
function claimNativeCoins(address _to) internal {
uint256 value = address(this).balance;
if (!_to.send(value)) {
(new Sacrifice).value(value)(_to);
}
}
function claimErc20Tokens(address _token, address _to) internal {
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
safeTransfer(_token, _to, balance);
}
function safeTransfer(address _token, address _to, uint256 _value) internal {
bytes memory returnData;
bool returnDataResult;
bytes memory callData = abi.encodeWithSelector(TRANSFER, _to, _value);
assembly {
let result := call(gas, _token, 0x0, add(callData, 0x20), mload(callData), 0, 32)
returnData := mload(0)
returnDataResult := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
// Return data is optional
if (returnData.length > 0) {
require(returnDataResult);
}
}
}
// File: contracts/upgradeable_contracts/VersionableBridge.sol
pragma solidity 0.4.24;
contract VersionableBridge {
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (2, 5, 0);
}
/* solcov ignore next */
function getBridgeMode() external pure returns (bytes4);
}
// File: contracts/upgradeable_contracts/BasicBridge.sol
pragma solidity 0.4.24;
contract BasicBridge is InitializableBridge, Validatable, Ownable, Upgradeable, Claimable, VersionableBridge {
event GasPriceChanged(uint256 gasPrice);
event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
bytes32 internal constant GAS_PRICE = 0x55b3774520b5993024893d303890baa4e84b1244a43c60034d1ced2d3cf2b04b; // keccak256(abi.encodePacked("gasPrice"))
bytes32 internal constant REQUIRED_BLOCK_CONFIRMATIONS = 0x916daedf6915000ff68ced2f0b6773fe6f2582237f92c3c95bb4d79407230071; // keccak256(abi.encodePacked("requiredBlockConfirmations"))
function setGasPrice(uint256 _gasPrice) external onlyOwner {
require(_gasPrice > 0);
uintStorage[GAS_PRICE] = _gasPrice;
emit GasPriceChanged(_gasPrice);
}
function gasPrice() external view returns (uint256) {
return uintStorage[GAS_PRICE];
}
function setRequiredBlockConfirmations(uint256 _blockConfirmations) external onlyOwner {
require(_blockConfirmations > 0);
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _blockConfirmations;
emit RequiredBlockConfirmationChanged(_blockConfirmations);
}
function requiredBlockConfirmations() external view returns (uint256) {
return uintStorage[REQUIRED_BLOCK_CONFIRMATIONS];
}
function claimTokens(address _token, address _to) public onlyIfUpgradeabilityOwner validAddress(_to) {
claimValues(_token, _to);
}
}
// File: contracts/upgradeable_contracts/BasicTokenBridge.sol
pragma solidity 0.4.24;
contract BasicTokenBridge is EternalStorage, Ownable {
using SafeMath for uint256;
event DailyLimitChanged(uint256 newLimit);
event ExecutionDailyLimitChanged(uint256 newLimit);
bytes32 internal constant MIN_PER_TX = 0xbbb088c505d18e049d114c7c91f11724e69c55ad6c5397e2b929e68b41fa05d1; // keccak256(abi.encodePacked("minPerTx"))
bytes32 internal constant MAX_PER_TX = 0x0f8803acad17c63ee38bf2de71e1888bc7a079a6f73658e274b08018bea4e29c; // keccak256(abi.encodePacked("maxPerTx"))
bytes32 internal constant DAILY_LIMIT = 0x4a6a899679f26b73530d8cf1001e83b6f7702e04b6fdb98f3c62dc7e47e041a5; // keccak256(abi.encodePacked("dailyLimit"))
bytes32 internal constant EXECUTION_MAX_PER_TX = 0xc0ed44c192c86d1cc1ba51340b032c2766b4a2b0041031de13c46dd7104888d5; // keccak256(abi.encodePacked("executionMaxPerTx"))
bytes32 internal constant EXECUTION_DAILY_LIMIT = 0x21dbcab260e413c20dc13c28b7db95e2b423d1135f42bb8b7d5214a92270d237; // keccak256(abi.encodePacked("executionDailyLimit"))
bytes32 internal constant DECIMAL_SHIFT = 0x1e8ecaafaddea96ed9ac6d2642dcdfe1bebe58a930b1085842d8fc122b371ee5; // keccak256(abi.encodePacked("decimalShift"))
function totalSpentPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
}
function totalExecutedPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
}
function dailyLimit() public view returns (uint256) {
return uintStorage[DAILY_LIMIT];
}
function executionDailyLimit() public view returns (uint256) {
return uintStorage[EXECUTION_DAILY_LIMIT];
}
function maxPerTx() public view returns (uint256) {
return uintStorage[MAX_PER_TX];
}
function executionMaxPerTx() public view returns (uint256) {
return uintStorage[EXECUTION_MAX_PER_TX];
}
function minPerTx() public view returns (uint256) {
return uintStorage[MIN_PER_TX];
}
function decimalShift() public view returns (uint256) {
return uintStorage[DECIMAL_SHIFT];
}
function withinLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function withinExecutionLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
}
function getCurrentDay() public view returns (uint256) {
// solhint-disable-next-line not-rely-on-time
return now / 1 days;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = _value;
}
function setTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = _value;
}
function setDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > maxPerTx() || _dailyLimit == 0);
uintStorage[DAILY_LIMIT] = _dailyLimit;
emit DailyLimitChanged(_dailyLimit);
}
function setExecutionDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > executionMaxPerTx() || _dailyLimit == 0);
uintStorage[EXECUTION_DAILY_LIMIT] = _dailyLimit;
emit ExecutionDailyLimitChanged(_dailyLimit);
}
function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < executionDailyLimit());
uintStorage[EXECUTION_MAX_PER_TX] = _maxPerTx;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx == 0 || (_maxPerTx > minPerTx() && _maxPerTx < dailyLimit()));
uintStorage[MAX_PER_TX] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx > 0 && _minPerTx < dailyLimit() && _minPerTx < maxPerTx());
uintStorage[MIN_PER_TX] = _minPerTx;
}
}
// File: contracts/upgradeable_contracts/BasicHomeBridge.sol
pragma solidity 0.4.24;
contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge {
using SafeMath for uint256;
event UserRequestForSignature(address recipient, uint256 value);
event AffirmationCompleted(address recipient, uint256 value, bytes32 transactionHash);
event SignedForUserRequest(address indexed signer, bytes32 messageHash);
event SignedForAffirmation(address indexed signer, bytes32 transactionHash);
event CollectedSignatures(
address authorityResponsibleForRelay,
bytes32 messageHash,
uint256 NumberOfCollectedSignatures
);
function executeAffirmation(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
if (withinExecutionLimit(value)) {
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
// Duplicated affirmations
require(!affirmationsSigned(hashSender));
setAffirmationsSigned(hashSender, true);
uint256 signed = numAffirmationsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
setNumAffirmationsSigned(hashMsg, signed);
emit SignedForAffirmation(msg.sender, transactionHash);
if (signed >= requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
if (value > 0) {
require(onExecuteAffirmation(recipient, value, transactionHash));
}
emit AffirmationCompleted(recipient, value, transactionHash);
}
} else {
onFailedAffirmation(recipient, value, transactionHash);
}
}
function submitSignature(bytes signature, bytes message) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message, false));
bytes32 hashMsg = keccak256(abi.encodePacked(message));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
uint256 signed = numMessagesSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
} else {
setMessages(hashMsg, message);
}
setMessagesSigned(hashSender, true);
bytes32 signIdx = keccak256(abi.encodePacked(hashMsg, (signed - 1)));
setSignatures(signIdx, signature);
setNumMessagesSigned(hashMsg, signed);
emit SignedForUserRequest(msg.sender, hashMsg);
uint256 reqSigs = requiredSignatures();
if (signed >= reqSigs) {
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg, reqSigs);
onSignaturesCollected(message);
}
}
function setMessagesSigned(bytes32 _hash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("messagesSigned", _hash))] = _status;
}
/* solcov ignore next */
function onExecuteAffirmation(address, uint256, bytes32) internal returns (bool);
/* solcov ignore next */
function onSignaturesCollected(bytes) internal;
function numAffirmationsSigned(bytes32 _withdrawal) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))];
}
function setAffirmationsSigned(bytes32 _withdrawal, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))] = _status;
}
function setNumAffirmationsSigned(bytes32 _withdrawal, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))] = _number;
}
function affirmationsSigned(bytes32 _withdrawal) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))];
}
function signature(bytes32 _hash, uint256 _index) external view returns (bytes) {
bytes32 signIdx = keccak256(abi.encodePacked(_hash, _index));
return bytesStorage[keccak256(abi.encodePacked("signatures", signIdx))];
}
function messagesSigned(bytes32 _message) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("messagesSigned", _message))];
}
function setSignatures(bytes32 _hash, bytes _signature) internal {
bytesStorage[keccak256(abi.encodePacked("signatures", _hash))] = _signature;
}
function setMessages(bytes32 _hash, bytes _message) internal {
bytesStorage[keccak256(abi.encodePacked("messages", _hash))] = _message;
}
function message(bytes32 _hash) external view returns (bytes) {
return bytesStorage[keccak256(abi.encodePacked("messages", _hash))];
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))] = _number;
}
function markAsProcessed(uint256 _v) internal pure returns (uint256) {
return _v | (2**255);
}
function isAlreadyProcessed(uint256 _number) public pure returns (bool) {
return _number & (2**255) == 2**255;
}
function numMessagesSigned(bytes32 _message) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))];
}
function requiredMessageLength() public pure returns (uint256) {
return Message.requiredMessageLength();
}
/* solcov ignore next */
function onFailedAffirmation(address, uint256, bytes32) internal;
}
// File: contracts/upgradeable_contracts/FeeTypes.sol
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
// File: contracts/upgradeable_contracts/RewardableBridge.sol
pragma solidity 0.4.24;
contract RewardableBridge is Ownable, FeeTypes {
event FeeDistributedFromAffirmation(uint256 feeAmount, bytes32 indexed transactionHash);
event FeeDistributedFromSignatures(uint256 feeAmount, bytes32 indexed transactionHash);
bytes32 internal constant FEE_MANAGER_CONTRACT = 0x779a349c5bee7817f04c960f525ee3e2f2516078c38c68a3149787976ee837e5; // keccak256(abi.encodePacked("feeManagerContract"))
bytes4 internal constant GET_HOME_FEE = 0x94da17cd; // getHomeFee()
bytes4 internal constant GET_FOREIGN_FEE = 0xffd66196; // getForeignFee()
bytes4 internal constant GET_FEE_MANAGER_MODE = 0xf2ba9561; // getFeeManagerMode()
bytes4 internal constant SET_HOME_FEE = 0x34a9e148; // setHomeFee(uint256)
bytes4 internal constant SET_FOREIGN_FEE = 0x286c4066; // setForeignFee(uint256)
bytes4 internal constant CALCULATE_FEE = 0x9862f26f; // calculateFee(uint256,bool,bytes32)
bytes4 internal constant DISTRIBUTE_FEE_FROM_SIGNATURES = 0x59d78464; // distributeFeeFromSignatures(uint256)
bytes4 internal constant DISTRIBUTE_FEE_FROM_AFFIRMATION = 0x054d46ec; // distributeFeeFromAffirmation(uint256)
function _getFee(bytes32 _feeType) internal view returns (uint256) {
uint256 fee;
address feeManager = feeManagerContract();
bytes4 method = _feeType == HOME_FEE ? GET_HOME_FEE : GET_FOREIGN_FEE;
bytes memory callData = abi.encodeWithSelector(method);
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function getFeeManagerMode() external view returns (bytes4) {
bytes4 mode;
bytes memory callData = abi.encodeWithSelector(GET_FEE_MANAGER_MODE);
address feeManager = feeManagerContract();
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 4)
mode := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return mode;
}
function feeManagerContract() public view returns (address) {
return addressStorage[FEE_MANAGER_CONTRACT];
}
function setFeeManagerContract(address _feeManager) external onlyOwner {
require(_feeManager == address(0) || AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
}
function _setFee(address _feeManager, uint256 _fee, bytes32 _feeType) internal {
bytes4 method = _feeType == HOME_FEE ? SET_HOME_FEE : SET_FOREIGN_FEE;
require(_feeManager.delegatecall(abi.encodeWithSelector(method, _fee)));
}
function calculateFee(uint256 _value, bool _recover, address _impl, bytes32 _feeType)
internal
view
returns (uint256)
{
uint256 fee;
bytes memory callData = abi.encodeWithSelector(CALCULATE_FEE, _value, _recover, _feeType);
assembly {
let result := callcode(gas, _impl, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function distributeFeeFromSignatures(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_SIGNATURES, _fee)));
emit FeeDistributedFromSignatures(_fee, _txHash);
}
function distributeFeeFromAffirmation(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_AFFIRMATION, _fee)));
emit FeeDistributedFromAffirmation(_fee, _txHash);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/RewardableHomeBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract RewardableHomeBridgeNativeToErc is RewardableBridge {
function setForeignFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, FOREIGN_FEE);
}
function setHomeFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, HOME_FEE);
}
function getForeignFee() public view returns (uint256) {
return _getFee(FOREIGN_FEE);
}
function getHomeFee() public view returns (uint256) {
return _getFee(HOME_FEE);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/HomeBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract HomeBridgeNativeToErc is EternalStorage, BasicHomeBridge, RewardableHomeBridgeNativeToErc {
function() public payable {
require(msg.data.length == 0);
nativeTransfer(msg.sender);
}
function nativeTransfer(address _receiver) internal {
require(msg.value > 0);
require(withinLimit(msg.value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value));
uint256 valueToTransfer = msg.value;
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, HOME_FEE);
valueToTransfer = valueToTransfer.sub(fee);
}
emit UserRequestForSignature(_receiver, valueToTransfer);
}
function relayTokens(address _receiver) external payable {
nativeTransfer(_receiver);
}
function initialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
uint256 _decimalShift
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_dailyLimitMaxPerTxMinPerTxArray,
_homeGasPrice,
_requiredBlockConfirmations,
_foreignDailyLimitForeignMaxPerTxArray,
_owner,
_decimalShift
);
setInitialize();
return isInitialized();
}
function rewardableInitialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
address _feeManager,
uint256[] _homeFeeForeignFeeArray, // [ 0 = _homeFee, 1 = _foreignFee ]
uint256 _decimalShift
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_dailyLimitMaxPerTxMinPerTxArray,
_homeGasPrice,
_requiredBlockConfirmations,
_foreignDailyLimitForeignMaxPerTxArray,
_owner,
_decimalShift
);
require(AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
_setFee(_feeManager, _homeFeeForeignFeeArray[0], HOME_FEE);
_setFee(_feeManager, _homeFeeForeignFeeArray[1], FOREIGN_FEE);
setInitialize();
return isInitialized();
}
function getBridgeMode() external pure returns (bytes4 _data) {
return 0x92a8d7fe; // bytes4(keccak256(abi.encodePacked("native-to-erc-core")))
}
function _initialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
uint256 _decimalShift
) internal {
require(!isInitialized());
require(AddressUtils.isContract(_validatorContract));
require(_homeGasPrice > 0);
require(_requiredBlockConfirmations > 0);
require(
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0
_dailyLimitMaxPerTxMinPerTxArray[1] > _dailyLimitMaxPerTxMinPerTxArray[2] && // _maxPerTx > _minPerTx
_dailyLimitMaxPerTxMinPerTxArray[0] > _dailyLimitMaxPerTxMinPerTxArray[1] // _dailyLimit > _maxPerTx
);
require(_foreignDailyLimitForeignMaxPerTxArray[1] < _foreignDailyLimitForeignMaxPerTxArray[0]); // _foreignMaxPerTx < _foreignDailyLimit
require(_owner != address(0));
addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
uintStorage[DAILY_LIMIT] = _dailyLimitMaxPerTxMinPerTxArray[0];
uintStorage[MAX_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[1];
uintStorage[MIN_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[2];
uintStorage[GAS_PRICE] = _homeGasPrice;
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _requiredBlockConfirmations;
uintStorage[EXECUTION_DAILY_LIMIT] = _foreignDailyLimitForeignMaxPerTxArray[0];
uintStorage[EXECUTION_MAX_PER_TX] = _foreignDailyLimitForeignMaxPerTxArray[1];
uintStorage[DECIMAL_SHIFT] = _decimalShift;
setOwner(_owner);
emit RequiredBlockConfirmationChanged(_requiredBlockConfirmations);
emit GasPriceChanged(_homeGasPrice);
emit DailyLimitChanged(_dailyLimitMaxPerTxMinPerTxArray[0]);
emit ExecutionDailyLimitChanged(_foreignDailyLimitForeignMaxPerTxArray[0]);
}
function onSignaturesCollected(bytes _message) internal {
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
address recipient;
uint256 amount;
bytes32 txHash;
address contractAddress;
(recipient, amount, txHash, contractAddress) = Message.parseMessage(_message);
uint256 fee = calculateFee(amount, true, feeManager, HOME_FEE);
if (fee != 0) {
distributeFeeFromSignatures(fee, feeManager, txHash);
}
}
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) {
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_value));
uint256 valueToTransfer = _value.mul(10**decimalShift());
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
valueToTransfer = valueToTransfer.sub(fee);
}
if (!_recipient.send(valueToTransfer)) {
(new Sacrifice).value(valueToTransfer)(_recipient);
}
return true;
}
function onFailedAffirmation(
address, /*_recipient*/
uint256, /*_value*/
bytes32 /*_txHash*/
) internal {
revert();
}
}
pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;
/// @title Multicall - Aggregate results from multiple read-only function calls
/// @author Michael Elliot <mike@makerdao.com>
/// @author Joshua Levine <joshua@makerdao.com>
/// @author Nick Johnson <arachnid@notdot.net>
contract Multicall {
struct Call {
address target;
bytes callData;
}
function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
returnData = new bytes[](calls.length);
for(uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
require(success);
returnData[i] = ret;
}
}
// Helper functions
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}
function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}
function getLastBlockHash() public view returns (bytes32 blockHash) {
blockHash = blockhash(block.number - 1);
}
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}
function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}
function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}
function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}
}
pragma solidity ^0.4.15;
/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWallet {
/*
* Events
*/
event Confirmation(address indexed sender, uint indexed transactionId);
event Revocation(address indexed sender, uint indexed transactionId);
event Submission(uint indexed transactionId);
event Execution(uint indexed transactionId);
event ExecutionFailure(uint indexed transactionId);
event Deposit(address indexed sender, uint value);
event OwnerAddition(address indexed owner);
event OwnerRemoval(address indexed owner);
event RequirementChange(uint required);
/*
* Constants
*/
uint constant public MAX_OWNER_COUNT = 50;
/*
* Storage
*/
mapping (uint => Transaction) public transactions;
mapping (uint => mapping (address => bool)) public confirmations;
mapping (address => bool) public isOwner;
address[] public owners;
uint public required;
uint public transactionCount;
struct Transaction {
address destination;
uint value;
bytes data;
bool executed;
}
/*
* Modifiers
*/
modifier onlyWallet() {
require(msg.sender == address(this));
_;
}
modifier ownerDoesNotExist(address owner) {
require(!isOwner[owner]);
_;
}
modifier ownerExists(address owner) {
require(isOwner[owner]);
_;
}
modifier transactionExists(uint transactionId) {
require(transactions[transactionId].destination != 0);
_;
}
modifier confirmed(uint transactionId, address owner) {
require(confirmations[transactionId][owner]);
_;
}
modifier notConfirmed(uint transactionId, address owner) {
require(!confirmations[transactionId][owner]);
_;
}
modifier notExecuted(uint transactionId) {
require(!transactions[transactionId].executed);
_;
}
modifier notNull(address _address) {
require(_address != 0);
_;
}
modifier validRequirement(uint ownerCount, uint _required) {
require(ownerCount <= MAX_OWNER_COUNT
&& _required <= ownerCount
&& _required != 0
&& ownerCount != 0);
_;
}
/// @dev Fallback function allows to deposit ether.
function()
payable
{
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners and required number of confirmations.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
function MultiSigWallet(address[] _owners, uint _required)
public
validRequirement(_owners.length, _required)
{
for (uint i=0; i<_owners.length; i++) {
require(!isOwner[_owners[i]] && _owners[i] != 0);
isOwner[_owners[i]] = true;
}
owners = _owners;
required = _required;
}
/// @dev Allows to add a new owner. Transaction has to be sent by wallet.
/// @param owner Address of new owner.
function addOwner(address owner)
public
onlyWallet
ownerDoesNotExist(owner)
notNull(owner)
validRequirement(owners.length + 1, required)
{
isOwner[owner] = true;
owners.push(owner);
OwnerAddition(owner);
}
/// @dev Allows to remove an owner. Transaction has to be sent by wallet.
/// @param owner Address of owner.
function removeOwner(address owner)
public
onlyWallet
ownerExists(owner)
{
isOwner[owner] = false;
for (uint i=0; i<owners.length - 1; i++)
if (owners[i] == owner) {
owners[i] = owners[owners.length - 1];
break;
}
owners.length -= 1;
if (required > owners.length)
changeRequirement(owners.length);
OwnerRemoval(owner);
}
/// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
/// @param owner Address of owner to be replaced.
/// @param newOwner Address of new owner.
function replaceOwner(address owner, address newOwner)
public
onlyWallet
ownerExists(owner)
ownerDoesNotExist(newOwner)
{
for (uint i=0; i<owners.length; i++)
if (owners[i] == owner) {
owners[i] = newOwner;
break;
}
isOwner[owner] = false;
isOwner[newOwner] = true;
OwnerRemoval(owner);
OwnerAddition(newOwner);
}
/// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
/// @param _required Number of required confirmations.
function changeRequirement(uint _required)
public
onlyWallet
validRequirement(owners.length, _required)
{
required = _required;
RequirementChange(_required);
}
/// @dev Allows an owner to submit and confirm a transaction.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function submitTransaction(address destination, uint value, bytes data)
public
returns (uint transactionId)
{
transactionId = addTransaction(destination, value, data);
confirmTransaction(transactionId);
}
/// @dev Allows an owner to confirm a transaction.
/// @param transactionId Transaction ID.
function confirmTransaction(uint transactionId)
public
ownerExists(msg.sender)
transactionExists(transactionId)
notConfirmed(transactionId, msg.sender)
{
confirmations[transactionId][msg.sender] = true;
Confirmation(msg.sender, transactionId);
executeTransaction(transactionId);
}
/// @dev Allows an owner to revoke a confirmation for a transaction.
/// @param transactionId Transaction ID.
function revokeConfirmation(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
confirmations[transactionId][msg.sender] = false;
Revocation(msg.sender, transactionId);
}
/// @dev Allows anyone to execute a confirmed transaction.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
if (isConfirmed(transactionId)) {
Transaction storage txn = transactions[transactionId];
txn.executed = true;
if (external_call(txn.destination, txn.value, txn.data.length, txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
}
}
}
// call has been separated into its own function in order to take advantage
// of the Solidity's code generator to produce a loop that copies tx.data into memory.
function external_call(address destination, uint value, uint dataLength, bytes data) private returns (bool) {
bool result;
assembly {
let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
result := call(
sub(gas, 34710), // 34710 is the value that solidity is currently emitting
// It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
// callNewAccountGas (25000, in case the destination address does not exist and needs creating)
destination,
value,
d,
dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
x,
0 // Output is ignored, therefore the output size is zero
)
}
return result;
}
/// @dev Returns the confirmation status of a transaction.
/// @param transactionId Transaction ID.
/// @return Confirmation status.
function isConfirmed(uint transactionId)
public
constant
returns (bool)
{
uint count = 0;
for (uint i=0; i<owners.length; i++) {
if (confirmations[transactionId][owners[i]])
count += 1;
if (count == required)
return true;
}
}
/*
* Internal functions
*/
/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function addTransaction(address destination, uint value, bytes data)
internal
notNull(destination)
returns (uint transactionId)
{
transactionId = transactionCount;
transactions[transactionId] = Transaction({
destination: destination,
value: value,
data: data,
executed: false
});
transactionCount += 1;
Submission(transactionId);
}
/*
* Web3 call functions
*/
/// @dev Returns number of confirmations of a transaction.
/// @param transactionId Transaction ID.
/// @return Number of confirmations.
function getConfirmationCount(uint transactionId)
public
constant
returns (uint count)
{
for (uint i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]])
count += 1;
}
/// @dev Returns total number of transactions after filers are applied.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Total number of transactions after filters are applied.
function getTransactionCount(bool pending, bool executed)
public
constant
returns (uint count)
{
for (uint i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
count += 1;
}
/// @dev Returns list of owners.
/// @return List of owner addresses.
function getOwners()
public
constant
returns (address[])
{
return owners;
}
/// @dev Returns array with owner addresses, which confirmed transaction.
/// @param transactionId Transaction ID.
/// @return Returns array of owner addresses.
function getConfirmations(uint transactionId)
public
constant
returns (address[] _confirmations)
{
address[] memory confirmationsTemp = new address[](owners.length);
uint count = 0;
uint i;
for (i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]]) {
confirmationsTemp[count] = owners[i];
count += 1;
}
_confirmations = new address[](count);
for (i=0; i<count; i++)
_confirmations[i] = confirmationsTemp[i];
}
/// @dev Returns list of transaction IDs in defined range.
/// @param from Index start position of transaction array.
/// @param to Index end position of transaction array.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Returns array of transaction IDs.
function getTransactionIds(uint from, uint to, bool pending, bool executed)
public
constant
returns (uint[] _transactionIds)
{
uint[] memory transactionIdsTemp = new uint[](transactionCount);
uint count = 0;
uint i;
for (i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
{
transactionIdsTemp[count] = i;
count += 1;
}
_transactionIds = new uint[](to - from);
for (i=from; i<to; i++)
_transactionIds[i - from] = transactionIdsTemp[i];
}
}
/// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWalletWithDailyLimit is MultiSigWallet {
/*
* Events
*/
event DailyLimitChange(uint dailyLimit);
/*
* Storage
*/
uint public dailyLimit;
uint public lastDay;
uint public spentToday;
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
/// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis.
function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit)
public
MultiSigWallet(_owners, _required)
{
dailyLimit = _dailyLimit;
}
/// @dev Allows to change the daily limit. Transaction has to be sent by wallet.
/// @param _dailyLimit Amount in wei.
function changeDailyLimit(uint _dailyLimit)
public
onlyWallet
{
dailyLimit = _dailyLimit;
DailyLimitChange(_dailyLimit);
}
/// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
Transaction storage txn = transactions[transactionId];
bool _confirmed = isConfirmed(transactionId);
if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) {
txn.executed = true;
if (!_confirmed)
spentToday += txn.value;
if (txn.destination.call.value(txn.value)(txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
if (!_confirmed)
spentToday -= txn.value;
}
}
}
/*
* Internal functions
*/
/// @dev Returns if amount is within daily limit and resets spentToday after one day.
/// @param amount Amount to withdraw.
/// @return Returns if amount is under daily limit.
function isUnderLimit(uint amount)
internal
returns (bool)
{
if (now > lastDay + 24 hours) {
lastDay = now;
spentToday = 0;
}
if (spentToday + amount > dailyLimit || spentToday + amount < spentToday)
return false;
return true;
}
/*
* Web3 call functions
*/
/// @dev Returns maximum withdraw amount.
/// @return Returns amount.
function calcMaxWithdraw()
public
constant
returns (uint)
{
if (now > lastDay + 24 hours)
return dailyLimit;
if (dailyLimit < spentToday)
return 0;
return dailyLimit - spentToday;
}
}
pragma solidity ^0.4.15;
contract Factory {
/*
* Events
*/
event ContractInstantiation(address sender, address instantiation);
/*
* Storage
*/
mapping(address => bool) public isInstantiation;
mapping(address => address[]) public instantiations;
/*
* Public functions
*/
/// @dev Returns number of instantiations by creator.
/// @param creator Contract creator.
/// @return Returns number of instantiations by creator.
function getInstantiationCount(address creator)
public
constant
returns (uint)
{
return instantiations[creator].length;
}
/*
* Internal functions
*/
/// @dev Registers contract in factory registry.
/// @param instantiation Address of contract instantiation.
function register(address instantiation)
internal
{
isInstantiation[instantiation] = true;
instantiations[msg.sender].push(instantiation);
ContractInstantiation(msg.sender, instantiation);
}
}
/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWallet {
/*
* Events
*/
event Confirmation(address indexed sender, uint indexed transactionId);
event Revocation(address indexed sender, uint indexed transactionId);
event Submission(uint indexed transactionId);
event Execution(uint indexed transactionId);
event ExecutionFailure(uint indexed transactionId);
event Deposit(address indexed sender, uint value);
event OwnerAddition(address indexed owner);
event OwnerRemoval(address indexed owner);
event RequirementChange(uint required);
/*
* Constants
*/
uint constant public MAX_OWNER_COUNT = 50;
/*
* Storage
*/
mapping (uint => Transaction) public transactions;
mapping (uint => mapping (address => bool)) public confirmations;
mapping (address => bool) public isOwner;
address[] public owners;
uint public required;
uint public transactionCount;
struct Transaction {
address destination;
uint value;
bytes data;
bool executed;
}
/*
* Modifiers
*/
modifier onlyWallet() {
require(msg.sender == address(this));
_;
}
modifier ownerDoesNotExist(address owner) {
require(!isOwner[owner]);
_;
}
modifier ownerExists(address owner) {
require(isOwner[owner]);
_;
}
modifier transactionExists(uint transactionId) {
require(transactions[transactionId].destination != 0);
_;
}
modifier confirmed(uint transactionId, address owner) {
require(confirmations[transactionId][owner]);
_;
}
modifier notConfirmed(uint transactionId, address owner) {
require(!confirmations[transactionId][owner]);
_;
}
modifier notExecuted(uint transactionId) {
require(!transactions[transactionId].executed);
_;
}
modifier notNull(address _address) {
require(_address != 0);
_;
}
modifier validRequirement(uint ownerCount, uint _required) {
require(ownerCount <= MAX_OWNER_COUNT
&& _required <= ownerCount
&& _required != 0
&& ownerCount != 0);
_;
}
/// @dev Fallback function allows to deposit ether.
function()
payable
{
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners and required number of confirmations.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
function MultiSigWallet(address[] _owners, uint _required)
public
validRequirement(_owners.length, _required)
{
for (uint i=0; i<_owners.length; i++) {
require(!isOwner[_owners[i]] && _owners[i] != 0);
isOwner[_owners[i]] = true;
}
owners = _owners;
required = _required;
}
/// @dev Allows to add a new owner. Transaction has to be sent by wallet.
/// @param owner Address of new owner.
function addOwner(address owner)
public
onlyWallet
ownerDoesNotExist(owner)
notNull(owner)
validRequirement(owners.length + 1, required)
{
isOwner[owner] = true;
owners.push(owner);
OwnerAddition(owner);
}
/// @dev Allows to remove an owner. Transaction has to be sent by wallet.
/// @param owner Address of owner.
function removeOwner(address owner)
public
onlyWallet
ownerExists(owner)
{
isOwner[owner] = false;
for (uint i=0; i<owners.length - 1; i++)
if (owners[i] == owner) {
owners[i] = owners[owners.length - 1];
break;
}
owners.length -= 1;
if (required > owners.length)
changeRequirement(owners.length);
OwnerRemoval(owner);
}
/// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
/// @param owner Address of owner to be replaced.
/// @param newOwner Address of new owner.
function replaceOwner(address owner, address newOwner)
public
onlyWallet
ownerExists(owner)
ownerDoesNotExist(newOwner)
{
for (uint i=0; i<owners.length; i++)
if (owners[i] == owner) {
owners[i] = newOwner;
break;
}
isOwner[owner] = false;
isOwner[newOwner] = true;
OwnerRemoval(owner);
OwnerAddition(newOwner);
}
/// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
/// @param _required Number of required confirmations.
function changeRequirement(uint _required)
public
onlyWallet
validRequirement(owners.length, _required)
{
required = _required;
RequirementChange(_required);
}
/// @dev Allows an owner to submit and confirm a transaction.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function submitTransaction(address destination, uint value, bytes data)
public
returns (uint transactionId)
{
transactionId = addTransaction(destination, value, data);
confirmTransaction(transactionId);
}
/// @dev Allows an owner to confirm a transaction.
/// @param transactionId Transaction ID.
function confirmTransaction(uint transactionId)
public
ownerExists(msg.sender)
transactionExists(transactionId)
notConfirmed(transactionId, msg.sender)
{
confirmations[transactionId][msg.sender] = true;
Confirmation(msg.sender, transactionId);
executeTransaction(transactionId);
}
/// @dev Allows an owner to revoke a confirmation for a transaction.
/// @param transactionId Transaction ID.
function revokeConfirmation(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
confirmations[transactionId][msg.sender] = false;
Revocation(msg.sender, transactionId);
}
/// @dev Allows anyone to execute a confirmed transaction.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
if (isConfirmed(transactionId)) {
Transaction storage txn = transactions[transactionId];
txn.executed = true;
if (external_call(txn.destination, txn.value, txn.data.length, txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
}
}
}
// call has been separated into its own function in order to take advantage
// of the Solidity's code generator to produce a loop that copies tx.data into memory.
function external_call(address destination, uint value, uint dataLength, bytes data) private returns (bool) {
bool result;
assembly {
let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
result := call(
sub(gas, 34710), // 34710 is the value that solidity is currently emitting
// It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
// callNewAccountGas (25000, in case the destination address does not exist and needs creating)
destination,
value,
d,
dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
x,
0 // Output is ignored, therefore the output size is zero
)
}
return result;
}
/// @dev Returns the confirmation status of a transaction.
/// @param transactionId Transaction ID.
/// @return Confirmation status.
function isConfirmed(uint transactionId)
public
constant
returns (bool)
{
uint count = 0;
for (uint i=0; i<owners.length; i++) {
if (confirmations[transactionId][owners[i]])
count += 1;
if (count == required)
return true;
}
}
/*
* Internal functions
*/
/// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
/// @param destination Transaction target address.
/// @param value Transaction ether value.
/// @param data Transaction data payload.
/// @return Returns transaction ID.
function addTransaction(address destination, uint value, bytes data)
internal
notNull(destination)
returns (uint transactionId)
{
transactionId = transactionCount;
transactions[transactionId] = Transaction({
destination: destination,
value: value,
data: data,
executed: false
});
transactionCount += 1;
Submission(transactionId);
}
/*
* Web3 call functions
*/
/// @dev Returns number of confirmations of a transaction.
/// @param transactionId Transaction ID.
/// @return Number of confirmations.
function getConfirmationCount(uint transactionId)
public
constant
returns (uint count)
{
for (uint i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]])
count += 1;
}
/// @dev Returns total number of transactions after filers are applied.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Total number of transactions after filters are applied.
function getTransactionCount(bool pending, bool executed)
public
constant
returns (uint count)
{
for (uint i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
count += 1;
}
/// @dev Returns list of owners.
/// @return List of owner addresses.
function getOwners()
public
constant
returns (address[])
{
return owners;
}
/// @dev Returns array with owner addresses, which confirmed transaction.
/// @param transactionId Transaction ID.
/// @return Returns array of owner addresses.
function getConfirmations(uint transactionId)
public
constant
returns (address[] _confirmations)
{
address[] memory confirmationsTemp = new address[](owners.length);
uint count = 0;
uint i;
for (i=0; i<owners.length; i++)
if (confirmations[transactionId][owners[i]]) {
confirmationsTemp[count] = owners[i];
count += 1;
}
_confirmations = new address[](count);
for (i=0; i<count; i++)
_confirmations[i] = confirmationsTemp[i];
}
/// @dev Returns list of transaction IDs in defined range.
/// @param from Index start position of transaction array.
/// @param to Index end position of transaction array.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Returns array of transaction IDs.
function getTransactionIds(uint from, uint to, bool pending, bool executed)
public
constant
returns (uint[] _transactionIds)
{
uint[] memory transactionIdsTemp = new uint[](transactionCount);
uint count = 0;
uint i;
for (i=0; i<transactionCount; i++)
if ( pending && !transactions[i].executed
|| executed && transactions[i].executed)
{
transactionIdsTemp[count] = i;
count += 1;
}
_transactionIds = new uint[](to - from);
for (i=from; i<to; i++)
_transactionIds[i - from] = transactionIdsTemp[i];
}
}
/// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWalletWithDailyLimit is MultiSigWallet {
/*
* Events
*/
event DailyLimitChange(uint dailyLimit);
/*
* Storage
*/
uint public dailyLimit;
uint public lastDay;
uint public spentToday;
/*
* Public functions
*/
/// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
/// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis.
function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit)
public
MultiSigWallet(_owners, _required)
{
dailyLimit = _dailyLimit;
}
/// @dev Allows to change the daily limit. Transaction has to be sent by wallet.
/// @param _dailyLimit Amount in wei.
function changeDailyLimit(uint _dailyLimit)
public
onlyWallet
{
dailyLimit = _dailyLimit;
DailyLimitChange(_dailyLimit);
}
/// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached.
/// @param transactionId Transaction ID.
function executeTransaction(uint transactionId)
public
ownerExists(msg.sender)
confirmed(transactionId, msg.sender)
notExecuted(transactionId)
{
Transaction storage txn = transactions[transactionId];
bool _confirmed = isConfirmed(transactionId);
if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) {
txn.executed = true;
if (!_confirmed)
spentToday += txn.value;
if (txn.destination.call.value(txn.value)(txn.data))
Execution(transactionId);
else {
ExecutionFailure(transactionId);
txn.executed = false;
if (!_confirmed)
spentToday -= txn.value;
}
}
}
/*
* Internal functions
*/
/// @dev Returns if amount is within daily limit and resets spentToday after one day.
/// @param amount Amount to withdraw.
/// @return Returns if amount is under daily limit.
function isUnderLimit(uint amount)
internal
returns (bool)
{
if (now > lastDay + 24 hours) {
lastDay = now;
spentToday = 0;
}
if (spentToday + amount > dailyLimit || spentToday + amount < spentToday)
return false;
return true;
}
/*
* Web3 call functions
*/
/// @dev Returns maximum withdraw amount.
/// @return Returns amount.
function calcMaxWithdraw()
public
constant
returns (uint)
{
if (now > lastDay + 24 hours)
return dailyLimit;
if (dailyLimit < spentToday)
return 0;
return dailyLimit - spentToday;
}
}
/// @title Multisignature wallet factory for daily limit version - Allows creation of multisig wallet.
/// @author Stefan George - <stefan.george@consensys.net>
contract MultiSigWalletWithDailyLimitFactory is Factory {
/*
* Public functions
*/
/// @dev Allows verified creation of multisignature wallet.
/// @param _owners List of initial owners.
/// @param _required Number of required confirmations.
/// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis.
/// @return Returns wallet address.
function create(address[] _owners, uint _required, uint _dailyLimit)
public
returns (address wallet)
{
wallet = new MultiSigWalletWithDailyLimit(_owners, _required, _dailyLimit);
register(wallet);
}
}
pragma solidity 0.4.24;
contract ValueOracleWithStorage {
bytes32 internal constant CURRENT_VALUE_STORAGE_KEY = 0xb53505488b5116d818428cffa63375d1511b19b5ec0c8f3968d4ca4d7b9dfe44; // keccak256(abi.encodePacked("currentValue"))
function setCurrentValue() public view returns (uint256);
function getCurrentValue() public view returns (uint256);
}
contract FixedCostOracleWithStorage {
bytes32 internal constant FIXED_COST_STORAGE_KEY = 0xaa468934eae6e639930f40053c6ca2fd7aec4efd6c2051480f1a6a1b61877bff; // keccak256(abi.encodePacked("fixedCost"))
function setFixedCost() public view returns (uint256);
function getFixedCost() public view returns (uint256);
function cost() public view returns(uint);
function costCalculated(uint _fixedFee) public view returns(uint);
function costRelative(uint _amount) public view returns(uint);
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// StablePriceOracle sets a price in USD, based on an oracle.
contract FixedCostOracle is Ownable, EternalStorage, FixedCostOracleWithStorage, ValueOracleWithStorage {
using SafeMath for *;
event OracleChanged(address oracle);
event FixedCostChanged(uint cost);
event CurrentValueChanged(uint currentValue);
constructor(
uint256 _currentValue,
uint256 _fixedCost
)
public
{
setCurrentValue(_currentValue);
setFixedCost(_fixedCost);
}
function setCurrentValue(uint256 _value)
public
onlyOwner
{
uintStorage[CURRENT_VALUE_STORAGE_KEY] = _value;
emit CurrentValueChanged(_value);
}
function setFixedCost(uint _fixedCost)
public
onlyOwner
{
uintStorage[FIXED_COST_STORAGE_KEY] = _fixedCost;
emit FixedCostChanged(_fixedCost);
}
function getCurrentValue()
public
view
onlyOwner
returns(uint256)
{
return uintStorage[CURRENT_VALUE_STORAGE_KEY];
}
function getFixedCost()
public
view
onlyOwner
returns(uint256)
{
return uintStorage[FIXED_COST_STORAGE_KEY];
}
function cost() view public returns(uint) {
// Price of one ether in attodollars
uint ethPrice = getCurrentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return getFixedCost().mul(1e18).div(ethPrice);
}
function costCalculated(uint _fixedFee) view public returns(uint) {
// Price of one ether in attodollars
uint ethPrice = getCurrentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return _fixedFee.mul(1e18).div(ethPrice);
}
function costRelative(uint256 _amount) view public returns(uint) {
// Price of one ether in attodollars
uint ethPrice = getCurrentValue();
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
uint ethCost = getFixedCost().mul(1e18).div(ethPrice);
return ethCost.mul(1e18).div(_amount);
}
}
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
interface IRewardableValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
function validatorList() external view returns (address[]);
function getValidatorRewardAddress(address _validator) external view returns (address);
function validatorCount() external view returns (uint256);
function getNextValidator(address _address) external view returns (address);
}
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
contract BaseFeeManager is EternalStorage, FeeTypes {
using SafeMath for uint256;
event HomeFeeUpdated(uint256 fee);
event ForeignFeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage
uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256);
function setHomeFee(uint256 _fee) external;
function setForeignFee(uint256 _fee) external;
function getHomeFee() public view returns (uint256) {
return uintStorage[HOME_FEE_STORAGE_KEY];
}
function getForeignFee() public view returns (uint256) {
return uintStorage[FOREIGN_FEE_STORAGE_KEY];
}
/* solcov ignore next */
function distributeFeeFromAffirmation(uint256 _fee) external;
/* solcov ignore next */
function distributeFeeFromSignatures(uint256 _fee) external;
/* solcov ignore next */
function getFeeManagerMode() external pure returns (bytes4);
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
}
contract ValidatorsFeeManager is BaseFeeManager, ValidatorStorage {
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_HOME = 0x2a11db67c480122765825a7e4bc5428e8b7b9eca0d4e62b91aac194f99edd0d7; // keccak256(abi.encodePacked("reward-transferring-from-home"))
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_FOREIGN = 0xb14796d751eb4f2570065a479f9e526eabeb2077c564c8a1c5ea559883ea2fab; // keccak256(abi.encodePacked("reward-transferring-from-foreign"))
function distributeFeeFromAffirmation(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_FOREIGN);
}
function distributeFeeFromSignatures(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_HOME);
}
function rewardableValidatorContract() internal view returns (IRewardableValidators) {
return IRewardableValidators(addressStorage[VALIDATOR_CONTRACT]);
}
function distributeFeeProportionally(uint256 _fee, bytes32 _direction) internal {
IRewardableValidators validators = rewardableValidatorContract();
// solhint-disable-next-line var-name-mixedcase
address F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 numOfValidators = validators.validatorCount();
uint256 feePerValidator = _fee.div(numOfValidators);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfValidators));
if (diff > 0) {
randomValidatorIndex = random(numOfValidators);
}
address nextValidator = validators.getNextValidator(F_ADDR);
require((nextValidator != F_ADDR) && (nextValidator != address(0)));
uint256 i = 0;
while (nextValidator != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
address rewardAddress = validators.getValidatorRewardAddress(nextValidator);
onFeeDistribution(rewardAddress, feeToDistribute, _direction);
nextValidator = validators.getNextValidator(nextValidator);
require(nextValidator != address(0));
i = i + 1;
}
}
function onFeeDistribution(address _rewardAddress, uint256 _fee, bytes32 _direction) internal {
if (_direction == REWARD_FOR_TRANSFERRING_FROM_FOREIGN) {
onAffirmationFeeDistribution(_rewardAddress, _fee);
} else {
onSignatureFeeDistribution(_rewardAddress, _fee);
}
}
/* solcov ignore next */
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal;
/* solcov ignore next */
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}
contract OraclizedFixedFeeManagerNativeToErcBothDirections is ValidatorsFeeManager, FixedCostOracle {
constructor(
uint256 _currentValue,
uint256 _fixedCost
)
public
FixedCostOracle(
_currentValue,
_fixedCost
)
{
}
function setHomeFee(uint256 _fee) external {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
emit HomeFeeUpdated(_fee);
}
function setForeignFee(uint256 _fee) external {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
emit ForeignFeeUpdated(_fee);
}
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 _fixedCost = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
uint256 _fee = costCalculated(_fixedCost);
require(_value >= _fee);
return _fee;
}
function getFeeManagerMode() external pure returns (bytes4) {
return 0xd7de965f; // bytes4(keccak256(abi.encodePacked("manages-both-directions")))
}
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function _sendReward(address _rewardAddress, uint256 _fee) internal {
if (!_rewardAddress.send(_fee)) {
(new Sacrifice).value(_fee)(_rewardAddress);
}
}
}
// File: contracts/interfaces/IBridgeValidators.sol
pragma solidity 0.4.24;
interface IBridgeValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
}
// File: contracts/libraries/Message.sol
pragma solidity 0.4.24;
library Message {
// function uintToString(uint256 inputValue) internal pure returns (string) {
// // figure out the length of the resulting string
// uint256 length = 0;
// uint256 currentValue = inputValue;
// do {
// length++;
// currentValue /= 10;
// } while (currentValue != 0);
// // allocate enough memory
// bytes memory result = new bytes(length);
// // construct the string backwards
// uint256 i = length - 1;
// currentValue = inputValue;
// do {
// result[i--] = byte(48 + currentValue % 10);
// currentValue /= 10;
// } while (currentValue != 0);
// return string(result);
// }
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
for (uint256 i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
// layout of message :: bytes:
// offset 0: 32 bytes :: uint256 - message length
// offset 32: 20 bytes :: address - recipient address
// offset 52: 32 bytes :: uint256 - value
// offset 84: 32 bytes :: bytes32 - transaction hash
// offset 104: 20 bytes :: address - contract address to prevent double spending
// mload always reads 32 bytes.
// so we can and have to start reading recipient at offset 20 instead of 32.
// if we were to read at 32 the address would contain part of value and be corrupted.
// when reading from offset 20 mload will read 12 bytes (most of them zeros) followed
// by the 20 recipient address bytes and correctly convert it into an address.
// this saves some storage/gas over the alternative solution
// which is padding address to 32 bytes and reading recipient at offset 32.
// for more details see discussion in:
// https://github.com/paritytech/parity-bridge/issues/61
function parseMessage(bytes message)
internal
pure
returns (address recipient, uint256 amount, bytes32 txHash, address contractAddress)
{
require(isMessageValid(message));
assembly {
recipient := mload(add(message, 20))
amount := mload(add(message, 52))
txHash := mload(add(message, 84))
contractAddress := mload(add(message, 104))
}
}
function isMessageValid(bytes _msg) internal pure returns (bool) {
return _msg.length == requiredMessageLength();
}
function requiredMessageLength() internal pure returns (uint256) {
return 104;
}
function recoverAddressFromSignedMessage(bytes signature, bytes message, bool isAMBMessage)
internal
pure
returns (address)
{
require(signature.length == 65);
bytes32 r;
bytes32 s;
bytes1 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := mload(add(signature, 0x60))
}
return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
}
function hashMessage(bytes message, bool isAMBMessage) internal pure returns (bytes32) {
bytes memory prefix = "\x19Ethereum Signed Message:\n";
if (isAMBMessage) {
return keccak256(abi.encodePacked(prefix, uintToString(message.length), message));
} else {
string memory msgLength = "104";
return keccak256(abi.encodePacked(prefix, msgLength, message));
}
}
function hasEnoughValidSignatures(
bytes _message,
uint8[] _vs,
bytes32[] _rs,
bytes32[] _ss,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
// It is not necessary to check that arrays have the same length since it will be handled
// during attempt to access to the corresponding elements in the loop and the call will be reverted.
// It will save gas for the rational validators actions and still be safe enough from security point of view
require(_vs.length >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
address recoveredAddress = ecrecover(hash, _vs[i], _rs[i], _ss[i]);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function hasEnoughValidSignatures(
bytes _message,
bytes _signatures,
IBridgeValidators _validatorContract,
bool isAMBMessage
) internal view {
require(isAMBMessage || (!isAMBMessage && isMessageValid(_message)));
uint256 requiredSignatures = _validatorContract.requiredSignatures();
uint256 amount;
assembly {
amount := and(mload(add(_signatures, 1)), 0xff)
}
require(amount >= requiredSignatures);
bytes32 hash = hashMessage(_message, isAMBMessage);
address[] memory encounteredAddresses = new address[](requiredSignatures);
for (uint256 i = 0; i < requiredSignatures; i++) {
uint8 v;
bytes32 r;
bytes32 s;
uint256 posr = 33 + amount + 32 * i;
uint256 poss = posr + 32 * amount;
assembly {
v := mload(add(_signatures, add(2, i)))
r := mload(add(_signatures, posr))
s := mload(add(_signatures, poss))
}
address recoveredAddress = ecrecover(hash, v, r, s);
require(_validatorContract.isValidator(recoveredAddress));
require(!addressArrayContains(encounteredAddresses, recoveredAddress));
encounteredAddresses[i] = recoveredAddress;
}
}
function uintToString(uint256 i) internal pure returns (string) {
if (i == 0) return "0";
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
bstr[k--] = bytes1(48 + (i % 10));
i /= 10;
}
return string(bstr);
}
}
// File: contracts/upgradeability/EternalStorage.sol
pragma solidity 0.4.24;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
pragma solidity ^0.4.24;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* See https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address _owner, address _spender)
public view returns (uint256);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
function approve(address _spender, uint256 _value) public returns (bool);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
// File: contracts/upgradeable_contracts/ValidatorStorage.sol
pragma solidity 0.4.24;
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
// File: contracts/upgradeable_contracts/Validatable.sol
pragma solidity 0.4.24;
contract Validatable is EternalStorage, ValidatorStorage {
function validatorContract() public view returns (IBridgeValidators) {
return IBridgeValidators(addressStorage[VALIDATOR_CONTRACT]);
}
modifier onlyValidator() {
require(validatorContract().isValidator(msg.sender));
/* solcov ignore next */
_;
}
function requiredSignatures() public view returns (uint256) {
return validatorContract().requiredSignatures();
}
}
// File: contracts/interfaces/IUpgradeabilityOwnerStorage.sol
pragma solidity 0.4.24;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}
// File: contracts/upgradeable_contracts/Upgradeable.sol
pragma solidity 0.4.24;
contract Upgradeable {
// Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract
modifier onlyIfUpgradeabilityOwner() {
require(msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner());
/* solcov ignore next */
_;
}
}
// File: contracts/upgradeable_contracts/Initializable.sol
pragma solidity 0.4.24;
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}
// File: contracts/upgradeable_contracts/InitializableBridge.sol
pragma solidity 0.4.24;
contract InitializableBridge is Initializable {
bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
function deployedAtBlock() external view returns (uint256) {
return uintStorage[DEPLOYED_AT_BLOCK];
}
}
// File: openzeppelin-solidity/contracts/AddressUtils.sol
pragma solidity ^0.4.24;
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param _addr address to check
* @return whether the target address is a contract
*/
function isContract(address _addr) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(_addr) }
return size > 0;
}
}
// File: contracts/upgradeable_contracts/Ownable.sol
pragma solidity 0.4.24;
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
/* solcov ignore next */
_;
}
/**
* @dev Throws if called by any account other than contract itself or owner.
*/
modifier onlyRelevantSender() {
// proxy owner if used through proxy, address(0) otherwise
require(
!address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
/* solcov ignore next */
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}
// File: contracts/upgradeable_contracts/Sacrifice.sol
pragma solidity 0.4.24;
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
// File: contracts/upgradeable_contracts/Claimable.sol
pragma solidity 0.4.24;
contract Claimable {
bytes4 internal constant TRANSFER = 0xa9059cbb; // transfer(address,uint256)
modifier validAddress(address _to) {
require(_to != address(0));
/* solcov ignore next */
_;
}
function claimValues(address _token, address _to) internal {
if (_token == address(0)) {
claimNativeCoins(_to);
} else {
claimErc20Tokens(_token, _to);
}
}
function claimNativeCoins(address _to) internal {
uint256 value = address(this).balance;
if (!_to.send(value)) {
(new Sacrifice).value(value)(_to);
}
}
function claimErc20Tokens(address _token, address _to) internal {
ERC20Basic token = ERC20Basic(_token);
uint256 balance = token.balanceOf(this);
safeTransfer(_token, _to, balance);
}
function safeTransfer(address _token, address _to, uint256 _value) internal {
bytes memory returnData;
bool returnDataResult;
bytes memory callData = abi.encodeWithSelector(TRANSFER, _to, _value);
assembly {
let result := call(gas, _token, 0x0, add(callData, 0x20), mload(callData), 0, 32)
returnData := mload(0)
returnDataResult := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
// Return data is optional
if (returnData.length > 0) {
require(returnDataResult);
}
}
}
// File: contracts/upgradeable_contracts/VersionableBridge.sol
pragma solidity 0.4.24;
contract VersionableBridge {
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (2, 5, 0);
}
/* solcov ignore next */
function getBridgeMode() external pure returns (bytes4);
}
// File: contracts/upgradeable_contracts/BasicBridge.sol
pragma solidity 0.4.24;
contract BasicBridge is InitializableBridge, Validatable, Ownable, Upgradeable, Claimable, VersionableBridge {
event GasPriceChanged(uint256 gasPrice);
event RequiredBlockConfirmationChanged(uint256 requiredBlockConfirmations);
bytes32 internal constant GAS_PRICE = 0x55b3774520b5993024893d303890baa4e84b1244a43c60034d1ced2d3cf2b04b; // keccak256(abi.encodePacked("gasPrice"))
bytes32 internal constant REQUIRED_BLOCK_CONFIRMATIONS = 0x916daedf6915000ff68ced2f0b6773fe6f2582237f92c3c95bb4d79407230071; // keccak256(abi.encodePacked("requiredBlockConfirmations"))
function setGasPrice(uint256 _gasPrice) external onlyOwner {
require(_gasPrice > 0);
uintStorage[GAS_PRICE] = _gasPrice;
emit GasPriceChanged(_gasPrice);
}
function gasPrice() external view returns (uint256) {
return uintStorage[GAS_PRICE];
}
function setRequiredBlockConfirmations(uint256 _blockConfirmations) external onlyOwner {
require(_blockConfirmations > 0);
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _blockConfirmations;
emit RequiredBlockConfirmationChanged(_blockConfirmations);
}
function requiredBlockConfirmations() external view returns (uint256) {
return uintStorage[REQUIRED_BLOCK_CONFIRMATIONS];
}
function claimTokens(address _token, address _to) public onlyIfUpgradeabilityOwner validAddress(_to) {
claimValues(_token, _to);
}
}
// File: contracts/upgradeable_contracts/BasicTokenBridge.sol
pragma solidity 0.4.24;
contract BasicTokenBridge is EternalStorage, Ownable {
using SafeMath for uint256;
event DailyLimitChanged(uint256 newLimit);
event ExecutionDailyLimitChanged(uint256 newLimit);
bytes32 internal constant MIN_PER_TX = 0xbbb088c505d18e049d114c7c91f11724e69c55ad6c5397e2b929e68b41fa05d1; // keccak256(abi.encodePacked("minPerTx"))
bytes32 internal constant MAX_PER_TX = 0x0f8803acad17c63ee38bf2de71e1888bc7a079a6f73658e274b08018bea4e29c; // keccak256(abi.encodePacked("maxPerTx"))
bytes32 internal constant DAILY_LIMIT = 0x4a6a899679f26b73530d8cf1001e83b6f7702e04b6fdb98f3c62dc7e47e041a5; // keccak256(abi.encodePacked("dailyLimit"))
bytes32 internal constant EXECUTION_MAX_PER_TX = 0xc0ed44c192c86d1cc1ba51340b032c2766b4a2b0041031de13c46dd7104888d5; // keccak256(abi.encodePacked("executionMaxPerTx"))
bytes32 internal constant EXECUTION_DAILY_LIMIT = 0x21dbcab260e413c20dc13c28b7db95e2b423d1135f42bb8b7d5214a92270d237; // keccak256(abi.encodePacked("executionDailyLimit"))
bytes32 internal constant DECIMAL_SHIFT = 0x1e8ecaafaddea96ed9ac6d2642dcdfe1bebe58a930b1085842d8fc122b371ee5; // keccak256(abi.encodePacked("decimalShift"))
function totalSpentPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))];
}
function totalExecutedPerDay(uint256 _day) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))];
}
function dailyLimit() public view returns (uint256) {
return uintStorage[DAILY_LIMIT];
}
function executionDailyLimit() public view returns (uint256) {
return uintStorage[EXECUTION_DAILY_LIMIT];
}
function maxPerTx() public view returns (uint256) {
return uintStorage[MAX_PER_TX];
}
function executionMaxPerTx() public view returns (uint256) {
return uintStorage[EXECUTION_MAX_PER_TX];
}
function minPerTx() public view returns (uint256) {
return uintStorage[MIN_PER_TX];
}
function decimalShift() public view returns (uint256) {
return uintStorage[DECIMAL_SHIFT];
}
function withinLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalSpentPerDay(getCurrentDay()).add(_amount);
return dailyLimit() >= nextLimit && _amount <= maxPerTx() && _amount >= minPerTx();
}
function withinExecutionLimit(uint256 _amount) public view returns (bool) {
uint256 nextLimit = totalExecutedPerDay(getCurrentDay()).add(_amount);
return executionDailyLimit() >= nextLimit && _amount <= executionMaxPerTx();
}
function getCurrentDay() public view returns (uint256) {
// solhint-disable-next-line not-rely-on-time
return now / 1 days;
}
function setTotalSpentPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalSpentPerDay", _day))] = _value;
}
function setTotalExecutedPerDay(uint256 _day, uint256 _value) internal {
uintStorage[keccak256(abi.encodePacked("totalExecutedPerDay", _day))] = _value;
}
function setDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > maxPerTx() || _dailyLimit == 0);
uintStorage[DAILY_LIMIT] = _dailyLimit;
emit DailyLimitChanged(_dailyLimit);
}
function setExecutionDailyLimit(uint256 _dailyLimit) external onlyOwner {
require(_dailyLimit > executionMaxPerTx() || _dailyLimit == 0);
uintStorage[EXECUTION_DAILY_LIMIT] = _dailyLimit;
emit ExecutionDailyLimitChanged(_dailyLimit);
}
function setExecutionMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx < executionDailyLimit());
uintStorage[EXECUTION_MAX_PER_TX] = _maxPerTx;
}
function setMaxPerTx(uint256 _maxPerTx) external onlyOwner {
require(_maxPerTx == 0 || (_maxPerTx > minPerTx() && _maxPerTx < dailyLimit()));
uintStorage[MAX_PER_TX] = _maxPerTx;
}
function setMinPerTx(uint256 _minPerTx) external onlyOwner {
require(_minPerTx > 0 && _minPerTx < dailyLimit() && _minPerTx < maxPerTx());
uintStorage[MIN_PER_TX] = _minPerTx;
}
}
// File: contracts/upgradeable_contracts/BasicHomeBridge.sol
pragma solidity 0.4.24;
contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge {
using SafeMath for uint256;
event UserRequestForSignature(address recipient, uint256 value);
event AffirmationCompleted(address recipient, uint256 value, bytes32 transactionHash);
event SignedForUserRequest(address indexed signer, bytes32 messageHash);
event SignedForAffirmation(address indexed signer, bytes32 transactionHash);
event CollectedSignatures(
address authorityResponsibleForRelay,
bytes32 messageHash,
uint256 NumberOfCollectedSignatures
);
function executeAffirmation(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
if (withinExecutionLimit(value)) {
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
// Duplicated affirmations
require(!affirmationsSigned(hashSender));
setAffirmationsSigned(hashSender, true);
uint256 signed = numAffirmationsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
setNumAffirmationsSigned(hashMsg, signed);
emit SignedForAffirmation(msg.sender, transactionHash);
if (signed >= requiredSignatures()) {
// If the bridge contract does not own enough tokens to transfer
// it will couse funds lock on the home side of the bridge
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
if (value > 0) {
require(onExecuteAffirmation(recipient, value, transactionHash));
}
emit AffirmationCompleted(recipient, value, transactionHash);
}
} else {
onFailedAffirmation(recipient, value, transactionHash);
}
}
function submitSignature(bytes signature, bytes message) external onlyValidator {
// ensure that `signature` is really `message` signed by `msg.sender`
require(Message.isMessageValid(message));
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message, false));
bytes32 hashMsg = keccak256(abi.encodePacked(message));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
uint256 signed = numMessagesSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
if (signed > 1) {
// Duplicated signatures
require(!messagesSigned(hashSender));
} else {
setMessages(hashMsg, message);
}
setMessagesSigned(hashSender, true);
bytes32 signIdx = keccak256(abi.encodePacked(hashMsg, (signed - 1)));
setSignatures(signIdx, signature);
setNumMessagesSigned(hashMsg, signed);
emit SignedForUserRequest(msg.sender, hashMsg);
uint256 reqSigs = requiredSignatures();
if (signed >= reqSigs) {
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
emit CollectedSignatures(msg.sender, hashMsg, reqSigs);
onSignaturesCollected(message);
}
}
function setMessagesSigned(bytes32 _hash, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("messagesSigned", _hash))] = _status;
}
/* solcov ignore next */
function onExecuteAffirmation(address, uint256, bytes32) internal returns (bool);
/* solcov ignore next */
function onSignaturesCollected(bytes) internal;
function numAffirmationsSigned(bytes32 _withdrawal) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))];
}
function setAffirmationsSigned(bytes32 _withdrawal, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))] = _status;
}
function setNumAffirmationsSigned(bytes32 _withdrawal, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))] = _number;
}
function affirmationsSigned(bytes32 _withdrawal) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))];
}
function signature(bytes32 _hash, uint256 _index) external view returns (bytes) {
bytes32 signIdx = keccak256(abi.encodePacked(_hash, _index));
return bytesStorage[keccak256(abi.encodePacked("signatures", signIdx))];
}
function messagesSigned(bytes32 _message) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("messagesSigned", _message))];
}
function setSignatures(bytes32 _hash, bytes _signature) internal {
bytesStorage[keccak256(abi.encodePacked("signatures", _hash))] = _signature;
}
function setMessages(bytes32 _hash, bytes _message) internal {
bytesStorage[keccak256(abi.encodePacked("messages", _hash))] = _message;
}
function message(bytes32 _hash) external view returns (bytes) {
return bytesStorage[keccak256(abi.encodePacked("messages", _hash))];
}
function setNumMessagesSigned(bytes32 _message, uint256 _number) internal {
uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))] = _number;
}
function markAsProcessed(uint256 _v) internal pure returns (uint256) {
return _v | (2**255);
}
function isAlreadyProcessed(uint256 _number) public pure returns (bool) {
return _number & (2**255) == 2**255;
}
function numMessagesSigned(bytes32 _message) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))];
}
function requiredMessageLength() public pure returns (uint256) {
return Message.requiredMessageLength();
}
/* solcov ignore next */
function onFailedAffirmation(address, uint256, bytes32) internal;
}
// File: contracts/upgradeable_contracts/FeeTypes.sol
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
// File: contracts/upgradeable_contracts/RewardableBridge.sol
pragma solidity 0.4.24;
contract RewardableBridge is Ownable, FeeTypes {
event FeeDistributedFromAffirmation(uint256 feeAmount, bytes32 indexed transactionHash);
event FeeDistributedFromSignatures(uint256 feeAmount, bytes32 indexed transactionHash);
bytes32 internal constant FEE_MANAGER_CONTRACT = 0x779a349c5bee7817f04c960f525ee3e2f2516078c38c68a3149787976ee837e5; // keccak256(abi.encodePacked("feeManagerContract"))
bytes4 internal constant GET_HOME_FEE = 0x94da17cd; // getHomeFee()
bytes4 internal constant GET_FOREIGN_FEE = 0xffd66196; // getForeignFee()
bytes4 internal constant GET_FEE_MANAGER_MODE = 0xf2ba9561; // getFeeManagerMode()
bytes4 internal constant SET_HOME_FEE = 0x34a9e148; // setHomeFee(uint256)
bytes4 internal constant SET_FOREIGN_FEE = 0x286c4066; // setForeignFee(uint256)
bytes4 internal constant CALCULATE_FEE = 0x9862f26f; // calculateFee(uint256,bool,bytes32)
bytes4 internal constant DISTRIBUTE_FEE_FROM_SIGNATURES = 0x59d78464; // distributeFeeFromSignatures(uint256)
bytes4 internal constant DISTRIBUTE_FEE_FROM_AFFIRMATION = 0x054d46ec; // distributeFeeFromAffirmation(uint256)
function _getFee(bytes32 _feeType) internal view returns (uint256) {
uint256 fee;
address feeManager = feeManagerContract();
bytes4 method = _feeType == HOME_FEE ? GET_HOME_FEE : GET_FOREIGN_FEE;
bytes memory callData = abi.encodeWithSelector(method);
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function getFeeManagerMode() external view returns (bytes4) {
bytes4 mode;
bytes memory callData = abi.encodeWithSelector(GET_FEE_MANAGER_MODE);
address feeManager = feeManagerContract();
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 4)
mode := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return mode;
}
function feeManagerContract() public view returns (address) {
return addressStorage[FEE_MANAGER_CONTRACT];
}
function setFeeManagerContract(address _feeManager) external onlyOwner {
require(_feeManager == address(0) || AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
}
function _setFee(address _feeManager, uint256 _fee, bytes32 _feeType) internal {
bytes4 method = _feeType == HOME_FEE ? SET_HOME_FEE : SET_FOREIGN_FEE;
require(_feeManager.delegatecall(abi.encodeWithSelector(method, _fee)));
}
function calculateFee(uint256 _value, bool _recover, address _impl, bytes32 _feeType)
internal
view
returns (uint256)
{
uint256 fee;
bytes memory callData = abi.encodeWithSelector(CALCULATE_FEE, _value, _recover, _feeType);
assembly {
let result := callcode(gas, _impl, 0x0, add(callData, 0x20), mload(callData), 0, 32)
fee := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return fee;
}
function distributeFeeFromSignatures(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_SIGNATURES, _fee)));
emit FeeDistributedFromSignatures(_fee, _txHash);
}
function distributeFeeFromAffirmation(uint256 _fee, address _feeManager, bytes32 _txHash) internal {
require(_feeManager.delegatecall(abi.encodeWithSelector(DISTRIBUTE_FEE_FROM_AFFIRMATION, _fee)));
emit FeeDistributedFromAffirmation(_fee, _txHash);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/RewardableHomeBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract RewardableHomeBridgeNativeToErc is RewardableBridge {
function setForeignFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, FOREIGN_FEE);
}
function setHomeFee(uint256 _fee) external onlyOwner {
_setFee(feeManagerContract(), _fee, HOME_FEE);
}
function getForeignFee() public view returns (uint256) {
return _getFee(FOREIGN_FEE);
}
function getHomeFee() public view returns (uint256) {
return _getFee(HOME_FEE);
}
}
// File: contracts/upgradeable_contracts/native_to_erc20/HomeBridgeNativeToErc.sol
pragma solidity 0.4.24;
contract HomeBridgeNativeToErc is EternalStorage, BasicHomeBridge, RewardableHomeBridgeNativeToErc {
function() public payable {
require(msg.data.length == 0);
nativeTransfer(msg.sender);
}
function nativeTransfer(address _receiver) internal {
require(msg.value > 0);
require(withinLimit(msg.value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value));
uint256 valueToTransfer = msg.value;
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, HOME_FEE);
valueToTransfer = valueToTransfer.sub(fee);
}
emit UserRequestForSignature(_receiver, valueToTransfer);
}
function relayTokens(address _receiver) external payable {
nativeTransfer(_receiver);
}
function initialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
uint256 _decimalShift
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_dailyLimitMaxPerTxMinPerTxArray,
_homeGasPrice,
_requiredBlockConfirmations,
_foreignDailyLimitForeignMaxPerTxArray,
_owner,
_decimalShift
);
setInitialize();
return isInitialized();
}
function rewardableInitialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
address _feeManager,
uint256[] _homeFeeForeignFeeArray, // [ 0 = _homeFee, 1 = _foreignFee ]
uint256 _decimalShift
) external onlyRelevantSender returns (bool) {
_initialize(
_validatorContract,
_dailyLimitMaxPerTxMinPerTxArray,
_homeGasPrice,
_requiredBlockConfirmations,
_foreignDailyLimitForeignMaxPerTxArray,
_owner,
_decimalShift
);
require(AddressUtils.isContract(_feeManager));
addressStorage[FEE_MANAGER_CONTRACT] = _feeManager;
_setFee(_feeManager, _homeFeeForeignFeeArray[0], HOME_FEE);
_setFee(_feeManager, _homeFeeForeignFeeArray[1], FOREIGN_FEE);
setInitialize();
return isInitialized();
}
function getBridgeMode() external pure returns (bytes4 _data) {
return 0x92a8d7fe; // bytes4(keccak256(abi.encodePacked("native-to-erc-core")))
}
function _initialize(
address _validatorContract,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256 _homeGasPrice,
uint256 _requiredBlockConfirmations,
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ]
address _owner,
uint256 _decimalShift
) internal {
require(!isInitialized());
require(AddressUtils.isContract(_validatorContract));
require(_homeGasPrice > 0);
require(_requiredBlockConfirmations > 0);
require(
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0
_dailyLimitMaxPerTxMinPerTxArray[1] > _dailyLimitMaxPerTxMinPerTxArray[2] && // _maxPerTx > _minPerTx
_dailyLimitMaxPerTxMinPerTxArray[0] > _dailyLimitMaxPerTxMinPerTxArray[1] // _dailyLimit > _maxPerTx
);
require(_foreignDailyLimitForeignMaxPerTxArray[1] < _foreignDailyLimitForeignMaxPerTxArray[0]); // _foreignMaxPerTx < _foreignDailyLimit
require(_owner != address(0));
addressStorage[VALIDATOR_CONTRACT] = _validatorContract;
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
uintStorage[DAILY_LIMIT] = _dailyLimitMaxPerTxMinPerTxArray[0];
uintStorage[MAX_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[1];
uintStorage[MIN_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[2];
uintStorage[GAS_PRICE] = _homeGasPrice;
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _requiredBlockConfirmations;
uintStorage[EXECUTION_DAILY_LIMIT] = _foreignDailyLimitForeignMaxPerTxArray[0];
uintStorage[EXECUTION_MAX_PER_TX] = _foreignDailyLimitForeignMaxPerTxArray[1];
uintStorage[DECIMAL_SHIFT] = _decimalShift;
setOwner(_owner);
emit RequiredBlockConfirmationChanged(_requiredBlockConfirmations);
emit GasPriceChanged(_homeGasPrice);
emit DailyLimitChanged(_dailyLimitMaxPerTxMinPerTxArray[0]);
emit ExecutionDailyLimitChanged(_foreignDailyLimitForeignMaxPerTxArray[0]);
}
function onSignaturesCollected(bytes _message) internal {
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
address recipient;
uint256 amount;
bytes32 txHash;
address contractAddress;
(recipient, amount, txHash, contractAddress) = Message.parseMessage(_message);
uint256 fee = calculateFee(amount, true, feeManager, HOME_FEE);
if (fee != 0) {
distributeFeeFromSignatures(fee, feeManager, txHash);
}
}
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) {
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_value));
uint256 valueToTransfer = _value.mul(10**decimalShift());
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
valueToTransfer = valueToTransfer.sub(fee);
}
if (!_recipient.send(valueToTransfer)) {
(new Sacrifice).value(valueToTransfer)(_recipient);
}
return true;
}
function onFailedAffirmation(
address, /*_recipient*/
uint256, /*_value*/
bytes32 /*_txHash*/
) internal {
revert();
}
}
contract IValueOracleWithStorage {
bytes32 internal constant CURRENT_VALUE_STORAGE_KEY = 0xb53505488b5116d818428cffa63375d1511b19b5ec0c8f3968d4ca4d7b9dfe44; // keccak256(abi.encodePacked("currentValue"))
function setCurrentValue() public view returns (uint256);
function getCurrentValue() public view returns (uint256);
}
contract IFixedCostOracleWithStorage {
bytes32 internal constant FIXED_COST_STORAGE_KEY = 0xaa468934eae6e639930f40053c6ca2fd7aec4efd6c2051480f1a6a1b61877bff; // keccak256(abi.encodePacked("fixedCost"))
function setFixedCost() public view returns (uint256);
function getFixedCost() public view returns (uint256);
}
contract OraclizedHomeBridgeNativeToErc is HomeBridgeNativeToErc, IValueOracleWithStorage, IFixedCostOracleWithStorage {
function setCurrentValue(uint256 _value)
public
onlyOwner
{
_set(IValueOracleWithStorage(this).setCurrentValue.selector, _value);
}
function setFixedCost(uint _value)
public
onlyOwner
{
_set(IFixedCostOracleWithStorage(this).setFixedCost.selector, _value);
}
function getCurrentValue()
public
view
returns(uint256)
{
return _get(this.getCurrentValue.selector);
}
function getFixedCost()
public
view
returns(uint256)
{
return _get(this.getFixedCost.selector);
}
function _get(bytes4 _method) internal view returns (uint256) {
uint256 _value;
address feeManager = feeManagerContract();
bytes memory callData = abi.encodeWithSelector(_method);
assembly {
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32)
_value := mload(0)
switch result
case 0 {
revert(0, 0)
}
}
return _value;
}
function _set(bytes4 _method, uint256 _value) internal {
address _fContract = feeManagerContract();
require(_fContract != address(0));
require(_fContract.delegatecall(abi.encodeWithSelector(_method, _value)));
}
}
//! The KeyServerSet contract. Owned version with migration support.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
//! The KeyServerSet contract.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
/// Simple key server set.
interface KeyServerSet {
/// Get number of block when current set has been changed last time.
function getCurrentLastChange() external view returns (uint256);
/// Get index of given key server in current set.
function getCurrentKeyServerIndex(address keyServer) external view returns (uint8);
/// Get count of key servers in current set.
function getCurrentKeyServersCount() external view returns (uint8);
/// Get address of key server in current set.
function getCurrentKeyServer(uint8 index) external view returns (address);
/// Get all current key servers.
function getCurrentKeyServers() external view returns (address[] memory);
/// Get current key server public key.
function getCurrentKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get current key server address.
function getCurrentKeyServerAddress(address keyServer) external view returns (string memory);
}
/// Key server set with migration support.
interface KeyServerSetWithMigration {
/// When new server is added to new set.
event KeyServerAdded(address keyServer);
/// When existing server is removed from new set.
event KeyServerRemoved(address keyServer);
/// When migration is started.
event MigrationStarted();
/// When migration is completed.
event MigrationCompleted();
/// Get number of block when current set has been changed last time.
function getCurrentLastChange() external view returns (uint256);
/// Get index of given key server in current set.
function getCurrentKeyServerIndex(address keyServer) external view returns (uint8);
/// Get count of key servers in current set.
function getCurrentKeyServersCount() external view returns (uint8);
/// Get address of key server in current set.
function getCurrentKeyServer(uint8 index) external view returns (address);
/// Get all current key servers.
function getCurrentKeyServers() external view returns (address[] memory);
/// Get current key server public key.
function getCurrentKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get current key server address.
function getCurrentKeyServerAddress(address keyServer) external view returns (string memory);
/// Get all migration key servers.
function getMigrationKeyServers() external view returns (address[] memory);
/// Get migration key server public key.
function getMigrationKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get migration key server address.
function getMigrationKeyServerAddress(address keyServer) external view returns (string memory);
/// Get all new key servers.
function getNewKeyServers() external view returns (address[] memory);
/// Get new key server public key.
function getNewKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get new key server address.
function getNewKeyServerAddress(address keyServer) external view returns (string memory);
/// Get migration id.
function getMigrationId() external view returns (bytes32);
/// Get migration master.
function getMigrationMaster() external view returns (address);
/// Is migration confirmed by given node?
function isMigrationConfirmed(address keyServer) external view returns (bool);
/// Start migration.
function startMigration(bytes32 id) external;
/// Confirm migration.
function confirmMigration(bytes32 id) external;
}
/// Single-owned KeyServerSet with migration support.
contract OwnedKeyServerSetWithMigration is Ownable, KeyServerSetWithMigration {
struct KeyServer {
/// Index in the keyServersList.
uint8 index;
/// Public key of key server.
bytes publicKey;
/// IP address of key server.
string ip;
}
struct Set {
/// Public keys of all active key servers.
address[] list;
/// Mapping public key => server IP address.
mapping(address => KeyServer) map;
}
/// Is initialized.
bool isInitialized;
/// Block at which current
uint256 currentSetChangeBlock;
/// Current key servers set.
Set currentSet;
/// Migration key servers set.
Set migrationSet;
/// New key servers set.
Set newSet;
/// Migration master.
address migrationMaster;
/// Migration id.
bytes32 migrationId;
/// Required migration confirmations.
mapping(address => bool) migrationConfirmations;
modifier notAnEmptyString(string memory value) {
bytes memory castValue = bytes(value);
require(castValue.length != 0, "String is empty.");
_;
}
/// Only if valid public is passed
modifier isValidPublic(bytes memory keyServerPublic) {
require(checkPublic(keyServerPublic), "Public key is not valid.");
_;
}
/// Only run if server is currently on current set.
modifier isOnCurrentSet(address keyServer) {
require(
keccak256(bytes(currentSet.map[keyServer].ip)) != keccak256(""),
"Server is not in the current set."
);
_;
}
/// Only run if server is currently on migration set.
modifier isOnMigrationSet(address keyServer) {
require(
keccak256(bytes(migrationSet.map[keyServer].ip)) != keccak256(""),
"Server is not in the migrations set."
);
_;
}
/// Only run if server is currently on new set.
modifier isOnNewSet(address keyServer) {
require(
keccak256(bytes(newSet.map[keyServer].ip)) != keccak256(""),
"Server is not in the new set."
);
_;
}
/// Only run if server is currently on new set.
modifier isNotOnNewSet(address keyServer) {
require(
keccak256(bytes(newSet.map[keyServer].ip)) == keccak256(""),
"Server is in the new set."
);
_;
}
/// Only when no active migration process.
modifier noActiveMigration {
require(migrationMaster == address(0), "There is an active migration.");
_;
}
/// Only when migration with given id is in progress.
modifier isActiveMigration(bytes32 id) {
require(migrationId == id, "Migration with given id is not active.");
_;
}
/// Only when migration id is valid.
modifier isValidMigrationId(bytes32 id) {
require(id != bytes32(0), "Migration id is not valid.");
_;
}
/// Only when migration is required.
modifier whenMigrationRequired {
require(!areEqualSets(currentSet, newSet), "Migration is not required.");
_;
}
/// Only run when sender is potential participant of migration.
modifier isPossibleMigrationParticipant {
require(
keccak256(bytes(currentSet.map[msg.sender].ip)) != keccak256("") ||
keccak256(bytes(newSet.map[msg.sender].ip)) != keccak256(""),
"Sender is not a potential migration participant.");
_;
}
/// Only run when sender is participant of migration.
modifier isMigrationParticipant(address keyServer) {
require(
keccak256(bytes(currentSet.map[keyServer].ip)) != keccak256("") ||
keccak256(bytes(migrationSet.map[keyServer].ip)) != keccak256(""),
"Server is not a migration participant.");
_;
}
/// We do not support direct payments.
receive() external payable {
revert("Contract does not support direct payments.");
}
/// Get number of block when current set has been changed last time.
function getCurrentLastChange()
external
view
override
returns (uint256)
{
return currentSetChangeBlock;
}
/// Get index of given key server in current set.
function getCurrentKeyServerIndex(address keyServer)
external
view
override
returns (uint8)
{
KeyServer storage entry = currentSet.map[keyServer];
require(keccak256(bytes(entry.ip)) != keccak256(""), "Entry does not exist.");
return entry.index;
}
/// Get count of key servers in current set.
function getCurrentKeyServersCount()
external
view
override
returns (uint8)
{
return uint8(currentSet.list.length);
}
/// Get address of key server in current set.
function getCurrentKeyServer(uint8 index)
external
view
override
returns (address)
{
require(index < currentSet.list.length, "Index is out of bounds.");
return currentSet.list[index];
}
/// Get all current key servers.
function getCurrentKeyServers()
external
view
override
returns (address[] memory)
{
return currentSet.list;
}
/// Get current key server public key.
function getCurrentKeyServerPublic(address keyServer)
external
view
override
isOnCurrentSet(keyServer)
returns (bytes memory)
{
return currentSet.map[keyServer].publicKey;
}
/// Get current key server address.
function getCurrentKeyServerAddress(address keyServer)
external
view
override
isOnCurrentSet(keyServer)
returns (string memory)
{
return currentSet.map[keyServer].ip;
}
/// Get all migration key servers.
function getMigrationKeyServers()
external
view
override
returns (address[] memory)
{
return migrationSet.list;
}
/// Get migration key server public key.
function getMigrationKeyServerPublic(address keyServer)
external
view
override
isOnMigrationSet(keyServer)
returns (bytes memory)
{
return migrationSet.map[keyServer].publicKey;
}
/// Get migration key server address.
function getMigrationKeyServerAddress(address keyServer)
external
view
override
isOnMigrationSet(keyServer)
returns (string memory)
{
return migrationSet.map[keyServer].ip;
}
/// Get all new key servers.
function getNewKeyServers()
external
view
override
returns (address[] memory)
{
return newSet.list;
}
/// Get new key server public key.
function getNewKeyServerPublic(address keyServer)
external
view
override
isOnNewSet(keyServer)
returns (bytes memory)
{
return newSet.map[keyServer].publicKey;
}
/// Get new key server address.
function getNewKeyServerAddress(address keyServer)
external
view
override
isOnNewSet(keyServer)
returns (string memory)
{
return newSet.map[keyServer].ip;
}
/// Get migration id.
function getMigrationId()
external
view
override
isValidMigrationId(migrationId)
returns (bytes32)
{
return migrationId;
}
/// Start migration.
function startMigration(bytes32 id)
external
override
noActiveMigration
isValidMigrationId(id)
whenMigrationRequired
isPossibleMigrationParticipant
{
// migration to empty set is impossible
require (newSet.list.length != 0, "Migration to empty set is impossible.");
migrationMaster = msg.sender;
migrationId = id;
copySet(migrationSet, newSet);
emit MigrationStarted();
}
/// Confirm migration.
function confirmMigration(bytes32 id)
external
override
isValidMigrationId(id)
isActiveMigration(id)
isOnMigrationSet(msg.sender)
{
require(!migrationConfirmations[msg.sender], "Migration is already confirmed.");
migrationConfirmations[msg.sender] = true;
// check if migration is completed
for (uint j = 0; j < migrationSet.list.length; ++j) {
if (!migrationConfirmations[migrationSet.list[j]]) {
return;
}
}
// migration is completed => delete confirmations
for (uint m = 0; m < migrationSet.list.length; ++m) {
delete migrationConfirmations[migrationSet.list[m]];
}
delete migrationMaster;
// ...and copy migration set to current set
copySet(currentSet, migrationSet);
// ...and also delete entries from migration set
clearSet(migrationSet);
// ...and fire completion event
emit MigrationCompleted();
// ...and update current server set change block
currentSetChangeBlock = block.number;
}
/// Get migration master.
function getMigrationMaster()
external
view
override
returns (address)
{
return migrationMaster;
}
/// Is migration confirmed.
function isMigrationConfirmed(address keyServer)
external
view
override
isMigrationParticipant(keyServer)
returns (bool)
{
return migrationConfirmations[keyServer];
}
/// Complete initialization. Before this function is called, all calls to addKeyServer/removeKeyServer
/// affect both newSet and currentSet.
function completeInitialization()
public
onlyOwner
{
require(!isInitialized, "Not initialized!");
isInitialized = true;
}
/// Add new key server to set.
function addKeyServer(
bytes memory keyServerPublic,
string memory keyServerIp
)
public
onlyOwner
notAnEmptyString(keyServerIp)
isValidPublic(keyServerPublic)
isNotOnNewSet(computeAddress(keyServerPublic))
{
// append to the new set
address keyServer = appendToSet(newSet, keyServerPublic, keyServerIp);
// also append to current set
if (!isInitialized) {
appendToSet(currentSet, keyServerPublic, keyServerIp);
}
// fire event
emit KeyServerAdded(keyServer);
}
/// Remove key server from set.
function removeKeyServer(address keyServer)
public
onlyOwner
isOnNewSet(keyServer)
{
// remove element from the new set
removeFromSet(newSet, keyServer);
// also remove from the current set
if (!isInitialized) {
removeFromSet(currentSet, keyServer);
}
// fire event
emit KeyServerRemoved(keyServer);
}
/// Compute address from public key.
function computeAddress(bytes memory keyServerPublic)
private
pure
returns (address)
{
return address(uint(keccak256(keyServerPublic)) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
}
/// 'Check' public key.
function checkPublic(bytes memory keyServerPublic)
private
pure
returns (bool)
{
return keyServerPublic.length == 64;
}
/// Copy set (assignment operator).
function copySet(Set storage set1, Set storage set2) private {
for (uint i = 0; i < set1.list.length; ++i) {
delete set1.map[set1.list[i]];
}
set1.list = set2.list;
for (uint j = 0; j < set1.list.length; ++j) {
set1.map[set1.list[j]] = set2.map[set1.list[j]];
}
}
/// Clear set.
function clearSet(Set storage set)
private
{
while (set.list.length > 0) {
address keyServer = set.list[set.list.length - 1];
delete set.map[keyServer];
set.list.pop();
}
}
/// Are two sets equal?
function areEqualSets(Set storage set1, Set storage set2)
private
view
returns (bool)
{
for (uint i = 0; i < set1.list.length; ++i) {
if (keccak256(bytes(set2.map[set1.list[i]].ip)) == keccak256("")) {
return false;
}
}
for (uint j = 0; j < set2.list.length; ++j) {
if (keccak256(bytes(set1.map[set2.list[j]].ip)) == keccak256("")) {
return false;
}
}
return true;
}
/// Append new key serer to set.
function appendToSet(
Set storage set,
bytes memory keyServerPublic,
string memory keyServerIp
)
private
returns (address)
{
// we do not support > 256 key servers in the list
require(set.list.length < 256, "Number of servers must be smaller than 256.");
address keyServer = computeAddress(keyServerPublic);
set.map[keyServer].index = uint8(set.list.length);
set.map[keyServer].publicKey = keyServerPublic;
set.map[keyServer].ip = keyServerIp;
set.list.push(keyServer);
return keyServer;
}
/// Remove existing key server set.
function removeFromSet(Set storage set, address keyServer) private {
// swap list elements (removedIndex, lastIndex)
uint8 removedIndex = uint8(set.map[keyServer].index);
uint8 lastIndex = uint8(set.list.length) - 1;
address lastKeyServer = set.list[lastIndex];
set.list[removedIndex] = lastKeyServer;
set.map[lastKeyServer].index = removedIndex;
// remove element from list and map
delete set.list[lastIndex];
delete set.map[keyServer];
set.list.pop();
}
}
pragma solidity 0.4.24;
contract FeeTypes {
bytes32 internal constant HOME_FEE = 0x89d93e5e92f7e37e490c25f0e50f7f4aad7cc94b308a566553280967be38bcf1; // keccak256(abi.encodePacked("home-fee"))
bytes32 internal constant FOREIGN_FEE = 0xdeb7f3adca07d6d1f708c1774389db532a2b2f18fd05a62b957e4089f4696ed5; // keccak256(abi.encodePacked("foreign-fee"))
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
contract ValidatorStorage {
bytes32 internal constant VALIDATOR_CONTRACT = 0x5a74bb7e202fb8e4bf311841c7d64ec19df195fee77d7e7ae749b27921b6ddfe; // keccak256(abi.encodePacked("validatorContract"))
}
interface IRewardableValidators {
function isValidator(address _validator) external view returns (bool);
function requiredSignatures() external view returns (uint256);
function owner() external view returns (address);
function validatorList() external view returns (address[]);
function getValidatorRewardAddress(address _validator) external view returns (address);
function validatorCount() external view returns (uint256);
function getNextValidator(address _address) external view returns (address);
}
contract Sacrifice {
constructor(address _recipient) public payable {
selfdestruct(_recipient);
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
contract BaseFeeManager is EternalStorage, FeeTypes {
using SafeMath for uint256;
event HomeFeeUpdated(uint256 fee);
event ForeignFeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage
uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
if (!_recover) {
return _value.mul(fee).div(MAX_FEE);
}
return _value.mul(fee).div(MAX_FEE.sub(fee));
}
modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}
function setHomeFee(uint256 _fee) external validFee(_fee) {
uintStorage[HOME_FEE_STORAGE_KEY] = _fee;
emit HomeFeeUpdated(_fee);
}
function getHomeFee() public view returns (uint256) {
return uintStorage[HOME_FEE_STORAGE_KEY];
}
function setForeignFee(uint256 _fee) external validFee(_fee) {
uintStorage[FOREIGN_FEE_STORAGE_KEY] = _fee;
emit ForeignFeeUpdated(_fee);
}
function getForeignFee() public view returns (uint256) {
return uintStorage[FOREIGN_FEE_STORAGE_KEY];
}
/* solcov ignore next */
function distributeFeeFromAffirmation(uint256 _fee) external;
/* solcov ignore next */
function distributeFeeFromSignatures(uint256 _fee) external;
/* solcov ignore next */
function getFeeManagerMode() external pure returns (bytes4);
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
}
contract ValidatorsFeeManager is BaseFeeManager, ValidatorStorage {
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_HOME = 0x2a11db67c480122765825a7e4bc5428e8b7b9eca0d4e62b91aac194f99edd0d7; // keccak256(abi.encodePacked("reward-transferring-from-home"))
bytes32 public constant REWARD_FOR_TRANSFERRING_FROM_FOREIGN = 0xb14796d751eb4f2570065a479f9e526eabeb2077c564c8a1c5ea559883ea2fab; // keccak256(abi.encodePacked("reward-transferring-from-foreign"))
function distributeFeeFromAffirmation(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_FOREIGN);
}
function distributeFeeFromSignatures(uint256 _fee) external {
distributeFeeProportionally(_fee, REWARD_FOR_TRANSFERRING_FROM_HOME);
}
function rewardableValidatorContract() internal view returns (IRewardableValidators) {
return IRewardableValidators(addressStorage[VALIDATOR_CONTRACT]);
}
function distributeFeeProportionally(uint256 _fee, bytes32 _direction) internal {
IRewardableValidators validators = rewardableValidatorContract();
// solhint-disable-next-line var-name-mixedcase
address F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 numOfValidators = validators.validatorCount();
uint256 feePerValidator = _fee.div(numOfValidators);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfValidators));
if (diff > 0) {
randomValidatorIndex = random(numOfValidators);
}
address nextValidator = validators.getNextValidator(F_ADDR);
require((nextValidator != F_ADDR) && (nextValidator != address(0)));
uint256 i = 0;
while (nextValidator != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
address rewardAddress = validators.getValidatorRewardAddress(nextValidator);
onFeeDistribution(rewardAddress, feeToDistribute, _direction);
nextValidator = validators.getNextValidator(nextValidator);
require(nextValidator != address(0));
i = i + 1;
}
}
function onFeeDistribution(address _rewardAddress, uint256 _fee, bytes32 _direction) internal {
if (_direction == REWARD_FOR_TRANSFERRING_FROM_FOREIGN) {
onAffirmationFeeDistribution(_rewardAddress, _fee);
} else {
onSignatureFeeDistribution(_rewardAddress, _fee);
}
}
/* solcov ignore next */
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal;
/* solcov ignore next */
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}
contract FeeManagerNativeToErcBothDirections is ValidatorsFeeManager {
function getFeeManagerMode() external pure returns (bytes4) {
return 0xd7de965f; // bytes4(keccak256(abi.encodePacked("manages-both-directions")))
}
function onAffirmationFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function onSignatureFeeDistribution(address _rewardAddress, uint256 _fee) internal {
_sendReward(_rewardAddress, _fee);
}
function _sendReward(address _rewardAddress, uint256 _fee) internal {
if (!_rewardAddress.send(_fee)) {
(new Sacrifice).value(_fee)(_rewardAddress);
}
}
}
contract PercentageFixedFeeManagerNativeToErcBothDirections is FeeManagerNativeToErcBothDirections {
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
uint256 _fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
require(_value >= _fee);
return _fee;
}
}
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface ISecretStorePermissioning {
// Must return true if user has permission to get the specified key, false otherwise
function checkPermissions(address user, bytes32 document)
external
view
returns (bool);
}
interface ISecretStorePermissioningExtended is ISecretStorePermissioning {
// Returns all available keys for the user
function availableKeys(address user)
external
view
returns (bytes32[] memory);
}
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
contract PermissioningRegistry is ISecretStorePermissioning, Ownable, ERC165 {
struct PermissionEntry {
address[] users;
address admin;
bool exposed;
}
mapping(bytes32 => PermissionEntry) public permissions;
event NewAdmin(bytes32 indexed document);
event Permission(bytes32 indexed document);
modifier onlyAdmins(bytes32 document) {
require(
_isAdmin(document),
"Caller has to be the current admin or owner."
);
_;
}
constructor() public {
// ERC 165 support
_registerInterface(this.checkPermissions.selector);
_registerInterface(
this.owner.selector ^
this.renounceOwnership.selector ^
this.transferOwnership.selector
);
}
function setAdmin(bytes32 document, address newAdmin)
external
onlyAdmins(document)
returns (bool)
{
require(newAdmin != address(0), "New admin address cannot be 0x0.");
permissions[document].admin = newAdmin;
emit NewAdmin(document);
return true;
}
function setUsers(bytes32 document, address[] calldata _users)
external
onlyAdmins(document)
returns (bool)
{
permissions[document].users = _users;
emit Permission(document);
return true;
}
function setExposed(bytes32 document, bool _exposed)
external
onlyAdmins(document)
returns (bool)
{
permissions[document].exposed = _exposed;
emit Permission(document);
return true;
}
function addUser(bytes32 document, address newUser)
external
onlyAdmins(document)
returns (bool)
{
permissions[document].users.push(newUser);
emit Permission(document);
return true;
}
function removePermission(bytes32 document)
external
onlyAdmins(document)
returns (bool)
{
delete permissions[document];
emit Permission(document);
return true;
}
function permission(bytes32 document, address[] calldata _users)
external
returns (bool)
{
require(
permissions[document].admin == address(0) || _isAdmin(document),
"You have to be admin or owner to change permissions."
);
permissions[document].admin = _msgSender();
permissions[document].users = _users;
emit Permission(document);
return true;
}
function checkPermissions(address user, bytes32 document)
public
view
override
returns (bool)
{
if (!_isInitialized(document)) {
return false;
}
if (permissions[document].exposed) {
return true;
}
address[] storage users = permissions[document].users;
for (uint i = 0; i < users.length; i++) {
if (users[i] == user)
return true;
}
return false;
}
function getAdmin(bytes32 document) public view returns (address) {
return permissions[document].admin;
}
function getUsers(bytes32 document) public view returns (address[] memory) {
return permissions[document].users;
}
function isExposed(bytes32 document) public view returns (bool) {
return permissions[document].exposed;
}
function _isAdmin(bytes32 document) internal view returns (bool) {
return (permissions[document].admin == _msgSender() || owner() == _msgSender());
}
function _isInitialized(bytes32 document) internal view returns (bool) {
return (permissions[document].admin != address(0));
}
}
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface ISecretStorePermissioning {
// Must return true if user has permission to get the specified key, false otherwise
function checkPermissions(address user, bytes32 document)
external
view
returns (bool);
}
interface ISecretStorePermissioningExtended is ISecretStorePermissioning {
// Returns all available keys for the user
function availableKeys(address user)
external
view
returns (bytes32[] memory);
}
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
contract PermissioningRegistryOpen is ISecretStorePermissioning, Ownable, ERC165 {
constructor() public {
// ERC 165 support
_registerInterface(this.checkPermissions.selector);
_registerInterface(
this.owner.selector ^
this.renounceOwnership.selector ^
this.transferOwnership.selector
);
}
function checkPermissions(address user, bytes32 document)
public
view
override
returns (bool)
{
return true;
}
}
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface IERC165Query {
function doesContractImplementInterface(address _contract, bytes4 _interfaceId)
external
view
returns (bool);
}
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Returns true if `account` supports the {IERC165} interface,
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
!_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) &&
_supportsERC165Interface(account, interfaceId);
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in _interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!_supportsERC165Interface(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
* Interface identification is specified in ERC-165.
*/
function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
// success determines whether the staticcall succeeded and result determines
// whether the contract at account indicates support of _interfaceId
(bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
return (success && result);
}
/**
* @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return success true if the STATICCALL succeeded, false otherwise
* @return result true if the STATICCALL succeeded and the contract at account
* indicates support of the interface with identifier interfaceId, false otherwise
*/
function _callERC165SupportsInterface(address account, bytes4 interfaceId)
private
view
returns (bool, bool)
{
bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
(bool success, bytes memory result) = account.staticcall.gas(30000)(encodedParams);
if (result.length < 32) return (false, false);
return (success, abi.decode(result, (bool)));
}
}
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
contract ERC165Query is IERC165Query, ERC165 {
constructor() public {
_registerInterface(this.doesContractImplementInterface.selector);
}
function doesContractImplementInterface(address _contract, bytes4 _interfaceId)
external
view
override
returns (bool)
{
return ERC165Checker.supportsInterface(_contract, _interfaceId);
}
}
interface ISecretStorePermissioning {
// Must return true if user has permission to get the specified key, false otherwise
function checkPermissions(address user, bytes32 document)
external
view
returns (bool);
}
interface ISecretStorePermissioningExtended is ISecretStorePermissioning {
// Returns all available keys for the user
function availableKeys(address user)
external
view
returns (bytes32[] memory);
}
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract PermissioningRelay is ISecretStorePermissioning, Ownable, ERC165 {
ISecretStorePermissioning public permissioningContract;
address[] public oldPermissioningContracts;
IERC165Query public erc165Query;
event NewPermissioningContract(address indexed newContractAddress);
constructor(address _erc165Query, address _permissioningContract)
public
{
require(_erc165Query != address(0), "ERC 165 Query contract address cannot be 0x0");
require(_permissioningContract != address(0), "Permissioning contract address cannot be 0x0");
erc165Query = IERC165Query(_erc165Query);
require(
_isValidPermissioning(_permissioningContract),
"Provided contract does not implement the 'checkPermissions' function, or does not support ERC-165"
);
// ERC 165 support
_registerInterface(this.checkPermissions.selector);
_registerInterface(
this.owner.selector ^
this.renounceOwnership.selector ^
this.transferOwnership.selector
);
permissioningContract = ISecretStorePermissioning(_permissioningContract);
emit NewPermissioningContract(_permissioningContract);
}
function checkPermissions(address user, bytes32 document)
public
view
override
returns (bool)
{
return permissioningContract.checkPermissions(user, document);
}
function setPermissioningContract(address _newContract)
public
onlyOwner
returns (address)
{
require(
address(permissioningContract) != _newContract,
"New permissioning contract address cannot be the same as the old one"
);
require(address(this) != _newContract, "New permissioning contract cannot be this relay");
require(
_isValidPermissioning(_newContract),
"Provided contract does not implement the 'checkPermissions' function, or does not support ERC-165"
);
oldPermissioningContracts.push(address(permissioningContract));
permissioningContract = ISecretStorePermissioning(_newContract);
emit NewPermissioningContract(_newContract);
return oldPermissioningContracts[oldPermissioningContracts.length-1];
}
function setERC165Query(address _newContract)
public
onlyOwner
returns (address)
{
require(_newContract != address(0), "ERC 165 Query contract address cannot be 0x0");
address _old = address(erc165Query);
erc165Query = IERC165Query(_newContract);
return _old;
}
function _isValidPermissioning(address _contract)
internal
view
returns (bool)
{
return erc165Query.doesContractImplementInterface(
_contract,
this.checkPermissions.selector
);
}
}
pragma solidity ^0.4.18;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public;
function setResolver(bytes32 node, address resolver) public;
function setOwner(bytes32 node, address owner) public;
function setTTL(bytes32 node, uint64 ttl) public;
function owner(bytes32 node) public view returns (address);
function resolver(bytes32 node) public view returns (address);
function ttl(bytes32 node) public view returns (uint64);
}
/**
* A simple resolver anyone can use; only allows the owner of a node to set its
* address.
*/
contract PublicResolver {
bytes4 constant INTERFACE_META_ID = 0x01ffc9a7;
bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de;
bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5;
bytes4 constant NAME_INTERFACE_ID = 0x691f3431;
bytes4 constant ABI_INTERFACE_ID = 0x2203ab56;
bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233;
bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c;
bytes4 constant MULTIHASH_INTERFACE_ID = 0xe89401a1;
event AddrChanged(bytes32 indexed node, address a);
event ContentChanged(bytes32 indexed node, bytes32 hash);
event NameChanged(bytes32 indexed node, string name);
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
event TextChanged(bytes32 indexed node, string indexedKey, string key);
event MultihashChanged(bytes32 indexed node, bytes hash);
struct PublicKey {
bytes32 x;
bytes32 y;
}
struct Record {
address addr;
bytes32 content;
string name;
PublicKey pubkey;
mapping(string=>string) text;
mapping(uint256=>bytes) abis;
bytes multihash;
}
ENS ens;
mapping (bytes32 => Record) records;
modifier only_owner(bytes32 node) {
require(ens.owner(node) == msg.sender);
_;
}
/**
* Constructor.
* @param ensAddr The ENS registrar contract.
*/
function PublicResolver(ENS ensAddr) public {
ens = ensAddr;
}
/**
* Sets the address associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param addr The address to set.
*/
function setAddr(bytes32 node, address addr) public only_owner(node) {
records[node].addr = addr;
AddrChanged(node, addr);
}
/**
* Sets the content hash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* Note that this resource type is not standardized, and will likely change
* in future to a resource type based on multihash.
* @param node The node to update.
* @param hash The content hash to set
*/
function setContent(bytes32 node, bytes32 hash) public only_owner(node) {
records[node].content = hash;
ContentChanged(node, hash);
}
/**
* Sets the multihash associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param hash The multihash to set
*/
function setMultihash(bytes32 node, bytes hash) public only_owner(node) {
records[node].multihash = hash;
MultihashChanged(node, hash);
}
/**
* Sets the name associated with an ENS node, for reverse records.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param name The name to set.
*/
function setName(bytes32 node, string name) public only_owner(node) {
records[node].name = name;
NameChanged(node, name);
}
/**
* Sets the ABI associated with an ENS node.
* Nodes may have one ABI of each content type. To remove an ABI, set it to
* the empty string.
* @param node The node to update.
* @param contentType The content type of the ABI
* @param data The ABI data.
*/
function setABI(bytes32 node, uint256 contentType, bytes data) public only_owner(node) {
// Content types must be powers of 2
require(((contentType - 1) & contentType) == 0);
records[node].abis[contentType] = data;
ABIChanged(node, contentType);
}
/**
* Sets the SECP256k1 public key associated with an ENS node.
* @param node The ENS node to query
* @param x the X coordinate of the curve point for the public key.
* @param y the Y coordinate of the curve point for the public key.
*/
function setPubkey(bytes32 node, bytes32 x, bytes32 y) public only_owner(node) {
records[node].pubkey = PublicKey(x, y);
PubkeyChanged(node, x, y);
}
/**
* Sets the text data associated with an ENS node and key.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param key The key to set.
* @param value The text data value to set.
*/
function setText(bytes32 node, string key, string value) public only_owner(node) {
records[node].text[key] = value;
TextChanged(node, key, key);
}
/**
* Returns the text data associated with an ENS node and key.
* @param node The ENS node to query.
* @param key The text data key to query.
* @return The associated text data.
*/
function text(bytes32 node, string key) public view returns (string) {
return records[node].text[key];
}
/**
* Returns the SECP256k1 public key associated with an ENS node.
* Defined in EIP 619.
* @param node The ENS node to query
* @return x, y the X and Y coordinates of the curve point for the public key.
*/
function pubkey(bytes32 node) public view returns (bytes32 x, bytes32 y) {
return (records[node].pubkey.x, records[node].pubkey.y);
}
/**
* Returns the ABI associated with an ENS node.
* Defined in EIP205.
* @param node The ENS node to query
* @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
* @return contentType The content type of the return value
* @return data The ABI data
*/
function ABI(bytes32 node, uint256 contentTypes) public view returns (uint256 contentType, bytes data) {
Record storage record = records[node];
for (contentType = 1; contentType <= contentTypes; contentType <<= 1) {
if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) {
data = record.abis[contentType];
return;
}
}
contentType = 0;
}
/**
* Returns the name associated with an ENS node, for reverse records.
* Defined in EIP181.
* @param node The ENS node to query.
* @return The associated name.
*/
function name(bytes32 node) public view returns (string) {
return records[node].name;
}
/**
* Returns the content hash associated with an ENS node.
* Note that this resource type is not standardized, and will likely change
* in future to a resource type based on multihash.
* @param node The ENS node to query.
* @return The associated content hash.
*/
function content(bytes32 node) public view returns (bytes32) {
return records[node].content;
}
/**
* Returns the multihash associated with an ENS node.
* @param node The ENS node to query.
* @return The associated multihash.
*/
function multihash(bytes32 node) public view returns (bytes) {
return records[node].multihash;
}
/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address) {
return records[node].addr;
}
/**
* Returns true if the resolver implements the interface specified by the provided hash.
* @param interfaceID The ID of the interface to check for.
* @return True if the contract implements the requested interface.
*/
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
return interfaceID == ADDR_INTERFACE_ID ||
interfaceID == CONTENT_INTERFACE_ID ||
interfaceID == NAME_INTERFACE_ID ||
interfaceID == ABI_INTERFACE_ID ||
interfaceID == PUBKEY_INTERFACE_ID ||
interfaceID == TEXT_INTERFACE_ID ||
interfaceID == MULTIHASH_INTERFACE_ID ||
interfaceID == INTERFACE_META_ID;
}
}
pragma solidity ^0.4.10;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public;
function setResolver(bytes32 node, address resolver) public;
function setOwner(bytes32 node, address owner) public;
function setTTL(bytes32 node, uint64 ttl) public;
function owner(bytes32 node) public view returns (address);
function resolver(bytes32 node) public view returns (address);
function ttl(bytes32 node) public view returns (uint64);
}
contract Resolver {
function setName(bytes32 node, string name) public;
}
contract ReverseRegistrar {
// namehash('addr.reverse')
bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
ENS public ens;
Resolver public defaultResolver;
/**
* @dev Constructor
* @param ensAddr The address of the ENS registry.
* @param resolverAddr The address of the default reverse resolver.
*/
function ReverseRegistrar(ENS ensAddr, Resolver resolverAddr) public {
ens = ensAddr;
defaultResolver = resolverAddr;
// Assign ownership of the reverse record to our deployer
ReverseRegistrar oldRegistrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE));
if (address(oldRegistrar) != 0) {
oldRegistrar.claim(msg.sender);
}
}
/**
* @dev Transfers ownership of the reverse ENS record associated with the
* calling account.
* @param owner The address to set as the owner of the reverse record in ENS.
* @return The ENS node hash of the reverse record.
*/
function claim(address owner) public returns (bytes32) {
return claimWithResolver(owner, 0);
}
/**
* @dev Transfers ownership of the reverse ENS record associated with the
* calling account.
* @param owner The address to set as the owner of the reverse record in ENS.
* @param resolver The address of the resolver to set; 0 to leave unchanged.
* @return The ENS node hash of the reverse record.
*/
function claimWithResolver(address owner, address resolver) public returns (bytes32) {
var label = sha3HexAddress(msg.sender);
bytes32 node = keccak256(ADDR_REVERSE_NODE, label);
var currentOwner = ens.owner(node);
// Update the resolver if required
if (resolver != 0 && resolver != ens.resolver(node)) {
// Transfer the name to us first if it's not already
if (currentOwner != address(this)) {
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, this);
currentOwner = address(this);
}
ens.setResolver(node, resolver);
}
// Update the owner if required
if (currentOwner != owner) {
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner);
}
return node;
}
/**
* @dev Sets the `name()` record for the reverse ENS record associated with
* the calling account. First updates the resolver to the default reverse
* resolver if necessary.
* @param name The name to set for this address.
* @return The ENS node hash of the reverse record.
*/
function setName(string name) public returns (bytes32) {
bytes32 node = claimWithResolver(this, defaultResolver);
defaultResolver.setName(node, name);
return node;
}
/**
* @dev Returns the node hash for a given account's reverse records.
* @param addr The address to hash
* @return The ENS node hash.
*/
function node(address addr) public view returns (bytes32) {
return keccak256(ADDR_REVERSE_NODE, sha3HexAddress(addr));
}
/**
* @dev An optimised function to compute the sha3 of the lower-case
* hexadecimal representation of an Ethereum address.
* @param addr The address to hash
* @return The SHA3 hash of the lower-case hexadecimal encoding of the
* input address.
*/
function sha3HexAddress(address addr) private returns (bytes32 ret) {
addr; ret; // Stop warning us about unused variables
assembly {
let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000
let i := 40
loop:
i := sub(i, 1)
mstore8(i, byte(and(addr, 0xf), lookup))
addr := div(addr, 0x10)
i := sub(i, 1)
mstore8(i, byte(and(addr, 0xf), lookup))
addr := div(addr, 0x10)
jumpi(loop, i)
ret := keccak256(0, 40)
}
}
}
pragma solidity 0.4.24;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
/* solcov ignore next */
_;
}
/**
* @dev Throws if called by any account other than contract itself or owner.
*/
modifier onlyRelevantSender() {
// proxy owner if used through proxy, address(0) otherwise
require(
!address(this).call(abi.encodeWithSelector(UPGRADEABILITY_OWNER)) || // covers usage without calling through storage proxy
msg.sender == IUpgradeabilityOwnerStorage(this).upgradeabilityOwner() || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
/* solcov ignore next */
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner the address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting '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;
}
c = _a * _b;
assert(c / _a == _b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
// assert(_b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return _a / _b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
assert(_b <= _a);
return _a - _b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
c = _a + _b;
assert(c >= _a);
return c;
}
}
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}
contract InitializableBridge is Initializable {
bytes32 internal constant DEPLOYED_AT_BLOCK = 0xb120ceec05576ad0c710bc6e85f1768535e27554458f05dcbb5c65b8c7a749b0; // keccak256(abi.encodePacked("deployedAtBlock"))
function deployedAtBlock() external view returns (uint256) {
return uintStorage[DEPLOYED_AT_BLOCK];
}
}
contract BaseBridgeValidators is InitializableBridge, Ownable {
using SafeMath for uint256;
address public constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 internal constant MAX_VALIDATORS = 50;
bytes32 internal constant REQUIRED_SIGNATURES = 0xd18ea17c351d6834a0e568067fb71804d2a588d5e26d60f792b1c724b1bd53b1; // keccak256(abi.encodePacked("requiredSignatures"))
bytes32 internal constant VALIDATOR_COUNT = 0x8656d603d9f985c3483946a92789d52202f49736384ba131cb92f62c4c1aa082; // keccak256(abi.encodePacked("validatorCount"))
event ValidatorAdded(address indexed validator);
event ValidatorRemoved(address indexed validator);
event RequiredSignaturesChanged(uint256 requiredSignatures);
function setRequiredSignatures(uint256 _requiredSignatures) external onlyOwner {
require(validatorCount() >= _requiredSignatures);
require(_requiredSignatures != 0);
uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
emit RequiredSignaturesChanged(_requiredSignatures);
}
function getBridgeValidatorsInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (2, 3, 0);
}
function validatorList() external view returns (address[]) {
address[] memory list = new address[](validatorCount());
uint256 counter = 0;
address nextValidator = getNextValidator(F_ADDR);
require(nextValidator != address(0));
while (nextValidator != F_ADDR) {
list[counter] = nextValidator;
nextValidator = getNextValidator(nextValidator);
counter++;
require(nextValidator != address(0));
}
return list;
}
function _addValidator(address _validator) internal {
require(_validator != address(0) && _validator != F_ADDR);
require(!isValidator(_validator));
address firstValidator = getNextValidator(F_ADDR);
require(firstValidator != address(0));
setNextValidator(_validator, firstValidator);
setNextValidator(F_ADDR, _validator);
setValidatorCount(validatorCount().add(1));
}
function _removeValidator(address _validator) internal {
require(validatorCount() > requiredSignatures());
require(isValidator(_validator));
address validatorsNext = getNextValidator(_validator);
address index = F_ADDR;
address next = getNextValidator(index);
require(next != address(0));
while (next != _validator) {
index = next;
next = getNextValidator(index);
require(next != F_ADDR && next != address(0));
}
setNextValidator(index, validatorsNext);
deleteItemFromAddressStorage("validatorsList", _validator);
setValidatorCount(validatorCount().sub(1));
}
function requiredSignatures() public view returns (uint256) {
return uintStorage[REQUIRED_SIGNATURES];
}
function validatorCount() public view returns (uint256) {
return uintStorage[VALIDATOR_COUNT];
}
function isValidator(address _validator) public view returns (bool) {
return _validator != F_ADDR && getNextValidator(_validator) != address(0);
}
function getNextValidator(address _address) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("validatorsList", _address))];
}
function deleteItemFromAddressStorage(string _mapName, address _address) internal {
delete addressStorage[keccak256(abi.encodePacked(_mapName, _address))];
}
function setValidatorCount(uint256 _validatorCount) internal {
require(_validatorCount <= MAX_VALIDATORS);
uintStorage[VALIDATOR_COUNT] = _validatorCount;
}
function setNextValidator(address _prevValidator, address _validator) internal {
addressStorage[keccak256(abi.encodePacked("validatorsList", _prevValidator))] = _validator;
}
function isValidatorDuty(address _validator) external view returns (bool) {
uint256 counter = 0;
address next = getNextValidator(F_ADDR);
require(next != address(0));
while (next != F_ADDR) {
if (next == _validator) {
return (block.number % validatorCount() == counter);
}
next = getNextValidator(next);
counter++;
require(next != address(0));
}
return false;
}
}
contract RewardableValidators is BaseBridgeValidators {
function initialize(
uint256 _requiredSignatures,
address[] _initialValidators,
address[] _initialRewards,
address _owner
) external onlyRelevantSender returns (bool) {
require(!isInitialized());
require(_owner != address(0));
setOwner(_owner);
require(_requiredSignatures != 0);
require(_initialValidators.length >= _requiredSignatures);
require(_initialValidators.length == _initialRewards.length);
for (uint256 i = 0; i < _initialValidators.length; i++) {
require(_initialValidators[i] != address(0) && _initialValidators[i] != F_ADDR);
require(_initialRewards[i] != address(0));
require(!isValidator(_initialValidators[i]));
if (i == 0) {
setNextValidator(F_ADDR, _initialValidators[i]);
if (_initialValidators.length == 1) {
setNextValidator(_initialValidators[i], F_ADDR);
}
} else if (i == _initialValidators.length - 1) {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
setNextValidator(_initialValidators[i], F_ADDR);
} else {
setNextValidator(_initialValidators[i - 1], _initialValidators[i]);
}
setValidatorRewardAddress(_initialValidators[i], _initialRewards[i]);
emit ValidatorAdded(_initialValidators[i]);
}
setValidatorCount(_initialValidators.length);
uintStorage[REQUIRED_SIGNATURES] = _requiredSignatures;
uintStorage[DEPLOYED_AT_BLOCK] = block.number;
setInitialize();
emit RequiredSignaturesChanged(_requiredSignatures);
return isInitialized();
}
function addRewardableValidator(address _validator, address _reward) external onlyOwner {
require(_reward != address(0));
_addValidator(_validator);
setValidatorRewardAddress(_validator, _reward);
emit ValidatorAdded(_validator);
}
function removeValidator(address _validator) external onlyOwner {
_removeValidator(_validator);
deleteItemFromAddressStorage("validatorsRewards", _validator);
emit ValidatorRemoved(_validator);
}
function getValidatorRewardAddress(address _validator) external view returns (address) {
return addressStorage[keccak256(abi.encodePacked("validatorsRewards", _validator))];
}
function setValidatorRewardAddress(address _validator, address _reward) internal {
addressStorage[keccak256(abi.encodePacked("validatorsRewards", _validator))] = _reward;
}
}
//! The Secret Store server key generation service contract.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
pragma solidity ^0.6.0;
//! The Secret Store service contract intefaces.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
/// Server Key generation service contract API (client view).
interface ServerKeyGenerationServiceClientApi {
/// When server key is generated.
event ServerKeyGenerated(bytes32 indexed serverKeyId, bytes serverKeyPublic);
/// When error occurs during server key generation.
event ServerKeyGenerationError(bytes32 indexed serverKeyId);
/// Request new server key generation. Generated key will be published via ServerKeyGenerated event when available.
function generateServerKey(bytes32 serverKeyId, uint8 threshold) external payable;
}
/// Server Key generation service contract API (key server view).
interface ServerKeyGenerationServiceKeyServerApi {
/// When sever key generation request is received.
event ServerKeyGenerationRequested(bytes32 serverKeyId, address author, uint8 threshold);
/// Called when generation is reported by key server.
function serverKeyGenerated(bytes32 serverKeyId, bytes calldata serverKeyPublic) external;
/// Called when error occurs during server key generation.
function serverKeyGenerationError(bytes32 serverKeyId) external;
/// Get count of pending server key generation requests.
function serverKeyGenerationRequestsCount() external view returns (uint256);
/// Get server key generation request with given index.
/// Returns: (serverKeyId, author, threshold)
function getServerKeyGenerationRequest(uint256 index) external view returns (bytes32, address, uint256);
/// Returs true if response from given keyServer is required.
function isServerKeyGenerationResponseRequired(bytes32 serverKeyId, address keyServer) external view returns (bool);
}
/// Server Key retrieval service contract API (client view).
interface ServerKeyRetrievalServiceClientApi {
/// When server key is retrieved.
event ServerKeyRetrieved(bytes32 indexed serverKeyId, bytes serverKeyPublic, uint256 threshold);
/// When error occurs during server key retrieval.
event ServerKeyRetrievalError(bytes32 indexed serverKeyId);
/// Retrieve existing server key. Retrieved key will be published via ServerKeyRetrieved or ServerKeyRetrievalError.
function retrieveServerKey(bytes32 serverKeyId) external payable;
}
/// Server Key retrieval service contract API (key server view).
interface ServerKeyRetrievalServiceKeyServerApi {
/// When sever key retrieval request is received.
event ServerKeyRetrievalRequested(bytes32 serverKeyId);
/// Called when retrieval is reported by key server.
function serverKeyRetrieved(bytes32 serverKeyId, bytes calldata serverKeyPublic, uint8 threshold) external;
/// Called when error occurs during server key retrieval.
function serverKeyRetrievalError(bytes32 serverKeyId) external;
/// Get count of pending server key retrieval requests.
function serverKeyRetrievalRequestsCount() external view returns (uint256);
/// Get server key retrieval request with given index.
/// Returns: (serverKeyId)
function getServerKeyRetrievalRequest(uint256 index) external view returns (bytes32);
/// Returs true if response from given keyServer is required.
function isServerKeyRetrievalResponseRequired(bytes32 serverKeyId, address keyServer) external view returns (bool);
}
/// Document Key store service contract API (client view).
interface DocumentKeyStoreServiceClientApi {
/// When document key is stored.
event DocumentKeyStored(bytes32 indexed serverKeyId);
/// When error occurs during document key store.
event DocumentKeyStoreError(bytes32 indexed serverKeyId);
/// Request document key store. Use `secretstore_generateDocumentKey` RPC to generate both
/// `commonPoint` and `encryptedPoint`.
function storeDocumentKey(
bytes32 serverKeyId,
bytes calldata commonPoint,
bytes calldata encryptedPoint
) external payable;
}
/// Document Key store service contract API (key server view).
interface DocumentKeyStoreServiceKeyServerApi {
/// When document key store request is received.
event DocumentKeyStoreRequested(bytes32 serverKeyId, address author, bytes commonPoint, bytes encryptedPoint);
/// Called when store is reported by key server.
function documentKeyStored(bytes32 serverKeyId) external;
/// Called when error occurs during document key store.
function documentKeyStoreError(bytes32 serverKeyId) external;
/// Get count of pending document key store requests.
function documentKeyStoreRequestsCount() external view returns (uint256);
/// Get document key store request with given index.
/// Returns: (serverKeyId, author, commonPoint, encryptedPoint)
function getDocumentKeyStoreRequest(uint256 index)
external
view
returns (bytes32, address, bytes memory, bytes memory);
/// Returs true if response from given keyServer is required.
function isDocumentKeyStoreResponseRequired(bytes32 serverKeyId, address keyServer) external view returns (bool);
}
/// Document Key shadow retrieval service contract API (client view).
interface DocumentKeyShadowRetrievalServiceClientApi {
/// When document key common portion is retrieved. Ater this event s fired, wait for
/// exactly `threshold+1` `DocumentKeyPersonalRetrieved` events with the same `decryptedSecret` value.
event DocumentKeyCommonRetrieved(
bytes32 indexed serverKeyId,
address indexed requester,
bytes commonPoint,
uint8 threshold
);
/// When document key personal portion is retrieved. After enough events are fired, use `secretstore_shadowDecrypt`
/// to decrypt document contents.
event DocumentKeyPersonalRetrieved(
bytes32 indexed serverKeyId,
address indexed requester,
bytes decryptedSecret,
bytes shadow
);
/// When error occurs during document key retrieval.
event DocumentKeyShadowRetrievalError(bytes32 indexed serverKeyId, address indexed requester);
/// Request document key retrieval.
function retrieveDocumentKeyShadow(bytes32 serverKeyId, bytes calldata requesterPublic) external payable;
}
/// Document Key shadow retrieval service contract API (key server view).
interface DocumentKeyShadowRetrievalServiceKeyServerApi {
/// When document key common-portion retrieval request is received.
event DocumentKeyCommonRetrievalRequested(bytes32 serverKeyId, address requester);
/// When document key personal-portion retrieval request is received.
event DocumentKeyPersonalRetrievalRequested(bytes32 serverKeyId, bytes requesterPublic);
/// Called when common data is reported by key server.
function documentKeyCommonRetrieved(
bytes32 serverKeyId,
address requester,
bytes calldata commonPoint,
uint8 threshold
) external;
/// Called when 'personal' data is reported by key server.
function documentKeyPersonalRetrieved(
bytes32 serverKeyId,
address requester,
uint256 participants,
bytes calldata decryptedSecret,
bytes calldata shadow
) external;
/// Called when error occurs during document key shadow retrieval.
function documentKeyShadowRetrievalError(bytes32 serverKeyId, address requester) external;
/// Get count of pending document key shadow retrieval requests.
function documentKeyShadowRetrievalRequestsCount() external view returns (uint256);
/// Get document key shadow retrieval request with given index.
/// Returns: (serverKeyId, requesterPublic, isCommonRetrievalCompleted)
function getDocumentKeyShadowRetrievalRequest(uint256 index) external view returns (bytes32, bytes memory, bool);
/// Returs true if response from given keyServer is required.
function isDocumentKeyShadowRetrievalResponseRequired(bytes32 serverKeyId, address keyServer, address requester)
external
view
returns (bool);
}
//! The Secret Store service contract intefaces.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
//! The KeyServerSet contract.
//!
//! Copyright 2017 Svyatoslav Nikolsky, Parity Technologies Ltd.
//!
//! Licensed under the Apache License, Version 2.0 (the "License");
//! you may not use this file except in compliance with the License.
//! You may obtain a copy of the License at
//!
//! http://www.apache.org/licenses/LICENSE-2.0
//!
//! Unless required by applicable law or agreed to in writing, software
//! distributed under the License is distributed on an "AS IS" BASIS,
//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//! See the License for the specific language governing permissions and
//! limitations under the License.
/// Simple key server set.
interface KeyServerSet {
/// Get number of block when current set has been changed last time.
function getCurrentLastChange() external view returns (uint256);
/// Get index of given key server in current set.
function getCurrentKeyServerIndex(address keyServer) external view returns (uint8);
/// Get count of key servers in current set.
function getCurrentKeyServersCount() external view returns (uint8);
/// Get address of key server in current set.
function getCurrentKeyServer(uint8 index) external view returns (address);
/// Get all current key servers.
function getCurrentKeyServers() external view returns (address[] memory);
/// Get current key server public key.
function getCurrentKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get current key server address.
function getCurrentKeyServerAddress(address keyServer) external view returns (string memory);
}
/// Key server set with migration support.
interface KeyServerSetWithMigration {
/// When new server is added to new set.
event KeyServerAdded(address keyServer);
/// When existing server is removed from new set.
event KeyServerRemoved(address keyServer);
/// When migration is started.
event MigrationStarted();
/// When migration is completed.
event MigrationCompleted();
/// Get number of block when current set has been changed last time.
function getCurrentLastChange() external view returns (uint256);
/// Get index of given key server in current set.
function getCurrentKeyServerIndex(address keyServer) external view returns (uint8);
/// Get count of key servers in current set.
function getCurrentKeyServersCount() external view returns (uint8);
/// Get address of key server in current set.
function getCurrentKeyServer(uint8 index) external view returns (address);
/// Get all current key servers.
function getCurrentKeyServers() external view returns (address[] memory);
/// Get current key server public key.
function getCurrentKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get current key server address.
function getCurrentKeyServerAddress(address keyServer) external view returns (string memory);
/// Get all migration key servers.
function getMigrationKeyServers() external view returns (address[] memory);
/// Get migration key server public key.
function getMigrationKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get migration key server address.
function getMigrationKeyServerAddress(address keyServer) external view returns (string memory);
/// Get all new key servers.
function getNewKeyServers() external view returns (address[] memory);
/// Get new key server public key.
function getNewKeyServerPublic(address keyServer) external view returns (bytes memory);
/// Get new key server address.
function getNewKeyServerAddress(address keyServer) external view returns (string memory);
/// Get migration id.
function getMigrationId() external view returns (bytes32);
/// Get migration master.
function getMigrationMaster() external view returns (address);
/// Is migration confirmed by given node?
function isMigrationConfirmed(address keyServer) external view returns (bool);
/// Start migration.
function startMigration(bytes32 id) external;
/// Confirm migration.
function confirmMigration(bytes32 id) external;
}
/// Base contract for all Secret Store services.
contract SecretStoreServiceBase is Ownable {
/// Response support.
enum ResponseSupport { Confirmed, Unconfirmed, Impossible }
/// Single service request responses.
struct RequestResponses {
/// Number of block when servers set has been changed last time.
/// This whole structure is valid when this value stays the same.
/// Once this changes, all previous responses are erased.
uint256 keyServerSetChangeBlock;
/// We only support up to 256 key servers. If bit is set, this means that key server
/// has already voted for some confirmation (we do not care about exact response).
uint256 respondedKeyServersMask;
/// Number of key servers that have responded to request (number of ones in respondedKeyServersMask).
uint8 respondedKeyServersCount;
/// Response => number of supporting key servers.
mapping (bytes32 => uint8) responsesSupport;
/// Maximal support of single response.
uint8 maxResponseSupport;
/// All responses that are in responsesSupport. In ideal world, when all
/// key servers are working correctly, there'll be 1 response. Max 256 responses.
bytes32[] responses;
}
/// Only pass when fee is paid.
modifier whenFeePaid(uint256 amount) {
require(msg.value >= amount, "Transaction value is not enough.");
_;
}
/// Only pass when 'valid' public is passed.
modifier validPublic(bytes memory publicKey) {
require(publicKey.length == 64, "Public key is not valid.");
_;
}
/// Constructor.
constructor(address keyServerSetAddressInit) internal {
keyServerSetAddress = keyServerSetAddressInit;
}
/// Return number of key servers.
function keyServersCount() public view returns (uint8) {
return KeyServerSet(keyServerSetAddress).getCurrentKeyServersCount();
}
/// Return index of key server at given address.
function requireKeyServer(address keyServer) public view returns (uint8) {
return KeyServerSet(keyServerSetAddress).getCurrentKeyServerIndex(keyServer);
}
/// Drain balance of sender key server.
function drain() public {
uint256 balance = balances[msg.sender];
require(balance != 0, "Balance is 0.");
balances[msg.sender] = 0;
msg.sender.transfer(balance);
}
/// Deposit equal share of amount to each of key servers.
function deposit() internal {
uint8 count = keyServersCount();
uint256 amount = msg.value;
uint256 share = amount / count;
for (uint8 i = 0; i < count - 1; i++) {
address keyServer = KeyServerSet(keyServerSetAddress).getCurrentKeyServer(i);
balances[keyServer] += share;
amount = amount - share;
}
address lastKeyServer = KeyServerSet(keyServerSetAddress).getCurrentKeyServer(count - 1);
balances[lastKeyServer] += amount;
}
/// Returns true if response from given keyServer is required.
function isResponseRequired(RequestResponses storage responses, uint8 keyServerIndex) internal view returns (bool) {
// if servers set has changed, new response is definitely required
uint256 keyServerSetChangeBlock = KeyServerSet(keyServerSetAddress).getCurrentLastChange();
if (keyServerSetChangeBlock != responses.keyServerSetChangeBlock) {
return true;
}
// only require response when server has not responded before
uint256 keyServerMask = (uint256(1) << keyServerIndex);
return ((responses.respondedKeyServersMask & keyServerMask) == 0);
}
/// Insert key server confirmation.
function insertResponse(
RequestResponses storage responses,
uint8 keyServerIndex,
uint8 threshold,
bytes32 response) internal returns (ResponseSupport)
{
// check that servers set is still the same (and all previous responses are valid)
uint256 keyServerSetChangeBlock = KeyServerSet(keyServerSetAddress).getCurrentLastChange();
if (responses.respondedKeyServersCount == 0) {
responses.keyServerSetChangeBlock = keyServerSetChangeBlock;
} else if (responses.keyServerSetChangeBlock != keyServerSetChangeBlock) {
resetResponses(responses, keyServerSetChangeBlock);
}
// check if key server has already responded
uint256 keyServerMask = (uint256(1) << keyServerIndex);
if ((responses.respondedKeyServersMask & keyServerMask) != 0) {
return ResponseSupport.Unconfirmed;
}
// insert response
uint8 responseSupport = responses.responsesSupport[response] + 1;
responses.respondedKeyServersMask |= keyServerMask;
responses.respondedKeyServersCount += 1;
responses.responsesSupport[response] = responseSupport;
if (responseSupport == 1) {
responses.responses.push(response);
}
if (responseSupport >= responses.maxResponseSupport) {
responses.maxResponseSupport = responseSupport;
// check if passed response has received enough support
if (threshold <= responseSupport - 1) {
return ResponseSupport.Confirmed;
}
}
// check if max confirmation CAN receive enough support
uint8 keyServersLeft = keyServersCount() - responses.respondedKeyServersCount;
if (threshold > responses.maxResponseSupport + keyServersLeft - 1) {
return ResponseSupport.Impossible;
}
return ResponseSupport.Unconfirmed;
}
/// Clear responses before removal.
function clearResponses(RequestResponses storage responses) internal {
for (uint256 i = 0; i < responses.responses.length; ++i) {
delete responses.responsesSupport[responses.responses[i]];
}
}
/// Remove request id from array.
function removeRequestKey(bytes32[] storage requests, bytes32 request) internal {
for (uint i = 0; i < requests.length; ++i) {
if (requests[i] == request) {
requests[i] = requests[requests.length - 1];
requests.pop();
break;
}
}
}
/// Reset responses.
function resetResponses(RequestResponses storage responses, uint256 keyServerSetChangeBlock) private {
clearResponses(responses);
responses.keyServerSetChangeBlock = keyServerSetChangeBlock;
responses.respondedKeyServersMask = 0;
responses.respondedKeyServersCount = 0;
responses.maxResponseSupport = 0;
delete responses.responses;
}
/// Address of KeyServerSet contract.
address private keyServerSetAddress;
/// Balances of key servers.
mapping (address => uint256) private balances;
/// Active requests.
mapping (bytes32 => RequestResponses) private requests;
}
/// Server Key generation service contract.
/* solium-disable-next-line */
contract SecretStoreServerKeyGenerationService is
SecretStoreServiceBase,
ServerKeyGenerationServiceClientApi,
ServerKeyGenerationServiceKeyServerApi
{
/// Server key generation request.
struct ServerKeyGenerationRequest {
address author;
uint256 threshold;
RequestResponses responses;
}
/// When sever key generation request is received.
event ServerKeyGenerationRequested(bytes32 serverKeyId, address author, uint8 threshold);
/// When server key is generated.
event ServerKeyGenerated(bytes32 indexed serverKeyId, bytes serverKeyPublic);
/// When error occurs during server key generation.
event ServerKeyGenerationError(bytes32 indexed serverKeyId);
/// Constructor.
constructor(address keyServerSetAddressInit) SecretStoreServiceBase(keyServerSetAddressInit) public {
serverKeyGenerationFee = 200 finney;
maxServerKeyGenerationRequests = 4;
}
// === Interface methods ===
/// We do not support direct payments.
receive() external payable {
revert("Direct payments are not supported.");
}
/// Request new server key generation. Generated key will be published via ServerKeyGenerated event when available.
function generateServerKey(bytes32 serverKeyId, uint8 threshold)
external
payable
override
whenFeePaid(serverKeyGenerationFee)
{
// we can't process requests with invalid threshold
require(threshold + 1 <= keyServersCount(), "Threshold is invalid.");
// check maximum number of requests
require(
serverKeyGenerationRequestsKeys.length < maxServerKeyGenerationRequests,
"Maximum number of requests reached."
);
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
require(request.author == address(0), "Request author address cannot be 0x0.");
deposit();
request.author = msg.sender;
request.threshold = threshold;
serverKeyGenerationRequestsKeys.push(serverKeyId);
emit ServerKeyGenerationRequested(serverKeyId, msg.sender, threshold);
}
/// Called when generation is reported by key server.
function serverKeyGenerated(bytes32 serverKeyId, bytes calldata serverKeyPublic)
external
override
validPublic(serverKeyPublic)
{
// check if request still active
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
if (request.author == address(0)) {
return;
}
// insert response (we're waiting for responses from all authorities here)
uint8 keyServerIndex = requireKeyServer(msg.sender);
bytes32 response = keccak256(serverKeyPublic);
ResponseSupport responseSupport = insertResponse(
request.responses,
keyServerIndex,
keyServersCount() - 1,
response);
// ...and check if there are enough support
if (responseSupport == ResponseSupport.Unconfirmed) { // not confirmed (yet)
return;
}
// delete request and fire event
clearServerKeyGenerationRequest(serverKeyId, request);
if (responseSupport == ResponseSupport.Confirmed) { // confirmed
emit ServerKeyGenerated(serverKeyId, serverKeyPublic);
} else { // no consensus possible at all
emit ServerKeyGenerationError(serverKeyId);
}
}
/// Called when error occurs during server key generation.
function serverKeyGenerationError(bytes32 serverKeyId)
external
override
{
// check that it is called by key server
requireKeyServer(msg.sender);
// check if request still active
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
if (request.author == address(0)) {
return;
}
// any error in key generation is fatal, because we need all key servers to participate in generation
// => delete request and fire event
clearServerKeyGenerationRequest(serverKeyId, request);
emit ServerKeyGenerationError(serverKeyId);
}
/// Get count of pending server key generation requests.
function serverKeyGenerationRequestsCount()
external
view
override
returns (uint256)
{
return serverKeyGenerationRequestsKeys.length;
}
/// Get server key generation request with given index.
/// Returns: (serverKeyId, author, threshold)
function getServerKeyGenerationRequest(uint256 index)
external
view
override
returns (bytes32, address, uint256)
{
bytes32 serverKeyId = serverKeyGenerationRequestsKeys[index];
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
return (
serverKeyId,
request.author,
request.threshold
);
}
/// Returs true if response from given keyServer is required.
function isServerKeyGenerationResponseRequired(
bytes32 serverKeyId,
address keyServer
)
external
view
override
returns (bool)
{
uint8 keyServerIndex = requireKeyServer(keyServer);
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
return isResponseRequired(request.responses, keyServerIndex);
}
// === Administrative methods ===
/// Set server key generation fee.
function setServerKeyGenerationFee(uint256 newFee)
public
onlyOwner
{
serverKeyGenerationFee = newFee;
}
/// Set server key generation requests limit.
function setMaxServerKeyGenerationRequests(uint256 newLimit)
public
onlyOwner
{
maxServerKeyGenerationRequests = newLimit;
}
/// Delete server key generation request.
function deleteServerKeyGenerationRequest(bytes32 serverKeyId)
public
onlyOwner
{
ServerKeyGenerationRequest storage request = serverKeyGenerationRequests[serverKeyId];
clearServerKeyGenerationRequest(serverKeyId, request);
emit ServerKeyGenerationError(serverKeyId);
}
// === Internal methods ===
/// Clear server key generation request traces.
function clearServerKeyGenerationRequest(bytes32 serverKeyId, ServerKeyGenerationRequest storage request) private {
clearResponses(request.responses);
delete serverKeyGenerationRequests[serverKeyId];
removeRequestKey(serverKeyGenerationRequestsKeys, serverKeyId);
}
/// Server key generation fee.
uint256 public serverKeyGenerationFee;
/// Maximal number of active server key generation requests. We're limiting this number to avoid
/// infinite gas costs of some functions.
uint256 public maxServerKeyGenerationRequests;
/// Pending generation requests.
mapping (bytes32 => ServerKeyGenerationRequest) private serverKeyGenerationRequests;
/// Pending generation requests keys.
bytes32[] private serverKeyGenerationRequestsKeys;
}
pragma solidity ^0.5.0;
library StringUtils {
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
uint len;
uint i = 0;
uint bytelength = bytes(s).length;
for(len = 0; i < bytelength; len++) {
byte b = bytes(s)[i];
if(b < 0x80) {
i += 1;
} else if (b < 0xE0) {
i += 2;
} else if (b < 0xF0) {
i += 3;
} else if (b < 0xF8) {
i += 4;
} else if (b < 0xFC) {
i += 5;
} else {
i += 6;
}
}
return len;
}
}
interface PriceOracle {
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param expires When the name presently expires (0 if this is a new registration).
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint expires, uint duration) external view returns(uint);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
interface DSValue {
function read() external view returns (bytes32);
}
// StablePriceOracle sets a price in USD, based on an oracle.
contract StablePriceOracle is Ownable, PriceOracle {
using SafeMath for *;
using StringUtils for *;
// Oracle address
DSValue usdOracle;
// Rent in attodollars (1e-18) per second
uint[] public rentPrices;
event OracleChanged(address oracle);
event RentPriceChanged(uint[] prices);
constructor(DSValue _usdOracle, uint[] memory _rentPrices) public {
setOracle(_usdOracle);
setPrices(_rentPrices);
}
/**
* @dev Sets the price oracle address
* @param _usdOracle The address of the price oracle to use.
*/
function setOracle(DSValue _usdOracle) public onlyOwner {
usdOracle = _usdOracle;
emit OracleChanged(address(_usdOracle));
}
/**
* @dev Sets rent prices.
* @param _rentPrices The price array. Each element corresponds to a specific
* name length; names longer than the length of the array
* default to the price of the last element.
*/
function setPrices(uint[] memory _rentPrices) public onlyOwner {
rentPrices = _rentPrices;
emit RentPriceChanged(_rentPrices);
}
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint /*expires*/, uint duration) view external returns(uint) {
uint len = name.strlen();
if(len > rentPrices.length) {
len = rentPrices.length;
}
require(len > 0);
uint priceUSD = rentPrices[len - 1].mul(duration);
// Price of one ether in attodollars
uint ethPrice = uint(usdOracle.read());
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return priceUSD.mul(1e18).div(ethPrice);
}
}
pragma solidity ^0.5.14;
contract TestAgg {
uint256 public current;
function feed(uint256 _feed) public {
current = _feed;
}
function sig(string memory _sig) public pure returns (bytes32) {
return keccak256(bytes(_sig));
}
function sig2(string memory _sig) public pure returns (bytes4) {
return bytes4(keccak256(bytes(_sig)));
}
}
pragma solidity ^0.5.0;
contract TokenId {
function getId(string memory _name) public pure returns(uint256) {
bytes32 label = keccak256(bytes(_name));
return uint256(label);
}
}
pragma solidity ^0.5.0;
library StringUtils {
/**
* @dev Returns the length of a given string
*
* @param s The string to measure the length of
* @return The length of the input string
*/
function strlen(string memory s) internal pure returns (uint) {
uint len;
uint i = 0;
uint bytelength = bytes(s).length;
for(len = 0; i < bytelength; len++) {
byte b = bytes(s)[i];
if(b < 0x80) {
i += 1;
} else if (b < 0xE0) {
i += 2;
} else if (b < 0xF0) {
i += 3;
} else if (b < 0xF8) {
i += 4;
} else if (b < 0xFC) {
i += 5;
} else {
i += 6;
}
}
return len;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
interface PriceOracle {
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param expires When the name presently expires (0 if this is a new registration).
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint expires, uint duration) external view returns(uint);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// 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;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
interface DSValue {
function read() external view returns (bytes32);
}
// StablePriceOracle sets a price in USD, based on an oracle.
contract StablePriceOracle is Ownable, PriceOracle {
using SafeMath for *;
using StringUtils for *;
// Oracle address
DSValue usdOracle;
// Rent in attodollars (1e-18) per second
uint[] public rentPrices;
event OracleChanged(address oracle);
event RentPriceChanged(uint[] prices);
constructor(DSValue _usdOracle, uint[] memory _rentPrices) public {
setOracle(_usdOracle);
setPrices(_rentPrices);
}
/**
* @dev Sets the price oracle address
* @param _usdOracle The address of the price oracle to use.
*/
function setOracle(DSValue _usdOracle) public onlyOwner {
usdOracle = _usdOracle;
emit OracleChanged(address(_usdOracle));
}
/**
* @dev Sets rent prices.
* @param _rentPrices The price array. Each element corresponds to a specific
* name length; names longer than the length of the array
* default to the price of the last element.
*/
function setPrices(uint[] memory _rentPrices) public onlyOwner {
rentPrices = _rentPrices;
emit RentPriceChanged(_rentPrices);
}
/**
* @dev Returns the price to register or renew a name.
* @param name The name being registered or renewed.
* @param duration How long the name is being registered or extended for, in seconds.
* @return The price of this renewal or registration, in wei.
*/
function price(string calldata name, uint /*expires*/, uint duration) view external returns(uint) {
uint len = name.strlen();
if(len > rentPrices.length) {
len = rentPrices.length;
}
require(len > 0);
uint priceUSD = rentPrices[len - 1].mul(duration);
// Price of one ether in attodollars
uint ethPrice = uint(usdOracle.read());
// priceUSD and ethPrice are both fixed-point values with 18dp, so we
// multiply the numerator by 1e18 before dividing.
return priceUSD.mul(1e18).div(ethPrice);
}
}
// StablePriceOracle sets a price in USD, based on an oracle.
contract TokenPriceOracle is Ownable, DSValue {
// Price of 1 ether in atto
uint256 public current;
constructor (uint256 _current)
public
{
current = _current;
}
function read()
external
view
returns (bytes32)
{
return bytes32(current);
}
function setCurrent(uint256 _current)
public
onlyOwner
{
current = _current;
}
}
// this line is added to create a gist. Empty file is not allowed.
pragma solidity ^0.5.0;
contract Withdraw {
constructor() public payable {}
function withdraw() public {
msg.sender.transfer(address(this).balance);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment