Source Code
Overview
GLMR Balance
GLMR Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 3899410 | 1017 days ago | Contract Creation | 0 GLMR |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
RangoMultichainFacet
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
import "../../interfaces/IRangoMultichain.sol";
import "../../interfaces/IRango.sol";
import "../../interfaces/IMultichainRouter.sol";
import "../../utils/ReentrancyGuard.sol";
import "../../utils/LibTransform.sol";
import "../../libraries/LibSwapper.sol";
import "../../libraries/LibDiamond.sol";
import "../../libraries/LibInterchain.sol";
import "../../interfaces/Interchain.sol";
/// @title The root contract that handles Rango's interaction with MultichainOrg bridge
/// @author George
contract RangoMultichainFacet is IRango, ReentrancyGuard, IRangoMultichain {
/// Storage ///
/// @dev keccak256("exchange.rango.facets.multichain")
bytes32 internal constant MULTICHAIN_NAMESPACE = hex"13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c";
struct MultichainStorage {
/// @notice List of whitelisted MultichainOrg routers in the current chain
mapping(address => bool) multichainRouters;
}
/// @notice Notifies that some new router addresses are whitelisted
/// @param _addresses The newly whitelisted addresses
event MultichainRoutersAdded(address[] _addresses);
/// @notice Notifies that some router addresses are blacklisted
/// @param _addresses The addresses that are removed
event MultichainRoutersRemoved(address[] _addresses);
/// @notice The constructor of this contract
/// @param _routers The address of whitelist contracts for bridge routers
function initMultichain(address[] calldata _routers) external {
LibDiamond.enforceIsContractOwner();
addMultichainRoutersInternal(_routers);
}
/// @notice Removes a list of routers from the whitelisted addresses
/// @param _routers The list of addresses that should be deprecated
function removeMultichainRouters(address[] calldata _routers) external {
LibDiamond.enforceIsContractOwner();
MultichainStorage storage s = getMultichainStorage();
for (uint i = 0; i < _routers.length; i++) {
delete s.multichainRouters[_routers[i]];
}
emit MultichainRoutersRemoved(_routers);
}
/// Bridge functions
/// @notice Executes a DEX (arbitrary) call + a MultichainOrg bridge call
/// @param request The general swap request containing from/to token and fee/affiliate rewards
/// @param calls The list of DEX calls, if this list is empty, it means that there is no DEX call and we are only bridging
/// @param bridgeRequest required data for the bridging step, including the destination chain and recipient wallet address
function multichainSwapAndBridge(
LibSwapper.SwapRequest memory request,
LibSwapper.Call[] calldata calls,
IRangoMultichain.MultichainBridgeRequest memory bridgeRequest
) external payable nonReentrant {
uint out = LibSwapper.onChainSwapsPreBridge(request, calls, 0);
if (bridgeRequest.bridgeType == MultichainBridgeType.TRANSFER) {
doMultichainBridge(bridgeRequest, request.toToken, out);
// event emission
emit RangoBridgeInitiated(
request.requestId,
request.toToken,
out,
bridgeRequest.receiverAddress,
bridgeRequest.receiverChainID,
false,
false,
uint8(BridgeType.Multichain),
request.dAppTag
);
} else {
Interchain.RangoInterChainMessage memory imMessage = abi.decode((bridgeRequest.imMessage), (Interchain.RangoInterChainMessage));
doMultichainBridgeAndAnyCall(bridgeRequest, request.toToken, out);
// event emission
emit RangoBridgeInitiated(
request.requestId,
request.toToken,
out,
bridgeRequest.receiverAddress,
bridgeRequest.receiverChainID,
true,
imMessage.actionType != Interchain.ActionType.NO_ACTION,
uint8(BridgeType.Multichain),
request.dAppTag
);
}
}
/// @notice Executes a bridge through Multichain
/// @param request The general swap request containing from/to token and fee/affiliate rewards
/// @param bridgeRequest required data for the bridging step, including the destination chain and recipient wallet address
function multichainBridge(
IRangoMultichain.MultichainBridgeRequest memory request,
RangoBridgeRequest memory bridgeRequest
) external payable nonReentrant {
uint amount = bridgeRequest.amount;
address token = bridgeRequest.token;
uint amountWithFee = amount + LibSwapper.sumFees(bridgeRequest);
if (token != LibSwapper.ETH) {
SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amountWithFee);
} else {
require(msg.value >= amountWithFee);
}
LibSwapper.collectFees(bridgeRequest);
if (request.bridgeType == MultichainBridgeType.TRANSFER) {
doMultichainBridge(request, token, amount);
// event emission
emit RangoBridgeInitiated(
bridgeRequest.requestId,
token,
amount,
request.receiverAddress,
request.receiverChainID,
false,
false,
uint8(BridgeType.Multichain),
bridgeRequest.dAppTag
);
} else {
Interchain.RangoInterChainMessage memory imMessage = abi.decode((request.imMessage), (Interchain.RangoInterChainMessage));
doMultichainBridgeAndAnyCall(request, token, amount);
// event emission
emit RangoBridgeInitiated(
bridgeRequest.requestId,
token,
amount,
request.receiverAddress,
request.receiverChainID,
true,
imMessage.actionType != Interchain.ActionType.NO_ACTION,
uint8(BridgeType.Multichain),
bridgeRequest.dAppTag
);
}
}
/// @notice Executes a MultichainOrg bridge call
/// @param fromToken The address of bridging token
/// @param inputAmount The amount of the token to be bridged
/// @param request The other required field by MultichainOrg bridge
function doMultichainBridge(
MultichainBridgeRequest memory request,
address fromToken,
uint inputAmount
) internal {
if (request.actionType == MultichainActionType.TOKEN_SWAP_OUT) {
CustomMultichainToken(fromToken).Swapout(inputAmount, request.receiverAddress);
return;
} else if (request.actionType == MultichainActionType.TOKEN_TRANSFER) {
CustomMultichainToken(fromToken).transfer(request.receiverAddress, inputAmount);
return;
}
address routerAddr = request.multichainRouter;
MultichainStorage storage s = getMultichainStorage();
require(s.multichainRouters[routerAddr], 'Requested router address not whitelisted');
if (request.actionType != MultichainActionType.OUT_NATIVE) {
LibSwapper.approveMax(fromToken, routerAddr, inputAmount);
} else {
require(fromToken == LibSwapper.ETH, 'invalid token');
}
IMultichainRouter router = IMultichainRouter(routerAddr);
if (request.actionType == MultichainActionType.OUT) {
require(request.underlyingToken == fromToken);
router.anySwapOut(request.underlyingToken, request.receiverAddress, inputAmount, request.receiverChainID);
} else if (request.actionType == MultichainActionType.OUT_UNDERLYING) {
require(IUnderlying(request.underlyingToken).underlying() == fromToken);
router.anySwapOutUnderlying(request.underlyingToken, request.receiverAddress, inputAmount, request.receiverChainID);
} else if (request.actionType == MultichainActionType.OUT_NATIVE) {
router.anySwapOutNative{value : inputAmount}(request.underlyingToken, request.receiverAddress, request.receiverChainID);
} else {
revert();
}
}
/// @notice Executes a MultichainOrg token bridge and call
function doMultichainBridgeAndAnyCall(
MultichainBridgeRequest memory request,
address fromToken,
uint inputAmount
) internal {
MultichainStorage storage s = getMultichainStorage();
require(s.multichainRouters[request.multichainRouter], 'router not allowed');
if (request.actionType != MultichainActionType.OUT_NATIVE) {
LibSwapper.approveMax(fromToken, request.multichainRouter, inputAmount);
} else {
require(fromToken == LibSwapper.ETH, 'invalid token');
}
IMultichainV7Router router = IMultichainV7Router(request.multichainRouter);
if (request.actionType == MultichainActionType.OUT) {
router.anySwapOutAndCall(
request.underlyingToken,
LibTransform.addressToString(request.receiverAddress),
inputAmount,
request.receiverChainID,
request.anycallTargetContractOnDestChain,
request.imMessage
);
} else if (request.actionType == MultichainActionType.OUT_UNDERLYING) {
router.anySwapOutUnderlyingAndCall(
request.underlyingToken,
LibTransform.addressToString(request.receiverAddress),
inputAmount,
request.receiverChainID,
request.anycallTargetContractOnDestChain,
request.imMessage
);
} else if (request.actionType == MultichainActionType.OUT_NATIVE) {
router.anySwapOutNativeAndCall{value : inputAmount}(
request.underlyingToken,
LibTransform.addressToString(request.receiverAddress),
request.receiverChainID,
request.anycallTargetContractOnDestChain,
request.imMessage
);
} else {
revert();
}
}
function addMultichainRoutersInternal(address[] calldata _addresses) private {
MultichainStorage storage s = getMultichainStorage();
address tmpAddr;
for (uint i = 0; i < _addresses.length; i++) {
tmpAddr = _addresses[i];
require(tmpAddr != address(0), "Invalid Router Address");
s.multichainRouters[tmpAddr] = true;
}
emit MultichainRoutersAdded(_addresses);
}
/// @dev fetch local storage
function getMultichainStorage() private pure returns (MultichainStorage storage s) {
bytes32 namespace = MULTICHAIN_NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly {
s.slot := namespace
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason 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 {
// 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);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
interface IDiamondCut {
enum FacetCutAction {
Add,
Replace,
Remove
}
// Add=0, Replace=1, Remove=2
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external;
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
/// @title The root contract that handles Rango's interaction with MultichainOrg bridge
/// @author George
interface IMultichainRouter {
function anySwapOutUnderlying(address token, address to, uint amount, uint toChainID) external;
function anySwapOutNative(address token, address to, uint toChainID) external payable;
function anySwapOut(address token, address to, uint amount, uint toChainID) external;
}
interface IMultichainV7Router {
// Swaps `amount` `token` from this chain to `toChainID` chain and call anycall proxy with `data`
// `to` is the fallback receive address when exec failed on the `destination` chain
function anySwapOutAndCall(
address token,
string calldata to,
uint256 amount,
uint256 toChainID,
string calldata anycallProxy,
bytes calldata data
) external;
// Swaps `amount` `token` from this chain to `toChainID` chain and call anycall proxy with `data`
// `to` is the fallback receive address when exec failed on the `destination` chain
function anySwapOutUnderlyingAndCall(
address token,
string calldata to,
uint256 amount,
uint256 toChainID,
string calldata anycallProxy,
bytes calldata data
) external;
// Swaps `msg.value` `Native` from this chain to `toChainID` chain and call anycall proxy with `data`
// `to` is the fallback receive address when exec failed on the `destination` chain
function anySwapOutNativeAndCall(
address token,
string calldata to,
uint256 toChainID,
string calldata anycallProxy,
bytes calldata data
) external payable;
}
/// IAnycallProxy interface of the anycall proxy
interface IAnycallProxy {
/// @notice Executor function (called in destination chain)
/// Note that the onlyAllowedExecutors limits the caller of this function to be only allowed executors.
/// @param token The token that is received on destination
/// @param receiver The address that should receive tokens in case of failure as a fallback.
/// @param amount Token amount
/// @param data The data sent along with the token
function exec(
address token,
address receiver,
uint256 amount,
bytes calldata data
) external payable returns (bool success, bytes memory result);
}
interface CustomMultichainToken {
function transfer(address toAddress, uint256 amount) external;
function Swapout(uint256 amount,address destination) external;
}
interface IUnderlying {
function underlying() external view returns (address);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
/// @title An interface to interchain message types
/// @author Uchiha Sasuke
interface Interchain {
enum ActionType { NO_ACTION, UNI_V2, UNI_V3, CALL }
enum CallSubActionType { WRAP, UNWRAP, NO_ACTION }
struct RangoInterChainMessage {
address requestId;
uint64 dstChainId;
// @dev bridgeRealOutput is only used to disambiguate receipt of WETH and ETH and SHOULD NOT be used anywhere else!
address bridgeRealOutput;
address toToken;
address originalSender;
address recipient;
ActionType actionType;
bytes action;
CallSubActionType postAction;
uint16 dAppTag;
// Extra message
bytes dAppMessage;
address dAppSourceContract;
address dAppDestContract;
}
struct UniswapV2Action {
address dexAddress;
uint amountOutMin;
address[] path;
uint deadline;
}
struct UniswapV3ActionExactInputSingleParams {
address dexAddress;
address tokenIn;
address tokenOut;
uint24 fee;
uint256 deadline;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice The requested call data which is computed off-chain and passed to the contract
/// @param target The dex contract address that should be called
/// @param callData The required data field that should be give to the dex contract to perform swap
struct CallAction {
address tokenIn;
address spender;
CallSubActionType preAction;
address payable target;
bytes callData;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
interface IRango {
struct RangoBridgeRequest {
address requestId;
address token;
uint amount;
uint platformFee;
uint affiliateFee;
address payable affiliatorAddress;
uint destinationExecutorFee;
uint16 dAppTag;
}
enum BridgeType {Across, CBridge, Hop, Hyphen, Multichain, Stargate, Synapse, Thorchain, Symbiosis, Axelar, Voyager, Poly, OptimismBridge, ArbitrumBridge, Wormhole, AllBridge}
/// @notice Status of cross-chain swap
/// @param Succeeded The whole process is success and end-user received the desired token in the destination
/// @param RefundInSource Bridge was out of liquidity and middle asset (ex: USDC) is returned to user on source chain
/// @param RefundInDestination Our handler on dest chain this.executeMessageWithTransfer failed and we send middle asset (ex: USDC) to user on destination chain
/// @param SwapFailedInDestination Everything was ok, but the final DEX on destination failed (ex: Market price change and slippage)
enum CrossChainOperationStatus {
Succeeded,
RefundInSource,
RefundInDestination,
SwapFailedInDestination
}
event RangoBridgeInitiated(
address indexed requestId,
address bridgeToken,
uint256 bridgeAmount,
address receiver,
uint destinationChainId,
bool hasInterchainMessage,
bool hasDestinationSwap,
uint8 indexed bridgeId,
uint16 indexed dAppTag
);
event RangoBridgeCompleted(
address indexed requestId,
address indexed token,
address indexed originalSender,
address receiver,
uint amount,
CrossChainOperationStatus status,
uint16 dAppTag
);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
interface IRangoMessageReceiver {
enum ProcessStatus { SUCCESS, REFUND_IN_SOURCE, REFUND_IN_DESTINATION }
function handleRangoMessage(
address token,
uint amount,
ProcessStatus status,
bytes memory message
) external;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
import "../libraries/LibSwapper.sol";
/// @title An interface to RangoMultichain.sol contract to improve type hinting
/// @author Uchiha Sasuke
interface IRangoMultichain {
enum MultichainActionType {OUT, OUT_UNDERLYING, OUT_NATIVE, TOKEN_SWAP_OUT, TOKEN_TRANSFER}
enum MultichainBridgeType {TRANSFER, TRANSFER_WITH_MESSAGE}
/// @notice The request object for MultichainOrg bridge call
/// @param bridge The type of bridge action which indicates whether there is a message or just a simple transfer.
/// @param actionType The type of bridge action which indicates the name of the function of MultichainOrg contract to be called
/// @param underlyingToken For actionType = OUT_UNDERLYING, it's the address of the underlying token
/// @param multichainRouter Address of MultichainOrg contract on the current chain
/// @param receiverAddress The address of end-user on the destination
/// @param receiverChainID The network id of destination chain
struct MultichainBridgeRequest {
IRangoMultichain.MultichainBridgeType bridgeType;
IRangoMultichain.MultichainActionType actionType;
address underlyingToken;
address multichainRouter;
address receiverAddress;
uint receiverChainID;
string anycallTargetContractOnDestChain;
bytes imMessage;
}
function multichainSwapAndBridge(
LibSwapper.SwapRequest memory request,
LibSwapper.Call[] calldata calls,
IRangoMultichain.MultichainBridgeRequest memory bridgeRequest
) external payable;
function multichainBridge(
IRangoMultichain.MultichainBridgeRequest memory request,
IRango.RangoBridgeRequest memory bridgeRequest
) external payable;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
import "./IStargateRouter.sol";
import "./Interchain.sol";
import "./IRango.sol";
import "../libraries/LibSwapper.sol";
/// @title An interface to interact with RangoStargateFacet
/// @author Uchiha Sasuke
interface IRangoStargate {
enum StargateBridgeType {TRANSFER, TRANSFER_WITH_MESSAGE}
struct StargateRequest {
StargateBridgeType bridgeType;
uint16 dstChainId;
uint256 srcPoolId;
uint256 dstPoolId;
address payable srcGasRefundAddress;
uint256 minAmountLD;
uint256 dstGasForCall;
uint256 dstNativeAmount;
bytes dstNativeAddr;
bytes to;
uint stgFee;
bytes imMessage;
}
function stargateSwapAndBridge(
LibSwapper.SwapRequest memory request,
LibSwapper.Call[] calldata calls,
IRangoStargate.StargateRequest memory stargateRequest
) external payable;
function stargateBridge(
IRangoStargate.StargateRequest memory stargateRequest,
IRango.RangoBridgeRequest memory bridgeRequest
) external payable;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
interface IStargateReceiver {
function sgReceive(
uint16 chainId,
bytes memory srcAddress,
uint256 nonce,
address token,
uint256 amountLD,
bytes memory payload
) payable external;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
interface IStargateRouter {
struct lzTxObj {
uint256 dstGasForCall;
uint256 dstNativeAmount;
bytes dstNativeAddr;
}
function swap(
uint16 _dstChainId,
uint256 _srcPoolId,
uint256 _dstPoolId,
address payable _refundAddress,
uint256 _amountLD,
uint256 _minAmountLD,
lzTxObj memory _lzTxParams,
bytes calldata _to,
bytes calldata _payload
) external payable;
function swapETH(
uint16 _dstChainId,
address payable _refundAddress,
bytes calldata _toAddress,
uint256 _amountLD,
uint256 _minAmountLD
) external payable;
function quoteLayerZeroFee(
uint16 _dstChainId,
uint8 _functionType,
bytes calldata _toAddress,
bytes calldata _transferAndCallPayload,
lzTxObj memory _lzTxParams
) external view returns (uint256, uint256);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.16;
/// @dev based on swap router of uniswap v2 https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02#swapexactethfortokens
interface IUniswapV2 {
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
// For pangolin and trader joe
function swapExactAVAXForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.16;
/// @dev based on IswapRouter of UniswapV3 https://docs.uniswap.org/protocol/reference/periphery/interfaces/ISwapRouter
interface IUniswapV3 {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.16;
interface IWETH {
function deposit() external payable;
function withdraw(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
/// Implementation of EIP-2535 Diamond Standard
/// https://eips.ethereum.org/EIPS/eip-2535
library LibDiamond {
/// @dev keccak256("diamond.standard.diamond.storage");
bytes32 internal constant DIAMOND_STORAGE_POSITION = hex"c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c";
// Diamond specific errors
error IncorrectFacetCutAction();
error NoSelectorsInFacet();
error FunctionAlreadyExists();
error FacetAddressIsZero();
error FacetAddressIsNotZero();
error FacetContainsNoCode();
error FunctionDoesNotExist();
error FunctionIsImmutable();
error InitZeroButCalldataNotEmpty();
error CalldataEmptyButInitNotZero();
error InitReverted();
// ----------------
struct FacetAddressAndPosition {
address facetAddress;
uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}
struct FacetFunctionSelectors {
bytes4[] functionSelectors;
uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
}
struct DiamondStorage {
// maps function selector to the facet address and
// the position of the selector in the facetFunctionSelectors.selectors array
mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
// maps facet addresses to function selectors
mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
// facet addresses
address[] facetAddresses;
// Used to query if a contract implements an interface.
// Used to implement ERC-165.
mapping(bytes4 => bool) supportedInterfaces;
// owner of the contract
address contractOwner;
}
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
// solhint-disable-next-line no-inline-assembly
assembly {
ds.slot := position
}
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function setContractOwner(address _newOwner) internal {
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
function contractOwner() internal view returns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
function enforceIsContractOwner() internal view {
require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
}
event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
// Internal function version of diamondCut
function diamondCut(
IDiamondCut.FacetCut[] memory _diamondCut,
address _init,
bytes memory _calldata
) internal {
for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else if (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else {
revert IncorrectFacetCutAction();
}
unchecked {
++facetIndex;
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_facetAddress == address(0)) {
revert FacetAddressIsZero();
}
if (_functionSelectors.length == 0) {
revert NoSelectorsInFacet();
}
DiamondStorage storage ds = diamondStorage();
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (oldFacetAddress != address(0)) {
revert FunctionAlreadyExists();
}
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_functionSelectors.length == 0) {
revert NoSelectorsInFacet();
}
if (_facetAddress == address(0)) {
revert FacetAddressIsZero();
}
DiamondStorage storage ds = diamondStorage();
uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not exist
if (selectorPosition == 0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (oldFacetAddress == _facetAddress) {
revert FunctionAlreadyExists();
}
removeFunction(ds, oldFacetAddress, selector);
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
if (_functionSelectors.length == 0) {
revert NoSelectorsInFacet();
}
DiamondStorage storage ds = diamondStorage();
// if function does not exist then do nothing and return
if (_facetAddress != address(0)) {
revert FacetAddressIsNotZero();
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
removeFunction(ds, oldFacetAddress, selector);
unchecked {
++selectorIndex;
}
}
}
function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
enforceHasContractCode(_facetAddress);
ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
ds.facetAddresses.push(_facetAddress);
}
function addFunction(
DiamondStorage storage ds,
bytes4 _selector,
uint96 _selectorPosition,
address _facetAddress
) internal {
ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
}
function removeFunction(
DiamondStorage storage ds,
address _facetAddress,
bytes4 _selector
) internal {
if (_facetAddress == address(0)) {
revert FunctionDoesNotExist();
}
// an immutable function is a function defined directly in a diamond
if (_facetAddress == address(this)) {
revert FunctionIsImmutable();
}
// replace selector with last selector, then delete last selector
uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
// if not the same then replace _selector with lastSelector
if (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
}
// delete the last selector
ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
delete ds.selectorToFacetAndPosition[_selector];
// if no more selectors for facet address then delete the facet address
if (lastSelectorPosition == 0) {
// replace facet address with last facet address and delete last facet address
uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
if (facetAddressPosition != lastFacetAddressPosition) {
address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
}
ds.facetAddresses.pop();
delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
}
}
function initializeDiamondCut(address _init, bytes memory _calldata) internal {
if (_init == address(0)) {
if (_calldata.length != 0) {
revert InitZeroButCalldataNotEmpty();
}
} else {
if (_calldata.length == 0) {
revert CalldataEmptyButInitNotZero();
}
if (_init != address(this)) {
enforceHasContractCode(_init);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory error) = _init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
// bubble up the error
revert(string(error));
} else {
revert InitReverted();
}
}
}
}
function enforceHasContractCode(address _contract) internal view {
uint256 contractSize;
// solhint-disable-next-line no-inline-assembly
assembly {
contractSize := extcodesize(_contract)
}
if (contractSize == 0) {
revert FacetContainsNoCode();
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IUniswapV2.sol";
import "../interfaces/IUniswapV3.sol";
import "../interfaces/IWETH.sol";
import "../interfaces/IRangoStargate.sol";
import "../interfaces/IStargateReceiver.sol";
import "../interfaces/IRangoMessageReceiver.sol";
import "./LibSwapper.sol";
library LibInterchain {
/// @dev keccak256("exchange.rango.library.interchain")
bytes32 internal constant BASE_MESSAGING_CONTRACT_NAMESPACE = hex"ff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb";
struct BaseInterchainStorage {
mapping (address => bool) whitelistMessagingContracts;
}
// @notice Adds a contract to the whitelisted messaging dApps that can be called
/// @param _dapp The address of dApp
function addMessagingDApp(address _dapp) internal {
BaseInterchainStorage storage baseStorage = getBaseMessagingContractStorage();
baseStorage.whitelistMessagingContracts[_dapp] = true;
}
/// @notice Removes a contract from dApps that can be called
/// @param _dapp The address of dApp
function removeMessagingDApp(address _dapp) internal {
BaseInterchainStorage storage baseStorage = getBaseMessagingContractStorage();
require(baseStorage.whitelistMessagingContracts[_dapp], "contract not whitelisted");
delete baseStorage.whitelistMessagingContracts[_dapp];
}
/// @notice This event indicates that a dApp used Rango messaging (dAppMessage field) and we delivered the message to it
/// @param _receiverContract The address of dApp's contract that was called
/// @param _token The address of the token that is sent to the dApp, ETH for native token
/// @param _amount The amount of the token sent to them
/// @param _status The status of operation, informing the dApp that the whole process was a success or refund
/// @param _appMessage The custom message that the dApp asked Rango to deliver
/// @param success Indicates that the function call to the dApp encountered error or not
/// @param failReason If success = false, failReason will be the string reason of the failure (aka message of require)
event CrossChainMessageCalled(
address _receiverContract,
address _token,
uint _amount,
IRangoMessageReceiver.ProcessStatus _status,
bytes _appMessage,
bool success,
string failReason
);
event ActionDone(Interchain.ActionType actionType, address contractAddress, bool success, string reason);
event SubActionDone(Interchain.CallSubActionType subActionType, address contractAddress, bool success, string reason);
function encodeIm(Interchain.RangoInterChainMessage memory im) external pure returns (bytes memory) {
return abi.encode(im);
}
function handleDestinationMessage(
address _token,
uint _amount,
Interchain.RangoInterChainMessage memory m
) internal returns (address, uint256 dstAmount, IRango.CrossChainOperationStatus status) {
LibSwapper.BaseSwapperStorage storage baseStorage = LibSwapper.getBaseSwapperStorage();
address sourceToken = m.bridgeRealOutput == LibSwapper.ETH && _token == baseStorage.WETH ? LibSwapper.ETH : _token;
bool ok = true;
address receivedToken = sourceToken;
dstAmount = _amount;
if (m.actionType == Interchain.ActionType.UNI_V2)
(ok, dstAmount, receivedToken) = _handleUniswapV2(sourceToken, _amount, m, baseStorage);
else if (m.actionType == Interchain.ActionType.UNI_V3)
(ok, dstAmount, receivedToken) = _handleUniswapV3(sourceToken, _amount, m, baseStorage);
else if (m.actionType == Interchain.ActionType.CALL)
(ok, dstAmount, receivedToken) = _handleCall(sourceToken, _amount, m, baseStorage);
else if (m.actionType != Interchain.ActionType.NO_ACTION)
revert("Unsupported actionType");
if (ok && m.postAction != Interchain.CallSubActionType.NO_ACTION) {
(ok, dstAmount, receivedToken) = _handlePostAction(receivedToken, dstAmount, m.postAction, baseStorage);
}
status = ok ? IRango.CrossChainOperationStatus.Succeeded : IRango.CrossChainOperationStatus.RefundInDestination;
IRangoMessageReceiver.ProcessStatus dAppStatus = ok
? IRangoMessageReceiver.ProcessStatus.SUCCESS
: IRangoMessageReceiver.ProcessStatus.REFUND_IN_DESTINATION;
_sendTokenWithDApp(receivedToken, dstAmount, m.recipient, m.dAppMessage, m.dAppDestContract, dAppStatus);
return (receivedToken, dstAmount, status);
}
/// @notice Performs a uniswap-v2 operation
/// @param _message The interchain message that contains the swap info
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handleUniswapV2(
address _token,
uint _amount,
Interchain.RangoInterChainMessage memory _message,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool ok, uint256 amountOut, address outToken) {
Interchain.UniswapV2Action memory action = abi.decode((_message.action), (Interchain.UniswapV2Action));
if (baseStorage.whitelistContracts[action.dexAddress] != true) {
// "Dex address is not whitelisted"
return (false, _amount, _token);
}
if (action.path.length < 2) {
// "Invalid uniswap-V2 path"
return (false, _amount, _token);
}
bool shouldDeposit = _token == LibSwapper.ETH && action.path[0] == baseStorage.WETH;
if (!shouldDeposit) {
if (_token != action.path[0]) {
// "bridged token must be the same as the first token in destination swap path"
return (false, _amount, _token);
}
} else {
IWETH(baseStorage.WETH).deposit{value : _amount}();
}
LibSwapper.approve(action.path[0], action.dexAddress, _amount);
address toToken = action.path[action.path.length - 1];
uint toBalanceBefore = LibSwapper.getBalanceOf(toToken);
try
IUniswapV2(action.dexAddress).swapExactTokensForTokens(
_amount,
action.amountOutMin,
action.path,
address(this),
action.deadline
)
returns (uint256[] memory) {
emit ActionDone(Interchain.ActionType.UNI_V2, action.dexAddress, true, "");
// Note: instead of using return amounts of swapExactTokensForTokens,
// we get the diff balance of before and after. This prevents errors for tokens with transfer fees
uint toBalanceAfter = LibSwapper.getBalanceOf(toToken);
SafeERC20.safeApprove(IERC20(action.path[0]), action.dexAddress, 0);
return (true, toBalanceAfter - toBalanceBefore, toToken);
} catch {
emit ActionDone(Interchain.ActionType.UNI_V2, action.dexAddress, true, "Uniswap-V2 call failed");
SafeERC20.safeApprove(IERC20(action.path[0]), action.dexAddress, 0);
return (false, _amount, shouldDeposit ? baseStorage.WETH : _token);
}
}
/// @notice Performs a uniswap-v3 operation
/// @param _message The interchain message that contains the swap info
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handleUniswapV3(
address _token,
uint _amount,
Interchain.RangoInterChainMessage memory _message,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool, uint256, address) {
Interchain.UniswapV3ActionExactInputSingleParams memory action = abi
.decode((_message.action), (Interchain.UniswapV3ActionExactInputSingleParams));
if (baseStorage.whitelistContracts[action.dexAddress] != true) {
// "Dex address is not whitelisted"
return (false, _amount, _token);
}
bool shouldDeposit = _token == LibSwapper.ETH && action.tokenIn == baseStorage.WETH;
if (!shouldDeposit) {
if (_token != action.tokenIn) {
// "bridged token must be the same as the tokenIn in uniswapV3"
return (false, _amount, _token);
}
} else {
IWETH(baseStorage.WETH).deposit{value : _amount}();
}
LibSwapper.approve(action.tokenIn, action.dexAddress, _amount);
uint toBalanceBefore = LibSwapper.getBalanceOf(action.tokenOut);
try
IUniswapV3(action.dexAddress).exactInputSingle(IUniswapV3.ExactInputSingleParams({
tokenIn : action.tokenIn,
tokenOut : action.tokenOut,
fee : action.fee,
recipient : address(this),
deadline : action.deadline,
amountIn : _amount,
amountOutMinimum : action.amountOutMinimum,
sqrtPriceLimitX96 : action.sqrtPriceLimitX96
}))
returns (uint) {
emit ActionDone(Interchain.ActionType.UNI_V3, action.dexAddress, true, "");
// Note: instead of using return amounts of exactInputSingle,
// we get the diff balance of before and after. This prevents errors for tokens with transfer fees.
uint toBalanceAfter = LibSwapper.getBalanceOf(action.tokenOut);
SafeERC20.safeApprove(IERC20(action.tokenIn), action.dexAddress, 0);
return (true, toBalanceAfter - toBalanceBefore, action.tokenOut);
} catch {
emit ActionDone(Interchain.ActionType.UNI_V3, action.dexAddress, false, "Uniswap-V3 call failed");
SafeERC20.safeApprove(IERC20(action.tokenIn), action.dexAddress, 0);
return (false, _amount, shouldDeposit ? baseStorage.WETH : _token);
}
}
/// @notice Performs a uniswap-v2 operation
/// @param _message The interchain message that contains the swap info
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handleCall(
address _token,
uint _amount,
Interchain.RangoInterChainMessage memory _message,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool ok, uint256 amountOut, address outToken) {
Interchain.CallAction memory action = abi.decode((_message.action), (Interchain.CallAction));
if (baseStorage.whitelistContracts[action.target] != true) {
// "Action.target is not whitelisted"
return (false, _amount, _token);
}
if (baseStorage.whitelistContracts[action.spender] != true) {
// "Action.spender is not whitelisted"
return (false, _amount, _token);
}
address sourceToken = _token;
if (action.preAction == Interchain.CallSubActionType.WRAP) {
if (_token != LibSwapper.ETH) {
// "Cannot wrap non-native"
return (false, _amount, _token);
}
if (action.tokenIn != baseStorage.WETH) {
// "action.tokenIn must be WETH"
return (false, _amount, _token);
}
(ok, amountOut, sourceToken) = _handleWrap(_token, _amount, baseStorage);
} else if (action.preAction == Interchain.CallSubActionType.UNWRAP) {
if (_token != baseStorage.WETH) {
// "Cannot unwrap non-WETH"
return (false, _amount, _token);
}
if (action.tokenIn != LibSwapper.ETH) {
// "action.tokenIn must be ETH"
return (false, _amount, _token);
}
(ok, amountOut, sourceToken) = _handleUnwrap(_token, _amount, baseStorage);
} else {
ok = true;
if (action.tokenIn != _token) {
// "_message.tokenIn mismatch in call"
return (false, _amount, _token);
}
}
if (!ok)
return (false, _amount, _token);
if (sourceToken != LibSwapper.ETH)
LibSwapper.approve(sourceToken, action.spender, _amount);
uint value = sourceToken == LibSwapper.ETH ? _amount : 0;
uint toBalanceBefore = LibSwapper.getBalanceOf(_message.toToken);
(bool success, bytes memory ret) = action.target.call{value: value}(action.callData);
if (sourceToken != LibSwapper.ETH)
SafeERC20.safeApprove(IERC20(sourceToken), action.spender, 0);
if (success) {
emit ActionDone(Interchain.ActionType.CALL, action.target, true, "");
uint toBalanceAfter = LibSwapper.getBalanceOf(_message.toToken);
return (true, toBalanceAfter - toBalanceBefore, _message.toToken);
} else {
emit ActionDone(Interchain.ActionType.CALL, action.target, false, LibSwapper._getRevertMsg(ret));
return (false, _amount, sourceToken);
}
}
/// @notice Performs a uniswap-v2 operation
/// @param _postAction The type of action to perform such as WRAP, UNWRAP
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handlePostAction(
address _token,
uint _amount,
Interchain.CallSubActionType _postAction,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool ok, uint256 amountOut, address outToken) {
if (_postAction == Interchain.CallSubActionType.WRAP) {
if (_token != LibSwapper.ETH) {
// "Cannot wrap non-native"
return (false, _amount, _token);
}
(ok, amountOut, outToken) = _handleWrap(_token, _amount, baseStorage);
} else if (_postAction == Interchain.CallSubActionType.UNWRAP) {
if (_token != baseStorage.WETH) {
// "Cannot unwrap non-WETH"
return (false, _amount, _token);
}
(ok, amountOut, outToken) = _handleUnwrap(_token, _amount, baseStorage);
} else {
// revert("Unsupported post-action");
return (false, _amount, _token);
}
if (!ok)
return (false, _amount, _token);
return (ok, amountOut, outToken);
}
/// @notice Performs a WETH.deposit operation
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handleWrap(
address _token,
uint _amount,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool ok, uint256 amountOut, address outToken) {
if (_token != LibSwapper.ETH) {
// "Cannot wrap non-ETH tokens"
return (false, _amount, _token);
}
IWETH(baseStorage.WETH).deposit{value: _amount}();
emit SubActionDone(Interchain.CallSubActionType.WRAP, baseStorage.WETH, true, "");
return (true, _amount, baseStorage.WETH);
}
/// @notice Performs a WETH.deposit operation
/// @param _amount The amount of input token
/// @return ok Indicates that the swap operation was success or fail
/// @return amountOut If ok = true, amountOut is the output amount of the swap
function _handleUnwrap(
address _token,
uint _amount,
LibSwapper.BaseSwapperStorage storage baseStorage
) private returns (bool ok, uint256 amountOut, address outToken) {
if (_token != baseStorage.WETH)
// revert("Non-WETH tokens unwrapped");
return (false, _amount, _token);
IWETH(baseStorage.WETH).withdraw(_amount);
emit SubActionDone(Interchain.CallSubActionType.UNWRAP, baseStorage.WETH, true, "");
return (true, _amount, LibSwapper.ETH);
}
/// @notice An internal function to send a token from the current contract to another contract or wallet
/// @dev This function also can convert WETH to ETH before sending if _withdraw flat is set to true
/// @dev To send native token _nativeOut param should be set to true, otherwise we assume it's an ERC20 transfer
/// @dev If there is a message from a dApp it sends the money to the contract instead of the end-user and calls its handleRangoMessage
/// @param _token The token that is going to be sent to a wallet, ZERO address for native
/// @param _amount The sent amount
/// @param _receiver The receiver wallet address or contract
function _sendTokenWithDApp(
address _token,
uint256 _amount,
address _receiver,
bytes memory _dAppMessage,
address _dAppReceiverContract,
IRangoMessageReceiver.ProcessStatus processStatus
) internal {
bool thereIsAMessage = _dAppReceiverContract != LibSwapper.ETH;
address immediateReceiver = thereIsAMessage ? _dAppReceiverContract : _receiver;
BaseInterchainStorage storage messagingStorage = getBaseMessagingContractStorage();
emit LibSwapper.SendToken(_token, _amount, immediateReceiver);
if (_token == LibSwapper.ETH) {
LibSwapper._sendNative(immediateReceiver, _amount);
} else {
SafeERC20.safeTransfer(IERC20(_token), immediateReceiver, _amount);
}
if (thereIsAMessage) {
require(
messagingStorage.whitelistMessagingContracts[_dAppReceiverContract],
"3rd-party contract not whitelisted"
);
try IRangoMessageReceiver(_dAppReceiverContract)
.handleRangoMessage(_token, _amount, processStatus, _dAppMessage)
{
emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, true, "");
} catch Error(string memory reason) {
emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, false, reason);
} catch (bytes memory lowLevelData) {
emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, false, LibSwapper._getRevertMsg(lowLevelData));
}
}
}
/// @notice A utility function to fetch storage from a predefined random slot using assembly
/// @return s The storage object
function getBaseMessagingContractStorage() internal pure returns (BaseInterchainStorage storage s) {
bytes32 namespace = BASE_MESSAGING_CONTRACT_NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly {
s.slot := namespace
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IWETH.sol";
import "../interfaces/IRango.sol";
/// @title BaseSwapper
/// @author 0xiden
/// @notice library to provide swap functionality
library LibSwapper {
/// @dev keccak256("exchange.rango.library.swapper")
bytes32 internal constant BASE_SWAPPER_NAMESPACE = hex"43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a";
address payable constant ETH = payable(0x0000000000000000000000000000000000000000);
struct BaseSwapperStorage {
address payable feeContractAddress;
address WETH;
mapping(address => bool) whitelistContracts;
mapping(address => mapping(bytes4 => bool)) whitelistMethods;
}
/// @notice Emitted if any fee transfer was required
/// @param token The address of received token, address(0) for native
/// @param affiliatorAddress The address of affiliate wallet
/// @param platformFee The amount received as platform fee
/// @param destinationExecutorFee The amount received to execute transaction on destination (only for cross chain txs)
/// @param affiliateFee The amount received by affiliate
/// @param dAppTag Optional identifier to make tracking easier.
event FeeInfo(
address token,
address indexed affiliatorAddress,
uint platformFee,
uint destinationExecutorFee,
uint affiliateFee,
uint16 indexed dAppTag
);
/// @notice A call to another dex or contract done and here is the result
/// @param target The address of dex or contract that is called
/// @param success A boolean indicating that the call was success or not
/// @param returnData The response of function call
event CallResult(address target, bool success, bytes returnData);
/// @notice A swap request is done and we also emit the output
/// @param requestId Optional parameter to make tracking of transaction easier
/// @param fromToken Input token address to be swapped from
/// @param toToken Output token address to be swapped to
/// @param amountIn Input amount of fromToken that is being swapped
/// @param dAppTag Optional identifier to make tracking easier
/// @param outputAmount The output amount of the swap, measured by the balance change before and after the swap
/// @param receiver The address to receive the output of swap. Can be address(0) when swap is before a bridge action
event RangoSwap(
address indexed requestId,
address fromToken,
address toToken,
uint amountIn,
uint minimumAmountExpected,
uint16 indexed dAppTag,
uint outputAmount,
address receiver
);
/// @notice Output amount of a dex calls is logged
/// @param _token The address of output token, ZERO address for native
/// @param amount The amount of output
event DexOutput(address _token, uint amount);
/// @notice The output money (ERC20/Native) is sent to a wallet
/// @param _token The token that is sent to a wallet, ZERO address for native
/// @param _amount The sent amount
/// @param _receiver The receiver wallet address
event SendToken(address _token, uint256 _amount, address _receiver);
/// @notice Notifies that Rango's fee receiver address updated
/// @param _oldAddress The previous fee wallet address
/// @param _newAddress The new fee wallet address
event FeeContractAddressUpdated(address _oldAddress, address _newAddress);
/// @notice Notifies that WETH address is updated
/// @param _oldAddress The previous weth address
/// @param _newAddress The new weth address
event WethContractAddressUpdated(address _oldAddress, address _newAddress);
/// @notice Notifies that admin manually refunded some money
/// @param _token The address of refunded token, 0x000..00 address for native token
/// @param _amount The amount that is refunded
event Refunded(address _token, uint _amount);
/// @notice The requested call data which is computed off-chain and passed to the contract
/// @dev swapFromToken and amount parameters are only helper params and the actual amount and
/// token are set in callData
/// @param spender The contract which the approval is given to if swapFromToken is not native.
/// @param target The dex contract address that should be called
/// @param swapFromToken Token address of to be used in the swap.
/// @param amount The amount to be approved or native amount sent.
/// @param callData The required data field that should be give to the dex contract to perform swap
struct Call {
address spender;
address payable target;
address swapFromToken;
address swapToToken;
bool needsTransferFromUser;
uint amount;
bytes callData;
}
/// @notice General swap request which is given to us in all relevant functions
/// @param requestId The request id passed to make tracking transactions easier
/// @param fromToken The source token that is going to be swapped (in case of simple swap or swap + bridge) or the briding token (in case of solo bridge)
/// @param toToken The output token of swapping. This is the output of DEX step and is also input of bridging step
/// @param amountIn The amount of input token to be swapped
/// @param platformFee The amount of fee charged by platform
/// @param destinationExecutorFee The amount of fee required for relayer execution on the destination
/// @param affiliateFee The amount of fee charged by affiliator dApp
/// @param affiliatorAddress The wallet address that the affiliator fee should be sent to
/// @param minimumAmountExpected The minimum amount of toToken expected after executing Calls
/// @param dAppTag An optional parameter
struct SwapRequest {
address requestId;
address fromToken;
address toToken;
uint amountIn;
uint platformFee;
uint destinationExecutorFee;
uint affiliateFee;
address payable affiliatorAddress;
uint minimumAmountExpected;
uint16 dAppTag;
}
/// @notice initializes the base swapper and sets the init params (such as Wrapped token address)
/// @param _weth Address of wrapped token (WETH, WBNB, etc.) on the current chain
function setWeth(address _weth) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
address oldAddress = baseStorage.WETH;
baseStorage.WETH = _weth;
require(_weth != address(0), "Invalid WETH!");
emit WethContractAddressUpdated(oldAddress, _weth);
}
/// @notice Sets the wallet that receives Rango's fees from now on
/// @param _address The receiver wallet address
function updateFeeContractAddress(address payable _address) internal {
BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
address oldAddress = baseSwapperStorage.feeContractAddress;
baseSwapperStorage.feeContractAddress = _address;
emit FeeContractAddressUpdated(oldAddress, _address);
}
/// Whitelist ///
/// @notice Adds a contract to the whitelisted DEXes that can be called
/// @param contractAddress The address of the DEX
function addWhitelist(address contractAddress) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
baseStorage.whitelistContracts[contractAddress] = true;
}
/// @notice Adds a method of contract to the whitelisted DEXes that can be called
/// @param contractAddress The address of the DEX
/// @param methodIds The method of the DEX
function addMethodWhitelists(address contractAddress, bytes4[] calldata methodIds) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
baseStorage.whitelistContracts[contractAddress] = true;
for (uint i = 0; i < methodIds.length; i++)
baseStorage.whitelistMethods[contractAddress][methodIds[i]] = true;
}
/// @notice Adds a method of contract to the whitelisted DEXes that can be called
/// @param contractAddress The address of the DEX
/// @param methodId The method of the DEX
function addMethodWhitelist(address contractAddress, bytes4 methodId) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
baseStorage.whitelistContracts[contractAddress] = true;
baseStorage.whitelistMethods[contractAddress][methodId] = true;
}
/// @notice Removes a contract from the whitelisted DEXes
/// @param contractAddress The address of the DEX or dApp
function removeWhitelist(address contractAddress) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
delete baseStorage.whitelistContracts[contractAddress];
}
/// @notice Removes a method of contract from the whitelisted DEXes
/// @param contractAddress The address of the DEX or dApp
/// @param methodId The method of the DEX
function removeMethodWhitelist(address contractAddress, bytes4 methodId) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
delete baseStorage.whitelistMethods[contractAddress][methodId];
}
function onChainSwapsPreBridge(
SwapRequest memory request,
Call[] calldata calls,
uint extraFee
) internal returns (uint out) {
bool isNative = request.fromToken == ETH;
uint minimumRequiredValue = (isNative ? request.platformFee + request.affiliateFee + request.amountIn + request.destinationExecutorFee : 0) + extraFee;
require(msg.value >= minimumRequiredValue, 'Send more ETH to cover input amount + fee');
(, out) = onChainSwapsInternal(request, calls, extraFee);
// when there is a bridge after swap, set the receiver in swap event to address(0)
emitSwapEvent(request, out, ETH);
return out;
}
/// @notice Internal function to compute output amount of DEXes
/// @param request The general swap request containing from/to token and fee/affiliate rewards
/// @param calls The list of DEX calls
/// @param extraNativeFee The amount of native tokens to keep and not return to user as excess amount.
/// @return The response of all DEX calls and the output amount of the whole process
function onChainSwapsInternal(
SwapRequest memory request,
Call[] calldata calls,
uint256 extraNativeFee
) internal returns (bytes[] memory, uint) {
uint toBalanceBefore = getBalanceOf(request.toToken);
uint fromBalanceBefore = getBalanceOf(request.fromToken);
uint256[] memory initialBalancesList = getInitialBalancesList(calls);
// transfer tokens from user for SwapRequest and Calls that require transfer from user.
transferTokensFromUserForSwapRequest(request);
transferTokensFromUserForCalls(calls);
bytes[] memory result = callSwapsAndFees(request, calls);
// check if any extra tokens were taken from contract and return excess tokens if any.
returnExcessAmounts(request, calls, initialBalancesList);
// get balance after returning excesses.
uint fromBalanceAfter = getBalanceOf(request.fromToken);
// check over-expense of fromToken and return excess if any.
if (request.fromToken != ETH) {
require(fromBalanceAfter >= fromBalanceBefore, "Source token balance on contract must not decrease after swap");
if (fromBalanceAfter > fromBalanceBefore)
_sendToken(request.fromToken, fromBalanceAfter - fromBalanceBefore, msg.sender);
}
else {
require(fromBalanceAfter >= fromBalanceBefore - msg.value, "Source token balance on contract must not decrease after swap");
// When we are keeping extraNativeFee for bridgingFee, we should consider it in calculations.
if (fromBalanceAfter > fromBalanceBefore - msg.value + extraNativeFee)
_sendToken(request.fromToken, fromBalanceAfter + msg.value - fromBalanceBefore - extraNativeFee, msg.sender);
}
uint toBalanceAfter = getBalanceOf(request.toToken);
uint secondaryBalance = toBalanceAfter - toBalanceBefore;
require(secondaryBalance >= request.minimumAmountExpected, "Output is less than minimum expected");
return (result, secondaryBalance);
}
/// @notice Private function to handle fetching money from wallet to contract, reduce fee/affiliate, perform DEX calls
/// @param request The general swap request containing from/to token and fee/affiliate rewards
/// @param calls The list of DEX calls
/// @dev It checks the whitelisting of all DEX addresses + having enough msg.value as input
/// @return The bytes of all DEX calls response
function callSwapsAndFees(SwapRequest memory request, Call[] calldata calls) private returns (bytes[] memory) {
bool isSourceNative = request.fromToken == ETH;
BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
for (uint256 i = 0; i < calls.length; i++) {
require(baseSwapperStorage.whitelistContracts[calls[i].spender], "Contract spender not whitelisted");
require(baseSwapperStorage.whitelistContracts[calls[i].target], "Contract target not whitelisted");
bytes4 sig = bytes4(calls[i].callData[: 4]);
require(baseSwapperStorage.whitelistMethods[calls[i].target][sig], "Unauthorized call data!");
}
// Get Platform fee
bool hasPlatformFee = request.platformFee > 0;
bool hasDestExecutorFee = request.destinationExecutorFee > 0;
bool hasAffiliateFee = request.affiliateFee > 0;
if (hasPlatformFee || hasDestExecutorFee) {
require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
_sendToken(request.fromToken, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false);
}
// Get affiliate fee
if (hasAffiliateFee) {
require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
_sendToken(request.fromToken, request.affiliateFee, request.affiliatorAddress, isSourceNative, false);
}
// emit Fee event
if (hasPlatformFee || hasDestExecutorFee || hasAffiliateFee) {
emit FeeInfo(
request.fromToken,
request.affiliatorAddress,
request.platformFee,
request.destinationExecutorFee,
request.affiliateFee,
request.dAppTag
);
}
// Execute swap Calls
bytes[] memory returnData = new bytes[](calls.length);
address tmpSwapFromToken;
for (uint256 i = 0; i < calls.length; i++) {
tmpSwapFromToken = calls[i].swapFromToken;
bool isTokenNative = tmpSwapFromToken == ETH;
if (isTokenNative == false)
approveMax(tmpSwapFromToken, calls[i].spender, calls[i].amount);
(bool success, bytes memory ret) = isTokenNative
? calls[i].target.call{value : calls[i].amount}(calls[i].callData)
: calls[i].target.call(calls[i].callData);
emit CallResult(calls[i].target, success, ret);
if (!success)
revert(_getRevertMsg(ret));
returnData[i] = ret;
}
return returnData;
}
/// @notice Approves an ERC20 token to a contract to transfer from the current contract
/// @param token The address of an ERC20 token
/// @param spender The contract address that should be approved
/// @param value The amount that should be approved
function approve(address token, address spender, uint value) internal {
SafeERC20.safeApprove(IERC20(token), spender, 0);
SafeERC20.safeIncreaseAllowance(IERC20(token), spender, value);
}
/// @notice Approves an ERC20 token to a contract to transfer from the current contract, approves for inf value
/// @param token The address of an ERC20 token
/// @param spender The contract address that should be approved
/// @param value The desired allowance. If current allowance is less than this value, infinite allowance will be given
function approveMax(address token, address spender, uint value) internal {
uint256 currentAllowance = IERC20(token).allowance(address(this), spender);
if (currentAllowance < value) {
if (currentAllowance != 0) {
// We set allowance to 0 if not already. tokens such as USDT require zero allowance first.
SafeERC20.safeApprove(IERC20(token), spender, 0);
}
SafeERC20.safeIncreaseAllowance(IERC20(token), spender, type(uint256).max);
}
}
function _sendToken(address _token, uint256 _amount, address _receiver) internal {
(_token == ETH) ? _sendNative(_receiver, _amount) : SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount);
}
function sumFees(IRango.RangoBridgeRequest memory request) internal pure returns (uint256) {
return request.platformFee + request.affiliateFee + request.destinationExecutorFee;
}
function sumFees(SwapRequest memory request) internal pure returns (uint256) {
return request.platformFee + request.affiliateFee + request.destinationExecutorFee;
}
function collectFees(IRango.RangoBridgeRequest memory request) internal {
// Get Platform fee
bool hasPlatformFee = request.platformFee > 0;
bool hasDestExecutorFee = request.destinationExecutorFee > 0;
bool hasAffiliateFee = request.affiliateFee > 0;
bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee;
if (!hasAnyFee) {
return;
}
bool isSourceNative = request.token == ETH;
BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
if (hasPlatformFee || hasDestExecutorFee) {
require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
_sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false);
}
// Get affiliate fee
if (hasAffiliateFee) {
require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
_sendToken(request.token, request.affiliateFee, request.affiliatorAddress, isSourceNative, false);
}
// emit Fee event
emit FeeInfo(
request.token,
request.affiliatorAddress,
request.platformFee,
request.destinationExecutorFee,
request.affiliateFee,
request.dAppTag
);
}
function collectFeesFromSender(IRango.RangoBridgeRequest memory request) internal {
// Get Platform fee
bool hasPlatformFee = request.platformFee > 0;
bool hasDestExecutorFee = request.destinationExecutorFee > 0;
bool hasAffiliateFee = request.affiliateFee > 0;
bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee;
if (!hasAnyFee) {
return;
}
bool isSourceNative = request.token == ETH;
BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage();
if (hasPlatformFee || hasDestExecutorFee) {
require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set");
if (isSourceNative)
_sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false);
else
SafeERC20.safeTransferFrom(
IERC20(request.token),
msg.sender,
baseSwapperStorage.feeContractAddress,
request.platformFee + request.destinationExecutorFee
);
}
// Get affiliate fee
if (hasAffiliateFee) {
require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress");
if (isSourceNative)
_sendToken(request.token, request.affiliateFee, request.affiliatorAddress, isSourceNative, false);
else
SafeERC20.safeTransferFrom(
IERC20(request.token),
msg.sender,
request.affiliatorAddress,
request.affiliateFee
);
}
// emit Fee event
emit FeeInfo(
request.token,
request.affiliatorAddress,
request.platformFee,
request.destinationExecutorFee,
request.affiliateFee,
request.dAppTag
);
}
/// @notice An internal function to send a token from the current contract to another contract or wallet
/// @dev This function also can convert WETH to ETH before sending if _withdraw flat is set to true
/// @dev To send native token _nativeOut param should be set to true, otherwise we assume it's an ERC20 transfer
/// @param _token The token that is going to be sent to a wallet, ZERO address for native
/// @param _amount The sent amount
/// @param _receiver The receiver wallet address or contract
/// @param _nativeOut means the output is native token
/// @param _withdraw If true, indicates that we should swap WETH to ETH before sending the money and _nativeOut must also be true
function _sendToken(
address _token,
uint256 _amount,
address _receiver,
bool _nativeOut,
bool _withdraw
) internal {
BaseSwapperStorage storage baseStorage = getBaseSwapperStorage();
emit SendToken(_token, _amount, _receiver);
if (_nativeOut) {
if (_withdraw) {
require(_token == baseStorage.WETH, "token mismatch");
IWETH(baseStorage.WETH).withdraw(_amount);
}
_sendNative(_receiver, _amount);
} else {
SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount);
}
}
/// @notice An internal function to send native token to a contract or wallet
/// @param _receiver The address that will receive the native token
/// @param _amount The amount of the native token that should be sent
function _sendNative(address _receiver, uint _amount) internal {
(bool sent,) = _receiver.call{value : _amount}("");
require(sent, "failed to send native");
}
/// @notice A utility function to fetch storage from a predefined random slot using assembly
/// @return s The storage object
function getBaseSwapperStorage() internal pure returns (BaseSwapperStorage storage s) {
bytes32 namespace = BASE_SWAPPER_NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly {
s.slot := namespace
}
}
/// @notice To extract revert message from a DEX/contract call to represent to the end-user in the blockchain
/// @param _returnData The resulting bytes of a failed call to a DEX or contract
/// @return A string that describes what was the error
function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (_returnData.length < 68) return 'Transaction reverted silently';
assembly {
// Slice the sighash.
_returnData := add(_returnData, 0x04)
}
return abi.decode(_returnData, (string));
// All that remains is the revert string
}
function getBalanceOf(address token) internal view returns (uint) {
return token == ETH ? address(this).balance : IERC20(token).balanceOf(address(this));
}
/// @notice Fetches the balances of swapToTokens.
/// @dev this fetches the balances for swapToToken of swap Calls. If native eth is received, the balance has already increased so we subtract msg.value.
function getInitialBalancesList(Call[] calldata calls) internal view returns (uint256[] memory) {
uint callsLength = calls.length;
uint256[] memory balancesList = new uint256[](callsLength);
address token;
for (uint256 i = 0; i < callsLength; i++) {
token = calls[i].swapToToken;
balancesList[i] = getBalanceOf(token);
if (token == ETH)
balancesList[i] -= msg.value;
}
return balancesList;
}
/// This function transfers tokens from users based on the SwapRequest, it transfers amountIn + fees.
function transferTokensFromUserForSwapRequest(SwapRequest memory request) private {
uint transferAmount = request.amountIn + sumFees(request);
if (request.fromToken != ETH)
SafeERC20.safeTransferFrom(IERC20(request.fromToken), msg.sender, address(this), transferAmount);
else
require(msg.value >= transferAmount);
}
/// This function iterates on calls and if needsTransferFromUser, transfers tokens from user
function transferTokensFromUserForCalls(Call[] calldata calls) private {
uint callsLength = calls.length;
Call calldata call;
address token;
for (uint256 i = 0; i < callsLength; i++) {
call = calls[i];
token = call.swapFromToken;
if (call.needsTransferFromUser && token != ETH)
SafeERC20.safeTransferFrom(IERC20(call.swapFromToken), msg.sender, address(this), call.amount);
}
}
/// @dev returns any excess token left by the contract.
/// We iterate over `swapToToken`s because each swapToToken is either the request.toToken or is the output of
/// another `Call` in the list of swaps which itself either has transferred tokens from user,
/// or is a middle token that is the output of another `Call`.
function returnExcessAmounts(
SwapRequest memory request,
Call[] calldata calls,
uint256[] memory initialBalancesList) internal {
uint excessAmountToToken;
address tmpSwapToToken;
uint currentBalanceTo;
for (uint256 i = 0; i < calls.length; i++) {
tmpSwapToToken = calls[i].swapToToken;
currentBalanceTo = getBalanceOf(tmpSwapToToken);
excessAmountToToken = currentBalanceTo - initialBalancesList[i];
if (excessAmountToToken > 0 && tmpSwapToToken != request.toToken) {
_sendToken(tmpSwapToToken, excessAmountToToken, msg.sender);
}
}
}
function emitSwapEvent(SwapRequest memory request, uint output, address receiver) internal {
emit RangoSwap(
request.requestId,
request.fromToken,
request.toToken,
request.amountIn,
request.minimumAmountExpected,
request.dAppTag,
output,
receiver
);
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
library LibTransform {
function addressToString(address a) internal pure returns (string memory) {
bytes memory data = abi.encodePacked(a);
bytes memory characters = '0123456789abcdef';
bytes memory byteString = new bytes(2 + data.length * 2);
byteString[0] = '0';
byteString[1] = 'x';
for (uint256 i; i < data.length; ++i) {
byteString[2 + i * 2] = characters[uint256(uint8(data[i] >> 4))];
byteString[3 + i * 2] = characters[uint256(uint8(data[i] & 0x0f))];
}
return string(byteString);
}
function bytesToAddress(bytes memory bs) internal pure returns (address addr) {
return address(uint160(bytes20(bs)));
}
function addressToBytes32LeftPadded(address addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
function bytes32LeftPaddedToAddress(bytes32 b) internal pure returns (address){
return address(uint160(uint256(b)));
}
function stringToBytes(string memory s) internal pure returns (bytes memory){
bytes memory b3 = bytes(s);
return b3;
}
function stringToAddress(string memory s) internal pure returns (address){
return bytesToAddress(stringToBytes(s));
}
function extractAddressFromEndOfBytes(bytes calldata bs) internal pure returns (address){
if (bs.length < 20)
return bytesToAddress(bs);
return bytesToAddress(bs[bs.length - 20 :]);
}
function extractAddressWithOffsetFromEnd(bytes calldata bs, uint256 offset) internal pure returns (address){
if (bs.length < 20 || bs.length < offset)
return bytesToAddress(bs);
return bytesToAddress(bs[bs.length - offset :]);
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.16;
/// @title Reentrancy Guard
/// @author
/// @notice Abstract contract to provide protection against reentrancy
abstract contract ReentrancyGuard {
/// Storage ///
/// @dev keccak256("exchange.rango.reentrancyguard");
bytes32 private constant NAMESPACE = hex"4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab2559";
/// Types ///
struct ReentrancyStorage {
uint256 status;
}
/// Errors ///
error ReentrancyError();
/// Constants ///
uint256 private constant _NOT_ENTERED = 0;
uint256 private constant _ENTERED = 1;
/// Modifiers ///
modifier nonReentrant() {
ReentrancyStorage storage s = reentrancyStorage();
if (s.status == _ENTERED) revert ReentrancyError();
s.status = _ENTERED;
_;
s.status = _NOT_ENTERED;
}
/// Private Methods ///
/// @dev fetch local storage
function reentrancyStorage() private pure returns (ReentrancyStorage storage data) {
bytes32 position = NAMESPACE;
// solhint-disable-next-line no-inline-assembly
assembly {
data.slot := position
}
}
}{
"optimizer": {
"enabled": true,
"runs": 10000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"ReentrancyError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"MultichainRoutersAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"MultichainRoutersRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"requestId","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"originalSender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IRango.CrossChainOperationStatus","name":"status","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"dAppTag","type":"uint16"}],"name":"RangoBridgeCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"requestId","type":"address"},{"indexed":false,"internalType":"address","name":"bridgeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"bridgeAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"hasInterchainMessage","type":"bool"},{"indexed":false,"internalType":"bool","name":"hasDestinationSwap","type":"bool"},{"indexed":true,"internalType":"uint8","name":"bridgeId","type":"uint8"},{"indexed":true,"internalType":"uint16","name":"dAppTag","type":"uint16"}],"name":"RangoBridgeInitiated","type":"event"},{"inputs":[{"internalType":"address[]","name":"_routers","type":"address[]"}],"name":"initMultichain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum IRangoMultichain.MultichainBridgeType","name":"bridgeType","type":"uint8"},{"internalType":"enum IRangoMultichain.MultichainActionType","name":"actionType","type":"uint8"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"multichainRouter","type":"address"},{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"uint256","name":"receiverChainID","type":"uint256"},{"internalType":"string","name":"anycallTargetContractOnDestChain","type":"string"},{"internalType":"bytes","name":"imMessage","type":"bytes"}],"internalType":"struct IRangoMultichain.MultichainBridgeRequest","name":"request","type":"tuple"},{"components":[{"internalType":"address","name":"requestId","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"address payable","name":"affiliatorAddress","type":"address"},{"internalType":"uint256","name":"destinationExecutorFee","type":"uint256"},{"internalType":"uint16","name":"dAppTag","type":"uint16"}],"internalType":"struct IRango.RangoBridgeRequest","name":"bridgeRequest","type":"tuple"}],"name":"multichainBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"requestId","type":"address"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"destinationExecutorFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"},{"internalType":"address payable","name":"affiliatorAddress","type":"address"},{"internalType":"uint256","name":"minimumAmountExpected","type":"uint256"},{"internalType":"uint16","name":"dAppTag","type":"uint16"}],"internalType":"struct LibSwapper.SwapRequest","name":"request","type":"tuple"},{"components":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"address payable","name":"target","type":"address"},{"internalType":"address","name":"swapFromToken","type":"address"},{"internalType":"address","name":"swapToToken","type":"address"},{"internalType":"bool","name":"needsTransferFromUser","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct LibSwapper.Call[]","name":"calls","type":"tuple[]"},{"components":[{"internalType":"enum IRangoMultichain.MultichainBridgeType","name":"bridgeType","type":"uint8"},{"internalType":"enum IRangoMultichain.MultichainActionType","name":"actionType","type":"uint8"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"multichainRouter","type":"address"},{"internalType":"address","name":"receiverAddress","type":"address"},{"internalType":"uint256","name":"receiverChainID","type":"uint256"},{"internalType":"string","name":"anycallTargetContractOnDestChain","type":"string"},{"internalType":"bytes","name":"imMessage","type":"bytes"}],"internalType":"struct IRangoMultichain.MultichainBridgeRequest","name":"bridgeRequest","type":"tuple"}],"name":"multichainSwapAndBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_routers","type":"address[]"}],"name":"removeMultichainRouters","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50613a35806100206000396000f3fe60806040526004361061003f5760003560e01c806395c2d6f314610044578063a6c3d51f14610059578063cc818ad11461006c578063da2ad9471461008c575b600080fd5b6100576100523660046130b3565b6100ac565b005b6100576100673660046131ce565b61031e565b34801561007857600080fd5b506100576100873660046132de565b610557565b34801561009857600080fd5b506100576100a73660046132de565b61056d565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610127576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181556040820151602083015160006101408561065b565b61014a908461334f565b90506001600160a01b0382161561016c5761016782333084610686565b610179565b8034101561017957600080fd5b6101828561073d565b60008651600181111561019757610197613362565b03610231576101a7868385610948565b60e0850151855160808089015160a0808b0151604080516001600160a01b038a81168252602082018c905294851691810191909152606081019190915260009381018490529081019290925261ffff9093169260049216907fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4269060c00160405180910390a4610312565b60008660e0015180602001905181019061024b9190613451565b9050610258878486610e3e565b60e086015161ffff16600460ff1687600001516001600160a01b03167fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d42686888c608001518d60a001516001600060038111156102b6576102b6613362565b8a60c0015160038111156102cc576102cc613362565b604080516001600160a01b039889168152602081019790975294909616858501526060850192909252151560808401529092141560a082015290519081900360c00190a4505b50506000909155505050565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610399576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001815560006103ab868686846110e5565b90506000835160018111156103c2576103c2613362565b03610462576103d683876040015183610948565b610120860151865160408089015160808781015160a0808a015185516001600160a01b039586168152602081018a905292851695830195909552606082019490945260009181018290529283015261ffff9093169260049216907fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4269060c00160405180910390a461054c565b60008360e0015180602001905181019061047c9190613451565b905061048d84886040015184610e3e565b61012087015161ffff16600460ff1688600001516001600160a01b03167fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4268a604001518689608001518a60a001516001600060038111156104f0576104f0613362565b8a60c00151600381111561050657610506613362565b604080516001600160a01b039889168152602081019790975294909616858501526060850192909252151560808401529092141560a082015290519081900360c00190a4505b506000905550505050565b61055f6111df565b6105698282611283565b5050565b6105756111df565b7f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c60005b8281101561061c578160008585848181106105b6576105b66135aa565b90506020020160208101906105cb91906135d9565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905580610614816135f6565b915050610599565b507ff60edbae2829c4ad242912d9f5c804cb71407ecebc5395f229534994239b7f06838360405161064e92919061362e565b60405180910390a1505050565b60008160c0015182608001518360600151610676919061334f565b610680919061334f565b92915050565b6040516001600160a01b03808516602483015283166044820152606481018290526107379085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526113c2565b50505050565b606081015160c0820151608083015191151591901515901515600083806107615750825b806107695750815b905080610777575050505050565b60208501516001600160a01b0316157f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a85806107b05750845b156108405780546001600160a01b03166108115760405162461bcd60e51b815260206004820152601c60248201527f46656520636f6e74726163742061646472657373206e6f74207365740000000060448201526064015b60405180910390fd5b61084087602001518860c00151896060015161082d919061334f565b83546001600160a01b03168560006114ac565b83156108ba5760a08701516001600160a01b03166108a05760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420616666696c6961746f7241646472657373000000000000006044820152606401610808565b6108ba876020015188608001518960a001518560006114ac565b8660e0015161ffff168760a001516001600160a01b03167ff14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b89602001518a606001518b60c001518c6080015160405161093794939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a350505050505050565b60038360200151600481111561096057610960613362565b036109ec5760808301516040517f628d6cba000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0391821660248201529083169063628d6cba906044015b600060405180830381600087803b1580156109cf57600080fd5b505af11580156109e3573d6000803e3d6000fd5b50505050505050565b600483602001516004811115610a0457610a04613362565b03610a5d5760808301516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152602481018390529083169063a9059cbb906044016109b5565b60608301516001600160a01b03811660009081527f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c602081905260409091205460ff16610b125760405162461bcd60e51b815260206004820152602860248201527f52657175657374656420726f757465722061646472657373206e6f742077686960448201527f74656c69737465640000000000000000000000000000000000000000000000006064820152608401610808565b600285602001516004811115610b2a57610b2a613362565b14610b3f57610b3a848385611618565b610b96565b6001600160a01b03841615610b965760405162461bcd60e51b815260206004820152600d60248201527f696e76616c696420746f6b656e000000000000000000000000000000000000006044820152606401610808565b81600086602001516004811115610baf57610baf613362565b03610c7657846001600160a01b031686604001516001600160a01b031614610bd657600080fd5b604080870151608088015160a089015192517f241dc2df0000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152908216602482015260448101879052606481019290925282169063241dc2df906084015b600060405180830381600087803b158015610c5957600080fd5b505af1158015610c6d573d6000803e3d6000fd5b50505050610e36565b600186602001516004811115610c8e57610c8e613362565b03610d8357846001600160a01b031686604001516001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d03919061367c565b6001600160a01b031614610d1657600080fd5b604080870151608088015160a089015192517fedbdf5e20000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152908216602482015260448101879052606481019290925282169063edbdf5e290608401610c3f565b600286602001516004811115610d9b57610d9b613362565b0361003f57604080870151608088015160a089015192517fa5e565710000000000000000000000000000000000000000000000000000000081526001600160a01b0392831660048201529082166024820152604481019290925282169063a5e565719086906064016000604051808303818588803b158015610e1c57600080fd5b505af1158015610e30573d6000803e3d6000fd5b50505050505b505050505050565b60608301516001600160a01b031660009081527f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c602081905260409091205460ff16610ecc5760405162461bcd60e51b815260206004820152601260248201527f726f75746572206e6f7420616c6c6f77656400000000000000000000000000006044820152606401610808565b600284602001516004811115610ee457610ee4613362565b14610efd57610ef883856060015184611618565b610f54565b6001600160a01b03831615610f545760405162461bcd60e51b815260206004820152600d60248201527f696e76616c696420746f6b656e000000000000000000000000000000000000006044820152606401610808565b6060840151600085602001516004811115610f7157610f71613362565b03610fff57806001600160a01b0316636b4b43768660400151610f9788608001516116ec565b868960a001518a60c001518b60e001516040518763ffffffff1660e01b8152600401610fc8969594939291906136c5565b600060405180830381600087803b158015610fe257600080fd5b505af1158015610ff6573d6000803e3d6000fd5b505050506110de565b60018560200151600481111561101757611017613362565b0361103d57806001600160a01b031663e0e9048e8660400151610f9788608001516116ec565b60028560200151600481111561105557611055613362565b0361003f57806001600160a01b031663ea0c968b84876040015161107c89608001516116ec565b8960a001518a60c001518b60e001516040518763ffffffff1660e01b81526004016110ab959493929190613726565b6000604051808303818588803b1580156110c457600080fd5b505af11580156110d8573d6000803e3d6000fd5b50505050505b5050505050565b60208401516000906001600160a01b031615818382611105576000611137565b8760a0015188606001518960c001518a60800151611123919061334f565b61112d919061334f565b611137919061334f565b611141919061334f565b9050803410156111b95760405162461bcd60e51b815260206004820152602960248201527f53656e64206d6f72652045544820746f20636f76657220696e70757420616d6f60448201527f756e74202b2066656500000000000000000000000000000000000000000000006064820152608401610808565b6111c587878787611a2f565b93506111d5905087846000611cc1565b5050949350505050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c600401546001600160a01b031633146112815760405162461bcd60e51b815260206004820152602260248201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610808565b565b7f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c6000805b83811015611382578484828181106112c2576112c26135aa565b90506020020160208101906112d791906135d9565b91506001600160a01b03821661132f5760405162461bcd60e51b815260206004820152601660248201527f496e76616c696420526f757465722041646472657373000000000000000000006044820152606401610808565b6001600160a01b038216600090815260208490526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558061137a816135f6565b9150506112a8565b507f81c16a53fbd623a5a4d8455016c10ed2dac832fbb7577c812df3d615bae7f3aa84846040516113b492919061362e565b60405180910390a150505050565b6000611417826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611d639092919063ffffffff16565b8051909150156114a75780806020019051810190611435919061378e565b6114a75760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610808565b505050565b604080516001600160a01b0387811682526020820187905285168183015290517f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a917fdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5919081900360600190a1821561160d5781156115fe5760018101546001600160a01b038781169116146115845760405162461bcd60e51b815260206004820152600e60248201527f746f6b656e206d69736d617463680000000000000000000000000000000000006044820152606401610808565b60018101546040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b1580156115e557600080fd5b505af11580156115f9573d6000803e3d6000fd5b505050505b6116088486611d7c565b610e36565b610e36868587611e1f565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a591906137ab565b9050818110156107375780156116c1576116c184846000611e68565b61073784847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611fb6565b604051606082811b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208301529060009060340160408051601f19818403018152828201909152601082527f3031323334353637383961626364656600000000000000000000000000000000602083015280519092506000906117729060026137c4565b61177d90600261334f565b67ffffffffffffffff81111561179557611795612e33565b6040519080825280601f01601f1916602001820160405280156117bf576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106117f6576117f66135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611859576118596135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060005b8351811015611a26578260048583815181106118a9576118a96135aa565b016020015182517fff0000000000000000000000000000000000000000000000000000000000000090911690911c60f81c9081106118e9576118e96135aa565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168261191c8360026137c4565b61192790600261334f565b81518110611937576119376135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535082848281518110611979576119796135aa565b602091010151815160f89190911c600f16908110611999576119996135aa565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016826119cc8360026137c4565b6119d790600361334f565b815181106119e7576119e76135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611a1f816135f6565b905061188b565b50949350505050565b6060600080611a41876040015161209a565b90506000611a52886020015161209a565b90506000611a608888612138565b9050611a6b89612241565b611a75888861228e565b6000611a828a8a8a61232e565b9050611a908a8a8a85612b4b565b6000611a9f8b6020015161209a565b60208c01519091506001600160a01b031615611b515783811015611b2b5760405162461bcd60e51b815260206004820152603d60248201527f536f7572636520746f6b656e2062616c616e6365206f6e20636f6e747261637460448201527f206d757374206e6f7420646563726561736520616674657220737761700000006064820152608401610808565b83811115611b4c5760208b0151611b4c90611b468684613801565b33612c14565b611c15565b611b5b3485613801565b811015611bd05760405162461bcd60e51b815260206004820152603d60248201527f536f7572636520746f6b656e2062616c616e6365206f6e20636f6e747261637460448201527f206d757374206e6f7420646563726561736520616674657220737761700000006064820152608401610808565b87611bdb3486613801565b611be5919061334f565b811115611c155760208b0151611c15908986611c01348661334f565b611c0b9190613801565b611b469190613801565b6000611c248c6040015161209a565b90506000611c328783613801565b90508c6101000151811015611cae5760405162461bcd60e51b8152602060048201526024808201527f4f7574707574206973206c657373207468616e206d696e696d756d206578706560448201527f63746564000000000000000000000000000000000000000000000000000000006064820152608401610808565b929c929b50919950505050505050505050565b82610120015161ffff1683600001516001600160a01b03167fc9ca33b4e1816939874cee596ae23410b4f3b26f345e8a93d5779a213ed5ab878560200151866040015187606001518861010001518888604051611d56969594939291906001600160a01b039687168152948616602086015260408501939093526060840191909152608083015290911660a082015260c00190565b60405180910390a3505050565b6060611d728484600085612c38565b90505b9392505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611dc9576040519150601f19603f3d011682016040523d82523d6000602084013e611dce565b606091505b50509050806114a75760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e617469766500000000000000000000006044820152606401610808565b6040516001600160a01b0383166024820152604481018290526114a79084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016106d3565b801580611efb57506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef991906137ab565b155b611f6d5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610808565b6040516001600160a01b0383166024820152604481018290526114a79084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016106d3565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015612020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204491906137ab565b61204e919061334f565b6040516001600160a01b0385166024820152604481018290529091506107379085907f095ea7b300000000000000000000000000000000000000000000000000000000906064016106d3565b60006001600160a01b03821615612131576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212c91906137ab565b610680565b4792915050565b60608160008167ffffffffffffffff81111561215657612156612e33565b60405190808252806020026020018201604052801561217f578160200160208202803683370190505b5090506000805b83811015612236578686828181106121a0576121a06135aa565b90506020028101906121b29190613814565b6121c39060808101906060016135d9565b91506121ce8261209a565b8382815181106121e0576121e06135aa565b60209081029190910101526001600160a01b038216612224573483828151811061220c5761220c6135aa565b602002602001018181516122209190613801565b9052505b8061222e816135f6565b915050612186565b509095945050505050565b600061224c82612d80565b826060015161225b919061334f565b60208301519091506001600160a01b031615612281576105698260200151333084610686565b8034101561056957600080fd5b80366000805b83811015610e36578585828181106122ae576122ae6135aa565b90506020028101906122c09190613814565b92506122d260608401604085016135d9565b91506122e460a0840160808501613852565b80156122f857506001600160a01b03821615155b1561231c5761231c61231060608501604086016135d9565b33308660a00135610686565b80612326816135f6565b915050612294565b60208301516060906001600160a01b0316157f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a60005b848110156125e557816002016000878784818110612384576123846135aa565b90506020028101906123969190613814565b6123a49060208101906135d9565b6001600160a01b0316815260208101919091526040016000205460ff1661240d5760405162461bcd60e51b815260206004820181905260248201527f436f6e7472616374207370656e646572206e6f742077686974656c69737465646044820152606401610808565b816002016000878784818110612425576124256135aa565b90506020028101906124379190613814565b6124489060408101906020016135d9565b6001600160a01b0316815260208101919091526040016000205460ff166124b15760405162461bcd60e51b815260206004820152601f60248201527f436f6e747261637420746172676574206e6f742077686974656c6973746564006044820152606401610808565b60008686838181106124c5576124c56135aa565b90506020028101906124d79190613814565b6124e59060c081019061386f565b6124f4916004916000916138d4565b6124fd916138fe565b9050826003016000888885818110612517576125176135aa565b90506020028101906125299190613814565b61253a9060408101906020016135d9565b6001600160a01b03168152602080820192909252604090810160009081207fffffffff000000000000000000000000000000000000000000000000000000008516825290925290205460ff166125d25760405162461bcd60e51b815260206004820152601760248201527f556e617574686f72697a65642063616c6c2064617461210000000000000000006044820152606401610808565b50806125dd816135f6565b915050612364565b50608086015160a087015160c08801519115159190151590151582806126085750815b156126935783546001600160a01b03166126645760405162461bcd60e51b815260206004820152601c60248201527f46656520636f6e74726163742061646472657373206e6f7420736574000000006044820152606401610808565b61269389602001518a60a001518b60800151612680919061334f565b86546001600160a01b03168860006114ac565b801561270d5760e08901516001600160a01b03166126f35760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420616666696c6961746f7241646472657373000000000000006044820152606401610808565b61270d89602001518a60c001518b60e001518860006114ac565b82806127165750815b8061271e5750805b156127aa5788610120015161ffff168960e001516001600160a01b03167ff14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b8b602001518c608001518d60a001518e60c001516040516127a194939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a35b60008767ffffffffffffffff8111156127c5576127c5612e33565b6040519080825280602002602001820160405280156127f857816020015b60608152602001906001900390816127e35790505b5090506000805b89811015612b3b578a8a82818110612819576128196135aa565b905060200281019061282b9190613814565b61283c9060608101906040016135d9565b91506001600160a01b0382161560008190036128b5576128b5838d8d85818110612868576128686135aa565b905060200281019061287a9190613814565b6128889060208101906135d9565b8e8e8681811061289a5761289a6135aa565b90506020028101906128ac9190613814565b60a00135611618565b60008082612983578d8d858181106128cf576128cf6135aa565b90506020028101906128e19190613814565b6128f29060408101906020016135d9565b6001600160a01b03168e8e8681811061290d5761290d6135aa565b905060200281019061291f9190613814565b61292d9060c081019061386f565b60405161293b929190613946565b6000604051808303816000865af19150503d8060008114612978576040519150601f19603f3d011682016040523d82523d6000602084013e61297d565b606091505b50612a6d565b8d8d85818110612995576129956135aa565b90506020028101906129a79190613814565b6129b89060408101906020016135d9565b6001600160a01b03168e8e868181106129d3576129d36135aa565b90506020028101906129e59190613814565b60a001358f8f878181106129fb576129fb6135aa565b9050602002810190612a0d9190613814565b612a1b9060c081019061386f565b604051612a29929190613946565b60006040518083038185875af1925050503d8060008114612a66576040519150601f19603f3d011682016040523d82523d6000602084013e612a6b565b606091505b505b915091507f2fc0d44e6ef6b3e7707cacd3cc326511198c3d1598c65dd54be5a9e37ce02f128e8e86818110612aa457612aa46135aa565b9050602002810190612ab69190613814565b612ac79060408101906020016135d9565b8383604051612ad893929190613956565b60405180910390a181612b0757612aee81612d9b565b60405162461bcd60e51b81526004016108089190613989565b80868581518110612b1a57612b1a6135aa565b60200260200101819052505050508080612b33906135f6565b9150506127ff565b50909a9950505050505050505050565b60008080805b85811015612c0a57868682818110612b6b57612b6b6135aa565b9050602002810190612b7d9190613814565b612b8e9060808101906060016135d9565b9250612b998361209a565b9150848181518110612bad57612bad6135aa565b602002602001015182612bc09190613801565b9350600084118015612be8575087604001516001600160a01b0316836001600160a01b031614155b15612bf857612bf8838533612c14565b80612c02816135f6565b915050612b51565b5050505050505050565b6001600160a01b03831615612c2e576114a7838284611e1f565b6114a78183611d7c565b606082471015612cb05760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610808565b6001600160a01b0385163b612d075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610808565b600080866001600160a01b03168587604051612d23919061399c565b60006040518083038185875af1925050503d8060008114612d60576040519150601f19603f3d011682016040523d82523d6000602084013e612d65565b606091505b5091509150612d75828286612dfa565b979650505050505050565b60008160a001518260c001518360800151610676919061334f565b6060604482511015612de057505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6004820191508180602001905181019061068091906139ae565b60608315612e09575081611d75565b825115612e195782518084602001fd5b8160405162461bcd60e51b81526004016108089190613989565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612e8657612e86612e33565b60405290565b604051610140810167ffffffffffffffff81118282101715612e8657612e86612e33565b6040516101a0810167ffffffffffffffff81118282101715612e8657612e86612e33565b604051601f8201601f1916810167ffffffffffffffff81118282101715612efd57612efd612e33565b604052919050565b803560028110612f1457600080fd5b919050565b803560058110612f1457600080fd5b6001600160a01b0381168114612f3d57600080fd5b50565b8035612f1481612f28565b600067ffffffffffffffff821115612f6557612f65612e33565b50601f01601f191660200190565b600082601f830112612f8457600080fd5b8135612f97612f9282612f4b565b612ed4565b818152846020838601011115612fac57600080fd5b816020850160208301376000918101602001919091529392505050565b60006101008284031215612fdc57600080fd5b612fe4612e62565b9050612fef82612f05565b8152612ffd60208301612f19565b602082015261300e60408301612f40565b604082015261301f60608301612f40565b606082015261303060808301612f40565b608082015260a082013560a082015260c082013567ffffffffffffffff8082111561305a57600080fd5b61306685838601612f73565b60c084015260e084013591508082111561307f57600080fd5b5061308c84828501612f73565b60e08301525092915050565b61ffff81168114612f3d57600080fd5b8035612f1481613098565b6000808284036101208112156130c857600080fd5b833567ffffffffffffffff8111156130df57600080fd5b6130eb86828701612fc9565b93505061010080601f198301121561310257600080fd5b61310a612e62565b9150602085013561311a81612f28565b825261312860408601612f40565b6020830152606085013560408301526080850135606083015260a0850135608083015261315760c08601612f40565b60a083015260e085013560c08301526131718186016130a8565b60e083015250809150509250929050565b60008083601f84011261319457600080fd5b50813567ffffffffffffffff8111156131ac57600080fd5b6020830191508360208260051b85010111156131c757600080fd5b9250929050565b6000806000808486036101808112156131e657600080fd5b610140808212156131f657600080fd5b6131fe612e8c565b915061320987612f40565b825261321760208801612f40565b602083015261322860408801612f40565b6040830152606087013560608301526080870135608083015260a087013560a083015260c087013560c083015261326160e08801612f40565b60e0830152610100878101359083015261012061327f8189016130a8565b9083015290945085013567ffffffffffffffff8082111561329f57600080fd5b6132ab88838901613182565b90955093506101608701359150808211156132c557600080fd5b506132d287828801612fc9565b91505092959194509250565b600080602083850312156132f157600080fd5b823567ffffffffffffffff81111561330857600080fd5b61331485828601613182565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561068057610680613320565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051612f1481612f28565b805167ffffffffffffffff81168114612f1457600080fd5b805160048110612f1457600080fd5b60005b838110156133de5781810151838201526020016133c6565b50506000910152565b60006133f5612f9284612f4b565b905082815283838301111561340957600080fd5b611d758360208301846133c3565b600082601f83011261342857600080fd5b611d75838351602085016133e7565b805160038110612f1457600080fd5b8051612f1481613098565b60006020828403121561346357600080fd5b815167ffffffffffffffff8082111561347b57600080fd5b908301906101a0828603121561349057600080fd5b613498612eb0565b6134a183613391565b81526134af6020840161339c565b60208201526134c060408401613391565b60408201526134d160608401613391565b60608201526134e260808401613391565b60808201526134f360a08401613391565b60a082015261350460c084016133b4565b60c082015260e08301518281111561351b57600080fd5b61352787828601613417565b60e08301525061010061353b818501613437565b9082015261012061354d848201613446565b90820152610140838101518381111561356557600080fd5b61357188828701613417565b8284015250506101609150613587828401613391565b82820152610180915061359b828401613391565b91810191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156135eb57600080fd5b8135611d7581612f28565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361362757613627613320565b5060010190565b60208082528181018390526000908460408401835b8681101561367157823561365681612f28565b6001600160a01b031682529183019190830190600101613643565b509695505050505050565b60006020828403121561368e57600080fd5b8151611d7581612f28565b600081518084526136b18160208601602086016133c3565b601f01601f19169290920160200192915050565b6001600160a01b038716815260c0602082015260006136e760c0830188613699565b86604084015285606084015282810360808401526137058186613699565b905082810360a08401526137198185613699565b9998505050505050505050565b6001600160a01b038616815260a06020820152600061374860a0830187613699565b85604084015282810360608401526137608186613699565b905082810360808401526137748185613699565b98975050505050505050565b8015158114612f3d57600080fd5b6000602082840312156137a057600080fd5b8151611d7581613780565b6000602082840312156137bd57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156137fc576137fc613320565b500290565b8181038181111561068057610680613320565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2183360301811261384857600080fd5b9190910192915050565b60006020828403121561386457600080fd5b8135611d7581613780565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126138a457600080fd5b83018035915067ffffffffffffffff8211156138bf57600080fd5b6020019150368190038213156131c757600080fd5b600080858511156138e457600080fd5b838611156138f157600080fd5b5050820193919092039150565b7fffffffff00000000000000000000000000000000000000000000000000000000813581811691600485101561393e5780818660040360031b1b83161692505b505092915050565b8183823760009101908152919050565b6001600160a01b038416815282151560208201526060604082015260006139806060830184613699565b95945050505050565b602081526000611d756020830184613699565b600082516138488184602087016133c3565b6000602082840312156139c057600080fd5b815167ffffffffffffffff8111156139d757600080fd5b8201601f810184136139e857600080fd5b6139f7848251602084016133e7565b94935050505056fea2646970667358221220b2c761907109a1a1d81bbfbf2f052805af4d4e216723988ada707c8a5c52036e64736f6c63430008100033
Deployed Bytecode
0x60806040526004361061003f5760003560e01c806395c2d6f314610044578063a6c3d51f14610059578063cc818ad11461006c578063da2ad9471461008c575b600080fd5b6100576100523660046130b3565b6100ac565b005b6100576100673660046131ce565b61031e565b34801561007857600080fd5b506100576100873660046132de565b610557565b34801561009857600080fd5b506100576100a73660046132de565b61056d565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610127576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181556040820151602083015160006101408561065b565b61014a908461334f565b90506001600160a01b0382161561016c5761016782333084610686565b610179565b8034101561017957600080fd5b6101828561073d565b60008651600181111561019757610197613362565b03610231576101a7868385610948565b60e0850151855160808089015160a0808b0151604080516001600160a01b038a81168252602082018c905294851691810191909152606081019190915260009381018490529081019290925261ffff9093169260049216907fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4269060c00160405180910390a4610312565b60008660e0015180602001905181019061024b9190613451565b9050610258878486610e3e565b60e086015161ffff16600460ff1687600001516001600160a01b03167fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d42686888c608001518d60a001516001600060038111156102b6576102b6613362565b8a60c0015160038111156102cc576102cc613362565b604080516001600160a01b039889168152602081019790975294909616858501526060850192909252151560808401529092141560a082015290519081900360c00190a4505b50506000909155505050565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610399576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001815560006103ab868686846110e5565b90506000835160018111156103c2576103c2613362565b03610462576103d683876040015183610948565b610120860151865160408089015160808781015160a0808a015185516001600160a01b039586168152602081018a905292851695830195909552606082019490945260009181018290529283015261ffff9093169260049216907fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4269060c00160405180910390a461054c565b60008360e0015180602001905181019061047c9190613451565b905061048d84886040015184610e3e565b61012087015161ffff16600460ff1688600001516001600160a01b03167fa551f5e7134cc110651fa6eb8a0423535b3ea90eedb01463af70e6798a75d4268a604001518689608001518a60a001516001600060038111156104f0576104f0613362565b8a60c00151600381111561050657610506613362565b604080516001600160a01b039889168152602081019790975294909616858501526060850192909252151560808401529092141560a082015290519081900360c00190a4505b506000905550505050565b61055f6111df565b6105698282611283565b5050565b6105756111df565b7f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c60005b8281101561061c578160008585848181106105b6576105b66135aa565b90506020020160208101906105cb91906135d9565b6001600160a01b03168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905580610614816135f6565b915050610599565b507ff60edbae2829c4ad242912d9f5c804cb71407ecebc5395f229534994239b7f06838360405161064e92919061362e565b60405180910390a1505050565b60008160c0015182608001518360600151610676919061334f565b610680919061334f565b92915050565b6040516001600160a01b03808516602483015283166044820152606481018290526107379085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526113c2565b50505050565b606081015160c0820151608083015191151591901515901515600083806107615750825b806107695750815b905080610777575050505050565b60208501516001600160a01b0316157f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a85806107b05750845b156108405780546001600160a01b03166108115760405162461bcd60e51b815260206004820152601c60248201527f46656520636f6e74726163742061646472657373206e6f74207365740000000060448201526064015b60405180910390fd5b61084087602001518860c00151896060015161082d919061334f565b83546001600160a01b03168560006114ac565b83156108ba5760a08701516001600160a01b03166108a05760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420616666696c6961746f7241646472657373000000000000006044820152606401610808565b6108ba876020015188608001518960a001518560006114ac565b8660e0015161ffff168760a001516001600160a01b03167ff14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b89602001518a606001518b60c001518c6080015160405161093794939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a350505050505050565b60038360200151600481111561096057610960613362565b036109ec5760808301516040517f628d6cba000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0391821660248201529083169063628d6cba906044015b600060405180830381600087803b1580156109cf57600080fd5b505af11580156109e3573d6000803e3d6000fd5b50505050505050565b600483602001516004811115610a0457610a04613362565b03610a5d5760808301516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152602481018390529083169063a9059cbb906044016109b5565b60608301516001600160a01b03811660009081527f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c602081905260409091205460ff16610b125760405162461bcd60e51b815260206004820152602860248201527f52657175657374656420726f757465722061646472657373206e6f742077686960448201527f74656c69737465640000000000000000000000000000000000000000000000006064820152608401610808565b600285602001516004811115610b2a57610b2a613362565b14610b3f57610b3a848385611618565b610b96565b6001600160a01b03841615610b965760405162461bcd60e51b815260206004820152600d60248201527f696e76616c696420746f6b656e000000000000000000000000000000000000006044820152606401610808565b81600086602001516004811115610baf57610baf613362565b03610c7657846001600160a01b031686604001516001600160a01b031614610bd657600080fd5b604080870151608088015160a089015192517f241dc2df0000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152908216602482015260448101879052606481019290925282169063241dc2df906084015b600060405180830381600087803b158015610c5957600080fd5b505af1158015610c6d573d6000803e3d6000fd5b50505050610e36565b600186602001516004811115610c8e57610c8e613362565b03610d8357846001600160a01b031686604001516001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d03919061367c565b6001600160a01b031614610d1657600080fd5b604080870151608088015160a089015192517fedbdf5e20000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152908216602482015260448101879052606481019290925282169063edbdf5e290608401610c3f565b600286602001516004811115610d9b57610d9b613362565b0361003f57604080870151608088015160a089015192517fa5e565710000000000000000000000000000000000000000000000000000000081526001600160a01b0392831660048201529082166024820152604481019290925282169063a5e565719086906064016000604051808303818588803b158015610e1c57600080fd5b505af1158015610e30573d6000803e3d6000fd5b50505050505b505050505050565b60608301516001600160a01b031660009081527f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c602081905260409091205460ff16610ecc5760405162461bcd60e51b815260206004820152601260248201527f726f75746572206e6f7420616c6c6f77656400000000000000000000000000006044820152606401610808565b600284602001516004811115610ee457610ee4613362565b14610efd57610ef883856060015184611618565b610f54565b6001600160a01b03831615610f545760405162461bcd60e51b815260206004820152600d60248201527f696e76616c696420746f6b656e000000000000000000000000000000000000006044820152606401610808565b6060840151600085602001516004811115610f7157610f71613362565b03610fff57806001600160a01b0316636b4b43768660400151610f9788608001516116ec565b868960a001518a60c001518b60e001516040518763ffffffff1660e01b8152600401610fc8969594939291906136c5565b600060405180830381600087803b158015610fe257600080fd5b505af1158015610ff6573d6000803e3d6000fd5b505050506110de565b60018560200151600481111561101757611017613362565b0361103d57806001600160a01b031663e0e9048e8660400151610f9788608001516116ec565b60028560200151600481111561105557611055613362565b0361003f57806001600160a01b031663ea0c968b84876040015161107c89608001516116ec565b8960a001518a60c001518b60e001516040518763ffffffff1660e01b81526004016110ab959493929190613726565b6000604051808303818588803b1580156110c457600080fd5b505af11580156110d8573d6000803e3d6000fd5b50505050505b5050505050565b60208401516000906001600160a01b031615818382611105576000611137565b8760a0015188606001518960c001518a60800151611123919061334f565b61112d919061334f565b611137919061334f565b611141919061334f565b9050803410156111b95760405162461bcd60e51b815260206004820152602960248201527f53656e64206d6f72652045544820746f20636f76657220696e70757420616d6f60448201527f756e74202b2066656500000000000000000000000000000000000000000000006064820152608401610808565b6111c587878787611a2f565b93506111d5905087846000611cc1565b5050949350505050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c600401546001600160a01b031633146112815760405162461bcd60e51b815260206004820152602260248201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60448201527f65720000000000000000000000000000000000000000000000000000000000006064820152608401610808565b565b7f13c8a23e4f93052e4f541b4dd19c72a5c4c1e8d163db3bede27c064fc6d5767c6000805b83811015611382578484828181106112c2576112c26135aa565b90506020020160208101906112d791906135d9565b91506001600160a01b03821661132f5760405162461bcd60e51b815260206004820152601660248201527f496e76616c696420526f757465722041646472657373000000000000000000006044820152606401610808565b6001600160a01b038216600090815260208490526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558061137a816135f6565b9150506112a8565b507f81c16a53fbd623a5a4d8455016c10ed2dac832fbb7577c812df3d615bae7f3aa84846040516113b492919061362e565b60405180910390a150505050565b6000611417826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611d639092919063ffffffff16565b8051909150156114a75780806020019051810190611435919061378e565b6114a75760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610808565b505050565b604080516001600160a01b0387811682526020820187905285168183015290517f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a917fdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef5919081900360600190a1821561160d5781156115fe5760018101546001600160a01b038781169116146115845760405162461bcd60e51b815260206004820152600e60248201527f746f6b656e206d69736d617463680000000000000000000000000000000000006044820152606401610808565b60018101546040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b1580156115e557600080fd5b505af11580156115f9573d6000803e3d6000fd5b505050505b6116088486611d7c565b610e36565b610e36868587611e1f565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015611681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a591906137ab565b9050818110156107375780156116c1576116c184846000611e68565b61073784847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611fb6565b604051606082811b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208301529060009060340160408051601f19818403018152828201909152601082527f3031323334353637383961626364656600000000000000000000000000000000602083015280519092506000906117729060026137c4565b61177d90600261334f565b67ffffffffffffffff81111561179557611795612e33565b6040519080825280601f01601f1916602001820160405280156117bf576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106117f6576117f66135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611859576118596135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060005b8351811015611a26578260048583815181106118a9576118a96135aa565b016020015182517fff0000000000000000000000000000000000000000000000000000000000000090911690911c60f81c9081106118e9576118e96135aa565b01602001517fff00000000000000000000000000000000000000000000000000000000000000168261191c8360026137c4565b61192790600261334f565b81518110611937576119376135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535082848281518110611979576119796135aa565b602091010151815160f89190911c600f16908110611999576119996135aa565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016826119cc8360026137c4565b6119d790600361334f565b815181106119e7576119e76135aa565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611a1f816135f6565b905061188b565b50949350505050565b6060600080611a41876040015161209a565b90506000611a52886020015161209a565b90506000611a608888612138565b9050611a6b89612241565b611a75888861228e565b6000611a828a8a8a61232e565b9050611a908a8a8a85612b4b565b6000611a9f8b6020015161209a565b60208c01519091506001600160a01b031615611b515783811015611b2b5760405162461bcd60e51b815260206004820152603d60248201527f536f7572636520746f6b656e2062616c616e6365206f6e20636f6e747261637460448201527f206d757374206e6f7420646563726561736520616674657220737761700000006064820152608401610808565b83811115611b4c5760208b0151611b4c90611b468684613801565b33612c14565b611c15565b611b5b3485613801565b811015611bd05760405162461bcd60e51b815260206004820152603d60248201527f536f7572636520746f6b656e2062616c616e6365206f6e20636f6e747261637460448201527f206d757374206e6f7420646563726561736520616674657220737761700000006064820152608401610808565b87611bdb3486613801565b611be5919061334f565b811115611c155760208b0151611c15908986611c01348661334f565b611c0b9190613801565b611b469190613801565b6000611c248c6040015161209a565b90506000611c328783613801565b90508c6101000151811015611cae5760405162461bcd60e51b8152602060048201526024808201527f4f7574707574206973206c657373207468616e206d696e696d756d206578706560448201527f63746564000000000000000000000000000000000000000000000000000000006064820152608401610808565b929c929b50919950505050505050505050565b82610120015161ffff1683600001516001600160a01b03167fc9ca33b4e1816939874cee596ae23410b4f3b26f345e8a93d5779a213ed5ab878560200151866040015187606001518861010001518888604051611d56969594939291906001600160a01b039687168152948616602086015260408501939093526060840191909152608083015290911660a082015260c00190565b60405180910390a3505050565b6060611d728484600085612c38565b90505b9392505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611dc9576040519150601f19603f3d011682016040523d82523d6000602084013e611dce565b606091505b50509050806114a75760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e617469766500000000000000000000006044820152606401610808565b6040516001600160a01b0383166024820152604481018290526114a79084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016106d3565b801580611efb57506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef991906137ab565b155b611f6d5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610808565b6040516001600160a01b0383166024820152604481018290526114a79084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016106d3565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015612020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204491906137ab565b61204e919061334f565b6040516001600160a01b0385166024820152604481018290529091506107379085907f095ea7b300000000000000000000000000000000000000000000000000000000906064016106d3565b60006001600160a01b03821615612131576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212c91906137ab565b610680565b4792915050565b60608160008167ffffffffffffffff81111561215657612156612e33565b60405190808252806020026020018201604052801561217f578160200160208202803683370190505b5090506000805b83811015612236578686828181106121a0576121a06135aa565b90506020028101906121b29190613814565b6121c39060808101906060016135d9565b91506121ce8261209a565b8382815181106121e0576121e06135aa565b60209081029190910101526001600160a01b038216612224573483828151811061220c5761220c6135aa565b602002602001018181516122209190613801565b9052505b8061222e816135f6565b915050612186565b509095945050505050565b600061224c82612d80565b826060015161225b919061334f565b60208301519091506001600160a01b031615612281576105698260200151333084610686565b8034101561056957600080fd5b80366000805b83811015610e36578585828181106122ae576122ae6135aa565b90506020028101906122c09190613814565b92506122d260608401604085016135d9565b91506122e460a0840160808501613852565b80156122f857506001600160a01b03821615155b1561231c5761231c61231060608501604086016135d9565b33308660a00135610686565b80612326816135f6565b915050612294565b60208301516060906001600160a01b0316157f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a60005b848110156125e557816002016000878784818110612384576123846135aa565b90506020028101906123969190613814565b6123a49060208101906135d9565b6001600160a01b0316815260208101919091526040016000205460ff1661240d5760405162461bcd60e51b815260206004820181905260248201527f436f6e7472616374207370656e646572206e6f742077686974656c69737465646044820152606401610808565b816002016000878784818110612425576124256135aa565b90506020028101906124379190613814565b6124489060408101906020016135d9565b6001600160a01b0316815260208101919091526040016000205460ff166124b15760405162461bcd60e51b815260206004820152601f60248201527f436f6e747261637420746172676574206e6f742077686974656c6973746564006044820152606401610808565b60008686838181106124c5576124c56135aa565b90506020028101906124d79190613814565b6124e59060c081019061386f565b6124f4916004916000916138d4565b6124fd916138fe565b9050826003016000888885818110612517576125176135aa565b90506020028101906125299190613814565b61253a9060408101906020016135d9565b6001600160a01b03168152602080820192909252604090810160009081207fffffffff000000000000000000000000000000000000000000000000000000008516825290925290205460ff166125d25760405162461bcd60e51b815260206004820152601760248201527f556e617574686f72697a65642063616c6c2064617461210000000000000000006044820152606401610808565b50806125dd816135f6565b915050612364565b50608086015160a087015160c08801519115159190151590151582806126085750815b156126935783546001600160a01b03166126645760405162461bcd60e51b815260206004820152601c60248201527f46656520636f6e74726163742061646472657373206e6f7420736574000000006044820152606401610808565b61269389602001518a60a001518b60800151612680919061334f565b86546001600160a01b03168860006114ac565b801561270d5760e08901516001600160a01b03166126f35760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420616666696c6961746f7241646472657373000000000000006044820152606401610808565b61270d89602001518a60c001518b60e001518860006114ac565b82806127165750815b8061271e5750805b156127aa5788610120015161ffff168960e001516001600160a01b03167ff14fbd8b6e3ad3ae34babfa1f3b6a099f57643662f4cfc24eb335ae8718f534b8b602001518c608001518d60a001518e60c001516040516127a194939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a35b60008767ffffffffffffffff8111156127c5576127c5612e33565b6040519080825280602002602001820160405280156127f857816020015b60608152602001906001900390816127e35790505b5090506000805b89811015612b3b578a8a82818110612819576128196135aa565b905060200281019061282b9190613814565b61283c9060608101906040016135d9565b91506001600160a01b0382161560008190036128b5576128b5838d8d85818110612868576128686135aa565b905060200281019061287a9190613814565b6128889060208101906135d9565b8e8e8681811061289a5761289a6135aa565b90506020028101906128ac9190613814565b60a00135611618565b60008082612983578d8d858181106128cf576128cf6135aa565b90506020028101906128e19190613814565b6128f29060408101906020016135d9565b6001600160a01b03168e8e8681811061290d5761290d6135aa565b905060200281019061291f9190613814565b61292d9060c081019061386f565b60405161293b929190613946565b6000604051808303816000865af19150503d8060008114612978576040519150601f19603f3d011682016040523d82523d6000602084013e61297d565b606091505b50612a6d565b8d8d85818110612995576129956135aa565b90506020028101906129a79190613814565b6129b89060408101906020016135d9565b6001600160a01b03168e8e868181106129d3576129d36135aa565b90506020028101906129e59190613814565b60a001358f8f878181106129fb576129fb6135aa565b9050602002810190612a0d9190613814565b612a1b9060c081019061386f565b604051612a29929190613946565b60006040518083038185875af1925050503d8060008114612a66576040519150601f19603f3d011682016040523d82523d6000602084013e612a6b565b606091505b505b915091507f2fc0d44e6ef6b3e7707cacd3cc326511198c3d1598c65dd54be5a9e37ce02f128e8e86818110612aa457612aa46135aa565b9050602002810190612ab69190613814565b612ac79060408101906020016135d9565b8383604051612ad893929190613956565b60405180910390a181612b0757612aee81612d9b565b60405162461bcd60e51b81526004016108089190613989565b80868581518110612b1a57612b1a6135aa565b60200260200101819052505050508080612b33906135f6565b9150506127ff565b50909a9950505050505050505050565b60008080805b85811015612c0a57868682818110612b6b57612b6b6135aa565b9050602002810190612b7d9190613814565b612b8e9060808101906060016135d9565b9250612b998361209a565b9150848181518110612bad57612bad6135aa565b602002602001015182612bc09190613801565b9350600084118015612be8575087604001516001600160a01b0316836001600160a01b031614155b15612bf857612bf8838533612c14565b80612c02816135f6565b915050612b51565b5050505050505050565b6001600160a01b03831615612c2e576114a7838284611e1f565b6114a78183611d7c565b606082471015612cb05760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610808565b6001600160a01b0385163b612d075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610808565b600080866001600160a01b03168587604051612d23919061399c565b60006040518083038185875af1925050503d8060008114612d60576040519150601f19603f3d011682016040523d82523d6000602084013e612d65565b606091505b5091509150612d75828286612dfa565b979650505050505050565b60008160a001518260c001518360800151610676919061334f565b6060604482511015612de057505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6004820191508180602001905181019061068091906139ae565b60608315612e09575081611d75565b825115612e195782518084602001fd5b8160405162461bcd60e51b81526004016108089190613989565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612e8657612e86612e33565b60405290565b604051610140810167ffffffffffffffff81118282101715612e8657612e86612e33565b6040516101a0810167ffffffffffffffff81118282101715612e8657612e86612e33565b604051601f8201601f1916810167ffffffffffffffff81118282101715612efd57612efd612e33565b604052919050565b803560028110612f1457600080fd5b919050565b803560058110612f1457600080fd5b6001600160a01b0381168114612f3d57600080fd5b50565b8035612f1481612f28565b600067ffffffffffffffff821115612f6557612f65612e33565b50601f01601f191660200190565b600082601f830112612f8457600080fd5b8135612f97612f9282612f4b565b612ed4565b818152846020838601011115612fac57600080fd5b816020850160208301376000918101602001919091529392505050565b60006101008284031215612fdc57600080fd5b612fe4612e62565b9050612fef82612f05565b8152612ffd60208301612f19565b602082015261300e60408301612f40565b604082015261301f60608301612f40565b606082015261303060808301612f40565b608082015260a082013560a082015260c082013567ffffffffffffffff8082111561305a57600080fd5b61306685838601612f73565b60c084015260e084013591508082111561307f57600080fd5b5061308c84828501612f73565b60e08301525092915050565b61ffff81168114612f3d57600080fd5b8035612f1481613098565b6000808284036101208112156130c857600080fd5b833567ffffffffffffffff8111156130df57600080fd5b6130eb86828701612fc9565b93505061010080601f198301121561310257600080fd5b61310a612e62565b9150602085013561311a81612f28565b825261312860408601612f40565b6020830152606085013560408301526080850135606083015260a0850135608083015261315760c08601612f40565b60a083015260e085013560c08301526131718186016130a8565b60e083015250809150509250929050565b60008083601f84011261319457600080fd5b50813567ffffffffffffffff8111156131ac57600080fd5b6020830191508360208260051b85010111156131c757600080fd5b9250929050565b6000806000808486036101808112156131e657600080fd5b610140808212156131f657600080fd5b6131fe612e8c565b915061320987612f40565b825261321760208801612f40565b602083015261322860408801612f40565b6040830152606087013560608301526080870135608083015260a087013560a083015260c087013560c083015261326160e08801612f40565b60e0830152610100878101359083015261012061327f8189016130a8565b9083015290945085013567ffffffffffffffff8082111561329f57600080fd5b6132ab88838901613182565b90955093506101608701359150808211156132c557600080fd5b506132d287828801612fc9565b91505092959194509250565b600080602083850312156132f157600080fd5b823567ffffffffffffffff81111561330857600080fd5b61331485828601613182565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561068057610680613320565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8051612f1481612f28565b805167ffffffffffffffff81168114612f1457600080fd5b805160048110612f1457600080fd5b60005b838110156133de5781810151838201526020016133c6565b50506000910152565b60006133f5612f9284612f4b565b905082815283838301111561340957600080fd5b611d758360208301846133c3565b600082601f83011261342857600080fd5b611d75838351602085016133e7565b805160038110612f1457600080fd5b8051612f1481613098565b60006020828403121561346357600080fd5b815167ffffffffffffffff8082111561347b57600080fd5b908301906101a0828603121561349057600080fd5b613498612eb0565b6134a183613391565b81526134af6020840161339c565b60208201526134c060408401613391565b60408201526134d160608401613391565b60608201526134e260808401613391565b60808201526134f360a08401613391565b60a082015261350460c084016133b4565b60c082015260e08301518281111561351b57600080fd5b61352787828601613417565b60e08301525061010061353b818501613437565b9082015261012061354d848201613446565b90820152610140838101518381111561356557600080fd5b61357188828701613417565b8284015250506101609150613587828401613391565b82820152610180915061359b828401613391565b91810191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156135eb57600080fd5b8135611d7581612f28565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361362757613627613320565b5060010190565b60208082528181018390526000908460408401835b8681101561367157823561365681612f28565b6001600160a01b031682529183019190830190600101613643565b509695505050505050565b60006020828403121561368e57600080fd5b8151611d7581612f28565b600081518084526136b18160208601602086016133c3565b601f01601f19169290920160200192915050565b6001600160a01b038716815260c0602082015260006136e760c0830188613699565b86604084015285606084015282810360808401526137058186613699565b905082810360a08401526137198185613699565b9998505050505050505050565b6001600160a01b038616815260a06020820152600061374860a0830187613699565b85604084015282810360608401526137608186613699565b905082810360808401526137748185613699565b98975050505050505050565b8015158114612f3d57600080fd5b6000602082840312156137a057600080fd5b8151611d7581613780565b6000602082840312156137bd57600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156137fc576137fc613320565b500290565b8181038181111561068057610680613320565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2183360301811261384857600080fd5b9190910192915050565b60006020828403121561386457600080fd5b8135611d7581613780565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126138a457600080fd5b83018035915067ffffffffffffffff8211156138bf57600080fd5b6020019150368190038213156131c757600080fd5b600080858511156138e457600080fd5b838611156138f157600080fd5b5050820193919092039150565b7fffffffff00000000000000000000000000000000000000000000000000000000813581811691600485101561393e5780818660040360031b1b83161692505b505092915050565b8183823760009101908152919050565b6001600160a01b038416815282151560208201526060604082015260006139806060830184613699565b95945050505050565b602081526000611d756020830184613699565b600082516138488184602087016133c3565b6000602082840312156139c057600080fd5b815167ffffffffffffffff8111156139d757600080fd5b8201601f810184136139e857600080fd5b6139f7848251602084016133e7565b94935050505056fea2646970667358221220b2c761907109a1a1d81bbfbf2f052805af4d4e216723988ada707c8a5c52036e64736f6c63430008100033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in GLMR
Multichain Portfolio | 32 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.