Contract Address Details

0x9971B0E163795c49cAF5DefF06C271fCd8f3Ebe9

Links Last Balance Update: Block #7337084
Created by 0x4b9d–d6a29f at 0xa81a–75aae5

Balance

374.309090305999999003 xDAI

(@ /xDAI)

Fetching tokens...

Contract name:
Links




Optimization enabled
true
Compiler version
v0.4.25+commit.59dbf8f1





Contract source code

/**
* Submitted for verification at blockscout.com on 2019-03-01 16:36:16.633458Z
*/
pragma solidity 0.4.25;
/**
* @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 Elliptic curve signature operations
* @dev Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
* TODO Remove this library once solidity supports passing a signature to ecrecover.
* See https://github.com/ethereum/solidity/issues/864
*/
library ECDSA {
/**
* @dev Recover signer address from a message by using their signature
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
* @param signature bytes signature, the signature is generated using web3.eth.sign()
*/
function recover(bytes32 hash, bytes signature)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}
// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
// solium-disable-next-line arg-overflow
return ecrecover(hash, v, r, s);
}
}
/**
* toEthSignedMessageHash
* @dev prefix a bytes32 value with "\x19Ethereum Signed Message:"
* and hash the result
*/
function toEthSignedMessageHash(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
);
}
}
// Contract that implements the relay recipient protocol. Inherited by Gatekeeper, or any other relay recipient.
//
// The recipient contract is responsible to:
// * pass a trusted RelayHub singleton to the constructor.
// * Implement accept_relayed_call, which acts as a whitelist/blacklist of senders. It is advised that the recipient's owner will be able to update that list to remove abusers.
// * In every function that cares about the sender, use "address sender = get_sender()" instead of msg.sender. It'll return msg.sender for non-relayed transactions, or the real sender in case of relayed transactions.
contract RelayRecipientApi {
/**
* return the relayHub of this contract.
*/
function get_hub_addr() public view returns (address);
/**
* return the contract's balance on the RelayHub.
* can be used to determine if the contract can pay for incoming calls,
* before making any.
*/
function get_recipient_balance() public view returns (uint);
}
contract RelayHubApi {
event Staked(address indexed relay, uint stake);
event Unstaked(address indexed relay, uint stake);
/* RelayAdded is emitted whenever a relay [re-]registers with the RelayHub.
* filtering on these events (and filtering out RelayRemoved events) lets the client
* find which relays are currently registered.
*/
event RelayAdded(address indexed relay, address indexed owner, uint transactionFee, uint stake, uint unstakeDelay, string url);
// emitted when a relay is removed
event RelayRemoved(address indexed relay, uint unstake_time);
/**
* this events is emited whenever a transaction is relayed.
* notice that the actual function call on the target contract might be reverted - in that case, the "success"
* flag will be set to false.
* the client uses this event so it can report correctly transaction complete (or revert) to the application.
* Monitoring tools can use this event to detect liveliness of clients and relays.
*/
event TransactionRelayed(address indexed relay, address indexed from, address indexed target, bytes32 hash, bool success, uint charge);
event Deposited(address src, uint amount);
event Withdrawn(address dest, uint amount);
event Penalized(address indexed relay, address sender, uint amount);
function get_nonce(address from) view external returns (uint);
function relay(address from, address to, bytes memory encoded_function, uint transaction_fee, uint gas_price, uint gas_limit, uint nonce, bytes memory sig) public;
function depositFor(address target) public payable;
function balanceOf(address target) external view returns (uint256);
function stake(address relayaddr, uint unstake_delay) external payable;
function stakeOf(address relayaddr) external view returns (uint256);
function ownerOf(address relayaddr) external view returns (address);
}
/*
* Taken from https://github.com/hamdiallam/Solidity-RLP
*/
library RLPReader {
uint8 constant STRING_SHORT_START = 0x80;
uint8 constant STRING_LONG_START = 0xb8;
uint8 constant LIST_SHORT_START = 0xc0;
uint8 constant LIST_LONG_START = 0xf8;
uint8 constant WORD_SIZE = 32;
struct RLPItem {
uint len;
uint memPtr;
}
using RLPReader for bytes;
using RLPReader for uint;
using RLPReader for RLPReader.RLPItem;
// helper function to decode rlp encoded ethereum transaction
/*
* @param raw_transaction RLP encoded ethereum transaction
* @return tuple (nonce,gas_price,gas_limit,to,value,data)
*/
function decode_transaction(bytes memory raw_transaction) public pure returns (uint, uint, uint, address, uint, bytes memory){
RLPReader.RLPItem[] memory values = raw_transaction.toRlpItem().toList(); // must convert to an rlpItem first!
return (values[0].toUint(), values[1].toUint(), values[2].toUint(), values[3].toAddress(), values[4].toUint(), values[5].toBytes());
}
function bytesToBytes32(bytes memory b, uint offset) private pure returns (bytes32) {
bytes32 out;
for (uint i = 0; i < 32; i++) {
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
}
return out;
}
/*
* @param item RLP encoded bytes
*/
function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
if (item.length == 0)
return RLPItem(0, 0);
uint memPtr;
assembly {
memPtr := add(item, 0x20)
}
return RLPItem(item.length, memPtr);
}
/*
* @param item RLP encoded list in bytes
*/
function toList(RLPItem memory item) internal pure returns (RLPItem[] memory result) {
require(isList(item), "isList failed");
uint items = numItems(item);
result = new RLPItem[](items);
uint memPtr = item.memPtr + _payloadOffset(item.memPtr);
uint dataLen;
for (uint i = 0; i < items; i++) {
dataLen = _itemLength(memPtr);
result[i] = RLPItem(dataLen, memPtr);
memPtr = memPtr + dataLen;
}
}
/*
* Helpers
*/
// @return indicator whether encoded payload is a list. negate this function call for isData.
function isList(RLPItem memory item) internal pure returns (bool) {
uint8 byte0;
uint memPtr = item.memPtr;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < LIST_SHORT_START)
return false;
return true;
}
// @return number of payload items inside an encoded list.
function numItems(RLPItem memory item) internal pure returns (uint) {
uint count = 0;
uint currPtr = item.memPtr + _payloadOffset(item.memPtr);
uint endPtr = item.memPtr + item.len;
while (currPtr < endPtr) {
currPtr = currPtr + _itemLength(currPtr);
// skip over an item
count++;
}
return count;
}
// @return entire rlp item byte length
function _itemLength(uint memPtr) internal pure returns (uint len) {
uint byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START)
return 1;
else if (byte0 < STRING_LONG_START)
return byte0 - STRING_SHORT_START + 1;
else if (byte0 < LIST_SHORT_START) {
assembly {
let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
memPtr := add(memPtr, 1) // skip over the first byte
/* 32 byte word size */
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
len := add(dataLen, add(byteLen, 1))
}
}
else if (byte0 < LIST_LONG_START) {
return byte0 - LIST_SHORT_START + 1;
}
else {
assembly {
let byteLen := sub(byte0, 0xf7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
len := add(dataLen, add(byteLen, 1))
}
}
}
// @return number of bytes until the data
function _payloadOffset(uint memPtr) internal pure returns (uint) {
uint byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START)
return 0;
else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START))
return 1;
else if (byte0 < LIST_SHORT_START) // being explicit
return byte0 - (STRING_LONG_START - 1) + 1;
else
return byte0 - (LIST_LONG_START - 1) + 1;
}
/** RLPItem conversions into data types **/
// @returns raw rlp encoding in bytes
function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
bytes memory result = new bytes(item.len);
uint ptr;
assembly {
ptr := add(0x20, result)
}
copy(item.memPtr, ptr, item.len);
return result;
}
function toBoolean(RLPItem memory item) internal pure returns (bool) {
require(item.len == 1, "Invalid RLPItem. Booleans are encoded in 1 byte");
uint result;
uint memPtr = item.memPtr;
assembly {
result := byte(0, mload(memPtr))
}
return result == 0 ? false : true;
}
function toAddress(RLPItem memory item) internal pure returns (address) {
// 1 byte for the length prefix according to RLP spec
require(item.len <= 21, "Invalid RLPItem. Addresses are encoded in 20 bytes or less");
return address(toUint(item));
}
function toUint(RLPItem memory item) internal pure returns (uint) {
uint offset = _payloadOffset(item.memPtr);
uint len = item.len - offset;
uint memPtr = item.memPtr + offset;
uint result;
assembly {
result := div(mload(memPtr), exp(256, sub(32, len))) // shift to the correct location
}
return result;
}
function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
uint offset = _payloadOffset(item.memPtr);
uint len = item.len - offset;
// data length
bytes memory result = new bytes(len);
uint destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(item.memPtr + offset, destPtr, len);
return result;
}
/*
* @param src Pointer to source
* @param dest Pointer to destination
* @param len Amount of memory to copy from the source
*/
function copy(uint src, uint dest, uint len) internal pure {
// copy as many word sizes as possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}
}
contract RelayHub is RelayHubApi {
// Anyone can call certain functions in this singleton and trigger relay processes.
uint constant timeout = 5 days; // XXX TBD
uint constant minimum_stake = 1; // XXX TBD
uint constant minimum_unstake_delay = 0; // XXX TBD
uint constant minimum_relay_balance = 0.5 ether; // XXX TBD - can't register/refresh below this amount.
uint constant low_ether = 1 ether; // XXX TBD - relay still works, but owner should be notified to fund the relay soon.
uint constant public gas_reserve = 99999; // XXX TBD - calculate how much reserve we actually need, to complete the post-call part of relay().
uint constant public gas_overhead = 47382; // the total gas overhead of relay(), before the first gasleft() and after the last gasleft(). Assume that relay has non-zero balance (costs 15'000 more otherwise).
mapping (address => uint) public nonces; // Nonces of senders, since their ether address nonce may never change.
struct Relay {
uint timestamp;
uint transaction_fee;
}
mapping (address => Relay) public relays;
struct Stake {
uint stake; // Size of the stake
uint unstake_delay; // How long between removal and unstaking
uint unstake_time; // When is the stake released. Non-zero means that the relay has been removed and is waiting for unstake.
address owner;
bool removed;
}
mapping (address => Stake) public stakes;
mapping (address => uint) public balances;
function validate_stake(address relay) private view {
require(stakes[relay].stake >= minimum_stake,"stake lower than minimum"); // Has enough stake?
require(stakes[relay].unstake_delay >= minimum_unstake_delay,"delay lower than minimum"); // Locked for enough time?
}
modifier lock_stake() {
validate_stake(msg.sender);
require(msg.sender.balance >= minimum_relay_balance,"balance lower than minimum");
stakes[msg.sender].unstake_time = 0; // Activate the lock
_;
}
function safe_add(uint a, uint b) internal pure returns (uint) {
uint256 c = a + b;
assert(c >= a);
return c;
}
function safe_sub(uint a, uint b) internal pure returns (uint) {
assert(b <= a);
return a - b;
}
function get_nonce(address from) view external returns (uint) {
return nonces[from];
}
/**
* deposit ether for a contract.
* This ether will be used to repay relay calls into this contract.
* Contract owner should monitor the balance of his contract, and make sure
* to deposit more, otherwise the contract won't be able to receive relayed calls.
* Unused deposited can be withdrawn with `withdraw()`
*/
function depositFor(address target) public payable {
balances[target] += msg.value;
require (balances[target] >= msg.value);
emit Deposited(target, msg.value);
}
function deposit() public payable {
depositFor(msg.sender);
}
/**
* withdraw funds.
* caller is either a relay owner, withdrawing collected transaction fees.
* or a RelayRecipient contract, withdrawing its deposit.
* note that while everyone can `depositFor()` a contract, only
* the contract itself can withdraw its funds.
*/
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount, "insufficient funds");
balances[msg.sender] -= amount;
msg.sender.transfer(amount);
emit Withdrawn(msg.sender, amount);
}
//check the deposit balance of a contract.
function balanceOf(address target) external view returns (uint256) {
return balances[target];
}
function stakeOf(address relay) external view returns (uint256) {
return stakes[relay].stake;
}
function ownerOf(address relay) external view returns (address) {
return stakes[relay].owner;
}
function stake(address relay, uint unstake_delay) external payable {
// Create or increase the stake and unstake_delay
require(stakes[relay].owner == address(0) || stakes[relay].owner == msg.sender, "not owner");
stakes[relay].owner = msg.sender;
stakes[relay].stake += msg.value;
// Make sure that the relay doesn't decrease his delay if already registered
require(unstake_delay >= stakes[relay].unstake_delay, "unstake_delay cannot be decreased");
stakes[relay].unstake_delay = unstake_delay;
validate_stake(relay);
emit Staked(relay, msg.value);
}
function can_unstake(address relay) public view returns(bool) {
// Only owner can unstake
if (stakes[relay].owner != msg.sender) {
return false;
}
if (relays[relay].timestamp != 0 || stakes[relay].unstake_time == 0) // Relay still registered so unstake time hasn't been set
return false;
return stakes[relay].unstake_time <= now; // Finished the unstaking delay period?
}
modifier unstake_allowed(address relay) {
require(can_unstake(relay));
_;
}
function unstake(address relay) public unstake_allowed(relay) {
uint amount = stakes[relay].stake;
msg.sender.transfer(stakes[relay].stake);
delete stakes[relay];
emit Unstaked(relay, amount);
}
function register_relay(uint transaction_fee, string memory url, address optional_relay_removal) public lock_stake {
// Anyone with a stake can register a relay. Apps choose relays by their transaction fee, stake size and unstake delay,
// optionally crossed against a blacklist. Apps verify the relay's action in realtime.
Stake storage relay_stake = stakes[msg.sender];
// Penalized relay cannot reregister
require(!relay_stake.removed, "Penalized relay cannot reregister");
relays[msg.sender] = Relay(now, transaction_fee);
emit RelayAdded(msg.sender, relay_stake.owner, transaction_fee, relay_stake.stake, relay_stake.unstake_delay, url);
// @optional_relay_removal is unrelated to registration, but incentivizes relays to help purging stale relays from the list.
// Providing a stale relay will cause its removal, and offset the gas price of registration.
if (optional_relay_removal != address(0))
remove_stale_relay(optional_relay_removal);
}
function remove_relay_internal(address relay) internal {
delete relays[relay];
stakes[relay].unstake_time = stakes[relay].unstake_delay + now; // Start the unstake counter
stakes[relay].removed = true;
emit RelayRemoved(relay, stakes[relay].unstake_time);
}
function remove_stale_relay(address relay) public { // Trustless, assumed to be called by anyone willing to pay for the gas. Verifies staleness. Normally called by relays to keep the list current.
require(relays[relay].timestamp != 0, "not a relay"); // Relay exists?
require(relays[relay].timestamp + timeout < now, "not stale"); // Did relay send a keeplive recently?
// Anyone can remove a stale relay.
remove_relay_internal(relay);
}
modifier relay_owner(address relay) {
require(stakes[relay].owner == msg.sender, "not owner");
_;
}
function remove_relay_by_owner(address relay) public relay_owner(relay) {
// The relay's owner can remove it at any time, to start the unstake countdown.
remove_relay_internal(relay);
}
function check_sig(address signer, bytes32 hash, bytes memory sig) pure internal returns (bool) {
// Check if @v,@r,@s are a valid signature of @signer for @hash
return signer == ecrecover(hash, uint8(sig[0]), bytesToBytes32(sig,1), bytesToBytes32(sig,33));
}
//check if the Hub can accept this relayed operation.
// it validates the caller's signature and nonce, and then delegates to the destination's accept_relayed_call
// for contract-specific checks.
// returns "0" if the relay is valid. other values represent errors.
// values 1..10 are reserved for can_relay. other values can be used by accept_relayed_call of target contracts.
function can_relay(address relay, address from, RelayRecipient to, bytes memory transaction, uint transaction_fee, uint gas_price, uint gas_limit, uint nonce, bytes memory sig) public view returns(uint32) {
bytes memory packed = abi.encodePacked("rlx:", from, to, transaction, transaction_fee, gas_price, gas_limit, nonce, address(this));
bytes32 hashed_message = keccak256(abi.encodePacked(packed, relay));
bytes32 signed_message = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hashed_message));
if (!check_sig(from, signed_message, sig)) // Verify the sender's signature on the transaction
return 1; // @from hasn't signed the transaction properly
if (nonces[from] != nonce)
return 2; // Not a current transaction. May be a replay attempt.
// XXX check @to's balance, roughly estimate if it has enough balance to pay the transaction fee. It's the relay's responsibility to verify, but check here too.
return to.accept_relayed_call(relay, from, transaction, gas_price, transaction_fee); // Check to.accept_relayed_call, see if it agrees to accept the charges.
}
/**
* relay a transaction.
* @param from the client originating the request.
* @param to the target RelayRecipient contract.
* @param encoded_function the function call to relay.
* @param transaction_fee fee (%) the relay takes over actual gas cost.
* @param gas_price gas price the client is willing to pay
* @param gas_limit limit the client want to put on its transaction
* @param transaction_fee fee (%) the relay takes over actual gas cost.
* @param nonce sender's nonce (in nonces[])
* @param sig client's signature over all params
*/
function relay(address from, address to, bytes memory encoded_function, uint transaction_fee, uint gas_price, uint gas_limit, uint nonce, bytes memory sig) public {
uint initial_gas = gasleft();
require(relays[msg.sender].timestamp > 0, "Unknown relay"); // Must be from a known relay
require(gas_price <= tx.gasprice, "Invalid gas price"); // Relay must use the gas price set by the signer
relays[msg.sender].timestamp = now;
require(0 == can_relay(msg.sender, from, RelayRecipient(to), encoded_function, transaction_fee, gas_price, gas_limit, nonce, sig), "can_relay failed");
// ensure that the last bytes of @transaction are the @from address.
// Recipient will trust this reported sender when msg.sender is the known RelayHub.
bytes memory transaction = abi.encodePacked(encoded_function,from);
// gas_reserve must be high enough to complete relay()'s post-call execution.
require(safe_sub(initial_gas,gas_limit) >= gas_reserve, "Not enough gasleft()");
bool success = executeCallWithGas(gas_limit, to, 0, transaction); // transaction must end with @from at this point
nonces[from]++;
RelayRecipient(to).post_relayed_call(msg.sender, from, encoded_function, success, (gas_overhead+initial_gas-gasleft()), transaction_fee );
// Relay transaction_fee is in %. E.g. if transaction_fee=40, payment will be 1.4*used_gas.
uint charge = (gas_overhead+initial_gas-gasleft())*gas_price*(100+transaction_fee)/100;
emit TransactionRelayed(msg.sender, from, to, keccak256(encoded_function), success, charge);
require(balances[to] >= charge, "insufficient funds");
balances[to] -= charge;
balances[stakes[msg.sender].owner] += charge;
}
function executeCallWithGas(uint allowed_gas, address to, uint256 value, bytes memory data) internal returns (bool success) {
assembly {
success := call(allowed_gas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
struct Transaction {
uint nonce;
uint gas_price;
uint gas_limit;
address to;
uint value;
bytes data;
}
function decode_transaction (bytes memory raw_transaction) private pure returns ( Transaction memory transaction) {
(transaction.nonce,transaction.gas_price,transaction.gas_limit,transaction.to, transaction.value, transaction.data) = RLPReader.decode_transaction(raw_transaction);
return transaction;
}
function penalize_repeated_nonce(bytes memory unsigned_tx1, bytes memory sig1 ,bytes memory unsigned_tx2, bytes memory sig2) public {
// Can be called by anyone.
// If a relay attacked the system by signing multiple transactions with the same nonce (so only one is accepted), anyone can grab both transactions from the blockchain and submit them here.
// Check whether unsigned_tx1 != unsigned_tx2, that both are signed by the same address, and that unsigned_tx1.nonce == unsigned_tx2.nonce. If all conditions are met, relay is considered an "offending relay".
// The offending relay will be unregistered immediately, its stake will be forfeited and given to the address who reported it (msg.sender), thus incentivizing anyone to report offending relays.
// If reported via a relay, the forfeited stake is split between msg.sender (the relay used for reporting) and the address that reported it.
Transaction memory decoded_tx1 = decode_transaction(unsigned_tx1);
Transaction memory decoded_tx2 = decode_transaction(unsigned_tx2);
bytes32 hash1 = keccak256(abi.encodePacked(unsigned_tx1));
address addr1 = ecrecover(hash1, uint8(sig1[0]), bytesToBytes32(sig1,1), bytesToBytes32(sig1,33));
bytes32 hash2 = keccak256(abi.encodePacked(unsigned_tx2));
address addr2 = ecrecover(hash2, uint8(sig2[0]), bytesToBytes32(sig2,1), bytesToBytes32(sig2,33));
//checking that the same nonce is used in both transaction, with both signed by the same address and the actual data is different
// note: we compare the hash of the data to save gas over iterating both byte arrays
require( decoded_tx1.nonce == decoded_tx2.nonce, "Different nonce");
require(addr1 == addr2, "Different signer");
require(keccak256(abi.encodePacked(decoded_tx1.data)) != keccak256(abi.encodePacked(decoded_tx2.data)), "tx.data is equal" ) ;
// Checking that we do have addr1 as a staked relay
require( stakes[addr1].stake > 0, "Unstaked relay" );
// Checking that the relay wasn't penalized yet
require(!stakes[addr1].removed, "Relay already penalized");
// compensating the sender with the stake of the relay
uint amount = stakes[addr1].stake;
// move ownership of relay
stakes[addr1].owner = msg.sender;
emit Penalized(addr1, msg.sender, amount);
remove_relay_by_owner(addr1);
}
function bytesToBytes32(bytes memory b, uint offset) private pure returns (bytes32) {
bytes32 out;
for (uint i = 0; i < 32; i++) {
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
}
return out;
}
}
contract RelayRecipient is RelayRecipientApi {
RelayHub private relay_hub; // The RelayHub singleton which is allowed to call us
function get_hub_addr() public view returns (address) {
return address(relay_hub);
}
/**
* initialize the relayhub.
* contracts usually call this method from the constructor (using a constract RelayHub, or receiving
* one in the constructor)
* This method might also be called by the owner, in order to use a new RelayHub - since the RelayHub
* itself is not an upgradable contract.
*/
function init_relay_hub(RelayHub _rhub) internal {
require(relay_hub == RelayHub(0), "init_relay_hub: rhub already set");
set_relay_hub(_rhub);
}
function set_relay_hub(RelayHub _rhub) internal {
// Normally called just once, during init_relay_hub.
// Left as a separate internal function, in case a contract wishes to have its own update mechanism for RelayHub.
relay_hub = _rhub;
//attempt a read method, just to validate the relay is a valid RelayHub contract.
get_recipient_balance();
}
function get_relay_hub() internal view returns (RelayHub) {
return relay_hub;
}
/**
* return the balance of this contract.
* Note that this method will revert on configuration error (invalid relay address)
*/
function get_recipient_balance() public view returns (uint) {
return get_relay_hub().balanceOf(address(this));
}
function get_sender_from_data(address orig_sender, bytes memory msg_data) public view returns(address) {
address sender = orig_sender;
if (orig_sender == get_hub_addr() ) {
// At this point we know that the sender is a trusted RelayHub, so we trust that the last bytes of msg.data are the verified sender address.
// extract sender address from the end of msg.data
bytes memory from = new bytes(20);
for (uint256 i = 0; i < from.length; i++)
{
from[i] = msg_data[msg_data.length - from.length + i];
}
sender = bytesToAddress(from);
}
return sender;
}
function get_sender() public view returns(address) {
return get_sender_from_data(msg.sender, msg.data);
}
function get_message_data() public view returns(bytes memory) {
bytes memory orig_msg_data = msg.data;
if (msg.sender == get_hub_addr()) {
// At this point we know that the sender is a trusted RelayHub, so we trust that the last bytes of msg.data are the verified sender address.
// extract original message data from the start of msg.data
orig_msg_data = new bytes(msg.data.length - 20);
for (uint256 i = 0; i < orig_msg_data.length; i++)
{
orig_msg_data[i] = msg.data[i];
}
}
return orig_msg_data;
}
/*
* Contract must inherit and re-implement this method.
* @return "0" if the the contract is willing to accept the charges from this sender, for this function call.
* any other value is a failure. actual value is for diagnostics only.
* values below 10 are reserved by can_relay
* @param relay the relay that attempts to relay this function call.
* the contract may restrict some encoded functions to specific known relays.
* @param from the sender (signer) of this function call.
* @param encoded_function the encoded function call (without any ethereum signature).
* the contract may check the method-id for valid methods
* @param gas_price - the gas price for this transaction
* @param transaction_fee - the relay compensation (in %) for this transaction
*/
function accept_relayed_call(address relay, address from, bytes memory encoded_function, uint gas_price, uint transaction_fee ) public view returns(uint32);
/**
* This method is called after the relayed call.
* It may be used to record the transaction (e.g. charge the caller by some contract logic) for this call.
* the method is given all parameters of accept_relayed_call, and also the success/failure status and actual used gas.
* - success - true if the relayed call succeeded, false if it reverted
* - used_gas - gas used up to this point. Note that gas calculation (for the purpose of compensation
* to the relay) is done after this method returns.
*/
function post_relayed_call(address relay, address from, bytes memory encoded_function, bool success, uint used_gas, uint transaction_fee ) public;
function bytesToAddress(bytes memory b) private pure returns (address addr) {
assembly {
addr := mload(add(b,20))
}
}
}
contract RecipientUtils {
//return the signature of a method.
// (can also be done off-chain)
function sig(string memory methodSig) public pure returns (bytes4) {
return bytes4(keccak256(bytes(methodSig)));
}
/**
* extract method sig from encoded function call
*/
function getMethodSig(bytes memory msg_data) public pure returns (bytes4) {
return bytes4(bytes32(extractUint(msg_data, 0)));
}
/**
* extract parameter from encoded-function block.
* see: https://solidity.readthedocs.io/en/develop/abi-spec.html#formal-specification-of-the-encoding
* note that the type of the parameter must be static.
* the return value should be casted to the right type.
*/
function getParam(bytes memory msg_data, uint index) public pure returns (uint) {
return extractUint(msg_data, 4 + index * 32);
}
/**
* extract dynamic-sized (string/bytes) parameter.
* we assume that there ARE dynamic parameters, hence getParam(0) is the offset to the first
* dynamic param
* https://solidity.readthedocs.io/en/develop/abi-spec.html#use-of-dynamic-types
*/
function getBytesParam(bytes memory msg_data, uint index) public pure returns (bytes memory ret) {
uint ofs = getParam(msg_data,index)+4;
uint len = extractUint(msg_data, ofs);
ret = extractBytes(msg_data, ofs+32, len);
}
function getStringParam(bytes memory msg_data, uint index) public pure returns (string memory) {
return string(getBytesParam(msg_data,index));
}
/**
* extract bytes32 block from a memory source.
* if offset is too large, then pad result with zeros.
* @param source a block of memory to extract from.
* @param ofs offset to start.
*/
function extractUint(bytes memory source, uint ofs) public pure returns (uint result) {
assembly {
result := mload(add(ofs, add(source, 32)))
}
}
//extracts bytes from a memory block
function extractBytes(bytes memory source, uint ofs, uint len) public pure returns (bytes memory ret) {
//TODO: check overflows (not really needed. it will exhaust gas on overflows)
require( ofs+len <= source.length, "asdasd");
//TODO: assembly?
ret = new bytes(len);
for ( uint i=0; i<len; i++ ) {
ret[i] = source[i+ofs];
}
}
}
/**
* @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 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);
}
}
/// @title Storage vault to send with a link.
/// @author Ricardo Rius - <[email protected]>
/// @notice Based on ARAGON VAULT.
contract Vault is RelayRecipient, RecipientUtils {
address internal constant ETH = address(0);
event VaultTransfer(address indexed token, address indexed to, uint256 amount, bool status);
event VaultDeposit(address indexed token, address indexed sender, uint256 amount, bool status);
function balance(address _token, address _from) public view returns (uint256) {
if (_token == ETH) {
return address(_from).balance;
} else {
return ERC20(_token).balanceOf(_from);
}
}
/// @notice vaultDeposit `_value` `_token` to the vault
/// @param _token Address of the token being transferred
/// @param _value Amount of tokens being transferred
function _deposit(address _token, uint256 _value) internal {
_vDeposit(_token, _value);
}
/// @notice vaultTransfer `_value` `_token` from the Vault to `_to`
/// @param _token Address of the token being transferred
/// @param _to Address of the recipient of tokens
/// @param _value Amount of tokens being transferred
function _transfer(address _token, address _to, uint256 _value) internal returns(bool){
return _vTransfer(_token, _to, _value);
}
/// @notice Transfer `_value` `_token` from the Vault to `_to`
/// @param _token Address of the token being transferred
/// @param _to Address of the recipient of tokens
/// @param _value Amount of tokens being transferred
/* solium-disable-next-line function-order */
function _vTransfer(address _token, address _to, uint256 _value) private returns(bool status) {
require(_value > 0, "Vault::_vTransfer - Invalid transfer");
status = false;
if (_token == ETH) {
status = _to.send(_value);
} else {
status = ERC20(_token).transfer(_to, _value);
}
emit VaultTransfer(_token, _to, _value, status);
}
/// @notice Deposit `_value` `_token` to the vault
/// @param _token Address of the token being transferred
/// @param _value Amount of tokens being transferred
function _vDeposit(address _token, uint256 _value) private {
require(_value > 0, "Vault::_vDeposit - Invalid deposit");
if (_token == ETH) {
// Deposit is implicit in this case
require(msg.value == _value, "Vault::_vDeposit - Value mismatch");
} else {
require(ERC20(_token).transferFrom(get_sender(), this, _value), "Vault::_vDeposit - Reverted token transfer");
}
emit VaultDeposit(_token, get_sender(), _value, true);
}
}
/// @title Send xDai/Eth with a link.
/// @author Ricardo Rius - <[email protected]>
/// @author TabooKey Team - <[email protected]>
/// @notice Funds have an adjustable expiration time.
/// After a fund expires it can only be claimed by the original sender.
contract Links is Vault {
using SafeMath for uint256;
using ECDSA for bytes32;
struct Fund {
address sender;
address signer;
address token;
uint256 amount;
uint256 msgVal;
uint256 nonce;
uint256 creationTime;
uint256 expirationTime;
bool claimed;
}
uint256 public contractNonce = 1;
mapping (bytes32 => Fund) public funds;
event Sent(
bytes32 indexed id,
address indexed sender,
uint256 value,
uint256 indexed nonce,
bool sent
);
event Claimed(
bytes32 indexed id,
address sender,
uint256 value,
address indexed receiver,
uint256 indexed nonce,
bool claimed
);
/// @dev Verifies if it is a valid Id.
modifier ifValidId(bytes32 Id){
require(isFundValid(Id),"Links::ifValidId - Id does NOT exists.");
_;
}
/// @dev Verifies if the Id exists.
modifier ifNotValidId(bytes32 Id){
require(!isFundValid(Id),"Links::ifNotValidId - Id exists.");
_;
}
/// @dev Verifies if it is a valid Signature lenght.
modifier ifValidSig(bytes memory Signature){
require(Signature.length == 65,"Links::ifValidSig - Invalid signature lenght");
_;
}
/// @dev fallback
function () external{
revert("Links::fallback");
}
/// @dev Create fund.
/// @param _id Fund lookup key value.
/// @param _signature Sender signature.
function send(
bytes32 _id,
bytes memory _signature,
address _token,
uint256 _amount,
uint256 _expirationDays
)
public
ifNotValidId(_id)
ifValidSig(_signature)
payable
returns (bool)
{
require(_expirationDays >= uint256(0),"Links::send - Invalid expiration days");
address signer = ECDSA.recover(_id.toEthSignedMessageHash(),_signature);
require(signer != address(0),"Links::send - Invalid signer");
uint256 nonce = contractNonce;
contractNonce = contractNonce.add(uint256(1));
// Default expiration time
uint256 expiration = now.add(1 days);
if (_expirationDays > 1){
expiration = now.add(_expirationDays.mul(1 days));
}
assert(nonce < contractNonce);
_deposit(_token,_amount);
funds[_id] = Fund({
sender: get_sender(),
signer: signer,
token: _token,
amount: _amount,
msgVal: msg.value,
nonce: nonce,
creationTime: now,
expirationTime: expiration,
claimed: false
});
require(isFundValid(_id),"Links::send - Invalid fund");
//send out events for frontend parsing
emit Sent(_id,get_sender(),msg.value,nonce,true);
return true;
}
/// @dev Claim fund value.
/// @param _id Claim lookup key value.
/// @param _signature Sender signature.
/// @param _destination Destination address.
function claim(
bytes32 _id,
bytes memory _signature,
bytes32 _claimHash,
address _destination
)
public
ifValidId(_id)
returns (bool)
{
return executeClaim(_id,_signature,_claimHash,_destination);
}
/// @dev Off chain relayer can validate the claim before submitting.
/// @param _id Claim lookup key value.
/// @param _signature Sender signature.
/// @param _destination Destination address.
function isClaimValid(
bytes32 _id,
bytes memory _signature,
bytes32 _claimHash,
address _destination
)
public
view
returns (bool)
{
// address(0) destination is valid
if(isFundValid(_id) && _signature.length == 65){
address signer = address(0);
uint256 nonce = funds[_id].nonce;
// keccak256(_id,_destination,nonce,address(this)) is a unique key
// remains unique if the id gets reused after fund deletion
bytes32 claimHash1 = keccak256(abi.encodePacked(_id,_destination,nonce,address(this)));
if(_claimHash == claimHash1){
signer = ECDSA.recover(claimHash1.toEthSignedMessageHash(),_signature);
} else{
return false;
}
if(signer != address(0)){
return(funds[_id].signer == signer);
} else{
return false;
}
} else{
return false;
}
}
/// @dev Validate fund status.
/// @param _id Lookup key id.
function isFundValid(
bytes32 _id
)
public
view
returns (bool)
{
address sender = funds[_id].sender;
address signer = funds[_id].signer;
uint256 nonce = funds[_id].nonce;
/* solium-disable-next-line security/no-inline-assembly */
assembly {
// Cannot assume empty initial values without initializating them.
sender := and(sender, 0xffffffff)
signer := and(signer, 0xffffffff)
nonce := and(nonce, 0xffffffff)
}
return (
(sender != address(0)) && (signer != address(0)) && (nonce > uint256(0)) && (nonce < contractNonce)
);
}
/// @dev Validate fund status.
/// @param _id Lookup key id.
/// @param _destination Destination address.
function isClaimExpired(
bytes32 _id,
bytes memory _signature,
bytes32 _claimHash,
address _destination
)
public
view
returns (bool)
{
if(isClaimValid(_id,_signature,_claimHash,_destination)){
return (funds[_id].expirationTime < now);
}else{
return true;
}
}
/// @dev Claim fund value.
/// @param _id Claim lookup key value.
/// @param _destination Destination address.
function executeClaim(
bytes32 _id,
bytes memory _signature,
bytes32 _claimHash,
address _destination
)
private
returns (bool)
{
require(isClaimValid(_id,_signature,_claimHash,_destination),"Links::executeClaim - Invalid claim.");
bool status = false;
uint256 nonce = funds[_id].nonce;
address token = funds[_id].token;
uint256 amount = funds[_id].amount;
assert(nonce < contractNonce);
// validate mutex/flag status
if(funds[_id].claimed == false){
// mutex activation
funds[_id].claimed = true;
// expired funds can only be claimed back by original sender.
if(isClaimExpired(_id,_signature,_claimHash,_destination)){
require(get_sender() == funds[_id].sender,"Links::executeClaim - Not original sender");
require(_transfer(token, get_sender(), amount),"Links::executeClaim - Could not transfer to sender");
delete funds[_id];
status = true;
}else{
status = _transfer(token, _destination, amount);
// update mutex with correct status
funds[_id].claimed = status;
// update fund
if(status == true){
delete funds[_id];
}
require(get_sender().send(0),"Links::executeClaim - Unsuccessful transaction");
}
} else{
// DESTROY object so it can't be claimed again and free storage space.
delete funds[_id];
status = true;
}
// send out events for frontend parsing
emit Claimed(_id,get_sender(),amount,_destination,nonce,status);
return status;
}
/// TabooKey Team - MetaTX Relay Section
function set_hub(
RelayHub rhub
)
public
{
init_relay_hub(rhub);
}
function deposit_to_relay_hub()
public
payable
{
RelayHub(get_hub_addr()).depositFor.value(msg.value)(this);
}
/// @dev decide whether this call should be allowed to called by a relay
/// @param encoded_function raw bytes of the transaction being relayed
/// @return fsdgf
function accept_relayed_call(
address /* relay */,
address /* from */,
bytes memory encoded_function,
uint /* gas_price */,
uint /* transaction_fee */
)
public
view
returns(uint32)
{
bytes4 claimFunctionIdentifier = RecipientUtils.sig("claim(bytes32,bytes,bytes32,address)");
bool is_call_to_claim = RecipientUtils.getMethodSig(encoded_function) == claimFunctionIdentifier;
if (!is_call_to_claim){
return 4;
}
bytes32 id = bytes32(RecipientUtils.getParam(encoded_function, 0));
bytes memory signature = RecipientUtils.getBytesParam(encoded_function, 1);
bytes32 claimHash = bytes32(RecipientUtils.getParam(encoded_function, 2));
address destination = address(RecipientUtils.getParam(encoded_function, 3));
bool is_claim_valid = isClaimValid(id, signature, claimHash, destination);
if (!is_claim_valid) {
return 5;
}
return 0;
}
function post_relayed_call(
address /* relay */,
address /* from */,
bytes memory/* encoded_function */,
bool /* success */,
uint /* used_gas */,
uint /* transaction_fee */
)
public
{
}
}

Contract ABI

[{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"get_hub_addr","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isClaimValid","inputs":[{"type":"bytes32","name":"_id"},{"type":"bytes","name":"_signature"},{"type":"bytes32","name":"_claimHash"},{"type":"address","name":"_destination"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"set_hub","inputs":[{"type":"address","name":"rhub"}],"constant":false},{"type":"function","stateMutability":"payable","payable":true,"outputs":[{"type":"bool","name":""}],"name":"send","inputs":[{"type":"bytes32","name":"_id"},{"type":"bytes","name":"_signature"},{"type":"address","name":"_token"},{"type":"uint256","name":"_amount"},{"type":"uint256","name":"_expirationDays"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"contractNonce","inputs":[],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"bytes4","name":""}],"name":"sig","inputs":[{"type":"string","name":"methodSig"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"post_relayed_call","inputs":[{"type":"address","name":""},{"type":"address","name":""},{"type":"bytes","name":""},{"type":"bool","name":""},{"type":"uint256","name":""},{"type":"uint256","name":""}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint32","name":""}],"name":"accept_relayed_call","inputs":[{"type":"address","name":""},{"type":"address","name":""},{"type":"bytes","name":"encoded_function"},{"type":"uint256","name":""},{"type":"uint256","name":""}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"sender"},{"type":"address","name":"signer"},{"type":"address","name":"token"},{"type":"uint256","name":"amount"},{"type":"uint256","name":"msgVal"},{"type":"uint256","name":"nonce"},{"type":"uint256","name":"creationTime"},{"type":"uint256","name":"expirationTime"},{"type":"bool","name":"claimed"}],"name":"funds","inputs":[{"type":"bytes32","name":""}],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"uint256","name":"result"}],"name":"extractUint","inputs":[{"type":"bytes","name":"source"},{"type":"uint256","name":"ofs"}],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"bytes4","name":""}],"name":"getMethodSig","inputs":[{"type":"bytes","name":"msg_data"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isFundValid","inputs":[{"type":"bytes32","name":"_id"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes","name":""}],"name":"get_message_data","inputs":[],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"bytes","name":"ret"}],"name":"extractBytes","inputs":[{"type":"bytes","name":"source"},{"type":"uint256","name":"ofs"},{"type":"uint256","name":"len"}],"constant":true},{"type":"function","stateMutability":"payable","payable":true,"outputs":[],"name":"deposit_to_relay_hub","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"get_sender","inputs":[],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"string","name":""}],"name":"getStringParam","inputs":[{"type":"bytes","name":"msg_data"},{"type":"uint256","name":"index"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"balance","inputs":[{"type":"address","name":"_token"},{"type":"address","name":"_from"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"get_sender_from_data","inputs":[{"type":"address","name":"orig_sender"},{"type":"bytes","name":"msg_data"}],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getParam","inputs":[{"type":"bytes","name":"msg_data"},{"type":"uint256","name":"index"}],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"bytes","name":"ret"}],"name":"getBytesParam","inputs":[{"type":"bytes","name":"msg_data"},{"type":"uint256","name":"index"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"claim","inputs":[{"type":"bytes32","name":"_id"},{"type":"bytes","name":"_signature"},{"type":"bytes32","name":"_claimHash"},{"type":"address","name":"_destination"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isClaimExpired","inputs":[{"type":"bytes32","name":"_id"},{"type":"bytes","name":"_signature"},{"type":"bytes32","name":"_claimHash"},{"type":"address","name":"_destination"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"get_recipient_balance","inputs":[],"constant":true},{"type":"fallback","stateMutability":"nonpayable","payable":false},{"type":"event","name":"Sent","inputs":[{"type":"bytes32","name":"id","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"value","indexed":false},{"type":"uint256","name":"nonce","indexed":true},{"type":"bool","name":"sent","indexed":false}],"anonymous":false},{"type":"event","name":"Claimed","inputs":[{"type":"bytes32","name":"id","indexed":true},{"type":"address","name":"sender","indexed":false},{"type":"uint256","name":"value","indexed":false},{"type":"address","name":"receiver","indexed":true},{"type":"uint256","name":"nonce","indexed":true},{"type":"bool","name":"claimed","indexed":false}],"anonymous":false},{"type":"event","name":"VaultTransfer","inputs":[{"type":"address","name":"token","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"amount","indexed":false},{"type":"bool","name":"status","indexed":false}],"anonymous":false},{"type":"event","name":"VaultDeposit","inputs":[{"type":"address","name":"token","indexed":true},{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"amount","indexed":false},{"type":"bool","name":"status","indexed":false}],"anonymous":false}]
            

Contract Byte Code

0x6080604052600436106101245763ffffffff60e060020a60003504166304badade81146101815780630b7d37b0146101b25780631794a07b1461023557806326e308291461025857806349d71f05146102be5780634b808215146102e5578063516dea30146103705780635dea186c146103ec5780636a29a6201461047d578063823e0669146104eb5780638a173e3a146105465780638af047b51461059f57806391719568146105b7578063993c3b2c14610641578063a3b6ab72146106a3578063a8d509ff146106ab578063b046e4b4146106c0578063b203bb991461071b578063c304e7a414610742578063c60d756e146107a9578063d1d3a76514610804578063d37e6d001461085f578063e0249720146108ce578063ed6cd5d31461093d575b34801561013057600080fd5b506040805160e560020a62461bcd02815260206004820152600f60248201527f4c696e6b733a3a66616c6c6261636b0000000000000000000000000000000000604482015290519081900360640190fd5b34801561018d57600080fd5b50610196610952565b60408051600160a060020a039092168252519081900360200190f35b3480156101be57600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526102219583359536956044949193909101919081908401838280828437509497505084359550505050602090910135600160a060020a03169050610961565b604080519115158252519081900360200190f35b34801561024157600080fd5b50610256600160a060020a0360043516610aa7565b005b60408051602060046024803582810135601f810185900485028601850190965285855261022195833595369560449491939091019190819084018382808284375094975050508335600160a060020a031694505050602082013591604001359050610ab3565b3480156102ca57600080fd5b506102d3610f1d565b60408051918252519081900360200190f35b3480156102f157600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261033e943694929360249392840191908190840183828082843750949750610f239650505050505050565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff199092168252519081900360200190f35b34801561037c57600080fd5b50604080516020600460443581810135601f8101849004840285018401909552848452610256948235600160a060020a0390811695602480359092169536959460649492930191908190840183828082843750949750505050823515159350505060208101359060400135610f87565b3480156103f857600080fd5b50604080516020600460443581810135601f8101849004840285018401909552848452610464948235600160a060020a0390811695602480359092169536959460649492930191908190840183828082843750949750508435955050506020909201359150610f8f9050565b6040805163ffffffff9092168252519081900360200190f35b34801561048957600080fd5b506104956004356110c3565b60408051600160a060020a039a8b168152988a1660208a015296909816878701526060870194909452608086019290925260a085015260c084015260e08301529115156101008201529051908190036101200190f35b3480156104f757600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526102d394369492936024939284019190819084018382808284375094975050933594506111209350505050565b34801561055257600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261033e9436949293602493928401919081908401838280828437509497506111289650505050505050565b3480156105ab57600080fd5b5061022160043561113b565b3480156105c357600080fd5b506105cc61119c565b6040805160208082528351818301528351919283929083019185019080838360005b838110156106065781810151838201526020016105ee565b50505050905090810190601f1680156106335780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561064d57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526105cc9436949293602493928401919081908401838280828437509497505084359550505060209092013591506112999050565b610256611391565b3480156106b757600080fd5b5061019661140e565b3480156106cc57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526105cc94369492936024939284019190819084018382808284375094975050933594506114509350505050565b34801561072757600080fd5b506102d3600160a060020a0360043581169060243516611463565b34801561074e57600080fd5b5060408051602060046024803582810135601f8101859004850286018501909652858552610196958335600160a060020a03169536956044949193909101919081908401838280828437509497506115149650505050505050565b3480156107b557600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526102d394369492936024939284019190819084018382808284375094975050933594506115d79350505050565b34801561081057600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526105cc94369492936024939284019190819084018382808284375094975050933594506115e99350505050565b34801561086b57600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526102219583359536956044949193909101919081908401838280828437509497505084359550505050602090910135600160a060020a03169050611617565b3480156108da57600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526102219583359536956044949193909101919081908401838280828437509497505084359550505050602090910135600160a060020a031690506116b5565b34801561094957600080fd5b506102d36116ee565b600054600160a060020a031690565b6000806000806109708861113b565b801561097d575086516041145b15610a615760008881526002602090815260408083206005015481518084018d90526c01000000000000000000000000600160a060020a038b1681028285015260548201839052300260748201528251606881830301815260889091019283905280519497509095509290918291908401908083835b60208310610a125780518252601f1990920191602091820191016109f3565b5181516020939093036101000a60001901801990911692169190911790526040519201829003909120935050505085811415610a6157610a5a610a5482611783565b886117fa565b9250610a6a565b60009350610a9c565b600160a060020a03831615610a6157600088815260026020526040902060010154600160a060020a0384811691161493505b505050949350505050565b610ab0816118ca565b50565b60008060008088610ac38161113b565b15610b18576040805160e560020a62461bcd02815260206004820181905260248201527f4c696e6b733a3a69664e6f7456616c69644964202d204964206578697374732e604482015290519081900360640190fd5b88518990604114610b99576040805160e560020a62461bcd02815260206004820152602c60248201527f4c696e6b733a3a696656616c6964536967202d20496e76616c6964207369676e60448201527f6174757265206c656e6768740000000000000000000000000000000000000000606482015290519081900360840190fd5b6000871015610c18576040805160e560020a62461bcd02815260206004820152602560248201527f4c696e6b733a3a73656e64202d20496e76616c69642065787069726174696f6e60448201527f2064617973000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b610c2a610c248c611783565b8b6117fa565b9450600160a060020a0385161515610c8c576040805160e560020a62461bcd02815260206004820152601c60248201527f4c696e6b733a3a73656e64202d20496e76616c6964207369676e657200000000604482015290519081900360640190fd5b600180549450610ca390859063ffffffff61193416565b600155610cb9426201518063ffffffff61193416565b92506001871115610cea57610ce7610cda886201518063ffffffff61195116565b429063ffffffff61193416565b92505b6001548410610cf557fe5b610cff898961197f565b61012060405190810160405280610d1461140e565b600160a060020a0316815260200186600160a060020a031681526020018a600160a060020a0316815260200189815260200134815260200185815260200142815260200184815260200160001515815250600260008d6000191660001916815260200190815260200160002060008201518160000160006101000a815481600160a060020a030219169083600160a060020a0316021790555060208201518160010160006101000a815481600160a060020a030219169083600160a060020a0316021790555060408201518160020160006101000a815481600160a060020a030219169083600160a060020a03160217905550606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e082015181600701556101008201518160080160006101000a81548160ff021916908315150217905550905050610e668b61113b565b1515610ebc576040805160e560020a62461bcd02815260206004820152601a60248201527f4c696e6b733a3a73656e64202d20496e76616c69642066756e64000000000000604482015290519081900360640190fd5b83610ec561140e565b60408051348152600160208201528151600160a060020a0393909316928f927fa3b60f1507010a7e27e9a8ab020d43601d456628bee71f1cc9c2f06547688d81928290030190a45060019a9950505050505050505050565b60015481565b6000816040518082805190602001908083835b60208310610f555780518252601f199092019160209182019101610f36565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b505050505050565b60008060008060606000806000611001606060405190810160405280602481526020017f636c61696d28627974657333322c62797465732c627974657333322c6164647281526020017f6573732900000000000000000000000000000000000000000000000000000000815250610f23565b96507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19871661102c8c611128565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161495508561105c57600497506110b3565b6110678b60006115d7565b94506110748b60016115e9565b93506110818b60026115d7565b925061108e8b60036115d7565b915061109c85858585610961565b90508015156110ae57600597506110b3565b600097505b5050505050505095945050505050565b6002602081905260009182526040909120805460018201549282015460038301546004840154600585015460068601546007870154600890970154600160a060020a03968716988716979590961695939492939192909160ff1689565b016020015190565b6000611135826000611120565b92915050565b60008181526002602052604081208054600182015460059092015463ffffffff9182169282169116821580159061117a5750600160a060020a03821615155b80156111865750600081115b8015611193575060015481105b95945050505050565b606080600080368080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091506111de610952565b600160a060020a031633600160a060020a031614156112935760146000369050036040519080825280601f01601f19166020018201604052801561122c578160200160208202803883390190505b509150600090505b8151811015611293576000368281811061124a57fe5b9050013560f860020a900460f860020a02600160f860020a031916828281518110151561127357fe5b906020010190600160f860020a031916908160001a905350600101611234565b50919050565b606060008451838501111515156112fa576040805160e560020a62461bcd02815260206004820152600660248201527f6173646173640000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b826040519080825280601f01601f191660200182016040528015611328578160200160208202803883390190505b509150600090505b82811015611389578484820181518110151561134857fe5b90602001015160f860020a900460f860020a02828281518110151561136957fe5b906020010190600160f860020a031916908160001a905350600101611330565b509392505050565b611399610952565b600160a060020a031663aa67c91934306040518363ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a031681526020019150506000604051808303818588803b1580156113f357600080fd5b505af1158015611407573d6000803e3d6000fd5b5050505050565b600061144b336000368080601f01602080910402602001604051908101604052809392919081815260200183838082843750611514945050505050565b905090565b606061145c83836115e9565b9392505050565b6000600160a060020a03831615156114865750600160a060020a03811631611135565b82600160a060020a03166370a08231836040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b1580156114e157600080fd5b505af11580156114f5573d6000803e3d6000fd5b505050506040513d602081101561150b57600080fd5b50519050611135565b600082606082611522610952565b600160a060020a031686600160a060020a031614156115ca57604080516014808252818301909252906020820161028080388339019050509150600090505b81518110156115be578151855186918103830190811061157d57fe5b90602001015160f860020a900460f860020a02828281518110151561159e57fe5b906020010190600160f860020a031916908160001a905350600101611561565b6115c78261198d565b92505b8293505b50505092915050565b600061145c8383602002600401611120565b60606000806115f885856115d7565b60040191506116078583611120565b9050611193858360200183611299565b6000846116238161113b565b151561169f576040805160e560020a62461bcd02815260206004820152602660248201527f4c696e6b733a3a696656616c69644964202d20496420646f6573204e4f54206560448201527f78697374732e0000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6116ab86868686611994565b9695505050505050565b60006116c385858585610961565b156116e2575060008481526002602052604090206007015442116116e6565b5060015b949350505050565b60006116f8610952565b600160a060020a03166370a08231306040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561175257600080fd5b505af1158015611766573d6000803e3d6000fd5b505050506040513d602081101561177c57600080fd5b5051905090565b604080517f19457468657265756d205369676e6564204d6573736167653a0a333200000000602080830191909152603c80830185905283518084039091018152605c9092019283905281516000939182919084019080838360208310610f555780518252601f199092019160209182019101610f36565b6000806000808451604114151561181457600093506115ce565b50505060208201516040830151606084015160001a601b60ff8216101561183957601b015b8060ff16601b1415801561185157508060ff16601c14155b1561185f57600093506115ce565b60408051600080825260208083018085528a905260ff8516838501526060830187905260808301869052925160019360a0808501949193601f19840193928390039091019190865af11580156118b9573d6000803e3d6000fd5b5050506020604051035193506115ce565b600054600160a060020a03161561192b576040805160e560020a62461bcd02815260206004820181905260248201527f696e69745f72656c61795f6875623a207268756220616c726561647920736574604482015290519081900360640190fd5b610ab081611e8c565b60008282018381101561194657600080fd5b8091505b5092915050565b600080831515611964576000915061194a565b5082820282848281151561197457fe5b041461194657600080fd5b6119898282611ebc565b5050565b6014015190565b60008060008060006119a889898989610961565b1515611a23576040805160e560020a62461bcd028152602060048201526024808201527f4c696e6b733a3a65786563757465436c61696d202d20496e76616c696420636c60448201527f61696d2e00000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b505050600086815260026020819052604082206005810154918101546003909101546001549394509192600160a060020a0390911691908310611a6257fe5b60008981526002602052604090206008015460ff161515611dab576000898152600260205260409020600801805460ff19166001179055611aa5898989896116b5565b15611c5657600089815260026020526040902054600160a060020a0316611aca61140e565b600160a060020a031614611b4e576040805160e560020a62461bcd02815260206004820152602960248201527f4c696e6b733a3a65786563757465436c61696d202d204e6f74206f726967696e60448201527f616c2073656e6465720000000000000000000000000000000000000000000000606482015290519081900360840190fd5b611b6082611b5a61140e565b83612130565b1515611bdc576040805160e560020a62461bcd02815260206004820152603260248201527f4c696e6b733a3a65786563757465436c61696d202d20436f756c64206e6f742060448201527f7472616e7366657220746f2073656e6465720000000000000000000000000000606482015290519081900360840190fd5b60008981526002602081905260408220805473ffffffffffffffffffffffffffffffffffffffff1990811682556001808301805483169055928201805490911690556003810183905560048101839055600581018390556006810183905560078101929092556008909101805460ff191690559350611da6565b611c61828783612130565b60008a8152600260205260409020600801805460ff191682151590811790915590945060011415611cfe5760008981526002602081905260408220805473ffffffffffffffffffffffffffffffffffffffff199081168255600182018054821690559181018054909216909155600381018290556004810182905560058101829055600681018290556007810191909155600801805460ff191690555b611d0661140e565b604051600160a060020a0391909116906108fc9060009081818181818888f193505050501515611da6576040805160e560020a62461bcd02815260206004820152602e60248201527f4c696e6b733a3a65786563757465436c61696d202d20556e737563636573736660448201527f756c207472616e73616374696f6e000000000000000000000000000000000000606482015290519081900360840190fd5b611e21565b60008981526002602081905260408220805473ffffffffffffffffffffffffffffffffffffffff1990811682556001808301805483169055928201805490911690556003810183905560048101839055600581018390556006810183905560078101929092556008909101805460ff1916905593505b82600160a060020a0387168a7f3f08a979439327bb6246f38c73f40f9159a93858bbcd4d50258d6902c2b1d782611e5661140e565b60408051600160a060020a0390921682526020820187905289151582820152519081900360600190a45091979650505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790556119896116ee565b60008111611f3a576040805160e560020a62461bcd02815260206004820152602260248201527f5661756c743a3a5f764465706f736974202d20496e76616c6964206465706f7360448201527f6974000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b600160a060020a0382161515611fcc57348114611fc7576040805160e560020a62461bcd02815260206004820152602160248201527f5661756c743a3a5f764465706f736974202d2056616c7565206d69736d61746360448201527f6800000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6120dd565b81600160a060020a03166323b872dd611fe361140e565b6040805160e060020a63ffffffff8516028152600160a060020a039092166004830152306024830152604482018590525160648083019260209291908290030181600087803b15801561203557600080fd5b505af1158015612049573d6000803e3d6000fd5b505050506040513d602081101561205f57600080fd5b505115156120dd576040805160e560020a62461bcd02815260206004820152602a60248201527f5661756c743a3a5f764465706f736974202d20526576657274656420746f6b6560448201527f6e207472616e7366657200000000000000000000000000000000000000000000606482015290519081900360840190fd5b6120e561140e565b60408051838152600160208201528151600160a060020a03938416938616927f60c917668e4621910c067614bbf4e2686a26b43b9dc2f822cbda69a30c0e035e928290030190a35050565b60006116e684848460008082116121b6576040805160e560020a62461bcd028152602060048201526024808201527f5661756c743a3a5f765472616e73666572202d20496e76616c6964207472616e60448201527f7366657200000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b506000600160a060020a03841615156121f557604051600160a060020a0384169083156108fc029084906000818181858888f193505050509050612287565b83600160a060020a031663a9059cbb84846040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b15801561225857600080fd5b505af115801561226c573d6000803e3d6000fd5b505050506040513d602081101561228257600080fd5b505190505b6040805183815282151560208201528151600160a060020a0380871693908816927fd7405714d768eadab6cd4ece000a72a4fece216416085ff10b363fa9b44e7f7c929081900390910190a393925050505600a165627a7a72305820f43fd7f7f79f078455474174749ca22b681579c669b779c25973288caa51d8ae0029