Address Contract Verified
Address
0x1493e7B8d4DfADe0a178dAD9335470337A3a219A
Balance
0.026293 ETH
Nonce
1
Code Size
7367 bytes
Creator
0xC6e63Fb7...0455 at tx 0x636cfb83...e9c6b0
Indexed Transactions
0
Contract Bytecode
7367 bytes
0x6080604052600436106100c05760003560e01c8063bcf225e611610074578063e30c39781161004e578063e30c39781461022c578063f2fde38b14610259578063fb214c2f1461027957600080fd5b8063bcf225e6146101a3578063c34c08e5146101e5578063d0a102601461021957600080fd5b80637200b829116100a55780637200b82914610103578063797b7f6b146101185780638da5cb5b1461017657600080fd5b806323452b9c146100cc5780632e144579146100e357600080fd5b366100c757005b600080fd5b3480156100d857600080fd5b506100e16102ad565b005b3480156100ef57600080fd5b506100e16100fe36600461156b565b610377565b34801561010f57600080fd5b506100e16104a9565b34801561012457600080fd5b5061014c7f0000000000000000000000001a44076050125825900e736c501f859c50fe728c81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561018257600080fd5b5060005461014c9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156101af57600080fd5b506101d77f00000000000000000000000000000000000000000000000000000000000186a081565b60405190815260200161016d565b3480156101f157600080fd5b5061014c7f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d81565b6100e16102273660046115f5565b61058f565b34801561023857600080fd5b5060015461014c9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561026557600080fd5b506100e1610274366004611696565b6107ab565b34801561028557600080fd5b5061014c7f0000000000000000000000006d6620efa72948c5f68a3c8646d58c00d3f4a98081565b60005473ffffffffffffffffffffffffffffffffffffffff1633146102fe576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff1661034d576040517f75cdea1200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103c8576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166104835760008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d806000811461043d576040519150601f19603f3d011682016040523d82523d6000602084013e610442565b606091505b505090508061047d576040517f350c20f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6104a473ffffffffffffffffffffffffffffffffffffffff84168383610909565b505050565b60015473ffffffffffffffffffffffffffffffffffffffff163381146104fb576040517f1853971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600180549091169055565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000001a44076050125825900e736c501f859c50fe728c16146105fe576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fd8e8dbc700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88811660048301527f0000000000000000000000006d6620efa72948c5f68a3c8646d58c00d3f4a980169063d8e8dbc7906024016020604051808303816000875af115801561068c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b091906116b3565b61ffff166000036106ed576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008773ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561073a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075e91906116e7565b9050600080600061076f89896109dd565b8060200190518101906107829190611861565b92509250925061079e838386846107998e8e610a2a565b610a4a565b5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146107fc576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116610849576040517f1beca37400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603610898576040517fbf1ea9fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526104a49084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152610fa8565b60606109ec82604c81866119ee565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293505050505b92915050565b6000610a3a602c600c84866119ee565b610a4391611a18565b9392505050565b60005a905073ffffffffffffffffffffffffffffffffffffffff8416610d53577f00000000000000000000000000000000000000000000000000000000000186a0811015610b935760008373ffffffffffffffffffffffffffffffffffffffff168360405160006040518083038185875af1925050503d8060008114610aec576040519150601f19603f3d011682016040523d82523d6000602084013e610af1565b606091505b5050905080610b2c576040517f350c20f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff80881682528616602082015290810184905242606082015287907f1fbfa988fd46deed0de12c94c7b5dcb537d51b804246d0083f245f7a8997d1709060800160405180910390a25050610fa1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d16634f91bc2b83610bfa7f00000000000000000000000000000000000000000000000000000000000186a085611a83565b90898989896040518763ffffffff1660e01b8152600401610c1e9493929190611ae0565b6000604051808303818589803b158015610c3757600080fd5b5088f19450505050508015610c4a575060015b610d4e5760008373ffffffffffffffffffffffffffffffffffffffff168360405160006040518083038185875af1925050503d8060008114610ca8576040519150601f19603f3d011682016040523d82523d6000602084013e610cad565b606091505b5050905080610ce8576040517f350c20f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff80881682528616602082015290810184905242606082015287907f1fbfa988fd46deed0de12c94c7b5dcb537d51b804246d0083f245f7a8997d1709060800160405180910390a250610f9f565b610f9f565b83610d9673ffffffffffffffffffffffffffffffffffffffff82167f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d60006110bc565b7f00000000000000000000000000000000000000000000000000000000000186a0821015610ddf57610b2c73ffffffffffffffffffffffffffffffffffffffff82168585610909565b610e2073ffffffffffffffffffffffffffffffffffffffff82167f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d8561123e565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d16634f91bc2b610e867f00000000000000000000000000000000000000000000000000000000000186a085611a83565b898989896040518663ffffffff1660e01b8152600401610ea99493929190611ae0565b600060405180830381600088803b158015610ec357600080fd5b5087f193505050508015610ed5575060015b610f5b57610efa73ffffffffffffffffffffffffffffffffffffffff82168585610909565b6040805173ffffffffffffffffffffffffffffffffffffffff80881682528616602082015290810184905242606082015287907f1fbfa988fd46deed0de12c94c7b5dcb537d51b804246d0083f245f7a8997d1709060800160405180910390a25b610f9d73ffffffffffffffffffffffffffffffffffffffff82167f0000000000000000000000002dfadab8266483bed9fd9a292ce56596a2d1378d60006110bc565b505b505b5050505050565b600061100a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166113379092919063ffffffff16565b905080516000148061102b57508080602001905181019061102b9190611c1b565b6104a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b80158061115c57506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611136573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115a9190611c36565b155b6111e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084016110b3565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526104a49084907f095ea7b3000000000000000000000000000000000000000000000000000000009060640161095b565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301526000919085169063dd62ed3e90604401602060405180830381865afa1580156112b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d89190611c36565b905061047d847f095ea7b3000000000000000000000000000000000000000000000000000000008561130a8686611c4f565b60405173ffffffffffffffffffffffffffffffffffffffff9092166024830152604482015260640161095b565b6060611346848460008561134e565b949350505050565b6060824710156113e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016110b3565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516114099190611c62565b60006040518083038185875af1925050503d8060008114611446576040519150601f19603f3d011682016040523d82523d6000602084013e61144b565b606091505b509150915061145c87838387611467565b979650505050505050565b606083156114fd5782516000036114f65773ffffffffffffffffffffffffffffffffffffffff85163b6114f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110b3565b5081611346565b61134683838151156115125781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110b39190611c7e565b73ffffffffffffffffffffffffffffffffffffffff8116811461156857600080fd5b50565b60008060006060848603121561158057600080fd5b833561158b81611546565b9250602084013561159b81611546565b929592945050506040919091013590565b60008083601f8401126115be57600080fd5b50813567ffffffffffffffff8111156115d657600080fd5b6020830191508360208285010111156115ee57600080fd5b9250929050565b600080600080600080600060a0888a03121561161057600080fd5b873561161b81611546565b965060208801359550604088013567ffffffffffffffff8082111561163f57600080fd5b61164b8b838c016115ac565b909750955060608a0135915061166082611546565b9093506080890135908082111561167657600080fd5b506116838a828b016115ac565b989b979a50959850939692959293505050565b6000602082840312156116a857600080fd5b8135610a4381611546565b6000602082840312156116c557600080fd5b815161ffff81168114610a4357600080fd5b80516116e281611546565b919050565b6000602082840312156116f957600080fd5b8151610a4381611546565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561175657611756611704565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156117a3576117a3611704565b604052919050565b60005b838110156117c65781810151838201526020016117ae565b50506000910152565b600082601f8301126117e057600080fd5b815167ffffffffffffffff8111156117fa576117fa611704565b61182b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161175c565b81815284602083860101111561184057600080fd5b6113468260208301602087016117ab565b805180151581146116e257600080fd5b60008060006060848603121561187657600080fd5b8351925060208085015167ffffffffffffffff8082111561189657600080fd5b818701915087601f8301126118aa57600080fd5b8151818111156118bc576118bc611704565b8060051b6118cb85820161175c565b918252838101850191858101908b8411156118e557600080fd5b86860192505b838310156119cf5782518581111561190257600080fd5b860160e0818e037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001121561193657600080fd5b61193e611733565b6119498983016116d7565b8152611957604083016116d7565b89820152611967606083016116d7565b6040820152611978608083016116d7565b606082015260a0820151608082015260c08201518781111561199a5760008081fd5b6119a88f8b838601016117cf565b60a0830152506119ba60e08301611851565b60c082015283525091860191908601906118eb565b809850505050505050506119e5604085016116d7565b90509250925092565b600080858511156119fe57600080fd5b83861115611a0b57600080fd5b5050820193919092039150565b80356020831015610a24577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610a2457610a24611a54565b60008151808452611aae8160208601602086016117ab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600060808083018784526020828186015281885180845260a093508387019150838160051b880101838b0160005b83811015611bcb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608a84030185528151805173ffffffffffffffffffffffffffffffffffffffff9081168552878201518116888601526040808301518216908601526060808301519091169085015288810151898501528781015160e089860181905290611b9f82870182611a96565b91505060c0808301519250611bb78187018415159052565b509587019593505090850190600101611b0e565b505073ffffffffffffffffffffffffffffffffffffffff8a1660408901529550611bf6945050505050565b73ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b600060208284031215611c2d57600080fd5b610a4382611851565b600060208284031215611c4857600080fd5b5051919050565b80820180821115610a2457610a24611a54565b60008251611c748184602087016117ab565b9190910192915050565b602081526000610a436020830184611a9656fea264697066735822122097762b40eff81d5c4813aa384f72f5419c5a76c6eeffb762d4a3bfbc9c57d8b464736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (1000000 runs)
ILiFi.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
interface ILiFi {
/// Structs ///
struct BridgeData {
bytes32 transactionId;
string bridge;
string integrator;
address referrer;
address sendingAssetId;
address receiver;
uint256 minAmount;
uint256 destinationChainId;
bool hasSourceSwaps;
bool hasDestinationCall;
}
/// Events ///
event LiFiTransferStarted(ILiFi.BridgeData bridgeData);
event LiFiTransferCompleted(
bytes32 indexed transactionId,
address receivingAssetId,
address receiver,
uint256 amount,
uint256 timestamp
);
event LiFiTransferRecovered(
bytes32 indexed transactionId,
address receivingAssetId,
address receiver,
uint256 amount,
uint256 timestamp
);
event LiFiGenericSwapCompleted(
bytes32 indexed transactionId,
string integrator,
string referrer,
address receiver,
address fromAssetId,
address toAssetId,
uint256 fromAmount,
uint256 toAmount
);
// Deprecated but kept here to include in ABI to parse historic events
event LiFiSwappedGeneric(
bytes32 indexed transactionId,
string integrator,
string referrer,
address fromAssetId,
address toAssetId,
uint256 fromAmount,
uint256 toAmount
);
}
LibSwap.sol 81 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import { LibAsset } from "./LibAsset.sol";
import { LibUtil } from "./LibUtil.sol";
import { InvalidContract, NoSwapFromZeroBalance, InsufficientBalance } from "../Errors/GenericErrors.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
library LibSwap {
struct SwapData {
address callTo;
address approveTo;
address sendingAssetId;
address receivingAssetId;
uint256 fromAmount;
bytes callData;
bool requiresDeposit;
}
event AssetSwapped(
bytes32 transactionId,
address dex,
address fromAssetId,
address toAssetId,
uint256 fromAmount,
uint256 toAmount,
uint256 timestamp
);
function swap(bytes32 transactionId, SwapData calldata _swap) internal {
if (!LibAsset.isContract(_swap.callTo)) revert InvalidContract();
uint256 fromAmount = _swap.fromAmount;
if (fromAmount == 0) revert NoSwapFromZeroBalance();
uint256 nativeValue = LibAsset.isNativeAsset(_swap.sendingAssetId)
? _swap.fromAmount
: 0;
uint256 initialSendingAssetBalance = LibAsset.getOwnBalance(
_swap.sendingAssetId
);
uint256 initialReceivingAssetBalance = LibAsset.getOwnBalance(
_swap.receivingAssetId
);
if (nativeValue == 0) {
LibAsset.maxApproveERC20(
IERC20(_swap.sendingAssetId),
_swap.approveTo,
_swap.fromAmount
);
}
if (initialSendingAssetBalance < _swap.fromAmount) {
revert InsufficientBalance(
_swap.fromAmount,
initialSendingAssetBalance
);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory res) = _swap.callTo.call{
value: nativeValue
}(_swap.callData);
if (!success) {
LibUtil.revertWith(res);
}
uint256 newBalance = LibAsset.getOwnBalance(_swap.receivingAssetId);
emit AssetSwapped(
transactionId,
_swap.callTo,
_swap.sendingAssetId,
_swap.receivingAssetId,
_swap.fromAmount,
newBalance > initialReceivingAssetBalance
? newBalance - initialReceivingAssetBalance
: newBalance,
block.timestamp
);
}
}
LibUtil.sol 32 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./LibBytes.sol";
library LibUtil {
using LibBytes for bytes;
function getRevertMsg(
bytes memory _res
) internal pure returns (string memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (_res.length < 68) return "Transaction reverted silently";
bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes
return abi.decode(revertData, (string)); // All that remains is the revert string
}
/// @notice Determines whether the given address is the zero address
/// @param addr The address to verify
/// @return Boolean indicating if the address is the zero address
function isZeroAddress(address addr) internal pure returns (bool) {
return addr == address(0);
}
function revertWith(bytes memory data) internal pure {
assembly {
let dataSize := mload(data) // Load the size of the data
let dataPtr := add(data, 0x20) // Advance data pointer to the next word
revert(dataPtr, dataSize) // Revert with the given data
}
}
}
IERC173.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
/// @title ERC-173 Contract Ownership Standard
/// Note: the ERC-165 identifier for this interface is 0x7f5828d0
/* is ERC165 */
interface IERC173 {
/// @dev This emits when ownership of a contract changes.
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/// @notice Get the address of the owner
/// @return owner_ The address of the owner.
function owner() external view returns (address owner_);
/// @notice Set the address of the new owner of the contract
/// @dev Set _newOwner to address(0) to renounce any ownership.
/// @param _newOwner The address of the new owner of the contract
function transferOwnership(address _newOwner) external;
}
LibAsset.sol 177 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;
import { InsufficientBalance, NullAddrIsNotAnERC20Token, NullAddrIsNotAValidSpender, NoTransferToNullAddress, InvalidAmount, NativeAssetTransferFailed } from "../Errors/GenericErrors.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { LibSwap } from "./LibSwap.sol";
/// @title LibAsset
/// @notice This library contains helpers for dealing with onchain transfers
/// of assets, including accounting for the native asset `assetId`
/// conventions and any noncompliant ERC20 transfers
library LibAsset {
uint256 private constant MAX_UINT = type(uint256).max;
address internal constant NULL_ADDRESS = address(0);
/// @dev All native assets use the empty address for their asset id
/// by convention
address internal constant NATIVE_ASSETID = NULL_ADDRESS; //address(0)
/// @notice Gets the balance of the inheriting contract for the given asset
/// @param assetId The asset identifier to get the balance of
/// @return Balance held by contracts using this library
function getOwnBalance(address assetId) internal view returns (uint256) {
return
isNativeAsset(assetId)
? address(this).balance
: IERC20(assetId).balanceOf(address(this));
}
/// @notice Transfers ether from the inheriting contract to a given
/// recipient
/// @param recipient Address to send ether to
/// @param amount Amount to send to given recipient
function transferNativeAsset(
address payable recipient,
uint256 amount
) private {
if (recipient == NULL_ADDRESS) revert NoTransferToNullAddress();
if (amount > address(this).balance)
revert InsufficientBalance(amount, address(this).balance);
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = recipient.call{ value: amount }("");
if (!success) revert NativeAssetTransferFailed();
}
/// @notice If the current allowance is insufficient, the allowance for a given spender
/// is set to MAX_UINT.
/// @param assetId Token address to transfer
/// @param spender Address to give spend approval to
/// @param amount Amount to approve for spending
function maxApproveERC20(
IERC20 assetId,
address spender,
uint256 amount
) internal {
if (isNativeAsset(address(assetId))) {
return;
}
if (spender == NULL_ADDRESS) {
revert NullAddrIsNotAValidSpender();
}
if (assetId.allowance(address(this), spender) < amount) {
SafeERC20.safeApprove(IERC20(assetId), spender, 0);
SafeERC20.safeApprove(IERC20(assetId), spender, MAX_UINT);
}
}
/// @notice Transfers tokens from the inheriting contract to a given
/// recipient
/// @param assetId Token address to transfer
/// @param recipient Address to send token to
/// @param amount Amount to send to given recipient
function transferERC20(
address assetId,
address recipient,
uint256 amount
) private {
if (isNativeAsset(assetId)) {
revert NullAddrIsNotAnERC20Token();
}
if (recipient == NULL_ADDRESS) {
revert NoTransferToNullAddress();
}
uint256 assetBalance = IERC20(assetId).balanceOf(address(this));
if (amount > assetBalance) {
revert InsufficientBalance(amount, assetBalance);
}
SafeERC20.safeTransfer(IERC20(assetId), recipient, amount);
}
/// @notice Transfers tokens from a sender to a given recipient
/// @param assetId Token address to transfer
/// @param from Address of sender/owner
/// @param to Address of recipient/spender
/// @param amount Amount to transfer from owner to spender
function transferFromERC20(
address assetId,
address from,
address to,
uint256 amount
) internal {
if (isNativeAsset(assetId)) {
revert NullAddrIsNotAnERC20Token();
}
if (to == NULL_ADDRESS) {
revert NoTransferToNullAddress();
}
IERC20 asset = IERC20(assetId);
uint256 prevBalance = asset.balanceOf(to);
SafeERC20.safeTransferFrom(asset, from, to, amount);
if (asset.balanceOf(to) - prevBalance != amount) {
revert InvalidAmount();
}
}
function depositAsset(address assetId, uint256 amount) internal {
if (amount == 0) revert InvalidAmount();
if (isNativeAsset(assetId)) {
if (msg.value < amount) revert InvalidAmount();
} else {
uint256 balance = IERC20(assetId).balanceOf(msg.sender);
if (balance < amount) revert InsufficientBalance(amount, balance);
transferFromERC20(assetId, msg.sender, address(this), amount);
}
}
function depositAssets(LibSwap.SwapData[] calldata swaps) internal {
for (uint256 i = 0; i < swaps.length; ) {
LibSwap.SwapData calldata swap = swaps[i];
if (swap.requiresDeposit) {
depositAsset(swap.sendingAssetId, swap.fromAmount);
}
unchecked {
i++;
}
}
}
/// @notice Determines whether the given assetId is the native asset
/// @param assetId The asset identifier to evaluate
/// @return Boolean indicating if the asset is the native asset
function isNativeAsset(address assetId) internal pure returns (bool) {
return assetId == NATIVE_ASSETID;
}
/// @notice Wrapper function to transfer a given asset (native or erc20) to
/// some recipient. Should handle all non-compliant return value
/// tokens as well by using the SafeERC20 contract by open zeppelin.
/// @param assetId Asset id for transfer (address(0) for native asset,
/// token address for erc20s)
/// @param recipient Address to send asset to
/// @param amount Amount to send to given recipient
function transferAsset(
address assetId,
address payable recipient,
uint256 amount
) internal {
isNativeAsset(assetId)
? transferNativeAsset(recipient, amount)
: transferERC20(assetId, recipient, amount);
}
/// @dev Checks whether the given address is a contract and contains code
function isContract(address _contractAddr) internal view returns (bool) {
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(_contractAddr)
}
return size > 0;
}
}
LibBytes.sol 125 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
library LibBytes {
// solhint-disable no-inline-assembly
// LibBytes specific errors
error SliceOverflow();
error SliceOutOfBounds();
error AddressOutOfBounds();
bytes16 private constant _SYMBOLS = "0123456789abcdef";
// -------------------------
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
) internal pure returns (bytes memory) {
if (_length + 31 < _length) revert SliceOverflow();
if (_bytes.length < _start + _length) revert SliceOutOfBounds();
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(
add(tempBytes, lengthmod),
mul(0x20, iszero(lengthmod))
)
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(
add(
add(_bytes, lengthmod),
mul(0x20, iszero(lengthmod))
),
_start
)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(
bytes memory _bytes,
uint256 _start
) internal pure returns (address) {
if (_bytes.length < _start + 20) {
revert AddressOutOfBounds();
}
address tempAddress;
assembly {
tempAddress := div(
mload(add(add(_bytes, 0x20), _start)),
0x1000000000000000000000000
)
}
return tempAddress;
}
/// Copied from OpenZeppelin's `Strings.sol` utility library.
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609/contracts/utils/Strings.sol
function toHexString(
uint256 value,
uint256 length
) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
GenericErrors.sol 36 lines
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; error AlreadyInitialized(); error CannotAuthoriseSelf(); error CannotBridgeToSameNetwork(); error ContractCallNotAllowed(); error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount); error ExternalCallFailed(); error InformationMismatch(); error InsufficientBalance(uint256 required, uint256 balance); error InvalidAmount(); error InvalidCallData(); error InvalidConfig(); error InvalidContract(); error InvalidDestinationChain(); error InvalidFallbackAddress(); error InvalidReceiver(); error InvalidSendingToken(); error NativeAssetNotSupported(); error NativeAssetTransferFailed(); error NoSwapDataProvided(); error NoSwapFromZeroBalance(); error NotAContract(); error NotInitialized(); error NoTransferToNullAddress(); error NullAddrIsNotAnERC20Token(); error NullAddrIsNotAValidSpender(); error OnlyContractOwner(); error RecoveryAddressCannotBeZero(); error ReentrancyError(); error TokenNotSupported(); error UnAuthorized(); error UnsupportedChainId(uint256 chainId); error WithdrawFailed(); error ZeroAmount();
IExecutor.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import { LibSwap } from "../Libraries/LibSwap.sol";
/// @title Interface for Executor
/// @author LI.FI (https://li.fi)
interface IExecutor {
/// @notice Performs a swap before completing a cross-chain transaction
/// @param _transactionId the transaction id associated with the operation
/// @param _swapData array of data needed for swaps
/// @param transferredAssetId token received from the other chain
/// @param receiver address that will receive tokens in the end
function swapAndCompleteBridgeTokens(
bytes32 _transactionId,
LibSwap.SwapData[] calldata _swapData,
address transferredAssetId,
address payable receiver
) external payable;
}
IStargate.sol 125 lines
// Interface for Stargate V2
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.17;
/// @notice Stargate implementation type.
enum StargateType {
Pool,
OFT
}
/// @notice Ticket data for bus ride.
struct Ticket {
uint72 ticketId;
bytes passengerBytes;
}
/// @title Interface for Stargate.
/// @notice Defines an API for sending tokens to destination chains.
interface IStargate {
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the the specific oft implementation.
*/
struct OFTLimit {
uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
/// @dev This function is same as `send` in OFT interface but returns the ticket data if in the bus ride mode,
/// which allows the caller to ride and drive the bus in the same transaction.
function sendToken(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
)
external
payable
returns (
MessagingReceipt memory msgReceipt,
OFTReceipt memory oftReceipt,
Ticket memory ticket
);
/**
* @notice Provides a quote for OFT-related operations.
* @param _sendParam The parameters for the send operation.
* @return limit The OFT limit information.
* @return oftFeeDetails The details of OFT fees.
* @return receipt The OFT receipt information.
*/
function quoteOFT(
SendParam calldata _sendParam
)
external
view
returns (
OFTLimit memory,
OFTFeeDetail[] memory oftFeeDetails,
OFTReceipt memory
);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(
SendParam calldata _sendParam,
bool _payInLzToken
) external view returns (MessagingFee memory);
}
interface ITokenMessaging {
function assetIds(address tokenAddress) external returns (uint16);
function stargateImpls(uint16 assetId) external returns (address);
}
OFTComposeMsgCodec.sol 95 lines
// SPDX-License-Identifier: MIT
pragma solidity =0.8.17;
// This library was taken from: https://github.com/LayerZero-Labs/LayerZero-v2/tree/38278c8d8f4606d0ce247d6edd473fc96674769b/packages/layerzero-v2/evm/oapp/contracts/oft/libs
// since the Solidity version did not match with ours, we decided to use a copy of this library with adjusted solc version for better compatibility
library OFTComposeMsgCodec {
// Offset constants for decoding composed messages
uint8 private constant NONCE_OFFSET = 8;
uint8 private constant SRC_EID_OFFSET = 12;
uint8 private constant AMOUNT_LD_OFFSET = 44;
uint8 private constant COMPOSE_FROM_OFFSET = 76;
/**
* @dev Encodes a OFT composed message.
* @param _nonce The nonce value.
* @param _srcEid The source endpoint ID.
* @param _amountLD The amount in local decimals.
* @param _composeMsg The composed message.
* @return _msg The encoded Composed message.
*/
function encode(
uint64 _nonce,
uint32 _srcEid,
uint256 _amountLD,
bytes memory _composeMsg // 0x[composeFrom][composeMsg]
) internal pure returns (bytes memory _msg) {
_msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg);
}
/**
* @dev Retrieves the nonce from the composed message.
* @param _msg The message.
* @return The nonce value.
*/
function nonce(bytes calldata _msg) internal pure returns (uint64) {
return uint64(bytes8(_msg[:NONCE_OFFSET]));
}
/**
* @dev Retrieves the source endpoint ID from the composed message.
* @param _msg The message.
* @return The source endpoint ID.
*/
function srcEid(bytes calldata _msg) internal pure returns (uint32) {
return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET]));
}
/**
* @dev Retrieves the amount in local decimals from the composed message.
* @param _msg The message.
* @return The amount in local decimals.
*/
function amountLD(bytes calldata _msg) internal pure returns (uint256) {
return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET]));
}
/**
* @dev Retrieves the composeFrom value from the composed message.
* @param _msg The message.
* @return The composeFrom value.
*/
function composeFrom(bytes calldata _msg) internal pure returns (bytes32) {
return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]);
}
/**
* @dev Retrieves the composed message.
* @param _msg The message.
* @return The composed message.
*/
function composeMsg(
bytes calldata _msg
) internal pure returns (bytes memory) {
return _msg[COMPOSE_FROM_OFFSET:];
}
/**
* @dev Converts an address to bytes32.
* @param _addr The address to convert.
* @return The bytes32 representation of the address.
*/
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
/**
* @dev Converts bytes32 to an address.
* @param _b The bytes32 value to convert.
* @return The address representation of bytes32.
*/
function bytes32ToAddress(bytes32 _b) internal pure returns (address) {
return address(uint160(uint256(_b)));
}
}
ReceiverStargateV2.sol 233 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { LibSwap } from "../Libraries/LibSwap.sol";
import { LibAsset } from "../Libraries/LibAsset.sol";
import { OFTComposeMsgCodec } from "../Libraries/OFTComposeMsgCodec.sol";
import { ILiFi } from "../Interfaces/ILiFi.sol";
import { IExecutor } from "../Interfaces/IExecutor.sol";
import { TransferrableOwnership } from "../Helpers/TransferrableOwnership.sol";
import { ExternalCallFailed, UnAuthorized } from "../Errors/GenericErrors.sol";
import { ITokenMessaging } from "../Interfaces/IStargate.sol";
interface IPool {
function token() external view returns (address tokenAddress);
}
interface ILayerZeroComposer {
/// @notice Composes a LayerZero message from an OApp.
/// @param _from The address initiating the composition, typically the OApp where the lzReceive was called.
/// @param _guid The unique identifier for the corresponding LayerZero src/dst tx.
/// @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive.
/// @param _executor The address of the executor for the composed message.
/// @param _extraData Additional arbitrary data in bytes passed by the entity who executes the lzCompose.
function lzCompose(
address _from,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) external payable;
}
/// @title ReceiverStargateV2
/// @author LI.FI (https://li.fi)
/// @notice Arbitrary execution contract used for cross-chain swaps and message passing via Stargate V2
/// @custom:version 1.0.0
contract ReceiverStargateV2 is
ILiFi,
TransferrableOwnership,
ILayerZeroComposer
{
using SafeERC20 for IERC20;
/// Storage ///
IExecutor public immutable executor;
ITokenMessaging public immutable tokenMessaging;
address public immutable endpointV2;
uint256 public immutable recoverGas;
/// Modifiers ///
modifier onlyEndpointV2() {
if (msg.sender != endpointV2) {
revert UnAuthorized();
}
_;
}
/// Constructor
constructor(
address _owner,
address _executor,
address _tokenMessaging,
address _endpointV2,
uint256 _recoverGas
) TransferrableOwnership(_owner) {
owner = _owner;
executor = IExecutor(_executor);
tokenMessaging = ITokenMessaging(_tokenMessaging);
endpointV2 = _endpointV2;
recoverGas = _recoverGas;
}
/// External Methods ///
/// @notice Completes a stargateV2 cross-chain transaction on the receiving chain
/// @dev This function is called by Stargate Router via LayerZero endpoint (sendCompose(...) function)
/// @param _from The address initiating the composition, typically the OApp where the lzReceive was called
/// @param * (unused) The unique identifier for the corresponding LayerZero src/dst tx
/// @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive
/// @param * (unused) The address of the executor for the composed message
/// @param * (unused) Additional arbitrary data in bytes passed by the entity who executes the lzCompose
function lzCompose(
address _from,
bytes32, // _guid (not used)
bytes calldata _message,
address, // _executor (not used)
bytes calldata // _extraData (not used)
) external payable onlyEndpointV2 {
// verify that _from address is actually a Stargate pool by checking if Stargate's
// TokenMessaging contract has an assetId registered for this address
if (tokenMessaging.assetIds(_from) == 0) revert UnAuthorized();
// get the address of the token that was received from Stargate bridge
address bridgedAssetId = IPool(_from).token();
// decode payload
(
bytes32 transactionId,
LibSwap.SwapData[] memory swapData,
address receiver
) = abi.decode(
OFTComposeMsgCodec.composeMsg(_message),
(bytes32, LibSwap.SwapData[], address)
);
// execute swap(s)
_swapAndCompleteBridgeTokens(
transactionId,
swapData,
bridgedAssetId,
payable(receiver),
OFTComposeMsgCodec.amountLD(_message)
);
}
/// @notice Send remaining token to receiver
/// @param assetId address of the token to be withdrawn (not to be confused with StargateV2's assetIds which are uint16 values)
/// @param receiver address that will receive tokens in the end
/// @param amount amount of token
function pullToken(
address assetId,
address payable receiver,
uint256 amount
) external onlyOwner {
if (LibAsset.isNativeAsset(assetId)) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = receiver.call{ value: amount }("");
if (!success) revert ExternalCallFailed();
} else {
IERC20(assetId).safeTransfer(receiver, amount);
}
}
/// Private Methods ///
/// @notice Performs a swap before completing a cross-chain transaction
/// @param _transactionId the transaction id associated with the operation
/// @param _swapData array of data needed for swaps
/// @param assetId address of the token received from the source chain (not to be confused with StargateV2's assetIds which are uint16 values)
/// @param receiver address that will receive tokens in the end
/// @param amount amount of token
function _swapAndCompleteBridgeTokens(
bytes32 _transactionId,
LibSwap.SwapData[] memory _swapData,
address assetId,
address payable receiver,
uint256 amount
) private {
uint256 cacheGasLeft = gasleft();
if (LibAsset.isNativeAsset(assetId)) {
// case 1: native asset
if (cacheGasLeft < recoverGas) {
// case 1a: not enough gas left to execute calls
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = receiver.call{ value: amount }("");
if (!success) revert ExternalCallFailed();
emit LiFiTransferRecovered(
_transactionId,
assetId,
receiver,
amount,
block.timestamp
);
return;
}
// case 1b: enough gas left to execute calls
// solhint-disable no-empty-blocks
try
executor.swapAndCompleteBridgeTokens{
value: amount,
gas: cacheGasLeft - recoverGas
}(_transactionId, _swapData, assetId, receiver)
{} catch {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = receiver.call{ value: amount }("");
if (!success) revert ExternalCallFailed();
emit LiFiTransferRecovered(
_transactionId,
assetId,
receiver,
amount,
block.timestamp
);
}
} else {
// case 2: ERC20 asset
IERC20 token = IERC20(assetId);
token.safeApprove(address(executor), 0);
if (cacheGasLeft < recoverGas) {
// case 2a: not enough gas left to execute calls
token.safeTransfer(receiver, amount);
emit LiFiTransferRecovered(
_transactionId,
assetId,
receiver,
amount,
block.timestamp
);
return;
}
// case 2b: enough gas left to execute calls
token.safeIncreaseAllowance(address(executor), amount);
try
executor.swapAndCompleteBridgeTokens{
gas: cacheGasLeft - recoverGas
}(_transactionId, _swapData, assetId, receiver)
{} catch {
token.safeTransfer(receiver, amount);
emit LiFiTransferRecovered(
_transactionId,
assetId,
receiver,
amount,
block.timestamp
);
}
token.safeApprove(address(executor), 0);
}
}
/// @notice Receive native asset directly.
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
}
TransferrableOwnership.sol 57 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import { IERC173 } from "../Interfaces/IERC173.sol";
import { LibAsset } from "../Libraries/LibAsset.sol";
contract TransferrableOwnership is IERC173 {
address public owner;
address public pendingOwner;
/// Errors ///
error UnAuthorized();
error NoNullOwner();
error NewOwnerMustNotBeSelf();
error NoPendingOwnershipTransfer();
error NotPendingOwner();
/// Events ///
event OwnershipTransferRequested(
address indexed _from,
address indexed _to
);
constructor(address initialOwner) {
owner = initialOwner;
}
modifier onlyOwner() {
if (msg.sender != owner) revert UnAuthorized();
_;
}
/// @notice Initiates transfer of ownership to a new address
/// @param _newOwner the address to transfer ownership to
function transferOwnership(address _newOwner) external onlyOwner {
if (_newOwner == LibAsset.NULL_ADDRESS) revert NoNullOwner();
if (_newOwner == msg.sender) revert NewOwnerMustNotBeSelf();
pendingOwner = _newOwner;
emit OwnershipTransferRequested(msg.sender, pendingOwner);
}
/// @notice Cancel transfer of ownership
function cancelOwnershipTransfer() external onlyOwner {
if (pendingOwner == LibAsset.NULL_ADDRESS)
revert NoPendingOwnershipTransfer();
pendingOwner = LibAsset.NULL_ADDRESS;
}
/// @notice Confirms transfer of ownership to the calling address (msg.sender)
function confirmOwnershipTransfer() external {
address _pendingOwner = pendingOwner;
if (msg.sender != _pendingOwner) revert NotPendingOwner();
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = LibAsset.NULL_ADDRESS;
}
}
Address.sol 244 lines
// 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);
}
}
}
IERC20.sol 78 lines
// 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);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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. Compatible with tokens that require the approval to be set to
* 0 before setting it to a non-zero value.
*/
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));
}
}
IERC20Permit.sol 60 lines
// 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);
}
Read Contract
endpointV2 0x797b7f6b → address
executor 0xc34c08e5 → address
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
recoverGas 0xbcf225e6 → uint256
tokenMessaging 0xfb214c2f → address
Write Contract 5 functions
These functions modify contract state and require a wallet transaction to execute.
cancelOwnershipTransfer 0x23452b9c
No parameters
confirmOwnershipTransfer 0x7200b829
No parameters
lzCompose 0xd0a10260
address _from
bytes32
bytes _message
address
bytes
pullToken 0x2e144579
address assetId
address receiver
uint256 amount
transferOwnership 0xf2fde38b
address _newOwner
Token Balances (1)
View Transfers →Recent Transactions
No transactions found for this address