Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.6.4+commit.1dca32f3.js&optimize=true&gist=
pragma solidity 0.6.4;
import "./Context.sol";
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Ownable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ABCToken is Context, IERC20, Ownable {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
uint8 public _decimals;
string public _symbol;
string public _name;
constructor() public {
_name = "ABC token";
_symbol = "abc";
_decimals = 18;
_totalSupply = 100000000000000000000000000;
_balances[msg.sender] = _totalSupply;
emit Transfer(address(0), msg.sender, _totalSupply);
}
/**
* @dev Returns the bep token owner.
*/
function getOwner() external override view returns (address) {
return owner();
}
/**
* @dev Returns the token decimals.
*/
function decimals() external override view returns (uint256) {
return _decimals;
}
/**
* @dev Returns the contract owner.
*/
function symbol() external override view returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() external override view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) external override view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) external override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) external override view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) external override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
}
pragma solidity 0.6.4;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
pragma solidity 0.6.4;
import "./Context.sol";
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Ownable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract DEFToken is Context, IERC20, Ownable {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
uint8 public _decimals;
string public _symbol;
string public _name;
constructor() public {
_name = "DEF token";
_symbol = "DEF";
_decimals = 18;
_totalSupply = 100000000000000000000000000;
_balances[msg.sender] = _totalSupply;
emit Transfer(address(0), msg.sender, _totalSupply);
}
/**
* @dev Returns the address of the current owner.
*/
function getOwner() override public view returns (address) {
return owner();
}
/**
* @dev Returns the token decimals.
*/
function decimals() override external view returns (uint256) {
return _decimals;
}
/**
* @dev Returns the contract owner.
*/
function symbol() override external view returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() override public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) override public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) override public returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) override public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) override public returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) override public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
}
pragma solidity 0.6.4;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the token decimals.
*/
function decimals() external view returns (uint256);
/**
* @dev Returns the contract owner.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the bep token owner.
*/
function getOwner() external view returns (address);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: 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
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity 0.6.4;
interface ILightClient {
function getAppHash(uint64 height) external view returns (bytes32);
function isHeaderSynced(uint64 height) external view returns (bool);
function getSubmitter(uint64 height) external view returns (address payable);
}
pragma solidity 0.6.4;
interface IRelayerHub {
function isRelayer(address sender) external view returns (bool);
}
pragma solidity 0.6.4;
interface IRelayerIncentivize {
function addReward(address payable headerRelayerAddr, address payable caller) external payable returns (bool);
}
pragma solidity 0.6.4;
interface ISystemReward {
function claimRewards(address payable to, uint256 amount) external;
}
pragma solidity 0.6.4;
interface ITokenHub {
function handleBindPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence)
external returns (bool);
function handleTransferInPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence)
external returns (bool);
function handleRefundPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence)
external returns (bool);
function transferOut(address contractAddr, address recipient, uint256 amount, uint256 expireTime, uint256 relayFee)
external payable returns (bool);
/* solium-disable-next-line */
function batchTransferOut(address[] calldata recipientAddrs, uint256[] calldata amounts, address[] calldata refundAddrs,
address contractAddr, uint256 expireTime, uint256 relayFee) external payable returns (bool);
}
pragma solidity 0.6.4;
library Memory {
// Size of a word, in bytes.
uint internal constant WORD_SIZE = 32;
// Size of the header of a 'bytes' array.
uint internal constant BYTES_HEADER_SIZE = 32;
// Address of the free memory pointer.
uint internal constant FREE_MEM_PTR = 0x40;
// Compares the 'len' bytes starting at address 'addr' in memory with the 'len'
// bytes starting at 'addr2'.
// Returns 'true' if the bytes are the same, otherwise 'false'.
function equals(uint addr, uint addr2, uint len) internal pure returns (bool equal) {
assembly {
equal := eq(keccak256(addr, len), keccak256(addr2, len))
}
}
// Compares the 'len' bytes starting at address 'addr' in memory with the bytes stored in
// 'bts'. It is allowed to set 'len' to a lower value then 'bts.length', in which case only
// the first 'len' bytes will be compared.
// Requires that 'bts.length >= len'
function equals(uint addr, uint len, bytes memory bts) internal pure returns (bool equal) {
require(bts.length >= len);
uint addr2;
assembly {
addr2 := add(bts, /*BYTES_HEADER_SIZE*/32)
}
return equals(addr, addr2, len);
}
// Allocates 'numBytes' bytes in memory. This will prevent the Solidity compiler
// from using this area of memory. It will also initialize the area by setting
// each byte to '0'.
function allocate(uint numBytes) internal pure returns (uint addr) {
// Take the current value of the free memory pointer, and update.
assembly {
addr := mload(/*FREE_MEM_PTR*/0x40)
mstore(/*FREE_MEM_PTR*/0x40, add(addr, numBytes))
}
uint words = (numBytes + WORD_SIZE - 1) / WORD_SIZE;
for (uint i = 0; i < words; i++) {
assembly {
mstore(add(addr, mul(i, /*WORD_SIZE*/32)), 0)
}
}
}
// Copy 'len' bytes from memory address 'src', to address 'dest'.
// This function does not check the or destination, it only copies
// the bytes.
function copy(uint src, uint dest, uint len) internal pure {
// Copy word-length chunks while possible
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
dest += WORD_SIZE;
src += WORD_SIZE;
}
// Copy remaining bytes
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
// Returns a memory pointer to the provided bytes array.
function ptr(bytes memory bts) internal pure returns (uint addr) {
assembly {
addr := bts
}
}
// Returns a memory pointer to the data portion of the provided bytes array.
function dataPtr(bytes memory bts) internal pure returns (uint addr) {
assembly {
addr := add(bts, /*BYTES_HEADER_SIZE*/32)
}
}
// This function does the same as 'dataPtr(bytes memory)', but will also return the
// length of the provided bytes array.
function fromBytes(bytes memory bts) internal pure returns (uint addr, uint len) {
len = bts.length;
assembly {
addr := add(bts, /*BYTES_HEADER_SIZE*/32)
}
}
// Creates a 'bytes memory' variable from the memory address 'addr', with the
// length 'len'. The function will allocate new memory for the bytes array, and
// the 'len bytes starting at 'addr' will be copied into that new memory.
function toBytes(uint addr, uint len) internal pure returns (bytes memory bts) {
bts = new bytes(len);
uint btsptr;
assembly {
btsptr := add(bts, /*BYTES_HEADER_SIZE*/32)
}
copy(addr, btsptr, len);
}
// Get the word stored at memory address 'addr' as a 'uint'.
function toUint(uint addr) internal pure returns (uint n) {
assembly {
n := mload(addr)
}
}
// Get the word stored at memory address 'addr' as a 'bytes32'.
function toBytes32(uint addr) internal pure returns (bytes32 bts) {
assembly {
bts := mload(addr)
}
}
/*
// Get the byte stored at memory address 'addr' as a 'byte'.
function toByte(uint addr, uint8 index) internal pure returns (byte b) {
require(index < WORD_SIZE);
uint8 n;
assembly {
n := byte(index, mload(addr))
}
b = byte(n);
}
*/
}
pragma solidity 0.6.4;
import "./Memory.sol";
library MerkleProof {
function validateMerkleProof(bytes32 appHash, string memory storeName, bytes memory key,
bytes memory value, bytes memory proof) public view returns (bool) {
if (appHash == bytes32(0)) {
return false;
}
// | storeName | key length | key | value length | value | appHash | proof |
// | 32 bytes | 32 bytes | | 32 bytes | | 32 bytes |
bytes memory input = new bytes(128+key.length+value.length+proof.length);
uint256 ptr = Memory.dataPtr(input);
bytes memory storeNameBytes = bytes(storeName);
assembly {
mstore(add(ptr, 0), mload(add(storeNameBytes, 32)))
}
uint256 src;
uint256 length;
// write key length and key to input
ptr+=32;
(src, length) = Memory.fromBytes(key);
assembly {
mstore(ptr, length)
}
ptr+=32;
Memory.copy(src, ptr, length);
// write value length and value to input
ptr+=length;
(src, length) = Memory.fromBytes(value);
assembly {
mstore(ptr, length)
}
ptr+=32;
Memory.copy(src, ptr, length);
// write appHash to input
ptr+=length;
assembly {
mstore(ptr, appHash)
}
// write proof to input
ptr+=32;
(src,length) = Memory.fromBytes(proof);
Memory.copy(src, ptr, length);
length = input.length+32;
uint256[1] memory result;
assembly {
// call validateMerkleProof precompile contract
// Contract address: 0x65
if iszero(staticcall(not(0), 0x65, input, length, result, 0x20)) {}
}
if (result[0] != 0x01) {
return false;
}
return true;
}
}
pragma solidity 0.6.4;
import "./Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
pragma solidity 0.6.4;
import "./IRelayerIncentivize.sol";
contract RelayerIncentivize is IRelayerIncentivize {
uint256 constant roundSize=1000;
uint256 constant maximumWeight=400;
mapping( uint256 => mapping(address => uint256) ) public _headerRelayersSubmitCount;
mapping( uint256 => address payable[] ) public _headerRelayerAddressRecord;
mapping( uint256 => mapping(address => uint256) ) public _transferRelayersSubmitCount;
mapping( uint256 => address payable[] ) public _transferRelayerAddressRecord;
mapping( uint256 => uint256) public _collectedRewardForHeaderRelayerPerRound;
mapping( uint256 => uint256) public _collectedRewardForTransferRelayerPerRound;
uint256 public _roundSequence = 0;
uint256 public _countInRound=0;
event LogRewardPeriodExpire(uint256 sequence, uint256 roundHeaderRelayerReward, uint256 roundTransferRelayerReward);
function addReward(address payable headerRelayerAddr, address payable caller) external override payable returns (bool) {
_countInRound++;
uint256 reward = calculateRewardForHeaderRelayer(msg.value);
_collectedRewardForHeaderRelayerPerRound[_roundSequence] += reward;
_collectedRewardForTransferRelayerPerRound[_roundSequence] += msg.value - reward;
if (_headerRelayersSubmitCount[_roundSequence][headerRelayerAddr]==0){
_headerRelayerAddressRecord[_roundSequence].push(headerRelayerAddr);
}
_headerRelayersSubmitCount[_roundSequence][headerRelayerAddr]++;
if (_transferRelayersSubmitCount[_roundSequence][caller]==0){
_transferRelayerAddressRecord[_roundSequence].push(caller);
}
_transferRelayersSubmitCount[_roundSequence][caller]++;
if (_countInRound==roundSize){
emit LogRewardPeriodExpire(_roundSequence, _collectedRewardForHeaderRelayerPerRound[_roundSequence], _collectedRewardForTransferRelayerPerRound[_roundSequence]);
claimHeaderRelayerReward(_roundSequence, caller);
claimTransferRelayerReward(_roundSequence, caller);
_roundSequence++;
_countInRound = 0;
}
return true;
}
//TODO need further discussion
function calculateRewardForHeaderRelayer(uint256 reward) internal pure returns (uint256) {
return reward/5; //20%
}
function claimHeaderRelayerReward(uint256 sequence, address payable caller) internal returns (bool) {
uint256 totalReward = _collectedRewardForHeaderRelayerPerRound[sequence];
address payable[] memory relayers = _headerRelayerAddressRecord[sequence];
uint256[] memory relayerWeight = new uint256[](relayers.length);
for(uint256 index = 0; index < relayers.length; index++) {
address relayer = relayers[index];
uint256 weight = calculateHeaderRelayerWeight(_headerRelayersSubmitCount[sequence][relayer]);
relayerWeight[index] = weight;
}
uint256 callerReward = totalReward * 5/100; //TODO need further discussion
totalReward = totalReward - callerReward;
uint256 remainReward = totalReward;
for(uint256 index = 1; index < relayers.length; index++) {
uint256 reward = relayerWeight[index]*totalReward/roundSize;
relayers[index].transfer(reward);
remainReward = remainReward-reward;
}
relayers[0].transfer(remainReward);
caller.transfer(callerReward);
delete _collectedRewardForHeaderRelayerPerRound[sequence];
for (uint256 index = 0; index < relayers.length; index++){
delete _headerRelayersSubmitCount[sequence][relayers[index]];
}
delete _headerRelayerAddressRecord[sequence];
return true;
}
function claimTransferRelayerReward(uint256 sequence, address payable caller) internal returns (bool) {
uint256 totalReward = _collectedRewardForTransferRelayerPerRound[sequence];
address payable[] memory relayers = _transferRelayerAddressRecord[sequence];
uint256[] memory relayerWeight = new uint256[](relayers.length);
for(uint256 index = 0; index < relayers.length; index++) {
address relayer = relayers[index];
uint256 weight = calculateTransferRelayerWeight(_transferRelayersSubmitCount[sequence][relayer]);
relayerWeight[index] = weight;
}
uint256 callerReward = totalReward * 5/100; //TODO need further discussion
totalReward = totalReward - callerReward;
uint256 remainReward = totalReward;
for(uint256 index = 1; index < relayers.length; index++) {
uint256 reward = relayerWeight[index]*totalReward/roundSize;
relayers[index].transfer(reward);
remainReward = remainReward-reward;
}
relayers[0].transfer(remainReward);
caller.transfer(callerReward);
delete _collectedRewardForTransferRelayerPerRound[sequence];
for (uint256 index = 0; index < relayers.length; index++){
delete _transferRelayersSubmitCount[sequence][relayers[index]];
}
delete _transferRelayerAddressRecord[sequence];
return true;
}
function calculateTransferRelayerWeight(uint256 count) public pure returns(uint256) {
if (count <= maximumWeight) {
return count;
} else if (maximumWeight < count && count <= 2*maximumWeight) {
return maximumWeight;
} else if (2*maximumWeight < count && count <= (2*maximumWeight + 3*maximumWeight/4 )) {
return 3*maximumWeight - count;
} else {
return count/4;
}
}
function calculateHeaderRelayerWeight(uint256 count) public pure returns(uint256) {
if (count <= maximumWeight) {
return count;
} else if (maximumWeight < count && count <= 2*maximumWeight) {
return maximumWeight;
} else {
return maximumWeight;
}
}
}
pragma solidity 0.6.4;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot 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-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
pragma solidity 0.6.4;
import "./Memory.sol";
import "./ILightClient.sol";
import "./ISystemReward.sol";
import "./IRelayerHub.sol";
contract TendermintLightClient is ILightClient {
struct ConsensusState {
uint64 preValidatorSetChangeHeight;
bytes32 appHash;
bytes32 curValidatorSetHash;
bytes nextValidatorSet;
}
mapping(uint64 => ConsensusState) public _BBCLightClientConsensusState;
mapping(uint64 => address payable) public _submitters;
uint64 public _initialHeight;
uint64 public _latestHeight;
bool public _alreadyInit;
address constant public _relayerHubContract=0x0000000000000000000000000000000000001006;
address constant public _systemRewardContract=0x0000000000000000000000000000000000001002;
string constant public _chainID="Binance-Chain-Nile";
bytes constant public _initConsensusStateBytes = hex"746573742d636861696e00000000000000000000000000000000000000000000000000000000000229eca254b3859bffefaf85f4c95da9fbd26527766b784272789c30ec56b380b6eb96442aaab207bc59978ba3dd477690f5c5872334fc39e627723daa97e441e88ba4515150ec3182bc82593df36f8abb25a619187fcfab7e552b94e64ed2deed000000e8d4a51000";
uint256 constant public _rewardForValidatorSetChange = 10000000000000000;
event InitConsensusState(uint64 initHeight, bytes32 appHash);
event SyncConsensusState(uint64 height, uint64 preValidatorSetChangeHeight, bytes32 appHash, bool validatorChanged);
/* solium-disable-next-line */
constructor() public {}
modifier onlyRelayer() {
require(IRelayerHub(_relayerHubContract).isRelayer(msg.sender), "the msg sender is not a relayer");
_;
}
function init() public {
require(!_alreadyInit, "already initialized");
uint256 pointer;
uint256 length;
(pointer, length) = Memory.fromBytes(_initConsensusStateBytes);
ConsensusState memory cs;
uint64 height;
(cs, height) = decodeConsensusState(pointer, length, false);
cs.preValidatorSetChangeHeight = 0;
_BBCLightClientConsensusState[height] = cs;
_initialHeight = height;
_latestHeight = height;
_alreadyInit = true;
emit InitConsensusState(_initialHeight, cs.appHash);
}
function syncTendermintHeader(bytes calldata header, uint64 height) external onlyRelayer returns (bool) {
require(_submitters[height] == address(0x0), "can't sync duplicated header");
require(height > _initialHeight, "can't sync header before _initialHeight");
uint64 preValidatorSetChangeHeight = _latestHeight;
ConsensusState memory cs = _BBCLightClientConsensusState[preValidatorSetChangeHeight];
for(; preValidatorSetChangeHeight >= _initialHeight;) {
if (preValidatorSetChangeHeight < height) {
// find nearest previous height
break;
}
preValidatorSetChangeHeight = cs.preValidatorSetChangeHeight;
cs = _BBCLightClientConsensusState[preValidatorSetChangeHeight];
}
if (cs.nextValidatorSet.length == 0) {
preValidatorSetChangeHeight = cs.preValidatorSetChangeHeight;
cs.nextValidatorSet = _BBCLightClientConsensusState[preValidatorSetChangeHeight].nextValidatorSet;
require(cs.nextValidatorSet.length != 0, "failed to load validator set data");
}
//32 + 32 + 8 + 32 + 32 + cs.nextValidatorSet.length;
uint256 length = 136 + cs.nextValidatorSet.length;
bytes memory input = new bytes(length+header.length);
uint256 ptr = Memory.dataPtr(input);
require(encodeConsensusState(cs, preValidatorSetChangeHeight, ptr, length), "failed to serialize consensus state");
// write header to input
uint256 src;
ptr = ptr+length;
(src, length) = Memory.fromBytes(header);
Memory.copy(src, ptr, length);
length = input.length+32;
// Maximum validator quantity is 99
bytes32[128] memory result;
/* solium-disable-next-line */
assembly {
// call validateTendermintHeader precompile contract
// Contract address: 0x64
if iszero(staticcall(not(0), 0x64, input, length, result, 4096)) {
revert(0, 0)
}
}
//Judge if the validator set is changed
/* solium-disable-next-line */
assembly {
length := mload(add(result, 0))
}
bool validatorChanged = false;
if ((length&0x0100000000000000000000000000000000000000000000000000000000000000)!=0x00) {
validatorChanged = true;
ISystemReward(_systemRewardContract).claimRewards(msg.sender, _rewardForValidatorSetChange);//TODO further discussion about reward amount
}
length = length&0x000000000000000000000000000000000000000000000000ffffffffffffffff;
/* solium-disable-next-line */
assembly {
ptr := add(result, 32)
}
uint64 actualHeaderHeight;
(cs, actualHeaderHeight) = decodeConsensusState(ptr, length, !validatorChanged);
require(actualHeaderHeight == height, "header height doesn't equal to the specified height");
_submitters[height] = msg.sender;
cs.preValidatorSetChangeHeight = preValidatorSetChangeHeight;
_BBCLightClientConsensusState[height] = cs;
if (height > _latestHeight) {
_latestHeight = height;
}
emit SyncConsensusState(height, preValidatorSetChangeHeight, cs.appHash, validatorChanged);
return true;
}
function isHeaderSynced(uint64 height) external override view returns (bool) {
return _submitters[height] != address(0x0);
}
function getAppHash(uint64 height) external override view returns (bytes32) {
return _BBCLightClientConsensusState[height].appHash;
}
function getSubmitter(uint64 height) external override view returns (address payable) {
return _submitters[height];
}
// | length | _chainID | height | appHash | curValidatorSetHash | [{validator pubkey, voting power}] |
// | 32 bytes | 32 bytes | 8 bytes | 32 bytes | 32 bytes | [{32 bytes, 8 bytes}] |
/* solium-disable-next-line */
function encodeConsensusState(ConsensusState memory cs, uint64 height, uint256 outputPtr, uint256 size) internal pure returns (bool) {
uint256 validatorQuantity = cs.nextValidatorSet.length/40;
outputPtr = outputPtr + size - 40 * validatorQuantity;
uint256 src;
uint256 length;
(src, length) = Memory.fromBytes(cs.nextValidatorSet);
Memory.copy(src, outputPtr, length);
outputPtr = outputPtr-32;
bytes32 hash = cs.curValidatorSetHash;
/* solium-disable-next-line */
assembly {
mstore(outputPtr, hash)
}
outputPtr = outputPtr-32;
hash = cs.appHash;
/* solium-disable-next-line */
assembly {
mstore(outputPtr, hash)
}
outputPtr = outputPtr-32;
/* solium-disable-next-line */
assembly {
mstore(outputPtr, height)
}
outputPtr = outputPtr-8;
bytes memory chainIDBytes = bytes(_chainID);
/* solium-disable-next-line */
assembly {
mstore(outputPtr, mload(add(chainIDBytes, 32)))
}
outputPtr = outputPtr-32;
// size doesn't contain length
size = size-32;
/* solium-disable-next-line */
assembly {
mstore(outputPtr, size)
}
return true;
}
// | _chainID | height | appHash | curValidatorSetHash | [{validator pubkey, voting power}] |
// | 32 bytes | 8 bytes | 32 bytes | 32 bytes | [{32 bytes, 8 bytes}] |
/* solium-disable-next-line */
function decodeConsensusState(uint256 ptr, uint256 size, bool leaveOutValidatorSet) internal pure returns(ConsensusState memory, uint64) {
// 104 = 32 +32 +8 + 32 +32
uint256 validatorSetLength = (size-104)/40;
ptr = ptr+8;
uint64 height;
/* solium-disable-next-line */
assembly {
height := mload(ptr)
}
ptr = ptr+32;
bytes32 appHash;
/* solium-disable-next-line */
assembly {
appHash := mload(ptr)
}
ptr = ptr+32;
bytes32 curValidatorSetHash;
/* solium-disable-next-line */
assembly {
curValidatorSetHash := mload(ptr)
}
ConsensusState memory cs;
cs.appHash = appHash;
cs.curValidatorSetHash = curValidatorSetHash;
if (!leaveOutValidatorSet) {
uint256 dest;
uint256 length;
cs.nextValidatorSet = new bytes(40*validatorSetLength);
(dest,length) = Memory.fromBytes(cs.nextValidatorSet);
Memory.copy(ptr+32, dest, length);
}
return (cs, height);
}
}
pragma solidity 0.6.4;
import "./IERC20.sol";
import "./ILightClient.sol";
import "./IRelayerIncentivize.sol";
import "./MerkleProof.sol";
import "./ISystemReward.sol";
import "./ITokenHub.sol";
import "./IRelayerHub.sol";
contract TokenHub is ITokenHub {
struct BindPackage {
bytes32 bep2TokenSymbol;
address contractAddr;
uint256 totalSupply;
uint256 peggyAmount;
uint8 erc20Decimals;
uint64 expireTime;
uint256 relayFee;
}
struct RefundPackage {
uint256 refundAmount;
address contractAddr;
address payable refundAddr;
uint16 reason;
}
struct TransferInPackage {
bytes32 bep2TokenSymbol;
address contractAddr;
address refundAddr;
address payable recipient;
uint256 amount;
uint64 expireTime;
uint256 relayFee;
}
uint8 constant public bindChannelID = 0x01;
uint8 constant public transferInChannelID = 0x02;
uint8 constant public refundChannelID=0x03;
// the store name of the package
string constant public STORE_NAME = "ibc";
uint256 constant public maxBep2TotalSupply = 9000000000000000000;
bytes32 constant bep2TokenSymbolForBNB = 0x424E420000000000000000000000000000000000000000000000000000000000; // "BNB"
bytes32 constant crossChainKeyPrefix = 0x0000000000000000000000000000000000000000000000000000000000010002; // last 5 bytes
uint256 constant public _maxGasForCallingERC20=50000;
uint256 constant public _minimumRelayFee=10000000000000000;
uint256 constant public _refundRelayReward=10000000000000000;
address constant public _relayerHubContract=0x0000000000000000000000000000000000001006;
address constant public _systemRewardContract=0x0000000000000000000000000000000000001002;
address constant public _lightClientContract=0x0000000000000000000000000000000000001003;
address constant public _incentivizeContractForRelayers=0x0000000000000000000000000000000000001005;
mapping(bytes32 => BindPackage) public _bindPackageRecord;
mapping(address => bytes32) public _contractAddrToBEP2Symbol;
mapping(address => uint256) public _erc20ContractDecimals;
mapping(bytes32 => address) public _bep2SymbolToContractAddr;
uint64 public _bindChannelSequence=0;
uint64 public _transferInChannelSequence=0;
uint64 public _refundChannelSequence=0;
uint64 public _transferOutChannelSequence=0;
uint64 public _bindResponseChannelSequence=0;
uint64 public _transferInFailureChannelSequence=0;
event LogBindRequest(address contractAddr, bytes32 bep2TokenSymbol, uint256 totalSupply, uint256 peggyAmount);
event LogBindSuccess(uint256 sequence, address contractAddr, bytes32 bep2TokenSymbol, uint256 totalSupply, uint256 peggyAmount, uint256 decimals);
event LogBindRejected(uint256 sequence, address contractAddr, bytes32 bep2TokenSymbol);
event LogBindTimeout(uint256 sequence, address contractAddr, bytes32 bep2TokenSymbol);
event LogBindInvalidParameter(uint256 sequence, address contractAddr, bytes32 bep2TokenSymbol);
event LogTransferOut(uint256 sequence, address refundAddr, address recipient, uint256 amount, address contractAddr, bytes32 bep2TokenSymbol, uint256 expireTime, uint256 relayFee);
event LogBatchTransferOut(uint256 sequence, uint256[] amounts, address contractAddr, bytes32 bep2TokenSymbol, uint256 expireTime, uint256 relayFee);
event LogBatchTransferOutAddrs(uint256 sequence, address[] recipientAddrs, address[] refundAddrs);
event LogTransferInSuccess(uint256 sequence, address recipient, uint256 amount, address contractAddr);
event LogTransferInFailureTimeout(uint256 sequence, address refundAddr, address recipient, uint256 bep2TokenAmount, address contractAddr, bytes32 bep2TokenSymbol, uint256 expireTime);
event LogTransferInFailureInsufficientBalance(uint256 sequence, address refundAddr, address recipient, uint256 bep2TokenAmount, address contractAddr, bytes32 bep2TokenSymbol, uint256 actualBalance);
event LogTransferInFailureUnboundToken(uint256 sequence, address refundAddr, address recipient, uint256 bep2TokenAmount, address contractAddr, bytes32 bep2TokenSymbol);
event LogTransferInFailureUnknownReason(uint256 sequence, address refundAddr, address recipient, uint256 bep2TokenAmount, address contractAddr, bytes32 bep2TokenSymbol);
event LogRefundSuccess(address contractAddr, address refundAddr, uint256 amount, uint16 reason);
event LogRefundFailureInsufficientBalance(address contractAddr, address refundAddr, uint256 amount, uint16 reason, uint256 actualBalance);
event LogRefundFailureUnboundToken(address contractAddr, address refundAddr, uint256 amount, uint16 reason);
event LogRefundFailureUnknownReason(address contractAddr, address refundAddr, uint256 amount, uint16 reason);
event LogUnexpectedRevertInERC20(address contractAddr, string reason);
event LogUnexpectedFailureAssertionInERC20(address contractAddr, bytes lowLevelData);
constructor() public {}
modifier onlyHeaderSynced(uint64 height) {
require(ILightClient(_lightClientContract).isHeaderSynced(height), "reference header is not synced");
_;
}
modifier onlyRelayer() {
require(IRelayerHub(_relayerHubContract).isRelayer(msg.sender), "the msg sender is not a relayer");
_;
}
function bep2TokenSymbolConvert(string memory symbol) public pure returns(bytes32) {
bytes32 result;
assembly {
result := mload(add(symbol, 32))
}
return result;
}
// | length | prefix | sourceChainID| destinationChainID | channelID | sequence |
// | 32 bytes | 1 byte | 2 bytes | 2 bytes | 1 bytes | 8 bytes |
function generateKey(uint8 channelID, uint256 sequence) internal pure returns(bytes memory) {
bytes memory key = new bytes(14);
uint256 ptr;
assembly {
ptr := add(key, 14)
}
assembly {
mstore(ptr, sequence)
}
ptr -= 8;
assembly {
mstore(ptr, channelID)
}
ptr -= 1;
assembly {
mstore(ptr, crossChainKeyPrefix)
}
ptr -= 5;
assembly {
mstore(ptr, 14)
}
return key;
}
// | length | bep2TokenSymbol | contractAddr | totalSupply | peggyAmount | decimals | expireTime | relayFee |
// | 32 bytes | 32 bytes | 20 bytes | 32 bytes | 32 bytes | 1 byte | 8 bytes | 32 bytes |
function decodeBindPackage(bytes memory value) internal pure returns(BindPackage memory) {
BindPackage memory bindPackage;
uint256 ptr;
assembly {
ptr := value
}
bytes32 bep2TokenSymbol;
ptr+=32;
assembly {
bep2TokenSymbol := mload(ptr)
}
bindPackage.bep2TokenSymbol = bep2TokenSymbol;
address addr;
ptr+=20;
assembly {
addr := mload(ptr)
}
bindPackage.contractAddr = addr;
uint256 tempValue;
ptr+=32;
assembly {
tempValue := mload(ptr)
}
bindPackage.totalSupply = tempValue;
ptr+=32;
assembly {
tempValue := mload(ptr)
}
bindPackage.peggyAmount = tempValue;
ptr+=1;
uint8 decimals;
assembly {
decimals := mload(ptr)
}
bindPackage.erc20Decimals = decimals;
ptr+=8;
uint64 expireTime;
assembly {
expireTime := mload(ptr)
}
bindPackage.expireTime = expireTime;
ptr+=32;
assembly {
tempValue := mload(ptr)
}
bindPackage.relayFee = tempValue;
return bindPackage;
}
function handleBindPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence) onlyHeaderSynced(height) onlyRelayer override external returns (bool) {
require(packageSequence==_bindChannelSequence, "wrong bind sequence");
require(msgBytes.length==157, "wrong bind package size");
bytes32 appHash = ILightClient(_lightClientContract).getAppHash(height);
require(MerkleProof.validateMerkleProof(appHash, STORE_NAME, generateKey(bindChannelID, _bindChannelSequence), msgBytes, proof), "invalid merkle proof");
_bindChannelSequence++;
address payable tendermintHeaderSubmitter = ILightClient(_lightClientContract).getSubmitter(height);
BindPackage memory bindPackage = decodeBindPackage(msgBytes);
IRelayerIncentivize(_incentivizeContractForRelayers).addReward{value: bindPackage.relayFee}(tendermintHeaderSubmitter, msg.sender);
_bindPackageRecord[bindPackage.bep2TokenSymbol]=bindPackage;
emit LogBindRequest(bindPackage.contractAddr, bindPackage.bep2TokenSymbol, bindPackage.totalSupply, bindPackage.peggyAmount);
return true;
}
function checkSymbol(string memory erc20Symbol, bytes32 bep2TokenSymbol) public pure returns(bool) {
bytes memory erc20SymbolBytes = bytes(erc20Symbol);
//Upper case string
for (uint i = 0; i < erc20SymbolBytes.length; i++) {
if (0x61 <= uint8(erc20SymbolBytes[i]) && uint8(erc20SymbolBytes[i]) <= 0x7A) {
erc20SymbolBytes[i] = byte(uint8(erc20SymbolBytes[i]) - 0x20);
}
}
bytes memory bep2TokenSymbolBytes = new bytes(32);
assembly {
mstore(add(bep2TokenSymbolBytes, 32), bep2TokenSymbol)
}
bool symbolMatch = true;
for(uint256 index=0; index < erc20SymbolBytes.length; index++) {
if (erc20SymbolBytes[index] != bep2TokenSymbolBytes[index]) {
symbolMatch = false;
break;
}
}
return symbolMatch;
}
function approveBind(address contractAddr, string memory bep2Symbol) public returns (bool) {
bytes32 bep2TokenSymbol = bep2TokenSymbolConvert(bep2Symbol);
BindPackage memory bindPackage = _bindPackageRecord[bep2TokenSymbol];
uint256 lockedAmount = bindPackage.totalSupply-bindPackage.peggyAmount;
require(contractAddr==bindPackage.contractAddr, "contact address doesn't equal to the contract address in bind request");
require(IERC20(contractAddr).getOwner()==msg.sender, "only erc20 owner can approve this bind request");
require(IERC20(contractAddr).allowance(msg.sender, address(this))==lockedAmount, "allowance doesn't equal to (totalSupply - peggyAmount)");
if (bindPackage.expireTime<block.timestamp) {
emit LogBindTimeout(_bindResponseChannelSequence++, bindPackage.contractAddr, bindPackage.bep2TokenSymbol);
delete _bindPackageRecord[bep2TokenSymbol];
return false;
}
uint256 decimals = IERC20(contractAddr).decimals();
string memory erc20Symbol = IERC20(contractAddr).symbol();
if (!checkSymbol(erc20Symbol, bep2TokenSymbol) ||
_bep2SymbolToContractAddr[bindPackage.bep2TokenSymbol]!=address(0x00)||
_contractAddrToBEP2Symbol[bindPackage.contractAddr]!=bytes32(0x00)||
IERC20(bindPackage.contractAddr).totalSupply()!=bindPackage.totalSupply||
decimals!=bindPackage.erc20Decimals) {
delete _bindPackageRecord[bep2TokenSymbol];
emit LogBindInvalidParameter(_bindResponseChannelSequence++, bindPackage.contractAddr, bindPackage.bep2TokenSymbol);
return false;
}
IERC20(contractAddr).transferFrom(msg.sender, address(this), lockedAmount);
_contractAddrToBEP2Symbol[bindPackage.contractAddr] = bindPackage.bep2TokenSymbol;
_erc20ContractDecimals[bindPackage.contractAddr] = bindPackage.erc20Decimals;
_bep2SymbolToContractAddr[bindPackage.bep2TokenSymbol] = bindPackage.contractAddr;
delete _bindPackageRecord[bep2TokenSymbol];
emit LogBindSuccess(_bindResponseChannelSequence++, bindPackage.contractAddr, bindPackage.bep2TokenSymbol, bindPackage.totalSupply, bindPackage.peggyAmount, decimals);
return true;
}
function rejectBind(address contractAddr, string memory bep2Symbol) public returns (bool) {
bytes32 bep2TokenSymbol = bep2TokenSymbolConvert(bep2Symbol);
BindPackage memory bindPackage = _bindPackageRecord[bep2TokenSymbol];
require(contractAddr==bindPackage.contractAddr, "contact address doesn't equal to the contract address in bind request");
require(IERC20(contractAddr).getOwner()==msg.sender, "only erc20 owner can reject");
delete _bindPackageRecord[bep2TokenSymbol];
emit LogBindRejected(_bindResponseChannelSequence++, bindPackage.contractAddr, bindPackage.bep2TokenSymbol);
return true;
}
function expireBind(string memory bep2Symbol) public returns (bool) {
bytes32 bep2TokenSymbol = bep2TokenSymbolConvert(bep2Symbol);
BindPackage memory bindPackage = _bindPackageRecord[bep2TokenSymbol];
require(bindPackage.expireTime<block.timestamp, "bind request is not expired");
delete _bindPackageRecord[bep2TokenSymbol];
emit LogBindTimeout(_bindResponseChannelSequence++, bindPackage.contractAddr, bindPackage.bep2TokenSymbol);
return true;
}
// | length | bep2TokenSymbol | contractAddr | sender | recipient | amount | expireTime | relayFee |
// | 32 bytes | 32 bytes | 20 bytes | 20 bytes | 20 bytes | 32 bytes | 8 bytes | 32 bytes |
function decodeTransferInPackage(bytes memory value) internal pure returns (TransferInPackage memory) {
TransferInPackage memory transferInPackage;
uint256 ptr;
assembly {
ptr := value
}
uint256 tempValue;
address payable recipient;
address addr;
ptr+=32;
bytes32 bep2TokenSymbol;
assembly {
bep2TokenSymbol := mload(ptr)
}
transferInPackage.bep2TokenSymbol = bep2TokenSymbol;
ptr+=20;
assembly {
addr := mload(ptr)
}
transferInPackage.contractAddr = addr;
ptr+=20;
assembly {
addr := mload(ptr)
}
transferInPackage.refundAddr = addr;
ptr+=20;
assembly {
recipient := mload(ptr)
}
transferInPackage.recipient = recipient;
ptr+=32;
assembly {
tempValue := mload(ptr)
}
transferInPackage.amount = tempValue;
ptr+=8;
uint64 expireTime;
assembly {
expireTime := mload(ptr)
}
transferInPackage.expireTime = expireTime;
ptr+=32;
assembly {
tempValue := mload(ptr)
}
transferInPackage.relayFee = tempValue;
return transferInPackage;
}
function handleTransferInPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence) onlyHeaderSynced(height) onlyRelayer override external returns (bool) {
require(packageSequence==_transferInChannelSequence, "wrong transfer sequence");
require(msgBytes.length==164, "wrong transfer package size");
bytes32 appHash = ILightClient(_lightClientContract).getAppHash(height);
require(MerkleProof.validateMerkleProof(appHash, STORE_NAME, generateKey(transferInChannelID, _transferInChannelSequence), msgBytes, proof), "invalid merkle proof");
_transferInChannelSequence++;
address payable tendermintHeaderSubmitter = ILightClient(_lightClientContract).getSubmitter(height);
TransferInPackage memory transferInPackage = decodeTransferInPackage(msgBytes);
IRelayerIncentivize(_incentivizeContractForRelayers).addReward{value: transferInPackage.relayFee}(tendermintHeaderSubmitter, msg.sender);
if (transferInPackage.contractAddr==address(0x0) && transferInPackage.bep2TokenSymbol==bep2TokenSymbolForBNB) {
if (block.timestamp > transferInPackage.expireTime) {
emit LogTransferInFailureTimeout(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, transferInPackage.amount/10**10, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol, transferInPackage.expireTime);
return false;
}
if (address(this).balance < transferInPackage.amount) {
emit LogTransferInFailureInsufficientBalance(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, transferInPackage.amount/10**10, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol, address(this).balance);
return false;
}
if (!transferInPackage.recipient.send(transferInPackage.amount)) {
emit LogTransferInFailureUnknownReason(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, transferInPackage.amount/10**10, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
return false;
}
emit LogTransferInSuccess(_transferInChannelSequence-1, transferInPackage.recipient, transferInPackage.amount, transferInPackage.contractAddr);
return true;
} else {
uint256 bep2Amount = transferInPackage.amount * (10**8) / (10**_erc20ContractDecimals[transferInPackage.contractAddr]);
if (_contractAddrToBEP2Symbol[transferInPackage.contractAddr]!= transferInPackage.bep2TokenSymbol) {
emit LogTransferInFailureUnboundToken(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
return false;
}
if (block.timestamp > transferInPackage.expireTime) {
emit LogTransferInFailureTimeout(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol, transferInPackage.expireTime);
return false;
}
try IERC20(transferInPackage.contractAddr).transfer{gas: _maxGasForCallingERC20}(transferInPackage.recipient, transferInPackage.amount) returns (bool success) {
if (success) {
emit LogTransferInSuccess(_transferInChannelSequence-1, transferInPackage.recipient, transferInPackage.amount, transferInPackage.contractAddr);
return true;
} else {
try IERC20(transferInPackage.contractAddr).balanceOf{gas: _maxGasForCallingERC20}(address(this)) returns (uint256 actualBalance) {
emit LogTransferInFailureInsufficientBalance(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol, actualBalance);
return false;
} catch Error(string memory reason) {
emit LogTransferInFailureUnknownReason(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
emit LogUnexpectedRevertInERC20(transferInPackage.contractAddr, reason);
return false;
} catch (bytes memory lowLevelData) {
emit LogTransferInFailureUnknownReason(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
emit LogUnexpectedFailureAssertionInERC20(transferInPackage.contractAddr, lowLevelData);
return false;
}
}
} catch Error(string memory reason) {
emit LogTransferInFailureUnknownReason(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
emit LogUnexpectedRevertInERC20(transferInPackage.contractAddr, reason);
return false;
} catch (bytes memory lowLevelData) {
emit LogTransferInFailureUnknownReason(_transferInFailureChannelSequence++, transferInPackage.refundAddr, transferInPackage.recipient, bep2Amount, transferInPackage.contractAddr, transferInPackage.bep2TokenSymbol);
emit LogUnexpectedFailureAssertionInERC20(transferInPackage.contractAddr, lowLevelData);
return false;
}
}
}
// | length | refundAmount | contractAddr | refundAddr | failureReason |
// | 32 bytes | 32 bytes | 20 bytes | 20 bytes | 2 bytes |
function decodeRefundPackage(bytes memory value) internal pure returns(RefundPackage memory) {
RefundPackage memory refundPackage;
uint256 ptr;
assembly {
ptr := value
}
ptr+=32;
uint256 refundAmount;
assembly {
refundAmount := mload(ptr)
}
refundPackage.refundAmount = refundAmount;
ptr+=20;
address contractAddr;
assembly {
contractAddr := mload(ptr)
}
refundPackage.contractAddr = contractAddr;
ptr+=20;
address payable refundAddr;
assembly {
refundAddr := mload(ptr)
}
refundPackage.refundAddr = refundAddr;
ptr+=2;
uint16 reason;
assembly {
reason := mload(ptr)
}
refundPackage.reason = reason;
return refundPackage;
}
function handleRefundPackage(bytes calldata msgBytes, bytes calldata proof, uint64 height, uint64 packageSequence) onlyHeaderSynced(height) onlyRelayer override external returns (bool) {
require(packageSequence==_refundChannelSequence, "wrong refund sequence");
require(msgBytes.length==74, "wrong refund package size");
bytes32 appHash = ILightClient(_lightClientContract).getAppHash(height);
require(MerkleProof.validateMerkleProof(appHash, STORE_NAME, generateKey(refundChannelID, _refundChannelSequence), msgBytes, proof), "invalid merkle proof");
_refundChannelSequence++;
address payable tendermintHeaderSubmitter = ILightClient(_lightClientContract).getSubmitter(height);
//TODO system reward, need further discussion,
//TODO taking malicious refund cases caused by inconsistent total supply into consideration, so this reward must be less than minimum relay fee
uint256 reward = _refundRelayReward / 5;
ISystemReward(_systemRewardContract).claimRewards(tendermintHeaderSubmitter, reward);
reward = _refundRelayReward-reward;
ISystemReward(_systemRewardContract).claimRewards(msg.sender, reward);
RefundPackage memory refundPackage = decodeRefundPackage(msgBytes);
if (refundPackage.contractAddr==address(0x0)) {
uint256 actualBalance = address(this).balance;
if (actualBalance < refundPackage.refundAmount) {
emit LogRefundFailureInsufficientBalance(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason, actualBalance);
return false;
}
if (!refundPackage.refundAddr.send(refundPackage.refundAmount)){
emit LogRefundFailureUnknownReason(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
return false;
}
emit LogRefundSuccess(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
return true;
} else {
if (_contractAddrToBEP2Symbol[refundPackage.contractAddr]==bytes32(0x00)) {
emit LogRefundFailureUnboundToken(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
return false;
}
try IERC20(refundPackage.contractAddr).transfer{gas: _maxGasForCallingERC20}(refundPackage.refundAddr, refundPackage.refundAmount) returns (bool success) {
if (success) {
emit LogRefundSuccess(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
return true;
} else {
try IERC20(refundPackage.contractAddr).balanceOf{gas: _maxGasForCallingERC20}(address(this)) returns (uint256 actualBalance) {
emit LogRefundFailureInsufficientBalance(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason, actualBalance);
return false;
} catch Error(string memory reason) {
emit LogRefundFailureUnknownReason(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
emit LogUnexpectedRevertInERC20(refundPackage.contractAddr, reason);
return false;
} catch (bytes memory lowLevelData) {
emit LogRefundFailureUnknownReason(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
emit LogUnexpectedFailureAssertionInERC20(refundPackage.contractAddr, lowLevelData);
return false;
}
}
} catch Error(string memory reason) {
emit LogRefundFailureUnknownReason(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
emit LogUnexpectedRevertInERC20(refundPackage.contractAddr, reason);
return false;
} catch (bytes memory lowLevelData) {
emit LogRefundFailureUnknownReason(refundPackage.contractAddr, refundPackage.refundAddr, refundPackage.refundAmount, refundPackage.reason);
emit LogUnexpectedFailureAssertionInERC20(refundPackage.contractAddr, lowLevelData);
return false;
}
}
}
function transferOut(address contractAddr, address recipient, uint256 amount, uint256 expireTime, uint256 relayFee) override external payable returns (bool) {
require(relayFee%(10**10)==0, "relayFee is must be N*10^10");
require(relayFee>=_minimumRelayFee, "relayFee is too little");
require(expireTime>=block.timestamp + 120, "expireTime must be two minutes later");
uint256 convertedRelayFee = relayFee / (10**10); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18
bytes32 bep2TokenSymbol;
uint256 convertedAmount;
if (contractAddr==address(0x0)) {
require(msg.value==amount+relayFee, "received BNB amount doesn't equal to the sum of transfer amount and relayFee");
convertedAmount = amount / (10**10); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18
bep2TokenSymbol=bep2TokenSymbolForBNB;
} else {
uint256 erc20TokenDecimals=_erc20ContractDecimals[contractAddr];
if (erc20TokenDecimals > 8) {
uint256 extraPrecision = 10**(erc20TokenDecimals-8);
require(amount%extraPrecision==0, "invalid transfer amount: precision loss in amount conversion");
}
bep2TokenSymbol = _contractAddrToBEP2Symbol[contractAddr];
require(bep2TokenSymbol!=bytes32(0x00), "the contract has not been bind to any bep2 token");
require(msg.value==relayFee, "received BNB amount doesn't equal to relayFee");
require(IERC20(contractAddr).transferFrom(msg.sender, address(this), amount));
convertedAmount = amount * (10**8)/ (10**erc20TokenDecimals); // bep2 token decimals is 8 on BBC
require(convertedAmount<=maxBep2TotalSupply, "amount is too large, int64 overflow");
}
emit LogTransferOut(_transferOutChannelSequence++, msg.sender, recipient, convertedAmount, contractAddr, bep2TokenSymbol, expireTime, convertedRelayFee);
return true;
}
function batchTransferOut(address[] calldata recipientAddrs, uint256[] calldata amounts, address[] calldata refundAddrs, address contractAddr, uint256 expireTime, uint256 relayFee) override external payable returns (bool) {
require(recipientAddrs.length == amounts.length, "Length of recipientAddrs doesn't equal to length of amounts");
require(recipientAddrs.length == refundAddrs.length, "Length of recipientAddrs doesn't equal to length of refundAddrs");
require(relayFee/amounts.length>=_minimumRelayFee, "relayFee is too little");
require(relayFee%(10**10)==0, "relayFee must be N*10^10");
require(expireTime>=block.timestamp + 120, "expireTime must be two minutes later");
uint256 totalAmount = 0;
for (uint i = 0; i < amounts.length; i++) {
totalAmount += amounts[i];
}
uint256[] memory convertedAmounts = new uint256[](amounts.length);
bytes32 bep2TokenSymbol;
if (contractAddr==address(0x0)) {
for (uint8 i = 0; i < amounts.length; i++) {
require(amounts[i]%10**10==0, "invalid transfer amount");
convertedAmounts[i] = amounts[i]/10**10;
}
require(msg.value==totalAmount+relayFee, "received BNB amount doesn't equal to the sum of transfer amount and relayFee");
bep2TokenSymbol=bep2TokenSymbolForBNB;
} else {
uint256 erc20TokenDecimals=_erc20ContractDecimals[contractAddr];
for (uint i = 0; i < amounts.length; i++) {
require((amounts[i]*(10**8)%(10**erc20TokenDecimals))==0, "invalid transfer amount");
uint256 convertedAmount = amounts[i]*(10**8)/(10**erc20TokenDecimals);
require(convertedAmount<=maxBep2TotalSupply, "amount is too large, int64 overflow");
convertedAmounts[i] = convertedAmount;
}
bep2TokenSymbol = _contractAddrToBEP2Symbol[contractAddr];
require(bep2TokenSymbol!=bytes32(0x00), "the contract has not been bind to any bep2 token");
require(msg.value==relayFee, "received BNB amount doesn't equal to relayFee");
require(IERC20(contractAddr).transferFrom(msg.sender, address(this), totalAmount));
}
emit LogBatchTransferOut(_transferOutChannelSequence, convertedAmounts, contractAddr, bep2TokenSymbol, expireTime, relayFee/(10**10));
emit LogBatchTransferOutAddrs(_transferOutChannelSequence++, recipientAddrs, refundAddrs);
return true;
}
}
pragma solidity 0.6.4;
import "./Context.sol";
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Ownable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract XYZToken is Context, IERC20, Ownable {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
uint8 public _decimals;
string public _symbol;
string public _name;
address private _bep2TokenOwner;
constructor() public {
_name = "XYZ token";
_symbol = "xyz";
_decimals = 8;
_totalSupply = 10000000000000000;
_balances[msg.sender] = _totalSupply;
_bep2TokenOwner=address(0x35D9D41a13D6c2e01c9b1e242BAf2dF98e7E8c48);
emit Transfer(address(0), msg.sender, _totalSupply);
}
/**
* @dev Returns the address of the current owner.
*/
function getOwner() override public view returns (address) {
return owner();
}
/**
* @dev Returns the token decimals.
*/
function decimals() override external view returns (uint256) {
return _decimals;
}
/**
* @dev Returns the contract owner.
*/
function symbol() override external view returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() override public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) override public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) override public returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) override public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) override public returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) override public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment