Created
December 9, 2019 18:05
-
-
Save patitonar/c0b77a59eceb76aad05e010c40569d47 to your computer and use it in GitHub Desktop.
ForeignBridgeErcToNative.sol for foreign xDai bridge in ethereum mainnet. 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/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/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/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(); | |
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: 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/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/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/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/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/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/upgradeable_contracts/ERC20Bridge.sol | |
pragma solidity 0.4.24; | |
contract ERC20Bridge is BasicForeignBridge { | |
bytes32 internal constant ERC20_TOKEN = 0x15d63b18dbc21bf4438b7972d80076747e1d93c4f87552fe498c90cbde51665e; // keccak256(abi.encodePacked("erc20token")) | |
function erc20token() public view returns (ERC20) { | |
return ERC20(addressStorage[ERC20_TOKEN]); | |
} | |
function setErc20token(address _token) internal { | |
require(AddressUtils.isContract(_token)); | |
addressStorage[ERC20_TOKEN] = _token; | |
} | |
function _relayTokens(address _sender, address _receiver, uint256 _amount) internal { | |
require(_receiver != address(0)); | |
require(_receiver != address(this)); | |
require(_amount > 0); | |
require(withinLimit(_amount)); | |
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_amount)); | |
erc20token().transferFrom(_sender, address(this), _amount); | |
emit UserRequestForAffirmation(_receiver, _amount); | |
} | |
function relayTokens(address _from, address _receiver, uint256 _amount) external { | |
require(_from == msg.sender || _from == _receiver); | |
_relayTokens(_from, _receiver, _amount); | |
} | |
function relayTokens(address _receiver, uint256 _amount) external { | |
_relayTokens(msg.sender, _receiver, _amount); | |
} | |
} | |
// 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/interfaces/IScdMcdMigration.sol | |
pragma solidity 0.4.24; | |
interface IScdMcdMigration { | |
function swapSaiToDai(uint256 wad) external; | |
function daiJoin() external returns (address); | |
} | |
interface IDaiAdapter { | |
function dai() public returns (address); | |
} | |
interface ISaiTop { | |
function caged() public returns (uint256); | |
} | |
// File: contracts/upgradeable_contracts/erc20_to_native/ForeignBridgeErcToNative.sol | |
pragma solidity 0.4.24; | |
contract ForeignBridgeErcToNative is BasicForeignBridge, ERC20Bridge, OtherSideBridgeStorage { | |
event TokensSwapped(address indexed from, address indexed to, uint256 value); | |
bytes32 internal constant MIN_HDTOKEN_BALANCE = 0x48649cf195feb695632309f41e61252b09f537943654bde13eb7bb1bca06964e; // keccak256(abi.encodePacked("minHDTokenBalance")) | |
bytes4 internal constant SWAP_TOKENS = 0x73d00224; // swapTokens() | |
function initialize( | |
address _validatorContract, | |
address _erc20token, | |
uint256 _requiredBlockConfirmations, | |
uint256 _gasPrice, | |
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ] | |
uint256[] _homeDailyLimitHomeMaxPerTxArray, //[ 0 = _homeDailyLimit, 1 = _homeMaxPerTx ] | |
address _owner, | |
uint256 _decimalShift, | |
address _bridgeOnOtherSide | |
) external returns (bool) { | |
require(!isInitialized()); | |
require(AddressUtils.isContract(_validatorContract)); | |
require(_requiredBlockConfirmations != 0); | |
require(_gasPrice > 0); | |
require( | |
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0 | |
_dailyLimitMaxPerTxMinPerTxArray[1] > _dailyLimitMaxPerTxMinPerTxArray[2] && // _maxPerTx > _minPerTx | |
_dailyLimitMaxPerTxMinPerTxArray[0] > _dailyLimitMaxPerTxMinPerTxArray[1] // _dailyLimit > _maxPerTx | |
); | |
require(_homeDailyLimitHomeMaxPerTxArray[1] < _homeDailyLimitHomeMaxPerTxArray[0]); // _homeMaxPerTx < _homeDailyLimit | |
require(_owner != address(0)); | |
require(_bridgeOnOtherSide != address(0)); | |
addressStorage[VALIDATOR_CONTRACT] = _validatorContract; | |
setErc20token(_erc20token); | |
uintStorage[DEPLOYED_AT_BLOCK] = block.number; | |
uintStorage[REQUIRED_BLOCK_CONFIRMATIONS] = _requiredBlockConfirmations; | |
uintStorage[GAS_PRICE] = _gasPrice; | |
uintStorage[DAILY_LIMIT] = _dailyLimitMaxPerTxMinPerTxArray[0]; | |
uintStorage[MAX_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[1]; | |
uintStorage[MIN_PER_TX] = _dailyLimitMaxPerTxMinPerTxArray[2]; | |
uintStorage[EXECUTION_DAILY_LIMIT] = _homeDailyLimitHomeMaxPerTxArray[0]; | |
uintStorage[EXECUTION_MAX_PER_TX] = _homeDailyLimitHomeMaxPerTxArray[1]; | |
uintStorage[DECIMAL_SHIFT] = _decimalShift; | |
setOwner(_owner); | |
_setBridgeContractOnOtherSide(_bridgeOnOtherSide); | |
setInitialize(); | |
emit RequiredBlockConfirmationChanged(_requiredBlockConfirmations); | |
emit GasPriceChanged(_gasPrice); | |
emit DailyLimitChanged(_dailyLimitMaxPerTxMinPerTxArray[0]); | |
emit ExecutionDailyLimitChanged(_homeDailyLimitHomeMaxPerTxArray[0]); | |
return isInitialized(); | |
} | |
function getBridgeMode() external pure returns (bytes4 _data) { | |
return 0x18762d46; // bytes4(keccak256(abi.encodePacked("erc-to-native-core"))) | |
} | |
function claimTokens(address _token, address _to) public { | |
require(_token != address(erc20token())); | |
if (_token == address(halfDuplexErc20token())) { | |
// SCD is not claimable if the bridge accepts deposits of this token | |
// solhint-disable-next-line not-rely-on-time | |
require(!isTokenSwapAllowed(now)); | |
} | |
super.claimTokens(_token, _to); | |
} | |
function onExecuteMessage( | |
address _recipient, | |
uint256 _amount, | |
bytes32 /*_txHash*/ | |
) internal returns (bool) { | |
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_amount)); | |
uint256 amount = _amount.div(10**decimalShift()); | |
bool res = erc20token().transfer(_recipient, amount); | |
if (tokenBalance(halfDuplexErc20token()) > 0) { | |
address(this).call(abi.encodeWithSelector(SWAP_TOKENS)); | |
} | |
return res; | |
} | |
function onFailedMessage(address, uint256, bytes32) internal { | |
revert(); | |
} | |
function _relayTokens(address _sender, address _receiver, uint256 _amount) internal { | |
require(_receiver != bridgeContractOnOtherSide()); | |
super._relayTokens(_sender, _receiver, _amount); | |
} | |
function migrateToMCD() external { | |
bytes32 storageAddress = 0x3378953eb16363e06fd9ea9701d36ed7285d206d9de7df55b778462d74596a89; // keccak256(abi.encodePacked("migrationToMcdCompleted")) | |
require(!boolStorage[storageAddress]); | |
address mcdContract = IDaiAdapter(migrationContract().daiJoin()).dai(); | |
setErc20token(mcdContract); | |
uintStorage[MIN_HDTOKEN_BALANCE] = 10 ether; | |
swapTokens(); | |
boolStorage[storageAddress] = true; | |
} | |
function saiTopContract() internal pure returns (ISaiTop) { | |
return ISaiTop(0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3); | |
} | |
function isTokenSwapAllowed(uint256 _ts) public view returns (bool) { | |
uint256 esTs = saiTopContract().caged(); | |
if (esTs > 0 && _ts > esTs) { | |
return false; | |
} | |
return true; | |
} | |
function halfDuplexErc20token() public pure returns (ERC20) { | |
return ERC20(0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359); | |
} | |
function setMinHDTokenBalance(uint256 _minBalance) external onlyOwner { | |
uintStorage[MIN_HDTOKEN_BALANCE] = _minBalance; | |
} | |
function minHDTokenBalance() public view returns (uint256) { | |
return uintStorage[MIN_HDTOKEN_BALANCE]; | |
} | |
function isHDTokenBalanceAboveMinBalance() public view returns (bool) { | |
if (tokenBalance(halfDuplexErc20token()) > minHDTokenBalance()) { | |
return true; | |
} | |
return false; | |
} | |
function tokenBalance(ERC20 _token) internal view returns (uint256) { | |
return _token.balanceOf(address(this)); | |
} | |
function migrationContract() internal pure returns (IScdMcdMigration) { | |
return IScdMcdMigration(0xc73e0383F3Aff3215E6f04B0331D58CeCf0Ab849); | |
} | |
function swapTokens() public { | |
// solhint-disable-next-line not-rely-on-time | |
require(isTokenSwapAllowed(now)); | |
IScdMcdMigration mcdMigrationContract = migrationContract(); | |
ERC20 hdToken = halfDuplexErc20token(); | |
ERC20 fdToken = erc20token(); | |
uint256 curHDTokenBalance = tokenBalance(hdToken); | |
require(curHDTokenBalance > 0); | |
uint256 curFDTokenBalance = tokenBalance(fdToken); | |
require(hdToken.approve(mcdMigrationContract, curHDTokenBalance)); | |
mcdMigrationContract.swapSaiToDai(curHDTokenBalance); | |
require(tokenBalance(fdToken).sub(curFDTokenBalance) == curHDTokenBalance); | |
emit TokensSwapped(hdToken, fdToken, curHDTokenBalance); | |
} | |
function relayTokens(address _from, address _receiver, uint256 _amount, address _token) external { | |
require(_from == msg.sender || _from == _receiver); | |
_relayTokens(_from, _receiver, _amount, _token); | |
} | |
function relayTokens(address _receiver, uint256 _amount, address _token) external { | |
_relayTokens(msg.sender, _receiver, _amount, _token); | |
} | |
function _relayTokens(address _sender, address _receiver, uint256 _amount, address _token) internal { | |
require(_receiver != bridgeContractOnOtherSide()); | |
require(_receiver != address(0)); | |
require(_receiver != address(this)); | |
require(_amount > 0); | |
require(withinLimit(_amount)); | |
ERC20 tokenToOperate = ERC20(_token); | |
ERC20 hdToken = halfDuplexErc20token(); | |
ERC20 fdToken = erc20token(); | |
if (tokenToOperate == ERC20(0x0)) { | |
tokenToOperate = fdToken; | |
} | |
require(tokenToOperate == fdToken || tokenToOperate == hdToken); | |
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_amount)); | |
tokenToOperate.transferFrom(_sender, address(this), _amount); | |
emit UserRequestForAffirmation(_receiver, _amount); | |
if (tokenToOperate == hdToken) { | |
swapTokens(); | |
} | |
} | |
function upgradeToV250() public { | |
bytes32 upgradeStorage = 0x221181d03d3c423368b412861518cd98276a115f4c2d9594ab6697d90fc99c19; // keccak256(abi.encodePacked('isUpgradedToV250')) | |
require(!boolStorage[upgradeStorage]); | |
uintStorage[DAILY_LIMIT] = 100000 ether; | |
emit DailyLimitChanged(100000 ether); | |
uintStorage[MIN_PER_TX] = 5000000000000000; | |
_setBridgeContractOnOtherSide(0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6); | |
boolStorage[upgradeStorage] = true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment