Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 14504670 | 17 days ago | 0 ETH | |||||
| 14504670 | 17 days ago | 0 ETH | |||||
| 14327919 | 19 days ago | 0.002 ETH | |||||
| 14327825 | 19 days ago | 0 ETH | |||||
| 14321603 | 19 days ago | 0 ETH | |||||
| 14321603 | 19 days ago | 0 ETH | |||||
| 14316629 | 19 days ago | 0 ETH | |||||
| 14230490 | 20 days ago | 0.00029273 ETH | |||||
| 14213661 | 20 days ago | 0 ETH | |||||
| 13886828 | 24 days ago | 0.0125 ETH | |||||
| 13815482 | 25 days ago | 0 ETH | |||||
| 13815431 | 25 days ago | 0 ETH | |||||
| 13726997 | 26 days ago | 0 ETH | |||||
| 13726997 | 26 days ago | 0 ETH | |||||
| 13652852 | 27 days ago | 0 ETH | |||||
| 13652766 | 27 days ago | 0 ETH | |||||
| 13652660 | 27 days ago | 0.00148965 ETH | |||||
| 13609668 | 27 days ago | 0 ETH | |||||
| 12846694 | 36 days ago | 0 ETH | |||||
| 12846551 | 36 days ago | 0.003 ETH | |||||
| 12830974 | 36 days ago | 0 ETH | |||||
| 12779570 | 37 days ago | 0 ETH | |||||
| 12779393 | 37 days ago | 0 ETH | |||||
| 12779339 | 37 days ago | 0 ETH | |||||
| 12757632 | 37 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
OneDeltaComposerKatana
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {BaseComposer} from "../../BaseComposer.sol";
import {SwapCallbacks} from "./flashSwap/SwapCallbacks.sol";
import {FlashLoanCallbacks} from "./flashLoan/FlashLoanCallbacks.sol";
import {UniversalFlashLoan} from "./flashLoan/UniversalFlashLoan.sol";
/**
* @title Chain-dependent Universal aggregator contract.
* @author 1delta Labs AG
*/
contract OneDeltaComposerKatana is BaseComposer, UniversalFlashLoan, SwapCallbacks {
/**
* Execute a set of packed operations
*/
function _deltaComposeInternal(
address callerAddress,
uint256 currentOffset,
uint256 calldataLength //
)
internal
override(BaseComposer, FlashLoanCallbacks, SwapCallbacks)
{
return BaseComposer._deltaComposeInternal(
callerAddress,
currentOffset,
calldataLength //
);
}
function _universalFlashLoan(
uint256 currentOffset,
address callerAddress
)
internal
override(UniversalFlashLoan, BaseComposer)
returns (uint256)
{
return UniversalFlashLoan._universalFlashLoan(
currentOffset,
callerAddress //
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ComposerCommands} from "./enums/DeltaEnums.sol";
import {ExternalCall} from "./generic/ExternalCall.sol";
import {Transfers} from "./transfers/Transfers.sol";
import {ERC4626Operations} from "./ERC4626/ERC4626Operations.sol";
import {UniversalLending} from "./lending/UniversalLending.sol";
import {Permits} from "./permit/Permits.sol";
import {Swaps} from "./swappers/Swaps.sol";
import {Gen2025DexActions} from "./singletons/Gen2025DexActions.sol";
import {DeadLogger} from "../shared/logs/DeadLogger.sol";
/**
* @title Base aggregator contract that needs overrides for explicit chains.
* Allows spot and margin swap aggregation
* Efficient batching through compact calldata usage.
* Needs to inherit callback implementations
* @author 1delta Labs AG
*/
abstract contract BaseComposer is
DeadLogger,
Swaps,
Gen2025DexActions,
UniversalLending,
ERC4626Operations,
Transfers,
Permits,
ExternalCall //
{
constructor() {}
receive() external payable {}
/**
* Batch-executes a series of operations
* The calldata is loaded in assembly and therefore not referred to here
*/
function deltaCompose(bytes calldata) external payable {
uint256 length;
assembly {
// the length of the calldata is stored per abi encoding standards
length := calldataload(0x24)
}
_deltaComposeInternal(
msg.sender,
0x44, // the offset is constant
length
);
// log for tracing
_deadLog();
}
/**
* Execute a set of packed operations
* @param callerAddress the address of the EOA/contract that
* initially triggered the `deltaCompose`
* - this is called within flash & swap callbacks
* - strict validations need to be made in these cases to
* prevent an entity to call this with a non-matching callerAddress
* @param currentOffset offset packed ops array
* @param calldataLength length of packed ops array
* | op0 | data0 | op1 | ...
* | 1 | ... | 1 | ...
*/
function _deltaComposeInternal(
address callerAddress,
uint256 currentOffset,
uint256 calldataLength //
)
internal
virtual
{
// data loop paramters
uint256 maxIndex;
assembly {
maxIndex := add(currentOffset, calldataLength)
}
////////////////////////////////////////////////////
// Progressively loop through the calldata
// The first byte defines the operation
// From there on, we read the data based on the
// what the operation expects, e.g. read the next 32 bytes as uint256.
//
// `currentOffset` represents the current byte at which we
// are in the calldata
// `maxIndex` is used as break criteria, this means that if
// currentOffset >= maxIndex, we iterated through
// the entire calldata.
////////////////////////////////////////////////////
while (true) {
uint256 operation;
// fetch op metadata
assembly {
operation := shr(248, calldataload(currentOffset)) // last byte
// we increment the current offset to skip the operation
currentOffset := add(1, currentOffset)
}
if (operation < ComposerCommands.PERMIT) {
if (operation == ComposerCommands.SWAPS) {
currentOffset = _swap(currentOffset, callerAddress);
} else if (operation == ComposerCommands.EXT_CALL) {
currentOffset = _callExternal(currentOffset);
} else if (operation == ComposerCommands.LENDING) {
currentOffset = _lendingOperations(callerAddress, currentOffset);
} else if (operation == ComposerCommands.TRANSFERS) {
currentOffset = _transfers(currentOffset, callerAddress);
} else {
_invalidOperation();
}
} else {
if (operation == ComposerCommands.PERMIT) {
currentOffset = _permit(currentOffset, callerAddress);
} else if (operation == ComposerCommands.FLASH_LOAN) {
currentOffset = _universalFlashLoan(currentOffset, callerAddress);
} else if (operation == ComposerCommands.ERC4626) {
currentOffset = _ERC4626Operations(currentOffset, callerAddress);
} else if (operation == ComposerCommands.GEN_2025_SINGELTONS) {
currentOffset = _gen2025DexActions(currentOffset, callerAddress);
} else {
_invalidOperation();
}
}
// break if we skipped over the calldata
if (currentOffset >= maxIndex) break;
}
// revert if some excess is left
if (currentOffset > maxIndex) revert InvalidCalldata();
}
function _universalFlashLoan(uint256 currentOffset, address callerAddress) internal virtual returns (uint256) {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";
/**
* @title All Morpho Blue flash callbacks
*/
contract MorphoFlashLoanCallback is Masks, DeltaErrors {
/// @dev Constant MorphoB address
address private constant MORPHO_BLUE = 0xD50F2DffFd62f94Ee4AEd9ca05C61d0753268aBc;
/**
* Morpho blue callbacks
*/
/// @dev Morpho Blue flash loan
function onMorphoFlashLoan(uint256, bytes calldata) external {
_onMorphoCallback();
}
/// @dev Morpho Blue supply callback
function onMorphoSupply(uint256, bytes calldata) external {
_onMorphoCallback();
}
/// @dev Morpho Blue repay callback
function onMorphoRepay(uint256, bytes calldata) external {
_onMorphoCallback();
}
/// @dev Morpho Blue supply collateral callback
function onMorphoSupplyCollateral(uint256, bytes calldata) external {
_onMorphoCallback();
}
/// @dev Morpho Blue is immutable and their flash loans are callbacks to msg.sender,
/// Since it is universal batching and the same validation for all
/// Morpho callbacks, we can use the same logic everywhere
function _onMorphoCallback() internal {
address origCaller;
uint256 calldataLength;
assembly {
// validate caller
// - extract id from params
let firstWord := calldataload(100)
switch and(UINT8_MASK, shr(88, firstWord))
case 0 {
if xor(caller(), MORPHO_BLUE) {
mstore(0, INVALID_CALLER)
revert(0, 0x4)
}
}
default {
mstore(0, INVALID_FLASH_LOAN)
revert(0, 0x4)
}
// Slice the original caller off the beginnig of the calldata
// From here on we have validated that the origCaller
// was attached in the deltaCompose function
// Otherwise, this would be a vulnerability
origCaller := shr(96, firstWord)
// shift / slice params
calldataLength := sub(calldataload(68), 21)
}
// within the flash loan, any compose operation
// can be executed
_deltaComposeInternal(
origCaller,
121, // offset is constant (100 native + 21)
calldataLength
);
}
function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {MorphoFlashLoanCallback} from "./callbacks/MorphoCallback.sol";
/**
* @title Flash loan callbacks - these are chain-specific
* @author 1delta Labs AG
*/
contract FlashLoanCallbacks is
MorphoFlashLoanCallback //
{
// override the compose
function _deltaComposeInternal(
address callerAddress,
uint256 offset,
uint256 length
)
internal
virtual
override(
MorphoFlashLoanCallback //
)
{}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {MorphoFlashLoans} from "../../../flashLoan/Morpho.sol";
import {FlashLoanCallbacks} from "./FlashLoanCallbacks.sol";
import {FlashLoanIds} from "../../../enums/DeltaEnums.sol";
import {DeltaErrors} from "../../../../shared/errors/Errors.sol";
/**
* @title Flash loan aggregator
* @author 1delta Labs AG
*/
contract UniversalFlashLoan is
MorphoFlashLoans,
FlashLoanCallbacks //
{
/**
* All flash ones in one function -what do you need more?
*/
function _universalFlashLoan(uint256 currentOffset, address callerAddress) internal virtual returns (uint256) {
uint256 flashLoanType; // architecture type
assembly {
flashLoanType := shr(248, calldataload(currentOffset)) // already masks uint8 as last byte
currentOffset := add(currentOffset, 1)
}
if (flashLoanType == FlashLoanIds.MORPHO) {
return morphoFlashLoan(currentOffset, callerAddress);
} else {
_invalidOperation();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* Author: Achthar | 1delta
* /*****************************************************************************
*/
import {ValidatorLib} from "../../../../swappers/callbacks/ValidatorLib.sol";
import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";
/**
* @title Contract Module for general Margin Trading on an borrow delegation compatible Lender
* @notice Contains main logic for uniswap-type callbacks and initiator functions
*/
abstract contract UniV2Callbacks is Masks, DeltaErrors {
// factories
bytes32 private constant SUSHISWAP_V2_FF_FACTORY = 0xff72D111b4d6f31B38919ae39779f570b747d6Acd90000000000000000000000;
bytes32 private constant SUSHISWAP_V2_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;
/**
* Generic Uniswap v2 style callbck executor
*/
function _executeUniV2IfSelector(bytes32 selector) internal {
bytes32 codeHash;
bytes32 ffFactoryAddress;
// this is a data strip that contains [tokenOut(20)|forkId(1)|calldataLength(2)|xxx...xxx(9)]
bytes32 outData;
uint256 forkId;
assembly {
outData := calldataload(204)
switch selector
case 0x10d1e85c00000000000000000000000000000000000000000000000000000000 {
forkId := and(UINT8_MASK, shr(88, outData))
ffFactoryAddress := SUSHISWAP_V2_FF_FACTORY
codeHash := SUSHISWAP_V2_CODE_HASH
}
}
if (ValidatorLib._hasData(ffFactoryAddress)) {
uint256 calldataLength;
address callerAddress;
assembly {
// revert if sender param is not this address
if xor(calldataload(4), address()) {
mstore(0, INVALID_CALLER)
revert(0, 0x4)
}
// get tokens
let tokenIn := shr(96, calldataload(184))
let tokenOut := shr(96, outData)
let ptr := mload(0x40)
switch lt(tokenIn, tokenOut)
case 0 {
mstore(add(ptr, 0x14), tokenIn)
mstore(ptr, tokenOut)
}
default {
mstore(add(ptr, 0x14), tokenOut)
mstore(ptr, tokenIn)
}
let salt
// 128 and higher is solidly
// 128-130 are reserved for the ones that have no isStable flag
switch gt(forkId, 130)
case 1 {
mstore8(
add(ptr, 0x34),
gt(forkId, 191) // store isStable (id>=192)
)
salt := keccak256(add(ptr, 0x0C), 0x29)
}
default { salt := keccak256(add(ptr, 0x0C), 0x28) }
// calculate pool address in next 4 lines
mstore(ptr, ffFactoryAddress)
mstore(add(ptr, 0x15), salt)
mstore(add(ptr, 0x35), codeHash)
// verify that the caller is a v2 type pool
if xor(and(ADDRESS_MASK, keccak256(ptr, 0x55)), caller()) {
mstore(0x0, BAD_POOL)
revert(0x0, 0x4)
}
calldataLength := and(UINT16_MASK, shr(72, outData))
// get caller address as provided in the call setup
callerAddress := shr(96, calldataload(164))
}
_deltaComposeInternal(
callerAddress,
// the naive offset is 164
// we skip the entire callback validation data
// that is tokens (+40), caller (+20), dexId (+1) datalength (+2)
// = 227
227,
calldataLength
);
// force return
assembly {
return(0, 0)
}
}
}
function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* Author: Achthar | 1delta
* /*****************************************************************************
*/
import {ValidatorLib} from "../../../../swappers/callbacks/ValidatorLib.sol";
import {Masks} from "../../../../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../../../../shared/errors/Errors.sol";
import {V3Callbacker} from "../../../../swappers/callbacks/V3Callbacker.sol";
/**
* @title Uniswap V3 type callback implementations
*/
abstract contract UniV3Callbacks is V3Callbacker, Masks, DeltaErrors {
// factory ff addresses
bytes32 private constant SUSHISWAP_V3_FF_FACTORY = 0xff203e8740894c8955cB8950759876d7E7E45E04c10000000000000000000000;
bytes32 private constant SUSHISWAP_V3_CODE_HASH = 0xe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb;
/**
* Generic UniswapV3 callback executor
* The call looks like
* function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata) external {...}
*
* Izumi deviates from this, we handle these below if it is deployed on this chain
*/
function _executeUniV3IfSelector(bytes32 selector) internal {
bytes32 codeHash;
bytes32 ffFactoryAddress;
// we use the amount to pay as shorthand here to
// allow paying without added calldata
uint256 amountToPay;
assembly {
switch selector
case 0xfa461e3300000000000000000000000000000000000000000000000000000000 {
ffFactoryAddress := SUSHISWAP_V3_FF_FACTORY
codeHash := SUSHISWAP_V3_CODE_HASH
let _amount1 := calldataload(36)
switch sgt(_amount1, 0)
case 1 { amountToPay := _amount1 }
default { amountToPay := calldataload(4) }
}
}
if (ValidatorLib._hasData(ffFactoryAddress)) {
uint256 calldataLength;
address callerAddress;
address tokenIn;
assembly {
tokenIn := shr(96, calldataload(152))
let tokenOutAndFee := calldataload(172)
let tokenOut := shr(96, tokenOutAndFee)
let s := mload(0x40)
mstore(s, ffFactoryAddress)
let p := add(s, 21)
// Compute the inner hash in-place
switch lt(tokenIn, tokenOut)
case 0 {
mstore(p, tokenOut)
mstore(add(p, 32), tokenIn)
}
default {
mstore(p, tokenIn)
mstore(add(p, 32), tokenOut)
}
switch and(FF_ADDRESS_COMPLEMENT, ffFactoryAddress)
case 0 {
// cases with fee
mstore(add(p, 64), and(UINT16_MASK, shr(72, tokenOutAndFee)))
mstore(p, keccak256(p, 96))
}
default {
// cases without fee, e.g. algebra case
mstore(p, keccak256(p, 64))
}
p := add(p, 32)
mstore(p, codeHash)
////////////////////////////////////////////////////
// If the caller is not the calculated pool, we revert
////////////////////////////////////////////////////
if xor(caller(), and(ADDRESS_MASK, keccak256(s, 85))) {
mstore(0x0, BAD_POOL)
revert(0x0, 0x4)
}
calldataLength := and(UINT16_MASK, shr(56, tokenOutAndFee))
// get original caller address
callerAddress := shr(96, calldataload(132))
}
clSwapCallback(amountToPay, tokenIn, callerAddress, calldataLength);
// force return
assembly {
return(0, 0)
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {UniV3Callbacks, V3Callbacker} from "./callbacks/UniV3Callback.sol";
import {UniV2Callbacks} from "./callbacks/UniV2Callback.sol";
/**
* @title Swap Callback executor
* @author 1delta Labs AG
*/
contract SwapCallbacks is
UniV3Callbacks,
UniV2Callbacks //
{
// override the compose
function _deltaComposeInternal(
address callerAddress,
uint256 offset,
uint256 length
)
internal
virtual
override(
V3Callbacker,
UniV2Callbacks //
)
{}
/**
* Swap callbacks are taken in the fallback
* We do this to have an easier time in validating similar callbacks
* with separate selectors
*
* We identify the selector in the fallback and then map it to the DEX
*
* Note that each "_execute..." function returns (exits) when a callback is run.
*
* If it falls through all variations, it reverts at the end.
*/
fallback() external {
bytes32 selector;
assembly {
selector :=
and(
0xffffffff00000000000000000000000000000000000000000000000000000000, // masks upper 4 bytes
calldataload(0)
)
}
_executeUniV3IfSelector(selector);
_executeUniV2IfSelector(selector);
// we do not allow a fallthrough
assembly {
revert(0, 0)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
/**
* Permit classifier enums
*/
library TransferIds {
uint256 internal constant TRANSFER_FROM = 0;
uint256 internal constant SWEEP = 1;
uint256 internal constant WRAP_NATIVE = 2;
uint256 internal constant UNWRAP_WNATIVE = 3;
uint256 internal constant PERMIT2_TRANSFER_FROM = 4;
uint256 internal constant APPROVE = 5;
}
/**
* Permit classifier enums
*/
library PermitIds {
uint256 internal constant TOKEN_PERMIT = 0;
uint256 internal constant AAVE_V3_CREDIT_PERMIT = 1;
uint256 internal constant ALLOW_CREDIT_PERMIT = 2;
}
/**
* Lender classifier enums, expected to be encoded as uint16
*/
library LenderIds {
uint256 internal constant UP_TO_AAVE_V3 = 1000;
uint256 internal constant UP_TO_AAVE_V2 = 2000;
uint256 internal constant UP_TO_COMPOUND_V3 = 3000;
uint256 internal constant UP_TO_COMPOUND_V2 = 4000;
uint256 internal constant UP_TO_MORPHO = 5000;
}
/**
* Operations enums, encoded as uint8
*/
library LenderOps {
uint256 internal constant DEPOSIT = 0;
uint256 internal constant BORROW = 1;
uint256 internal constant REPAY = 2;
uint256 internal constant WITHDRAW = 3;
uint256 internal constant DEPOSIT_LENDING_TOKEN = 4;
uint256 internal constant WITHDRAW_LENDING_TOKEN = 5;
}
/**
* Lender classifier enums, expected to be encoded as uint16
*/
library FlashLoanIds {
uint256 internal constant MORPHO = 0;
uint256 internal constant BALANCER_V2 = 1;
uint256 internal constant AAVE_V3 = 2;
uint256 internal constant AAVE_V2 = 3;
}
/**
* ERC4626 classifier enums
*/
library ERC4626Ids {
uint256 internal constant DEPOSIT = 0;
uint256 internal constant WITHDRAW = 1;
}
/**
* Uniswp V4 operations outside of swaps
*/
library Gen2025ActionIds {
uint256 internal constant UNLOCK = 0;
uint256 internal constant UNI_V4_TAKE = 1;
uint256 internal constant UNI_V4_SETTLE = 2;
uint256 internal constant UNI_V4_SYNC = 3;
uint256 internal constant BAL_V3_TAKE = 4;
uint256 internal constant BAL_V3_SETTLE = 5;
}
/// @title Commands for OneDeltaComposer
library ComposerCommands {
uint256 internal constant SWAPS = 0x10;
uint256 internal constant EXT_CALL = 0x20;
uint256 internal constant EXT_TRY_CALL = 0x21;
uint256 internal constant LENDING = 0x30;
uint256 internal constant TRANSFERS = 0x40;
uint256 internal constant PERMIT = 0x50;
uint256 internal constant FLASH_LOAN = 0x60;
uint256 internal constant ERC4626 = 0x70;
uint256 internal constant GEN_2025_SINGELTONS = 0x80;
uint256 internal constant BRIDGING = 0x90;
}
/// @title Commands for Bridge
library BridgeIds {
uint256 internal constant STARGATE_V2 = 0x00;
uint256 internal constant ACROSS = 0x0A;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {ERC4626Transfers} from "./ERC4626Transfers.sol";
import {ERC4626Ids} from "../enums/DeltaEnums.sol";
/**
* @notice ERC4626 deposit and withdraw actions
*/
abstract contract ERC4626Operations is ERC4626Transfers {
/// @notice withdraw from (e.g. morpho) vault
function _ERC4626Operations(uint256 currentOffset, address callerAddress) internal returns (uint256) {
uint256 erc4626Operation;
assembly {
erc4626Operation := shr(248, calldataload(currentOffset))
currentOffset := add(currentOffset, 1)
}
/**
* ERC6464 deposit
*/
if (erc4626Operation == ERC4626Ids.DEPOSIT) {
currentOffset = _encodeErc4646Deposit(currentOffset);
}
/**
* ERC6464 withdraw
*/
else if (erc4626Operation == ERC4626Ids.WITHDRAW) {
currentOffset = _encodeErc4646Withdraw(currentOffset, callerAddress);
} else {
_invalidOperation();
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
// solhint-disable max-line-length
/**
* @notice ERC4626 deposit and withdraw actions
*/
abstract contract ERC4626Transfers is ERC20Selectors, Masks, DeltaErrors {
/// @dev mint(...)
bytes32 private constant ERC4626_MINT = 0x94bf804d00000000000000000000000000000000000000000000000000000000;
/// @dev deposit(...)
bytes32 private constant ERC4626_DEPOSIT = 0x6e553f6500000000000000000000000000000000000000000000000000000000;
/// @dev withdraw(...)
bytes32 private constant ERC4626_WITHDRAW = 0xb460af9400000000000000000000000000000000000000000000000000000000;
/// @dev redeem(...)
bytes32 private constant ERC4626_REDEEM = 0xba08765200000000000000000000000000000000000000000000000000000000;
/// @notice Deposit to (e.g. morpho) vault
function _encodeErc4646Deposit(uint256 currentOffset) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// loan token
let asset := shr(96, calldataload(currentOffset))
currentOffset := add(currentOffset, 20)
let vaultContract := shr(96, calldataload(currentOffset))
currentOffset := add(currentOffset, 20)
let amount := shr(128, calldataload(currentOffset))
let amountToDeposit := and(UINT120_MASK, amount)
currentOffset := add(currentOffset, 16)
/**
* check if it is by shares or assets
*/
switch and(USE_SHARES_FLAG, amount)
case 0 {
mstore(ptr, ERC4626_DEPOSIT)
/**
* if the amount is zero, we assume that the contract balance is deposited
*/
if iszero(amountToDeposit) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to asset
pop(staticcall(gas(), asset, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amountToDeposit := mload(0x0)
}
}
default { mstore(ptr, ERC4626_MINT) }
mstore(add(ptr, 0x4), amountToDeposit) // shares or assets
mstore(add(ptr, 0x24), shr(96, calldataload(currentOffset))) // receiver
if iszero(call(gas(), vaultContract, 0x0, ptr, 0x44, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0x0, returndatasize())
}
currentOffset := add(currentOffset, 20)
}
return currentOffset;
}
/// @notice withdraw from (e.g. morpho) vault
function _encodeErc4646Withdraw(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
let vaultContract := shr(96, calldataload(currentOffset))
currentOffset := add(currentOffset, 20)
let amount := shr(128, calldataload(currentOffset))
let amountToWithdrawOrRedeem := and(UINT120_MASK, amount)
currentOffset := add(currentOffset, 16)
/**
* check if it is by shares or assets
*/
switch and(USE_SHARES_FLAG, amount)
case 0 {
// plain withdraw amount
mstore(ptr, ERC4626_WITHDRAW)
}
default {
// note that this covers max withdraw already as the user can apply the
// static shares amount hey own
mstore(ptr, ERC4626_REDEEM)
}
mstore(add(ptr, 0x4), amountToWithdrawOrRedeem) // shares or assets
mstore(add(ptr, 0x24), shr(96, calldataload(currentOffset))) // receiver
currentOffset := add(currentOffset, 20)
mstore(add(ptr, 0x44), callerAddress) // owner
if iszero(call(gas(), vaultContract, 0x0, ptr, 0x64, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Masks} from "../../shared/masks/Masks.sol";
/**
* @title Morpho flash loans
* @author 1delta Labs AG
*/
contract MorphoFlashLoans is Masks {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | asset |
* | 20 | 20 | pool | <-- we allow ANY morpho style pool here
* | 40 | 16 | amount |
* | 56 | 2 | paramsLength |
* | 58 | paramsLength | params |
*/
function morphoFlashLoan(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
// get token to loan
let token := shr(96, calldataload(currentOffset))
// morpho-like pool as target
let pool := shr(96, calldataload(add(currentOffset, 20)))
// second calldata slice including amount annd params length
let slice := calldataload(add(currentOffset, 40))
let amount := shr(128, slice) // shr will already mask uint128 here
// length of params
let calldataLength := and(UINT16_MASK, shr(112, slice))
// skip uint128 and uint16
currentOffset := add(currentOffset, 58)
// morpho should be the primary choice
let ptr := mload(0x40)
/**
* Prepare call
*/
// flashLoan(...)
mstore(ptr, 0xe0232b4200000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 4), token)
mstore(add(ptr, 36), amount)
mstore(add(ptr, 68), 0x60) // offset
mstore(add(ptr, 100), add(20, calldataLength)) // data length
mstore(add(ptr, 132), shl(96, callerAddress)) // caller
calldatacopy(add(ptr, 152), currentOffset, calldataLength) // calldata
if iszero(
call(
gas(),
pool,
0x0,
ptr,
add(calldataLength, 152),
0x0,
0x0 //
)
) {
returndatacopy(0, 0, returndatasize())
revert(0x0, returndatasize())
}
// increment offset
currentOffset := add(currentOffset, calldataLength)
}
return currentOffset;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {ERC20Selectors} from "contracts/1delta/shared/selectors/ERC20Selectors.sol";
import {Masks} from "contracts/1delta/shared/masks/Masks.sol";
import {DeltaErrors} from "contracts/1delta/shared/errors/Errors.sol";
contract BaseUtils is ERC20Selectors, Masks, DeltaErrors {
error InvalidAssetId(uint16 assetId);
error InsufficientValue();
error SlippageTooHigh(uint256 expected, uint256 actual);
error ZeroBalance();
error BridgeFailed();
uint256 internal constant FEE_DENOMINATOR = 1e9;
uint256 internal constant INSUFFICIENT_VALUE = 0x1101129400000000000000000000000000000000000000000000000000000000;
uint256 internal constant ZERO_BALANCE = 0x669567ea00000000000000000000000000000000000000000000000000000000;
uint256 internal constant BRIDGE_FAILED = 0xc3b9eede00000000000000000000000000000000000000000000000000000000;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {BaseUtils} from "contracts/1delta/composer/generic/BaseUtils.sol";
// solhint-disable max-line-length
/**
* @notice External call on call forwarder which can safely execute any calls for a specific selector
* without comprimising this contract
*/
abstract contract ExternalCall is BaseUtils {
/// @notice selector for deltaForwardCompose(bytes)
bytes32 private constant DELTA_FORWARD_COMPOSE = 0x6a0c90ff00000000000000000000000000000000000000000000000000000000;
/**
* This is not a real external call, this one has a pre-determined selector
* that prevents collision with any calls that can be made in this contract
* This prevents unauthorized calls that would pull funds from other users
*
* On top of that, this makes the contract arbitrarily extensible.
*/
function _callExternal(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | target |
* | 20 | 16 | nativeValue |
* | 36 | 2 | calldataLength |
* | 38 | calldataLength | calldata |
*/
assembly {
let target := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let callValue := shr(128, calldataload(currentOffset))
currentOffset := add(16, currentOffset)
let dataLength := shr(240, calldataload(currentOffset))
currentOffset := add(2, currentOffset)
// this is a slightly different behavior as, unlike for ERC20, the
// 0-value is a commonly used one, as such, a flag is used for this
switch and(NATIVE_FLAG, callValue)
case 0 { callValue := and(callValue, UINT120_MASK) }
default { callValue := selfbalance() }
// free memo ptr for populating the tx
let ptr := mload(0x40)
mstore(ptr, DELTA_FORWARD_COMPOSE)
mstore(add(ptr, 0x4), 0x20) // offset
mstore(add(ptr, 0x24), dataLength) // length
// copy calldata
calldatacopy(add(ptr, 0x44), currentOffset, dataLength)
if iszero(
call(
gas(),
target,
callValue,
ptr, //
add(0x44, dataLength),
//selector plus 0x44 (selector, offset, length)
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// increment offset by data length
currentOffset := add(currentOffset, dataLength)
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
// solhint-disable max-line-length
/**
* @notice Lending base contract that wraps multiple Aave lender types (V2, V3, non-ir mode based).
*/
abstract contract AaveLending is ERC20Selectors, Masks {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | aToken |
* | 96 | 20 | pool |
*/
/// @notice Withdraw from lender lastgiven user address and lender Id
function _withdrawFromAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// Aave types need to trasfer collateral tokens
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
let amount := and(UINT120_MASK, amountData)
// get aToken
let collateralToken := shr(96, calldataload(add(currentOffset, 56)))
// skip to end
currentOffset := add(currentOffset, 76)
// apply max if needed
switch amount
case 0xffffffffffffffffffffffffffff {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add caller address as parameter
mstore(0x04, callerAddress)
// call to collateral token
pop(staticcall(gas(), collateralToken, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
/**
* PREPARE TRANSFER_FROM USER
*/
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), address())
mstore(add(ptr, 0x44), amount)
let success := call(gas(), collateralToken, 0x0, ptr, 0x64, 0x0, 0x20)
let rdsize := returndatasize()
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0x0), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0x0, 0x0, rdsize)
revert(0x0, rdsize)
}
// selector withdraw(address,uint256,address)
mstore(ptr, 0x69328dec00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), receiver)
let pool := shr(96, calldataload(currentOffset))
// skip token (end of data)
currentOffset := add(currentOffset, 20)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x64, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 1 | mode |
* | 77 | 20 | pool |
*/
function _borrowFromAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
let receiverAndMode := calldataload(add(currentOffset, 36))
// receiver
let receiver := shr(96, receiverAndMode)
let mode := and(UINT8_MASK, shr(88, receiverAndMode))
// get pool
let pool := shr(96, calldataload(add(currentOffset, 57)))
// skip pool (end of data)
currentOffset := add(currentOffset, 77)
let amount := and(UINT120_MASK, amountData)
let ptr := mload(0x40)
switch mode
case 0 {
// borrowing with no irMode (special aave forks)
// selector borrow(address,uint256,uint16,address)
mstore(ptr, 0x1d5d723700000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), 0x0)
mstore(add(ptr, 0x64), callerAddress)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
default {
// selector borrow(address,uint256,uint256,uint16,address)
mstore(ptr, 0xa415bcad00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), mode)
mstore(add(ptr, 0x64), 0x0)
mstore(add(ptr, 0x84), callerAddress)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0xA4, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
// transfer underlying if needed
if xor(receiver, address()) {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
// mstore(add(ptr, 0x24), amount) <-- this one is still in this memo location
let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | pool |
*/
/// @notice Withdraw from lender lastgiven user address and lender Id
function _depositToAaveV3(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get pool
let pool := shr(96, calldataload(add(currentOffset, 56)))
// skip pool (end of data)
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
// zero is this balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40)
// selector supply(address,uint256,address,uint16)
mstore(ptr, 0x617ba03700000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), receiver)
mstore(add(ptr, 0x64), 0x0)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | pool |
*/
/// @notice Withdraw from lender lastgiven user address and lender Id
function _depositToAaveV2(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get pool
let pool := shr(96, calldataload(add(currentOffset, 56)))
// skip pool (end of data)
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
// zero is this balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(
staticcall(
gas(),
underlying, // token
0x0,
0x24,
0x0,
0x20
)
)
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40)
// selector deposit(address,uint256,address,uint16)
mstore(ptr, 0xe8eda9df00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), receiver)
mstore(add(ptr, 0x64), 0x0)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 1 | mode |
* | 77 | 20 | debtToken |
* | 97 | 20 | pool |
*/
function _repayToAave(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
let receiverAndMode := calldataload(add(currentOffset, 36))
// receiver
let receiver := shr(96, receiverAndMode)
let mode := and(UINT8_MASK, shr(88, receiverAndMode))
let amount := and(UINT120_MASK, amountData)
switch amount
case 0 {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
// safe repay maximum: fetch contract balance and user debt and take minimum
case 0xffffffffffffffffffffffffffff {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x4, 0x20))
// load the retrieved balance
amount := mload(0x4)
// add caller address as parameter
mstore(0x04, callerAddress)
// call to debt token
pop(staticcall(gas(), shr(96, calldataload(add(currentOffset, 57))), 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
let borrowBalance := mload(0x0)
// if borrow balance is less than the amount, select borrow balance
if lt(borrowBalance, amount) { amount := borrowBalance }
}
// get pool
let pool := shr(96, calldataload(add(currentOffset, 77)))
// skip pool (end of data)
currentOffset := add(currentOffset, 97)
let ptr := mload(0x40)
// some Aaves dropped the IR mode, mode=0 is using their selector
switch mode
case 0 {
// selector repay(address,uint256,address)
mstore(ptr, 0x5ceae9c400000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), receiver)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x64, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
default {
// selector repay(address,uint256,uint256,address)
mstore(ptr, 0x573ade8100000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), underlying)
mstore(add(ptr, 0x24), amount)
mstore(add(ptr, 0x44), mode)
mstore(add(ptr, 0x64), receiver)
// call pool
if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
// solhint-disable max-line-length
/**
* @notice Lending base contract that wraps multiple Compound V2 lender types.
* Most effective for Venus
*/
abstract contract CompoundV2Lending is ERC20Selectors, Masks {
/*
* Note this is for Venus Finance only as other Compound forks
* do not have this feature.
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | cToken |
*/
function _borrowFromCompoundV2(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// Compound V3 types need to trasfer collateral tokens
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
let cToken := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
// selector for borrowBehlaf(address,uint256)
mstore(ptr, 0x856e5bb300000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), callerAddress) // user
mstore(add(ptr, 0x24), amount) // to this address
if iszero(call(gas(), cToken, 0x0, ptr, 0x44, ptr, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
if xor(address(), receiver) {
switch underlying
// native case
case 0 { if iszero(call(gas(), receiver, amount, 0, 0, 0, 0)) { revert(0, 0) } }
// erc20 case
default {
// 4) TRANSFER TO RECIPIENT
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amount)
let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | cToken |
*/
function _withdrawFromCompoundV2(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// Compound V2 types need to trasfer collateral tokens
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
let cToken := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
if eq(amount, 0xffffffffffffffffffffffffffff) {
// selector for balanceOfUnderlying(address)
mstore(0, 0x3af9e66900000000000000000000000000000000000000000000000000000000)
// add caller address as parameter
mstore(0x04, callerAddress)
// call to token
pop(
call(
gas(),
cToken, // collateral token
0x0,
0x0,
0x24,
0x0,
0x20
)
)
// load the retrieved balance
amount := mload(0x0)
}
// 1) CALCULTAE TRANSFER AMOUNT
// Store fnSig (=bytes4(abi.encodeWithSignature("exchangeRateCurrent()"))) at params
// - here we store 32 bytes : 4 bytes of fnSig and 28 bytes of RIGHT padding
mstore(
0x0,
0xbd6d894d00000000000000000000000000000000000000000000000000000000 // with padding
)
// call to collateralToken
// accrues interest. No real risk of failure.
pop(
call(
gas(),
cToken,
0x0,
0x0,
0x24,
0x0, // store back to ptr
0x20
)
)
// load the retrieved protocol share
let refAmount := mload(0x0)
// calculate collateral token amount, rounding up
let cTokenTransferAmount :=
add(
div(
mul(amount, 1000000000000000000), // multiply with 1e18
refAmount // divide by rate
),
1
)
// FETCH BALANCE
// selector for balanceOf(address)
mstore(0x0, ERC20_BALANCE_OF)
// add _from address as parameter
mstore(0x4, callerAddress)
// call to collateralToken
pop(staticcall(gas(), cToken, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
refAmount := mload(0x0)
// floor to the balance
if gt(cTokenTransferAmount, refAmount) { cTokenTransferAmount := refAmount }
// 2) TRANSFER VTOKENS
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress) // from user
mstore(add(ptr, 0x24), address()) // to this address
mstore(add(ptr, 0x44), cTokenTransferAmount)
let success := call(gas(), cToken, 0, ptr, 0x64, ptr, 32)
if iszero(success) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// 3) REDEEM
// selector for redeem(uint256)
mstore(0, 0xdb006a7500000000000000000000000000000000000000000000000000000000)
mstore(0x4, cTokenTransferAmount)
if iszero(
call(
gas(),
cToken,
0x0,
0x0, // input = selector
0x24, // input selector + uint256
0x0, // output
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// transfer tokens only if the receiver is not this address
if xor(address(), receiver) {
// 4) TRANSFER TO RECIPIENT
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amount)
success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
return currentOffset;
}
/*
* Note: Some Compound V2 forks might not have this feature and need a separate
* function.
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | cToken |
*/
/// @notice Withdraw from lender lastgiven user address and lender Id
function _depositToCompoundV2(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get cToken
let cToken := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
switch underlying
// case native
case 0 {
let amount
amount := and(UINT120_MASK, amountData)
// zero is this balance
if iszero(amount) { amount := selfbalance() }
// selector for mint()
mstore(0, 0x1249c58b00000000000000000000000000000000000000000000000000000000)
if iszero(call(gas(), cToken, amount, 0x0, 0x4, 0x0, 0x0)) {
returndatacopy(0x0, 0, returndatasize())
revert(0x0, returndatasize())
}
// need to transfer collateral to receiver
if xor(receiver, address()) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), cToken, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
let cBalance := mload(0x0)
let ptr := mload(0x40)
// TRANSFER COLLATERAL
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), cBalance)
let success := call(gas(), cToken, 0, ptr, 0x44, ptr, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
// erc20 case
default {
let amount
amount := and(UINT120_MASK, amountData)
// zero is this balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40)
// selector for mintBehalf(address,uint256)
mstore(ptr, 0x23323e0300000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amount)
if iszero(call(gas(), cToken, 0x0, ptr, 0x44, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | cToken |
*/
function _repayToCompoundV2(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get comet
let cToken := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let ptr := mload(0x40)
switch underlying
// case native
case 0 {
let amount := and(UINT120_MASK, amountData)
switch amount
case 0 {
// load the retrieved balance
amount := selfbalance()
}
// safe repay the maximum
case 0xffffffffffffffffffffffffffff {
// contract balance
amount := selfbalance()
// selector for borrowBalanceCurrent(address)
mstore(0, 0x17bfdfbc00000000000000000000000000000000000000000000000000000000)
// add this address as parameter
mstore(0x04, receiver)
// call to token
pop(call(gas(), cToken, 0x0, 0x0, 0x24, 0x0, 0x20))
// borrow balance smaller than amount available - use max
// otherwise, repay whatever is in the contract
if lt(mload(0x0), amount) { amount := MAX_UINT256 }
}
// selector for repayBorrowBehalf(address)
mstore(0, 0xe597461900000000000000000000000000000000000000000000000000000000)
mstore(4, receiver) // user
if iszero(
call(
gas(),
cToken,
amount,
0, // input = empty for fallback
0x24, // input size = selector + address + uint256
0, // output
0x0 // output size = zero
)
) {
returndatacopy(0x0, 0, returndatasize())
revert(0x0, returndatasize())
}
}
// case ERC20
default {
let amount := and(UINT120_MASK, amountData)
switch amount
case 0 {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
// safe repay the maximum
case 0xffffffffffffffffffffffffffff {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
// selector for borrowBalanceCurrent(address)
mstore(0, 0x17bfdfbc00000000000000000000000000000000000000000000000000000000)
// add this address as parameter
mstore(0x04, receiver)
// call to collateral token
pop(call(gas(), cToken, 0x0, 0x0, 0x24, 0x0, 0x20))
// borrow balance smaller than amount available - use max
// otherwise, repay whatever is in the contract
if lt(mload(0x0), amount) { amount := MAX_UINT256 }
}
// selector for repayBorrowBehalf(address,uint256)
mstore(ptr, 0x2608f81800000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), receiver) // user
mstore(add(ptr, 0x24), amount) // to this address
if iszero(
call(
gas(),
cToken,
0x0,
ptr, // input = empty for fallback
0x44, // input size = selector + address + uint256
ptr, // output
0x0 // output size = zero
)
) {
returndatacopy(0x0, 0, returndatasize())
revert(0x0, returndatasize())
}
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
// solhint-disable max-line-length
/**
* @notice Lending base contract that wraps Cmpound V3 markets
*/
abstract contract CompoundV3Lending is ERC20Selectors, Masks {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 1 | isBase |
* | 77 | 20 | pool |
*/
function _withdrawFromCompoundV3(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// Compound V3 types need to trasfer collateral tokens
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
let isBase := calldataload(add(currentOffset, 36))
// receiver
let receiver := shr(96, isBase)
// adjust isBase flag
let cometPool := shr(96, calldataload(add(currentOffset, 57)))
currentOffset := add(currentOffset, 77)
let amount := and(UINT120_MASK, amountData)
if eq(amount, 0xffffffffffffffffffffffffffff) {
switch and(UINT8_MASK, shr(88, isBase))
case 0 {
// selector for userCollateral(address,address)
mstore(ptr, 0x2b92a07d00000000000000000000000000000000000000000000000000000000)
// add caller address as parameter
mstore(add(ptr, 0x04), callerAddress)
// add underlying address
mstore(add(ptr, 0x24), underlying)
// call to comet
pop(staticcall(gas(), cometPool, ptr, 0x44, ptr, 0x20))
// load the retrieved balance (lower 128 bits)
amount := and(UINT128_MASK, mload(ptr))
}
// comet.balanceOf(...) is lending token balance
default {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add caller address as parameter
mstore(0x04, callerAddress)
// call to comet
pop(staticcall(gas(), cometPool, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
}
// selector withdrawFrom(address,address,address,uint256)
mstore(ptr, 0x2644131800000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), receiver)
mstore(add(ptr, 0x44), underlying)
mstore(add(ptr, 0x64), amount)
// call pool
if iszero(call(gas(), cometPool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | comet |
*/
function _borrowFromCompoundV3(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptr := mload(0x40)
// Compound V3 types need to trasfer collateral tokens
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
let cometPool := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
// selector withdrawFrom(address,address,address,uint256)
mstore(ptr, 0x2644131800000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), receiver)
mstore(add(ptr, 0x44), underlying)
mstore(add(ptr, 0x64), amount)
// call pool
if iszero(call(gas(), cometPool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | comet |
*/
/// @notice Withdraw from lender lastgiven user address and lender Id
function _depositToCompoundV3(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get comet
let comet := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
// zero is this balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40)
// selector supplyTo(address,address,uint256)
mstore(ptr, 0x4232cd6300000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), underlying)
mstore(add(ptr, 0x44), amount)
// call pool
if iszero(call(gas(), comet, 0x0, ptr, 0x64, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | underlying |
* | 20 | 16 | amount |
* | 36 | 20 | receiver |
* | 76 | 20 | comet |
*/
function _repayToCompoundV3(uint256 currentOffset) internal returns (uint256) {
assembly {
let underlying := shr(96, calldataload(currentOffset))
// offset for amount at lower bytes
let amountData := shr(128, calldataload(add(currentOffset, 20)))
// receiver
let receiver := shr(96, calldataload(add(currentOffset, 36)))
// get comet
let comet := shr(96, calldataload(add(currentOffset, 56)))
currentOffset := add(currentOffset, 76)
let amount := and(UINT120_MASK, amountData)
switch amount
case 0 {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
}
// repay maximum safely
// comet will fail when using blind maxima if the contract has not
// enough balance
// to prevent this, we read the contract balance and user borrow balance and take the minimum
case 0xffffffffffffffffffffffffffff {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), underlying, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amount := mload(0x0)
// selector for borrowBalanceOf(address)
mstore(0, 0x374c49b400000000000000000000000000000000000000000000000000000000)
// add receiver as parameter
mstore(0x04, receiver)
// call to comet
pop(staticcall(gas(), comet, 0x0, 0x24, 0x0, 0x20))
let userBorrowBalance := mload(0x0)
// amount greater than borrow balance -> use borrow balance
// otherwise repay less than the borrow balance safely
if gt(amount, userBorrowBalance) { amount := userBorrowBalance }
}
let ptr := mload(0x40)
// selector supplyTo(address,address,uint256)
mstore(ptr, 0x4232cd6300000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), underlying)
mstore(add(ptr, 0x44), amount)
// call pool
if iszero(call(gas(), comet, 0x0, ptr, 0x64, 0x0, 0x0)) {
returndatacopy(0x0, 0x0, returndatasize())
revert(0x0, returndatasize())
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ERC20Selectors} from "../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../shared/masks/Masks.sol";
/**
* @notice Lending base contract that wraps Morpho Blue
*/
abstract contract MorphoLending is ERC20Selectors, Masks {
/// @dev Constant MorphoB address
// address internal constant MORPHO_BLUE = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;
/// @dev position(...)
bytes32 private constant MORPHO_POSITION = 0x93c5206200000000000000000000000000000000000000000000000000000000;
/// @dev market(...)
bytes32 private constant MORPHO_MARKET = 0x5c60e39a00000000000000000000000000000000000000000000000000000000;
/// @dev repay(...)
bytes32 private constant MORPHO_REPAY = 0x20b76e8100000000000000000000000000000000000000000000000000000000;
/// @dev supplyCollateral(...)
bytes32 private constant MORPHO_SUPPLY_COLLATERAL = 0x238d657900000000000000000000000000000000000000000000000000000000;
/// @dev supply(...)
bytes32 private constant MORPHO_SUPPLY = 0xa99aad8900000000000000000000000000000000000000000000000000000000;
/// @dev borrow(...)
bytes32 private constant MORPHO_BORROW = 0x50d8cd4b00000000000000000000000000000000000000000000000000000000;
/// @dev withdrawCollateral(...)
bytes32 private constant MORPHO_WITHDRAW_COLLATERAL = 0x8720316d00000000000000000000000000000000000000000000000000000000;
/**
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | MarketParams.loanToken |
* | 20 | 20 | MarketParams.collateralToken |
* | 40 | 20 | MarketParams.oracle |
* | 60 | 20 | MarketParams.irm |
* | 80 | 16 | MarketParams.lltv |
* | 96 | 1 | Assets or Shares |
* | 97 | 15 | Amount (borrowAm) |
* | 112 | 20 | receiver |
* | 132 | 20 | morpho | <-- we allow all morphos (incl forks)
*/
function _morphoBorrow(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
// morpho should be the primary choice
let ptr := mload(0x40)
// borrow(...)
mstore(ptr, MORPHO_BORROW)
// market data
mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
let lltvAndAmount := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
let borrowAm := and(UINT112_MASK, lltvAndAmount)
/**
* check if it is by shares or assets
*/
switch and(USE_SHARES_FLAG, lltvAndAmount)
case 0 {
mstore(add(ptr, 164), borrowAm) // assets
mstore(add(ptr, 196), 0) // shares
}
default {
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), borrowAm) // shares
}
// onbehalf
mstore(add(ptr, 228), callerAddress) // onBehalfOf
let lastBit := calldataload(add(currentOffset, 112))
mstore(add(ptr, 260), shr(96, lastBit)) // receiver
let morpho := shr(96, calldataload(add(currentOffset, 132)))
currentOffset := add(currentOffset, 152)
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
292, // = 9 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
/**
* This deposits LENDING TOKEN
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | MarketParams.loanToken |
* | 20 | 20 | MarketParams.collateralToken |
* | 40 | 20 | MarketParams.oracle |
* | 60 | 20 | MarketParams.irm |
* | 80 | 16 | MarketParams.lltv |
* | 96 | 1 | Assets or Shares |
* | 97 | 15 | Amount (depositAm) |
* | 112 | 20 | receiver |
* | 132 | 20 | morpho | <-- we allow all morphos (incl forks)
* | 152 | 2 | calldataLength |
* | 154 | calldataLength | calldata |
*/
function _encodeMorphoDeposit(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
let ptrBase := mload(0x40)
let ptr := add(128, ptrBase)
// loan token
let token := shr(96, calldataload(currentOffset))
// supply(...)
mstore(ptr, MORPHO_SUPPLY)
// market data
mstore(add(ptr, 4), token) // MarketParams.loanToken
mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
let lltvAndAmount := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
let amountToDeposit := and(UINT112_MASK, lltvAndAmount)
// increment for the amounts
/**
* check if it is by shares or assets
*/
switch and(USE_SHARES_FLAG, lltvAndAmount)
case 0 {
/**
* if the amount is zero, we assume that the contract balance is deposited
*/
if iszero(amountToDeposit) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amountToDeposit := mload(0x0)
}
mstore(add(ptr, 164), amountToDeposit) // assets
mstore(add(ptr, 196), 0) // shares
}
default {
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), amountToDeposit) // shares
}
// receiver address
let receiver := shr(96, calldataload(add(currentOffset, 112)))
let morpho := shr(96, calldataload(add(currentOffset, 132)))
// get calldatalength
let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
let calldataLength := inputCalldataLength
currentOffset := add(currentOffset, 154)
// leftover params
mstore(add(ptr, 228), receiver) // onBehalfOf is the receiver here
mstore(add(ptr, 260), 0x120) // offset
// add calldata if needed
if xor(0, calldataLength) {
calldataLength := add(calldataLength, 20)
mstore(add(ptr, 324), shl(96, callerAddress)) // caller
calldatacopy(add(ptr, 344), currentOffset, inputCalldataLength) // calldata
currentOffset := add(currentOffset, inputCalldataLength)
}
mstore(add(ptr, 292), calldataLength) // calldatalength
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
add(calldataLength, 324), // = 10 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
/**
* This deposits COLLATERAL - never uses shares
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 20 | MarketParams.loanToken |
* | 20 | 20 | MarketParams.collateralToken |
* | 40 | 20 | MarketParams.oracle |
* | 60 | 20 | MarketParams.irm |
* | 80 | 16 | MarketParams.lltv |
* | 96 | 16 | Amount (depositAm) |
* | 112 | 20 | receiver |
* | 132 | 20 | morpho | <-- we allow all morphos (incl forks)
* | 152 | 2 | calldataLength |
* | 154 | calldataLength | calldata |
*/
function _encodeMorphoDepositCollateral(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
// use two memory ranges
let ptrBase := mload(0x40)
let ptr := add(256, ptrBase)
// supplyCollateral(...)
mstore(ptr, MORPHO_SUPPLY_COLLATERAL)
mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
// get the collateral token and approve if needed
let token := shr(96, calldataload(add(currentOffset, 20)))
mstore(add(ptr, 36), token) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
let lltvAndAmount := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
// we ignore flags as this only allows assets
let amountToDeposit := and(UINT120_MASK, lltvAndAmount)
/**
* if the amount is zero, we assume that the contract balance is deposited
*/
if iszero(amountToDeposit) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amountToDeposit := mload(0x0)
}
// receiver address
let receiver := shr(96, calldataload(add(currentOffset, 112)))
mstore(add(ptr, 164), amountToDeposit) // assets
mstore(add(ptr, 196), receiver) // onBehalfOf
mstore(add(ptr, 228), 0x100) // offset
// get morpho
let morpho := shr(96, calldataload(add(currentOffset, 132)))
// get calldatalength
let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
let calldataLength := inputCalldataLength
currentOffset := add(currentOffset, 154)
// add calldata if needed
if xor(0, calldataLength) {
calldataLength := add(calldataLength, 20)
mstore(add(ptr, 292), shl(96, callerAddress)) // caller
calldatacopy(add(ptr, 312), currentOffset, inputCalldataLength) // calldata
currentOffset := add(currentOffset, inputCalldataLength)
}
mstore(add(ptr, 260), calldataLength) // calldatalength
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
add(calldataLength, 292), // = 10 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
/// @notice Withdraw collateral from Morpho Blue
function _encodeMorphoWithdrawCollateral(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
// morpho should be the primary choice
let ptr := mload(0x40)
// withdrawCollateral(...)
mstore(ptr, MORPHO_WITHDRAW_COLLATERAL)
// market stuff
mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
let lltvAndAmount := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
mstore(add(ptr, 196), callerAddress) // onBehalfOf
// store receiver
mstore(add(ptr, 228), shr(96, calldataload(add(currentOffset, 112)))) // receiver
// skip receiver in offset
let morpho := shr(96, calldataload(add(currentOffset, 132)))
// get amount, ignore flags
lltvAndAmount := and(UINT120_MASK, lltvAndAmount)
// technically not needed, hwoever, we keep it consistent
// to withdraw all like this - maxUnit112 means read collateral balance
if eq(lltvAndAmount, 0xffffffffffffffffffffffffffff) {
let ptrBase := add(ptr, 280)
let marketId := keccak256(add(ptr, 4), 160)
// position datas (1st slot of return data is the user shares)
mstore(ptrBase, MORPHO_POSITION)
mstore(add(ptrBase, 0x4), marketId)
mstore(add(ptrBase, 0x24), callerAddress)
if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x60)) { revert(0x0, 0x0) }
lltvAndAmount := mload(add(ptrBase, 0x40))
}
// amount is stored last
mstore(add(ptr, 164), lltvAndAmount) // assets
currentOffset := add(currentOffset, 152)
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
260, // = 8 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
/// @notice Withdraw borrowAsset from Morpho
function _encodeMorphoWithdraw(uint256 currentOffset, address callerAddress) internal returns (uint256) {
assembly {
// morpho should be the primary choice
let ptrBase := mload(0x40)
let ptr := add(ptrBase, 256)
// market data
mstore(add(ptr, 4), shr(96, calldataload(currentOffset))) // MarketParams.loanToken
mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
let lltvAndAmount := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, lltvAndAmount)) // MarketParams.lltv
let withdrawAm := and(UINT120_MASK, lltvAndAmount)
mstore(add(ptr, 228), callerAddress) // onBehalfOf
mstore(add(ptr, 260), shr(96, calldataload(add(currentOffset, 112)))) // receiver
// get morpho
let morpho := shr(96, calldataload(add(currentOffset, 132)))
currentOffset := add(currentOffset, 152)
/**
* check if it is by shares or assets
* 0 => by assets
* 1 => by shares
*/
switch and(USE_SHARES_FLAG, lltvAndAmount)
case 0 {
/**
* Withdraw amount variations
* type(uint120).max: user supply balance
* other: amount provided
*/
switch withdrawAm
// maximum uint112 means withdraw everything
case 0xffffffffffffffffffffffffffff {
// we need to fetch user shares and just withdraw all shares
// https://docs.morpho.org/morpho/tutorials/manage-positions/#repayAll
let marketId := keccak256(add(ptr, 4), 160)
// position datas (1st slot of return data is the user shares)
mstore(ptrBase, MORPHO_POSITION)
mstore(add(ptrBase, 0x4), marketId)
mstore(add(ptrBase, 0x24), callerAddress)
if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x20)) { revert(0x0, 0x0) }
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), mload(ptrBase)) // shares
}
// explicit amount
default {
mstore(add(ptr, 164), withdrawAm) // assets
mstore(add(ptr, 196), 0) // shares
}
}
default {
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), withdrawAm) // shares
}
// withdraw(...)
// we have to do it like this to override the selector only in this memory position
mstore(sub(ptr, 28), 0x5c2bea49)
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
292, // = 9 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
/// @notice Repay to morpho blue
function _morphoRepay(
uint256 currentOffset,
address callerAddress
)
internal
returns (
// this will be returned as the offset, but initialized as lltvAndAmount
// we use it here to avoid stack-too deep
uint256 tempData
)
{
assembly {
// morpho should be the primary choice
let ptrBase := mload(0x40)
let ptr := add(ptrBase, 256)
let token := shr(96, calldataload(currentOffset))
// market data
mstore(add(ptr, 4), token) // MarketParams.loanToken
mstore(add(ptr, 36), shr(96, calldataload(add(currentOffset, 20)))) // MarketParams.collateralToken
mstore(add(ptr, 68), shr(96, calldataload(add(currentOffset, 40)))) // MarketParams.oracle
mstore(add(ptr, 100), shr(96, calldataload(add(currentOffset, 60)))) // MarketParams.irm
tempData := calldataload(add(currentOffset, 80))
mstore(add(ptr, 132), shr(128, tempData)) // MarketParams.lltv
let repayAm := and(UINT120_MASK, tempData)
// skip amounts
// receiver address
let receiver := shr(96, calldataload(add(currentOffset, 112)))
let morpho := shr(96, calldataload(add(currentOffset, 132)))
/**
* if repayAmount is Max -> repay safe maximum (to prevent too low contract balance to revert)
* else if repayAmount is 0 -> repay contract balance as assets
* else repay amount as shares or assets, based on flag set
*/
switch repayAm
case 0xffffffffffffffffffffffffffff {
// get the contract balance
mstore(0x0, ERC20_BALANCE_OF)
mstore(0x04, address())
if iszero(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20)) { revert(0x0, 0x0) }
// this is the maximum we can repay
repayAm := mload(0x0)
// by assets safe - will not revert if too much is repaid
// we need to fetch everything and acrure interest
// https://docs.morpho.org/morpho/tutorials/manage-positions/#repayAll
// accrue interest
// add accrueInterest (0x151c1ade)
mstore(sub(ptr, 28), 0x151c1ade)
if iszero(call(gas(), morpho, 0x0, ptr, 0xA4, 0x0, 0x0)) { revert(0x0, 0x0) }
// get market params for conversion
let marketId := keccak256(add(ptr, 4), 160)
mstore(0x0, MORPHO_MARKET)
mstore(0x4, marketId)
if iszero(staticcall(gas(), morpho, 0x0, 0x24, ptrBase, 0x80)) { revert(0x0, 0x0) }
let totalBorrowAssets := mload(add(ptrBase, 0x40))
let totalBorrowShares := mload(add(ptrBase, 0x60))
// position datas
mstore(ptrBase, MORPHO_POSITION)
mstore(add(ptrBase, 0x4), marketId)
mstore(add(ptrBase, 0x24), receiver)
if iszero(staticcall(gas(), morpho, ptrBase, 0x44, ptrBase, 0x40)) { revert(0x0, 0x0) }
let userBorrowShares := mload(add(ptrBase, 0x20))
// mulDivUp(shares, totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
let maxAssets := add(totalBorrowShares, 1000000) // VIRTUAL_SHARES=1e6
maxAssets :=
div(
add(
mul(userBorrowShares, add(totalBorrowAssets, 1)), // VIRTUAL_ASSETS=1
sub(maxAssets, 1) //
),
maxAssets //
)
// if maxAssets is greater than repay amount
// we repay whatever is possible
switch gt(maxAssets, repayAm)
case 1 {
mstore(add(ptr, 164), repayAm) // assets
mstore(add(ptr, 196), 0) // shares
}
// otherwise, repay all shares, leaving no dust
default {
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), userBorrowShares) // shares
}
}
// by balance (using assets)
case 0 {
// get balance
mstore(0x0, ERC20_BALANCE_OF)
mstore(0x04, address())
if iszero(staticcall(gas(), token, 0x0, 0x24, 0x0, 0x20)) { revert(0x0, 0x0) }
// use balance by assets
mstore(add(ptr, 164), mload(0x0)) // assets
mstore(add(ptr, 196), 0) // shares
}
// plain amount (assets or shares)
default {
switch and(USE_SHARES_FLAG, tempData)
case 0 {
// by assets
mstore(add(ptr, 164), repayAm) // assets
mstore(add(ptr, 196), 0) // shares
}
default {
// by shares
mstore(add(ptr, 164), 0) // assets
mstore(add(ptr, 196), repayAm) // shares
}
}
mstore(add(ptr, 228), receiver) // onBehalfOf is the receiver here
mstore(add(ptr, 260), 0x120) // offset
// get calldatalength
let inputCalldataLength := and(UINT16_MASK, shr(240, calldataload(add(currentOffset, 152))))
let calldataLength := inputCalldataLength
currentOffset := add(currentOffset, 154)
// add calldata if needed
if xor(0, calldataLength) {
calldataLength := add(calldataLength, 20)
mstore(add(ptr, 324), shl(96, callerAddress)) // caller
calldatacopy(add(ptr, 344), currentOffset, inputCalldataLength) // calldata
currentOffset := add(currentOffset, inputCalldataLength)
}
// repay(...)
// we have to do it like this to override the selector only in this memory position
mstore(sub(ptr, 28), 0x20b76e81)
mstore(add(ptr, 292), calldataLength) // calldatalength
if iszero(
call(
gas(),
morpho,
0x0,
ptr,
add(calldataLength, 324), // = 10 * 32 + 4
0x0,
0x0 //
)
) {
let rdlen := returndatasize()
returndatacopy(0, 0, rdlen)
revert(0x0, rdlen)
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {AaveLending} from "./AaveLending.sol";
import {CompoundV3Lending} from "./CompoundV3Lending.sol";
import {CompoundV2Lending} from "./CompoundV2Lending.sol";
import {MorphoLending} from "./MorphoLending.sol";
import {LenderIds, LenderOps} from "../enums/DeltaEnums.sol";
import {DeltaErrors} from "contracts/1delta/shared/errors/Errors.sol";
// solhint-disable max-line-length
/**
* @notice Merge all lending ops in one operation
* Can inject parameters
* - paramPush for receiving funds (e.g. receiving funds from swaps or flash loans)
* - paramPull for being required to pay an exact amount (e.g. DEX swap payments, flash loan amounts)
*/
abstract contract UniversalLending is AaveLending, CompoundV3Lending, CompoundV2Lending, MorphoLending, DeltaErrors {
/**
* execute ANY lending operation across various lenders
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 1 | lendingOperation |
* | 1 | 2 | lender |
* | 3 | variable | rest |
*/
function _lendingOperations(
address callerAddress,
uint256 currentOffset // params similar to deltaComposeInternal
)
internal
returns (uint256)
{
uint256 lendingOperation;
uint256 lender;
assembly {
let slice := calldataload(currentOffset)
lendingOperation := shr(248, slice)
lender := and(UINT16_MASK, shr(232, slice))
currentOffset := add(currentOffset, 3)
}
/**
* Deposit collateral
*/
if (lendingOperation == LenderOps.DEPOSIT) {
if (lender < LenderIds.UP_TO_AAVE_V3) {
return _depositToAaveV3(currentOffset);
} else if (lender < LenderIds.UP_TO_AAVE_V2) {
return _depositToAaveV2(currentOffset);
} else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
return _depositToCompoundV3(currentOffset);
} else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
return _depositToCompoundV2(currentOffset);
} else {
return _encodeMorphoDepositCollateral(currentOffset, callerAddress);
}
}
/**
* Borrow
*/
else if (lendingOperation == LenderOps.BORROW) {
if (lender < LenderIds.UP_TO_AAVE_V2) {
return _borrowFromAave(currentOffset, callerAddress);
} else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
return _borrowFromCompoundV3(currentOffset, callerAddress);
} else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
return _borrowFromCompoundV2(currentOffset, callerAddress);
} else {
return _morphoBorrow(currentOffset, callerAddress);
}
}
/**
* Repay
*/
else if (lendingOperation == LenderOps.REPAY) {
if (lender < LenderIds.UP_TO_AAVE_V2) {
return _repayToAave(currentOffset, callerAddress);
} else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
return _repayToCompoundV3(currentOffset);
} else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
return _repayToCompoundV2(currentOffset);
} else {
return _morphoRepay(currentOffset, callerAddress);
}
}
/**
* Withdraw collateral
*/
else if (lendingOperation == LenderOps.WITHDRAW) {
if (lender < LenderIds.UP_TO_AAVE_V2) {
return _withdrawFromAave(currentOffset, callerAddress);
} else if (lender < LenderIds.UP_TO_COMPOUND_V3) {
return _withdrawFromCompoundV3(currentOffset, callerAddress);
} else if (lender < LenderIds.UP_TO_COMPOUND_V2) {
return _withdrawFromCompoundV2(currentOffset, callerAddress);
} else {
return _encodeMorphoWithdrawCollateral(currentOffset, callerAddress);
}
}
/**
* deposit lendingToken
*/
else if (lendingOperation == LenderOps.DEPOSIT_LENDING_TOKEN) {
return _encodeMorphoDeposit(currentOffset, callerAddress);
}
/**
* withdraw lendingToken
*/
else if (lendingOperation == LenderOps.WITHDRAW_LENDING_TOKEN) {
return _encodeMorphoWithdraw(currentOffset, callerAddress);
} else {
_invalidOperation();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {PermitUtils} from "../../shared/permit/PermitUtils.sol";
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {PermitIds} from "../enums/DeltaEnums.sol";
abstract contract Permits is Masks, PermitUtils, DeltaErrors {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------------------|
* | 0 | 1 | permitOperation |
* | 1 | 20 | asset |
* | 21 | 2 | permitLength |
* | 23 | permitLength | data |
*/
function _permit(uint256 currentOffset, address callerAddress) internal returns (uint256) {
uint256 permitOperation;
address permitTarget;
uint256 permitLength;
uint256 permitOffset;
assembly {
let firstSlice := calldataload(currentOffset)
permitOperation := shr(248, firstSlice)
// this can be a token or morpho blue or compound V3 comet
permitTarget := and(ADDRESS_MASK, shr(88, firstSlice))
// calldata length
permitLength := and(UINT16_MASK, shr(72, firstSlice))
// increment offset
permitOffset := add(currentOffset, 23)
// increment offset
currentOffset := add(permitOffset, permitLength)
}
if (permitOperation == PermitIds.TOKEN_PERMIT) {
_tryPermit(permitTarget, permitOffset, permitLength, callerAddress);
return currentOffset;
} else if (permitOperation == PermitIds.AAVE_V3_CREDIT_PERMIT) {
_tryCreditPermit(permitTarget, permitOffset, permitLength, callerAddress);
return currentOffset;
} else if (permitOperation == PermitIds.ALLOW_CREDIT_PERMIT) {
_tryFlagBasedLendingPermit(permitTarget, permitOffset, permitLength, callerAddress);
return currentOffset;
} else {
_invalidOperation();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";
// solhint-disable max-line-length
/**
* @notice Balancer V3 actions
*/
abstract contract BalancerV3VaultActions is Masks, DeltaErrors {
// Balancer V3 selectors needed for executing a flash loan
/// @notice UniV4 pendant: take()
bytes32 private constant SEND_TO = 0xae63932900000000000000000000000000000000000000000000000000000000;
/// @notice same selector string name as for UniV4, different params for balancer
bytes32 private constant SETTLE = 0x15afd40900000000000000000000000000000000000000000000000000000000;
constructor() {}
function _encodeBalancerV3Take(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | manager |
* | 20 | 20 | asset |
* | 40 | 20 | receiver |
* | 60 | 16 | amount |
*/
assembly {
let manager := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let asset := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let receiver := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let amount := shr(128, calldataload(currentOffset))
// free memo ptr for populating the tx
let ptr := mload(0x40)
mstore(ptr, SEND_TO)
mstore(add(ptr, 4), asset) // offset
mstore(add(ptr, 36), receiver)
mstore(add(ptr, 68), amount)
if iszero(
call(
gas(),
manager,
0x0,
ptr, //
100,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// increment offset by amount length
currentOffset := add(currentOffset, 16)
}
return currentOffset;
}
function _balancerV3Settle(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|-------------------|
* | 0 | 20 | manager |
* | 20 | 20 | asset | <-- never native
* | 40 | 16 | amountHint |
*/
assembly {
let manager := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let asset := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let amountHint := shr(128, calldataload(currentOffset))
// we can settle exactly for the credit as for the B3 logic
if eq(amountHint, UINT128_MASK) { amountHint := MAX_UINT256 }
currentOffset := add(16, currentOffset)
let ptr := mload(0x40)
// settle amount
mstore(ptr, SETTLE)
mstore(add(ptr, 4), asset)
mstore(add(ptr, 36), amountHint)
if iszero(
call(
gas(),
manager,
0x0, // no native
ptr, //
68,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";
import {UniswapV4SingletonActions} from "./UniswapV4Singleton.sol";
import {BalancerV3VaultActions} from "./BalancerV3Vault.sol";
import {SharedSingletonActions} from "./Shared.sol";
// solhint-disable max-line-length
/**
* @notice Everything Uniswap V4 & Balancer V3, the major upgrades for DEXs in 2025
*/
abstract contract Gen2025DexActions is UniswapV4SingletonActions, BalancerV3VaultActions, SharedSingletonActions {
function _gen2025DexActions(uint256 currentOffset, address callerAddress) internal returns (uint256) {
uint256 transferOperation;
assembly {
let firstSlice := calldataload(currentOffset)
transferOperation := shr(248, firstSlice)
currentOffset := add(currentOffset, 1)
}
if (transferOperation < Gen2025ActionIds.BAL_V3_TAKE) {
if (transferOperation == Gen2025ActionIds.UNLOCK) {
return _singletonUnlock(currentOffset, callerAddress);
} else if (transferOperation == Gen2025ActionIds.UNI_V4_TAKE) {
return _unoV4Take(currentOffset);
} else if (transferOperation == Gen2025ActionIds.UNI_V4_SYNC) {
return _unoV4Sync(currentOffset);
} else if (transferOperation == Gen2025ActionIds.UNI_V4_SETTLE) {
return _unoV4Settle(currentOffset);
} else {
_invalidOperation();
}
} else {
if (transferOperation == Gen2025ActionIds.BAL_V3_TAKE) {
return _encodeBalancerV3Take(currentOffset);
} else if (transferOperation == Gen2025ActionIds.BAL_V3_SETTLE) {
return _balancerV3Settle(currentOffset);
} else {
_invalidOperation();
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Masks} from "../../shared/masks/Masks.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";
// solhint-disable max-line-length
abstract contract SharedSingletonActions is Masks {
// Uni V4 & Balancer V3 unlock
bytes32 private constant UNLOCK = 0x48c8949100000000000000000000000000000000000000000000000000000000;
// Hard-coded selector used for Balancer V3 callback
/// @notice selector by bytes4(keccak256("balancerUnlockCallback(bytes)"))
bytes32 private constant CB_SELECTOR = 0x480cf7ef00000000000000000000000000000000000000000000000000000000;
/**
* Here we need to add a selector deterministically as this function is Identical for B3 and U4
*/
function _singletonUnlock(uint256 currentOffset, address callerAddress) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|-----------------|
* | 0 | 20 | manager |
* | 20 | 2 | length |
* | 22 | length | data |
*/
assembly {
let manager := calldataload(currentOffset)
let dataLength := and(UINT16_MASK, shr(80, manager))
manager := shr(96, manager)
// free memo ptr for populating the tx
let ptr := mload(0x40)
/**
* We populate
* manager.unlock(
* abi.encodeWithSelector(
* CB_SELECTOR,
* poolId, <- this is for validation purposes, we only allow correct uni V4s or balancer V3s
* data
* )
* )
*/
mstore(ptr, UNLOCK)
mstore(add(ptr, 4), 0x20) // offset
mstore(add(ptr, 36), add(dataLength, 88)) // selector, address, poolId, offset, length 4+32+32+20+1
mstore(add(ptr, 68), CB_SELECTOR)
mstore(add(ptr, 72), 0x20) // offset as for cb selector
mstore(add(ptr, 104), add(dataLength, 20)) // length for within cb selector
mstore(add(ptr, 136), shl(96, callerAddress))
// increment by manager and dataLength
currentOffset := add(currentOffset, 22)
// copy calldata
calldatacopy(add(ptr, 156), currentOffset, dataLength)
if iszero(
call(
gas(),
manager,
0x0,
ptr, //
add(dataLength, 156),
// selector, 2x offset, 2x length, data * address + uint8
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// increment offset by data length
currentOffset := add(currentOffset, dataLength)
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Masks} from "../../shared/masks/Masks.sol";
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {Gen2025ActionIds} from "../enums/DeltaEnums.sol";
// solhint-disable max-line-length
/**
* @notice Everything Uniswap V4 & Balancer V3, the major upgrades for DEXs in 2025
*/
abstract contract UniswapV4SingletonActions is Masks, DeltaErrors {
// Uni V4 selectors needed for executing a flash loan
bytes32 private constant TAKE = 0x0b0d9c0900000000000000000000000000000000000000000000000000000000;
bytes32 private constant SETTLE = 0x11da60b400000000000000000000000000000000000000000000000000000000;
bytes32 private constant SYNC = 0xa584119400000000000000000000000000000000000000000000000000000000;
function _unoV4Take(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | manager |
* | 20 | 20 | asset |
* | 40 | 20 | receiver |
* | 60 | 16 | amount |
*/
assembly {
let manager := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let asset := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let receiver := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let amount := shr(128, calldataload(currentOffset))
// free memo ptr for populating the tx
let ptr := mload(0x40)
mstore(ptr, TAKE)
mstore(add(ptr, 4), asset) // offset
mstore(add(ptr, 36), receiver)
mstore(add(ptr, 68), amount)
if iszero(
call(
gas(),
manager,
0x0,
ptr, //
100,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// increment offset by amount length
currentOffset := add(currentOffset, 16)
}
return currentOffset;
}
function _unoV4Sync(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------|
* | 0 | 20 | manager |
* | 20 | 20 | asset |
*/
assembly {
let manager := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let asset := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
mstore(0, SYNC)
mstore(4, asset) // offset
if iszero(
call(
gas(),
manager,
0x0,
0, //
36,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
return currentOffset;
}
function _unoV4Settle(uint256 currentOffset) internal returns (uint256) {
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|-------------------|
* | 0 | 20 | manager |
* | 20 | 16 | nativeAmount |
*/
assembly {
let manager := shr(96, calldataload(currentOffset))
currentOffset := add(20, currentOffset)
let amount := shr(128, calldataload(currentOffset))
currentOffset := add(16, currentOffset)
mstore(0, SETTLE)
if iszero(
call(
gas(),
manager,
amount,
0, //
4,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {DeltaErrors} from "../../shared/errors/Errors.sol";
import {DexTypeMappings} from "./dex/DexTypeMappings.sol";
import {V4TypeGeneric} from "./dex/V4Type.sol";
import {V3TypeGeneric} from "./dex/V3Type.sol";
import {V2TypeGeneric} from "./dex/V2Type.sol";
import {WooFiSwapper} from "./dex/WooFi.sol";
import {DodoV2Swapper} from "./dex/DodoV2Swapper.sol";
import {LBSwapper} from "./dex/LBSwapper.sol";
import {Wrapper} from "./dex/Wrapper.sol";
import {GMXSwapper} from "./dex/GMXSwapper.sol";
import {SyncSwapper} from "./dex/SyncSwapper.sol";
import {CurveSwapper} from "./dex/CurveSwapper.sol";
import {BalancerV2Swapper} from "./dex/BalancerV2Swapper.sol";
import {BalancerV3Swapper} from "./dex/BalancerV3Swapper.sol";
// solhint-disable max-line-length
/**
* Core logic: Encode swaps as nested matrices (r: rows - multihops; c:columns - splits)
* Every element in a matrix can be another matrix
* The nesting stops at a (0,0) entry
* E.g. a multihop with each hop having 2 splits is identified as
* (1,0) <-- 1 as row implicates a multihop of length 2 (max index is 1)
* \
* (0,1) --------------- (0,1) <- the 1s in the columns indicate that
* | | there are 2 splits in each sub step
* ├(0,0)** ├(0,0)
* | |
* ├(1,0) ├(0,0) <- the (0,0) entries indicate an atomic swap (e.g. a uni V2 swap)
* \
* (0,0) --- (0,0) <- this is a branching multihip within a split (indicated by (1,0)
* the output token is expected to be the same as for the
* prior split in **
*
* The logic accumulates values per column to enable consistent multihops without additional balance reads
*
* Multihops progressively update value (in amount -> out amount) too always ensure that values
* within sub splits ((x,0) or (0,y)) are correctly accumulated
*
* A case like (1,2) is a violation as we always demand a clear gruping of the branch
* This is intuitive as we cannot have a split and a multihop at the same time.
*
* Every node with (x,0) is expected to have consistent multihop connections
*
* Every node with (0,y) is expected to have sub nodes and path that have all the same output currency
*
* Swap execution is always along rows then columns
* In the example above, we indicate a multihop
* Each hop has length 0 (single swap) but 1 split
* Each split is a single swap (0,0)
*
* This allows arbitrary deep nesting of sub-routes and splits
*
* Rows are prioritized over columns.
* /
*
* /**
* @title Base swapper contract
* @notice Contains basic logic for swap executions with DEXs
* DEX Id layout:
* 0 --- 100 : Self swappers (Uni V3, Curve, Clipper)
* 100 - 255 : Funded swaps (Uni V2, Solidly, Moe,Joe LB, WooFI, GMX)
* Uni V2: 100 - 110
* Solidly:121 - 130
*/
abstract contract BaseSwapper is
Wrapper,
V4TypeGeneric,
V3TypeGeneric,
V2TypeGeneric,
BalancerV3Swapper,
BalancerV2Swapper,
LBSwapper,
DodoV2Swapper,
WooFiSwapper,
CurveSwapper,
GMXSwapper, //
SyncSwapper,
DeltaErrors
{
/*
* multihop swapper that allows for splits in each hops
* forward the amountOut received from the last hop
*/
function _multihopSplitSwap(
uint256 amountIn,
uint256 swapMaxIndex,
address tokenIn,
address callerAddress,
uint256 currentOffset //
)
internal
returns (uint256 amount, uint256, address _tokenIn)
{
amount = amountIn;
_tokenIn = tokenIn;
uint256 i;
while (true) {
(amount, currentOffset, _tokenIn) = _singleSwapSplitOrRoute(
amount,
_tokenIn,
callerAddress,
currentOffset //
);
// break criteria
if (i == swapMaxIndex) {
break;
} else {
// update context
assembly {
i := add(i, 1)
}
}
}
return (amount, currentOffset, _tokenIn);
}
/**
* Ensure that all paths end with the same CCY
* parallel swaps a->...->b; a->...->b for different dexs
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 0-16 | splits |
* | sC | Variable | datas |
*
* `splits` looks like follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 1 | count |
* | 1 | 2*count - 1 | splits | <- count = 0 means there is no data, otherwise uint16 splits
*
* `datas` looks like follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 2 | (r,c) | <- indicates whether the swap is non-simple (further splits or hops)
* | 2 | 1 | dexId |
* | 3 | variable | params | <- depends on dexId (fixed for each one)
* | 3+v | 2 | (r,c) |
* | 4+v | 1 | dexId |
* | ... | variable | params | <- depends on dexId (fixed for each one)
* | ... | ... | ... | <- count + 1 times of repeating this pattern
*
* returns cumulative output, updated offset and nextToken address
*/
function _singleSwapOrSplit(
uint256 amountIn,
uint256 splitsMaxIndex,
address tokenIn,
address callerAddress, // caller
uint256 currentOffset
)
internal
returns (uint256, uint256, address nextToken)
{
// no splits, single swap
if (splitsMaxIndex == 0) {
(amountIn, currentOffset, nextToken) = _singleSwapSplitOrRoute(
amountIn,
tokenIn, //
callerAddress,
currentOffset
);
} else {
uint256 splits;
assembly {
splits := shr(128, calldataload(currentOffset))
currentOffset := add(mul(2, splitsMaxIndex), currentOffset)
}
uint256 amount;
uint256 i;
uint256 swapsLeft = amountIn;
while (true) {
uint256 split;
assembly {
switch eq(i, splitsMaxIndex)
case 1 {
// assign remaing amount to split
split := swapsLeft
}
default {
// splits are uint16s as share of uint16.max
split :=
div(
mul(
and(
UINT16_MASK,
shr(sub(112, mul(i, 16)), splits) // read the uin16 in the splits sequence
),
amountIn //
),
UINT16_MASK //
)
}
i := add(i, 1)
}
uint256 received;
// reenter-universal swap
// can be another split or a multi-path
(received, currentOffset, nextToken) = _singleSwapSplitOrRoute(
split,
tokenIn, //
callerAddress,
currentOffset
);
// increment and decrement
assembly {
amount := add(amount, received)
}
// if nothing is left, break
if (i > splitsMaxIndex) break;
// otherwise, we decrement the swaps left amount
assembly {
swapsLeft := sub(swapsLeft, split)
}
}
amountIn = amount;
}
return (amountIn, currentOffset, nextToken);
}
/*
* execute swap or split amounts
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 2 | (r,c) |
* | 2 | 20 | nextToken |
* | 22 | any | swapData |
*
* if r=0
* if c=0 : single swap
* else: split swap
* else: multihop swap
*
* always return output amount, updated offset and nextToken address
*/
function _singleSwapSplitOrRoute(
uint256 amountIn,
address tokenIn,
address callerAddress,
uint256 currentOffset //
)
internal
returns (uint256 received, uint256, address nextToken)
{
uint256 swapMaxIndex;
uint256 splitsMaxIndex;
assembly {
let datas := calldataload(currentOffset)
swapMaxIndex := shr(248, datas)
splitsMaxIndex := and(UINT8_MASK, shr(240, datas))
currentOffset := add(currentOffset, 2)
}
// swapMaxIndex = 0 is simple single swap
// that is where each single step MUST end
if (swapMaxIndex == 0) {
// splitsMaxIndex zero is single swap
if (splitsMaxIndex == 0) {
// if the swapMaxIndex is single-swap,
// the next two addresses are nextToken and receiver
address receiver;
assembly {
nextToken := shr(96, calldataload(currentOffset))
currentOffset := add(currentOffset, 20)
receiver := shr(96, calldataload(currentOffset))
currentOffset := add(currentOffset, 20)
}
(received, currentOffset) = _swapExactInSimple(
amountIn,
tokenIn,
nextToken,
callerAddress,
receiver, //
currentOffset
);
} else {
// nonzero is a split swap
(received, currentOffset, nextToken) = _singleSwapOrSplit(
amountIn,
splitsMaxIndex,
tokenIn,
callerAddress,
currentOffset //
);
}
} else {
// otherwise, execute universal swap (path & splits)
(received, currentOffset, nextToken) = _multihopSplitSwap(
amountIn, //
swapMaxIndex,
tokenIn,
callerAddress,
currentOffset
);
}
return (received, currentOffset, nextToken);
}
/**
* Swaps exact in internally using all implemented Dexs
* Will NOT use a flash swap
* The dexId is assumed to be fetched before in a prefunding action
* As such, the parameter can be plugged in here directly
* @param amountIn sell amount
* @return (amountOut, new offset) buy amount
*/
function _swapExactInSimple(
uint256 amountIn,
address tokenIn,
address tokenOut,
address payer, // first step
address receiver, // last step
uint256 currentOffset
)
internal
returns (uint256, uint256)
{
uint256 dexTypeId;
assembly {
dexTypeId := shr(248, calldataload(currentOffset))
currentOffset := add(currentOffset, 1)
}
// First block: prioritize Uniswap
if (dexTypeId <= 63) {
if (dexTypeId == DexTypeMappings.UNISWAP_V3_ID) {
return _swapUniswapV3PoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
} else if (dexTypeId == DexTypeMappings.UNISWAP_V4_ID) {
return _swapUniswapV4ExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
} else if (dexTypeId == DexTypeMappings.UNISWAP_V2_ID) {
return _swapUniswapV2PoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
} else if (dexTypeId == DexTypeMappings.IZI_ID) {
return _swapIZIPoolExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
} else if (dexTypeId == DexTypeMappings.UNISWAP_V2_FOT_ID) {
return _swapUniV2ExactInFOTGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
}
}
// Second block: prioritize Curve
else if (dexTypeId <= 127) {
if (dexTypeId == DexTypeMappings.CURVE_V1_STANDARD_ID) {
return _swapCurveGeneral(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.CURVE_RECEIVED_ID) {
return _swapCurveReceived(tokenIn, amountIn, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.CURVE_FORK_ID) {
return _swapCurveFork(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.WOO_FI_ID) {
return _swapWooFiExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.GMX_ID) {
return _swapGMXExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
}
}
// Third block: prioritize Balancer
else if (dexTypeId <= 191) {
if (dexTypeId == DexTypeMappings.BALANCER_V2_ID) {
return _swapBalancerV2ExactIn(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.BALANCER_V3_ID) {
return _swapBalancerV3ExactInGeneric(amountIn, tokenIn, tokenOut, receiver, currentOffset, payer);
} else if (dexTypeId == DexTypeMappings.LB_ID) {
return _swapLBexactIn(amountIn, tokenIn, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.DODO_ID) {
return _swapDodoV2ExactIn(amountIn, tokenIn, tokenOut, receiver, payer, currentOffset);
} else if (dexTypeId == DexTypeMappings.SYNC_SWAP_ID) {
return _swapSyncExactIn(amountIn, tokenIn, receiver, payer, currentOffset);
}
}
// Rest: Rare wrap/unwrap operations
else if (dexTypeId == DexTypeMappings.ASSET_WRAP_ID) {
return _wrapperOperation(tokenIn, tokenOut, amountIn, receiver, payer, currentOffset);
}
// If no match was found, revert
assembly {
mstore(0, INVALID_DEX)
revert(0, 0x4)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* Author: Achthar | 1delta
* /*****************************************************************************
*/
import {DeltaErrors} from "../../../shared/errors/Errors.sol";
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
/**
* @title Uniswap V3 type callback implementations
*/
abstract contract V3Callbacker is ERC20Selectors {
/**
* This functione executes a simple transfer to shortcut the callback if there is no further calldata
*/
function clSwapCallback(uint256 amountToPay, address tokenIn, address callerAddress, uint256 calldataLength) internal {
assembly {
// one can pass no path to continue
// we then assume the calldataLength as flag to
// indicate the pay type
if lt(calldataLength, 2) {
let ptr := mload(0x40)
let success
// transfer from caller
switch calldataLength
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), caller())
mstore(add(ptr, 0x44), amountToPay)
success := call(gas(), tokenIn, 0, ptr, 0x64, ptr, 32)
}
// transfer plain
default {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), caller())
mstore(add(ptr, 0x24), amountToPay)
success :=
call(
gas(),
tokenIn, // tokenIn, pool + 5x uint8 (i,j,s,a)
0,
ptr,
0x44,
ptr,
32
)
}
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
return(0, 0)
}
}
_deltaComposeInternal(
callerAddress,
// the naive offset is 132
// we skip the entire callback validation data
// that is tokens (+40), fee (+2), caller (+20), forkId (+1) datalength (+2)
// = 197
197,
calldataLength
);
}
function _deltaComposeInternal(address callerAddress, uint256 offset, uint256 length) internal virtual {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* This is to check whether the provided parameter is nonempty
*/
library ValidatorLib {
function _hasData(bytes32 data) internal pure returns (bool hasData) {
assembly {
hasData := xor(0, data)
}
}
function _hasAddress(address data) internal pure returns (bool hasData) {
assembly {
hasData := xor(0, data)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Balancer V2 swapper contract that uses Symmetric's vault
* @notice Balancer V2 is fun (mostly)
*/
abstract contract BalancerV2Swapper is ERC20Selectors, Masks {
/// @dev Balancer's single swap function
bytes32 private constant BALANCER_SWAP = 0x52bbbe2900000000000000000000000000000000000000000000000000000000;
/**
* Swaps exact input on Balancer V2
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 32 | pool |
* | 32 | 20 | vault |
* | 52 | 1 | pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _swapBalancerV2ExactIn(
address tokenIn,
address tokenOut,
uint256 amountIn,
address receiver,
address callerAddress,
uint256 currentOffset //
)
internal
returns (uint256 amountOut, uint256 balancerData)
{
assembly {
// balancer vault plus pay flag
balancerData := calldataload(add(32, currentOffset))
let ptr := mload(0x40)
// only need to check whether we have to pull from caller
if iszero(and(UINT8_MASK, shr(88, balancerData))) {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), address())
mstore(add(ptr, 0x44), amountIn)
let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
let vault := shr(96, balancerData)
{
////////////////////////////////////////////////////
// Approve vault if needed
////////////////////////////////////////////////////
mstore(0x0, tokenIn)
mstore(0x20, 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3) // CALL_MANAGEMENT_APPROVALS
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x0, vault)
let key := keccak256(0x0, 0x40)
// check if already approved
if iszero(sload(key)) {
// selector for approve(address,uint256)
mstore(ptr, ERC20_APPROVE)
mstore(add(ptr, 0x04), vault)
mstore(add(ptr, 0x24), MAX_UINT256)
pop(call(gas(), tokenIn, 0, ptr, 0x44, ptr, 32))
sstore(key, 1)
}
}
////////////////////////////////////////////////////
// Execute swap function on B2 Vault
////////////////////////////////////////////////////
mstore(ptr, BALANCER_SWAP)
mstore(add(ptr, 0x4), 0xe0) // FundManagement struct
mstore(add(ptr, 0x24), address()) // sender
mstore(add(ptr, 0x44), 0) // fromInternalBalance
mstore(add(ptr, 0x64), receiver) // receiver
mstore(add(ptr, 0x84), 0) // toInternalBalance
mstore(add(ptr, 0xA4), 0) // limit
mstore(add(ptr, 0xC4), MAX_UINT256) // deadline
mstore(add(ptr, 0xE4), calldataload(currentOffset)) // poolId
mstore(add(ptr, 0x104), 0) // swapKind = GIVEN_IN
mstore(add(ptr, 0x124), tokenIn) // assetIn
mstore(add(ptr, 0x144), tokenOut) // assetOut
mstore(add(ptr, 0x164), amountIn) // amount
mstore(add(ptr, 0x184), 0xC0) // offest
mstore(add(ptr, 0x1A4), 0) // userData length
if iszero(
call(
gas(),
vault,
0x0,
ptr,
0x1C4,
0x0,
0x20 // we use the return amount
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
amountOut := mload(0x0)
balancerData := add(53, currentOffset)
}
return (amountOut, balancerData);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Balancer V3 type swapper contract
* @notice Can only be executed within `manager.unlock()`
* Ergo, if Balancer v3 is in the path (no matter how many times), one has to push
* the swap data and execution into the BalancerV3.unlock
* This should be usable together with flash loans from their singleton
*
* Can unlock arbitrary times times!
*
* The execution of a swap follows the steps:
*
* 1) pm.unlock(...) (outside of this contract)
* 2) call pm.swap(...)
* 3) call pm.settle to settle the swap (no native accepted)
*
*/
abstract contract BalancerV3Swapper is ERC20Selectors, Masks {
/**
* We need all these selectors for executing a single swap
*/
bytes32 private constant SWAP = 0x2bfb780c00000000000000000000000000000000000000000000000000000000;
/// @notice same selector string name as for UniV4, different params for balancer
bytes32 private constant SETTLE = 0x15afd40900000000000000000000000000000000000000000000000000000000;
/// @notice pull funds from vault with this
bytes32 private constant SEND_TO = 0xae63932900000000000000000000000000000000000000000000000000000000;
constructor() {}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 20 | manager |
* | 40 | 1 | payFlag |
* | 41 | 2 | calldataLength | <- this here might be pool-dependent, cannot be used as flag
* | 43 | calldataLength | calldata |
*/
function _swapBalancerV3ExactInGeneric(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (
uint256 receivedAmount,
// similar to other implementations, we use this temp variable
// to avoid stackToo deep
uint256 tempVar
)
{
// enum SwapKind {
// EXACT_IN,
// EXACT_OUT
// }
// struct VaultSwapParams {
// SwapKind kind; 4
// address pool; 36
// IERC20 tokenIn; 68
// IERC20 tokenOut; 100
// uint256 amountGivenRaw; 132
// uint256 limitRaw; 164
// bytes userData; (196, 228, 260 - X)
// }
////////////////////////////////////////////
// This is the function selector we need
////////////////////////////////////////////
// function swap(
// VaultSwapParams memory vaultSwapParams
// )
// external
// returns (uint256 amountCalculated, uint256 amountIn, uint256 amountOut)
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
// read the hook address and insta store it to keep stack smaller
mstore(add(ptr, 132), shr(96, calldataload(currentOffset)))
let pool := shr(96, calldataload(currentOffset))
// skip hook
currentOffset := add(currentOffset, 20)
// read the pool address
let vault := calldataload(currentOffset)
// skip vault plus params
currentOffset := add(currentOffset, 23)
// pay flag
tempVar := and(UINT8_MASK, shr(88, vault))
let clLength := and(UINT16_MASK, shr(72, vault))
vault := shr(96, vault)
// Prepare external call data
// Store swap selector
mstore(ptr, SWAP)
mstore(add(ptr, 4), 0)
mstore(add(ptr, 36), pool)
mstore(add(ptr, 68), tokenIn)
mstore(add(ptr, 100), tokenOut)
mstore(add(ptr, 132), fromAmount)
mstore(add(ptr, 164), 1)
mstore(add(ptr, 196), 0xe0)
mstore(add(ptr, 228), clLength)
if xor(0, clLength) {
// Store further calldata for the pool
calldatacopy(add(ptr, 260), currentOffset, clLength)
currentOffset := add(currentOffset, clLength)
}
// Perform the external 'swap' call
if iszero(call(gas(), vault, 0, ptr, add(260, clLength), ptr, 0x60)) {
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// get real amounts
fromAmount := mload(add(ptr, 0x20))
receivedAmount := mload(add(ptr, 0x40))
/**
* Pull funds to receiver
*/
mstore(ptr, SEND_TO)
mstore(add(ptr, 4), tokenOut) //
mstore(add(ptr, 36), receiver)
mstore(add(ptr, 68), receivedAmount)
if iszero(
call(
gas(),
vault,
0x0,
ptr, //
100,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
/**
* If the pay mode is >=3, we assume deferred payment
* This means that the composer must manually settle
* for the input amount
* Warning: This should not be done for pools with
* arbitrary hooks as these can have cases where
* `amountIn` selected != actual `amountIn`
*/
if lt(tempVar, 2) {
let success
switch tempVar
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), vault)
mstore(add(ptr, 0x44), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), vault)
mstore(add(ptr, 0x24), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
/**
* Settle funds in vault
*/
// settle amount
mstore(ptr, SETTLE)
mstore(add(ptr, 4), tokenIn)
mstore(add(ptr, 36), fromAmount)
if iszero(
call(
gas(),
vault,
0x0, // no native
ptr,
68,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
return (receivedAmount, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Curve swapper contract
* @notice We do Curve stuff here
*/
abstract contract CurveSwapper is ERC20Selectors, Masks {
// approval slot
bytes32 private constant CALL_MANAGEMENT_APPROVALS = 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3;
/**
* Standard curve pool selectors
*/
////////////////////////////////////////////////////
// General info on the selectors for Curve:
// There are 5 variations
// 1) indexes as int128
// 2) indexes as uint256
// 3) has receiver
// 4) has no receiver
// 5) fork with solidity implementation (typically uint8 indexes)
// The int128 indexes are preferred and have lower indexes
// The ones with receiver have even indexes
////////////////////////////////////////////////////
/// @notice selector exchange(int128,int128,uint256,uint256)
bytes32 private constant EXCHANGE_INT = 0x3df0212400000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange(int128,int128,uint256,uint256,address)
bytes32 private constant EXCHANGE_INT_WITH_RECEIVER = 0xddc1f59d00000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange(uint256,uint256,uint256,uint256)
bytes32 private constant EXCHANGE = 0x5b41b90800000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange(uint256,uint256,uint256,uint256,address)
bytes32 private constant EXCHANGE_WITH_RECEIVER = 0xa64833a000000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_underlying(uint256,uint256,uint256,uint256)
bytes32 private constant EXCHANGE_UNDERLYING = 0x65b2489b00000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_underlying(uint256,uint256,uint256,uint256,address)
bytes32 private constant EXCHANGE_UNDERLYING_WITH_RECEIVER = 0xe2ad025a00000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_underlying(uint256,uint256,uint256,uint256)
bytes32 private constant EXCHANGE_UNDERLYING_INT = 0xa6417ed600000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_underlying(uint256,uint256,uint256,uint256,address)
bytes32 private constant EXCHANGE_UNDERLYING_INT_WITH_RECEIVER = 0x44ee198600000000000000000000000000000000000000000000000000000000;
////////////////////////////////////////////////////
// General info on the selectors for Curve Received:
// They are pre-funded
// 1) indexes as int128 (NG) or uint256 (Some of the TriCryptos) as above
// 2) has always a receiver (optinally could be called without it, bet there is no utility for it)
////////////////////////////////////////////////////
/// @notice selector exchange_received(uint256,uint256,uint256,uint256)
bytes32 private constant EXCHANGE_RECEIVED = 0x29b244bb00000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_received(uint256,uint256,uint256,uint256,address)
bytes32 private constant EXCHANGE_RECEIVED_WITH_RECEIVER = 0x767691e700000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange(int128,int128,uint256,uint256)
bytes32 private constant EXCHANGE_RECEIVED_INT = 0x7e3db03000000000000000000000000000000000000000000000000000000000;
/// @notice selector exchange_received(int128,int128,uint256,uint256,address)
bytes32 private constant EXCHANGE_RECEIVED_INT_WITH_RECEIVER = 0xafb4301200000000000000000000000000000000000000000000000000000000;
/// @notice selector for cuve forks using solidity swap(uint8,uint8,uint256,uint256,uint256)
bytes32 private constant SWAP = 0x9169558600000000000000000000000000000000000000000000000000000000;
function _fundAndApproveIfNeeded(address callerAddress, address tokenIn, uint256 amount, uint256 data) private returns (address pool) {
assembly {
let ptr := mload(0x40)
pool := shr(96, data)
mstore(0x0, tokenIn)
mstore(0x20, CALL_MANAGEMENT_APPROVALS)
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x0, pool)
let key := keccak256(0x0, 0x40)
// check if already approved
if iszero(sload(key)) {
// approveFlag
// selector for approve(address,uint256)
mstore(ptr, ERC20_APPROVE)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), MAX_UINT256)
pop(
call(
gas(),
tokenIn, //
0,
ptr,
0x44,
ptr,
32
)
)
sstore(key, 1)
}
if iszero(and(UINT16_MASK, shr(56, data))) {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), address())
mstore(add(ptr, 0x44), amount)
let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
}
/**
* Swaps using a standard curve pool
* Data is supposed to be packed as follows
* tokenIn | actionId | dexId | pool | i | j | sm | tokenOut
* sm is the selector,
* i,j are the swap indexes for the pool
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | i |
* | 21 | 1 | j |
* | 22 | 1 | sm |
* | 23 | 2 | payMode | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
*/
function _swapCurveGeneral(
address tokenIn,
address tokenOut,
uint256 amountIn,
address receiver, //
address callerAddress,
uint256 currentOffset
)
internal
returns (
uint256 amountOut,
// curve data is a transient memory variable to
// avoid stack too deep errors
uint256 curveData
)
{
address pool;
assembly {
curveData := calldataload(currentOffset)
}
// extract pool
pool = _fundAndApproveIfNeeded(
callerAddress,
tokenIn,
amountIn,
curveData // use generic data
);
assembly {
let ptr := mload(0x40)
// consistent params not overlapping with 32 bytes from selector
mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK))
mstore(add(ptr, 0x44), amountIn)
mstore(add(ptr, 0x64), 0) // min out
let success
////////////////////////////////////////////////////
// Execute swap function
////////////////////////////////////////////////////
switch and(shr(72, curveData), UINT8_MASK)
// selectorId
case 0 {
// selector for exchange(int128,int128,uint256,uint256,address)
mstore(ptr, EXCHANGE_INT_WITH_RECEIVER)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x84), receiver) // receiver, set curveData accordingly
success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
curveData := 0
}
case 1 {
// selector for exchange(int128,int128,uint256,uint256)
mstore(ptr, EXCHANGE_INT)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
curveData := MAX_UINT256
}
case 2 {
// selector for exchange(uint256,uint256,uint256,uint256,address)
mstore(ptr, EXCHANGE_WITH_RECEIVER)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x84), receiver) // receiver, set curveData accordingly
success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
curveData := 0
}
case 3 {
// selector for exchange(uint256,uint256,uint256,uint256)
mstore(ptr, EXCHANGE)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
curveData := MAX_UINT256
}
case 4 {
// selector for exchange_underlying(int128,int128,uint256,uint256,address)
mstore(ptr, EXCHANGE_UNDERLYING_INT_WITH_RECEIVER)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x84), receiver)
success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
curveData := 0
}
case 5 {
// selector for exchange_underlying(int128,int128,uint256,uint256)
mstore(ptr, EXCHANGE_UNDERLYING_INT)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
curveData := MAX_UINT256
}
case 6 {
// selector for exchange_underlying(uint256,uint256,uint256,uint256,address)
mstore(ptr, EXCHANGE_UNDERLYING_WITH_RECEIVER)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x84), receiver)
success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
curveData := 0
}
case 7 {
// selector for exchange_underlying(uint256,uint256,uint256,uint256)
mstore(ptr, EXCHANGE_UNDERLYING)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
success := call(gas(), pool, 0x0, ptr, 0x84, ptr, 0x20)
curveData := MAX_UINT256
}
case 200 {
// selector for swap(uint8,uint8,uint256,uint256,uint256)
mstore(ptr, SWAP)
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x84), MAX_UINT256) // deadline
success := call(gas(), pool, 0x0, ptr, 0xA4, ptr, 0x20)
curveData := MAX_UINT256
}
default { revert(0, 0) }
if iszero(success) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
amountOut := mload(ptr)
////////////////////////////////////////////////////
// Send funds to receiver if needed
// curveData is now the flag for manually
// transferuing to the receiver
////////////////////////////////////////////////////
if and(curveData, xor(receiver, address())) {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amountOut)
success := call(gas(), tokenOut, 0, ptr, 0x44, 0, 32)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
curveData := add(currentOffset, 25)
}
return (amountOut, curveData);
}
/**
* Swaps using a standard curve pool
* Data is supposed to be packed as follows
* tokenIn | actionId | dexId | pool | i | j | sm | tokenOut
* sm is the selector,
* i,j are the swap indexes for the pool
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | i |
* | 21 | 1 | j |
* | 22 | 1 | sm |
* | 23 | 2 | payMode | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
*/
function _swapCurveFork(
address tokenIn,
address tokenOut,
uint256 amountIn,
address receiver, //
address callerAddress,
uint256 currentOffset
)
internal
returns (
uint256 amountOut,
// curve data is a transient memory variable to
// avoid stack too deep errors
uint256 curveData
)
{
address pool;
assembly {
curveData := calldataload(currentOffset)
}
// extract pool
pool = _fundAndApproveIfNeeded(
callerAddress,
tokenIn,
amountIn,
curveData // use generic data
);
assembly {
let ptr := mload(0x40)
mstore(0x0, ERC20_BALANCE_OF)
mstore(0x4, address())
// call to token
pop(
staticcall(
gas(),
tokenOut, // token
0x0,
0x24,
ptr, // use ptr here so that we don't override the scrap space
0x20
)
)
amountOut := mload(ptr)
////////////////////////////////////////////////////
// Execute swap function
////////////////////////////////////////////////////
// get selector
switch and(shr(72, curveData), UINT8_MASK)
// selectorId
case 3 {
// selector for exchange(int128,int128,uint256,uint256,address)
mstore(ptr, EXCHANGE)
}
case 5 {
// selector for exchange(int128,int128,uint256,uint256)
mstore(ptr, EXCHANGE_UNDERLYING)
}
default { revert(0, 0) }
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK))
mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK))
mstore(add(ptr, 0x44), amountIn)
mstore(add(ptr, 0x64), 0) // min out
if iszero(call(gas(), pool, 0x0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// call to token - note that 0x-0x24 still holds the respective calldata
pop(
staticcall(
gas(),
tokenOut, // token
0x0,
0x24,
0x0, // output to ptr
0x20
)
)
// load the retrieved balance
amountOut := sub(mload(0x0), amountOut)
////////////////////////////////////////////////////
// Send funds to receiver if needed
////////////////////////////////////////////////////
if xor(receiver, address()) {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amountOut)
let success :=
call(
gas(),
tokenOut, // tokenIn, pool + 5x uint8 (i,j,s,a)
0,
ptr,
0x44,
ptr,
32
)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
curveData := add(currentOffset, 25)
}
return (amountOut, curveData);
}
/**
* Swaps using a NG pool that allows for pre-funded swaps
* Data is supposed to be packed as follows
* tokenIn | actionId | dexId | pool | sm | i | j | tokenOut
* sm is the selector,
* i,j are the swap indexes for the pool
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | i |
* | 21 | 1 | j |
* | 22 | 1 | sm |
* | 23 | 2 | payMode | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
*/
function _swapCurveReceived(
address tokenIn,
uint256 amountIn,
address receiver, //
address callerAddress,
uint256 currentOffset
)
internal
returns (
// assign payFlag then amountOut
uint256 payFlagAmountOut,
uint256 curveData
)
{
assembly {
let ptr := mload(0x40)
curveData := calldataload(currentOffset)
let pool := shr(96, curveData)
payFlagAmountOut := and(UINT16_MASK, shr(56, curveData))
if lt(payFlagAmountOut, 2) {
let success
switch payFlagAmountOut
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pool)
mstore(add(ptr, 0x44), amountIn)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), amountIn)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
////////////////////////////////////////////////////
// Execute swap function
////////////////////////////////////////////////////
switch and(shr(72, curveData), UINT8_MASK)
case 0 {
// selector for exchange_received(int128,int128,uint256,uint256,address)
mstore(ptr, EXCHANGE_RECEIVED_INT_WITH_RECEIVER)
}
case 2 {
// selector for exchange_received(uint256,uint256,uint256,uint256,address)
mstore(ptr, EXCHANGE_RECEIVED_WITH_RECEIVER)
}
default { revert(0, 0) }
mstore(add(ptr, 0x4), and(shr(88, curveData), UINT8_MASK)) // indexIn
mstore(add(ptr, 0x24), and(shr(80, curveData), UINT8_MASK)) // indexOut
mstore(add(ptr, 0x44), amountIn)
mstore(add(ptr, 0x64), 0) // min out
mstore(add(ptr, 0x84), receiver)
if iszero(
call(
gas(),
pool, //
0x0,
ptr,
0xA4,
0,
0x20
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
payFlagAmountOut := mload(0)
curveData := add(currentOffset, 25)
}
return (payFlagAmountOut, curveData);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
library DexTypeMappings {
/**
* First Block: Blue Chip DEXs (1)
*/
uint256 internal constant UNISWAP_V3_ID = 0;
uint256 internal constant UNISWAP_V2_ID = 1;
uint256 internal constant UNISWAP_V4_ID = 2;
uint256 internal constant IZI_ID = 5;
uint256 internal constant UNISWAP_V2_FOT_ID = 3;
/**
* Second Block: Blue Chip DEXs (2): all DEX that behave like curve
*/
// indexs as input
// returns out amount
uint256 internal constant CURVE_V1_STANDARD_ID = 64;
// curve NG
uint256 internal constant CURVE_RECEIVED_ID = 65;
// almost like curve, but slight different implementation,
// e.g. the function returns no output
uint256 internal constant CURVE_FORK_ID = 66;
uint256 internal constant WOO_FI_ID = 80;
// GMXs (rather rare)
uint256 internal constant GMX_ID = 90;
uint256 internal constant KTX_ID = 91;
/**
* Third Block: Blue Chips (3): Balancers
*/
uint256 internal constant BALANCER_V2_ID = 128;
uint256 internal constant BALANCER_V3_ID = 129;
// LFM/LFJ LB
uint256 internal constant LB_ID = 140;
// more exotics
uint256 internal constant DODO_ID = 150;
uint256 internal constant SYNC_SWAP_ID = 160;
/**
* Fourth Block: Wrappers
*/
// wrappers
uint256 internal constant ERC4626_ID = 253;
uint256 internal constant ASSET_WRAP_ID = 254;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title DodoV2 swapper contract
*/
abstract contract DodoV2Swapper is ERC20Selectors, Masks {
/**
* We need this to avoid stack too deep in the overall context
* if `clLength<3` no flash loan will be executed,
* othewise, we skip the funding transfers
* 0 is pulling from caller
* 1 is transferring from contract
* 2 is pre-funded (meaning no transfers here)
*
*/
function _dodoPrepare(
uint256 amountIn,
address tokenIn,
address callerAddress,
uint256 currentOffset
)
private
returns (uint256 dodoData, address pool, uint256 clLength)
{
assembly {
dodoData := calldataload(currentOffset)
pool := shr(96, dodoData)
clLength := and(UINT16_MASK, shr(56, dodoData))
let ptr := mload(0x40)
// less than 2: funding via token transfers
if lt(clLength, 2) {
let success
switch clLength
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pool)
mstore(add(ptr, 0x44), amountIn)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), amountIn)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
}
/**
* Swaps exact input on Dodo V2
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | sellQuote |
* | 21 | 2 | pId | pool index for flash validation
* | 22 | 2 | clLength / pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
* | 25 | clLength | calldata | calldata for fash loan
*/
function _swapDodoV2ExactIn(
uint256 amountIn,
address tokenIn,
address tokenOut,
address receiver,
address callerAddress,
uint256 currentOffset
)
internal
returns (uint256 amountOut, uint256 clLength)
{
address pool;
(amountOut, pool, clLength) = _dodoPrepare(
amountIn,
tokenIn,
callerAddress, //
currentOffset
);
assembly {
let ptr := mload(0x40)
// if it is a spot swap, it is already funded
switch lt(clLength, 3)
case 1 {
// determine selector
switch and(UINT8_MASK, shr(88, amountOut))
case 0 {
// sellBase
mstore(0x0, 0xbd6015b400000000000000000000000000000000000000000000000000000000)
}
default {
// sellQuote
mstore(0x0, 0xdd93f59a00000000000000000000000000000000000000000000000000000000)
}
mstore(0x4, receiver)
// call swap, revert if invalid/undefined pair
if iszero(call(gas(), pool, 0x0, 0x0, 0x24, 0x0, 0x20)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// the swap call returns the output amount directly
amountOut := mload(0x0)
// increment offset
currentOffset := add(25, currentOffset)
}
// otherwise, execute flash loan
default {
let ptrAfter := add(ptr, 256)
/**
* Similar to Uni V2 flash swaps
* We request the output amount and the other one is zero
*/
// flashLoan(
// uint256 baseAmount,
// uint256 quoteAmount,
// address assetTo,
// bytes calldata data
// )
// map data to the pool
mstore(ptrAfter, 0xd0a494e400000000000000000000000000000000000000000000000000000000)
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | caller |
* | 20 | 20 | base |
* | 40 | 20 | quote |
* | 60 | 2 | pId | <- we use calldatacopy from here
* | 62 | 2 | calldataLength |
* | 64 | calldataLength | calldata |
*/
// this is for the next call
mstore(add(ptr, 0x24), amountIn)
// determine selector
switch and(UINT8_MASK, shr(88, amountOut))
case 0 {
// querySellBase(address,uint256)
mstore(ptr, 0x79a0487600000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), 0) // trader is zero
// call pool
if iszero(
staticcall(
gas(),
pool,
ptr,
0x44, //
0,
0x20
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
amountOut := mload(0)
// sell base -> input is base
mstore(add(ptrAfter, 4), 0)
mstore(add(ptrAfter, 36), amountOut)
// gap will be populated outside this
mstore(add(ptrAfter, 164), shl(96, callerAddress))
mstore(add(ptrAfter, 184), shl(96, tokenIn))
mstore(add(ptrAfter, 204), shl(96, tokenOut))
}
default {
// querySellQuote(address,uint256)
mstore(ptr, 0x66410a2100000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), 0) // trader is zero
// call pool
if iszero(
staticcall(
gas(),
pool,
ptr,
0x44, //
0,
0x20
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
amountOut := mload(0)
// sell quote -> input is quote
mstore(add(ptrAfter, 4), amountOut)
mstore(add(ptrAfter, 36), 0)
// gap will be populated outside this
mstore(add(ptrAfter, 164), shl(96, callerAddress))
mstore(add(ptrAfter, 184), shl(96, tokenOut))
mstore(add(ptrAfter, 204), shl(96, tokenIn))
}
mstore(add(ptrAfter, 68), receiver) // (callback-) receiver - should be self
mstore(add(ptrAfter, 100), 0x80) // bytes offset
mstore(add(ptrAfter, 132), add(64, clLength)) // 3x address + pId + length
calldatacopy(add(ptrAfter, 224), add(21, currentOffset), add(clLength, 4))
// call swap, revert if invalid/undefined pair
if iszero(
call(
gas(),
pool,
0x0,
ptrAfter,
add(228, clLength), //
0x0,
0x0
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
currentOffset := add(add(25, currentOffset), clLength)
}
}
return (amountOut, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title GMX V1 swapper, works for most forks, too
*/
abstract contract GMXSwapper is ERC20Selectors, Masks {
/**
* Swaps exact input on GMX V1
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _swapGMXExactIn(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver, //
address callerAddress,
uint256 currentOffset
)
internal
returns (uint256 amountOut, uint256)
{
assembly {
let ptr := mload(0x40)
let gmxData := calldataload(currentOffset)
let vault := shr(96, gmxData)
switch and(UINT8_MASK, shr(88, gmxData))
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), vault)
mstore(add(ptr, 0x44), fromAmount)
let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), vault)
mstore(add(ptr, 0x24), fromAmount)
let success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// selector for swap(address,address,address)
mstore(
ptr, //
0x9331621200000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x04), tokenIn)
mstore(add(ptr, 0x24), tokenOut)
mstore(add(ptr, 0x44), receiver)
if iszero(
call(
gas(),
vault,
0x0, // no native transfer
ptr,
0x64, // input length 66 bytes
ptr, // store output here
0x20 // output is just uint
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
amountOut := mload(ptr)
currentOffset := add(currentOffset, 21)
}
return (amountOut, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title LB swapper contract
*/
abstract contract LBSwapper is ERC20Selectors, Masks {
/**
* Swaps exact input on LB
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | swapForY |
* | 21 | 1 | pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _swapLBexactIn(
uint256 fromAmount,
address tokenIn,
address receiver,
address callerAddress,
uint256 currentOffset //
)
internal
returns (uint256 amountOut, uint256 payFlag)
{
assembly {
let ptr := mload(0x40)
let lbData := calldataload(currentOffset)
let pool := shr(96, lbData)
// pre-funded is >= 2
payFlag := and(UINT8_MASK, shr(80, lbData))
if lt(payFlag, 2) {
let success
// payFlag evaluation
switch payFlag
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pool)
mstore(add(ptr, 0x44), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
// revert if needed
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// swap for Y flag
let swapForY := and(UINT8_MASK, shr(88, lbData))
////////////////////////////////////////////////////
// Execute swap function
////////////////////////////////////////////////////
// swap(bool,address)
mstore(ptr, 0x53c059a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), swapForY)
mstore(add(ptr, 0x24), receiver)
// call swap, revert if invalid/undefined pool
if iszero(call(gas(), pool, 0x0, ptr, 0x44, ptr, 0x20)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// the swap call returns both amounts encoded into a single bytes32 as (amountX,amountY)
switch swapForY
case 0 { amountOut := and(mload(ptr), UINT128_MASK) }
default { amountOut := shr(128, mload(ptr)) }
// skip 22 bytes
currentOffset := add(currentOffset, 22)
}
return (amountOut, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title SyncSwap style swapper, pre-funded, all pool variations
*/
abstract contract SyncSwapper is ERC20Selectors, Masks {
/// @dev selector for swap(bytes,address,address,bytes)
bytes32 internal constant SYNCSWAP_SELECTOR = 0x7132bb7f00000000000000000000000000000000000000000000000000000000;
/**
* Swaps exact input on SyncSwap
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _swapSyncExactIn(
uint256 fromAmount,
address tokenIn,
address receiver,
address callerAddress,
uint256 currentOffset //
)
internal
returns (uint256 buyAmount, uint256 payFlag)
{
assembly {
let syncSwapData := calldataload(currentOffset)
let ptr := mload(0x40)
// facilitate payment if needed
payFlag := and(UINT8_MASK, shr(88, syncSwapData))
let pool := shr(96, syncSwapData)
if lt(payFlag, 2) {
let success
switch payFlag
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pool)
mstore(add(ptr, 0x44), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// selector for swap(...)
mstore(ptr, SYNCSWAP_SELECTOR)
mstore(add(ptr, 4), 0x80) // first param set offset
mstore(add(ptr, 36), 0x0) // sender address
////////////////////////////////////////////////////
// We store the bytes length to zero (no callback)
// and directly trigger the swap
////////////////////////////////////////////////////
mstore(add(ptr, 68), 0x0) // callback receiver address
mstore(add(ptr, 100), 0x100) // calldata offset
mstore(add(ptr, 132), 0x60) // datalength
mstore(add(ptr, 164), tokenIn) // tokenIn
mstore(add(ptr, 196), receiver) // to
mstore(add(ptr, 228), 0) // withdraw mode
mstore(add(ptr, 260), 0) // path length is zero
if iszero(
call(
gas(),
pool, // pool
0x0,
ptr, // input selector
292, // input size = 164 (selector (4bytes) plus 5*32bytes)
ptr, // output
0x40 // output size = 0x40
)
) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
buyAmount := mload(add(ptr, 0x20))
currentOffset := add(currentOffset, 21)
}
return (buyAmount, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Uniswap V2 type swapper contract
* @notice We do everything UniV2 here, incl Solidly, FoT, exactIn and -Out
*/
abstract contract V2TypeGeneric is ERC20Selectors, Masks {
////////////////////////////////////////////////////
// Uni V2 type selctors
////////////////////////////////////////////////////
/// @dev selector for getReserves()
bytes32 private constant UNI_V2_GET_RESERVES = 0x0902f1ac00000000000000000000000000000000000000000000000000000000;
/// @dev selector for swap(...)
bytes32 private constant UNI_V2_SWAP = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;
/// @notice fixed selector transferFrom(...) on permit2
bytes32 private constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;
/// @notice deterministically deployed pemrit2 address
address private constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 2 | feeDenom |
* | 22 | 1 | forkId |
* | 23 | 2 | calldataLength | <-- 0: pay from self; 1: caller pays; 3: pre-funded;
* | 25 | calldataLength | calldata |
*/
function _swapUniswapV2PoolExactInGeneric(
uint256 amountIn,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (
uint256 buyAmount,
// we need this as transient variable to not overflow the stacksize
// the clLength is the calldata length for the callback at the beginning
// and willl be used as the new incremented offset
// this is to prevent `stack too deep` without using additional `mstore`
uint256 clLength
)
{
assembly {
let ptr := mload(0x40) // free memory pointer
////////////////////////////////////////////////////
// We extract all relevant data from the path bytes blob
////////////////////////////////////////////////////
let pool := calldataload(currentOffset)
clLength := and(UINT16_MASK, shr(56, pool))
// Compute the buy amount based on the pair reserves.
let zeroForOne :=
lt(
tokenIn,
tokenOut //
)
switch lt(
and(UINT8_MASK, shr(72, pool)), // this is the forkId
128 // less than 128 indicates that it is classic uni V2, solidly otherwise
)
case 1 {
// Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
// buyAmount = (pairSellAmount * feeAm * buyReserve) /
// (pairSellAmount * feeAm + sellReserve * 10000);
// this is expected to be 10000 - x, where x is the poolfee in bps
let poolFeeDenom := and(shr(80, pool), UINT16_MASK)
pool := shr(96, pool)
// Call pair.getReserves(), store the results in scrap space
mstore(0x0, UNI_V2_GET_RESERVES)
if iszero(staticcall(gas(), pool, 0x0, 0x4, 0x0, 0x40)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
let reserveIn
switch zeroForOne
case 1 {
// Transpose if pair order is different.
buyAmount := mload(0x20)
reserveIn := mload(0x0)
}
default {
reserveIn := mload(0x20)
buyAmount := mload(0x0)
}
// compute out amount
poolFeeDenom := mul(amountIn, poolFeeDenom)
buyAmount :=
div(
mul(poolFeeDenom, buyAmount),
add(poolFeeDenom, mul(reserveIn, 10000)) //
)
}
// all solidly-based protocols
default {
// we ignore the fee denominator for solidly type DEXs
pool := shr(96, pool)
// selector for getAmountOut(uint256,address)
mstore(ptr, 0xf140a35a00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x4), amountIn)
mstore(add(ptr, 0x24), tokenIn)
if iszero(staticcall(gas(), pool, ptr, 0x44, 0, 0x20)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
buyAmount := mload(0)
}
////////////////////////////////////////////////////
// Prepare the swap tx
////////////////////////////////////////////////////
// selector for swap(...)
mstore(ptr, UNI_V2_SWAP)
switch zeroForOne
case 0 {
mstore(add(ptr, 4), buyAmount)
mstore(add(ptr, 36), 0)
}
default {
mstore(add(ptr, 4), 0)
mstore(add(ptr, 36), buyAmount)
}
mstore(add(ptr, 68), receiver)
mstore(add(ptr, 100), 0x80) // bytes offset
////////////////////////////////////////////////////
// This one is tricky:
// if data length is > 3, we assume a flash swap and defer payment
// if it is 0: we pull from caller,
// 1: we pay from contract, and
// 2: we assume pre-funding
////////////////////////////////////////////////////
switch lt(clLength, 3)
case 0 {
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | caller |
* | 20 | 20 | tokenIn |
* | 40 | 20 | tokenOut |
* | 60 | 1 | forkId |
* | 61 | 2 | calldataLength |
* | 63 | calldataLength | calldata |
*/
mstore(add(ptr, 132), add(clLength, 63)) // calldataLength (within bytes)
mstore(add(ptr, 164), shl(96, callerAddress))
mstore(add(ptr, 184), shl(96, tokenIn))
mstore(add(ptr, 204), shl(96, tokenOut))
// we skip to the forkId offset
currentOffset := add(22, currentOffset)
// increment by 3 here (forkId, calldataLength)
clLength := add(clLength, 3)
// Store callback (incl forkId)
calldatacopy(add(ptr, 224), currentOffset, clLength)
if iszero(
call(
gas(),
pool,
0x0,
ptr, // input selector
add(224, clLength), // 164 + (63+clLength)
0x0, // output = 0
0x0 // output size = 0
)
) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// update clLength as new offset
// we already added forkId and clLengh and datalength
clLength := add(currentOffset, clLength)
}
////////////////////////////////////////////////////
// Otherwise, we have to assume that payment needs to
// be facilitated outside the callback
// 0: caller pays
// 1: pay self
// 2: the swap is pre-funded
// the operator needs to ensure that `amountIn`
// was already sent to the pool
////////////////////////////////////////////////////
default {
switch clLength
case 0 {
// we need an incremented ptr here to not override the swap call below
clLength := add(ptr, 0xC4)
// selector for transferFrom(address,address,uint256)
mstore(clLength, ERC20_TRANSFER_FROM)
mstore(add(clLength, 0x04), callerAddress)
mstore(add(clLength, 0x24), pool)
mstore(add(clLength, 0x44), amountIn)
clLength := call(gas(), tokenIn, 0, clLength, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
clLength, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// transfer plain
case 1 {
// we need an incremented ptr here to not override the swap call below
clLength := add(ptr, 0xC4)
// selector for transfer(address,uint256)
mstore(clLength, ERC20_TRANSFER)
mstore(add(clLength, 0x04), pool)
mstore(add(clLength, 0x24), amountIn)
clLength := call(gas(), tokenIn, 0, clLength, 0x44, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
clLength, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
////////////////////////////////////////////////////
// We store the bytes length to zero (no callback)
// and directly trigger the swap
////////////////////////////////////////////////////
mstore(add(ptr, 0x84), 0) // bytes length
if iszero(call(gas(), pool, 0x0, ptr, 0xA4, 0, 0)) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// update clLength as new offset
clLength := add(currentOffset, 25)
}
}
return (buyAmount, clLength);
}
/**
* Executes an exact input swap internally across major UniV2 forks supporting
* FOT tokens. Will only be used at the begining of a swap path where users sell a FOT token
* Due to the nature of the V2 impleemntation, the callback is not triggered if no calldata is provided
* As such, we never enter the callback implementation when using this function
* @param amountIn sell amount
* @return buyAmount output amount
*/
function _swapUniV2ExactInFOTGeneric(
uint256 amountIn,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (uint256 buyAmount, uint256)
{
assembly {
let ptr := mload(0x40) // free memory pointer
////////////////////////////////////////////////////
// We extract all relevant data from the path bytes blob
////////////////////////////////////////////////////
let pair := calldataload(currentOffset)
// this is expected to be 10000 - x, where x is the poolfee in bps
let poolFeeDenom := and(shr(80, pair), UINT16_MASK)
// We only allow the caller to pay as otherwise, the fee is charged twice
switch and(UINT16_MASK, shr(56, pair))
case 0 {
pair := shr(96, pair)
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pair)
mstore(add(ptr, 0x44), amountIn)
let success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
case 1 {
pair := shr(96, pair)
// the 1-case here is a permit2 transfer
// this is needed as FOT usually has no permit, meaning
// direct permissioning is not possible
// the "real" 1-case is not mixed up with the "regular" uni V2 case
// as for FOT the intermediate holding of the asset cannot be facilitated
mstore(ptr, PERMIT2_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pair)
mstore(add(ptr, 0x44), amountIn)
mstore(add(ptr, 0x64), tokenIn)
if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
default { revert(0, 0) }
// we define this as token in and later re-assign this to
// reserve in to prevent stack too deep errors
// Compute the buy amount based on the pair reserves.
{
let zeroForOne :=
lt(
tokenIn,
tokenOut // tokenOut
)
// Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
// buyAmount = (pairSellAmount * feeAm * buyReserve) /
// (pairSellAmount * feeAm + sellReserve * 1000);
// Call pair.getReserves(), store the results in scrap space
mstore(0x0, UNI_V2_GET_RESERVES)
if iszero(staticcall(gas(), pair, 0x0, 0x4, 0x0, 0x40)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// Revert if the pair contract does not return at least two words.
if lt(returndatasize(), 0x40) { revert(0, 0) }
let sellReserve
switch zeroForOne
case 1 {
// Transpose if pair order is different.
sellReserve := mload(0x0)
buyAmount := mload(0x20)
}
default {
sellReserve := mload(0x20)
buyAmount := mload(0x0)
}
// call tokenIn.balanceOf(pair)
mstore(0x0, ERC20_BALANCE_OF)
mstore(0x4, pair)
// we store the result
pop(staticcall(gas(), tokenIn, 0x0, 0x24, 0x0, 0x20))
amountIn := sub(mload(0x0), sellReserve)
// adjustment via denominator
poolFeeDenom := mul(amountIn, poolFeeDenom)
buyAmount := div(mul(poolFeeDenom, buyAmount), add(poolFeeDenom, mul(sellReserve, 10000)))
////////////////////////////////////////////////////
// Prepare the swap tx
////////////////////////////////////////////////////
// selector for swap(...)
mstore(ptr, UNI_V2_SWAP)
switch zeroForOne
case 0 {
mstore(add(ptr, 0x4), buyAmount)
mstore(add(ptr, 0x24), 0)
}
default {
mstore(add(ptr, 0x4), 0)
mstore(add(ptr, 0x24), buyAmount)
}
mstore(add(ptr, 0x44), receiver)
mstore(add(ptr, 0x64), 0x80) // bytes offset
////////////////////////////////////////////////////
// We store the bytes length to zero (no callback)
// and directly trigger the swap
////////////////////////////////////////////////////
mstore(add(ptr, 0x84), 0) // bytes length
if iszero(call(gas(), pair, 0x0, ptr, 0xA4, 0, 0)) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
// update clLength as new offset
currentOffset := add(currentOffset, 25)
}
return (buyAmount, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Uniswap V3 type swapper contract
* @notice Executes Cl swaps and pushes data to the callbacks
* the data can be empty, the callback then jsut fills the swap
*/
abstract contract V3TypeGeneric is Masks {
constructor() {}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 20 | 1 | forkId |
* | 21 | 2 | fee |
* | 23 | 2 | calldataLength |
* | 25 | calldataLength | calldata |
*/
function _swapUniswapV3PoolExactInGeneric(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (uint256 receivedAmount, uint256)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
// read the pool address
let pool := calldataload(currentOffset)
// skip pool
currentOffset := add(currentOffset, 20)
let clLength := and(UINT16_MASK, shr(56, pool))
pool :=
shr(
96,
pool // starts as first param
)
let zeroForOne :=
lt(
tokenIn,
tokenOut //
)
// Prepare external call data
// Store swap selector (0x128acb08)
mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000)
// Store toAddress
mstore(add(ptr, 4), receiver)
// Store direction
mstore(add(ptr, 36), zeroForOne)
// Store fromAmount
mstore(add(ptr, 68), fromAmount)
// Store data offset
mstore(add(ptr, 132), 0xa0)
let plStored := add(clLength, 65)
// Store data length
mstore(add(ptr, 164), plStored)
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | caller |
* | 20 | 20 | tokenIn |
* | 40 | 20 | tokenOut |
* | 60 | 1 | dexId | <- we use calldatacopy from here
* | 61 | 2 | fee |
* | 63 | 2 | calldataLength |
* | 65 | calldataLength | calldata |
*/
mstore(add(ptr, 196), shl(96, callerAddress))
mstore(add(ptr, 216), shl(96, tokenIn))
mstore(add(ptr, 236), shl(96, tokenOut))
// Store furhter calldata (add 4 to length due to fee and clLength)
calldatacopy(add(ptr, 256), currentOffset, add(clLength, 5))
switch zeroForOne
case 0 {
// Store sqrtPriceLimitX96
mstore(add(ptr, 100), MAX_SQRT_RATIO)
// Perform the external 'swap' call
if iszero(call(gas(), pool, 0, ptr, add(228, plStored), ptr, 32)) {
// store return value directly to free memory pointer
// The call failed; we retrieve the exact error message and revert with it
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// If direction is 0, return amount0
receivedAmount := mload(ptr)
}
default {
// Store sqrtPriceLimitX96
mstore(add(ptr, 100), MIN_SQRT_RATIO)
// Perform the external 'swap' call
if iszero(call(gas(), pool, 0, ptr, add(228, plStored), ptr, 64)) {
// store return value directly to free memory pointer
// The call failed; we retrieve the exact error message and revert with it
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// If direction is 1, return amount1
receivedAmount := mload(add(ptr, 32))
}
// receivedAmount = -receivedAmount
receivedAmount := sub(0, receivedAmount)
switch lt(clLength, 2)
case 1 { currentOffset := add(currentOffset, 5) }
default { currentOffset := add(currentOffset, add(5, clLength)) }
}
return (receivedAmount, currentOffset);
}
/// @dev Swap exact input through izumi
function _swapIZIPoolExactInGeneric(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (uint256 receivedAmount, uint256)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
// read the pool address
let pool := calldataload(currentOffset)
// skip pool
currentOffset := add(currentOffset, 20)
let clLength := and(UINT16_MASK, shr(56, pool))
pool :=
shr(
96,
pool // starts as first param
)
switch lt(
tokenIn,
tokenOut //
)
case 0 {
// Prepare external call data
// Store swapY2X selector (0x2c481252)
mstore(ptr, 0x2c48125200000000000000000000000000000000000000000000000000000000)
// Store recipient
mstore(add(ptr, 4), receiver)
// Store fromAmount
mstore(add(ptr, 36), fromAmount)
// Store highPt
mstore(add(ptr, 68), 799999)
// Store data offset
mstore(add(ptr, 100), 0x80)
let plStored := add(clLength, 65)
// Store data length
mstore(add(ptr, 132), plStored)
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | caller |
* | 20 | 20 | tokenIn |
* | 40 | 20 | tokenOut |
* | 60 | 1 | dexId |
* | 61 | 2 | fee | <- we use calldatacopy from here
* | 63 | 2 | calldataLength |
* | 65 | calldataLength | calldata |
*/
mstore(add(ptr, 164), shl(96, callerAddress))
mstore(add(ptr, 184), shl(96, tokenIn))
mstore(add(ptr, 204), shl(96, tokenOut))
// Store furhter calldata
calldatacopy(add(ptr, 224), currentOffset, add(clLength, 5))
// Perform the external 'swap' call
if iszero(call(gas(), pool, 0, ptr, add(196, plStored), ptr, 32)) {
// store return value directly to free memory pointer
// The call failed; we retrieve the exact error message and revert with it
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// If direction is 0, return amount0
receivedAmount := mload(ptr)
}
default {
// Prepare external call data
// Store swapX2Y selector (0x857f812f)
mstore(ptr, 0x857f812f00000000000000000000000000000000000000000000000000000000)
// Store toAddress
mstore(add(ptr, 4), receiver)
// Store fromAmount
mstore(add(ptr, 36), fromAmount)
// Store sqrtPriceLimitX96
mstore(add(ptr, 68), sub(0, 799999))
// Store data offset
mstore(add(ptr, 100), 0x80)
let plStored := add(clLength, 65)
// Store data length
mstore(add(ptr, 132), plStored)
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | caller |
* | 20 | 20 | tokenIn |
* | 40 | 20 | tokenOut |
* | 60 | 1 | dexId |
* | 61 | 2 | fee | <- we use calldatacopy from here
* | 63 | 2 | calldataLength |
* | 65 | calldataLength | calldata |
*/
mstore(add(ptr, 164), shl(96, callerAddress))
mstore(add(ptr, 184), shl(96, tokenIn))
mstore(add(ptr, 204), shl(96, tokenOut))
// Store furhter calldata
calldatacopy(add(ptr, 224), currentOffset, add(clLength, 5))
// Perform the external 'swap' call
if iszero(call(gas(), pool, 0, ptr, add(196, plStored), ptr, 64)) {
// store return value directly to free memory pointer
// The call failed; we retrieve the exact error message and revert with it
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// If direction is 1, return amount1
receivedAmount := mload(add(ptr, 32))
}
switch lt(clLength, 2)
case 1 { currentOffset := add(currentOffset, 5) }
default { currentOffset := add(currentOffset, add(5, clLength)) }
}
return (receivedAmount, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title Uniswap V4 type swapper contract
* @notice Can only be executed within `manager.unlock()`
* Ergo, if Uniswap v4 is in the path (no matter how many times), one has to push
* the swap data and execution into the UniswapV4.unlock
* This should be usable together with flash loans from their singleton
*
* Cannot unlock multiple times!
*
* The execution of a swap follows the steps:
*
* 1) pm.unlock(...) (outside of this contract)
* 2) call pm.swap(...)
* 3) getDeltas via pm.exttload(bytes[]) (we get it for input/output at once)
* we get [inputDelta, outputDelta] (pay amount, receive amount)
* As for the V4 docs, `swap` does not necessarily return the correct
* deltas when using hooks, that is why we remain with fetching the deltas
* 4) call pm.take to pull the outputDelta from pm
* 4) if input nonnative call pm.sync() and send inputDelta to pm
* 5) call pm.settle to settle the swap (if native with input value)
*
* Technically it is possible to call multihops within V4 where one would
* skip the `take` when the next swap is also for V4
* This is a bit annoying to implement and for this first version we skip it
*/
abstract contract V4TypeGeneric is ERC20Selectors, Masks {
/**
* We need all these selectors for executing a single swap
*/
bytes32 private constant SWAP = 0xf3cd914c00000000000000000000000000000000000000000000000000000000;
bytes32 private constant TAKE = 0x0b0d9c0900000000000000000000000000000000000000000000000000000000;
bytes32 private constant SETTLE = 0x11da60b400000000000000000000000000000000000000000000000000000000;
bytes32 private constant SYNC = 0xa584119400000000000000000000000000000000000000000000000000000000;
bytes32 private constant EXTTLOAD = 0x9bf6645f00000000000000000000000000000000000000000000000000000000;
constructor() {}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | hooks |
* | 20 | 20 | manager |
* | 40 | 3 | fee |
* | 43 | 3 | tickSpacing |
* | 46 | 1 | payFlag |
* | 47 | 2 | calldataLength |
* | 49 | calldataLength | calldata |
*/
function _swapUniswapV4ExactInGeneric(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver,
uint256 currentOffset,
address callerAddress
)
internal
returns (
// this var changes from zeroToOne to the received amount
uint256 receivedAmount,
// similar to other implementations, we use this temp variable
// to avoid stackToo deep
uint256 tempVar
)
{
// struct PoolKey {
// address currency0; 4
// address currency1; 36
// uint24 fee; 68
// int24 tickSpacing; 100
// address hooks; 132
// }
// struct SwapParams {
// bool zeroForOne; 164
// int256 amountSpecified; 196
// uint160 sqrtPriceLimitX96; 228
// }
////////////////////////////////////////////
// This is the function selector we need
////////////////////////////////////////////
// swap(
// PoolKey memory key,
// SwapParams memory params,
// bytes calldata hookData //
// )
// solhint-disable-next-line no-inline-assembly
assembly {
let ptr := mload(0x40)
// read the pool address
let pool := calldataload(add(currentOffset, 20))
// let tickSpacing := and(UINT24_MASK, shr(48, pool))
// pay flag
tempVar := and(UINT8_MASK, shr(40, pool))
let clLength := and(UINT16_MASK, shr(24, pool))
// Prepare external call data
// Store swap selector
mstore(ptr, SWAP)
/**
* PoolKey (2/2)
*/
// Store fee
mstore(add(ptr, 68), and(UINT24_MASK, shr(72, pool)))
// Store tickSpacing
mstore(add(ptr, 100), and(UINT24_MASK, shr(48, pool)))
// get the hook
let hook := shr(96, calldataload(currentOffset))
mstore(add(ptr, 132), hook)
pool := shr(96, pool)
// Store data offset
mstore(add(ptr, 260), 0x120)
// Store data length
mstore(add(ptr, 292), clLength)
/**
* SwapParams
*/
// Store fromAmount
mstore(add(ptr, 196), sub(0, fromAmount))
// skip pool plus params
currentOffset := add(currentOffset, 49)
if xor(0, clLength) {
// Store furhter calldata (add 4 to length due to fee and clLength)
calldatacopy(add(ptr, 324), currentOffset, clLength)
}
// use receivedAmount as zeroForOne
receivedAmount := lt(tokenIn, tokenOut)
// Store zeroForOne
mstore(add(ptr, 164), receivedAmount)
// store pool key and limits
switch receivedAmount
// zeroForOne
case 1 {
/**
* PoolKey (1/2)
*/
// Store ccy0
mstore(add(ptr, 4), tokenIn)
// Store ccy1
mstore(add(ptr, 36), tokenOut)
// Store sqrtPriceLimitX96
mstore(add(ptr, 228), MIN_SQRT_RATIO)
}
default {
/**
* PoolKey (1/2)
*/
// Store ccy0
mstore(add(ptr, 4), tokenOut)
// Store ccy1
mstore(add(ptr, 36), tokenIn)
// Store sqrtPriceLimitX96
mstore(add(ptr, 228), MAX_SQRT_RATIO)
}
// Perform the external 'swap' call
if iszero(call(gas(), pool, 0, ptr, add(324, clLength), 0, 0x20)) {
// store return value directly to free memory pointer
// The call failed; we retrieve the exact error message and revert with it
returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory
revert(0, returndatasize()) // Revert with the error message
}
// if no hook provided, read plain swap, otherwise, read deltas
switch iszero(hook)
case 1 {
fromAmount := mload(0)
switch receivedAmount
case 1 {
receivedAmount := and(UINT128_MASK, fromAmount)
fromAmount := sub(0, sar(128, fromAmount))
}
default {
receivedAmount := shr(128, fromAmount)
fromAmount := sub(0, signextend(15, fromAmount))
}
}
default {
/**
* Load actual deltas from pool manager if hook provided
* This is recommended in the docs
*/
mstore(ptr, EXTTLOAD)
mstore(add(ptr, 4), 0x20) // offset
mstore(add(ptr, 36), 2) // array length
mstore(0, address())
mstore(0x20, tokenIn)
// first key
mstore(add(ptr, 68), keccak256(0, 0x40))
// output token for 2nd key
mstore(0x20, tokenOut)
// second key
mstore(add(ptr, 100), keccak256(0, 0x40))
// get the deltas
pop(
staticcall(
gas(),
pool,
ptr,
132, // selector + offs + length + key0 + key1
ptr,
0x80 // output (offset, length, data0, data1)
)
)
// 1st array output element
fromAmount := sub(0, mload(add(ptr, 0x40)))
// 2nd array output element
receivedAmount := mload(add(ptr, 0x60))
}
/**
* Pull funds to receiver
*/
mstore(ptr, TAKE)
mstore(add(ptr, 4), tokenOut) //
mstore(add(ptr, 36), receiver)
mstore(add(ptr, 68), receivedAmount)
if iszero(
call(
gas(),
pool,
0x0,
ptr, //
100,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
/**
* If the pay mode is >=2, we assume deferred payment
* This means that the composer must manually settle
* for the input amount
* Warning: This should not be done for pools with
* arbitrary hooks as these can have cases where
* `amountIn` selected != actual `amountIn`
*/
if lt(tempVar, 2) {
/**
* Pull funds from payer
*/
switch iszero(tokenIn)
// nonnative
case 0 {
/**
* Sync pay asset
*/
mstore(0, SYNC)
mstore(4, tokenIn) // offset
if iszero(
call(
gas(),
pool,
0x0,
0, //
36,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
{
// temp var is the pay mode and then succes flag
switch tempVar
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), pool)
mstore(add(ptr, 0x44), fromAmount)
tempVar := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), pool)
mstore(add(ptr, 0x24), fromAmount)
tempVar := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
if iszero(
and(
tempVar, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// we continue to use it as native value
// zero for erc20 transfer
tempVar := 0
}
// native (temVar is the fromAmount)
default { tempVar := fromAmount }
/**
* Settle funds in pool manager
*/
// settle amount
mstore(0, SETTLE)
if iszero(
call(
gas(),
pool,
tempVar,
0, //
4,
// selector, offset, length, data
0x0, // output = empty
0x0 // output size = zero
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
return (receivedAmount, currentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title WooFi swapper contract
*/
abstract contract WooFiSwapper is ERC20Selectors, Masks {
/// @dev WooFi rebate receiver
address private constant REBATE_RECIPIENT = 0x0000000000000000000000000000000000000000;
constructor() {}
/**
* Swaps exact input on WOOFi DEX
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | pool |
* | 21 | 1 | pay flag | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _swapWooFiExactIn(
uint256 fromAmount,
address tokenIn,
address tokenOut,
address receiver,
address callerAddress,
uint256 currentOffset
)
internal
returns (
uint256 poolThenAmountOut,
// first assign payFlag, then return offset
uint256 payFlagCurrentOffset
)
{
assembly {
let ptr := mload(0x40)
// load first 32 bytes
poolThenAmountOut := calldataload(currentOffset)
// pay flag extraction
payFlagCurrentOffset := and(UINT8_MASK, shr(88, poolThenAmountOut))
// get pool
poolThenAmountOut := shr(96, poolThenAmountOut)
switch lt(payFlagCurrentOffset, 2)
case 1 {
let success
// payFlag evaluation
switch payFlagCurrentOffset
case 0 {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), poolThenAmountOut)
mstore(add(ptr, 0x44), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x64, 0, 32)
}
// transfer plain
case 1 {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), poolThenAmountOut)
mstore(add(ptr, 0x24), fromAmount)
success := call(gas(), tokenIn, 0, ptr, 0x44, 0, 32)
}
let rdsize := returndatasize()
// revert if needed
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// selector for swap(address,address,uint256,uint256,address,address)
mstore(
ptr, //
0x7dc2038200000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x04), tokenIn)
mstore(add(ptr, 0x24), tokenOut)
mstore(add(ptr, 0x44), fromAmount)
mstore(add(ptr, 0x64), 0x0) // amountOutMin unused
mstore(add(ptr, 0x84), receiver) // recipient
mstore(add(ptr, 0xA4), REBATE_RECIPIENT) // rebateTo
if iszero(
call(
gas(),
poolThenAmountOut,
0x0, // no native transfer
ptr,
0xC4, // input length 196
ptr, // store output here
0x20 // output is just uint
)
) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
// map amountOut to var
poolThenAmountOut := mload(ptr)
// skip 21 bytes
payFlagCurrentOffset := add(currentOffset, 21)
}
return (poolThenAmountOut, payFlagCurrentOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
// solhint-disable max-line-length
import {ERC20Selectors} from "../../../shared/selectors/ERC20Selectors.sol";
import {Masks} from "../../../shared/masks/Masks.sol";
/**
* @title ERC4626 vault & native wrap "swapper" contract
* The logic is as follows
* - the only parameter is an indicator for the operation (0: native wrap | 1: deposit | 2: redeem)
* - the native case has the direction implied by the asset address (asset=0 mean native),
* as such, assetIn=0 means wrap, in this case amountIn=amountOut
* - ERC4626 only uses deposit and redeem (as these are the exact in operations), these
* require the output amount to be read from the returndata
*/
abstract contract Wrapper is ERC20Selectors, Masks {
/// @dev deposit(...)
bytes32 private constant ERC4626_DEPOSIT = 0x6e553f6500000000000000000000000000000000000000000000000000000000;
/// @dev redeem(...)
bytes32 private constant ERC4626_REDEEM = 0xba08765200000000000000000000000000000000000000000000000000000000;
// NativeTransferFailed()
bytes4 private constant NATIVE_TRANSFER = 0xf4b3b1bc;
// WrapFailed()
bytes4 private constant WRAP = 0xc30d93ce;
// Note that the for native wrapping, the assetIn/assetOut constellation defines the direction
// For ERC4626, the direction is defined by the operation parameter
// 1: deposit: deposit to vault, assetIn is the underlying and assetOut is the vault
// 2: redeem: redeem shares, assetIn is the vault, assetOut is the underlyinh
/**
* This one is for overring the DEX implementation
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 1 | operation |
* | 1 | 1 | pay config | <- 0: caller pays; 1: contract pays; greater: pre-funded
*/
function _wrapperOperation(
address assetIn,
address assetOut,
uint256 amount,
address receiver,
address callerAddress,
uint256 currentOffset
)
internal
virtual
returns (uint256 amountOut, uint256 operationThenOffset)
{
assembly {
operationThenOffset := calldataload(currentOffset)
let ptr := mload(0x40)
// only need to check whether we have to pull from caller
// Note: this should be avoided for native in
if iszero(and(shr(240, operationThenOffset), UINT8_MASK)) {
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), address())
mstore(add(ptr, 0x44), amount)
let success := call(gas(), assetIn, 0, ptr, 0x64, 0, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
// shift operation to lowest byte
switch shr(248, operationThenOffset)
case 0 {
amountOut := amount
// native is input: wrap
switch iszero(assetIn)
case 1 {
if iszero(
call(
gas(),
assetOut,
amount, // ETH to deposit
0x0, // no input
0x0, // input size = zero
0x0, // output = empty
0x0 // output size = zero
)
) {
// revert when native transfer fails
mstore(0, NATIVE_TRANSFER)
revert(0, 0x4)
}
// transfer to destination if needed
// this is to make receipts of native consistent with pre-funded
// calls
if xor(receiver, address()) {
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), amount)
let success := call(gas(), assetOut, 0, ptr, 0x44, 0, 32)
let rdsize := returndatasize()
// Check for ERC20 success. ERC20 tokens should return a boolean,
// but some don't. We accept 0-length return data as success, or at
// least 32 bytes that starts with a 32-byte boolean true.
success :=
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(0), 1) // starts with uint256(1)
)
)
)
if iszero(success) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
// assetIn is expected to be wNative
// note that we do not do a transfer to a receiver as DEXs that require native require
// to attach native to the respective swap call
default {
// selector for withdraw(uint256)
mstore(0x0, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
mstore(0x4, amount)
if iszero(
call(
gas(),
assetIn,
0x0, // no ETH
0x0, // start of data
0x24, // input size = selector plus amount
0x0, // output = empty
0x0 // output size = zero
)
) {
// revert when native transfer fails
mstore(0, WRAP)
revert(0, 0x4)
}
// transfer native to receiver if needed
if xor(receiver, address()) {
if iszero(call(gas(), receiver, amount, 0x0, 0x0, 0x0, 0x0)) {
// revert when native transfer fails
mstore(0, NATIVE_TRANSFER)
revert(0, 0x4)
}
}
}
}
// the other 2 cases have receiver as parameter
case 1 {
// Approve vault if needed
mstore(0x0, assetIn)
mstore(0x20, 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3) // CALL_MANAGEMENT_APPROVALS
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x0, assetOut)
let key := keccak256(0x0, 0x40)
// check if already approved
if iszero(sload(key)) {
// selector for approve(address,uint256)
mstore(ptr, ERC20_APPROVE)
mstore(add(ptr, 0x04), assetOut)
mstore(add(ptr, 0x24), MAX_UINT256)
pop(call(gas(), assetIn, 0, ptr, 0x44, ptr, 32))
sstore(key, 1)
}
mstore(ptr, ERC4626_DEPOSIT)
mstore(add(ptr, 0x4), amount) // assets
mstore(add(ptr, 0x24), receiver) // receiver
if iszero(call(gas(), assetOut, 0x0, ptr, 0x44, 0x0, 0x20)) {
returndatacopy(0, 0, returndatasize())
revert(0x0, returndatasize())
}
amountOut := mload(0)
}
default {
// this one should not need an approve
mstore(ptr, ERC4626_REDEEM)
mstore(add(ptr, 0x4), amount) // shares
mstore(add(ptr, 0x24), receiver) // receiver
mstore(add(ptr, 0x44), address()) // owner is self as we expect to hold the vault shares
if iszero(call(gas(), assetIn, 0x0, ptr, 0x64, 0x0, 0x20)) {
returndatacopy(0, 0, returndatasize())
revert(0x0, returndatasize())
}
amountOut := mload(0)
}
operationThenOffset := add(currentOffset, 2)
}
return (amountOut, operationThenOffset);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {BaseSwapper} from "./BaseSwapper.sol";
// solhint-disable max-line-length
/**
* @notice entrypoint for swaps
* - supports a broad variety of DEXs
* - this also is the entrypoint for flash-swaps, one only needs to
* add calldata to the respective call to trigger it
*/
abstract contract Swaps is BaseSwapper {
function _swap(uint256 currentOffset, address callerAddress) internal returns (uint256) {
uint256 amountIn;
uint256 minimumAmountReceived;
address tokenIn;
/*
* Store the data for the callback as follows
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 16 | amount | <-- input amount
* | 16 | 16 | amountMax | <-- slippage check
* | 32 | 20 | tokenIn |
* | 52 | any | data |
*
* `data` is a path matrix definition (see BaseSwapepr)
*/
assembly {
minimumAmountReceived := calldataload(currentOffset)
amountIn := shr(128, minimumAmountReceived)
minimumAmountReceived := and(UINT128_MASK, minimumAmountReceived)
currentOffset := add(currentOffset, 32)
let dataStart := calldataload(currentOffset)
tokenIn := shr(96, dataStart)
currentOffset := add(20, currentOffset)
/**
* if the amount is zero, we assume that the contract balance is swapped
*/
if iszero(amountIn) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(staticcall(gas(), tokenIn, 0x0, 0x24, 0x0, 0x20))
// load the retrieved balance
amountIn := mload(0x0)
}
}
(amountIn, currentOffset,) = _singleSwapSplitOrRoute(
amountIn,
tokenIn,
callerAddress,
currentOffset //
);
assembly {
if gt(minimumAmountReceived, amountIn) {
mstore(0x0, SLIPPAGE)
revert(0x0, 0x4)
}
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {BaseUtils} from "contracts/1delta/composer/generic/BaseUtils.sol";
/**
* @title Token transfer contract - should work across all EVMs - use Uniswap style Permit2
*/
contract AssetTransfers is BaseUtils {
// approval slot
bytes32 private constant CALL_MANAGEMENT_APPROVALS = 0x1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f3;
/// @notice fixed selector transferFrom(...) on permit2
bytes32 private constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;
/// @notice deterministically deployed pemrit2 address
address private constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | asset |
* | 20 | 20 | receiver |
* | 40 | 16 | amount |
*/
function _permit2TransferFrom(uint256 currentOffset, address callerAddress) internal returns (uint256) {
////////////////////////////////////////////////////
// Transfers tokens from caller to this address
// zero amount flags that the entire balance is sent
////////////////////////////////////////////////////
assembly {
let underlying := shr(96, calldataload(currentOffset))
let receiver := shr(96, calldataload(add(currentOffset, 20)))
let amount := shr(128, calldataload(add(currentOffset, 40)))
// when entering 0 as amount, use the callwe balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, callerAddress)
// call to token
pop(
staticcall(
gas(),
underlying, // token
0x0,
0x24,
0x0,
0x20
)
)
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40)
mstore(ptr, PERMIT2_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), receiver)
mstore(add(ptr, 0x44), amount)
mstore(add(ptr, 0x64), underlying)
if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
currentOffset := add(currentOffset, 56)
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | asset |
* | 20 | 20 | receiver |
* | 40 | 16 | amount |
*/
function _transferFrom(uint256 currentOffset, address callerAddress) internal returns (uint256) {
////////////////////////////////////////////////////
// Transfers tokens from caller to this address
// zero amount flags that the entire balance is sent
////////////////////////////////////////////////////
assembly {
let underlying := shr(96, calldataload(currentOffset))
let receiver := shr(96, calldataload(add(currentOffset, 20)))
let amount := shr(128, calldataload(add(currentOffset, 40)))
// when entering 0 as amount, use the callwe balance
if iszero(amount) {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, callerAddress)
// call to token
pop(
staticcall(
gas(),
underlying, // token
0x0,
0x24,
0x0,
0x20
)
)
// load the retrieved balance
amount := mload(0x0)
}
let ptr := mload(0x40) // free memory pointer
// selector for transferFrom(address,address,uint256)
mstore(ptr, ERC20_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), receiver)
mstore(add(ptr, 0x44), amount)
let success := call(gas(), underlying, 0, ptr, 0x64, ptr, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
currentOffset := add(currentOffset, 56)
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | asset |
* | 20 | 20 | receiver | <- use wrapped native here to wrap
* | 40 | 1 | config |
* | 41 | 16 | amount |
*/
function _sweep(uint256 currentOffset) internal returns (uint256) {
////////////////////////////////////////////////////
// Transfers either token or native balance from this
// contract to receiver. Reverts if minAmount is
// less than the contract balance
// config
// 0: sweep balance and validate against amount
// fetches the balance and checks balance >= amount
// 1: transfer amount to receiver, skip validation
////////////////////////////////////////////////////
assembly {
let underlying := shr(96, calldataload(currentOffset))
// we skip shr by loading the address to the lower bytes
let receiver := shr(96, calldataload(add(currentOffset, 20)))
// load so that amount is in the lower 14 bytes already
let providedAmount := calldataload(add(currentOffset, 25))
// load config
let config := and(UINT8_MASK, shr(128, providedAmount))
// mask amount
providedAmount := and(UINT128_MASK, providedAmount)
// initialize transferAmount
let transferAmount
// zero address is native
switch iszero(underlying)
////////////////////////////////////////////////////
// Transfer token
////////////////////////////////////////////////////
case 0 {
// for config = 0, the amount is the balance and we
// check that the balance is larger tha the amount provided
switch config
case 0 {
// selector for balanceOf(address)
mstore(0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x04, address())
// call to token
pop(
staticcall(
gas(),
underlying,
0x0,
0x24,
0x0,
0x20 //
)
)
// load the retrieved balance
transferAmount := mload(0x0)
// revert if balance is not enough
if lt(transferAmount, providedAmount) {
mstore(0, SLIPPAGE)
revert(0, 0x4)
}
}
default { transferAmount := providedAmount }
if gt(transferAmount, 0) {
let ptr := mload(0x40) // free memory pointer
// selector for transfer(address,uint256)
mstore(ptr, ERC20_TRANSFER)
mstore(add(ptr, 0x04), receiver)
mstore(add(ptr, 0x24), transferAmount)
let success := call(gas(), underlying, 0, ptr, 0x44, ptr, 32)
let rdsize := returndatasize()
if iszero(
and(
success, // call itself succeeded
or(
iszero(rdsize), // no return data, or
and(
gt(rdsize, 31), // at least 32 bytes
eq(mload(ptr), 1) // starts with uint256(1)
)
)
)
) {
returndatacopy(0, 0, rdsize)
revert(0, rdsize)
}
}
}
////////////////////////////////////////////////////
// Transfer native
////////////////////////////////////////////////////
default {
switch config
case 0 {
transferAmount := selfbalance()
// revert if balance is not enough
if lt(transferAmount, providedAmount) {
mstore(0, SLIPPAGE)
revert(0, 0x4)
}
}
default { transferAmount := providedAmount }
if gt(transferAmount, 0) {
if iszero(
call(
gas(),
receiver,
transferAmount,
0x0, // input = empty for fallback/receive
0x0, // input size = zero
0x0, // output = empty
0x0 // output size = zero
)
) {
mstore(0, NATIVE_TRANSFER)
revert(0, 0x4) // revert when native transfer fails
}
}
}
currentOffset := add(currentOffset, 57)
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|----------------------|
* | 0 | 20 | token |
* | 20 | 20 | target |
*/
function _approve(uint256 currentOffset) internal returns (uint256) {
assembly {
// load underlying and target
let underlying := shr(96, calldataload(currentOffset))
let target := shr(96, calldataload(add(currentOffset, 20)))
// check whether the approval alderady was done
// if so, we can skip this part silently
mstore(0x0, underlying)
mstore(0x20, CALL_MANAGEMENT_APPROVALS)
mstore(0x20, keccak256(0x0, 0x40))
mstore(0x0, target)
let key := keccak256(0x0, 0x40)
// check if already approved
if iszero(sload(key)) {
let ptr := mload(0x40)
// selector for approve(address,uint256)
mstore(ptr, ERC20_APPROVE)
mstore(add(ptr, 0x04), target)
mstore(add(ptr, 0x24), MAX_UINT256)
pop(
call(
gas(),
underlying, //
0,
ptr,
0x44,
ptr,
32
)
)
sstore(key, 1)
}
currentOffset := add(currentOffset, 40)
}
return currentOffset;
}
/*
* | Offset | Length (bytes) | Description |
* |--------|----------------|---------------------|
* | 0 | 20 | wrappedNativeAddress|
* | 20 | 20 | receiver |
* | 40 | 1 | config |
* | 41 | 16 | amount |
*/
function _unwrap(uint256 currentOffset) internal virtual returns (uint256) {
////////////////////////////////////////////////////
// Transfers either token or native balance from this
// contract to receiver. Reverts if minAmount is
// less than the contract balance
// config
// 0: sweep balance and validate against amount
// fetches the balance and checks balance >= amount
// 1: transfer amount to receiver, skip validation
////////////////////////////////////////////////////
assembly {
// load receiver
let wrapperAsset := shr(96, calldataload(currentOffset))
// load receiver
let receiver := shr(96, calldataload(add(currentOffset, 20)))
// load so that amount is in the lower 14 bytes already
let providedAmount := calldataload(add(currentOffset, 25))
// load config
let config := and(UINT8_MASK, shr(128, providedAmount))
// mask amount
providedAmount := and(UINT128_MASK, providedAmount)
let transferAmount
// mask away the top bitmap
providedAmount := and(UINT120_MASK, providedAmount)
// validate if config is zero, otherwise skip
switch config
case 0 {
// selector for balanceOf(address)
mstore(0x0, ERC20_BALANCE_OF)
// add this address as parameter
mstore(0x4, address())
// call to underlying
pop(staticcall(gas(), wrapperAsset, 0x0, 0x24, 0x0, 0x20))
transferAmount := mload(0x0)
if lt(transferAmount, providedAmount) {
mstore(0, SLIPPAGE)
revert(0, 0x4)
}
}
default { transferAmount := providedAmount }
if gt(transferAmount, 0) {
// selector for withdraw(uint256)
mstore(0x0, 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000)
mstore(0x4, transferAmount)
if iszero(
call(
gas(),
wrapperAsset,
0x0, // no ETH
0x0, // start of data
0x24, // input size = selector plus amount
0x0, // output = empty
0x0 // output size = zero
)
) {
// should only revert if receiver cannot receive native
mstore(0, NATIVE_TRANSFER)
revert(0, 0x4)
}
// transfer to receiver if different from this address
if xor(receiver, address()) {
// transfer native to receiver
if iszero(
call(
gas(),
receiver,
transferAmount,
0x0, // input = empty for fallback
0x0, // input size = zero
0x0, // output = empty
0x0 // output size = zero
)
) {
// should only revert if receiver cannot receive native
mstore(0, NATIVE_TRANSFER)
revert(0, 0x4)
}
}
}
currentOffset := add(currentOffset, 57)
}
return currentOffset;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {AssetTransfers} from "./AssetTransfers.sol";
import {TransferIds} from "../enums/DeltaEnums.sol";
/**
* @title Token transfer contract - should work across all EVMs - user Uniswap style Permit2
*/
contract Transfers is AssetTransfers {
function _transfers(uint256 currentOffset, address callerAddress) internal returns (uint256) {
uint256 transferOperation;
assembly {
let firstSlice := calldataload(currentOffset)
transferOperation := shr(248, firstSlice)
currentOffset := add(currentOffset, 1)
}
if (transferOperation == TransferIds.TRANSFER_FROM) {
return _transferFrom(currentOffset, callerAddress);
} else if (transferOperation == TransferIds.SWEEP) {
return _sweep(currentOffset);
} else if (transferOperation == TransferIds.UNWRAP_WNATIVE) {
return _unwrap(currentOffset);
} else if (transferOperation == TransferIds.PERMIT2_TRANSFER_FROM) {
return _permit2TransferFrom(currentOffset, callerAddress);
} else if (transferOperation == TransferIds.APPROVE) {
return _approve(currentOffset);
} else {
_invalidOperation();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
/**
* @title Raw error data holder contract
*/
abstract contract DeltaErrors {
////////////////////////////////////////////////////
// Error data
////////////////////////////////////////////////////
// the compiler should drop these since they are unused
// but it should still be included in the ABI to parse the
// errors below
error Slippage();
error NativeTransferFailed();
error WrapFailed();
error InvalidDex();
error BadPool();
error InvalidFlashLoan();
error InvalidOperation();
error InvalidCaller();
error InvalidInitiator();
error InvalidCalldata();
error Target();
error InvalidDexId();
// Slippage()
bytes4 internal constant SLIPPAGE = 0x7dd37f70;
// NativeTransferFailed()
bytes4 internal constant NATIVE_TRANSFER = 0xf4b3b1bc;
// WrapFailed()
bytes4 internal constant WRAP = 0xc30d93ce;
// InvalidDex()
bytes4 internal constant INVALID_DEX = 0x7948739e;
// BadPool()
bytes4 internal constant BAD_POOL = 0xb2c02722;
// InvalidFlashLoan()
bytes4 internal constant INVALID_FLASH_LOAN = 0xbafe1c53;
// InvalidOperation()
bytes4 internal constant INVALID_OPERATION = 0x398d4d32;
// InvalidCaller()
bytes4 internal constant INVALID_CALLER = 0x48f5c3ed;
// InvalidInitiator()
bytes4 internal constant INVALID_INITIATOR = 0xbfda1f28;
// InvalidCalldata()
bytes4 internal constant INVALID_CALLDATA = 0x8129bbcd;
// Target()
bytes4 internal constant INVALID_TARGET = 0x4fe6f55f;
// InvalidDexId()
bytes4 internal constant INVALID_DEX_ID = 0x0bbef348;
function _invalidOperation() internal pure {
assembly {
mstore(0, INVALID_OPERATION)
revert(0, 0x4)
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.28;
/// @notice logs a dead log without any content
abstract contract DeadLogger {
function _deadLog() internal {
assembly {
log0(0x0, 0x0)
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
abstract contract Masks {
/// @dev Mask of lower 20 bytes.
uint256 internal constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff;
/// @dev Mask of lower 1 byte.
uint256 internal constant UINT8_MASK = 0xff;
/// @dev Mask of lower 2 bytes.
uint256 internal constant UINT16_MASK = 0xffff;
/// @dev Mask of lower 3 bytes.
uint256 internal constant UINT24_MASK = 0xffffff;
/// @dev Mask of lower 4 bytes.
uint256 internal constant UINT32_MASK = 0xffffffff;
/// @dev Mask of lower 16 bytes.
uint256 internal constant UINT128_MASK = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
/// @dev Mask of lower 15 bytes.
uint256 internal constant UINT120_MASK = 0x0000000000000000000000000000000000ffffffffffffffffffffffffffffff;
/// @dev MIN_SQRT_RATIO + 1 from Uniswap's TickMath
uint160 internal constant MIN_SQRT_RATIO = 4295128740;
/// @dev MAX_SQRT_RATIO - 1 from Uniswap's TickMath
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970341;
/// @dev Maximum Uint256 value
uint256 internal constant MAX_UINT256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
/// @dev Use this to distinguish FF upper bytes addresses and lower bytes addresses
uint256 internal constant FF_ADDRESS_COMPLEMENT = 0x000000000000000000000000000000000000000000ffffffffffffffffffffff;
/// @dev We use uint112-encoded amounts to typically fit one bit flag, one path length (uint16)
/// add 2 amounts (2xuint112) into 32bytes, as such we use this mask for extracting those
uint256 internal constant UINT112_MASK = 0x000000000000000000000000000000000000ffffffffffffffffffffffffffff;
/// @dev Mask for Is Native
uint256 internal constant NATIVE_FLAG = 1 << 127;
/// @dev Mask for shares
uint256 internal constant USE_SHARES_FLAG = 1 << 126;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
/// @title PermitConstants
/// @notice A contract containing constants used for Permit2 & ERC20 type permits
abstract contract PermitConstants {
/// @dev default Permit2 address
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; // solhint-disable-line var-name-mixedcase
bytes32 internal constant ERC20_PERMIT = 0xd505accf00000000000000000000000000000000000000000000000000000000;
bytes32 internal constant DAI_PERMIT = 0x8fcbaf0c00000000000000000000000000000000000000000000000000000000;
bytes32 internal constant PERMIT2_PERMIT = 0x2b67b57000000000000000000000000000000000000000000000000000000000;
bytes32 internal constant CREDIT_PERMIT = 0x0b52d55800000000000000000000000000000000000000000000000000000000;
bytes32 internal constant COMPOUND_V3_CREDIT_PERMIT = 0xbb24d99400000000000000000000000000000000000000000000000000000000;
bytes32 internal constant MORPHO_CREDIT_PERMIT = 0x8069218f00000000000000000000000000000000000000000000000000000000;
bytes32 internal constant PERMIT2_TRANSFER_FROM = 0x36c7851600000000000000000000000000000000000000000000000000000000;
bytes4 internal constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector
// bitmap padding
uint256 internal constant HIGH_BIT = 1 << 255;
uint256 internal constant SECOND_HIGH_BIT = 1 << 254;
uint256 internal constant LOWER_BITS = 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {PermitConstants} from "./PermitConstants.sol";
// solhint-disable max-line-length
/// @title PermitUtils
/// @notice A contract containing utilities for Permits
abstract contract PermitUtils is PermitConstants {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error SafePermitBadLength();
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor() {}
/**
* @notice The function attempts to call the permit function on a given ERC20 token.
* @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, and IPermit2.
* It accommodates both Compact and Full formats of these permit types.
* Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
* for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
* gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
* zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
* before invocation for optimized performance.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
* @param token The address of the ERC20 token on which to call the permit function.
* @param permitOffset The off-chain permit data, containing different fields depending on the type of permit function.
* @param permitLength Length of the permit calldata.
*/
function _tryPermit(address token, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
assembly {
// solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
let success
// Switch case for different permit lengths, indicating different permit standards
switch permitLength
// Compact IERC20Permit
case 100 {
mstore(ptr, ERC20_PERMIT) // store selector
mstore(add(ptr, 0x04), callerAddress) // store owner
mstore(add(ptr, 0x24), address()) // store spender
// Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
{
// stack too deep
let deadline := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63
calldatacopy(add(ptr, 0x44), permitOffset, 0x20) // store value = copy permitOffset 0x00..0x19
mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline = deadline - 1
mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xa4), add(permitOffset, 0x24), 0x20) // store r = copy permitOffset 0x24..0x43
mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
// Compact IDaiLikePermit
case 72 {
mstore(ptr, DAI_PERMIT) // store selector
mstore(add(ptr, 0x04), callerAddress) // store owner
mstore(add(ptr, 0x24), address()) // store spender
// Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
{
// stack too deep
let expiry := shr(224, calldataload(add(permitOffset, 0x04))) // loads permitOffset 0x04..0x07
let vs := calldataload(add(permitOffset, 0x28)) // loads permitOffset 0x28..0x47
mstore(add(ptr, 0x44), shr(224, calldataload(permitOffset))) // store nonce = copy permitOffset 0x00..0x03
mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry = expiry - 1
mstore(add(ptr, 0x84), true) // store allowed = true
mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xc4), add(permitOffset, 0x08), 0x20) // store r = copy permitOffset 0x08..0x27
mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
// Compact IPermit2
case 96 {
// Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
mstore(ptr, PERMIT2_PERMIT) // store selector
mstore(add(ptr, 0x04), callerAddress) // store owner
mstore(add(ptr, 0x24), token) // store token
calldatacopy(add(ptr, 0x50), permitOffset, 0x14) // store amount = copy permitOffset 0x00..0x13
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permitOffset, 0x14))), 1))) // store expiration = ((permitOffset 0x14..0x17 - 1) & 0xffffffffffff)
mstore(add(ptr, 0x84), shr(224, calldataload(add(permitOffset, 0x18)))) // store nonce = copy permitOffset 0x18..0x1b
mstore(add(ptr, 0xa4), address()) // store spender
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permitOffset, 0x1c))), 1))) // store sigDeadline = ((permitOffset 0x1c..0x1f - 1) & 0xffffffffffff)
mstore(add(ptr, 0xe4), 0x100) // store offset = 256
mstore(add(ptr, 0x104), 65) // store length = 64
let vs := calldataload(add(permitOffset, 0x40)) // copy permitOffset 0x40..0x5f
calldatacopy(add(ptr, 0x124), add(permitOffset, 0x20), 0x20) // store r = copy permitOffset 0x20..0x3f
mstore(add(ptr, 0x144), shr(1, shl(1, vs))) // store s = vs without most significant bit
mstore8(add(ptr, 0x164), add(27, shr(255, vs))) // store v = copy permitOffset 0x40..0x5f
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), PERMIT2, 0, ptr, 0x165, 0, 0)
}
// Unknown
default {
mstore(ptr, _PERMIT_LENGTH_ERROR)
revert(ptr, 4)
}
// revert if not successful
if iszero(success) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
/**
* Executes credit delegation on given tokens / lenders
* Note that for lenders like Aave V3, the token needs to
* be the respective debt token and NOT the underlying
* Others like compound will not use it at all.
* @param token asset to permit / delegate
* @param permitOffset calldata
*/
function _tryCreditPermit(address token, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
assembly {
let ptr := mload(0x40)
switch permitLength
// Compact ICreditPermit
case 100 {
mstore(ptr, CREDIT_PERMIT) // store selector
mstore(add(ptr, 0x04), callerAddress) // store owner
mstore(add(ptr, 0x24), address()) // store spender
// Compact ICreditPermit.delegationWithSig(uint256 value, uint32 deadline, uint256 r, uint256 vs)
{
// stack too deep
let deadline := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63
calldatacopy(add(ptr, 0x44), permitOffset, 0x20) // store value = copy permitOffset 0x00..0x19
mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline = deadline - 1
mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xa4), add(permitOffset, 0x24), 0x20) // store r = copy permitOffset 0x24..0x43
mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// ICreditPermit.delegationWithSig(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
if iszero(call(gas(), token, 0, ptr, 0xe4, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
// Unknown
default {
mstore(ptr, _PERMIT_LENGTH_ERROR)
revert(ptr, 4)
}
}
}
/**
* Executes compound or morpho permit.
* @param target target address to permit / delegate
* @param permitOffset calldata
* @param permitLength calldata
*/
function _tryFlagBasedLendingPermit(address target, uint256 permitOffset, uint256 permitLength, address callerAddress) internal {
assembly {
let ptr := mload(0x40)
switch permitLength
// Compact ICreditPermit
case 100 {
let allowedAndNonce := calldataload(permitOffset) // load [allowed nonce] 2 single bits and number
// mopho blue and CompoundV3 are similarly parametrized
// if the second high bit is set, use Morpho
switch and(SECOND_HIGH_BIT, allowedAndNonce)
case 0 { mstore(ptr, COMPOUND_V3_CREDIT_PERMIT) }
// store selector
default { mstore(ptr, MORPHO_CREDIT_PERMIT) }
mstore(add(ptr, 0x04), callerAddress) // store owner
mstore(add(ptr, 0x24), address()) // store manager
// Compact ICreditPermit.allowBySig(uint256 isAllowedAndNonce, uint32 expiry, uint256 r, uint256 vs)
{
// stack too deep
let expiry := shr(224, calldataload(add(permitOffset, 0x20))) // loads permitOffset 0x20..0x23
let vs := calldataload(add(permitOffset, 0x44)) // loads permitOffset 0x44..0x63
// check if high bit is pupulated
mstore(add(ptr, 0x44), iszero(iszero(and(HIGH_BIT, allowedAndNonce))))
mstore(add(ptr, 0x64), and(LOWER_BITS, allowedAndNonce)) // nonce
mstore(add(ptr, 0x84), sub(expiry, 1)) // store expiry = expiry - 1
mstore(add(ptr, 0xA4), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xC4), add(permitOffset, 0x24), 0x20) // store r = copy permitOffset 0x24..0x43
mstore(add(ptr, 0xE4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// ICreditPermit.allowBySig(address owner, address manager, bool isAllowed, uint256 value, uint256 expiry, uint8 v, bytes32 r, bytes32 s)
if iszero(call(gas(), target, 0, ptr, 0x104, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
// Unknown
default {
mstore(ptr, _PERMIT_LENGTH_ERROR)
revert(ptr, 4)
}
}
}
/// @notice transferERC20from version using permit2
function _transferFromPermit2(address token, address to, uint256 amount, address callerAddress) internal {
assembly {
let ptr := mload(0x40)
////////////////////////////////////////////////////
// transferFrom through permit2
////////////////////////////////////////////////////
mstore(ptr, PERMIT2_TRANSFER_FROM)
mstore(add(ptr, 0x04), callerAddress)
mstore(add(ptr, 0x24), to)
mstore(add(ptr, 0x44), amount)
mstore(add(ptr, 0x64), token)
if iszero(call(gas(), PERMIT2, 0, ptr, 0x84, 0x0, 0x0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
abstract contract ERC20Selectors {
////////////////////////////////////////////////////
// ERC20 selectors
////////////////////////////////////////////////////
/// @dev selector for approve(address,uint256)
bytes32 internal constant ERC20_APPROVE = 0x095ea7b300000000000000000000000000000000000000000000000000000000;
/// @dev selector for transferFrom(address,address,uint256)
bytes32 internal constant ERC20_TRANSFER_FROM = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
/// @dev selector for transfer(address,uint256)
bytes32 internal constant ERC20_TRANSFER = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;
/// @dev selector for allowance(address,address)
bytes32 internal constant ERC20_ALLOWANCE = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000;
/// @dev selector for balanceOf(address)
bytes32 internal constant ERC20_BALANCE_OF = 0x70a0823100000000000000000000000000000000000000000000000000000000;
}{
"optimizer": {
"enabled": true,
"runs": 1000000
},
"evmVersion": "shanghai",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"BadPool","type":"error"},{"inputs":[],"name":"BridgeFailed","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[{"internalType":"uint16","name":"assetId","type":"uint16"}],"name":"InvalidAssetId","type":"error"},{"inputs":[],"name":"InvalidCalldata","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidDex","type":"error"},{"inputs":[],"name":"InvalidDexId","type":"error"},{"inputs":[],"name":"InvalidFlashLoan","type":"error"},{"inputs":[],"name":"InvalidInitiator","type":"error"},{"inputs":[],"name":"InvalidOperation","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"SafePermitBadLength","type":"error"},{"inputs":[],"name":"Slippage","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"SlippageTooHigh","type":"error"},{"inputs":[],"name":"Target","type":"error"},{"inputs":[],"name":"WrapFailed","type":"error"},{"inputs":[],"name":"ZeroBalance","type":"error"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"deltaCompose","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onMorphoSupplyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6080604052348015600e575f5ffd5b50615b8e8061001c5f395ff3fe60806040526004361061005d575f3560e01c80632075be03116100425780632075be03146100aa57806331f57072146100aa578063b1022fdf146100aa57610064565b806305b4591c146100aa57806317d73091146100cb57610064565b3661006457005b34801561006f575f5ffd5b507fffffffff000000000000000000000000000000000000000000000000000000005f351661009d816100de565b6100a68161025d565b5f5ffd5b3480156100b5575f5ffd5b506100c96100c4366004615ad1565b6103f6565b005b6100c96100d9366004615b19565b610403565b5f5f5f837ffa461e3300000000000000000000000000000000000000000000000000000000810361016e577fff203e8740894c8955cb8950759876d7e7e45e04c1000000000000000000000092507fe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb93506024355f81136001811461016757600435935061016b565b8193505b50505b508115610257575f5f5f60983560601c905060ac358060601c604051878152601581018285105f81146101a9578582528360208301526101b3565b8382528560208301525b506affffffffffffffffffffff891680156101d3576040822082526101e7565b8460481c61ffff1660408301526060822082525b506020018990526055812073ffffffffffffffffffffffffffffffffffffffff163314610236577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508060381c61ffff1693505060843560601c91506100c98482848661041a565b50505050565b5f8060cc3581847fef2e17a40000000000000000000000000000000000000000000000000000000081016102db578260581c60ff1691507fff72d111b4d6f31b38919ae39779f570b747d6acd9000000000000000000000093507fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c630394505b5082156103ef575f806004353014610315577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b60b83560601c8460601c6040518183105f811461033a57826014830152838252610344565b8360148301528282525b505f92506082861160018114610362576028600c8301209350610375565b60bf871160348301536029600c83012093505b50878152826015820152886035820152336055822073ffffffffffffffffffffffffffffffffffffffff1618156103ce577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050508360481c61ffff16915060a43560601c90506100c98160e3846104d5565b5050505050565b6103fe6104e0565b505050565b602435610412336044836104d5565b6103fe61059a565b60028110156104cd576040515f82801561046e577fa9059cbb0000000000000000000000000000000000000000000000000000000083523360048401528660248401526020836044855f8a5af191506104b0565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528460048401523360248401528660448401526020836064855f8a5af191505b503d6001835114601f82111681151782166100c957805f5f3e805ffd5b6102578260c5835b6103fe83838361059f565b5f5f6064358060581c60ff165f811461051b577fbafe1c53000000000000000000000000000000000000000000000000000000005f5260045ffd5b3373d50f2dfffd62f94ee4aed9ca05c61d0753268abc1461055e577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060601c9150507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb60443501610596826079836104d5565b5050565b5f5fa0565b8181015b60018301923560f81c605081101561061057601081036105ce576105c784866106ad565b9350610660565b602081036105df576105c784610761565b603081036105f1576105c78585610812565b60408103610603576105c784866109a7565b61060b610a1d565b610660565b60508103610622576105c78486610a45565b60608103610634576105c78486610add565b60708103610646576105c78486610aef565b60808103610658576105c78486610b31565b610660610a1d565b81841061066d5750610673565b506105a3565b80831115610257576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60348201915f908035608081901c916fffffffffffffffffffffffffffffffff909116906020013560601c82610713577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f845afa505f5192505b61071f83828789610bb6565b509650925082821115610754577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b8593505050505b92915050565b60268101905f90803560601c90601481013560801c906024013560f01c6f800000000000000000000000000000008216801561079f574792506107b4565b6effffffffffffffffffffffffffffff831692505b506040517f6a0c90ff00000000000000000000000000000000000000000000000000000000815260206004820152816024820152818660448301375f5f836044018386885af1610806573d5f5f3e3d5ffd5b50939093019392505050565b60038101905f903560f881901c9060e81c61ffff168161088b576103e88110156108485761083f84610c4a565b9250505061075b565b6107d081101561085b5761083f84610d1b565b610bb881101561086e5761083f84610de1565b610fa08110156108815761083f84610ea1565b61083f848661105c565b600182036108d9576107d08110156108a75761083f848661118e565b610bb88110156108bb5761083f84866112f1565b610fa08110156108cf5761083f8486611388565b61083f8486611485565b60028203610925576107d08110156108f55761083f8486611579565b610bb88110156109085761083f8461172a565b610fa081101561091b5761083f84611882565b61083f8486611ae3565b60038203610973576107d08110156109415761083f8486611d8f565b610bb88110156109555761083f8486611ecc565b610fa08110156109695761083f8486612007565b61083f84866121fc565b600482036109855761083f8486612329565b600582036109975761083f8486612495565b61099f610a1d565b505092915050565b60018201915f903560f81c806109c9576109c184846125df565b91505061075b565b600181036109da576109c1846126a1565b600381036109eb576109c184612844565b600481036109fd576109c18484612995565b60058103610a0e576109c184612a47565b610a16610a1d565b5092915050565b7f398d4d32000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f5f5f5f5f86358060f81c94508060581c73ffffffffffffffffffffffffffffffffffffffff1693508060481c61ffff1692505060178701905081810196505f8403610aa357610a9783828489612b01565b8694505050505061075b565b60018403610ab757610a9783828489612d8c565b60028403610acb57610a9783828489612e61565b610ad3610a1d565b5050505092915050565b5f610ae88383612fd7565b9392505050565b60018201915f903560f81c80610b0f57610b0884612ff1565b9350610b29565b60018103610b2157610b0884846130f9565b610b29610a1d565b509192915050565b60018201915f903560f81c6004811015610b945780610b54576109c184846131bb565b60018103610b65576109c184613265565b60038103610b76576109c1846132e9565b60028103610b87576109c18461333f565b610b8f610a1d565b610a16565b60048103610ba5576109c18461338b565b60058103610a0e576109c1846133ff565b60028101905f90819081903560f881901c9060f01c60ff16818303610c2557805f03610c0b5760288601958035606090811c9450601490910135901c610c008a8a868b858c6134b0565b97509550610c3b9050565b610c1889828a8a8a613671565b9097509095509250610c3b565b610c3289838a8a8a613717565b90975090955092505b50849250509450945094915050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610cbd577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f617ba0370000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b509495945050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610d8e577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517fe8eda9df0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610e54577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550835f8114610f67576effffffffffffffffffffffffffffff841680610f19577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa50505f515b6040517f23323e030000000000000000000000000000000000000000000000000000000081528460048201528160248201525f5f6044835f885af1610f60573d5f5f3e3d5ffd5b5050610d10565b6effffffffffffffffffffffffffffff841680610f815750475b7f1249c58b000000000000000000000000000000000000000000000000000000005f525f5f60045f84875af1610fb9573d5f5f3e3d5ffd5b50308314610d10577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa505f516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528460048201528160248201526020816044835f885af191503d6001825114601f821116811517831661104e57805f5f3e805ffd5b505050509495945050505050565b5f604051806101000190507f238d6579000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c806024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806effffffffffffffffffffffffffffff16905080611118577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa50505f515b60a4830152506070840135606090811c60c483015261010060e4830152609a850194608481013590911c906098013560f01c80801561116c57606086901b6101248501526014018187610138860137958101955b806101048501525f5f6101248301865f875af1610d10573d9150815f5f3e815ffd5b5f823560601c601484013560801c60248501358060601c8160581c60ff169150603987013560601c604d88019750836effffffffffffffffffffffffffffff169350604051835f8114611236577fa415bcad0000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301525f60648301528860848301525f5f60a4845f875af1611231573d5f5f3e3d5ffd5b611286565b7f1d5d72370000000000000000000000000000000000000000000000000000000082528660048301528560248301525f60448301528860648301525f5f6084845f875af1611286573d5f5f3e3d5ffd5b503083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000081528260048201526020816044835f8a5af194503d93506001815114601f85111684151785169450846112e457835f5f3e835ffd5b5096979650505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f264413180000000000000000000000000000000000000000000000000000000085528660048601528160248601528360448601528260648601525f5f6084875f855af161137c573d5f5f3e3d5ffd5b50959695505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f856e5bb30000000000000000000000000000000000000000000000000000000085528660048601528260248601525f856044875f855af1611407573d5f5f3e3d5ffd5b50308114610d1057828015611475577fa9059cbb0000000000000000000000000000000000000000000000000000000085528160048601528260248601526020856044875f885af13d6001875114601f821116811517821691508161146e57805f5f3e805ffd5b505061137c565b5f5f5f5f86865af161137c575f5ffd5b5f6040517f50d8cd4b000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c6084830152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f8114611527575f60a48501528160c4850152611534565b8160a48501525f60c48501525b50505060e481018390526070840135606090811c610104830152609885019460840135901c5f806101248482855af1611570573d805f5f3e805ffd5b50929392505050565b5f823560601c601484013560801c60248501358060601c8160581c60ff169150826effffffffffffffffffffffffffffff169250825f81146115cf576dffffffffffffffffffffffffffff811461160957611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f52306004526020600460245f885afa5060045193508660045260205f60245f60398c013560601c5afa505f5184811015611663578094505b505b506040516061880197604d013560601c908380156116d2577f573ade810000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301528360648301525f5f6084845f875af16116cd573d5f5f3e3d5ffd5b61171c565b7f5ceae9c40000000000000000000000000000000000000000000000000000000082528660048301528560248301528360448301525f5f6064845f875af161171c573d5f5f3e3d5ffd5b509798975050505050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff169250825f8114611784576dffffffffffffffffffffffffffff81146117be57611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5193507f374c49b4000000000000000000000000000000000000000000000000000000005f528260045260205f60245f855afa505f5180851115611832578094505b505b506040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550604051845f81146119fc576effffffffffffffffffffffffffffff85168080156118e4576dffffffffffffffffffffffffffff811461191e576119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191506119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f5110156119b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507f2608f8180000000000000000000000000000000000000000000000000000000083526004830185905260248301525f8260448183875af16119f7573d5f5f3e3d5ffd5b61137c565b6effffffffffffffffffffffffffffff8516808015611a2f576dffffffffffffffffffffffffffff8114611a3757611a99565b479150611a99565b4791507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f511015611a99577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507fe5974619000000000000000000000000000000000000000000000000000000005f52846004525f5f60245f84885af1611ad6573d5f5f3e3d5ffd5b5050959695505050505050565b5f6040516101008101843560601c806004830152601486013560601c6024830152602886013560601c6044830152603c86013560601c6064830152605086013593508360801c6084830152836effffffffffffffffffffffffffffff16607087013560601c608488013560601c826dffffffffffffffffffffffffffff8114611ba9578015611cd8576f4000000000000000000000000000000088168015611b96575f60a48801528460c4880152611ba3565b8460a48801525f60c48801525b50611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611be1575f5ffd5b5f51935063151c1ade601c8703525f5f60a4885f865af1611c00575f5ffd5b60a060048701207f5c60e39a000000000000000000000000000000000000000000000000000000005f528060045260808860245f865afa611c3f575f5ffd5b604088015160608901517f93c52062000000000000000000000000000000000000000000000000000000008a528260048b01528560248b015260408a60448c885afa611c89575f5ffd5b60208a01519250620f4240810160018381018502909201620f423f010490878211908114611cc2575f60a48b01528360c48b0152611ccf565b8760a48b01525f60c48b01525b50505050611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611d10575f5ffd5b5f5160a48701525f60c48701525b5060e48501829052610120610104860152609a8901986098013560f01c95508593508315611d63576014840193508760601b6101448601528589610158870137978501975b6320b76e81601c860352836101248601525f5f6101448601875f855af16112e4573d9550855f5f3e855ffd5b5f604051833560601c601485013560801c602486013560601c816effffffffffffffffffffffffffffff169150603887013560601c604c88019750826dffffffffffffffffffffffffffff8103611e16577f70a08231000000000000000000000000000000000000000000000000000000005f528760045260205f60245f855afa505f5193505b507f23b872dd00000000000000000000000000000000000000000000000000000000855286600486015230602486015282604486015260205f6064875f855af190503d60015f5114601f8211168115178216915081611e7757805f5f3e805ffd5b50507f69328dec00000000000000000000000000000000000000000000000000000000845260048401929092526024830152604482015260148401933560601c5f8060648482855af1611570573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c60248601358060601c603988013560601c604d89019850836effffffffffffffffffffffffffffff1693506dffffffffffffffffffffffffffff8403611fb7578260581c60ff165f8114611f65577f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f855afa505f519450611fb5565b7f2b92a07d000000000000000000000000000000000000000000000000000000008752886004880152856024880152602087604489855afa5086516fffffffffffffffffffffffffffffffff1694505b505b7f264413180000000000000000000000000000000000000000000000000000000086528760048701528160248701528460448701528360648701525f5f6084885f855af16112e4573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692506dffffffffffffffffffffffffffff830361208e577f3af9e669000000000000000000000000000000000000000000000000000000005f528660045260205f60245f5f855af1505f5192505b7fbd6d894d000000000000000000000000000000000000000000000000000000005f5260205f60245f5f855af1505f51600181670de0b6b3a7640000860204017f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f865afa505f5191508181111561210e5750805b7f23b872dd0000000000000000000000000000000000000000000000000000000087528860048801523060248801528060448801526020876064895f875af191508161215c573d5f5f3e3d5ffd5b7fdb006a75000000000000000000000000000000000000000000000000000000005f5280600452505f5f60245f5f865af1612199573d5f5f3e3d5ffd5b3083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000086528260048701528360248701526020866044885f895af190503d91506001865114601f83111682151781169050806112e457815f5f3e815ffd5b5f6040517f8720316d000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c60848301528360c4830152607085013560601c60e4830152608485013560601c816effffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff82036122fa57610118830160a060048501207f93c5206200000000000000000000000000000000000000000000000000000000825280600483015250856024820152606081604483855afa6122f3575f5ffd5b6040015191505b8160a48401526098860195505f5f610104855f855af161231f573d9150815f5f3e815ffd5b5093949350505050565b5f604051806080019050833560601c7fa99aad89000000000000000000000000000000000000000000000000000000008252806004830152601485013560601c6024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f81146123d2575f60a48601528160c486015261241a565b8161240d577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5191505b8160a48601525f60c48601525b50505050607084013560601c608485013560601c609886013560f01c61ffff1680609a880197508360e4860152610120610104860152805f181561247357606087901b6101448601526014018188610158870137968101965b806101248601525f5f6101448301875f875af161137c573d9350835f5f3e835ffd5b5f6040516101008101843560601c6004820152601485013560601c6024820152602885013560601c6044820152603c85013560601c606482015260508501358060801c6084830152806effffffffffffffffffffffffffffff168560e4840152607087013560601c610104840152608487013560601c609888019750826f40000000000000000000000000000000165f811461253c575f60a48601528260c48601526125bb565b826dffffffffffffffffffffffffffff8114612563578360a48701525f60c48701526125b9565b60a060048701207f93c5206200000000000000000000000000000000000000000000000000000000885280600489015250886024880152602087604489865afa6125ab575f5ffd5b5f60a4870152865160c48701525b505b50635c2bea49601c8503525f5f610124865f855af161137c573d9450845f5f3e845ffd5b5f823560601c601484013560601c602885013560801c8061262f577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201526020816064835f885af193503d92506001815114601f841116831517841661269157825f5f3e825ffd5b5050506038939093019392505050565b5f813560601c601483013560601c60198401358060801c60ff16816fffffffffffffffffffffffffffffffff1691505f84155f811461275c578280156126e95784925061271c565b4792508483101561271c577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612757575f5f5f5f85895af1612757577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b612833565b82801561276b578492506127d0565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8a5afa505f519250848310156127d0577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612833576040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528560048201528260248201526020816044835f8b5af13d6001835114601f821116811517821661282f57805f5f3e805ffd5b5050505b505050603994909401949350505050565b5f8135606090811c906014840135901c60198401356effffffffffffffffffffffffffffff81169060801c60ff1684818015612882578391506128e7565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa505f519150838210156128e7577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612985577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52806004525f5f60245f5f895af161294a577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308414612985575f5f5f5f84885af1612985577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050506039939093019392505050565b5f823560601c601484013560601c602885013560801c806129e5577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f36c785160000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201528360648201525f5f6084835f6e22d473030f116ddee9f6b43ac78ba35af1612691573d5f5f3e3d5ffd5b5f813560601c601483013560601c815f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054612af3576040517f095ea7b30000000000000000000000000000000000000000000000000000000081528260048201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526020816044835f885af15050600181555b505050602891909101919050565b6040515f8360648114612b465760488114612be25760608114612c88577f68275857000000000000000000000000000000000000000000000000000000008352600483fd5b7fd505accf000000000000000000000000000000000000000000000000000000008352836004840152306024840152602086013560e01c604487013560208860448701376001820360648601528060ff1c601b01608486015260206024890160a48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4850152505f8060e485828b5af19150612d76565b7f8fcbaf0c000000000000000000000000000000000000000000000000000000008352836004840152306024840152600486013560e01c6028870135873560e01c6044860152600182036064860152600160848601528060ff1c601b0160a486015260206008890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f8061010485828b5af19150612d76565b7f2b67b57000000000000000000000000000000000000000000000000000000000835283600484015286602484015260148660508501376001601487013560e01c0365ffffffffffff166064840152601886013560e01c60848401523060a48401526001601c87013560e01c0365ffffffffffff1660c484015261010060e48401526041610104840152604086013560208088016101248601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116610144850152601b60ff82901c01610164850153505f5f610165855f6e22d473030f116ddee9f6b43ac78ba35af191505b5080612d84573d5f5f3e3d5ffd5b505050505050565b6040518260648114612dc0577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b7f0b52d558000000000000000000000000000000000000000000000000000000008252826004830152306024830152602085013560e01c604486013560208760448601376001820360648501528060ff1c601b01608485015260206024880160a48601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4840152505f8060e484828a5af1612d84573d5f5f3e3d5ffd5b6040518260648114612e95577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b84357f400000000000000000000000000000000000000000000000000000000000000081168015612ee8577f8069218f000000000000000000000000000000000000000000000000000000008452612f0c565b7fbb24d9940000000000000000000000000000000000000000000000000000000084525b50836004840152306024840152602086013560e01c6044870135827f80000000000000000000000000000000000000000000000000000000000000001615156044860152827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660648601526001820360848601528060ff1c601b0160a486015260206024890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f90508061010484828a5af1612d84573d5f5f3e3d5ffd5b60018201915f903560f81c80610a0e576109c1848461374e565b60405160388201915f91908035606090811c91601481013590911c906028013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613067577f94bf804d0000000000000000000000000000000000000000000000000000000086526130c5565b7f6e553f65000000000000000000000000000000000000000000000000000000008652816130c5577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5191505b50600485015250843560601c60248401525f8060448582855af16130eb573d5f5f3e3d5ffd5b505050601491909101919050565b60405160248301925f9190803560601c906014013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613164577fba087652000000000000000000000000000000000000000000000000000000008552613188565b7fb460af940000000000000000000000000000000000000000000000000000000085525b50600484015250843560601c6024830152604482018490526014909401935f8060648482855af1611570573d5f5f3e3d5ffd5b5f82358060501c61ffff168160601c91506040517f48c89491000000000000000000000000000000000000000000000000000000008152602060048201526058820160248201527f480cf7ef000000000000000000000000000000000000000000000000000000006044820152602060488201526014820160688201528460601b60888201526016860195508186609c8301375f5f609c8401835f875af1610806573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517f0b0d9c090000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b5050506010939093019392505050565b7fa5841194000000000000000000000000000000000000000000000000000000005f9081526014820135606090811c600452602883019235901c818060248180855af1613338573d5f5f3e3d5ffd5b5090919050565b7f11da60b4000000000000000000000000000000000000000000000000000000005f9081526024820191803560601c906014013560801c828060048184865af1610b29573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517fae6393290000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b60288101905f908035606090811c9160140135901c833560801c7fffffffffffffffffffffffffffffffff00000000000000000000000000000001810161346357507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b8460100194506040517f15afd4090000000000000000000000000000000000000000000000000000000081528260048201528160248201525f5f6044835f885af161231f573d5f5f3e3d5ffd5b60018101905f9081903560f81c603f811161353f57806134e2576134d889898988888b6137ec565b9250925050613666565b600281036134f8576134d889898988888b61391c565b6001810361350e576134d889898988888b613c7f565b60058103613524576134d889898988888b613f3a565b6003810361353a576134d889898988888b614099565b61363e565b607f81116135b4576040810361355d576134d888888b888a896142ce565b60418103613572576134d8888a87898861470f565b60428103613588576134d888888b888a896148a2565b6050810361359e576134d8898989888a89614a0f565b605a810361353a576134d8898989888a89614b5b565b60bf811161362857608081036135d2576134d888888b888a89614c9d565b608181036135e8576134d889898988888b614e7e565b608c81036135fd576134d889898789886150a8565b60968103613613576134d8898989888a8961520a565b60a0810361353a576134d8898987898861541c565b60fe810361363e576134d888888b888a8961557e565b7f7948739e000000000000000000000000000000000000000000000000000000005f5260045ffd5b965096945050505050565b5f5f5f865f036136935761368788878787610bb6565b91995094509050613709565b600287028401933560801c5f808a5b5f8b8314600181146136c85761ffff8e87601087026070031c61ffff16020491506136cc565b8291505b506001830192505f6136e0828d8d8d610bb6565b909b5097509485019490508c8411156136fa575050613702565b5090036136a2565b5090995050505b969792969550919350505050565b845f84815b61372884838888610bb6565b909650909450915080881461373f5760010161371c565b50839150955095509592505050565b5f823560601c601484013560601c60288501358060801c8160701c61ffff169150603a870196506040517fe0232b42000000000000000000000000000000000000000000000000000000008152846004820152816024820152606060448201528260140160648201528660601b6084820152828860988301375f5f60988501835f885af16137de573d5f5f3e3d5ffd5b505094909401949350505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a107f128acb080000000000000000000000000000000000000000000000000000000084528860048501528060248501528b604485015260a06084850152604182018060a48601528760601b60c48601528b60601b60d88601528a60601b60ec86015260058301896101008701378180156138ac576401000276a460648701526040868360e401885f895af16138a0573d5f5f3e3d5ffd5b602086015197506138e3565b73fffd8963efd1fc6a506488495d951d5263988d2560648701526020868360e401885f895af16138de573d5f5f3e3d5ffd5b855197505b505050845f0394506002811060018114613903579681016005019661390a565b6005880197505b50869350505050965096945050505050565b5f5f60405160148501358060281c60ff1692508060181c61ffff167ff3cd914c0000000000000000000000000000000000000000000000000000000083528160481c62ffffff1660448401528160301c62ffffff166064840152863560601c8060848501528260601c9250610120610104850152816101248501528b5f0360c4850152603188019750815f18156139b65781886101448601375b898b1095508560a485015285600181146139f5578a60048601528b602486015273fffd8963efd1fc6a506488495d951d5263988d2560e4860152613a0d565b8b60048601528a60248601526401000276a460e48601525b5060205f8361014401865f875af1613a27573d5f5f3e3d5ffd5b801560018114613a9e577f9bf6645f0000000000000000000000000000000000000000000000000000000085526020600486015260026024860152305f528b60205260405f2060448601528a60205260405f206064860152608085608487875afa5060408501515f039c5060608501519650613adc565b5f519c508660018114613abd57600f8e900b5f039d60801c9750613ada565b60808e901d5f039d6fffffffffffffffffffffffffffffffff1697505b505b5050507f0b0d9c090000000000000000000000000000000000000000000000000000000082528760048301528660248301528360448301525f5f6064845f855af1613b29573d5f5f3e3d5ffd5b6002831015613c6f5788158015613b42578a9350613c36565b7fa5841194000000000000000000000000000000000000000000000000000000005f52896004525f5f60245f5f865af1613b7e573d5f5f3e3d5ffd5b838015613b925760018114613bd857613c14565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af19450613c14565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528260048501528b602485015260205f6044865f8f5af194505b503d60015f5114601f8211168115178516613c3157805f5f3e805ffd5b505f93505b507f11da60b4000000000000000000000000000000000000000000000000000000005f525f5f60045f86855af1613c6f573d5f5f3e3d5ffd5b5084915050965096945050505050565b5f5f60405184358060381c61ffff16925087891060808260481c60ff161060018114613cf8578260601c92507ff140a35a0000000000000000000000000000000000000000000000000000000084528b60048501528a602485015260205f604486865afa613cef573d5f5f3e3d5ffd5b5f519550613d73565b61ffff8360501c168360601c93507f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f875afa613d3e573d5f5f3e3d5ffd5b5f8360018114613d565760205191505f519850613d60565b60205198505f5191505b5061271002908d02908101960295909504945b507f022c0d9f000000000000000000000000000000000000000000000000000000008352808015613daf575f6004850152856024850152613dbc565b8560048501525f60248501525b505086604483015260806064830152600383105f8114613ed957838015613dea5760018114613e5357613eb2565b60c4840194507f23b872dd0000000000000000000000000000000000000000000000000000000085528660048601528260248601528b604486015260205f6064875f8f5af194503d60015f5114601f8211168115178616613e4d57805f5f3e805ffd5b50613eb2565b60c4840194507fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af194503d60015f5114601f8211168115178616613eb057805f5f3e805ffd5b505b505f60848401525f5f60a4855f865af1613ece573d5f5f3e3d5ffd5b601987019350613f2c565b603f840160848401528560601b60a48401528960601b60b88401528860601b60cc840152866016019650600384019350838760e08501375f5f8560e001855f865af1613f27573d5f5f3e3d5ffd5b928601925b505050965096945050505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a105f8114613ff4577f857f812f0000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff5f03604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376040858260c401875f885af1613fe7573d5f5f3e3d5ffd5b5060208401519550614080565b7f2c4812520000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376020858260c401875f885af161407a573d5f5f3e3d5ffd5b50835195505b506002811060018114613903579681016005019661390a565b5f5f604051843561ffff8160501c168160381c61ffff165f81146140c3576001811461412b575f5ffd5b8260601c92507f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af13d60015f5114601f821116811517821661412457805f5f3e805ffd5b5050614190565b8260601c92507f36c785160000000000000000000000000000000000000000000000000000000084528660048501528260248501528b60448501528a60648501525f5f6084865f6e22d473030f116ddee9f6b43ac78ba35af1614190573d5f5f3e3d5ffd5b50888a107f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f865afa6141cc573d5f5f3e3d5ffd5b60403d10156141d9575f5ffd5b5f81600181146141f15760205191505f5197506141fb565b5f51915060205197505b507f70a08231000000000000000000000000000000000000000000000000000000005f528360045260205f60245f8f5afa505f517f022c0d9f0000000000000000000000000000000000000000000000000000000086528190039c5061271002918c029182019582029590950494808015614281575f600486015286602486015261428e565b8660048601525f60248601525b5050876044840152608060648401525f60848401525f5f60a4855f865af16142b8573d5f5f3e3d5ffd5b5092996019959095019850939650505050505050565b5f8135816142de858a8985615893565b905060405160ff8360501c1660248201528760448201525f60648201525f60ff8460481c165f811461434e576001811461439757600281146143fa576003811461444357600481146144a657600581146144ef5760068114614552576007811461459b5760c881146145fe575f5ffd5b7fddc1f59d00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f3df0212400000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fa64833a000000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f5b41b90800000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f44ee198600000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7fa6417ed600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fe2ad025a00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f65b2489b00000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f9169558600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608484015260208360a4855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b5080614691573d5f5f3e3d5ffd5b815194503088188416156146fb577fa9059cbb00000000000000000000000000000000000000000000000000000000825287600483015284602483015260205f6044845f8e5af190503d60015f5114601f82111681151782169150816146f957805f5f3e805ffd5b505b505060198401915050965096945050505050565b5f5f604051833591508160601c8260381c61ffff16935060028410156147e6575f848015614744576001811461478a576147c6565b7f23b872dd00000000000000000000000000000000000000000000000000000000845287600485015282602485015289604485015260205f6064865f8f5af191506147c6565b7fa9059cbb00000000000000000000000000000000000000000000000000000000845282600485015289602485015260205f6044865f8f5af191505b503d60015f5114601f82111681151782166147e357805f5f3e805ffd5b50505b60ff8360481c165f81146148005760028114614828575f5ffd5b7fafb4301200000000000000000000000000000000000000000000000000000000835261484c565b7f767691e70000000000000000000000000000000000000000000000000000000083525b5060ff8360581c16600483015260ff8360501c1660248301528760448301525f606483015286608483015260205f60a4845f855af161488d573d5f5f3e3d5ffd5b50505f51915050601982019550959350505050565b5f8135816148b2858a8985615893565b90506040517f70a08231000000000000000000000000000000000000000000000000000000005f523060045260208160245f8c5afa508051935060ff8360481c1660038114614907576005811461492f575f5ffd5b7f5b41b908000000000000000000000000000000000000000000000000000000008252614953565b7f65b2489b0000000000000000000000000000000000000000000000000000000082525b5060ff8360581c16600482015260ff8360501c1660248201528760448201525f60648201525f5f6084835f865af161498d573d5f5f3e3d5ffd5b60205f60245f8c5afa50835f51039350308718156149fd577fa9059cbb0000000000000000000000000000000000000000000000000000000081528660048201528360248201526020816044835f8d5af13d6001835114601f82111681151782169150816146f957805f5f3e805ffd5b50505060198201965096945050505050565b5f5f604051833592508260581c60ff1691508260601c92506002821060018103614aea575f838015614a485760018114614a8e57614aca565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528560248501528b604485015260205f6064865f8f5af19150614aca565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528560048501528b602485015260205f6044865f8f5af191505b503d60015f5114601f8211168115178216614ae757805f5f3e805ffd5b50505b507f7dc203820000000000000000000000000000000000000000000000000000000081528760048201528660248201528860448201525f60648201528560848201525f60a482015260208160c4835f875af1614b48573d5f5f3e3d5ffd5b5191505060158201965096945050505050565b5f5f60405183358060601c8160581c60ff165f8114614b815760018114614be357614c3b565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528160248501528b604485015260205f6064865f8f5af13d60015f5114601f8211168115178216614bdc57805f5f3e805ffd5b5050614c3b565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af13d60015f5114601f8211168115178216614c3857805f5f3e805ffd5b50505b507f933162120000000000000000000000000000000000000000000000000000000083528960048401528860248401528760448401526020836064855f855af1614c87573d5f5f3e3d5ffd5b5050519860159390930197509195505050505050565b5f5f826020013590506040518160581c60ff16614d12577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f8211168115178216614d0f57805f5f3e805ffd5b50505b8160601c895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054614db0577f095ea7b30000000000000000000000000000000000000000000000000000000083528160048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f52bbbe2900000000000000000000000000000000000000000000000000000000825260e060048301523060248301525f60448301528660648301525f60848301525f60a48301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c4830152843560e48301525f61010483015289610124830152886101448301528761016483015260c06101848301525f6101a483015260205f6101c4845f855af1614e68573d5f5f3e3d5ffd5b50505f5191505060358201965096945050505050565b5f5f604051843560601c6084820152843560601c60148601955085356017870196508060581c60ff1693508060481c61ffff168160601c91507f2bfb780c0000000000000000000000000000000000000000000000000000000084525f60048501528260248501528a60448501528960648501528b6084850152600160a485015260e060c48501528060e4850152805f1815614f21578088610104860137968701965b6060848261010401865f865af1614f3a573d5f5f3e3d5ffd5b5060208301519a50604083015194507fae6393290000000000000000000000000000000000000000000000000000000083528860048401528760248401528460448401525f5f6064855f855af1614f93573d5f5f3e3d5ffd5b6002841015615097575f9150838015614fb35760018114614ff957615035565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528160248501528b604485015260205f6064865f8f5af19250615035565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af192505b503d60015f5114601f821116811517831661505257805f5f3e805ffd5b507f15afd4090000000000000000000000000000000000000000000000000000000083528960048401528a60248401525f5f6044855f855af1615097573d5f5f3e3d5ffd5b508592505050965096945050505050565b5f5f60405183358060601c8160501c60ff169350600284101561517c575f8480156150da57600181146151205761515c565b7f23b872dd0000000000000000000000000000000000000000000000000000000085528860048601528260248601528b604486015260205f6064875f8f5af1915061515c565b7fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af191505b503d60015f5114601f821116811517821661517957805f5f3e805ffd5b50505b8160581c60ff1691507f53c059a00000000000000000000000000000000000000000000000000000000083528160048401528760248401526020836044855f855af16151ca573d5f5f3e3d5ffd5b508080156151de57825160801c94506151f5565b6fffffffffffffffffffffffffffffffff83511694505b50929860169490940197509295505050505050565b5f5f5f615219898987876159ab565b60405192955093509150600383106001811461538b5761010082017fd0a494e40000000000000000000000000000000000000000000000000000000081528b60248401528560581c60ff165f81146152d9577f66410a210000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa6152a9573d5f5f3e3d5ffd5b5f5196508660048301525f60248301528860601b60a48301528a60601b60b88301528b60601b60cc830152615343565b7f79a048760000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa615317573d5f5f3e3d5ffd5b5f5196505f60048301528660248301528860601b60a48301528b60601b60b88301528a60601b60cc8301525b5088604482015260806064820152846040016084820152600485018760150160e08301375f5f8660e401835f885af161537e573d5f5f3e3d5ffd5b509483016019019461540b565b8460581c60ff165f81146153c1577fdd93f59a000000000000000000000000000000000000000000000000000000005f526153e5565b7fbd6015b4000000000000000000000000000000000000000000000000000000005f525b508760045260205f60245f5f875af1615400573d5f5f3e3d5ffd5b5f5194508560190195505b508492505050965096945050505050565b5f5f82356040518160581c60ff1692508160601c915060028310156154f2575f8380156154505760018114615496576154d2565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528760048401528360248401528a604484015260205f6064855f8e5af191506154d2565b7fa9059cbb0000000000000000000000000000000000000000000000000000000083528360048401528a602484015260205f6044855f8e5af191505b503d60015f5114601f82111681151782166154ef57805f5f3e805ffd5b50505b7f7132bb7f000000000000000000000000000000000000000000000000000000008152608060048201525f60248201525f60448201526101006064820152606060848201528760a48201528660c48201525f60e48201525f610104820152604081610124835f865af1615567573d5f5f3e3d5ffd5b602001519860159490940197509295505050505050565b5f5f8235905060405160ff8260f01c166155f0577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f82111681151782166155ed57805f5f3e805ffd5b50505b8160f81c5f8114615657576001811461579c577fba08765200000000000000000000000000000000000000000000000000000000825287600483015286602483015230604483015260205f6064845f8e5af161564e573d5f5f3e3d5ffd5b5f519350615881565b879350891560018114615700577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52886004525f5f60245f5f8f5af16156c0577fc30d93ce000000000000000000000000000000000000000000000000000000005f5260045ffd5b3088146156fb575f5f5f5f8c8c5af16156fb577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b615796565b5f5f5f5f8c8e5af1615734577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308814615796577fa9059cbb00000000000000000000000000000000000000000000000000000000835287600484015288602484015260205f6044855f8e5af13d60015f5114601f821116811517821691508161579357805f5f3e805ffd5b50505b50615881565b895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052885f5260405f208054615836577f095ea7b30000000000000000000000000000000000000000000000000000000083528960048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f6e553f6500000000000000000000000000000000000000000000000000000000825287600483015286602483015260205f6044845f8d5af161587c573d5f5f3e3d5ffd5b5f5193505b50505060028201965096945050505050565b5f6040518260601c9150845f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052815f5260405f208054615937577f095ea7b30000000000000000000000000000000000000000000000000000000082528260048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301526020826044845f8a5af150600181555b508260381c61ffff166159a2577f23b872dd00000000000000000000000000000000000000000000000000000000815285600482015230602482015283604482015260205f6064835f895af13d60015f5114601f821116811517821661599f57805f5f3e805ffd5b50505b50949350505050565b604051813590606082901c9061ffff603884901c16906002821015615a81575f8280156159df5760018114615a2557615a61565b7f23b872dd00000000000000000000000000000000000000000000000000000000835287600484015284602484015289604484015260205f6064855f8d5af19150615a61565b7fa9059cbb00000000000000000000000000000000000000000000000000000000835284600484015289602484015260205f6044855f8d5af191505b503d60015f5114601f8211168115178216615a7e57805f5f3e805ffd5b50505b509450945094915050565b5f5f83601f840112615a9c575f5ffd5b50813567ffffffffffffffff811115615ab3575f5ffd5b602083019150836020828501011115615aca575f5ffd5b9250929050565b5f5f5f60408486031215615ae3575f5ffd5b83359250602084013567ffffffffffffffff811115615b00575f5ffd5b615b0c86828701615a8c565b9497909650939450505050565b5f5f60208385031215615b2a575f5ffd5b823567ffffffffffffffff811115615b40575f5ffd5b615b4c85828601615a8c565b9096909550935050505056fea2646970667358221220be71ab4bfdc67325f0fe02c45071abb89f031a89e6d0733865dd66f4cbd088c264736f6c634300081c0033
Deployed Bytecode
0x60806040526004361061005d575f3560e01c80632075be03116100425780632075be03146100aa57806331f57072146100aa578063b1022fdf146100aa57610064565b806305b4591c146100aa57806317d73091146100cb57610064565b3661006457005b34801561006f575f5ffd5b507fffffffff000000000000000000000000000000000000000000000000000000005f351661009d816100de565b6100a68161025d565b5f5ffd5b3480156100b5575f5ffd5b506100c96100c4366004615ad1565b6103f6565b005b6100c96100d9366004615b19565b610403565b5f5f5f837ffa461e3300000000000000000000000000000000000000000000000000000000810361016e577fff203e8740894c8955cb8950759876d7e7e45e04c1000000000000000000000092507fe040f12c7cee3904b78f24f8fc395629c2e69525c2815da7a659f7483e378ecb93506024355f81136001811461016757600435935061016b565b8193505b50505b508115610257575f5f5f60983560601c905060ac358060601c604051878152601581018285105f81146101a9578582528360208301526101b3565b8382528560208301525b506affffffffffffffffffffff891680156101d3576040822082526101e7565b8460481c61ffff1660408301526060822082525b506020018990526055812073ffffffffffffffffffffffffffffffffffffffff163314610236577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508060381c61ffff1693505060843560601c91506100c98482848661041a565b50505050565b5f8060cc3581847fef2e17a40000000000000000000000000000000000000000000000000000000081016102db578260581c60ff1691507fff72d111b4d6f31b38919ae39779f570b747d6acd9000000000000000000000093507fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c630394505b5082156103ef575f806004353014610315577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b60b83560601c8460601c6040518183105f811461033a57826014830152838252610344565b8360148301528282525b505f92506082861160018114610362576028600c8301209350610375565b60bf871160348301536029600c83012093505b50878152826015820152886035820152336055822073ffffffffffffffffffffffffffffffffffffffff1618156103ce577fb2c02722000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050508360481c61ffff16915060a43560601c90506100c98160e3846104d5565b5050505050565b6103fe6104e0565b505050565b602435610412336044836104d5565b6103fe61059a565b60028110156104cd576040515f82801561046e577fa9059cbb0000000000000000000000000000000000000000000000000000000083523360048401528660248401526020836044855f8a5af191506104b0565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528460048401523360248401528660448401526020836064855f8a5af191505b503d6001835114601f82111681151782166100c957805f5f3e805ffd5b6102578260c5835b6103fe83838361059f565b5f5f6064358060581c60ff165f811461051b577fbafe1c53000000000000000000000000000000000000000000000000000000005f5260045ffd5b3373d50f2dfffd62f94ee4aed9ca05c61d0753268abc1461055e577f48f5c3ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060601c9150507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb60443501610596826079836104d5565b5050565b5f5fa0565b8181015b60018301923560f81c605081101561061057601081036105ce576105c784866106ad565b9350610660565b602081036105df576105c784610761565b603081036105f1576105c78585610812565b60408103610603576105c784866109a7565b61060b610a1d565b610660565b60508103610622576105c78486610a45565b60608103610634576105c78486610add565b60708103610646576105c78486610aef565b60808103610658576105c78486610b31565b610660610a1d565b81841061066d5750610673565b506105a3565b80831115610257576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60348201915f908035608081901c916fffffffffffffffffffffffffffffffff909116906020013560601c82610713577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f845afa505f5192505b61071f83828789610bb6565b509650925082821115610754577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b8593505050505b92915050565b60268101905f90803560601c90601481013560801c906024013560f01c6f800000000000000000000000000000008216801561079f574792506107b4565b6effffffffffffffffffffffffffffff831692505b506040517f6a0c90ff00000000000000000000000000000000000000000000000000000000815260206004820152816024820152818660448301375f5f836044018386885af1610806573d5f5f3e3d5ffd5b50939093019392505050565b60038101905f903560f881901c9060e81c61ffff168161088b576103e88110156108485761083f84610c4a565b9250505061075b565b6107d081101561085b5761083f84610d1b565b610bb881101561086e5761083f84610de1565b610fa08110156108815761083f84610ea1565b61083f848661105c565b600182036108d9576107d08110156108a75761083f848661118e565b610bb88110156108bb5761083f84866112f1565b610fa08110156108cf5761083f8486611388565b61083f8486611485565b60028203610925576107d08110156108f55761083f8486611579565b610bb88110156109085761083f8461172a565b610fa081101561091b5761083f84611882565b61083f8486611ae3565b60038203610973576107d08110156109415761083f8486611d8f565b610bb88110156109555761083f8486611ecc565b610fa08110156109695761083f8486612007565b61083f84866121fc565b600482036109855761083f8486612329565b600582036109975761083f8486612495565b61099f610a1d565b505092915050565b60018201915f903560f81c806109c9576109c184846125df565b91505061075b565b600181036109da576109c1846126a1565b600381036109eb576109c184612844565b600481036109fd576109c18484612995565b60058103610a0e576109c184612a47565b610a16610a1d565b5092915050565b7f398d4d32000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f5f5f5f5f86358060f81c94508060581c73ffffffffffffffffffffffffffffffffffffffff1693508060481c61ffff1692505060178701905081810196505f8403610aa357610a9783828489612b01565b8694505050505061075b565b60018403610ab757610a9783828489612d8c565b60028403610acb57610a9783828489612e61565b610ad3610a1d565b5050505092915050565b5f610ae88383612fd7565b9392505050565b60018201915f903560f81c80610b0f57610b0884612ff1565b9350610b29565b60018103610b2157610b0884846130f9565b610b29610a1d565b509192915050565b60018201915f903560f81c6004811015610b945780610b54576109c184846131bb565b60018103610b65576109c184613265565b60038103610b76576109c1846132e9565b60028103610b87576109c18461333f565b610b8f610a1d565b610a16565b60048103610ba5576109c18461338b565b60058103610a0e576109c1846133ff565b60028101905f90819081903560f881901c9060f01c60ff16818303610c2557805f03610c0b5760288601958035606090811c9450601490910135901c610c008a8a868b858c6134b0565b97509550610c3b9050565b610c1889828a8a8a613671565b9097509095509250610c3b565b610c3289838a8a8a613717565b90975090955092505b50849250509450945094915050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610cbd577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f617ba0370000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b509495945050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610d8e577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517fe8eda9df0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201525f60648201525f5f6084835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff16925082610e54577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5192505b6040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550835f8114610f67576effffffffffffffffffffffffffffff841680610f19577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa50505f515b6040517f23323e030000000000000000000000000000000000000000000000000000000081528460048201528160248201525f5f6044835f885af1610f60573d5f5f3e3d5ffd5b5050610d10565b6effffffffffffffffffffffffffffff841680610f815750475b7f1249c58b000000000000000000000000000000000000000000000000000000005f525f5f60045f84875af1610fb9573d5f5f3e3d5ffd5b50308314610d10577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa505f516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528460048201528160248201526020816044835f885af191503d6001825114601f821116811517831661104e57805f5f3e805ffd5b505050509495945050505050565b5f604051806101000190507f238d6579000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c806024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806effffffffffffffffffffffffffffff16905080611118577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f855afa50505f515b60a4830152506070840135606090811c60c483015261010060e4830152609a850194608481013590911c906098013560f01c80801561116c57606086901b6101248501526014018187610138860137958101955b806101048501525f5f6101248301865f875af1610d10573d9150815f5f3e815ffd5b5f823560601c601484013560801c60248501358060601c8160581c60ff169150603987013560601c604d88019750836effffffffffffffffffffffffffffff169350604051835f8114611236577fa415bcad0000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301525f60648301528860848301525f5f60a4845f875af1611231573d5f5f3e3d5ffd5b611286565b7f1d5d72370000000000000000000000000000000000000000000000000000000082528660048301528560248301525f60448301528860648301525f5f6084845f875af1611286573d5f5f3e3d5ffd5b503083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000081528260048201526020816044835f8a5af194503d93506001815114601f85111684151785169450846112e457835f5f3e835ffd5b5096979650505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f264413180000000000000000000000000000000000000000000000000000000085528660048601528160248601528360448601528260648601525f5f6084875f855af161137c573d5f5f3e3d5ffd5b50959695505050505050565b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692507f856e5bb30000000000000000000000000000000000000000000000000000000085528660048601528260248601525f856044875f855af1611407573d5f5f3e3d5ffd5b50308114610d1057828015611475577fa9059cbb0000000000000000000000000000000000000000000000000000000085528160048601528260248601526020856044875f885af13d6001875114601f821116811517821691508161146e57805f5f3e805ffd5b505061137c565b5f5f5f5f86865af161137c575f5ffd5b5f6040517f50d8cd4b000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c6084830152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f8114611527575f60a48501528160c4850152611534565b8160a48501525f60c48501525b50505060e481018390526070840135606090811c610104830152609885019460840135901c5f806101248482855af1611570573d805f5f3e805ffd5b50929392505050565b5f823560601c601484013560801c60248501358060601c8160581c60ff169150826effffffffffffffffffffffffffffff169250825f81146115cf576dffffffffffffffffffffffffffff811461160957611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611665565b7f70a08231000000000000000000000000000000000000000000000000000000005f52306004526020600460245f885afa5060045193508660045260205f60245f60398c013560601c5afa505f5184811015611663578094505b505b506040516061880197604d013560601c908380156116d2577f573ade810000000000000000000000000000000000000000000000000000000082528660048301528560248301528460448301528360648301525f5f6084845f875af16116cd573d5f5f3e3d5ffd5b61171c565b7f5ceae9c40000000000000000000000000000000000000000000000000000000082528660048301528560248301528360448301525f5f6064845f875af161171c573d5f5f3e3d5ffd5b509798975050505050505050565b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550826effffffffffffffffffffffffffffff169250825f8114611784576dffffffffffffffffffffffffffff81146117be57611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f519350611834565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5193507f374c49b4000000000000000000000000000000000000000000000000000000005f528260045260205f60245f855afa505f5180851115611832578094505b505b506040517f4232cd630000000000000000000000000000000000000000000000000000000081528260048201528460248201528360448201525f5f6064835f865af1610d10573d5f5f3e3d5ffd5b5f813560601c601483013560801c602484013560601c603885013560601c604c86019550604051845f81146119fc576effffffffffffffffffffffffffffff85168080156118e4576dffffffffffffffffffffffffffff811461191e576119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191506119b2565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8b5afa505f5191507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f5110156119b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507f2608f8180000000000000000000000000000000000000000000000000000000083526004830185905260248301525f8260448183875af16119f7573d5f5f3e3d5ffd5b61137c565b6effffffffffffffffffffffffffffff8516808015611a2f576dffffffffffffffffffffffffffff8114611a3757611a99565b479150611a99565b4791507f17bfdfbc000000000000000000000000000000000000000000000000000000005f528560045260205f60245f5f895af150815f511015611a99577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91505b507fe5974619000000000000000000000000000000000000000000000000000000005f52846004525f5f60245f84885af1611ad6573d5f5f3e3d5ffd5b5050959695505050505050565b5f6040516101008101843560601c806004830152601486013560601c6024830152602886013560601c6044830152603c86013560601c6064830152605086013593508360801c6084830152836effffffffffffffffffffffffffffff16607087013560601c608488013560601c826dffffffffffffffffffffffffffff8114611ba9578015611cd8576f4000000000000000000000000000000088168015611b96575f60a48801528460c4880152611ba3565b8460a48801525f60c48801525b50611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611be1575f5ffd5b5f51935063151c1ade601c8703525f5f60a4885f865af1611c00575f5ffd5b60a060048701207f5c60e39a000000000000000000000000000000000000000000000000000000005f528060045260808860245f865afa611c3f575f5ffd5b604088015160608901517f93c52062000000000000000000000000000000000000000000000000000000008a528260048b01528560248b015260408a60448c885afa611c89575f5ffd5b60208a01519250620f4240810160018381018502909201620f423f010490878211908114611cc2575f60a48b01528360c48b0152611ccf565b8760a48b01525f60c48b01525b50505050611d1e565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa611d10575f5ffd5b5f5160a48701525f60c48701525b5060e48501829052610120610104860152609a8901986098013560f01c95508593508315611d63576014840193508760601b6101448601528589610158870137978501975b6320b76e81601c860352836101248601525f5f6101448601875f855af16112e4573d9550855f5f3e855ffd5b5f604051833560601c601485013560801c602486013560601c816effffffffffffffffffffffffffffff169150603887013560601c604c88019750826dffffffffffffffffffffffffffff8103611e16577f70a08231000000000000000000000000000000000000000000000000000000005f528760045260205f60245f855afa505f5193505b507f23b872dd00000000000000000000000000000000000000000000000000000000855286600486015230602486015282604486015260205f6064875f855af190503d60015f5114601f8211168115178216915081611e7757805f5f3e805ffd5b50507f69328dec00000000000000000000000000000000000000000000000000000000845260048401929092526024830152604482015260148401933560601c5f8060648482855af1611570573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c60248601358060601c603988013560601c604d89019850836effffffffffffffffffffffffffffff1693506dffffffffffffffffffffffffffff8403611fb7578260581c60ff165f8114611f65577f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f855afa505f519450611fb5565b7f2b92a07d000000000000000000000000000000000000000000000000000000008752886004880152856024880152602087604489855afa5086516fffffffffffffffffffffffffffffffff1694505b505b7f264413180000000000000000000000000000000000000000000000000000000086528760048701528160248701528460448701528360648701525f5f6084885f855af16112e4573d5f5f3e3d5ffd5b5f604051833560601c601485013560801c602486013560601c603887013560601c604c88019750826effffffffffffffffffffffffffffff1692506dffffffffffffffffffffffffffff830361208e577f3af9e669000000000000000000000000000000000000000000000000000000005f528660045260205f60245f5f855af1505f5192505b7fbd6d894d000000000000000000000000000000000000000000000000000000005f5260205f60245f5f855af1505f51600181670de0b6b3a7640000860204017f70a08231000000000000000000000000000000000000000000000000000000005f528860045260205f60245f865afa505f5191508181111561210e5750805b7f23b872dd0000000000000000000000000000000000000000000000000000000087528860048801523060248801528060448801526020876064895f875af191508161215c573d5f5f3e3d5ffd5b7fdb006a75000000000000000000000000000000000000000000000000000000005f5280600452505f5f60245f5f865af1612199573d5f5f3e3d5ffd5b3083146112e4577fa9059cbb0000000000000000000000000000000000000000000000000000000086528260048701528360248701526020866044885f895af190503d91506001865114601f83111682151781169050806112e457815f5f3e815ffd5b5f6040517f8720316d000000000000000000000000000000000000000000000000000000008152833560601c6004820152601484013560601c6024820152602884013560601c6044820152603c84013560601c606482015260508401358060801c60848301528360c4830152607085013560601c60e4830152608485013560601c816effffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff82036122fa57610118830160a060048501207f93c5206200000000000000000000000000000000000000000000000000000000825280600483015250856024820152606081604483855afa6122f3575f5ffd5b6040015191505b8160a48401526098860195505f5f610104855f855af161231f573d9150815f5f3e815ffd5b5093949350505050565b5f604051806080019050833560601c7fa99aad89000000000000000000000000000000000000000000000000000000008252806004830152601485013560601c6024830152602885013560601c6044830152603c85013560601c606483015260508501358060801c6084840152806dffffffffffffffffffffffffffff16816f40000000000000000000000000000000165f81146123d2575f60a48601528160c486015261241a565b8161240d577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f875afa505f5191505b8160a48601525f60c48601525b50505050607084013560601c608485013560601c609886013560f01c61ffff1680609a880197508360e4860152610120610104860152805f181561247357606087901b6101448601526014018188610158870137968101965b806101248601525f5f6101448301875f875af161137c573d9350835f5f3e835ffd5b5f6040516101008101843560601c6004820152601485013560601c6024820152602885013560601c6044820152603c85013560601c606482015260508501358060801c6084830152806effffffffffffffffffffffffffffff168560e4840152607087013560601c610104840152608487013560601c609888019750826f40000000000000000000000000000000165f811461253c575f60a48601528260c48601526125bb565b826dffffffffffffffffffffffffffff8114612563578360a48701525f60c48701526125b9565b60a060048701207f93c5206200000000000000000000000000000000000000000000000000000000885280600489015250886024880152602087604489865afa6125ab575f5ffd5b5f60a4870152865160c48701525b505b50635c2bea49601c8503525f5f610124865f855af161137c573d9450845f5f3e845ffd5b5f823560601c601484013560601c602885013560801c8061262f577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201526020816064835f885af193503d92506001815114601f841116831517841661269157825f5f3e825ffd5b5050506038939093019392505050565b5f813560601c601483013560601c60198401358060801c60ff16816fffffffffffffffffffffffffffffffff1691505f84155f811461275c578280156126e95784925061271c565b4792508483101561271c577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612757575f5f5f5f85895af1612757577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b612833565b82801561276b578492506127d0565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f8a5afa505f519250848310156127d0577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508115612833576040517fa9059cbb0000000000000000000000000000000000000000000000000000000081528560048201528260248201526020816044835f8b5af13d6001835114601f821116811517821661282f57805f5f3e805ffd5b5050505b505050603994909401949350505050565b5f8135606090811c906014840135901c60198401356effffffffffffffffffffffffffffff81169060801c60ff1684818015612882578391506128e7565b7f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f895afa505f519150838210156128e7577f7dd37f70000000000000000000000000000000000000000000000000000000005f5260045ffd5b508015612985577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52806004525f5f60245f5f895af161294a577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308414612985575f5f5f5f84885af1612985577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b5050506039939093019392505050565b5f823560601c601484013560601c602885013560801c806129e5577f70a08231000000000000000000000000000000000000000000000000000000005f528460045260205f60245f865afa50505f515b6040517f36c785160000000000000000000000000000000000000000000000000000000081528560048201528260248201528160448201528360648201525f5f6084835f6e22d473030f116ddee9f6b43ac78ba35af1612691573d5f5f3e3d5ffd5b5f813560601c601483013560601c815f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054612af3576040517f095ea7b30000000000000000000000000000000000000000000000000000000081528260048201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201526020816044835f885af15050600181555b505050602891909101919050565b6040515f8360648114612b465760488114612be25760608114612c88577f68275857000000000000000000000000000000000000000000000000000000008352600483fd5b7fd505accf000000000000000000000000000000000000000000000000000000008352836004840152306024840152602086013560e01c604487013560208860448701376001820360648601528060ff1c601b01608486015260206024890160a48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4850152505f8060e485828b5af19150612d76565b7f8fcbaf0c000000000000000000000000000000000000000000000000000000008352836004840152306024840152600486013560e01c6028870135873560e01c6044860152600182036064860152600160848601528060ff1c601b0160a486015260206008890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f8061010485828b5af19150612d76565b7f2b67b57000000000000000000000000000000000000000000000000000000000835283600484015286602484015260148660508501376001601487013560e01c0365ffffffffffff166064840152601886013560e01c60848401523060a48401526001601c87013560e01c0365ffffffffffff1660c484015261010060e48401526041610104840152604086013560208088016101248601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116610144850152601b60ff82901c01610164850153505f5f610165855f6e22d473030f116ddee9f6b43ac78ba35af191505b5080612d84573d5f5f3e3d5ffd5b505050505050565b6040518260648114612dc0577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b7f0b52d558000000000000000000000000000000000000000000000000000000008252826004830152306024830152602085013560e01c604486013560208760448601376001820360648501528060ff1c601b01608485015260206024880160a48601377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660c4840152505f8060e484828a5af1612d84573d5f5f3e3d5ffd5b6040518260648114612e95577f68275857000000000000000000000000000000000000000000000000000000008252600482fd5b84357f400000000000000000000000000000000000000000000000000000000000000081168015612ee8577f8069218f000000000000000000000000000000000000000000000000000000008452612f0c565b7fbb24d9940000000000000000000000000000000000000000000000000000000084525b50836004840152306024840152602086013560e01c6044870135827f80000000000000000000000000000000000000000000000000000000000000001615156044860152827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660648601526001820360848601528060ff1c601b0160a486015260206024890160c48701377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e4850152505f90508061010484828a5af1612d84573d5f5f3e3d5ffd5b60018201915f903560f81c80610a0e576109c1848461374e565b60405160388201915f91908035606090811c91601481013590911c906028013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613067577f94bf804d0000000000000000000000000000000000000000000000000000000086526130c5565b7f6e553f65000000000000000000000000000000000000000000000000000000008652816130c5577f70a08231000000000000000000000000000000000000000000000000000000005f523060045260205f60245f885afa505f5191505b50600485015250843560601c60248401525f8060448582855af16130eb573d5f5f3e3d5ffd5b505050601491909101919050565b60405160248301925f9190803560601c906014013560801c6effffffffffffffffffffffffffffff81166f4000000000000000000000000000000082168015613164577fba087652000000000000000000000000000000000000000000000000000000008552613188565b7fb460af940000000000000000000000000000000000000000000000000000000085525b50600484015250843560601c6024830152604482018490526014909401935f8060648482855af1611570573d5f5f3e3d5ffd5b5f82358060501c61ffff168160601c91506040517f48c89491000000000000000000000000000000000000000000000000000000008152602060048201526058820160248201527f480cf7ef000000000000000000000000000000000000000000000000000000006044820152602060488201526014820160688201528460601b60888201526016860195508186609c8301375f5f609c8401835f875af1610806573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517f0b0d9c090000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b5050506010939093019392505050565b7fa5841194000000000000000000000000000000000000000000000000000000005f9081526014820135606090811c600452602883019235901c818060248180855af1613338573d5f5f3e3d5ffd5b5090919050565b7f11da60b4000000000000000000000000000000000000000000000000000000005f9081526024820191803560601c906014013560801c828060048184865af1610b29573d5f5f3e3d5ffd5b5f813560601c826014019250823560601c836014019350833560601c846014019450843560801c6040517fae6393290000000000000000000000000000000000000000000000000000000081528360048201528260248201528160448201525f5f6064835f895af16132d9573d5f5f3e3d5ffd5b60288101905f908035606090811c9160140135901c833560801c7fffffffffffffffffffffffffffffffff00000000000000000000000000000001810161346357507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b8460100194506040517f15afd4090000000000000000000000000000000000000000000000000000000081528260048201528160248201525f5f6044835f885af161231f573d5f5f3e3d5ffd5b60018101905f9081903560f81c603f811161353f57806134e2576134d889898988888b6137ec565b9250925050613666565b600281036134f8576134d889898988888b61391c565b6001810361350e576134d889898988888b613c7f565b60058103613524576134d889898988888b613f3a565b6003810361353a576134d889898988888b614099565b61363e565b607f81116135b4576040810361355d576134d888888b888a896142ce565b60418103613572576134d8888a87898861470f565b60428103613588576134d888888b888a896148a2565b6050810361359e576134d8898989888a89614a0f565b605a810361353a576134d8898989888a89614b5b565b60bf811161362857608081036135d2576134d888888b888a89614c9d565b608181036135e8576134d889898988888b614e7e565b608c81036135fd576134d889898789886150a8565b60968103613613576134d8898989888a8961520a565b60a0810361353a576134d8898987898861541c565b60fe810361363e576134d888888b888a8961557e565b7f7948739e000000000000000000000000000000000000000000000000000000005f5260045ffd5b965096945050505050565b5f5f5f865f036136935761368788878787610bb6565b91995094509050613709565b600287028401933560801c5f808a5b5f8b8314600181146136c85761ffff8e87601087026070031c61ffff16020491506136cc565b8291505b506001830192505f6136e0828d8d8d610bb6565b909b5097509485019490508c8411156136fa575050613702565b5090036136a2565b5090995050505b969792969550919350505050565b845f84815b61372884838888610bb6565b909650909450915080881461373f5760010161371c565b50839150955095509592505050565b5f823560601c601484013560601c60288501358060801c8160701c61ffff169150603a870196506040517fe0232b42000000000000000000000000000000000000000000000000000000008152846004820152816024820152606060448201528260140160648201528660601b6084820152828860988301375f5f60988501835f885af16137de573d5f5f3e3d5ffd5b505094909401949350505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a107f128acb080000000000000000000000000000000000000000000000000000000084528860048501528060248501528b604485015260a06084850152604182018060a48601528760601b60c48601528b60601b60d88601528a60601b60ec86015260058301896101008701378180156138ac576401000276a460648701526040868360e401885f895af16138a0573d5f5f3e3d5ffd5b602086015197506138e3565b73fffd8963efd1fc6a506488495d951d5263988d2560648701526020868360e401885f895af16138de573d5f5f3e3d5ffd5b855197505b505050845f0394506002811060018114613903579681016005019661390a565b6005880197505b50869350505050965096945050505050565b5f5f60405160148501358060281c60ff1692508060181c61ffff167ff3cd914c0000000000000000000000000000000000000000000000000000000083528160481c62ffffff1660448401528160301c62ffffff166064840152863560601c8060848501528260601c9250610120610104850152816101248501528b5f0360c4850152603188019750815f18156139b65781886101448601375b898b1095508560a485015285600181146139f5578a60048601528b602486015273fffd8963efd1fc6a506488495d951d5263988d2560e4860152613a0d565b8b60048601528a60248601526401000276a460e48601525b5060205f8361014401865f875af1613a27573d5f5f3e3d5ffd5b801560018114613a9e577f9bf6645f0000000000000000000000000000000000000000000000000000000085526020600486015260026024860152305f528b60205260405f2060448601528a60205260405f206064860152608085608487875afa5060408501515f039c5060608501519650613adc565b5f519c508660018114613abd57600f8e900b5f039d60801c9750613ada565b60808e901d5f039d6fffffffffffffffffffffffffffffffff1697505b505b5050507f0b0d9c090000000000000000000000000000000000000000000000000000000082528760048301528660248301528360448301525f5f6064845f855af1613b29573d5f5f3e3d5ffd5b6002831015613c6f5788158015613b42578a9350613c36565b7fa5841194000000000000000000000000000000000000000000000000000000005f52896004525f5f60245f5f865af1613b7e573d5f5f3e3d5ffd5b838015613b925760018114613bd857613c14565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af19450613c14565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528260048501528b602485015260205f6044865f8f5af194505b503d60015f5114601f8211168115178516613c3157805f5f3e805ffd5b505f93505b507f11da60b4000000000000000000000000000000000000000000000000000000005f525f5f60045f86855af1613c6f573d5f5f3e3d5ffd5b5084915050965096945050505050565b5f5f60405184358060381c61ffff16925087891060808260481c60ff161060018114613cf8578260601c92507ff140a35a0000000000000000000000000000000000000000000000000000000084528b60048501528a602485015260205f604486865afa613cef573d5f5f3e3d5ffd5b5f519550613d73565b61ffff8360501c168360601c93507f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f875afa613d3e573d5f5f3e3d5ffd5b5f8360018114613d565760205191505f519850613d60565b60205198505f5191505b5061271002908d02908101960295909504945b507f022c0d9f000000000000000000000000000000000000000000000000000000008352808015613daf575f6004850152856024850152613dbc565b8560048501525f60248501525b505086604483015260806064830152600383105f8114613ed957838015613dea5760018114613e5357613eb2565b60c4840194507f23b872dd0000000000000000000000000000000000000000000000000000000085528660048601528260248601528b604486015260205f6064875f8f5af194503d60015f5114601f8211168115178616613e4d57805f5f3e805ffd5b50613eb2565b60c4840194507fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af194503d60015f5114601f8211168115178616613eb057805f5f3e805ffd5b505b505f60848401525f5f60a4855f865af1613ece573d5f5f3e3d5ffd5b601987019350613f2c565b603f840160848401528560601b60a48401528960601b60b88401528860601b60cc840152866016019650600384019350838760e08501375f5f8560e001855f865af1613f27573d5f5f3e3d5ffd5b928601925b505050965096945050505050565b5f5f60405184356014860195508060381c61ffff168160601c9150888a105f8114613ff4577f857f812f0000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff5f03604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376040858260c401875f885af1613fe7573d5f5f3e3d5ffd5b5060208401519550614080565b7f2c4812520000000000000000000000000000000000000000000000000000000084528860048501528b6024850152620c34ff604485015260806064850152604182018060848601528760601b60a48601528b60601b60b88601528a60601b60cc860152600583018960e08701376020858260c401875f885af161407a573d5f5f3e3d5ffd5b50835195505b506002811060018114613903579681016005019661390a565b5f5f604051843561ffff8160501c168160381c61ffff165f81146140c3576001811461412b575f5ffd5b8260601c92507f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528260248501528b604485015260205f6064865f8f5af13d60015f5114601f821116811517821661412457805f5f3e805ffd5b5050614190565b8260601c92507f36c785160000000000000000000000000000000000000000000000000000000084528660048501528260248501528b60448501528a60648501525f5f6084865f6e22d473030f116ddee9f6b43ac78ba35af1614190573d5f5f3e3d5ffd5b50888a107f0902f1ac000000000000000000000000000000000000000000000000000000005f5260405f60045f865afa6141cc573d5f5f3e3d5ffd5b60403d10156141d9575f5ffd5b5f81600181146141f15760205191505f5197506141fb565b5f51915060205197505b507f70a08231000000000000000000000000000000000000000000000000000000005f528360045260205f60245f8f5afa505f517f022c0d9f0000000000000000000000000000000000000000000000000000000086528190039c5061271002918c029182019582029590950494808015614281575f600486015286602486015261428e565b8660048601525f60248601525b5050876044840152608060648401525f60848401525f5f60a4855f865af16142b8573d5f5f3e3d5ffd5b5092996019959095019850939650505050505050565b5f8135816142de858a8985615893565b905060405160ff8360501c1660248201528760448201525f60648201525f60ff8460481c165f811461434e576001811461439757600281146143fa576003811461444357600481146144a657600581146144ef5760068114614552576007811461459b5760c881146145fe575f5ffd5b7fddc1f59d00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f3df0212400000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fa64833a000000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f5b41b90800000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f44ee198600000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7fa6417ed600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7fe2ad025a00000000000000000000000000000000000000000000000000000000835260ff8560581c16600484015288608484015260208360a4855f885af191505f9450614683565b7f65b2489b00000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401526020836084855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9450614683565b7f9169558600000000000000000000000000000000000000000000000000000000835260ff8560581c1660048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608484015260208360a4855f885af191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b5080614691573d5f5f3e3d5ffd5b815194503088188416156146fb577fa9059cbb00000000000000000000000000000000000000000000000000000000825287600483015284602483015260205f6044845f8e5af190503d60015f5114601f82111681151782169150816146f957805f5f3e805ffd5b505b505060198401915050965096945050505050565b5f5f604051833591508160601c8260381c61ffff16935060028410156147e6575f848015614744576001811461478a576147c6565b7f23b872dd00000000000000000000000000000000000000000000000000000000845287600485015282602485015289604485015260205f6064865f8f5af191506147c6565b7fa9059cbb00000000000000000000000000000000000000000000000000000000845282600485015289602485015260205f6044865f8f5af191505b503d60015f5114601f82111681151782166147e357805f5f3e805ffd5b50505b60ff8360481c165f81146148005760028114614828575f5ffd5b7fafb4301200000000000000000000000000000000000000000000000000000000835261484c565b7f767691e70000000000000000000000000000000000000000000000000000000083525b5060ff8360581c16600483015260ff8360501c1660248301528760448301525f606483015286608483015260205f60a4845f855af161488d573d5f5f3e3d5ffd5b50505f51915050601982019550959350505050565b5f8135816148b2858a8985615893565b90506040517f70a08231000000000000000000000000000000000000000000000000000000005f523060045260208160245f8c5afa508051935060ff8360481c1660038114614907576005811461492f575f5ffd5b7f5b41b908000000000000000000000000000000000000000000000000000000008252614953565b7f65b2489b0000000000000000000000000000000000000000000000000000000082525b5060ff8360581c16600482015260ff8360501c1660248201528760448201525f60648201525f5f6084835f865af161498d573d5f5f3e3d5ffd5b60205f60245f8c5afa50835f51039350308718156149fd577fa9059cbb0000000000000000000000000000000000000000000000000000000081528660048201528360248201526020816044835f8d5af13d6001835114601f82111681151782169150816146f957805f5f3e805ffd5b50505060198201965096945050505050565b5f5f604051833592508260581c60ff1691508260601c92506002821060018103614aea575f838015614a485760018114614a8e57614aca565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528560248501528b604485015260205f6064865f8f5af19150614aca565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528560048501528b602485015260205f6044865f8f5af191505b503d60015f5114601f8211168115178216614ae757805f5f3e805ffd5b50505b507f7dc203820000000000000000000000000000000000000000000000000000000081528760048201528660248201528860448201525f60648201528560848201525f60a482015260208160c4835f875af1614b48573d5f5f3e3d5ffd5b5191505060158201965096945050505050565b5f5f60405183358060601c8160581c60ff165f8114614b815760018114614be357614c3b565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528760048501528160248501528b604485015260205f6064865f8f5af13d60015f5114601f8211168115178216614bdc57805f5f3e805ffd5b5050614c3b565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af13d60015f5114601f8211168115178216614c3857805f5f3e805ffd5b50505b507f933162120000000000000000000000000000000000000000000000000000000083528960048401528860248401528760448401526020836064855f855af1614c87573d5f5f3e3d5ffd5b5050519860159390930197509195505050505050565b5f5f826020013590506040518160581c60ff16614d12577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f8211168115178216614d0f57805f5f3e805ffd5b50505b8160601c895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052805f5260405f208054614db0577f095ea7b30000000000000000000000000000000000000000000000000000000083528160048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f52bbbe2900000000000000000000000000000000000000000000000000000000825260e060048301523060248301525f60448301528660648301525f60848301525f60a48301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60c4830152843560e48301525f61010483015289610124830152886101448301528761016483015260c06101848301525f6101a483015260205f6101c4845f855af1614e68573d5f5f3e3d5ffd5b50505f5191505060358201965096945050505050565b5f5f604051843560601c6084820152843560601c60148601955085356017870196508060581c60ff1693508060481c61ffff168160601c91507f2bfb780c0000000000000000000000000000000000000000000000000000000084525f60048501528260248501528a60448501528960648501528b6084850152600160a485015260e060c48501528060e4850152805f1815614f21578088610104860137968701965b6060848261010401865f865af1614f3a573d5f5f3e3d5ffd5b5060208301519a50604083015194507fae6393290000000000000000000000000000000000000000000000000000000083528860048401528760248401528460448401525f5f6064855f855af1614f93573d5f5f3e3d5ffd5b6002841015615097575f9150838015614fb35760018114614ff957615035565b7f23b872dd0000000000000000000000000000000000000000000000000000000084528660048501528160248501528b604485015260205f6064865f8f5af19250615035565b7fa9059cbb0000000000000000000000000000000000000000000000000000000084528160048501528b602485015260205f6044865f8f5af192505b503d60015f5114601f821116811517831661505257805f5f3e805ffd5b507f15afd4090000000000000000000000000000000000000000000000000000000083528960048401528a60248401525f5f6044855f855af1615097573d5f5f3e3d5ffd5b508592505050965096945050505050565b5f5f60405183358060601c8160501c60ff169350600284101561517c575f8480156150da57600181146151205761515c565b7f23b872dd0000000000000000000000000000000000000000000000000000000085528860048601528260248601528b604486015260205f6064875f8f5af1915061515c565b7fa9059cbb0000000000000000000000000000000000000000000000000000000085528260048601528b602486015260205f6044875f8f5af191505b503d60015f5114601f821116811517821661517957805f5f3e805ffd5b50505b8160581c60ff1691507f53c059a00000000000000000000000000000000000000000000000000000000083528160048401528760248401526020836044855f855af16151ca573d5f5f3e3d5ffd5b508080156151de57825160801c94506151f5565b6fffffffffffffffffffffffffffffffff83511694505b50929860169490940197509295505050505050565b5f5f5f615219898987876159ab565b60405192955093509150600383106001811461538b5761010082017fd0a494e40000000000000000000000000000000000000000000000000000000081528b60248401528560581c60ff165f81146152d9577f66410a210000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa6152a9573d5f5f3e3d5ffd5b5f5196508660048301525f60248301528860601b60a48301528a60601b60b88301528b60601b60cc830152615343565b7f79a048760000000000000000000000000000000000000000000000000000000084525f600485015260205f604486885afa615317573d5f5f3e3d5ffd5b5f5196505f60048301528660248301528860601b60a48301528b60601b60b88301528a60601b60cc8301525b5088604482015260806064820152846040016084820152600485018760150160e08301375f5f8660e401835f885af161537e573d5f5f3e3d5ffd5b509483016019019461540b565b8460581c60ff165f81146153c1577fdd93f59a000000000000000000000000000000000000000000000000000000005f526153e5565b7fbd6015b4000000000000000000000000000000000000000000000000000000005f525b508760045260205f60245f5f875af1615400573d5f5f3e3d5ffd5b5f5194508560190195505b508492505050965096945050505050565b5f5f82356040518160581c60ff1692508160601c915060028310156154f2575f8380156154505760018114615496576154d2565b7f23b872dd0000000000000000000000000000000000000000000000000000000083528760048401528360248401528a604484015260205f6064855f8e5af191506154d2565b7fa9059cbb0000000000000000000000000000000000000000000000000000000083528360048401528a602484015260205f6044855f8e5af191505b503d60015f5114601f82111681151782166154ef57805f5f3e805ffd5b50505b7f7132bb7f000000000000000000000000000000000000000000000000000000008152608060048201525f60248201525f60448201526101006064820152606060848201528760a48201528660c48201525f60e48201525f610104820152604081610124835f865af1615567573d5f5f3e3d5ffd5b602001519860159490940197509295505050505050565b5f5f8235905060405160ff8260f01c166155f0577f23b872dd00000000000000000000000000000000000000000000000000000000815284600482015230602482015286604482015260205f6064835f8d5af13d60015f5114601f82111681151782166155ed57805f5f3e805ffd5b50505b8160f81c5f8114615657576001811461579c577fba08765200000000000000000000000000000000000000000000000000000000825287600483015286602483015230604483015260205f6064845f8e5af161564e573d5f5f3e3d5ffd5b5f519350615881565b879350891560018114615700577f2e1a7d4d000000000000000000000000000000000000000000000000000000005f52886004525f5f60245f5f8f5af16156c0577fc30d93ce000000000000000000000000000000000000000000000000000000005f5260045ffd5b3088146156fb575f5f5f5f8c8c5af16156fb577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b615796565b5f5f5f5f8c8e5af1615734577ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b308814615796577fa9059cbb00000000000000000000000000000000000000000000000000000000835287600484015288602484015260205f6044855f8e5af13d60015f5114601f821116811517821691508161579357805f5f3e805ffd5b50505b50615881565b895f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052885f5260405f208054615836577f095ea7b30000000000000000000000000000000000000000000000000000000083528960048401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248401526020836044855f8f5af150600181555b507f6e553f6500000000000000000000000000000000000000000000000000000000825287600483015286602483015260205f6044845f8d5af161587c573d5f5f3e3d5ffd5b5f5193505b50505060028201965096945050505050565b5f6040518260601c9150845f527f1aae13105d9b6581c36534caba5708726e5ea1e03175e823c989a5756966d1f360205260405f20602052815f5260405f208054615937577f095ea7b30000000000000000000000000000000000000000000000000000000082528260048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301526020826044845f8a5af150600181555b508260381c61ffff166159a2577f23b872dd00000000000000000000000000000000000000000000000000000000815285600482015230602482015283604482015260205f6064835f895af13d60015f5114601f821116811517821661599f57805f5f3e805ffd5b50505b50949350505050565b604051813590606082901c9061ffff603884901c16906002821015615a81575f8280156159df5760018114615a2557615a61565b7f23b872dd00000000000000000000000000000000000000000000000000000000835287600484015284602484015289604484015260205f6064855f8d5af19150615a61565b7fa9059cbb00000000000000000000000000000000000000000000000000000000835284600484015289602484015260205f6044855f8d5af191505b503d60015f5114601f8211168115178216615a7e57805f5f3e805ffd5b50505b509450945094915050565b5f5f83601f840112615a9c575f5ffd5b50813567ffffffffffffffff811115615ab3575f5ffd5b602083019150836020828501011115615aca575f5ffd5b9250929050565b5f5f5f60408486031215615ae3575f5ffd5b83359250602084013567ffffffffffffffff811115615b00575f5ffd5b615b0c86828701615a8c565b9497909650939450505050565b5f5f60208385031215615b2a575f5ffd5b823567ffffffffffffffff811115615b40575f5ffd5b615b4c85828601615a8c565b9096909550935050505056fea2646970667358221220be71ab4bfdc67325f0fe02c45071abb89f031a89e6d0733865dd66f4cbd088c264736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.