// File: SignatureUtils.sol
pragma solidity ^0.4.24;
/// @title A library of utilities for (multi)signatures
/// @author Alexander Kern <alex@cleargraph.com>
/// @dev This library can be linked to another Solidity contract to expose signature manipulation functions.
contract SignatureUtils {
/// @notice Converts a bytes32 to an signed message hash.
/// @param _msg The bytes32 message (i.e. keccak256 result) to encrypt
function toEthBytes32SignedMessageHash(
bytes32 _msg
)
pure
public
returns (bytes32 signHash)
{
signHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _msg));
}
/// @notice Converts a byte array to a personal signed message hash (result of `web3.personal.sign(...)`) by concatenating its length.
/// @param _msg The bytes array to encrypt
function toEthPersonalSignedMessageHash(
bytes _msg
)
pure
public
returns (bytes32 signHash)
{
signHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", uintToString(_msg.length), _msg));
}
/// @notice Converts a uint to its decimal string representation.
/// @param v The uint to convert
function uintToString(
uint v
)
pure
public
returns (string)
{
uint w = v;
bytes32 x;
if (v == 0) {
x = "0";
} else {
while (w > 0) {
x = bytes32(uint(x) / (2 ** 8));
x |= bytes32(((w % 10) + 48) * 2 ** (8 * 31));
w /= 10;
}
}
bytes memory bytesString = new bytes(32);
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory resultBytes = new bytes(charCount);
for (j = 0; j < charCount; j++) {
resultBytes[j] = bytesString[j];
}
return string(resultBytes);
}
/// @notice Extracts the r, s, and v parameters to `ecrecover(...)` from the signature at position `_pos` in a densely packed signatures bytes array.
/// @dev Based on [OpenZeppelin's ECRecovery](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol)
/// @param _signatures The signatures bytes array
/// @param _pos The position of the signature in the bytes array (0 indexed)
function parseSignature(
bytes _signatures,
uint _pos
)
pure
public
returns (uint8 v, bytes32 r, bytes32 s)
{
uint offset = _pos * 65;
// The signature format is a compact form of:
// {bytes32 r}{bytes32 s}{uint8 v}
// Compact means, uint8 is not padded to 32 bytes.
assembly { // solium-disable-line security/no-inline-assembly
r := mload(add(_signatures, add(32, offset)))
s := mload(add(_signatures, add(64, offset)))
// Here we are loading the last 32 bytes, including 31 bytes
// of 's'. There is no 'mload8' to do this.
//
// 'byte' is not working due to the Solidity parser, so lets
// use the second best option, 'and'
v := and(mload(add(_signatures, add(65, offset))), 0xff)
}
if (v < 27) v += 27;
require(v == 27 || v == 28);
}
/// @notice Counts the number of signatures in a signatures bytes array. Returns 0 if the length is invalid.
/// @param _signatures The signatures bytes array
/// @dev Signatures are 65 bytes long and are densely packed.
function countSignatures(
bytes _signatures
)
pure
public
returns (uint)
{
return _signatures.length % 65 == 0 ? _signatures.length / 65 : 0;
}
/// @notice Recovers an address using a message hash and a signature in a bytes array.
/// @param _hash The signed message hash
/// @param _signatures The signatures bytes array
/// @param _pos The signature's position in the bytes array (0 indexed)
function recoverAddress(
bytes32 _hash,
bytes _signatures,
uint _pos
)
pure
public
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = parseSignature(_signatures, _pos);
return ecrecover(_hash, v, r, s);
}
/// @notice Recovers an array of addresses using a message hash and a signatures bytes array.
/// @param _hash The signed message hash
/// @param _signatures The signatures bytes array
function recoverAddresses(
bytes32 _hash,
bytes _signatures
)
pure
public
returns (address[] addresses)
{
uint8 v;
bytes32 r;
bytes32 s;
uint count = countSignatures(_signatures);
addresses = new address[](count);
for (uint i = 0; i < count; i++) {
(v, r, s) = parseSignature(_signatures, i);
addresses[i] = ecrecover(_hash, v, r, s);
}
}
}
// File: ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender)
external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value)
external returns (bool);
function transferFrom(address from, address to, uint256 value)
external returns (bool);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts 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 Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
* Originally based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowed;
uint256 private _totalSupply;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(
address owner,
address spender
)
public
view
returns (uint256)
{
return _allowed[owner][spender];
}
/**
* @dev Transfer token for a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(address to, uint256 value) public returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function approve(address spender, uint256 value) public returns (bool) {
require(spender != address(0));
_allowed[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function transferFrom(
address from,
address to,
uint256 value
)
public
returns (bool)
{
require(value <= _allowed[from][msg.sender]);
_allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
_transfer(from, to, value);
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(
address spender,
uint256 addedValue
)
public
returns (bool)
{
require(spender != address(0));
_allowed[msg.sender][spender] = (
_allowed[msg.sender][spender].add(addedValue));
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
)
public
returns (bool)
{
require(spender != address(0));
_allowed[msg.sender][spender] = (
_allowed[msg.sender][spender].sub(subtractedValue));
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
return true;
}
/**
* @dev Transfer token for a specified addresses
* @param from The address to transfer from.
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(value <= _balances[from]);
require(to != address(0));
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != 0);
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal {
require(account != 0);
require(value <= _balances[account]);
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal burn function.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burnFrom(address account, uint256 value) internal {
require(value <= _allowed[account][msg.sender]);
// Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
// this function needs to emit an event with the updated approval.
_allowed[account][msg.sender] = _allowed[account][msg.sender].sub(
value);
_burn(account, value);
}
}
// File: Bridgeable.sol
pragma solidity ^0.4.24;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
if (msg.sender != owner) {
throw;
}
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
contract Bridgeable is ERC20, SignatureUtils, Ownable {
address[] public validators;
address public foreignContract;
mapping (bytes32 => bool) foreignTransactions;
event EnterBridgeEvent(address indexed from, uint256 amount);
event ExitBridgeEvent(address indexed sender, uint256 amount);
event Mint(address indexed to, uint256 amount);
event Burn(address indexed burner, uint256 value);
function addValidator(address _validator) public onlyOwner {
validators.push(_validator);
}
function pair(address _foreignContract) public onlyOwner {
foreignContract = _foreignContract;
}
function enter(uint256 _amount) public {
emit EnterBridgeEvent(msg.sender, _amount);
burn(_amount);
}
function exit(bytes32 _txnHash, address _foreignContract, uint256 _amount, bytes _signatures) public {
require(contains(_txnHash) == false, 'Foreign transaction has already been processed');
bytes32 hash = toEthBytes32SignedMessageHash(entranceHash(_txnHash,_foreignContract, _amount));
address[] memory recovered = recoverAddresses(hash, _signatures);
require(verifyValidators(recovered), "Validator verification failed.");
require(_foreignContract == foreignContract, "Invalid contract target.");
mint(msg.sender, _amount);
foreignTransactions[_txnHash] = true;
emit ExitBridgeEvent(msg.sender, _amount);
}
function contains(bytes32 _txnHash) internal view returns (bool){
return foreignTransactions[_txnHash];
}
function verifyValidators(address[] recovered) internal view returns (bool) {
require(recovered.length == validators.length, "Invalid number of signatures");
for(uint i = 0 ; i < validators.length; i++) {
if(validators[i] != recovered[i]) {
return false;
}
}
return true;
}
function mint( address _to, uint256 _amount )
internal returns (bool) {
_mint(_to, _amount);
// emit Mint(_to, _amount);
return true;
}
/**
* @dev Burns a specific amount of tokens.
* @param _value The amount of token to be burned.
*/
function burn(uint256 _value) internal {
_burn(msg.sender, _value);
}
/**
* @notice Hash (keccak256) of the payload used by deposit
* @param _contractAddress the target ERC20 address
* @param _amount the original minter
*/
function entranceHash(bytes32 txnHash, address _contractAddress, uint256 _amount) public view returns (bytes32) {
// "0x8177cf3c": entranceHash(bytes32, address,uint256)
return keccak256(abi.encode( bytes4(0x8177cf3c), msg.sender, txnHash, _contractAddress, _amount));
}
}
// File: bECH.sol
pragma solidity ^0.4.25;
contract BridgedEchelon is Bridgeable {
string public name = "Bridged Echelon";
string public symbol = "bECH";
uint public decimals = 18;
constructor() public {
// totalSupply = INITIAL_SUPPLY / 2;
// balanceOf[msg.sender] = totalSupply;
}
}
{
"compilationTarget": {
"BridgedEchelon.sol": "BridgedEchelon"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_signatures","type":"bytes"},{"name":"_pos","type":"uint256"}],"name":"recoverAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_signatures","type":"bytes"}],"name":"countSignatures","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"validators","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_validator","type":"address"}],"name":"addValidator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_foreignContract","type":"address"}],"name":"pair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"txnHash","type":"bytes32"},{"name":"_contractAddress","type":"address"},{"name":"_amount","type":"uint256"}],"name":"entranceHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"enter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"foreignContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_signatures","type":"bytes"},{"name":"_pos","type":"uint256"}],"name":"parseSignature","outputs":[{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"_msg","type":"bytes"}],"name":"toEthPersonalSignedMessageHash","outputs":[{"name":"signHash","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_msg","type":"bytes32"}],"name":"toEthBytes32SignedMessageHash","outputs":[{"name":"signHash","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_txnHash","type":"bytes32"},{"name":"_foreignContract","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_signatures","type":"bytes"}],"name":"exit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"v","type":"uint256"}],"name":"uintToString","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"_hash","type":"bytes32"},{"name":"_signatures","type":"bytes"}],"name":"recoverAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"EnterBridgeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ExitBridgeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}]