Created
November 15, 2019 12:14
-
-
Save patitonar/2bcfd309ab3097c312dee7296c7fac72 to your computer and use it in GitHub Desktop.
Simplified Home Bridge for xDai upgrade includes zero value transfer fix(https://gist.github.com/patitonar/88e6d2a6b2253056ace9ec662b7465f6) and security fixes https://github.com/poanetwork/tokenbridge-contracts/pull/213 and https://github.com/poanetwork/tokenbridge-contracts/pull/247
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/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); | |
} | |
contract Sacrifice { | |
constructor(address _recipient) public payable { | |
selfdestruct(_recipient); | |
} | |
} | |
contract Claimable { | |
modifier validAddress(address _to) { | |
require(_to != address(0)); | |
_; | |
} | |
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.encodeWithSignature("transfer(address,uint256)", _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/BasicBridge.sol | |
pragma solidity 0.4.24; | |
contract BasicBridge is EternalStorage, Validatable, Ownable, OwnedUpgradeability, Claimable { | |
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 validAddress(_to) { | |
claimValues(_token, _to); | |
} | |
function isContract(address _addr) internal view returns (bool) | |
{ | |
uint length; | |
assembly { length := extcodesize(_addr) } | |
return length > 0; | |
} | |
} | |
// File: contracts/IBlockReward.sol | |
pragma solidity 0.4.24; | |
interface IBlockReward { | |
function addExtraReceiver(uint256 _amount, address _receiver) external; | |
function mintedTotally() public view returns (uint256); | |
function mintedTotallyByBridge(address _bridge) public view returns(uint256); | |
function bridgesAllowedLength() external view returns(uint256); | |
} | |
// 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)); | |
} | |
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); | |
} | |
} | |
function setMessagesSigned(bytes32 _hash, bool _status) internal { | |
boolStorage[keccak256(abi.encodePacked("messagesSigned", _hash))] = _status; | |
} | |
function onExecuteAffirmation(address, uint256) internal returns(bool) { | |
} | |
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: 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/upgradeable_contracts/ERC677Bridge.sol | |
pragma solidity 0.4.24; | |
contract ERC677Bridge is BasicBridge { | |
function erc677token() public view returns(IBurnableMintableERC677Token) { | |
return IBurnableMintableERC677Token(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) { | |
require(msg.sender == address(erc677token())); | |
require(withinLimit(_value)); | |
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value)); | |
erc677token().burn(_value); | |
fireEventOnTokenTransfer(_from, _value); | |
return true; | |
} | |
function fireEventOnTokenTransfer(address /*_from */, uint256 /* _value */) internal { | |
// has to be defined | |
} | |
} | |
// File: contracts/upgradeable_contracts/OverdrawManagement.sol | |
pragma solidity 0.4.24; | |
contract OverdrawManagement is EternalStorage, OwnedUpgradeability, BasicBridge { | |
using SafeMath for uint256; | |
event UserRequestForSignature(address recipient, uint256 value); | |
event AssetAboveLimitsFixed(bytes32 indexed transactionHash, uint256 value, uint256 remaining); | |
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign, uint256 valueToUnlock) external onlyIfOwnerOfProxy { | |
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, true); | |
} | |
if (unlockOnForeign) { | |
emit UserRequestForSignature(recipient, valueToUnlock); | |
} | |
} | |
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; | |
setTxAboveLimitsValue(_value, _txHash); | |
} | |
function setTxAboveLimitsValue(uint256 _value, bytes32 _txHash) internal { | |
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/erc20_to_native/HomeBridgeErcToNative.sol | |
pragma solidity 0.4.24; | |
contract HomeBridgeErcToNative is EternalStorage, BasicBridge, BasicHomeBridge, OverdrawManagement { | |
event AmountLimitExceeded(address recipient, uint256 value, bytes32 transactionHash); | |
function () public payable { | |
require(msg.value > 0); | |
require(msg.data.length == 0); | |
require(withinLimit(msg.value)); | |
IBlockReward blockReward = blockRewardContract(); | |
uint256 totalMinted = blockReward.mintedTotallyByBridge(address(this)); | |
require(msg.value <= totalMinted.sub(totalBurntCoins())); | |
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(msg.value)); | |
setTotalBurntCoins(totalBurntCoins().add(msg.value)); | |
address(0).transfer(msg.value); | |
emit UserRequestForSignature(msg.sender, msg.value); | |
} | |
function initialize ( | |
address _validatorContract, | |
uint256 _dailyLimit, | |
uint256 _maxPerTx, | |
uint256 _minPerTx, | |
uint256 _homeGasPrice, | |
uint256 _requiredBlockConfirmations, | |
address _blockReward, | |
uint256 _foreignDailyLimit, | |
uint256 _foreignMaxPerTx, | |
address _owner | |
) public returns(bool) | |
{ | |
require(!isInitialized()); | |
require(_validatorContract != address(0) && isContract(_validatorContract)); | |
require(_requiredBlockConfirmations > 0); | |
require(_minPerTx > 0 && _maxPerTx > _minPerTx && _dailyLimit > _maxPerTx); | |
require(_blockReward == address(0) || isContract(_blockReward)); | |
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; | |
addressStorage[keccak256(abi.encodePacked("blockRewardContract"))] = _blockReward; | |
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _foreignDailyLimit; | |
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _foreignMaxPerTx; | |
setOwner(_owner); | |
setInitialize(true); | |
return isInitialized(); | |
} | |
function getBridgeMode() public pure returns(bytes4 _data) { | |
return bytes4(keccak256(abi.encodePacked("erc-to-native-core"))); | |
} | |
function blockRewardContract() public view returns(IBlockReward) { | |
return IBlockReward(addressStorage[keccak256(abi.encodePacked("blockRewardContract"))]); | |
} | |
function totalBurntCoins() public view returns(uint256) { | |
return uintStorage[keccak256(abi.encodePacked("totalBurntCoins"))]; | |
} | |
function setBlockRewardContract(address _blockReward) public onlyOwner { | |
require(_blockReward != address(0) && isContract(_blockReward) && (IBlockReward(_blockReward).bridgesAllowedLength() != 0)); | |
addressStorage[keccak256(abi.encodePacked("blockRewardContract"))] = _blockReward; | |
} | |
function onExecuteAffirmation(address _recipient, uint256 _value) internal returns(bool) { | |
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_value)); | |
IBlockReward blockReward = blockRewardContract(); | |
require(blockReward != address(0)); | |
blockReward.addExtraReceiver(_value, _recipient); | |
return true; | |
} | |
function fireEventOnTokenTransfer(address _from, uint256 _value) internal { | |
emit UserRequestForSignature(_from, _value); | |
} | |
function setTotalBurntCoins(uint256 _amount) internal { | |
uintStorage[keccak256(abi.encodePacked("totalBurntCoins"))] = _amount; | |
} | |
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment