Address Contract Partially Verified
Address
0x9E092cb431e5F1aa70e47e052773711d2Ba4917E
Balance
0 ETH
Nonce
1
Code Size
6162 bytes
Creator
Create2 Deployer at tx 0x2182749f...b9e1ec
Indexed Transactions
0
Contract Bytecode
6162 bytes
0x608060405234801561001057600080fd5b50600436106100725760003560e01c8063d9d98ce411610050578063d9d98ce4146100d4578063e563037e146100e7578063f04f27071461013357600080fd5b806340a08f1014610077578063613255ab146100a0578063af2511c0146100c1575b600080fd5b61008a610085366004610da8565b610148565b6040516100979190610eae565b60405180910390f35b6100b36100ae366004610ec1565b6101f6565b604051908152602001610097565b61008a6100cf366004610ede565b610207565b6100b36100e2366004610f9c565b610297565b61010e7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610097565b61014661014136600461116c565b610354565b005b6060600060405180608001604052808a73ffffffffffffffffffffffffffffffffffffffff1681526020013373ffffffffffffffffffffffffffffffffffffffff168152602001858563ffffffff169060201b1760401b815260200187878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505091525090506101e988888361046a565b9998505050505050505050565b60006102018261053f565b92915050565b6040805160808101825273ffffffffffffffffffffffffffffffffffffffff891681523360208201526060916000919081018560e086901c63ffffffff169060201b1760401b815260200187878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505091525090506101e988888361046a565b6000806102a38461053f565b905060008111610314576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f556e737570706f727465642063757272656e637900000000000000000000000060448201526064015b60405180910390fd5b8083101561032a57610325836105f3565b61034c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b949350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c816146103c3576040517f698bba4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001548151602083012014610404576040517f3f4d605300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600090556104648460008151811061042057610420611277565b60200260200101518460008151811061043b5761043b611277565b60200260200101518460008151811061045657610456611277565b6020026020010151846106fc565b50505050565b606061049684848460405160200161048291906112a6565b60405160208183030381529060405261081c565b600080546104a39061131b565b80601f01602080910402602001604051908101604052809291908181526020018280546104cf9061131b565b801561051c5780601f106104f15761010080835404028352916020019161051c565b820191906000526020600020905b8154815290600101906020018083116104ff57829003601f168201915b5050505050905060008151111561053857610538600080610cb9565b9392505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881166004830152600091908316906370a0823190602401602060405180830381865afa1580156105cf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610201919061136e565b60006102017f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff1663d2946c2b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610663573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106879190611387565b73ffffffffffffffffffffffffffffffffffffffff1663d877845c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f5919061136e565b83906108dd565b60008180602001905181019061071291906113f4565b9050610723858583600001516108f2565b600081604001518060601c9060401c63ffffffff1683602001516107647f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c890565b89898988606001516040518763ffffffff1660e01b815260040161078d969594939291906114b0565b6000604051808303816000875af11580156107ac573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526107f29190810190611509565b90506107ff868686610918565b8051156108145760006108128282611584565b505b505050505050565b80805190602001206001819055507f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16635c38449e306108868673ffffffffffffffffffffffffffffffffffffffff16610a10565b61088f86610a85565b856040518563ffffffff1660e01b81526004016108af949392919061169e565b600060405180830381600087803b1580156108c957600080fd5b505af1158015610812573d6000803e3d6000fd5b60006105388383670de0b6b3a7640000610acc565b61091373ffffffffffffffffffffffffffffffffffffffff84168284610b10565b505050565b307f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c873ffffffffffffffffffffffffffffffffffffffff16036109135773ffffffffffffffffffffffffffffffffffffffff831663095ea7b33361097c8486611751565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303816000875af11580156109ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610464919061178b565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110610a4657610a46611277565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050919050565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110610abb57610abb611277565b602002602001018181525050919050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202610b0157600080fd5b50910281810615159190040190565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790529151600092839290871691610ba791906117ad565b6000604051808303816000865af19150503d8060008114610be4576040519150601f19603f3d011682016040523d82523d6000602084013e610be9565b606091505b5091509150818015610c13575080511580610c13575080806020019051810190610c13919061178b565b610c5357610c2081610c5a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030b9190610eae565b5050505050565b6060604482511015610c9f57505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6004820191508180602001905181019061020191906117c9565b508054610cc59061131b565b6000825580601f10610cd5575050565b601f016020900490600052602060002090810190610cf39190610cf6565b50565b5b80821115610d0b5760008155600101610cf7565b5090565b73ffffffffffffffffffffffffffffffffffffffff81168114610cf357600080fd5b60008083601f840112610d4357600080fd5b50813567ffffffffffffffff811115610d5b57600080fd5b602083019150836020828501011115610d7357600080fd5b9250929050565b7fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000081168114610cf357600080fd5b600080600080600080600060a0888a031215610dc357600080fd5b8735610dce81610d0f565b96506020880135610dde81610d0f565b955060408801359450606088013567ffffffffffffffff811115610e0157600080fd5b610e0d8a828b01610d31565b9095509350506080880135610e2181610d7a565b8060601c925063ffffffff8160401c1691505092959891949750929550565b60005b83811015610e5b578181015183820152602001610e43565b50506000910152565b60008151808452610e7c816020860160208601610e40565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006105386020830184610e64565b600060208284031215610ed357600080fd5b813561053881610d0f565b600080600080600080600060c0888a031215610ef957600080fd5b8735610f0481610d0f565b96506020880135610f1481610d0f565b955060408801359450606088013567ffffffffffffffff811115610f3757600080fd5b610f438a828b01610d31565b9095509350506080880135610f5781610d0f565b915060a08801357fffffffff0000000000000000000000000000000000000000000000000000000081168114610f8c57600080fd5b8091505092959891949750929550565b60008060408385031215610faf57600080fd5b8235610fba81610d0f565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561103e5761103e610fc8565b604052919050565b600067ffffffffffffffff82111561106057611060610fc8565b5060051b60200190565b600082601f83011261107b57600080fd5b8135602061109061108b83611046565b610ff7565b82815260059290921b840181019181810190868411156110af57600080fd5b8286015b848110156110ca57803583529183019183016110b3565b509695505050505050565b600067ffffffffffffffff8211156110ef576110ef610fc8565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f83011261112c57600080fd5b813561113a61108b826110d5565b81815284602083860101111561114f57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561118257600080fd5b843567ffffffffffffffff8082111561119a57600080fd5b818701915087601f8301126111ae57600080fd5b813560206111be61108b83611046565b82815260059290921b8401810191818101908b8411156111dd57600080fd5b948201945b838610156112045785356111f581610d0f565b825294820194908201906111e2565b9850508801359250508082111561121a57600080fd5b6112268883890161106a565b9450604087013591508082111561123c57600080fd5b6112488883890161106a565b9350606087013591508082111561125e57600080fd5b5061126b8782880161111b565b91505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60208152600073ffffffffffffffffffffffffffffffffffffffff808451166020840152806020850151166040840152507fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040840151166060830152606083015160808084015261034c60a0840182610e64565b600181811c9082168061132f57607f821691505b602082108103611368577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60006020828403121561138057600080fd5b5051919050565b60006020828403121561139957600080fd5b815161053881610d0f565b60006113b261108b846110d5565b90508281528383830111156113c657600080fd5b610538836020830184610e40565b600082601f8301126113e557600080fd5b610538838351602085016113a4565b60006020828403121561140657600080fd5b815167ffffffffffffffff8082111561141e57600080fd5b908301906080828603121561143257600080fd5b60405160808101818110838211171561144d5761144d610fc8565b604052825161145b81610d0f565b8152602083015161146b81610d0f565b6020820152604083015161147e81610d7a565b604082015260608301518281111561149557600080fd5b6114a1878286016113d4565b60608301525095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015283608083015260c060a08301526114fd60c0830184610e64565b98975050505050505050565b60006020828403121561151b57600080fd5b815167ffffffffffffffff81111561153257600080fd5b61034c848285016113d4565b601f82111561091357600081815260208120601f850160051c810160208610156115655750805b601f850160051c820191505b8181101561081457828155600101611571565b815167ffffffffffffffff81111561159e5761159e610fc8565b6115b2816115ac845461131b565b8461153e565b602080601f83116001811461160557600084156115cf5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610814565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561165257888601518255948401946001909101908401611633565b508582101561168e57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60006080820173ffffffffffffffffffffffffffffffffffffffff8088168452602060808186015282885180855260a087019150828a01945060005b818110156116f85785518516835294830194918301916001016116da565b5050858103604087015287518082529082019350915080870160005b8381101561173057815185529382019390820190600101611714565b5050505082810360608401526117468185610e64565b979650505050505050565b80820180821115610201577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006020828403121561179d57600080fd5b8151801515811461053857600080fd5b600082516117bf818460208701610e40565b9190910192915050565b6000602082840312156117db57600080fd5b815167ffffffffffffffff8111156117f257600080fd5b8201601f8101841361180357600080fd5b61034c848251602084016113a456
Verified Source Code Partial Match
Compiler: v0.8.19+commit.7dd6d404
EVM: london
Optimization: Yes (10000 runs)
BaseWrapper.sol 134 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;
import "erc7399/IERC7399.sol";
import { TransferHelper, ERC20 } from "./utils/TransferHelper.sol";
import { FunctionCodec } from "./utils/FunctionCodec.sol";
/// @dev All ERC7399 flash loan wrappers have the same general structure.
/// - The ERC7399 `flash` function is the entry point for the flash loan.
/// - The wrapper calls the underlying lender flash lender on their non-ERC7399 flash lending call to borrow the funds.
/// - The lender sends the funds to the wrapper.
/// - The wrapper receives the callback from the lender.
/// - The wrapper sends the funds to the loan receiver.
/// - The wrapper calls the callback supplied by the original borrower.
/// - The callback from the original borrower executes.
/// - Depending on the lender, the wrapper may have to approve it to pull the repayment.
/// - If there is any data to return, it is kept in a storage variable.
/// - The wrapper exits the callback.
/// - The lender verifies or pulls the repayment.
/// - The wrapper returns to the original borrower the stored result of its callback.
abstract contract BaseWrapper is IERC7399 {
using TransferHelper for address;
struct Data {
address loanReceiver;
address initiator;
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback;
bytes initiatorData;
}
bytes internal _callbackResult;
/// @inheritdoc IERC7399
/// @dev The entry point for the ERC7399 flash loan. Packs data to convert the legacy flash loan into an ERC7399
/// flash loan. Then it calls the legacy flash loan. Once the flash loan is done, checks if there is any return
/// data and returns it.
function flash(
address loanReceiver,
address asset,
uint256 amount,
bytes calldata initiatorData,
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
)
external
returns (bytes memory result)
{
Data memory data = Data({
loanReceiver: loanReceiver,
initiator: msg.sender,
callback: callback,
initiatorData: initiatorData
});
return _flash(asset, amount, data);
}
/// @dev Alternative entry point for the ERC7399 flash loan, without function pointers. Packs data to convert the
/// legacy flash loan into an ERC7399 flash loan. Then it calls the legacy flash loan. Once the flash loan is done,
/// checks if there is any return data and returns it.
function flash(
address loanReceiver,
address asset,
uint256 amount,
bytes calldata initiatorData,
address callbackTarget,
bytes4 callbackSelector
)
external
returns (bytes memory result)
{
Data memory data = Data({
loanReceiver: loanReceiver,
initiator: msg.sender,
callback: FunctionCodec.decodeFunction(callbackTarget, callbackSelector),
initiatorData: initiatorData
});
return _flash(asset, amount, data);
}
function _flash(address asset, uint256 amount, Data memory data) internal virtual returns (bytes memory result) {
_flashLoan(asset, amount, abi.encode(data));
result = _callbackResult;
// Avoid storage write if not needed
if (result.length > 0) {
delete _callbackResult;
}
return result;
}
/// @dev Call the legacy flashloan function in the child contract. This is where we borrow from Aave, Uniswap, etc.
function _flashLoan(address asset, uint256 amount, bytes memory data) internal virtual;
/// @dev Handle the common parts of bridging the callback from legacy to ERC7399. Transfer the funds to the loan
/// receiver. Call the callback supplied by the original borrower. Approve the repayment if necessary. If there is
/// any result, it is kept in a storage variable to be retrieved on `flash` after the legacy flash loan is finished.
function _bridgeToCallback(address asset, uint256 amount, uint256 fee, bytes memory params) internal {
Data memory data = abi.decode(params, (Data));
_transferAssets(asset, amount, data.loanReceiver);
// call the callback and tell the callback receiver to repay the loan to this contract
bytes memory result = data.callback(data.initiator, _repayTo(), address(asset), amount, fee, data.initiatorData);
_approveRepayment(asset, amount, fee);
if (result.length > 0) {
// if there's any result, it is kept in a storage variable to be retrieved later in this tx
_callbackResult = result;
}
}
/// @dev Transfer the assets to the loan receiver.
/// Override it if the provider can send the funds directly
function _transferAssets(address asset, uint256 amount, address loanReceiver) internal virtual {
asset.safeTransfer(loanReceiver, amount);
}
/// @dev Approve the repayment of the loan to the provider if needed.
/// Override it if the provider can receive the funds directly and you want to avoid the if condition
function _approveRepayment(address asset, uint256 amount, uint256 fee) internal virtual {
if (_repayTo() == address(this)) {
ERC20(asset).approve(msg.sender, amount + fee);
}
}
/// @dev Where should the end client send the funds to repay the loan
/// Override it if the provider can receive the funds directly
function _repayTo() internal view virtual returns (address) {
return address(this);
}
}
Arrays.sol 15 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;
library Arrays {
function toArray(uint256 n) internal pure returns (uint256[] memory arr) {
arr = new uint[](1);
arr[0] = n;
}
function toArray(address a) internal pure returns (address[] memory arr) {
arr = new address[](1);
arr[0] = a;
}
}
FunctionCodec.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
library FunctionCodec {
function encodeParams(address contractAddr, bytes4 selector) internal pure returns (bytes24) {
return bytes24(bytes20(contractAddr)) | bytes24(selector) >> 160;
}
function decodeParams(bytes24 encoded) internal pure returns (address contractAddr, bytes4 selector) {
contractAddr = address(bytes20(encoded));
selector = bytes4(encoded << 160);
}
function encodeFunction(
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f
)
internal
pure
returns (bytes24)
{
return encodeParams(f.address, f.selector);
}
function decodeFunction(
address contractAddr,
bytes4 selector
)
internal
pure
returns (function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f)
{
uint32 s = uint32(selector);
// solhint-disable-next-line no-inline-assembly
assembly {
f.address := contractAddr
f.selector := s
}
}
function decodeFunction(bytes24 encoded)
internal
pure
returns (function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) f)
{
(address contractAddr, bytes4 selector) = decodeParams(encoded);
return decodeFunction(contractAddr, selector);
}
}
IERC7399.sol 42 lines
// SPDX-License-Identifier: CC0
pragma solidity >=0.6.4;
/// @dev Specification for flash lenders compatible with ERC-7399
interface IERC7399 {
/// @dev The amount of currency available to be lent.
/// @param asset The loan currency.
/// @return The amount of `asset` that can be borrowed.
function maxFlashLoan(address asset) external view returns (uint256);
/// @dev The fee to be charged for a given loan.
/// @param asset The loan currency.
/// @param amount The amount of assets lent.
/// @return The amount of `asset` to be charged for the loan, on top of the returned principal.
function flashFee(address asset, uint256 amount) external view returns (uint256);
/// @dev Initiate a flash loan.
/// @param loanReceiver The address receiving the flash loan
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param data The ABI encoded user data
/// @param callback The address and signature of the callback function
/// @return result ABI encoded result of the callback
function flash(
address loanReceiver,
address asset,
uint256 amount,
bytes calldata data,
/// @dev callback. This is a combination of the callback receiver address, and the signature of callback
/// function. It is encoded packed as 20 bytes + 4 bytes.
/// @dev the return of the callback function is not encoded in the parameter, but must be `returns (bytes
/// memory)` for compliance with the standard.
/// @param initiator The address that called this function
/// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
/// @param asset The asset to be loaned
/// @param amount The amount to loaned
/// @param fee The fee to be paid
/// @param data The ABI encoded data to be passed to the callback
/// @return result ABI encoded result of the callback
function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
) external returns (bytes memory);
}
TransferHelper.sol 23 lines
// SPDX-License-Identifier: MIT
// Taken from https://github.com/Uniswap/uniswap-lib/blob/master/src/libraries/TransferHelper.sol
pragma solidity ^0.8.19;
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { RevertMsgExtractor } from "./RevertMsgExtractor.sol";
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
// USDT is a well known token that returns nothing for its transfer, transferFrom, and approve functions
// and part of the reason this library exists
library TransferHelper {
/// @notice Transfers assets from msg.sender to a recipient
/// @dev Errors with the underlying revert message if transfer fails
/// @param asset The contract address of the asset which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(address asset, address to, uint256 value) internal {
(bool success, bytes memory data) =
// solhint-disable-next-line avoid-low-level-calls
address(asset).call(abi.encodeWithSelector(ERC20.transfer.selector, to, value));
if (!(success && (data.length == 0 || abi.decode(data, (bool))))) revert(RevertMsgExtractor.getRevertMsg(data));
}
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
BalancerWrapper.sol 77 lines
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;
import { IFlashLoanRecipient } from "./interfaces/IFlashLoanRecipient.sol";
import { IFlashLoaner } from "./interfaces/IFlashLoaner.sol";
import { Arrays } from "../utils/Arrays.sol";
import { FixedPointMathLib } from "lib/solmate/src/utils/FixedPointMathLib.sol";
import { BaseWrapper, IERC7399, ERC20 } from "../BaseWrapper.sol";
/// @dev Balancer Flash Lender that uses Balancer Pools as source of liquidity.
/// Balancer allows pushing repayments, so we override `_repayTo`.
contract BalancerWrapper is BaseWrapper, IFlashLoanRecipient {
using Arrays for uint256;
using Arrays for address;
using FixedPointMathLib for uint256;
error NotBalancer();
error HashMismatch();
IFlashLoaner public immutable balancer;
bytes32 private flashLoanDataHash;
constructor(IFlashLoaner _balancer) {
balancer = _balancer;
}
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) external view returns (uint256) {
return _maxFlashLoan(asset);
}
/// @inheritdoc IERC7399
function flashFee(address asset, uint256 amount) external view returns (uint256) {
uint256 max = _maxFlashLoan(asset);
require(max > 0, "Unsupported currency");
return amount >= max ? type(uint256).max : _flashFee(amount);
}
/// @inheritdoc IFlashLoanRecipient
function receiveFlashLoan(
address[] memory assets,
uint256[] memory amounts,
uint256[] memory fees,
bytes memory params
)
external
override
{
if (msg.sender != address(balancer)) revert NotBalancer();
if (keccak256(params) != flashLoanDataHash) revert HashMismatch();
delete flashLoanDataHash;
_bridgeToCallback(assets[0], amounts[0], fees[0], params);
}
function _flashLoan(address asset, uint256 amount, bytes memory data) internal override {
flashLoanDataHash = keccak256(data);
balancer.flashLoan(this, asset.toArray(), amount.toArray(), data);
}
function _repayTo() internal view override returns (address) {
return address(balancer);
}
function _flashFee(uint256 amount) internal view returns (uint256) {
return amount.mulWadUp(balancer.getProtocolFeesCollector().getFlashLoanFeePercentage());
}
function _maxFlashLoan(address asset) internal view returns (uint256) {
return ERC20(asset).balanceOf(address(balancer));
}
}
RevertMsgExtractor.sol 21 lines
// SPDX-License-Identifier: MIT
// Taken from
// https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/src/BoringBatchable.sol
pragma solidity ^0.8.19;
library RevertMsgExtractor {
/// @dev Helper function to extract a useful revert message from a failed call.
/// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
function getRevertMsg(bytes memory returnData) internal pure returns (string memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (returnData.length < 68) return "Transaction reverted silently";
// solhint-disable-next-line no-inline-assembly
assembly {
// Slice the sighash.
returnData := add(returnData, 0x04)
}
return abi.decode(returnData, (string)); // All that remains is the revert string
}
}
IFlashLoaner.sol 17 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import { IFlashLoanRecipient } from "./IFlashLoanRecipient.sol";
import { IProtocolFeesCollector } from "./IProtocolFeesCollector.sol";
interface IFlashLoaner {
function flashLoan(
IFlashLoanRecipient recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
)
external;
function getProtocolFeesCollector() external view returns (IProtocolFeesCollector);
}
FixedPointMathLib.sol 255 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
IFlashLoanRecipient.sol 21 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
interface IFlashLoanRecipient {
/**
* @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
*
* At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
* call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
* Vault, or else the entire flash loan will revert.
*
* `userData` is the same value passed in the `IVault.flashLoan` call.
*/
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
)
external;
}
IProtocolFeesCollector.sol 6 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
interface IProtocolFeesCollector {
function getFlashLoanFeePercentage() external view returns (uint256);
}
Read Contract
balancer 0xe563037e → address
flashFee 0xd9d98ce4 → uint256
maxFlashLoan 0x613255ab → uint256
Write Contract 3 functions
These functions modify contract state and require a wallet transaction to execute.
flash 0x40a08f10
address loanReceiver
address asset
uint256 amount
bytes initiatorData
function callback
returns: bytes
flash 0xaf2511c0
address loanReceiver
address asset
uint256 amount
bytes initiatorData
address callbackTarget
bytes4 callbackSelector
returns: bytes
receiveFlashLoan 0xf04f2707
address[] assets
uint256[] amounts
uint256[] fees
bytes params
Token Balances (1)
View Transfers →Recent Transactions
No transactions found for this address