Created
December 9, 2019 18:06
-
-
Save patitonar/a19287408dfa878d7be1dca95835625f to your computer and use it in GitHub Desktop.
HomeBridgeErcToNative.sol for home xDai bridge in xDai chain. Related to https://github.com/poanetwork/tokenbridge-contracts/pull/319
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
uint8 amount; | |
assembly { | |
amount := mload(add(_signatures, 1)) | |
} | |
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: contracts/interfaces/IBlockReward.sol | |
pragma solidity 0.4.24; | |
interface IBlockReward { | |
function addExtraReceiver(uint256 _amount, address _receiver) external; | |
function mintedTotally() external view returns (uint256); | |
function mintedTotallyByBridge(address _bridge) external view returns (uint256); | |
function bridgesAllowedLength() external view returns (uint256); | |
function addBridgeTokenFeeReceivers(uint256 _amount) external; | |
function addBridgeNativeFeeReceivers(uint256 _amount) external; | |
function blockRewardContractId() external pure returns (bytes4); | |
} | |
// 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 { | |
/** | |
* @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 */ | |
_; | |
} | |
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 < dailyLimit()); | |
uintStorage[MAX_PER_TX] = _maxPerTx; | |
} | |
function setMinPerTx(uint256 _minPerTx) external onlyOwner { | |
require(_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/BaseOverdrawManagement.sol | |
pragma solidity 0.4.24; | |
contract BaseOverdrawManagement is EternalStorage { | |
event AmountLimitExceeded(address recipient, uint256 value, bytes32 transactionHash); | |
event AssetAboveLimitsFixed(bytes32 indexed transactionHash, uint256 value, uint256 remaining); | |
bytes32 internal constant OUT_OF_LIMIT_AMOUNT = 0x145286dc85799b6fb9fe322391ba2d95683077b2adf34dd576dedc437e537ba7; // keccak256(abi.encodePacked("outOfLimitAmount")) | |
function outOfLimitAmount() public view returns (uint256) { | |
return uintStorage[OUT_OF_LIMIT_AMOUNT]; | |
} | |
function fixedAssets(bytes32 _txHash) public view returns (bool) { | |
return boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))]; | |
} | |
function setOutOfLimitAmount(uint256 _value) internal { | |
uintStorage[OUT_OF_LIMIT_AMOUNT] = _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; | |
setTxAboveLimitsValue(_value, _txHash); | |
} | |
function setTxAboveLimitsValue(uint256 _value, bytes32 _txHash) internal { | |
uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _txHash))] = _value; | |
} | |
function setFixedAssets(bytes32 _txHash) internal { | |
boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))] = true; | |
} | |
/* solcov ignore next */ | |
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign, uint256 valueToUnlock) external; | |
} | |
// File: contracts/upgradeable_contracts/OverdrawManagement.sol | |
pragma solidity 0.4.24; | |
contract OverdrawManagement is BaseOverdrawManagement, RewardableBridge, Upgradeable, BasicTokenBridge { | |
using SafeMath for uint256; | |
event UserRequestForSignature(address recipient, uint256 value); | |
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign, uint256 valueToUnlock) | |
external | |
onlyIfUpgradeabilityOwner | |
{ | |
require(!fixedAssets(txHash)); | |
require(valueToUnlock <= maxPerTx()); | |
address recipient; | |
uint256 value; | |
(recipient, value) = txAboveLimits(txHash); | |
require(recipient != address(0) && value > 0 && value >= valueToUnlock); | |
setOutOfLimitAmount(outOfLimitAmount().sub(valueToUnlock)); | |
uint256 pendingValue = value.sub(valueToUnlock); | |
setTxAboveLimitsValue(pendingValue, txHash); | |
emit AssetAboveLimitsFixed(txHash, valueToUnlock, pendingValue); | |
if (pendingValue == 0) { | |
setFixedAssets(txHash); | |
} | |
if (unlockOnForeign) { | |
address feeManager = feeManagerContract(); | |
uint256 eventValue = valueToUnlock; | |
if (feeManager != address(0)) { | |
uint256 fee = calculateFee(valueToUnlock, false, feeManager, HOME_FEE); | |
eventValue = valueToUnlock.sub(fee); | |
} | |
emit UserRequestForSignature(recipient, eventValue); | |
} | |
} | |
} | |
// File: contracts/upgradeable_contracts/erc20_to_native/RewardableHomeBridgeErcToNative.sol | |
pragma solidity 0.4.24; | |
contract RewardableHomeBridgeErcToNative is RewardableBridge { | |
bytes4 internal constant GET_AMOUNT_TO_BURN = 0x916850e9; // getAmountToBurn(uint256) | |
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); | |
} | |
function getAmountToBurn(uint256 _value) public view returns (uint256) { | |
uint256 amount; | |
bytes memory callData = abi.encodeWithSelector(GET_AMOUNT_TO_BURN, _value); | |
address feeManager = feeManagerContract(); | |
assembly { | |
let result := callcode(gas, feeManager, 0x0, add(callData, 0x20), mload(callData), 0, 32) | |
amount := mload(0) | |
switch result | |
case 0 { | |
revert(0, 0) | |
} | |
} | |
return amount; | |
} | |
} | |
// File: contracts/upgradeable_contracts/BlockRewardBridge.sol | |
pragma solidity 0.4.24; | |
contract BlockRewardBridge is EternalStorage { | |
bytes32 internal constant BLOCK_REWARD_CONTRACT = 0x20ae0b8a761b32f3124efb075f427dd6ca669e88ae7747fec9fd1ad688699f32; // keccak256(abi.encodePacked("blockRewardContract")) | |
bytes4 internal constant BLOCK_REWARD_CONTRACT_ID = 0x2ee57f8d; // blockRewardContractId() | |
bytes4 internal constant BRIDGES_ALLOWED_LENGTH = 0x10f2ee7c; // bridgesAllowedLength() | |
function _blockRewardContract() internal view returns (IBlockReward) { | |
return IBlockReward(addressStorage[BLOCK_REWARD_CONTRACT]); | |
} | |
function _setBlockRewardContract(address _blockReward) internal { | |
require(AddressUtils.isContract(_blockReward)); | |
// Before store the contract we need to make sure that it is the block reward contract in actual fact, | |
// call a specific method from the contract that should return a specific value | |
bool isBlockRewardContract = false; | |
if (_blockReward.call(BLOCK_REWARD_CONTRACT_ID)) { | |
isBlockRewardContract = | |
IBlockReward(_blockReward).blockRewardContractId() == bytes4(keccak256("blockReward")); | |
} else if (_blockReward.call(BRIDGES_ALLOWED_LENGTH)) { | |
isBlockRewardContract = IBlockReward(_blockReward).bridgesAllowedLength() != 0; | |
} | |
require(isBlockRewardContract); | |
addressStorage[BLOCK_REWARD_CONTRACT] = _blockReward; | |
} | |
} | |
// File: contracts/upgradeable_contracts/erc20_to_native/HomeBridgeErcToNative.sol | |
pragma solidity 0.4.24; | |
contract HomeBridgeErcToNative is | |
EternalStorage, | |
BasicHomeBridge, | |
OverdrawManagement, | |
RewardableHomeBridgeErcToNative, | |
BlockRewardBridge | |
{ | |
bytes32 internal constant TOTAL_BURNT_COINS = 0x17f187b2e5d1f8770602b32c1159b85c9600859277fae1eaa9982e9bcf63384c; // keccak256(abi.encodePacked("totalBurntCoins")) | |
function() public payable { | |
require(msg.data.length == 0); | |
nativeTransfer(msg.sender); | |
} | |
function nativeTransfer(address _receiver) internal { | |
require(msg.value > 0); | |
require(withinLimit(msg.value)); | |
IBlockReward blockReward = blockRewardContract(); | |
uint256 totalMinted = blockReward.mintedTotallyByBridge(address(this)); | |
uint256 totalBurnt = totalBurntCoins(); | |
require(msg.value <= totalMinted.sub(totalBurnt)); | |
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value)); | |
uint256 valueToTransfer = msg.value; | |
address feeManager = feeManagerContract(); | |
uint256 valueToBurn = msg.value; | |
if (feeManager != address(0)) { | |
uint256 fee = calculateFee(valueToTransfer, false, feeManager, HOME_FEE); | |
valueToTransfer = valueToTransfer.sub(fee); | |
valueToBurn = getAmountToBurn(valueToBurn); | |
} | |
setTotalBurntCoins(totalBurnt.add(valueToBurn)); | |
address(0).transfer(valueToBurn); | |
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, | |
address _blockReward, | |
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ] | |
address _owner, | |
uint256 _decimalShift | |
) external returns (bool) { | |
_initialize( | |
_validatorContract, | |
_dailyLimitMaxPerTxMinPerTxArray, | |
_homeGasPrice, | |
_requiredBlockConfirmations, | |
_blockReward, | |
_foreignDailyLimitForeignMaxPerTxArray, | |
_owner, | |
_decimalShift | |
); | |
setInitialize(); | |
return isInitialized(); | |
} | |
function rewardableInitialize( | |
address _validatorContract, | |
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ] | |
uint256 _homeGasPrice, | |
uint256 _requiredBlockConfirmations, | |
address _blockReward, | |
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ] | |
address _owner, | |
address _feeManager, | |
uint256[] _homeFeeForeignFeeArray, // [ 0 = _homeFee, 1 = _foreignFee ] | |
uint256 _decimalShift | |
) external returns (bool) { | |
_initialize( | |
_validatorContract, | |
_dailyLimitMaxPerTxMinPerTxArray, | |
_homeGasPrice, | |
_requiredBlockConfirmations, | |
_blockReward, | |
_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 0x18762d46; // bytes4(keccak256(abi.encodePacked("erc-to-native-core"))) | |
} | |
function blockRewardContract() public view returns (IBlockReward) { | |
return _blockRewardContract(); | |
} | |
function totalBurntCoins() public view returns (uint256) { | |
return uintStorage[TOTAL_BURNT_COINS]; | |
} | |
function setBlockRewardContract(address _blockReward) external onlyOwner { | |
_setBlockRewardContract(_blockReward); | |
} | |
function _initialize( | |
address _validatorContract, | |
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ] | |
uint256 _homeGasPrice, | |
uint256 _requiredBlockConfirmations, | |
address _blockReward, | |
uint256[] _foreignDailyLimitForeignMaxPerTxArray, // [ 0 = _foreignDailyLimit, 1 = _foreignMaxPerTx ] | |
address _owner, | |
uint256 _decimalShift | |
) internal { | |
require(!isInitialized()); | |
require(AddressUtils.isContract(_validatorContract)); | |
require(_requiredBlockConfirmations > 0); | |
require( | |
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0 | |
_dailyLimitMaxPerTxMinPerTxArray[1] > _dailyLimitMaxPerTxMinPerTxArray[2] && // _maxPerTx > _minPerTx | |
_dailyLimitMaxPerTxMinPerTxArray[0] > _dailyLimitMaxPerTxMinPerTxArray[1] // _dailyLimit > _maxPerTx | |
); | |
require(_blockReward == address(0) || AddressUtils.isContract(_blockReward)); | |
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; | |
addressStorage[BLOCK_REWARD_CONTRACT] = _blockReward; | |
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 onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) { | |
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_value)); | |
IBlockReward blockReward = blockRewardContract(); | |
require(blockReward != address(0)); | |
uint256 valueToMint = _value.mul(10**decimalShift()); | |
address feeManager = feeManagerContract(); | |
if (feeManager != address(0)) { | |
uint256 fee = calculateFee(valueToMint, false, feeManager, FOREIGN_FEE); | |
distributeFeeFromAffirmation(fee, feeManager, txHash); | |
valueToMint = valueToMint.sub(fee); | |
} | |
blockReward.addExtraReceiver(valueToMint, _recipient); | |
return true; | |
} | |
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 setTotalBurntCoins(uint256 _amount) internal { | |
uintStorage[TOTAL_BURNT_COINS] = _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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment