Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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 IERC20PermitUpgradeable {
/**
* @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.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20Upgradeable 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(IERC20Upgradeable 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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20PermitUpgradeable 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(IERC20Upgradeable 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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
// EIP-2612 is Final as of 2022-11-01. This file is deprecated.
import "./IERC20Permit.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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.9.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.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/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;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
import "../helpers/Constants.sol";
import "../helpers/Errors.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/// @title BridgeAdaptorBase
/// @notice All Bridge adaptor must implement it
/// @dev All Bridge adaptor must implement it
abstract contract BridgeAdaptorBase is Ownable {
using SafeERC20 for IERC20;
address public immutable xBridge;
mapping(address => bool) public routers;
constructor(address _xBridge, address[] memory _routersList) {
require(_xBridge != address(0), XBridgeErrors.ADDRESS_0);
xBridge = _xBridge;
for (uint256 i = 0; i < _routersList.length; i++) {
routers[_routersList[i]] = true;
}
}
//-------------------------------
//------- Events ----------------
//-------------------------------
event LogOutboundBridgeTo(address _from, address _to, address _token, uint256 _amount, bytes32 _extraData);
event EmergencyWithdraw(address indexed _to, address _token, uint amount);
//-------------------------------
//------- Modifier --------------
//-------------------------------
modifier onlyXBridge() {
require(msg.sender == xBridge, XBridgeErrors.ONLY_X_BRIDGE);
_;
}
//-------------------------------
//------- Internal Functions ----
//-------------------------------
function _approve(address token, address spender, uint256 amount) internal {
if (IERC20(token).allowance(address(this), spender) == 0) {
IERC20(token).safeApprove(spender, amount);
} else {
IERC20(token).safeApprove(spender, 0);
IERC20(token).safeApprove(spender, amount);
}
}
function _approve2(address token, address spender, uint256 amount) internal {
uint256 preAllowance = IERC20(token).allowance(address(this), spender);
if (preAllowance == 0) {
IERC20(token).safeApprove(spender, type(uint256).max);
} else if (preAllowance < amount){
IERC20(token).safeApprove(spender, 0);
IERC20(token).safeApprove(spender, type(uint256).max);
}
}
//-------------------------------
//------- Admin functions -------
//-------------------------------
function setRouters(address[] calldata _routersList, bool[] calldata _v) public onlyOwner {
for (uint256 i = 0; i < _routersList.length; i++) {
routers[_routersList[i]] = _v[i];
}
}
// workaround for a possible solidity bug
function withdrawEmergency(address _to, address _token, uint _amount) public onlyOwner {
if (_token == XBridgeConstants.NATIVE_TOKEN) {
payable(_to).transfer(_amount);
} else {
IERC20(_token).safeTransfer(_to, _amount);
}
emit EmergencyWithdraw(_to, _token, _amount);
}
//-------------------------------
//------- Users Functions -------
//-------------------------------
function outboundBridgeTo(
address _from,
address _to,
address _refundAddress,
address _token,
uint256 _amount,
uint256 _toChainId,
bytes memory _data
) external payable virtual;
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library XBridgeConstants {
address public constant NATIVE_TOKEN = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @dev WETH address is network-specific and needs to be changed before deployment.
/// It can not be moved to immutable as immutables are not supported in assembly
// ETH: C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
// BSC: bb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
// OEC: 8F8526dbfd6E38E3D8307702cA8469Bae6C56C15
// LOCAL: 5FbDB2315678afecb367f032d93F642f64180aa3
// LOCAL2: 02121128f1Ed0AdA5Df3a87f42752fcE4Ad63e59
// POLYGON: 0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270
// AVAX: B31f66AA3C1e785363F0875A1B74E27b85FD66c7
// FTM: 21be370D5312f44cB42ce377BC9b8a0cEF1A4C83
// ARB: 82aF49447D8a07e3bd95BD0d56f35241523fBab1
// OP: 4200000000000000000000000000000000000006
// CRO: 5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23
// CFX: 14b2D3bC65e74DAE1030EAFd8ac30c533c976A9b
// POLYZK 4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9
// MANTA 0Dc808adcE2099A9F62AA87D9670745AbA741746
// METIS 75cb093E4D61d2A2e65D8e0BBb01DE8d89b53481
// MERLIN F6D226f9Dc15d9bB51182815b320D3fBE324e1bA
// ZETA 5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf
// MODE 4200000000000000000000000000000000000006
// XLAYER e538905cf8410324e03A5A23C1c177a474D59b2b
// SONIC 039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38
address public constant WETH = 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38;
// ETH: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58
// ETH-DEV:02D0131E5Cc86766e234EbF1eBe33444443b98a3
// BSC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98
// OEC: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF
// LOCAL: e7f1725E7734CE288F8367e1Bb143E90bb3F0512
// LOCAL2: 95D7fF1684a8F2e202097F28Dc2e56F773A55D02
// POLYGON: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f
// AVAX: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58
// FTM: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF
// ARB: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF
// ARB_DEV: eDC3a1C195591968488cA2E41E54d5Ac6c8016e2
// OP: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a
// CRO: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF
// CFX: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a
// POLYZK 1b5d39419C268b76Db06DE49e38B010fbFB5e226
// MANTA 1b5d39419C268b76Db06DE49e38B010fbFB5e226
// METIS 1b5d39419C268b76Db06DE49e38B010fbFB5e226
// MERLIN fAd6a9eEe5b32E9B81bb217BaeF37742B2ca5B83
// ZETA 8009858707810928CCe2c3526B78A4Eb8043888c
// MODE 6A4F2a37f188eC7079854cA7C9C0E1181f858A69
// XLAYER fAd6a9eEe5b32E9B81bb217BaeF37742B2ca5B83
// SONIC CFF09e645a627B8208ef67dA294d0eDC2d255517
address public constant APPROVE_PROXY = 0xCFF09e645a627B8208ef67dA294d0eDC2d255517;
// ETH: 5703B683c7F928b721CA95Da988d73a3299d4757
// BSC: 0B5f474ad0e3f7ef629BD10dbf9e4a8Fd60d9A48
// OEC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98
// LOCAL: D49a0e9A4CD5979aE36840f542D2d7f02C4817Be
// LOCAL2: 11457D5b1025D162F3d9B7dBeab6E1fBca20e043
// POLYGON: f332761c673b59B21fF6dfa8adA44d78c12dEF09
// AVAX: 3B86917369B83a6892f553609F3c2F439C184e31
// FTM: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f
// ARB: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98
// ARB_DEV: C183cFF4aC3B6D2b9405D856143C35a36E4C8710
// OP: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f
// CRO: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f
// CFX: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f
// POLYZK d2F0aC2012C8433F235c8e5e97F2368197DD06C7
// MANTA d2F0aC2012C8433F235c8e5e97F2368197DD06C7
// METIS d2F0aC2012C8433F235c8e5e97F2368197DD06C7
// MERLIN 38a342664aBE254Fc2Ca4A98d85eaB7C5aE63c85
// ZETA 4CD5e737D1aD509573f6924465168e54417F013E
// MODE 897a69668aDa305A7FB9FE3318e14f90dD76D070
// XLAYER 38a342664aBE254Fc2Ca4A98d85eaB7C5aE63c85
// SONIC 0007ef1Cde141326b0da51c83CBAc3F6fEaF507C
address public constant WNATIVE_RELAY = 0x0007ef1Cde141326b0da51c83CBAc3F6fEaF507C;
// sysRatio
uint256 public constant GAS_TOKEN_RECEIVE_MAX_INDEX = 1;
uint256 public constant CLAIM_TOKEN_RATIO_MAX_INDEX = 2;
uint256 public constant CHAIN_ID_INDEX = 3;
// sysAddressConfig
// uint256 public constant ORACLE_ADDRESS_INDEX = 1; // deprecated
uint256 public constant DEFAULT_RATIO_BASE = 100;
uint256 public constant ADAPTER_ID_ANYSWAP = 1;
uint256 public constant ADAPTER_ID_CBRIDGE = 2;
uint256 public constant ADAPTER_ID_SWFT = 3;
string public constant __REFUND__ = string("__refund__");
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library XBridgeErrors {
string internal constant ONLY_X_BRIDGE = "only XBridge";
string internal constant ONLY_MPC = "only mpc";
string internal constant ONLY_ADMIN = "only admin";
string internal constant ADDRESS_0 = "address 0";
string internal constant LENGTH_NOT_EQUAL = "length not equal";
string internal constant DEX_ROUTER_ERR = "dex router err : ";
string internal constant ADDRESS_EQUAL = "address equal";
string internal constant ADDRESS_NOT_EQUAL = "address not equal";
string internal constant MIN_AMOUNT_ERR = "min amount err";
string internal constant REFUND_ETH_ERROR = "refund eth err";
string internal constant REFUND_EXIST = "refund exist";
string internal constant CBRIDGE_HAS_WITHDRAW = "has withdraw";
string internal constant HAS_PAID = "has paid";
string internal constant HAS_RECEIVE_GAS = "has receive gas";
string internal constant NO_ENOUGH_MONEY = "no enough money";
string internal constant SLASH_MUCH_TOO_MONEY = "slash much too money";
string internal constant ERROR_SELECTOR_ID = "err selector id";
string internal constant EXCEED_ALLOWED_GAS = "exceed allowed gas";
string internal constant ALLOWANCE_NOT_ENOUGH = "allowance not enough";
string internal constant ORACLE_NO_INFO = "claim no oracle info";
string internal constant ORACLE_TO_ADDRESS_ERR = "claim to address err";
string internal constant ORACLE_TOKEN_ADDRESS_ERR = "claim token address err";
string internal constant ORACLE_TOKEN_AMOUNT_ERR = "claim token amount err";
string internal constant NOT_ORACLE_PROXY = "not oracle proxy";
string internal constant ERR_CHAIN_ID = "err chain id";
string internal constant ZERO_SIGNER = "zero signer";
string internal constant CONTRACT_ADDRESS_ERROR = "contract address error";
string internal constant INTERNAL_WRAP_FAIL = "internal wrap fail";
string internal constant WRAP_AMOUNT_ZERO = "wrap amount must be > 0";
string internal constant TRANSFER_ETH_FAILD = "ETH transfer failed";
string internal constant AMOUNT_ZERO = "amount must be > 0";
string internal constant MIN_AMOUNT_ZERO = "min amount must be > 0";
string internal constant LEFT_VALUE_NOT_ZERO = "left value must be 0";
string internal constant NOT_SUPPORT_CHAIN = "not support chain";
string internal constant NOT_SUPPORT_TOKEN = "not support token";
string internal constant AMOUNT_NOT_EQ_VALUE = "amount must == msg.value";
string internal constant VALUE_NOT_ENOUGH = "amount must <= msg.value";
string internal constant VALUE_MUST_ZERO = "msg.value == 0";
string internal constant INVALID_ADAPTOR_ID = "invalid adaptorID";
string internal constant INVALID_ADAPTOR_ADDRESS = "invalid adaptor address";
string internal constant INVALID_ROUTER = "invalid router";
string internal constant INVALID_MSG_VALUE = "invalid msg value";
string internal constant COMMISSION_ERROR_RATE = "error commission rate limit";
string internal constant COMMISSION_ERROR_ETHER = "commission with ether error";
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IApproveProxy {
function owner() external view returns (address);
function isAllowedProxy(address _proxy) external view returns (bool);
function claimTokens(
address token,
address who,
address dest,
uint256 amount
) external;
function tokenApprove() external view returns (address);
function addProxy(address _newProxy) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Interface for DAI-style permits
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
interface IWETH {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address src,
address dst,
uint256 wad
) external returns (bool);
function deposit() external payable;
function withdraw(uint256 wad) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
interface IWNativeRelayer {
function owner() external view returns (address);
function withdraw(uint256 _amount) external;
function setCallerOk(address[] calldata whitelistedCallers, bool isOk) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Bytes {
function bytesToBytes32Array(bytes memory data)
internal
pure
returns (bytes32[] memory dataList)
{
uint256 N = (data.length + 31) / 32;
dataList = new bytes32[](N);
for (uint256 index = 0; index < N; index++) {
bytes32 element;
uint256 start = 32 + index * 32;
assembly {
element := mload(add(data, start))
}
dataList[index] = element;
}
}
}
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract CommissionLib {
/**
* commissionInfo uint256
* commissionInfo = flag + commissionRate + referrerAddress
* [ 48 bits | 48 bits | 160 bits ]
* [ flag | commissionRate | referrerAddress ]
* [ MSB LSB ]
*/
uint256 internal constant _REFERRER_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
uint256 internal constant _COMMISSION_FEE_MASK = 0x000000000000ffffffffffff0000000000000000000000000000000000000000;
uint256 internal constant _COMMISSION_FLAG_MASK = 0xffffffffffff0000000000000000000000000000000000000000000000000000;
uint256 internal constant OKX_COMMISSION = 0x3ca20afc2aaa0000000000000000000000000000000000000000000000000000;
event CommissionRecord(uint256 commissionAmount, address referrerAddress);
// set default vaule. can change when need.
uint256 public constant commissionRateLimit = 300;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Library that allows to parse unsuccessful arbitrary calls revert reasons.
/// See https://solidity.readthedocs.io/en/latest/control-structures.html#revert for details.
/// Note that we assume revert reason being abi-encoded as Error(string) so it may fail to parse reason
/// if structured reverts appear in the future.
///
/// All unsuccessful parsings get encoded as Unknown(data) string
library RevertReasonParser {
bytes4 private constant _PANIC_SELECTOR =
bytes4(keccak256("Panic(uint256)"));
bytes4 private constant _ERROR_SELECTOR =
bytes4(keccak256("Error(string)"));
function parse(bytes memory data, string memory prefix)
internal
pure
returns (string memory)
{
if (data.length >= 4) {
bytes4 selector;
assembly {
// solhint-disable-line no-inline-assembly
selector := mload(add(data, 0x20))
}
// 68 = 4-byte selector + 32 bytes offset + 32 bytes length
if (selector == _ERROR_SELECTOR && data.length >= 68) {
uint256 offset;
bytes memory reason;
// solhint-disable no-inline-assembly
assembly {
// 36 = 32 bytes data length + 4-byte selector
offset := mload(add(data, 36))
reason := add(data, add(36, offset))
}
/*
revert reason is padded up to 32 bytes with ABI encoder: Error(string)
also sometimes there is extra 32 bytes of zeros padded in the end:
https://github.com/ethereum/solidity/issues/10170
because of that we can't check for equality and instead check
that offset + string length + extra 36 bytes is less than overall data length
*/
require(
data.length >= 36 + offset + reason.length,
"Invalid revert reason"
);
return string(abi.encodePacked(prefix, "Error(", reason, ")"));
}
// 36 = 4-byte selector + 32 bytes integer
else if (selector == _PANIC_SELECTOR && data.length == 36) {
uint256 code;
// solhint-disable no-inline-assembly
assembly {
// 36 = 32 bytes data length + 4-byte selector
code := mload(add(data, 36))
}
return
string(
abi.encodePacked(prefix, "Panic(", _toHex(code), ")")
);
}
}
return string(abi.encodePacked(prefix, "Unknown(", _toHex(data), ")"));
}
function _toHex(uint256 value) private pure returns (string memory) {
return _toHex(abi.encodePacked(value));
}
function _toHex(bytes memory data) private pure returns (string memory) {
bytes16 alphabet = 0x30313233343536373839616263646566;
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 * i + 2] = alphabet[uint8(data[i] >> 4)];
str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)];
}
return string(str);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "./adaptor/BridgeAdaptorBase.sol";
import "./helpers/Constants.sol";
import "./helpers/Errors.sol";
import "./interfaces/IDaiLikePermit.sol";
import "./interfaces/IApproveProxy.sol";
import "./interfaces/IWNativeRelayer.sol";
import "./interfaces/IWETH.sol";
import "./libraries/Bytes.sol";
import "./libraries/RevertReasonParser.sol";
import "./libraries/CommissionLib.sol";
/**
* @title XBridge
* @notice Entrance for Bridge
* - Users can:
* # Bridge: Initiate cross-chain asset transfers.
* # Swap and bridge: Perform token swaps and initiate cross-chain transfers.
* @dev XBridge is a smart contract that serves as the entrance for cross-chain operations,
* allowing users to interact with various functionalities such as bridging assets,
* swapping and bridging tokens, and claiming assets on the destination chain.
*/
contract XBridge is PausableUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, CommissionLib {
using SafeERC20Upgradeable for IERC20Upgradeable;
// Struct representing the information needed for a bridge transaction
struct BridgeRequestV2 {
uint256 adaptorId;
address to;
address token;
uint256 toChainId; // orderId[64bit] | gasFeeAmount[160] | target chainId[32bit]
uint256 amount;
bytes data; // router data
bytes extData;
}
// Struct representing the information needed for a swap and bridge transaction
struct SwapBridgeRequestV2 {
address fromToken; // the source token
address toToken; // the token to be bridged
address to; // the address to be bridged to
uint256 adaptorId;
uint256 toChainId; // orderId[64bit] | gasFeeAmount[160] | target chainId[32bit]
uint256 fromTokenAmount; // the source token amount
uint256 toTokenMinAmount;
uint256 toChainToTokenMinAmount;
bytes data; // router data
bytes dexData; // the call data for dexRouter
bytes extData;
}
// Struct representing the information needed for a swap transaction
struct SwapRequest {
address fromToken;
address toToken;
address to;
uint256 amount; // amount of swapped fromToken
uint256 gasFeeAmount; // tx gas fee slash from fromToken
uint256 srcChainId;
bytes32 srcTxHash;
bytes dexData;
bytes extData;
}
// Struct representing the information needed for receiving gas tokens on another chain
struct ReceiveGasRequest {
address to;
uint256 amount;
uint256 srcChainId;
bytes32 srcTxHash;
bytes extData;
}
// Struct representing a threshold configuration for a specific address
struct Threshold {
bool opened;
uint256 amount;
}
// Struct representing information related to an oracle, used for verifying certain transactions
struct OracleInfo {
uint256 srcChainId;
bytes32 txHash;
bytes32 to;
bytes32 token;
uint256 amount;
uint256 actualAmount;
}
//-------------------------------
//------- storage ---------------
//-------------------------------
mapping(uint256 => address) public adaptorInfo;
/**
* @dev This state variable is deprecated and should not be used anymore.
*/
address public approveProxy;
address public dexRouter;
address public payer;
address public receiver;
address public feeTo;
address public admin;
mapping(address => bool) public mpc;
mapping(uint256 => mapping(bytes32 => bool)) public paidTx;
mapping(uint256 => mapping(bytes32 => bool)) public receiveGasTx;
/**
* @dev Set by admin
*/
mapping(uint256 => uint256) public sysRatio;
/**
* @dev This state variable is deprecated and should not be used anymore.
*/
mapping(uint256 => address) public sysAddressConfig;
mapping(address => Threshold) public thresholdConfig;
mapping(address => bool) public proxies; // oracle proxy
mapping(bytes4 => bool) public accessSelectorId; // for swap
/**
* @notice Initializes the XBridge contract.
* @dev This function is part of the Upgradable pattern and is called once to initialize contract state.
* It sets up the initial state by invoking the initializers of the inherited contracts.
* The `admin` variable is set to the address of the account that deploys the contract.
* Note: This function is meant to be called only once during the contract deployment.
*/
function initialize() public initializer {
__Pausable_init();
__ReentrancyGuard_init();
__Ownable_init();
admin = msg.sender;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
//-------------------------------
//------- Events ----------------
//-------------------------------
event DexRouterChanged(address _dexRouter);
/**
* @notice Event emitted when a bridge transaction occurs
*/
event LogBridgeTo(
uint256 indexed _adaptorId,
address _from,
address _to,
address _token,
uint256 _amount,
uint256 _receiveFee,
bytes32[] ext
);
/**
* @notice Event emitted when a swap and bridge transaction occurs
*/
event LogSwapAndBridgeTo(
uint256 indexed _adaptorId,
address _from,
address _to,
address _fromToken,
uint256 _fromAmount,
address _toToken,
uint256 _toAmount,
uint256 _receiveFee,
bytes32[] ext
);
event FeeToChanged(address _feeTo);
event AdminChanged(address _newAdmin);
event GasTokenReceived(
address to,
uint256 amount,
uint256 srcChainId,
bytes32[] ext
);
/**
* @notice Event emitted when a claim transaction occurs
*/
event Claimed(
address to,
address fromToken,
address toToken,
uint256 fromTokenAmount,
uint256 toTokenAmount,
uint256 gasFeeAmount,
uint256 srcChainId,
string errInfo,
bytes32[] ext
);
event AdaptorsChanged(uint256 indexed _adaptorId, address _adaptor);
event MpcChanged(address _mpc, bool _enable);
event SysRatioChanged(uint256 _index, uint256 _ratio);
event ProxiesChanged(address _proxy, bool _enable);
event AccessSelectorIdChanged(bytes4 _selectorId, bool _enable);
//-------------------------------
//------- Modifier --------------
//-------------------------------
modifier onlyMPC() {
require(mpc[msg.sender], XBridgeErrors.ONLY_MPC);
_;
}
modifier onlyAdmin() {
require(msg.sender == admin, XBridgeErrors.ONLY_ADMIN);
_;
}
//-------------------------------
//------- Internal Functions ----
//-------------------------------
/**
* @notice Internal pure function to extract information from a packed uint256 value representing gas receive details
* @param toChainId Packed uint256 value containing order ID, gas fee amount, and chain ID
*/
function _getGasReceiveAmount(uint256 toChainId)
internal
pure
returns (
uint256 orderId,
uint256 gasFeeAmount,
uint256 chainId
)
{
orderId = (toChainId & 0xffffffffffffffff000000000000000000000000000000000000000000000000) >> 192;
gasFeeAmount = (toChainId & 0x0000000000000000ffffffffffffffffffffffffffffffffffffffff00000000) >> 32;
chainId = toChainId & 0x00000000000000000000000000000000000000000000000000000000ffffffff;
}
/**
* @notice Internal function to perform a token deposit operation.
* @dev Ensures that the caller has sufficient allowance to deposit the specified amount of tokens.
* @param from The address from which tokens are transferred.
* @param to The recipient address to receive the deposited tokens.
* @param token The address of the ERC20 token being deposited.
* @param amount The amount of tokens to be deposited.
*/
function _deposit(
address from,
address to,
address token,
uint256 amount
) internal {
IApproveProxy(XBridgeConstants.APPROVE_PROXY).claimTokens(token, from, to, amount);
}
function _getBalanceOf(address token) internal view returns (uint256) {
return _getBalanceOf(token, address(this));
}
function _getBalanceOf(address token, address who) internal view returns(uint256) {
return token == XBridgeConstants.NATIVE_TOKEN ? who.balance : IERC20Upgradeable(token).balanceOf(who);
}
/**
* @notice Internal function to transfer ERC20 tokens or native tokens (ETH) to a specified address.
* @param to The address to which tokens are transferred.
* @param token The address of the ERC20 token to be transferred.
* @param amount The amount of tokens to be transferred.
*/
function _transferToken(address to, address token, uint256 amount) internal {
if (amount > 0) {
if (token == XBridgeConstants.NATIVE_TOKEN) {
(bool success, ) = payable(to).call{value: amount}("");
require(success, XBridgeErrors.TRANSFER_ETH_FAILD);
} else {
IERC20Upgradeable(token).safeTransfer(to, amount);
}
}
}
/**
* @notice Internal pure function to construct extension data for cross-chain transaction.
* @param orderId The unique identifier for the cross-chain transaction.
* @param toChainId The identifier of the target chain.
* @param adaptorId The identifier of the cross-chain adaptor used.
* @param to The destination address on the target chain.
* @param data Additional data specific to the cross-chain adaptor.
* @param extData Additional extension data containing user-specific information.
* @return ext An array of bytes32 values representing the constructed extension data.
*/
function _constructExt(uint256 orderId, uint256 toChainId, uint256 adaptorId, address to, bytes memory data, bytes memory extData)
internal
pure
returns(bytes32[] memory ext)
{
ext = new bytes32[](6);
ext[0] = bytes32(orderId);
ext[1] = bytes32(toChainId);
if (adaptorId == XBridgeConstants.ADAPTER_ID_ANYSWAP
|| adaptorId == XBridgeConstants.ADAPTER_ID_CBRIDGE) {
ext[2] = bytes32(abi.encodePacked(to));
ext[3] = bytes32(abi.encodePacked(""));
} else if (adaptorId == XBridgeConstants.ADAPTER_ID_SWFT) {
(,,string memory destination,) = abi.decode(data, (address, string, string, uint256));
bytes32[] memory destBytes32Arr = Bytes.bytesToBytes32Array(bytes(destination));
ext[2] = destBytes32Arr[0];
if (destBytes32Arr.length > 1) {
ext[3] = destBytes32Arr[1];
}
}
if (extData.length > 0) {
(string memory userAddress) = abi.decode(extData, (string));
bytes32[] memory userAddressBytes32Arr = Bytes.bytesToBytes32Array(bytes(userAddress));
ext[4] = userAddressBytes32Arr[0];
if (userAddressBytes32Arr.length > 1) {
ext[5] = userAddressBytes32Arr[1];
}
} else {
ext[4] = ext[2];
ext[5] = ext[3];
}
return ext;
}
/**
* @notice Struct to represent the result of a commission operation.
*/
struct CommissionReturn {
uint256 commissionAmount; // commission amount
bytes extDataWithoutLast32; // extData without last 32 bytes
}
/**
* @notice Internal function to initiate a cross-chain transaction using the specified BridgeRequestV2 parameters.
* @param _request The BridgeRequestV2 struct containing transaction details.
* @dev Performs necessary validations, token transfers, and calls the outboundBridgeTo function on the selected adaptor.
*/
function _bridgeToV2Internal(BridgeRequestV2 memory _request) internal {
require(_request.adaptorId != 0, XBridgeErrors.INVALID_ADAPTOR_ID);
address adaptor = adaptorInfo[_request.adaptorId];
require(adaptor != address(0), XBridgeErrors.INVALID_ADAPTOR_ADDRESS);
require(_request.to != address(0), XBridgeErrors.ADDRESS_0);
require(_request.token != address(0), XBridgeErrors.ADDRESS_0);
require(_request.amount != 0, XBridgeErrors.AMOUNT_ZERO);
// doCommission
CommissionReturn memory vars;
(vars.commissionAmount, vars.extDataWithoutLast32) = _doCommission(_request.amount, _request.token, _request.extData);
_request.extData = vars.extDataWithoutLast32;
uint256 ethValue = msg.value;
// Extract gas fee details from toChainId
(uint256 orderId, uint256 gasFeeAmount, uint256 toChainId) = _getGasReceiveAmount(_request.toChainId);
if (_request.token == XBridgeConstants.NATIVE_TOKEN) {
require(msg.value >= _request.amount + gasFeeAmount + vars.commissionAmount, XBridgeErrors.INVALID_MSG_VALUE);
ethValue -= vars.commissionAmount; // after docommission
if (gasFeeAmount > 0) {
(bool success, ) = payable(feeTo).call{value: gasFeeAmount}("");
require(success, XBridgeErrors.TRANSFER_ETH_FAILD);
ethValue -= gasFeeAmount;
}
} else {
if (gasFeeAmount > 0) {
_deposit(msg.sender, feeTo, _request.token, gasFeeAmount);
}
_deposit(msg.sender, adaptor, _request.token, _request.amount);
}
// Call the outboundBridgeTo function on the selected adaptor
BridgeAdaptorBase(payable(adaptor)).outboundBridgeTo{value : ethValue}(
msg.sender,
_request.to,
msg.sender, // refund to msg.sender
_request.token,
_request.amount,
toChainId,
_request.data
);
// Construct extension data and emit the LogBridgeTo event
bytes32[] memory ext = _constructExt(
orderId,
toChainId,
_request.adaptorId,
_request.to,
_request.data,
_request.extData
);
emit LogBridgeTo(
_request.adaptorId,
msg.sender,
_request.to,
_request.token,
_request.amount,
gasFeeAmount,
ext
);
}
/**
* @notice Struct to represent the result of a cross-chain bridge operation.
* @dev Holds information about the adaptor, token balances, gas fee, chain ID, order ID, success status, function selector, and result data.
*/
struct BridgeVariants {
address adaptor;
uint256 toTokenBalance;
uint256 toTokenBalanceOrigin;
uint256 gasFeeAmount;
uint256 toChainId;
uint256 orderId;
bool success;
bytes4 selectorId;
bytes result;
}
/**
* @notice Internal function to perform a token swap and bridge operation using the specified SwapBridgeRequestV2 parameters.
* @param _request The SwapBridgeRequestV2 struct containing swap and bridge details.
* @dev Performs necessary validations, token swaps, bridge calls, and balance checks.
*/
function _swapBridgeToInternal(SwapBridgeRequestV2 memory _request) internal {
BridgeVariants memory vars;
require(_request.adaptorId != 0, XBridgeErrors.INVALID_ADAPTOR_ID);
vars.adaptor = adaptorInfo[_request.adaptorId];
require(vars.adaptor != address(0), XBridgeErrors.INVALID_ADAPTOR_ADDRESS);
require(_request.fromToken != address(0), XBridgeErrors.ADDRESS_0);
require(_request.toToken != address(0), XBridgeErrors.ADDRESS_0);
require(_request.fromToken != _request.toToken, XBridgeErrors.ADDRESS_EQUAL);
require(_request.to != address(0), XBridgeErrors.ADDRESS_0);
require(dexRouter != address(0), XBridgeErrors.ADDRESS_0);
require(_request.fromTokenAmount != 0, XBridgeErrors.AMOUNT_ZERO);
require(_request.toTokenMinAmount != 0, XBridgeErrors.MIN_AMOUNT_ZERO);
// Extract gas fee details from toChainId
(vars.orderId, vars.gasFeeAmount, vars.toChainId) = _getGasReceiveAmount(_request.toChainId);
vars.toTokenBalanceOrigin = _getBalanceOf(_request.toToken);
// Validate the dexData function selector
require(accessSelectorId[bytes4(_request.dexData)], XBridgeErrors.ERROR_SELECTOR_ID);
// Set payer and receiver addresses for potential refund
payer = msg.sender;
receiver = address(this);
// doCommission
(uint256 commissionAmount, bytes memory extDataWithoutLast32) = _doCommission(_request.fromTokenAmount, _request.fromToken, _request.extData);
_request.extData = extDataWithoutLast32;
// 1. prepare and swap
if (_request.fromToken == XBridgeConstants.NATIVE_TOKEN) { //FROM NATIVE
require(msg.value - commissionAmount >= _request.fromTokenAmount, XBridgeErrors.INVALID_MSG_VALUE);
if (_request.toToken == XBridgeConstants.WETH) { //ETH => WETH
vars.success = _swapWrap(address(this), address(this), _request.fromTokenAmount, false);
} else { // ETH => ERC20, use dexRouter
(vars.success, vars.result) = dexRouter.call{value : _request.fromTokenAmount}(_request.dexData);
}
} else { // FROM ERC20
if (_request.fromToken == XBridgeConstants.WETH && _request.toToken == XBridgeConstants.NATIVE_TOKEN) {
// WETH => ETH
vars.success = _swapWrap(msg.sender, address(this), _request.fromTokenAmount, true);
} else { // ERC20 => ERC20, use dexRouter
(vars.success, vars.result) = dexRouter.call(_request.dexData);
}
}
delete payer;
delete receiver;
// 2. check result and balance
require(vars.success,vars.result.length == 0 ? XBridgeErrors.INTERNAL_WRAP_FAIL : RevertReasonParser.parse(vars.result, XBridgeErrors.DEX_ROUTER_ERR));
vars.toTokenBalance = _getBalanceOf(_request.toToken) - vars.toTokenBalanceOrigin; // toToken added
require(vars.toTokenBalance >= vars.gasFeeAmount + _request.toTokenMinAmount, XBridgeErrors.MIN_AMOUNT_ERR);
// 3. Receive to token for relay gas token on target chain to user
_transferToken(feeTo, _request.toToken, vars.gasFeeAmount);
// 4. Bridge the toToken to the target chain
vars.toTokenBalance = vars.toTokenBalance - vars.gasFeeAmount;
if (_request.toToken == XBridgeConstants.NATIVE_TOKEN) {
// Internal with BridgeAdaptorBase, so it is safe to use payable
BridgeAdaptorBase(payable(vars.adaptor)).outboundBridgeTo{
value: vars.toTokenBalance + msg.value
}(
msg.sender,
_request.to,
msg.sender, // refund to msg.sender
_request.toToken,
vars.toTokenBalance,
vars.toChainId,
_request.data
);
} else {
_transferToken(vars.adaptor, _request.toToken, vars.toTokenBalance);
if (_request.fromToken == XBridgeConstants.NATIVE_TOKEN){
BridgeAdaptorBase(payable(vars.adaptor)).outboundBridgeTo{value : msg.value - commissionAmount - _request.fromTokenAmount }(
msg.sender,
_request.to,
msg.sender, // refund to msg.sender
_request.toToken,
vars.toTokenBalance,
vars.toChainId,
_request.data
);
} else {
BridgeAdaptorBase(payable(vars.adaptor)).outboundBridgeTo{value : msg.value }(
msg.sender,
_request.to,
msg.sender, // refund to msg.sender
_request.toToken,
vars.toTokenBalance,
vars.toChainId,
_request.data
);
}
}
// Construct extension data and emit the LogBridgeTo event
bytes32[] memory ext = _constructExt(
vars.orderId,
vars.toChainId,
_request.adaptorId,
_request.to,
_request.data,
_request.extData
);
emit LogSwapAndBridgeTo(
_request.adaptorId,
msg.sender,
_request.to,
_request.fromToken,
_request.fromTokenAmount,
_request.toToken,
vars.toTokenBalance,
vars.gasFeeAmount,
ext
);
// 5. Check balance
if (_request.toToken == XBridgeConstants.NATIVE_TOKEN){
// if toToken equal nativeToken, should add msg.value
require(_getBalanceOf(_request.toToken) + msg.value >= vars.toTokenBalanceOrigin, XBridgeErrors.SLASH_MUCH_TOO_MONEY);
} else {
require(_getBalanceOf(_request.toToken) >= vars.toTokenBalanceOrigin, XBridgeErrors.SLASH_MUCH_TOO_MONEY);
}
}
/**
* @notice Internal function to execute a permit on an ERC20 token if a permit data is provided.
* @param token Address of the ERC20 token.
* @param permit Permit data containing the necessary parameters for the permit function.
*/
function _permit(address token, bytes calldata permit) internal {
if (permit.length > 0) {
bool success;
bytes memory result;
if (permit.length == 32 * 7) {
// solhint-disable-next-line avoid-low-level-calls
(success, result) = token.call(abi.encodePacked(IERC20Permit.permit.selector, permit));
} else if (permit.length == 32 * 8) {
// solhint-disable-next-line avoid-low-level-calls
(success, result) = token.call(abi.encodePacked(IDaiLikePermit.permit.selector, permit));
} else {
revert("Wrong permit length");
}
if (!success) {
revert(RevertReasonParser.parse(result, "Permit failed: "));
}
}
}
/**
* @notice Internal function to receive gas tokens from the source chain and transfer them to the specified recipient.
* @param _request The ReceiveGasRequest struct containing details about the gas token receipt.
* @dev Performs necessary validations, updates state, and emits the GasTokenReceived event.
*/
function _receiveGasTokenInternal(ReceiveGasRequest memory _request) internal {
require(_request.amount <= sysRatio[XBridgeConstants.GAS_TOKEN_RECEIVE_MAX_INDEX], XBridgeErrors.EXCEED_ALLOWED_GAS);
require(!receiveGasTx[_request.srcChainId][_request.srcTxHash], XBridgeErrors.HAS_RECEIVE_GAS);
receiveGasTx[_request.srcChainId][_request.srcTxHash] = true;
_transferToken(_request.to, XBridgeConstants.NATIVE_TOKEN, _request.amount);
bytes32[] memory ext = new bytes32[](1);
ext[0] = _request.srcTxHash;
emit GasTokenReceived(_request.to, _request.amount, _request.srcChainId, ext);
}
/**
* @notice Internal function to decode a message and its signature to extract relevant information.
* @param _message The encoded message containing information about the oracle request.
* @param _signature The signature of the message for authentication.
* @return source The address of the message sender recovered from the signature.
* @return thisChainId The chain ID of this contract.
* @return thisContractAddress The address of this contract.
* @return oracleInfo An OracleInfo struct containing details of the oracle request.
* @dev Decodes the message and signature to extract source address, chain ID, contract address, and oracle request details.
*/
function _decode(bytes memory _message, bytes memory _signature)
internal
pure
returns (
address source,
uint256 thisChainId,
address thisContractAddress,
OracleInfo memory oracleInfo
)
{
{ // fix Stack too deep
(bytes32 r, bytes32 s, uint8 v) = abi.decode(_signature, (bytes32, bytes32, uint8));
bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(_message)));
source = ecrecover(hash, v, r, s);
}
(
thisChainId,
thisContractAddress,
oracleInfo.srcChainId,
oracleInfo.txHash,
oracleInfo.to,
oracleInfo.token,
oracleInfo.amount,
oracleInfo.actualAmount
) = abi.decode(_message, (uint256, address, uint256, bytes32, bytes32, bytes32, uint256, uint256));
return (source, thisChainId, thisContractAddress, oracleInfo);
}
/**
* @notice Internal function to verify the oracle signature and details for a swap request.
* @param _request The SwapRequest struct containing swap details.
* @param _amount The amount to be verified against the oracle threshold.
* @dev Verifies the oracle signature, source address, and additional details for the swap request.
*/
function _verifyOracle(
SwapRequest memory _request,
uint256 _amount
)
view
internal
{
(bytes memory message, bytes memory signature) = abi.decode(_request.extData, (bytes, bytes));
(
address source,
uint256 thisChainId,
address thisContractAddress,
OracleInfo memory oracleInfo
) = _decode(message, signature);
// Validate the source address, oracle proxy status, chain ID, contract address, and request details
require(source != address(0), XBridgeErrors.ZERO_SIGNER);
require(proxies[source], XBridgeErrors.NOT_ORACLE_PROXY);
require(thisChainId == sysRatio[XBridgeConstants.CHAIN_ID_INDEX], XBridgeErrors.ERR_CHAIN_ID);
require(thisContractAddress == address(this), XBridgeErrors.CONTRACT_ADDRESS_ERROR);
require(_request.srcTxHash == oracleInfo.txHash, XBridgeErrors.ORACLE_NO_INFO);
require(_request.to == address(uint160(uint256(oracleInfo.to))), XBridgeErrors.ORACLE_TO_ADDRESS_ERR);
require(_request.fromToken == address(uint160(uint256(oracleInfo.token))), XBridgeErrors.ORACLE_TOKEN_ADDRESS_ERR);
// Calculate the high threshold based on the actualAmount and configured ratio
uint256 ratio = sysRatio[XBridgeConstants.CLAIM_TOKEN_RATIO_MAX_INDEX];
uint256 high = oracleInfo.actualAmount * (ratio + XBridgeConstants.DEFAULT_RATIO_BASE) / XBridgeConstants.DEFAULT_RATIO_BASE;
// Check if the requested amount is within the allowed high threshold
require(_amount <= high, XBridgeErrors.ORACLE_TOKEN_AMOUNT_ERR);
}
/**
* @notice Internal function to process the claim for a swap request, including gas fee handling and token transfer.
* @param _request The SwapRequest struct containing swap details.
* @dev Verifies the oracle, handles gas fees, performs token swap or transfer and emits the Claimed event.
*/
function _claimInternal(SwapRequest memory _request) internal {
uint256 fromTokenOriginBalance = _getBalanceOf(_request.fromToken);
// Calculate the total amount needed, including swap amount and gas fees
uint256 fromTokenNeed = _request.amount + _request.gasFeeAmount;
// Verify the oracle signature and threshold for the source token
_verifyOracle(_request, fromTokenNeed);
require(fromTokenOriginBalance >= fromTokenNeed, XBridgeErrors.NO_ENOUGH_MONEY);
require(dexRouter != address(0), XBridgeErrors.ADDRESS_0);
require(!paidTx[_request.srcChainId][_request.srcTxHash], XBridgeErrors.HAS_PAID);
paidTx[_request.srcChainId][_request.srcTxHash] = true;
// Initialize extension data for the Claimed event
bytes32[] memory ext = new bytes32[](1);
ext[0] = _request.srcTxHash;
bool success;
bytes memory result;
string memory errInfo;
// 1. Handle gas fee
_transferToken(feeTo, _request.fromToken, _request.gasFeeAmount);
// 2. Perform token swap or transfer to the user
if (_request.dexData.length > 0) {
// swap
uint256 toTokenReceiverBalance = _getBalanceOf(_request.toToken, _request.to);
// Exchange anypair using the dexRouter except WETH<=>ETH
payer = address(this);
receiver = _request.to;
if (_request.fromToken == XBridgeConstants.NATIVE_TOKEN) { // FROM NATIVE
if (_request.toToken == XBridgeConstants.WETH) { // ETH => WETH
success = _swapWrap(address(this), _request.to, _request.amount, false);
if (!success) {
errInfo = XBridgeErrors.INTERNAL_WRAP_FAIL;
}
} else { // ETH => ERC20, use dexRouter
(success, result) = dexRouter.call{value : _request.amount}(_request.dexData);
}
} else { // FROM ERC20
if (_request.fromToken == XBridgeConstants.WETH && _request.toToken == XBridgeConstants.NATIVE_TOKEN) {
// WETH => ETH
success =_swapWrap(address(this), _request.to, _request.amount, true);
if (!success) {
errInfo = XBridgeErrors.INTERNAL_WRAP_FAIL;
}
} else { // ERC20 => ERC20, use dexRouter
address tokenApprove = IApproveProxy(XBridgeConstants.APPROVE_PROXY).tokenApprove();
IERC20Upgradeable(_request.fromToken).safeApprove(tokenApprove, _request.amount);
(success, result) = dexRouter.call(_request.dexData);
if (IERC20Upgradeable(_request.fromToken).allowance(address(this), tokenApprove) != 0){
IERC20Upgradeable(_request.fromToken).safeApprove(tokenApprove, 0);
}
}
}
if (!success && result.length > 0) {
errInfo = RevertReasonParser.parse(result, XBridgeErrors.DEX_ROUTER_ERR);
}
delete payer; // payer = 0;
delete receiver;
if (!success) { // transfer fromToken if swap failed
_transferToken(_request.to, _request.fromToken, _request.amount);
emit Claimed(_request.to, _request.fromToken, _request.toToken, _request.amount, 0, _request.gasFeeAmount, _request.srcChainId, errInfo, ext);
} else {
toTokenReceiverBalance = _getBalanceOf(_request.toToken, _request.to) - toTokenReceiverBalance;
emit Claimed(_request.to, _request.fromToken, _request.toToken, 0, toTokenReceiverBalance, _request.gasFeeAmount, _request.srcChainId, errInfo, ext);
}
} else { // transfer token
errInfo = XBridgeConstants.__REFUND__;
_transferToken(_request.to, _request.fromToken, _request.amount);
emit Claimed(_request.to, _request.fromToken, _request.toToken, _request.amount, 0, _request.gasFeeAmount, _request.srcChainId, errInfo, ext);
}
// 3. Check the final balance of the source token
require(fromTokenOriginBalance - _getBalanceOf(_request.fromToken) <= fromTokenNeed, XBridgeErrors.SLASH_MUCH_TOO_MONEY);
}
/**
* @dev Internal function to swap and wrap tokens.
* @param from The address to transfer the tokens from.
* @param to The address to transfer the wrapped tokens to.
* @param amount The amount of tokens to swap and wrap.
* @param reversed Boolean indicating whether the swap is reversed (WETH => ETH).
* @return A boolean indicating the success of the swap and wrap operation.
*/
function _swapWrap(
address from,
address to,
uint256 amount,
bool reversed
) internal returns (bool) {
require(amount > 0, XBridgeErrors.WRAP_AMOUNT_ZERO);
if (reversed) {
// reversed == true: WETH => ETH
if (from == address(this)){
IWETH(address(uint160(XBridgeConstants.WETH))).transfer(XBridgeConstants.WNATIVE_RELAY, amount);
} else {
_deposit(from, XBridgeConstants.WNATIVE_RELAY, XBridgeConstants.WETH, amount);
}
IWNativeRelayer(XBridgeConstants.WNATIVE_RELAY).withdraw(amount);
if (to != address(this)){
(bool success, ) = payable(to).call{value: amount}("");
require(success, XBridgeErrors.TRANSFER_ETH_FAILD);
}
} else {
// reversed == false: ETH => WETH
IWETH(XBridgeConstants.WETH).deposit{value: amount}();
if (to != address(this)){
IERC20Upgradeable(XBridgeConstants.WETH).safeTransfer(to, amount);
}
}
return true;
}
/**
* @notice Internal function to handle commission logic
* @param inputAmount The amount of tokens to be transferred.
* @param commissionToken The address of the ERC20 token to be transferred.
* @param extData Additional extension data containing user-specific information.
* @return commissionAmount The amount of commission tokens to be transferred.
* @return extDataWithoutLast32 Additional extension data containing user-specific information without last 32 bytes.
*/
function _doCommission( uint256 inputAmount, address commissionToken, bytes memory extData) internal returns (uint256 commissionAmount, bytes memory extDataWithoutLast32) {
// Retrieve commission info from the last 32 bytes of extData
uint256 commissionInfo;
assembly {
commissionInfo := calldataload(sub(calldatasize(),0x20))
}
if ((commissionInfo & _COMMISSION_FLAG_MASK) == OKX_COMMISSION) {
// 0. decode the commissionInfo
address referrerAddress = address(uint160(commissionInfo & _REFERRER_MASK));
uint256 commissionRate = uint256((commissionInfo & _COMMISSION_FEE_MASK) >> 160);
// 1. Check the commission ratio. CommissionFeeAmount = fromTokenAmount * Rate / (10000 - Rate)
require(commissionRate <= commissionRateLimit, XBridgeErrors.COMMISSION_ERROR_RATE);
commissionAmount = (inputAmount * commissionRate) / (10000 - commissionRate);
// 2. Perform commission
if (commissionToken == XBridgeConstants.NATIVE_TOKEN) {
(bool success,) = payable(referrerAddress).call{value: commissionAmount}("");
require(success, XBridgeErrors.COMMISSION_ERROR_ETHER);
} else {
_deposit(msg.sender, referrerAddress, commissionToken, commissionAmount);
}
// 3. Restore extData
uint256 extDataSize = extData.length;
extDataWithoutLast32 = new bytes(extDataSize - 32);
for (uint256 i = 0; i < extDataSize - 32; i++) {
extDataWithoutLast32[i] = extData[i];
}
emit CommissionRecord(commissionAmount, referrerAddress);
} else {
extDataWithoutLast32 = extData;
}
}
//-------------------------------
//------- Admin functions -------
//-------------------------------
function setAdmin(address _newAdmin) external onlyOwner {
require(_newAdmin != address(0), XBridgeErrors.ADDRESS_0);
admin = _newAdmin;
emit AdminChanged(_newAdmin);
}
function setDexRouter(address _newDexRouter) external onlyAdmin {
require(_newDexRouter != address(0), XBridgeErrors.ADDRESS_0);
dexRouter = _newDexRouter;
emit DexRouterChanged(_newDexRouter);
}
function pause() external onlyAdmin {
_pause();
}
function unpause() external onlyAdmin {
_unpause();
}
function setAdaptors(uint256[] calldata _ids, address[] calldata _adaptors) external onlyAdmin {
require(_ids.length == _adaptors.length, XBridgeErrors.LENGTH_NOT_EQUAL);
for (uint256 i = 0; i < _ids.length; i++) {
adaptorInfo[_ids[i]] = _adaptors[i];
emit AdaptorsChanged(_ids[i], _adaptors[i]);
}
}
function setFeeTo(address _newFeeTo) external onlyAdmin {
require(_newFeeTo != address(0), XBridgeErrors.ADDRESS_0);
feeTo = _newFeeTo;
emit FeeToChanged(_newFeeTo);
}
function setMpc(address[] memory _mpcList, bool[] memory _v) external onlyAdmin {
require(_mpcList.length == _v.length, XBridgeErrors.LENGTH_NOT_EQUAL);
for (uint256 i = 0; i < _mpcList.length; i++) {
mpc[_mpcList[i]] = _v[i];
emit MpcChanged(_mpcList[i], _v[i]);
}
}
function setSysRatio(uint256 _index, uint256 _v) external onlyAdmin {
sysRatio[_index] = _v;
emit SysRatioChanged(_index, _v);
}
function setProxies(address[] memory proxiesList, bool[] memory values)
external
onlyAdmin
{
require(proxiesList.length == values.length, XBridgeErrors.LENGTH_NOT_EQUAL);
for (uint256 i = 0; i < proxiesList.length; i++) {
proxies[proxiesList[i]] = values[i];
emit ProxiesChanged(proxiesList[i], values[i]);
}
}
function setAccessSelectorId(bytes4[] memory selectorIds, bool[] memory values) external onlyAdmin{
require(selectorIds.length == values.length, XBridgeErrors.LENGTH_NOT_EQUAL);
for (uint256 i = 0; i < selectorIds.length; i++) {
accessSelectorId[selectorIds[i]] = values[i];
emit AccessSelectorIdChanged(selectorIds[i], values[i]);
}
}
//-------------------------------
//------- Users Functions -------
//-------------------------------
/**
* @notice Initiates the bridge operation to transfer assets to another chain using the bridge.
* @param _request The BridgeRequestV2 struct containing the details of the bridge operation.
*/
function bridgeToV2(BridgeRequestV2 memory _request)
external
payable
nonReentrant
whenNotPaused
{
_bridgeToV2Internal(_request);
}
/**
* @notice Initiates a swap and bridge operation using the bridge.
* @param _request The SwapBridgeRequestV2 struct containing the details of the swap and bridge operation.
*/
function swapBridgeToV2(SwapBridgeRequestV2 memory _request)
public
payable
nonReentrant
whenNotPaused
{
_swapBridgeToInternal(_request);
}
/**
* @notice Initiates a swap and bridge operation with permit using V2 of the bridge.
* @param _request The SwapBridgeRequestV2 struct containing the details of the swap and bridge operation.
* @param _signature The permit signature for the fromToken.
*/
function swapBridgeToWithPermit(
SwapBridgeRequestV2 calldata _request,
bytes calldata _signature
) external nonReentrant whenNotPaused {
_permit(_request.fromToken, _signature);
_swapBridgeToInternal(_request);
}
/**
* @notice Completed receiving gas tokens from the source chain.
* @param _request The ReceiveGasRequest struct containing the details of this operation.
*/
function receiveGasToken(ReceiveGasRequest memory _request)
public
payable
nonReentrant
whenNotPaused
onlyMPC
{
require(msg.value == _request.amount, XBridgeErrors.INVALID_MSG_VALUE);
_receiveGasTokenInternal(_request);
}
/**
* @notice Claims the assets on the current chain as part of the cross-chain swap.
* @param _request The SwapRequest struct containing details of the asset claiming operation.
*/
function claim(SwapRequest memory _request)
public
nonReentrant
whenNotPaused
onlyMPC
{
_claimInternal(_request);
}
/**
* @notice Performs batch operations including Gas Token receiving and asset claiming.
* @param _gasRequest Array of ReceiveGasRequest structs containing details of Gas Token receiving operations.
* @param _claimRequest Array of SwapRequest structs containing details of asset claiming operations.
*/
function doBatch(ReceiveGasRequest[] memory _gasRequest, SwapRequest[] memory _claimRequest)
public
payable
nonReentrant
whenNotPaused
onlyMPC
{
uint256 leftValue = msg.value;
for (uint256 i = 0; i < _gasRequest.length; i++) {
_receiveGasTokenInternal(_gasRequest[i]);
// DOES NOT need check, because it will overflow if less than amount
leftValue -= _gasRequest[i].amount;
}
for (uint256 i = 0; i < _claimRequest.length; i++) {
_claimInternal(_claimRequest[i]);
}
require(leftValue == 0, XBridgeErrors.LEFT_VALUE_NOT_ZERO);
}
function payerReceiver() external view returns(address, address) {
return (payer, receiver);
}
receive() external payable {}
}