Skip to content

Instantly share code, notes, and snippets.

@patitonar
Created December 9, 2019 18:05
Show Gist options
  • Save patitonar/c0b77a59eceb76aad05e010c40569d47 to your computer and use it in GitHub Desktop.
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
// 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