- Contract name:
- Bridge
- Optimization enabled
- true
- Compiler version
- v0.8.19+commit.7dd6d404
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2024-11-26T13:43:01.401060Z
Constructor Arguments
000000000000000000000000000000000000000000000000000000000000002f00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018380000000000000000000000000000000000000000000000000000000000000001000000000000000000000000119262bf0d1e7ee2c0bc0c1bef0958021064d9c8
Arg [0] (uint8) : 47
Arg [1] (address[]) : [0x119262bf0d1e7ee2c0bc0c1bef0958021064d9c8]
Arg [2] (uint256) : 1
Arg [3] (uint256) : 0
Arg [4] (uint256) : 6200
Contract source code
// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity 0.8.19;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// File: @openzeppelin/contracts/utils/Address.sol
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// File: @openzeppelin/contracts/utils/Context.sol
/**
* @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 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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File: contracts/utils/AccessControl.sol
// This is adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/access/AccessControl.sol
// The only difference is added getRoleMemberIndex(bytes32 role, address account) function.
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
/**
* @dev Returns the index of the account that have `role`.
*/
function getRoleMemberIndex(bytes32 role, address account) public view returns (uint256) {
return _roles[role].members._inner._indexes[bytes32(uint256(uint160(account)))];
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
}
// File: contracts/utils/Pausable.sol
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This is a stripped down version of Open zeppelin's Pausable contract.
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/EnumerableSet.sol
*
*/
contract Pausable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor () {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_whenNotPaused();
_;
}
function _whenNotPaused() private view {
require(!_paused, "Pausable: paused");
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenPaused() {
_whenPaused();
_;
}
function _whenPaused() private view {
require(_paused, "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
* @param sender Address which executes pause.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause(address sender) internal virtual whenNotPaused {
_paused = true;
emit Paused(sender);
}
/**
* @dev Returns to normal state.
* @param sender Address which executes unpause.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause(address sender) internal virtual whenPaused {
_paused = false;
emit Unpaused(sender);
}
}
// File: contracts/utils/SafeMath.sol
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* note that this is a stripped down version of open zeppelin's safemath
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
*/
contract SafeMath {
/**
* @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.
*/
function _sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
}
// File: contracts/utils/SafeCast.sol
library SafeCast {
function toUint200(uint256 value) internal pure returns (uint200) {
require(value < 2**200, "value does not fit in 200 bits");
return uint200(value);
}
function toUint128(uint256 value) internal pure returns (uint128) {
require(value < 2**128, "value does not fit in 128 bits");
return uint128(value);
}
function toUint40(uint256 value) internal pure returns (uint40) {
require(value < 2**40, "value does not fit in 40 bits");
return uint40(value);
}
function toUint8(uint256 value) internal pure returns (uint8) {
require(value < 2**8, "value does not fit in 8 bits");
return uint8(value);
}
}
// File: contracts/interfaces/IDepositExecute.sol
/**
@title Interface for handler contracts that support deposits and deposit executions.
@author ChainSafe Systems.
*/
interface IDepositExecute {
/**
@notice It is intended that deposit are made using the Bridge contract.
@param depositer Address of account making the deposit in the Bridge contract.
@param data Consists of additional data needed for a specific deposit.
*/
function deposit(bytes32 resourceID, address depositer, bytes calldata data) external returns (bytes memory);
/**
@notice It is intended that coin deposit are made using the Bridge contract.
@param data Consists of additional data needed for a specific deposit.
*/
function depositCoin(bytes32 resourceID, bytes calldata data) external payable returns (bytes memory);
/**
@notice It is intended that proposals are executed by the Bridge contract.
@param data Consists of additional data needed for a specific deposit execution.
*/
function executeProposal(bytes32 resourceID, bytes calldata data) external;
}
// File: contracts/interfaces/IERCHandler.sol
/**
@title Interface to be used with handlers that support ERC20s and ERC721s.
@author ChainSafe Systems.
*/
interface IERCHandler {
/**
@notice Correlates {resourceID} with {contractAddress}.
@param resourceID ResourceID to be used when making deposits.
@param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
*/
function setResource(bytes32 resourceID, address contractAddress) external;
/**
@notice Marks {contractAddress} as mintable/burnable.
@param contractAddress Address of contract to be used when making or executing deposits.
*/
function setBurnable(address contractAddress) external;
/**
@notice Withdraw funds from ERC safes.
@param data ABI-encoded withdrawal params relevant to the handler.
*/
function withdraw(bytes memory data) external;
}
// File: contracts/interfaces/IGenericHandler.sol
/**
@title Interface for handler that handles generic deposits and deposit executions.
@author ChainSafe Systems.
*/
interface IGenericHandler {
/**
@notice Correlates {resourceID} with {contractAddress}, {depositFunctionSig}, and {executeFunctionSig}.
@param resourceID ResourceID to be used when making deposits.
@param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
@param depositFunctionSig Function signature of method to be called in {contractAddress} when a deposit is made.
@param depositFunctionDepositerOffset Depositer address position offset in the metadata, in bytes.
@param executeFunctionSig Function signature of method to be called in {contractAddress} when a deposit is executed.
*/
function setResource(
bytes32 resourceID,
address contractAddress,
bytes4 depositFunctionSig,
uint depositFunctionDepositerOffset,
bytes4 executeFunctionSig) external;
}
// File: contracts/Bridge.sol
pragma experimental ABIEncoderV2;
/**
@title Facilitates deposits, creation and voting of deposit proposals, and deposit executions.
@author ChainSafe Systems.
*/
contract Bridge is Pausable, AccessControl, SafeMath {
using SafeCast for *;
// Limit relayers number because proposal can fit only so much votes
uint256 constant public MAX_RELAYERS = 200;
uint8 public _domainID;
uint8 public _relayerThreshold;
uint128 public _fee;
address payable _feeReceiver;
uint40 public _expiry;
enum ProposalStatus {Inactive, Active, Passed, Executed, Cancelled}
struct Proposal {
ProposalStatus _status;
uint200 _yesVotes; // bitmap, 200 maximum votes
uint8 _yesVotesTotal;
uint40 _proposedBlock; // 1099511627775 maximum block
}
// destinationDomainID => number of deposits
mapping(uint8 => uint64) public _depositCounts;
// resourceID => handler address
mapping(bytes32 => address) public _resourceIDToHandlerAddress;
// forwarder address => is Valid
mapping(address => bool) public isValidForwarder;
// destinationDomainID + depositNonce => dataHash => Proposal
mapping(uint72 => mapping(bytes32 => Proposal)) private _proposals;
event RelayerThresholdChanged(uint256 newThreshold);
event RelayerAdded(address relayer);
event RelayerRemoved(address relayer);
event Deposit(
uint8 destinationDomainID,
bytes32 resourceID,
uint64 depositNonce,
address indexed user,
bytes data,
bytes handlerResponse
);
event ProposalEvent(
uint8 originDomainID,
uint64 depositNonce,
ProposalStatus status,
bytes32 dataHash
);
event ProposalVote(
uint8 originDomainID,
uint64 depositNonce,
ProposalStatus status,
bytes32 dataHash
);
event FailedHandlerExecution(
bytes lowLevelData
);
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
modifier onlyAdmin() {
_onlyAdmin();
_;
}
modifier onlyAdminOrRelayer() {
_onlyAdminOrRelayer();
_;
}
modifier onlyRelayers() {
_onlyRelayers();
_;
}
function _onlyAdminOrRelayer() private view {
address sender = _msgSender();
require(hasRole(DEFAULT_ADMIN_ROLE, sender) || hasRole(RELAYER_ROLE, sender),
"sender is not relayer or admin");
}
function _onlyAdmin() private view {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "sender doesn't have admin role");
}
function _onlyRelayers() private view {
require(hasRole(RELAYER_ROLE, _msgSender()), "sender doesn't have relayer role");
}
function _relayerBit(address relayer) private view returns(uint) {
return uint(1) << sub(AccessControl.getRoleMemberIndex(RELAYER_ROLE, relayer), 1);
}
function _hasVoted(Proposal memory proposal, address relayer) private view returns(bool) {
return (_relayerBit(relayer) & uint(proposal._yesVotes)) > 0;
}
function _msgSender() internal override view returns (address) {
address signer = msg.sender;
if (msg.data.length >= 20 && isValidForwarder[signer]) {
assembly {
signer := shr(96, calldataload(sub(calldatasize(), 20)))
}
}
return signer;
}
/**
@notice Initializes Bridge, creates and grants {_msgSender()} the admin role,
creates and grants {initialRelayers} the relayer role.
@param domainID ID of chain the Bridge contract exists on.
@param initialRelayers Addresses that should be initially granted the relayer role.
@param initialRelayerThreshold Number of votes needed for a deposit proposal to be considered passed.
*/
constructor (uint8 domainID, address[] memory initialRelayers, uint256 initialRelayerThreshold, uint256 fee, uint256 expiry) public {
_domainID = domainID;
_relayerThreshold = initialRelayerThreshold.toUint8();
_fee = fee.toUint128();
_expiry = expiry.toUint40();
if (initialRelayers.length >= 0) {
_feeReceiver = payable(initialRelayers[0]);
}
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
for (uint256 i; i < initialRelayers.length; i++) {
grantRole(RELAYER_ROLE, initialRelayers[i]);
}
}
/**
@notice Returns true if {relayer} has voted on {destNonce} {dataHash} proposal.
@notice Naming left unchanged for backward compatibility.
@param destNonce destinationDomainID + depositNonce of the proposal.
@param dataHash Hash of data to be provided when deposit proposal is executed.
@param relayer Address to check.
*/
function _hasVotedOnProposal(uint72 destNonce, bytes32 dataHash, address relayer) public view returns(bool) {
return _hasVoted(_proposals[destNonce][dataHash], relayer);
}
/**
@notice Returns true if {relayer} has the relayer role.
@param relayer Address to check.
*/
function isRelayer(address relayer) external view returns (bool) {
return hasRole(RELAYER_ROLE, relayer);
}
/**
@notice Removes admin role from {_msgSender()} and grants it to {newAdmin}.
@notice Only callable by an address that currently has the admin role.
@param newAdmin Address that admin role will be granted to.
*/
function renounceAdmin(address newAdmin) external onlyAdmin {
address sender = _msgSender();
require(sender != newAdmin, 'Cannot renounce oneself');
grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
renounceRole(DEFAULT_ADMIN_ROLE, sender);
}
/**
@notice Pauses deposits, proposal creation and voting, and deposit executions.
@notice Only callable by an address that currently has the admin role.
*/
function adminPauseTransfers() external onlyAdmin {
_pause(_msgSender());
}
/**
@notice Unpauses deposits, proposal creation and voting, and deposit executions.
@notice Only callable by an address that currently has the admin role.
*/
function adminUnpauseTransfers() external onlyAdmin {
_unpause(_msgSender());
}
/**
@notice Modifies the number of votes required for a proposal to be considered passed.
@notice Only callable by an address that currently has the admin role.
@param newThreshold Value {_relayerThreshold} will be changed to.
@notice Emits {RelayerThresholdChanged} event.
*/
function adminChangeRelayerThreshold(uint256 newThreshold) external onlyAdmin {
_relayerThreshold = newThreshold.toUint8();
emit RelayerThresholdChanged(newThreshold);
}
/**
@notice Grants {relayerAddress} the relayer role.
@notice Only callable by an address that currently has the admin role, which is
checked in grantRole().
@param relayerAddress Address of relayer to be added.
@notice Emits {RelayerAdded} event.
*/
function adminAddRelayer(address relayerAddress) external {
require(!hasRole(RELAYER_ROLE, relayerAddress), "addr already has relayer role!");
require(_totalRelayers() < MAX_RELAYERS, "relayers limit reached");
grantRole(RELAYER_ROLE, relayerAddress);
emit RelayerAdded(relayerAddress);
}
/**
@notice Removes relayer role for {relayerAddress}.
@notice Only callable by an address that currently has the admin role, which is
checked in revokeRole().
@param relayerAddress Address of relayer to be removed.
@notice Emits {RelayerRemoved} event.
*/
function adminRemoveRelayer(address relayerAddress) external {
require(hasRole(RELAYER_ROLE, relayerAddress), "addr doesn't have relayer role!");
revokeRole(RELAYER_ROLE, relayerAddress);
emit RelayerRemoved(relayerAddress);
}
/**
@notice Sets a new resource for handler contracts that use the IERCHandler interface,
and maps the {handlerAddress} to {resourceID} in {_resourceIDToHandlerAddress}.
@notice Only callable by an address that currently has the admin role.
@param handlerAddress Address of handler resource will be set for.
@param resourceID ResourceID to be used when making deposits.
@param tokenAddress Address of contract to be called when a deposit is made and a deposited is executed.
*/
function adminSetResource(address handlerAddress, bytes32 resourceID, address tokenAddress) external onlyAdmin {
_resourceIDToHandlerAddress[resourceID] = handlerAddress;
IERCHandler handler = IERCHandler(handlerAddress);
handler.setResource(resourceID, tokenAddress);
}
/**
@notice Sets a new resource for handler contracts that use the IGenericHandler interface,
and maps the {handlerAddress} to {resourceID} in {_resourceIDToHandlerAddress}.
@notice Only callable by an address that currently has the admin role.
@param handlerAddress Address of handler resource will be set for.
@param resourceID ResourceID to be used when making deposits.
@param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
*/
function adminSetGenericResource(
address handlerAddress,
bytes32 resourceID,
address contractAddress,
bytes4 depositFunctionSig,
uint256 depositFunctionDepositerOffset,
bytes4 executeFunctionSig
) external onlyAdmin {
_resourceIDToHandlerAddress[resourceID] = handlerAddress;
IGenericHandler handler = IGenericHandler(handlerAddress);
handler.setResource(resourceID, contractAddress, depositFunctionSig, depositFunctionDepositerOffset, executeFunctionSig);
}
/**
@notice Sets a resource as burnable for handler contracts that use the IERCHandler interface.
@notice Only callable by an address that currently has the admin role.
@param handlerAddress Address of handler resource will be set for.
@param tokenAddress Address of contract to be called when a deposit is made and a deposited is executed.
*/
function adminSetBurnable(address handlerAddress, address tokenAddress) external onlyAdmin {
IERCHandler handler = IERCHandler(handlerAddress);
handler.setBurnable(tokenAddress);
}
/**
@notice Sets the nonce for the specific domainID.
@notice Only callable by an address that currently has the admin role.
@param domainID Domain ID for increasing nonce.
@param nonce The nonce value to be set.
*/
function adminSetDepositNonce(uint8 domainID, uint64 nonce) external onlyAdmin {
require(nonce > _depositCounts[domainID], "Does not allow decrements of the nonce");
_depositCounts[domainID] = nonce;
}
/**
@notice Set a forwarder to be used.
@notice Only callable by an address that currently has the admin role.
@param forwarder Forwarder address to be added.
@param valid Decision for the specific forwarder.
*/
function adminSetForwarder(address forwarder, bool valid) external onlyAdmin {
isValidForwarder[forwarder] = valid;
}
/**
@notice Returns a proposal.
@param originDomainID Chain ID deposit originated from.
@param depositNonce ID of proposal generated by proposal's origin Bridge contract.
@param dataHash Hash of data to be provided when deposit proposal is executed.
@return Proposal which consists of:
- _dataHash Hash of data to be provided when deposit proposal is executed.
- _yesVotes Number of votes in favor of proposal.
- _noVotes Number of votes against proposal.
- _status Current status of proposal.
*/
function getProposal(uint8 originDomainID, uint64 depositNonce, bytes32 dataHash) external view returns (Proposal memory) {
uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(originDomainID);
return _proposals[nonceAndID][dataHash];
}
/**
@notice Returns total relayers number.
@notice Added for backwards compatibility.
*/
function _totalRelayers() public view returns (uint) {
return AccessControl.getRoleMemberCount(RELAYER_ROLE);
}
/**
@notice Changes deposit fee.
@notice Only callable by admin.
@param newFee Value {_fee} will be updated to.
*/
function adminChangeFee(uint256 newFee) external onlyAdmin {
require(_fee != newFee, "Current fee is equal to new fee");
_fee = newFee.toUint128();
}
/**
@notice Changes deposit fee receiver.
@notice Only callable by admin.
@param newFeeReceiver Value {_feeReceiver} will be updated to.
*/
function adminChangeFeeReceiver(address payable newFeeReceiver) external onlyAdmin {
_feeReceiver = newFeeReceiver;
}
/**
@notice Used to manually withdraw funds from ERC safes.
@param handlerAddress Address of handler to withdraw from.
@param data ABI-encoded withdrawal params relevant to the specified handler.
*/
function adminWithdraw(
address handlerAddress,
bytes memory data
) external onlyAdmin {
IERCHandler handler = IERCHandler(handlerAddress);
handler.withdraw(data);
}
/**
@notice Initiates a transfer using a specified handler contract.
@notice Only callable when Bridge is not paused.
@param destinationDomainID ID of chain deposit will be bridged to.
@param resourceID ResourceID used to find address of handler to be used for deposit.
@param data Additional data to be passed to specified handler.
@notice Emits {Deposit} event with all necessary parameters and a handler response.
- ERC20Handler: responds with an empty data.
- ERC721Handler: responds with the deposited token metadata acquired by calling a tokenURI method in the token contract.
- GenericHandler: responds with the raw bytes returned from the call to the target contract.
*/
function deposit(uint8 destinationDomainID, bytes32 resourceID, bytes calldata data) external payable whenNotPaused {
require(msg.value == _fee, "Incorrect fee supplied");
if (_feeReceiver != address(0)) _feeReceiver.transfer(_fee);
address handler = _resourceIDToHandlerAddress[resourceID];
require(handler != address(0), "resourceID not mapped to handler");
uint64 depositNonce = ++_depositCounts[destinationDomainID];
address sender = _msgSender();
IDepositExecute depositHandler = IDepositExecute(handler);
bytes memory handlerResponse = depositHandler.deposit(resourceID, sender, data);
emit Deposit(destinationDomainID, resourceID, depositNonce, sender, data, handlerResponse);
}
/**
@notice Initiates a coin transfer using a specified handler contract.
@notice Only callable when Bridge is not paused.
@param destinationDomainID ID of chain deposit will be bridged to.
@param resourceID ResourceID used to find address of handler to be used for deposit.
@param data Additional data to be passed to specified handler.
@notice Emits {Deposit} event with all necessary parameters and a handler response.
- ERC20Handler: responds with an empty data.
- ERC721Handler: responds with the deposited token metadata acquired by calling a tokenURI method in the token contract.
- GenericHandler: responds with the raw bytes returned from the call to the target contract.
*/
function depositCoin(uint8 destinationDomainID, bytes32 resourceID, bytes calldata data) external payable whenNotPaused {
require(msg.value >= _fee, "Can't pay fee");
if (_feeReceiver != address(0)) _feeReceiver.transfer(_fee);
address handler = _resourceIDToHandlerAddress[resourceID];
require(handler != address(0), "resourceID not mapped to handler");
uint64 depositNonce = ++_depositCounts[destinationDomainID];
address sender = _msgSender();
IDepositExecute depositHandler = IDepositExecute(handler);
bytes memory handlerResponse = depositHandler.depositCoin{value: msg.value - _fee}(resourceID, data);
emit Deposit(destinationDomainID, resourceID, depositNonce, sender, data, handlerResponse);
}
/**
@notice When called, {_msgSender()} will be marked as voting in favor of proposal.
@notice Only callable by relayers when Bridge is not paused.
@param domainID ID of chain deposit originated from.
@param depositNonce ID of deposited generated by origin Bridge contract.
@param data Data originally provided when deposit was made.
@notice Proposal must not have already been passed or executed.
@notice {_msgSender()} must not have already voted on proposal.
@notice Emits {ProposalEvent} event with status indicating the proposal status.
@notice Emits {ProposalVote} event.
*/
function voteProposal(uint8 domainID, uint64 depositNonce, bytes32 resourceID, bytes calldata data) external onlyRelayers whenNotPaused {
address handler = _resourceIDToHandlerAddress[resourceID];
uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(domainID);
bytes32 dataHash = keccak256(abi.encodePacked(handler, data));
Proposal memory proposal = _proposals[nonceAndID][dataHash];
require(_resourceIDToHandlerAddress[resourceID] != address(0), "no handler for resourceID");
if (proposal._status == ProposalStatus.Passed) {
executeProposal(domainID, depositNonce, data, resourceID, true);
return;
}
address sender = _msgSender();
require(uint(proposal._status) <= 1, "proposal already executed/cancelled");
require(!_hasVoted(proposal, sender), "relayer already voted");
if (proposal._status == ProposalStatus.Inactive) {
proposal = Proposal({
_status : ProposalStatus.Active,
_yesVotes : 0,
_yesVotesTotal : 0,
_proposedBlock : uint40(block.number) // Overflow is desired.
});
emit ProposalEvent(domainID, depositNonce, ProposalStatus.Active, dataHash);
} else if (uint40(sub(block.number, proposal._proposedBlock)) > _expiry) {
// if the number of blocks that has passed since this proposal was
// submitted exceeds the expiry threshold set, cancel the proposal
proposal._status = ProposalStatus.Cancelled;
emit ProposalEvent(domainID, depositNonce, ProposalStatus.Cancelled, dataHash);
}
if (proposal._status != ProposalStatus.Cancelled) {
proposal._yesVotes = (proposal._yesVotes | _relayerBit(sender)).toUint200();
proposal._yesVotesTotal++; // TODO: check if bit counting is cheaper.
emit ProposalVote(domainID, depositNonce, proposal._status, dataHash);
// Finalize if _relayerThreshold has been reached
if (proposal._yesVotesTotal >= _relayerThreshold) {
proposal._status = ProposalStatus.Passed;
emit ProposalEvent(domainID, depositNonce, ProposalStatus.Passed, dataHash);
}
}
_proposals[nonceAndID][dataHash] = proposal;
if (proposal._status == ProposalStatus.Passed) {
executeProposal(domainID, depositNonce, data, resourceID, false);
}
}
/**
@notice Cancels a deposit proposal that has not been executed yet.
@notice Only callable by relayers when Bridge is not paused.
@param domainID ID of chain deposit originated from.
@param depositNonce ID of deposited generated by origin Bridge contract.
@param dataHash Hash of data originally provided when deposit was made.
@notice Proposal must be past expiry threshold.
@notice Emits {ProposalEvent} event with status {Cancelled}.
*/
function cancelProposal(uint8 domainID, uint64 depositNonce, bytes32 dataHash) public onlyAdminOrRelayer {
uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(domainID);
Proposal memory proposal = _proposals[nonceAndID][dataHash];
ProposalStatus currentStatus = proposal._status;
require(currentStatus == ProposalStatus.Active || currentStatus == ProposalStatus.Passed,
"Proposal cannot be cancelled");
require(uint40(sub(block.number, proposal._proposedBlock)) > _expiry, "Proposal not at expiry threshold");
proposal._status = ProposalStatus.Cancelled;
_proposals[nonceAndID][dataHash] = proposal;
emit ProposalEvent(domainID, depositNonce, ProposalStatus.Cancelled, dataHash);
}
/**
@notice Executes a deposit proposal that is considered passed using a specified handler contract.
@notice Only callable by relayers when Bridge is not paused.
@param domainID ID of chain deposit originated from.
@param resourceID ResourceID to be used when making deposits.
@param depositNonce ID of deposited generated by origin Bridge contract.
@param data Data originally provided when deposit was made.
@param revertOnFail Decision if the transaction should be reverted in case of handler's executeProposal is reverted or not.
@notice Proposal must have Passed status.
@notice Hash of {data} must equal proposal's {dataHash}.
@notice Emits {ProposalEvent} event with status {Executed}.
@notice Emits {FailedExecution} event with the failed reason.
*/
function executeProposal(uint8 domainID, uint64 depositNonce, bytes calldata data, bytes32 resourceID, bool revertOnFail) public onlyRelayers whenNotPaused {
address handler = _resourceIDToHandlerAddress[resourceID];
uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(domainID);
bytes32 dataHash = keccak256(abi.encodePacked(handler, data));
Proposal storage proposal = _proposals[nonceAndID][dataHash];
require(proposal._status == ProposalStatus.Passed, "Proposal must have Passed status");
proposal._status = ProposalStatus.Executed;
IDepositExecute depositHandler = IDepositExecute(handler);
if (revertOnFail) {
depositHandler.executeProposal(resourceID, data);
} else {
try depositHandler.executeProposal(resourceID, data) {
} catch (bytes memory lowLevelData) {
proposal._status = ProposalStatus.Passed;
emit FailedHandlerExecution(lowLevelData);
return;
}
}
emit ProposalEvent(domainID, depositNonce, ProposalStatus.Executed, dataHash);
}
/**
@notice Transfers eth in the contract to the specified addresses. The parameters addrs and amounts are mapped 1-1.
This means that the address at index 0 for addrs will receive the amount (in WEI) from amounts at index 0.
@param addrs Array of addresses to transfer {amounts} to.
@param amounts Array of amonuts to transfer to {addrs}.
*/
function transferFunds(address payable[] calldata addrs, uint[] calldata amounts) external onlyAdmin {
for (uint256 i = 0; i < addrs.length; i++) {
addrs[i].transfer(amounts[i]);
}
}
}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint8","name":"domainID","internalType":"uint8"},{"type":"address[]","name":"initialRelayers","internalType":"address[]"},{"type":"uint256","name":"initialRelayerThreshold","internalType":"uint256"},{"type":"uint256","name":"fee","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"}]},{"type":"event","name":"Deposit","inputs":[{"type":"uint8","name":"destinationDomainID","internalType":"uint8","indexed":false},{"type":"bytes32","name":"resourceID","internalType":"bytes32","indexed":false},{"type":"uint64","name":"depositNonce","internalType":"uint64","indexed":false},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"bytes","name":"data","internalType":"bytes","indexed":false},{"type":"bytes","name":"handlerResponse","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"FailedHandlerExecution","inputs":[{"type":"bytes","name":"lowLevelData","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalEvent","inputs":[{"type":"uint8","name":"originDomainID","internalType":"uint8","indexed":false},{"type":"uint64","name":"depositNonce","internalType":"uint64","indexed":false},{"type":"uint8","name":"status","internalType":"enum Bridge.ProposalStatus","indexed":false},{"type":"bytes32","name":"dataHash","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"ProposalVote","inputs":[{"type":"uint8","name":"originDomainID","internalType":"uint8","indexed":false},{"type":"uint64","name":"depositNonce","internalType":"uint64","indexed":false},{"type":"uint8","name":"status","internalType":"enum Bridge.ProposalStatus","indexed":false},{"type":"bytes32","name":"dataHash","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"RelayerAdded","inputs":[{"type":"address","name":"relayer","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RelayerRemoved","inputs":[{"type":"address","name":"relayer","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RelayerThresholdChanged","inputs":[{"type":"uint256","name":"newThreshold","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_RELAYERS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"RELAYER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"_depositCounts","inputs":[{"type":"uint8","name":"","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"_domainID","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint40","name":"","internalType":"uint40"}],"name":"_expiry","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"","internalType":"uint128"}],"name":"_fee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"_hasVotedOnProposal","inputs":[{"type":"uint72","name":"destNonce","internalType":"uint72"},{"type":"bytes32","name":"dataHash","internalType":"bytes32"},{"type":"address","name":"relayer","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"_relayerThreshold","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"_resourceIDToHandlerAddress","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"_totalRelayers","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminAddRelayer","inputs":[{"type":"address","name":"relayerAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminChangeFee","inputs":[{"type":"uint256","name":"newFee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminChangeFeeReceiver","inputs":[{"type":"address","name":"newFeeReceiver","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminChangeRelayerThreshold","inputs":[{"type":"uint256","name":"newThreshold","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminPauseTransfers","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminRemoveRelayer","inputs":[{"type":"address","name":"relayerAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetBurnable","inputs":[{"type":"address","name":"handlerAddress","internalType":"address"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetDepositNonce","inputs":[{"type":"uint8","name":"domainID","internalType":"uint8"},{"type":"uint64","name":"nonce","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetForwarder","inputs":[{"type":"address","name":"forwarder","internalType":"address"},{"type":"bool","name":"valid","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetGenericResource","inputs":[{"type":"address","name":"handlerAddress","internalType":"address"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"address","name":"contractAddress","internalType":"address"},{"type":"bytes4","name":"depositFunctionSig","internalType":"bytes4"},{"type":"uint256","name":"depositFunctionDepositerOffset","internalType":"uint256"},{"type":"bytes4","name":"executeFunctionSig","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetResource","inputs":[{"type":"address","name":"handlerAddress","internalType":"address"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUnpauseTransfers","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminWithdraw","inputs":[{"type":"address","name":"handlerAddress","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelProposal","inputs":[{"type":"uint8","name":"domainID","internalType":"uint8"},{"type":"uint64","name":"depositNonce","internalType":"uint64"},{"type":"bytes32","name":"dataHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"deposit","inputs":[{"type":"uint8","name":"destinationDomainID","internalType":"uint8"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"depositCoin","inputs":[{"type":"uint8","name":"destinationDomainID","internalType":"uint8"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeProposal","inputs":[{"type":"uint8","name":"domainID","internalType":"uint8"},{"type":"uint64","name":"depositNonce","internalType":"uint64"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"bool","name":"revertOnFail","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Bridge.Proposal","components":[{"type":"uint8","name":"_status","internalType":"enum Bridge.ProposalStatus"},{"type":"uint200","name":"_yesVotes","internalType":"uint200"},{"type":"uint8","name":"_yesVotesTotal","internalType":"uint8"},{"type":"uint40","name":"_proposedBlock","internalType":"uint40"}]}],"name":"getProposal","inputs":[{"type":"uint8","name":"originDomainID","internalType":"uint8"},{"type":"uint64","name":"depositNonce","internalType":"uint64"},{"type":"bytes32","name":"dataHash","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getRoleMember","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRoleMemberCount","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRoleMemberIndex","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRelayer","inputs":[{"type":"address","name":"relayer","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isValidForwarder","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceAdmin","inputs":[{"type":"address","name":"newAdmin","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferFunds","inputs":[{"type":"address[]","name":"addrs","internalType":"address payable[]"},{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"voteProposal","inputs":[{"type":"uint8","name":"domainID","internalType":"uint8"},{"type":"uint64","name":"depositNonce","internalType":"uint64"},{"type":"bytes32","name":"resourceID","internalType":"bytes32"},{"type":"bytes","name":"data","internalType":"bytes"}]}]
Deployed ByteCode
0x60806040526004361061025c5760003560e01c806391d1485411610144578063c5ec8970116100b6578063d15ef64e1161007a578063d15ef64e14610798578063d547741f146107b8578063d7a9cd79146107d8578063edc20c3c146107f7578063f8c39e4414610817578063ffaac0eb1461084757600080fd5b8063c5ec8970146106dd578063ca15c87314610718578063cb10f21514610738578063cdb0f73a14610758578063d0673e121461077857600080fd5b8063a217fddf11610108578063a217fddf1461060a578063a9cf69fa1461061f578063b3ecc9a71461064c578063bd2a18201461065f578063c0331b3e1461067f578063c5b37c221461069f57600080fd5b806391d1485414610567578063926d7d7f146105875780639d82dd63146105a95780639dd694f4146105c95780639debb3bd146105f557600080fd5b8063541d5548116101dd578063802aabe8116101a1578063802aabe81461048f57806380ae1c28146104a457806384db809f146104b95780638c0c2631146105075780639010d07c1461052757806391c404ac1461054757600080fd5b8063541d5548146103e75780635a1ad87c146104175780635c975abb146104375780635e1fab0f1461044f5780637febe63f1461046f57600080fd5b806336568abe1161022457806336568abe146103195780634603ae38146103395780634b0b919d146103595780634e056005146103a75780634e0df3f6146103c757600080fd5b806305e2ca171461026157806317f03ce514610276578063206a98fd14610296578063248a9ca3146102b65780632f2ff15d146102f9575b600080fd5b61027461026f36600461294f565b61085c565b005b34801561028257600080fd5b506102746102913660046129bf565b610ab8565b3480156102a257600080fd5b506102746102b1366004612a0b565b610d4b565b3480156102c257600080fd5b506102e66102d1366004612a87565b60009081526001602052604090206002015490565b6040519081526020015b60405180910390f35b34801561030557600080fd5b50610274610314366004612ab5565b610fd3565b34801561032557600080fd5b50610274610334366004612ab5565b611063565b34801561034557600080fd5b50610274610354366004612b29565b6110ed565b34801561036557600080fd5b5061038f610374366004612b88565b6004602052600090815260409020546001600160401b031681565b6040516001600160401b0390911681526020016102f0565b3480156103b357600080fd5b506102746103c2366004612a87565b611191565b3480156103d357600080fd5b506102e66103e2366004612ab5565b6111f4565b3480156103f357600080fd5b50610407610402366004612ba3565b611220565b60405190151581526020016102f0565b34801561042357600080fd5b50610274610432366004612bd8565b61123a565b34801561044357600080fd5b5060005460ff16610407565b34801561045b57600080fd5b5061027461046a366004612ba3565b6112f0565b34801561047b57600080fd5b5061040761048a366004612c36565b61137b565b34801561049b57600080fd5b506102e6611421565b3480156104b057600080fd5b5061027461143f565b3480156104c557600080fd5b506104ef6104d4366004612a87565b6005602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102f0565b34801561051357600080fd5b50610274610522366004612c86565b611459565b34801561053357600080fd5b506104ef610542366004612cb4565b6114c5565b34801561055357600080fd5b50610274610562366004612a87565b6114e4565b34801561057357600080fd5b50610407610582366004612ab5565b61157e565b34801561059357600080fd5b506102e660008051602061321183398151915281565b3480156105b557600080fd5b506102746105c4366004612ba3565b611596565b3480156105d557600080fd5b506002546105e39060ff1681565b60405160ff90911681526020016102f0565b34801561060157600080fd5b506102e660c881565b34801561061657600080fd5b506102e6600081565b34801561062b57600080fd5b5061063f61063a3660046129bf565b61164b565b6040516102f09190612d0e565b61027461065a36600461294f565b611719565b34801561066b57600080fd5b5061027461067a366004612dc4565b61192c565b34801561068b57600080fd5b5061027461069a366004612e56565b611962565b3480156106ab57600080fd5b506002546106c5906201000090046001600160801b031681565b6040516001600160801b0390911681526020016102f0565b3480156106e957600080fd5b5060035461070290600160a01b900464ffffffffff1681565b60405164ffffffffff90911681526020016102f0565b34801561072457600080fd5b506102e6610733366004612a87565b611e7a565b34801561074457600080fd5b50610274610753366004612ec4565b611e91565b34801561076457600080fd5b50610274610773366004612ba3565b611f25565b34801561078457600080fd5b50610274610793366004612ba3565b61202b565b3480156107a457600080fd5b506102746107b3366004612ee4565b612055565b3480156107c457600080fd5b506102746107d3366004612ab5565b612088565b3480156107e457600080fd5b506002546105e390610100900460ff1681565b34801561080357600080fd5b50610274610812366004612f19565b61210b565b34801561082357600080fd5b50610407610832366004612ba3565b60066020526000908152604090205460ff1681565b34801561085357600080fd5b506102746121c4565b6108646121dc565b6002546201000090046001600160801b031634146108c25760405162461bcd60e51b8152602060048201526016602482015275125b98dbdc9c9958dd08199959481cdd5c1c1b1a595960521b60448201526064015b60405180910390fd5b6003546001600160a01b031615610923576003546002546040516001600160a01b03909216916001600160801b03620100009092049190911680156108fc02916000818181858888f19350505050158015610921573d6000803e3d6000fd5b505b6000838152600560205260409020546001600160a01b0316806109885760405162461bcd60e51b815260206004820181905260248201527f7265736f757263654944206e6f74206d617070656420746f2068616e646c657260448201526064016108b9565b60ff85166000908152600460205260408120805482906109b0906001600160401b0316612f59565b91906101000a8154816001600160401b0302191690836001600160401b031602179055905060006109df612222565b60405163b07e54bb60e01b815290915083906000906001600160a01b0383169063b07e54bb90610a19908b9087908c908c90600401612fa8565b6000604051808303816000875af1158015610a38573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a609190810190613001565b9050826001600160a01b03167f17bc3181e17a9620a479c24e6c606e474ba84fc036877b768926872e8cd0e11f8a8a878b8b87604051610aa59695949392919061309a565b60405180910390a2505050505050505050565b610ac0612263565b60ff838116600884901b68ffffffffffffffff0016176000818152600760209081526040808320868452909152808220815160808101909252805493949293919290918391166004811115610b1757610b17612cd6565b6004811115610b2857610b28612cd6565b8152905461010081046001600160c81b03166020830152600160d01b810460ff166040830152600160d81b900464ffffffffff1660609091015280519091506001816004811115610b7b57610b7b612cd6565b1480610b9857506002816004811115610b9657610b96612cd6565b145b610be45760405162461bcd60e51b815260206004820152601c60248201527f50726f706f73616c2063616e6e6f742062652063616e63656c6c65640000000060448201526064016108b9565b600354606083015164ffffffffff600160a01b909204821691610c09914391166122e7565b64ffffffffff1611610c5d5760405162461bcd60e51b815260206004820181905260248201527f50726f706f73616c206e6f7420617420657870697279207468726573686f6c6460448201526064016108b9565b600480835268ffffffffffffffffff841660009081526007602090815260408083208884529091529020835181548593839160ff1916906001908490811115610ca857610ca8612cd6565b02179055506020820151815460408085015160609095015164ffffffffff16600160d81b026001600160d81b0360ff909616600160d01b0260ff60d01b196001600160c81b039095166101000294909416610100600160d81b0319909316929092179290921793909316929092179055516000805160206131f183398151915290610d3b908890889060049089906130eb565b60405180910390a1505050505050565b610d53612329565b610d5b6121dc565b60008281526005602090815260408083205490516001600160a01b039091169268ffffffffffffffff0060088a901b1660ff8b1617929091610da39185918a918a9101613120565b60408051601f19818403018152918152815160209283012068ffffffffffffffffff851660009081526007845282812082825290935291209091506002815460ff166004811115610df657610df6612cd6565b14610e435760405162461bcd60e51b815260206004820181905260248201527f50726f706f73616c206d7573742068617665205061737365642073746174757360448201526064016108b9565b805460ff19166003178155838515610ebc5760405163712467f960e11b81526001600160a01b0382169063e248cff290610e85908a908d908d9060040161314c565b600060405180830381600087803b158015610e9f57600080fd5b505af1158015610eb3573d6000803e3d6000fd5b50505050610f99565b60405163712467f960e11b81526001600160a01b0382169063e248cff290610eec908a908d908d9060040161314c565b600060405180830381600087803b158015610f0657600080fd5b505af1925050508015610f17575060015b610f99573d808015610f45576040519150601f19603f3d011682016040523d82523d6000602084013e610f4a565b606091505b50825460ff191660021783556040517fbd37c1f0d53bb2f33fe4c2104de272fcdeb4d2fef3acdbf1e4ddc3d6833ca37690610f86908390613166565b60405180910390a1505050505050610fcb565b6000805160206131f18339815191528b8b600386604051610fbd94939291906130eb565b60405180910390a150505050505b505050505050565b600082815260016020526040902060020154610ff190610582612222565b6110555760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60448201526e0818591b5a5b881d1bc819dc985b9d608a1b60648201526084016108b9565b61105f828261238f565b5050565b61106b612222565b6001600160a01b0316816001600160a01b0316146110e35760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016108b9565b61105f82826123f8565b6110f5612461565b60005b8381101561118a5784848281811061111257611112613179565b90506020020160208101906111279190612ba3565b6001600160a01b03166108fc84848481811061114557611145613179565b905060200201359081150290604051600060405180830381858888f19350505050158015611177573d6000803e3d6000fd5b50806111828161318f565b9150506110f8565b5050505050565b611199612461565b6111a2816124ba565b6002805460ff929092166101000261ff00199092169190911790556040518181527fa20d6b84cd798a24038be305eff8a45ca82ef54a2aa2082005d8e14c0a4746c8906020015b60405180910390a150565b60008281526001602081815260408084206001600160a01b038616855290920190529020545b92915050565b600061121a6000805160206132118339815191528361157e565b611242612461565b6000858152600560205260409081902080546001600160a01b0319166001600160a01b03898116918217909255915163de319d9960e01b81526004810188905290861660248201526001600160e01b03198086166044830152606482018590528316608482015287919063de319d999060a401600060405180830381600087803b1580156112cf57600080fd5b505af11580156112e3573d6000803e3d6000fd5b5050505050505050505050565b6112f8612461565b6000611302612222565b9050816001600160a01b0316816001600160a01b0316036113655760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f742072656e6f756e6365206f6e6573656c6600000000000000000060448201526064016108b9565b611370600083610fd3565b61105f600082611063565b68ffffffffffffffffff831660009081526007602090815260408083208584529091528082208151608081019092528054611419929190829060ff1660048111156113c8576113c8612cd6565b60048111156113d9576113d9612cd6565b8152905461010081046001600160c81b03166020830152600160d01b810460ff166040830152600160d81b900464ffffffffff1660609091015283612511565b949350505050565b600061143a600080516020613211833981519152611e7a565b905090565b611447612461565b611457611452612222565b612534565b565b611461612461565b6040516307b7ed9960e01b81526001600160a01b0382811660048301528391908216906307b7ed99906024015b600060405180830381600087803b1580156114a857600080fd5b505af11580156114bc573d6000803e3d6000fd5b50505050505050565b60008281526001602052604081206114dd9083612582565b9392505050565b6114ec612461565b6002546201000090046001600160801b031681900361154d5760405162461bcd60e51b815260206004820152601f60248201527f43757272656e742066656520697320657175616c20746f206e6577206665650060448201526064016108b9565b6115568161258e565b6002806101000a8154816001600160801b0302191690836001600160801b0316021790555050565b60008281526001602052604081206114dd90836125e3565b6115ae6000805160206132118339815191528261157e565b6115fa5760405162461bcd60e51b815260206004820152601f60248201527f6164647220646f65736e277420686176652072656c6179657220726f6c65210060448201526064016108b9565b61161260008051602061321183398151915282612088565b6040516001600160a01b03821681527f10e1f7ce9fd7d1b90a66d13a2ab3cb8dd7f29f3f8d520b143b063ccfbab6906b906020016111e9565b60408051608081018252600080825260208201819052918101829052606081019190915260ff848116600885901b68ffffffffffffffff001617600081815260076020908152604080832087845290915290819020815160808101909252805492939192909183911660048111156116c5576116c5612cd6565b60048111156116d6576116d6612cd6565b8152905461010081046001600160c81b03166020830152600160d01b810460ff166040830152600160d81b900464ffffffffff1660609091015295945050505050565b6117216121dc565b6002546201000090046001600160801b03163410156117725760405162461bcd60e51b815260206004820152600d60248201526c43616e2774207061792066656560981b60448201526064016108b9565b6003546001600160a01b0316156117d3576003546002546040516001600160a01b03909216916001600160801b03620100009092049190911680156108fc02916000818181858888f193505050501580156117d1573d6000803e3d6000fd5b505b6000838152600560205260409020546001600160a01b0316806118385760405162461bcd60e51b815260206004820181905260248201527f7265736f757263654944206e6f74206d617070656420746f2068616e646c657260448201526064016108b9565b60ff8516600090815260046020526040812080548290611860906001600160401b0316612f59565b91906101000a8154816001600160401b0302191690836001600160401b0316021790559050600061188f612222565b60025490915083906000906001600160a01b0383169063026be9e3906118c4906201000090046001600160801b0316346131a8565b8a8a8a6040518563ffffffff1660e01b81526004016118e59392919061314c565b60006040518083038185885af1158015611903573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052610a609190810190613001565b611934612461565b60405163025a3c9960e21b815282906001600160a01b03821690630968f2649061148e908590600401613166565b61196a612329565b6119726121dc565b60008381526005602090815260408083205490516001600160a01b039091169268ffffffffffffffff00600889901b1660ff8a16179290916119ba9185918891889101613120565b60408051601f19818403018152828252805160209182012068ffffffffffffffffff861660009081526007835283812082825290925282822060808501909352825490945090929190829060ff166004811115611a1957611a19612cd6565b6004811115611a2a57611a2a612cd6565b8152905461010081046001600160c81b0316602080840191909152600160d01b820460ff16604080850191909152600160d81b90920464ffffffffff1660609093019290925260008a815260059092529020549091506001600160a01b0316611ad55760405162461bcd60e51b815260206004820152601960248201527f6e6f2068616e646c657220666f72207265736f7572636549440000000000000060448201526064016108b9565b600281516004811115611aea57611aea612cd6565b03611b0757611afe898988888b6001610d4b565b5050505061118a565b6000611b11612222565b9050600182600001516004811115611b2b57611b2b612cd6565b1115611b855760405162461bcd60e51b815260206004820152602360248201527f70726f706f73616c20616c72656164792065786563757465642f63616e63656c6044820152621b195960ea1b60648201526084016108b9565b611b8f8282612511565b15611bd45760405162461bcd60e51b81526020600482015260156024820152741c995b185e595c88185b1c9958591e481d9bdd1959605a1b60448201526064016108b9565b600082516004811115611be957611be9612cd6565b03611c48576040805160808101825260018082526000602083018190528284015264ffffffffff4316606083015291519093506000805160206131f183398151915291611c3b918d918d9188906130eb565b60405180910390a1611caa565b600354606083015164ffffffffff600160a01b909204821691611c6d914391166122e7565b64ffffffffff161115611caa5760048083526040516000805160206131f183398151915291611ca1918d918d9188906130eb565b60405180910390a15b600482516004811115611cbf57611cbf612cd6565b14611d8f57611ce4611cd082612605565b83602001516001600160c81b031617612633565b6001600160c81b0316602083015260408201805190611d02826131bb565b60ff1690525081516040517f25f8daaa4635a7729927ba3f5b3d59cc3320aca7c32c9db4e7ca7b957434364091611d3e918d918d9188906130eb565b60405180910390a1600254604083015160ff6101009092048216911610611d8f5760028083526040516000805160206131f183398151915291611d86918d918d9188906130eb565b60405180910390a15b68ffffffffffffffffff8416600090815260076020908152604080832086845290915290208251815484929190829060ff19166001836004811115611dd657611dd6612cd6565b021790555060208201518154604084015160609094015164ffffffffff16600160d81b026001600160d81b0360ff909516600160d01b0260ff60d01b196001600160c81b039094166101000293909316610100600160d81b0319909216919091179190911792909216919091179055600282516004811115611e5a57611e5a612cd6565b03611e6e57611e6e8a8a89898c6000610d4b565b50505050505050505050565b600081815260016020526040812061121a90612688565b611e99612461565b6000828152600560205260409081902080546001600160a01b0319166001600160a01b038681169182179092559151635c7d1b9b60e11b815260048101859052908316602482015284919063b8fa373690604401600060405180830381600087803b158015611f0757600080fd5b505af1158015611f1b573d6000803e3d6000fd5b5050505050505050565b611f3d6000805160206132118339815191528261157e565b15611f8a5760405162461bcd60e51b815260206004820152601e60248201527f6164647220616c7265616479206861732072656c6179657220726f6c6521000060448201526064016108b9565b60c8611f94611421565b10611fda5760405162461bcd60e51b81526020600482015260166024820152751c995b185e595c9cc81b1a5b5a5d081c995858da195960521b60448201526064016108b9565b611ff260008051602061321183398151915282610fd3565b6040516001600160a01b03821681527f03580ee9f53a62b7cb409a2cb56f9be87747dd15017afc5cef6eef321e4fb2c5906020016111e9565b612033612461565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b61205d612461565b6001600160a01b03919091166000908152600660205260409020805460ff1916911515919091179055565b6000828152600160205260409020600201546120a690610582612222565b6110e35760405162461bcd60e51b815260206004820152603060248201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60448201526f2061646d696e20746f207265766f6b6560801b60648201526084016108b9565b612113612461565b60ff82166000908152600460205260409020546001600160401b03908116908216116121905760405162461bcd60e51b815260206004820152602660248201527f446f6573206e6f7420616c6c6f772064656372656d656e7473206f6620746865604482015265206e6f6e636560d01b60648201526084016108b9565b60ff919091166000908152600460205260409020805467ffffffffffffffff19166001600160401b03909216919091179055565b6121cc612461565b6114576121d7612222565b612692565b60005460ff16156114575760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016108b9565b6000336014361080159061224e57506001600160a01b03811660009081526006602052604090205460ff165b1561225e575060131936013560601c5b919050565b600061226d612222565b905061227a60008261157e565b8061229857506122986000805160206132118339815191528261157e565b6122e45760405162461bcd60e51b815260206004820152601e60248201527f73656e646572206973206e6f742072656c61796572206f722061646d696e000060448201526064016108b9565b50565b60006114dd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506126dd565b612343600080516020613211833981519152610582612222565b6114575760405162461bcd60e51b815260206004820181905260248201527f73656e64657220646f65736e277420686176652072656c6179657220726f6c6560448201526064016108b9565b60008281526001602052604090206123a79082612717565b1561105f576123b4612222565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152600160205260409020612410908261272c565b1561105f5761241d612222565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b61246e6000610582612222565b6114575760405162461bcd60e51b815260206004820152601e60248201527f73656e64657220646f65736e277420686176652061646d696e20726f6c65000060448201526064016108b9565b6000610100821061250d5760405162461bcd60e51b815260206004820152601c60248201527f76616c756520646f6573206e6f742066697420696e203820626974730000000060448201526064016108b9565b5090565b60008083602001516001600160c81b031661252b84612605565b16119392505050565b61253c6121dc565b6000805460ff191660011790556040516001600160a01b03821681527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016111e9565b60006114dd8383612741565b6000600160801b821061250d5760405162461bcd60e51b815260206004820152601e60248201527f76616c756520646f6573206e6f742066697420696e203132382062697473000060448201526064016108b9565b6001600160a01b038116600090815260018301602052604081205415156114dd565b6000612629612622600080516020613211833981519152846111f4565b60016122e7565b6001901b92915050565b6000600160c81b821061250d5760405162461bcd60e51b815260206004820152601e60248201527f76616c756520646f6573206e6f742066697420696e203230302062697473000060448201526064016108b9565b600061121a825490565b61269a61276b565b6000805460ff191690556040516001600160a01b03821681527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020016111e9565b600081848411156127015760405162461bcd60e51b81526004016108b99190613166565b50600061270e84866131a8565b95945050505050565b60006114dd836001600160a01b0384166127b4565b60006114dd836001600160a01b038416612803565b600082600001828154811061275857612758613179565b9060005260206000200154905092915050565b60005460ff166114575760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016108b9565b60008181526001830160205260408120546127fb5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561121a565b50600061121a565b600081815260018301602052604081205480156128ec5760006128276001836131a8565b855490915060009061283b906001906131a8565b90508181146128a057600086600001828154811061285b5761285b613179565b906000526020600020015490508087600001848154811061287e5761287e613179565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806128b1576128b16131da565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061121a565b600091505061121a565b803560ff8116811461225e57600080fd5b60008083601f84011261291957600080fd5b5081356001600160401b0381111561293057600080fd5b60208301915083602082850101111561294857600080fd5b9250929050565b6000806000806060858703121561296557600080fd5b61296e856128f6565b93506020850135925060408501356001600160401b0381111561299057600080fd5b61299c87828801612907565b95989497509550505050565b80356001600160401b038116811461225e57600080fd5b6000806000606084860312156129d457600080fd5b6129dd846128f6565b92506129eb602085016129a8565b9150604084013590509250925092565b8035801515811461225e57600080fd5b60008060008060008060a08789031215612a2457600080fd5b612a2d876128f6565b9550612a3b602088016129a8565b945060408701356001600160401b03811115612a5657600080fd5b612a6289828a01612907565b90955093505060608701359150612a7b608088016129fb565b90509295509295509295565b600060208284031215612a9957600080fd5b5035919050565b6001600160a01b03811681146122e457600080fd5b60008060408385031215612ac857600080fd5b823591506020830135612ada81612aa0565b809150509250929050565b60008083601f840112612af757600080fd5b5081356001600160401b03811115612b0e57600080fd5b6020830191508360208260051b850101111561294857600080fd5b60008060008060408587031215612b3f57600080fd5b84356001600160401b0380821115612b5657600080fd5b612b6288838901612ae5565b90965094506020870135915080821115612b7b57600080fd5b5061299c87828801612ae5565b600060208284031215612b9a57600080fd5b6114dd826128f6565b600060208284031215612bb557600080fd5b81356114dd81612aa0565b80356001600160e01b03198116811461225e57600080fd5b60008060008060008060c08789031215612bf157600080fd5b8635612bfc81612aa0565b9550602087013594506040870135612c1381612aa0565b9350612c2160608801612bc0565b925060808701359150612a7b60a08801612bc0565b600080600060608486031215612c4b57600080fd5b833568ffffffffffffffffff81168114612c6457600080fd5b9250602084013591506040840135612c7b81612aa0565b809150509250925092565b60008060408385031215612c9957600080fd5b8235612ca481612aa0565b91506020830135612ada81612aa0565b60008060408385031215612cc757600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b60058110612d0a57634e487b7160e01b600052602160045260246000fd5b9052565b6000608082019050612d21828451612cec565b60018060c81b03602084015116602083015260ff604084015116604083015264ffffffffff606084015116606083015292915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612d9557612d95612d57565b604052919050565b60006001600160401b03821115612db657612db6612d57565b50601f01601f191660200190565b60008060408385031215612dd757600080fd5b8235612de281612aa0565b915060208301356001600160401b03811115612dfd57600080fd5b8301601f81018513612e0e57600080fd5b8035612e21612e1c82612d9d565b612d6d565b818152866020838501011115612e3657600080fd5b816020840160208301376000602083830101528093505050509250929050565b600080600080600060808688031215612e6e57600080fd5b612e77866128f6565b9450612e85602087016129a8565b93506040860135925060608601356001600160401b03811115612ea757600080fd5b612eb388828901612907565b969995985093965092949392505050565b600080600060608486031215612ed957600080fd5b8335612c6481612aa0565b60008060408385031215612ef757600080fd5b8235612f0281612aa0565b9150612f10602084016129fb565b90509250929050565b60008060408385031215612f2c57600080fd5b612f35836128f6565b9150612f10602084016129a8565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b03808316818103612f7557612f75612f43565b6001019392505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526001600160a01b0384166020820152606060408201819052600090612fd39083018486612f7f565b9695505050505050565b60005b83811015612ff8578181015183820152602001612fe0565b50506000910152565b60006020828403121561301357600080fd5b81516001600160401b0381111561302957600080fd5b8201601f8101841361303a57600080fd5b8051613048612e1c82612d9d565b81815285602083850101111561305d57600080fd5b61270e826020830160208601612fdd565b60008151808452613086816020860160208601612fdd565b601f01601f19169290920160200192915050565b60ff871681528560208201526001600160401b038516604082015260a0606082015260006130cc60a083018587612f7f565b82810360808401526130de818561306e565b9998505050505050505050565b60ff851681526001600160401b0384166020820152608081016131116040830185612cec565b82606083015295945050505050565b6bffffffffffffffffffffffff198460601b168152818360148301376000910160140190815292915050565b83815260406020820152600061270e604083018486612f7f565b6020815260006114dd602083018461306e565b634e487b7160e01b600052603260045260246000fd5b6000600182016131a1576131a1612f43565b5060010190565b8181038181111561121a5761121a612f43565b600060ff821660ff81036131d1576131d1612f43565b60010192915050565b634e487b7160e01b600052603160045260246000fdfe968626a768e76ba1363efe44e322a6c4900c5f084e0b45f35e294dfddaa9e0d5e2b7fb3b832174769106daebcfd6d1970523240dda11281102db9363b83b0dc4a2646970667358221220d9189754d5a05764703a72bf67b46170c0a01f9413b0ddb0e24a9d73cb527f8364736f6c63430008130033