// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {Panic} from"./Panic.sol";
import {UnsafeMath} from"./UnsafeMath.sol";
libraryAddressDerivation{
usingUnsafeMathforuint256;
uint256internalconstant _SECP256K1_P =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
uint256internalconstant _SECP256K1_N =0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
uint256internalconstant SECP256K1_GX =0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
uint256internalconstant SECP256K1_GY =0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
errorInvalidCurve(uint256 x, uint256 y);
// keccak256(abi.encodePacked(ECMUL([x, y], k)))[12:]functionderiveEOA(uint256 x, uint256 y, uint256 k) internalpurereturns (address) {
if (k ==0) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
if (k >= _SECP256K1_N || x >= _SECP256K1_P || y >= _SECP256K1_P) {
Panic.panic(Panic.ARITHMETIC_OVERFLOW);
}
// +/-7 are neither square nor cube mod p, so we only have to check one// coordinate against 0. if it is 0, then the other is too (the point at// infinity) or the point is invalidif (
x ==0|| y.unsafeMulMod(y, _SECP256K1_P)
!= x.unsafeMulMod(x, _SECP256K1_P).unsafeMulMod(x, _SECP256K1_P).unsafeAddMod(7, _SECP256K1_P)
) {
revert InvalidCurve(x, y);
}
unchecked {
// https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384returnecrecover(
bytes32(0), uint8(27+ (y &1)), bytes32(x), bytes32(UnsafeMath.unsafeMulMod(x, k, _SECP256K1_N))
);
}
}
// keccak256(RLP([deployer, nonce]))[12:]functionderiveContract(address deployer, uint64 nonce) internalpurereturns (address result) {
if (nonce ==0) {
assembly ("memory-safe") {
mstore(
0x00,
or(
0xd694000000000000000000000000000000000000000080,
shl(8, and(0xffffffffffffffffffffffffffffffffffffffff, deployer))
)
)
result :=keccak256(0x09, 0x17)
}
} elseif (nonce <0x80) {
assembly ("memory-safe") {
// we don't care about dirty bits in `deployer`; they'll be overwritten latermstore(0x14, deployer)
mstore(0x00, 0xd694)
mstore8(0x34, nonce)
result :=keccak256(0x1e, 0x17)
}
} else {
// compute ceil(log_256(nonce)) + 1uint256 nonceLength =8;
unchecked {
if ((uint256(nonce) >>32) !=0) {
nonceLength +=32;
if (nonce ==type(uint64).max) {
Panic.panic(Panic.ARITHMETIC_OVERFLOW);
}
}
if ((uint256(nonce) >>8) >= (1<< nonceLength)) {
nonceLength +=16;
}
if (uint256(nonce) >= (1<< nonceLength)) {
nonceLength +=8;
}
// ceilif ((uint256(nonce) <<8) >= (1<< nonceLength)) {
nonceLength +=8;
}
// bytes, not bits
nonceLength >>=3;
}
assembly ("memory-safe") {
// we don't care about dirty bits in `deployer` or `nonce`. they'll be overwritten latermstore(nonceLength, nonce)
mstore8(0x20, add(0x7f, nonceLength))
mstore(0x00, deployer)
mstore8(0x0a, add(0xd5, nonceLength))
mstore8(0x0b, 0x94)
result :=keccak256(0x0a, add(0x16, nonceLength))
}
}
}
// keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initHash))[12:]functionderiveDeterministicContract(address deployer, bytes32 salt, bytes32 initHash)
internalpurereturns (address result)
{
assembly ("memory-safe") {
let ptr :=mload(0x40)
// we don't care about dirty bits in `deployer`; they'll be overwritten latermstore(ptr, deployer)
mstore8(add(ptr, 0x0b), 0xff)
mstore(add(ptr, 0x20), salt)
mstore(add(ptr, 0x40), initHash)
result :=keccak256(add(ptr, 0x0b), 0x55)
}
}
}
Contract Source Code
File 2 of 33: AllowanceHolderContext.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {Context} from"../Context.sol";
import {IAllowanceHolder} from"./IAllowanceHolder.sol";
abstractcontractAllowanceHolderContextisContext{
IAllowanceHolder internalconstant _ALLOWANCE_HOLDER = IAllowanceHolder(0x0000000000001fF3684f28c67538d4D072C22734);
function_isForwarded() internalviewvirtualoverridereturns (bool) {
returnsuper._isForwarded() ||super._msgSender() ==address(_ALLOWANCE_HOLDER);
}
function_msgSender() internalviewvirtualoverridereturns (address sender) {
sender =super._msgSender();
if (sender ==address(_ALLOWANCE_HOLDER)) {
// ERC-2771 like usage where the _trusted_ `AllowanceHolder` has appended the appropriate// msg.sender to the msg dataassembly ("memory-safe") {
sender :=shr(0x60, calldataload(sub(calldatasize(), 0x14)))
}
}
}
// this is here to avoid foot-guns and make it very explicit that we intend// to pass the confused deputy check in AllowanceHolderfunctionbalanceOf(address) externalpure{
assembly ("memory-safe") {
mstore8(0x00, 0x00)
revert(0x00, 0x01)
}
}
}
Contract Source Code
File 3 of 33: Basic.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {SettlerAbstract} from"../SettlerAbstract.sol";
import {InvalidOffset, ConfusedDeputy, InvalidTarget} from"./SettlerErrors.sol";
import {IERC20} from"../IERC20.sol";
import {SafeTransferLib} from"../vendor/SafeTransferLib.sol";
import {FullMath} from"../vendor/FullMath.sol";
import {Panic} from"../utils/Panic.sol";
import {Revert} from"../utils/Revert.sol";
abstractcontractBasicisSettlerAbstract{
usingSafeTransferLibforIERC20;
usingFullMathforuint256;
usingRevertforbool;
IERC20 internalconstant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @dev Sell to a pool with a generic approval, transferFrom interaction./// offset in the calldata is used to update the sellAmount given a proportion of the sellToken balancefunctionbasicSellToPool(IERC20 sellToken, uint256 bps, address pool, uint256 offset, bytesmemory data) internal{
if (_isRestrictedTarget(pool)) {
revert ConfusedDeputy();
}
bool success;
bytesmemory returnData;
uint256 value;
if (sellToken == IERC20(ETH_ADDRESS)) {
value =address(this).balance.mulDiv(bps, 10_000);
if (data.length==0) {
if (offset !=0) revert InvalidOffset();
(success, returnData) =payable(pool).call{value: value}("");
success.maybeRevert(returnData);
return;
} else {
if ((offset +=32) > data.length) {
Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
}
assembly ("memory-safe") {
mstore(add(data, offset), value)
}
}
} elseif (address(sellToken) ==address(0)) {
// TODO: check for zero `bps`if (offset !=0) revert InvalidOffset();
} else {
uint256 amount = sellToken.balanceOf(address(this)).mulDiv(bps, 10_000);
if ((offset +=32) > data.length) {
Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
}
assembly ("memory-safe") {
mstore(add(data, offset), amount)
}
if (address(sellToken) != pool) {
sellToken.safeApproveIfBelow(pool, amount);
}
}
(success, returnData) =payable(pool).call{value: value}(data);
success.maybeRevert(returnData);
// forbid sending data to EOAsif (returnData.length==0&& pool.code.length==0) revert InvalidTarget();
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {UnsafeMath} from"../utils/UnsafeMath.sol";
import {Panic} from"../utils/Panic.sol";
/// @title Contains 512-bit math functions/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldivlibraryFullMath{
usingUnsafeMathforuint256;
/// @notice 512-bit multiply [prod1 prod0] = a * b/// @param a The multiplicand/// @param b The multiplier/// @param denominator The divisor/// @return prod0 Least significant 256 bits of the product/// @return prod1 Most significant 256 bits of the product/// @return remainder Remainder of full-precision divisionfunction_mulDivSetup(uint256 a, uint256 b, uint256 denominator)
privatepurereturns (uint256 prod0, uint256 prod1, uint256 remainder)
{
// Compute the product mod 2**256 and mod 2**256 - 1 then use the Chinese// Remainder Theorem to reconstruct the 512 bit result. The result is stored// in two 256 variables such that product = prod1 * 2**256 + prod0assembly ("memory-safe") {
// Full-precision multiplication
{
let mm :=mulmod(a, b, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
prod0 :=mul(a, b)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
remainder :=mulmod(a, b, denominator)
}
}
/// @notice 512-bit by 256-bit division./// @param prod0 Least significant 256 bits of the product/// @param prod1 Most significant 256 bits of the product/// @param denominator The divisor/// @param remainder Remainder of full-precision division/// @return The 256-bit result/// @dev Overflow and division by zero aren't checked and are GIGO errorsfunction_mulDivInvert(uint256 prod0, uint256 prod1, uint256 denominator, uint256 remainder)
privatepurereturns (uint256)
{
uint256 inv;
assembly ("memory-safe") {
// Make division exact by rounding [prod1 prod0] down to a multiple of// denominator// Subtract 256 bit number from 512 bit number
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
// Factor powers of two out of denominator
{
// Compute largest power of two divisor of denominator.// Always >= 1.let twos :=and(sub(0, denominator), denominator)
// Divide denominator by power of two
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by the factors of two
prod0 :=div(prod0, twos)
// Shift in bits from prod1 into prod0. For this we need to flip `twos`// such that it is 2**256 / twos.// If twos is zero, then it becomes one
twos :=add(div(sub(0, twos), twos), 1)
prod0 :=or(prod0, mul(prod1, twos))
}
// Invert denominator mod 2**256// Now that denominator is an odd number, it has an inverse modulo 2**256// such that denominator * inv = 1 mod 2**256.// Compute the inverse by starting with a seed that is correct correct for// four bits. That is, denominator * inv = 1 mod 2**4
inv :=xor(mul(3, denominator), 2)
// Now use Newton-Raphson iteration to improve the precision.// Thanks to Hensel's lifting lemma, this also works in modular// arithmetic, doubling the correct bits in each step.
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**8
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**16
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**32
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**64
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**128
inv :=mul(inv, sub(2, mul(denominator, inv))) // inverse mod 2**256
}
// Because the division is now exact we can divide by multiplying with the// modular inverse of denominator. This will give us the correct result// modulo 2**256. Since the precoditions guarantee that the outcome is less// than 2**256, this is the final result. We don't need to compute the high// bits of the result and prod1 is no longer required.unchecked {
return prod0 * inv;
}
}
/// @notice Calculates a×b÷denominator with full precision then rounds towards 0. Throws if result overflows a uint256 or denominator == 0/// @param a The multiplicand/// @param b The multiplier/// @param denominator The divisor/// @return The 256-bit resultfunctionmulDiv(uint256 a, uint256 b, uint256 denominator) internalpurereturns (uint256) {
(uint256 prod0, uint256 prod1, uint256 remainder) = _mulDivSetup(a, b, denominator);
// Make sure the result is less than 2**256.// Also prevents denominator == 0if (denominator <= prod1) {
Panic.panic(denominator ==0 ? Panic.DIVISION_BY_ZERO : Panic.ARITHMETIC_OVERFLOW);
}
// Handle non-overflow cases, 256 by 256 divisionif (prod1 ==0) {
return prod0.unsafeDiv(denominator);
}
return _mulDivInvert(prod0, prod1, denominator, remainder);
}
/// @notice Calculates a×b÷denominator with full precision then rounds towards 0. Overflowing a uint256 or denominator == 0 are GIGO errors/// @param a The multiplicand/// @param b The multiplier/// @param denominator The divisor/// @return The 256-bit resultfunctionunsafeMulDiv(uint256 a, uint256 b, uint256 denominator) internalpurereturns (uint256) {
(uint256 prod0, uint256 prod1, uint256 remainder) = _mulDivSetup(a, b, denominator);
// Overflow and zero-division checks are skipped// Handle non-overflow cases, 256 by 256 divisionif (prod1 ==0) {
return prod0.unsafeDiv(denominator);
}
return _mulDivInvert(prod0, prod1, denominator, remainder);
}
}
Contract Source Code
File 9 of 33: IAllowanceHolder.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;interfaceIAllowanceHolder{
/// @notice Executes against `target` with the `data` payload. Prior to execution, token permits/// are temporarily stored for the duration of the transaction. These permits can be/// consumed by the `operator` during the execution/// @notice `operator` consumes the funds during its operations by calling back into/// `AllowanceHolder` with `transferFrom`, consuming a token permit./// @dev Neither `exec` nor `transferFrom` check that `token` contains code./// @dev msg.sender is forwarded to target appended to the msg data (similar to ERC-2771)/// @param operator An address which is allowed to consume the token permits/// @param token The ERC20 token the caller has authorised to be consumed/// @param amount The quantity of `token` the caller has authorised to be consumed/// @param target A contract to execute operations with `data`/// @param data The data to forward to `target`/// @return result The returndata from calling `target` with `data`/// @notice If calling `target` with `data` reverts, the revert is propagatedfunctionexec(address operator, address token, uint256 amount, addresspayable target, bytescalldata data)
externalpayablereturns (bytesmemory result);
/// @notice The counterpart to `exec` which allows for the consumption of token permits later/// during execution/// @dev *DOES NOT* check that `token` contains code. This function vacuously succeeds if/// `token` is empty./// @dev can only be called by the `operator` previously registered in `exec`/// @param token The ERC20 token to transfer/// @param owner The owner of tokens to transfer/// @param recipient The destination/beneficiary of the ERC20 `transferFrom`/// @param amount The quantity of `token` to transfer`/// @return truefunctiontransferFrom(address token, address owner, address recipient, uint256 amount) externalreturns (bool);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
interfaceISettlerActions{
/// @dev Transfer funds from msg.sender Permit2.functionTRANSFER_FROM(address recipient, ISignatureTransfer.PermitTransferFrom memory permit, bytesmemory sig)
external;
/// @dev Transfer funds from metatransaction requestor into the Settler contract using Permit2. Only for use in `Settler.executeMetaTxn` where the signature is provided as calldatafunctionMETATXN_TRANSFER_FROM(address recipient, ISignatureTransfer.PermitTransferFrom memory permit) external;
/// @dev Settle an RfqOrder between maker and taker transfering funds directly between the parties// Post-req: Payout if recipient != takerfunctionRFQ_VIP(address recipient,
ISignatureTransfer.PermitTransferFrom memory makerPermit,
address maker,
bytesmemory makerSig,
ISignatureTransfer.PermitTransferFrom memory takerPermit,
bytesmemory takerSig
) external;
/// @dev Settle an RfqOrder between maker and taker transfering funds directly between the parties for the entire amountfunctionMETATXN_RFQ_VIP(address recipient,
ISignatureTransfer.PermitTransferFrom memory makerPermit,
address maker,
bytesmemory makerSig,
ISignatureTransfer.PermitTransferFrom memory takerPermit
) external;
/// @dev Settle an RfqOrder between Maker and Settler. Transfering funds from the Settler contract to maker./// Retaining funds in the settler contract.// Pre-req: Funded// Post-req: PayoutfunctionRFQ(address recipient,
ISignatureTransfer.PermitTransferFrom memory permit,
address maker,
bytesmemory makerSig,
address takerToken,
uint256 maxTakerAmount
) external;
/// @dev Trades against UniswapV3 using the contracts balance for funding// Pre-req: Funded// Post-req: PayoutfunctionUNISWAPV3(address recipient, uint256 bps, bytesmemory path, uint256 amountOutMin) external;
/// @dev Trades against UniswapV3 using user funds via Permit2 for fundingfunctionUNISWAPV3_VIP(address recipient,
bytesmemory path,
ISignatureTransfer.PermitTransferFrom memory permit,
bytesmemory sig,
uint256 amountOutMin
) external;
functionMAKERPSM(address recipient, address gemToken, uint256 bps, address psm, bool buyGem) external;
functionCURVE_TRICRYPTO_VIP(address recipient,
uint80 poolInfo,
ISignatureTransfer.PermitTransferFrom memory permit,
bytesmemory sig,
uint256 minBuyAmount
) external;
functionMETATXN_CURVE_TRICRYPTO_VIP(address recipient,
uint80 poolInfo,
ISignatureTransfer.PermitTransferFrom memory permit,
uint256 minBuyAmount
) external;
functionDODOV1(address sellToken, uint256 bps, address pool, bool quoteForBase, uint256 minBuyAmount) external;
functionVELODROME(address recipient, uint256 bps, address pool, uint24 swapInfo, uint256 minBuyAmount) external;
/// @dev Trades against UniswapV3 using user funds via Permit2 for funding. Metatransaction variant. Signature is over all actions.functionMETATXN_UNISWAPV3_VIP(address recipient,
bytesmemory path,
ISignatureTransfer.PermitTransferFrom memory permit,
uint256 amountOutMin
) external;
/// @dev Trades against UniswapV2 using the contracts balance for funding/// @param swapInfo is encoded as the upper 16 bits as the fee of the pool in bps, the second/// lowest bit as "sell token has transfer fee", and the lowest bit as the/// "token0 for token1" flag.functionUNISWAPV2(address recipient,
address sellToken,
uint256 bps,
address pool,
uint24 swapInfo,
uint256 amountOutMin
) external;
functionPOSITIVE_SLIPPAGE(address recipient, address token, uint256 expectedAmount) external;
/// @dev Trades against a basic AMM which follows the approval, transferFrom(msg.sender) interaction// Pre-req: Funded// Post-req: PayoutfunctionBASIC(address sellToken, uint256 bps, address pool, uint256 offset, bytescalldata data) external;
}
Contract Source Code
File 14 of 33: ISignatureTransfer.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;import {IEIP712} from"./IEIP712.sol";
/// @title SignatureTransfer/// @notice Handles ERC20 token transfers through signature based actions/// @dev Requires user's token approval on the Permit2 contractinterfaceISignatureTransferisIEIP712{
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount/// @param maxAmount The maximum amount a spender can request to transfererrorInvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferrederrorLengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.eventUnorderedNonceInvalidation(addressindexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signaturestructTokenPermissions {
// ERC20 token addressaddress token;
// the maximum amount that can be spentuint256 amount;
}
/// @notice The signed permit message for a single token transferstructPermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replaysuint256 nonce;
// deadline on the permit signatureuint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers./// @dev Recipients and amounts correspond to the index of the signed token permissions array./// @dev Reverts if the requested amount is greater than the permitted signed amount.structSignatureTransferDetails {
// recipient addressaddress to;
// spender requested amountuint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers/// @dev Do not need to pass in spender address as it is required that it is msg.sender/// @dev Note that a user still signs over a spender addressstructPermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replaysuint256 nonce;
// deadline on the permit signatureuint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce/// @dev It returns a uint256 bitmap/// @dev The index, or wordPosition is capped at type(uint248).maxfunctionnonceBitmap(address, uint256) externalviewreturns (uint256);
/// @notice Transfers a token using a signed permit message/// @dev Reverts if the requested amount is greater than the permitted signed amount/// @param permit The permit data signed over by the owner/// @param owner The owner of the tokens to transfer/// @param transferDetails The spender's requested transfer details for the permitted token/// @param signature The signature to verifyfunctionpermitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytescalldata signature
) external;
/// @notice Transfers a token using a signed permit message/// @notice Includes extra data provided by the caller to verify signature over/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition/// @dev Reverts if the requested amount is greater than the permitted signed amount/// @param permit The permit data signed over by the owner/// @param owner The owner of the tokens to transfer/// @param transferDetails The spender's requested transfer details for the permitted token/// @param witness Extra data to include when checking the user signature/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash/// @param signature The signature to verifyfunctionpermitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
stringcalldata witnessTypeString,
bytescalldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message/// @param permit The permit data signed over by the owner/// @param owner The owner of the tokens to transfer/// @param transferDetails Specifies the recipient and requested amount for the token transfer/// @param signature The signature to verifyfunctionpermitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytescalldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition/// @notice Includes extra data provided by the caller to verify signature over/// @param permit The permit data signed over by the owner/// @param owner The owner of the tokens to transfer/// @param transferDetails Specifies the recipient and requested amount for the token transfer/// @param witness Extra data to include when checking the user signature/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash/// @param signature The signature to verifyfunctionpermitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
stringcalldata witnessTypeString,
bytescalldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position/// @dev The wordPos is maxed at type(uint248).max/// @param wordPos A number to index the nonceBitmap at/// @param mask A bitmap masked against msg.sender's current bitmap at the word positionfunctioninvalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20, IERC20Meta} from"../IERC20.sol";
import {SafeTransferLib} from"../vendor/SafeTransferLib.sol";
import {UnsafeMath} from"../utils/UnsafeMath.sol";
interfaceIPSM{
/// @dev Get the fee for selling DAI to USDC in PSM/// @return tout toll out [wad]functiontout() externalviewreturns (uint256);
/// @dev Get the address of the underlying vault powering PSM/// @return address of gemJoin contractfunctiongemJoin() externalviewreturns (address);
/// @dev Sell USDC for DAI/// @param usr The address of the account trading USDC for DAI./// @param gemAmt The amount of USDC to sell in USDC base unitsfunctionsellGem(address usr, uint256 gemAmt) external;
/// @dev Buy USDC for DAI/// @param usr The address of the account trading DAI for USDC/// @param gemAmt The amount of USDC to buy in USDC base unitsfunctionbuyGem(address usr, uint256 gemAmt) external;
}
abstractcontractMakerPSM{
usingUnsafeMathforuint256;
usingSafeTransferLibforIERC20;
usingSafeTransferLibforIERC20Meta;
// Maker units https://github.com/makerdao/dss/blob/master/DEVELOPING.md// wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)uint256internalconstant WAD =10**18;
IERC20 internalconstant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
constructor() {
assert(block.chainid==1||block.chainid==31337);
}
functionsellToMakerPsm(address recipient, IERC20Meta gemToken, uint256 bps, IPSM psm, bool buyGem) internal{
if (buyGem) {
// phantom overflow can't happen here because DAI has decimals = 18uint256 sellAmount = (DAI.balanceOf(address(this)) * bps).unsafeDiv(10_000);
unchecked {
uint256 feeDivisor = psm.tout() + WAD; // eg. 1.001 * 10 ** 18 with 0.1% fee [tout is in wad];// overflow can't happen at all because DAI is reasonable and PSM prohibits gemToken with decimals > 18uint256 buyAmount = (sellAmount *10**uint256(gemToken.decimals())).unsafeDiv(feeDivisor);
DAI.safeApproveIfBelow(address(psm), sellAmount);
psm.buyGem(recipient, buyAmount);
}
} else {
// phantom overflow can't happen here because PSM prohibits gemToken with decimals > 18uint256 sellAmount = (gemToken.balanceOf(address(this)) * bps).unsafeDiv(10_000);
gemToken.safeApproveIfBelow(psm.gemJoin(), sellAmount);
psm.sellGem(recipient, sellAmount);
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {
CallbackNotSpent,
ConfusedDeputy,
ForwarderNotAllowed,
InvalidSignatureLen,
PayerSpent,
ReentrantCallback,
ReentrantMetatransaction,
ReentrantPayer,
SignatureExpired,
WitnessNotSpent
} from"./SettlerErrors.sol";
import {SettlerAbstract} from"../SettlerAbstract.sol";
import {Permit2PaymentAbstract} from"./Permit2PaymentAbstract.sol";
import {Panic} from"../utils/Panic.sol";
import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
import {Revert} from"../utils/Revert.sol";
import {Context} from"../Context.sol";
import {AllowanceHolderContext} from"../allowanceholder/AllowanceHolderContext.sol";
libraryTransientStorage{
// bytes32(uint256(keccak256("operator slot")) - 1)bytes32privateconstant _OPERATOR_SLOT =0x009355806b743562f351db2e3726091207f49fa1cdccd5c65a7d4860ce3abbe9;
// bytes32(uint256(keccak256("witness slot")) - 1)bytes32privateconstant _WITNESS_SLOT =0x1643bf8e9fdaef48c4abf5a998de359be44a235ac7aebfbc05485e093720deaa;
// bytes32(uint256(keccak256("payer slot")) - 1)bytes32privateconstant _PAYER_SLOT =0x46bacb9b87ba1d2910347e4a3e052d06c824a45acd1e9517bb0cb8d0d5cde893;
// We assume (and our CI enforces) that internal function pointers cannot be// greater than 2 bytes. On chains not supporting the ViaIR pipeline, not// supporting EOF, and where the Spurious Dragon size limit is not enforced,// it might be possible to violate this assumption. However, our// `foundry.toml` enforces the use of the IR pipeline, so the point is moot.//// `operator` must not be `address(0)`. This is not checked.// `callback` must not be zero. This is checked in `_invokeCallback`.functionsetOperatorAndCallback(address operator,
uint32 selector,
function (bytescalldata) internalreturns (bytesmemory) callback
) internal{
address currentSigner;
assembly ("memory-safe") {
currentSigner := tload(_PAYER_SLOT)
}
if (operator == currentSigner) {
revert ConfusedDeputy();
}
uint256 callbackInt;
assembly ("memory-safe") {
callbackInt := tload(_OPERATOR_SLOT)
}
if (callbackInt !=0) {
// It should be impossible to reach this error because the first thing the fallback does// is clear the operator. It's also not possible to reenter the entrypoint function// because `_PAYER_SLOT` is an implicit reentrancy guard.revert ReentrantCallback(callbackInt);
}
assembly ("memory-safe") {
tstore(
_OPERATOR_SLOT,
or(
shl(0xe0, selector),
or(shl(0xa0, and(0xffff, callback)), and(0xffffffffffffffffffffffffffffffffffffffff, operator))
)
)
}
}
functioncheckSpentOperatorAndCallback() internalview{
uint256 callbackInt;
assembly ("memory-safe") {
callbackInt := tload(_OPERATOR_SLOT)
}
if (callbackInt !=0) {
revert CallbackNotSpent(callbackInt);
}
}
functiongetAndClearOperatorAndCallback()
internalreturns (bytes4 selector, function (bytescalldata) internalreturns (bytesmemory) callback, address operator)
{
assembly ("memory-safe") {
selector := tload(_OPERATOR_SLOT)
callback :=and(0xffff, shr(0xa0, selector))
operator := selector
tstore(_OPERATOR_SLOT, 0x00)
}
}
// `newWitness` must not be `bytes32(0)`. This is not checked.functionsetWitness(bytes32 newWitness) internal{
bytes32 currentWitness;
assembly ("memory-safe") {
currentWitness := tload(_WITNESS_SLOT)
}
if (currentWitness !=bytes32(0)) {
// It should be impossible to reach this error because the first thing a metatransaction// does on entry is to spend the `witness` (either directly or via a callback)revert ReentrantMetatransaction(currentWitness);
}
assembly ("memory-safe") {
tstore(_WITNESS_SLOT, newWitness)
}
}
functioncheckSpentWitness() internalview{
bytes32 currentWitness;
assembly ("memory-safe") {
currentWitness := tload(_WITNESS_SLOT)
}
if (currentWitness !=bytes32(0)) {
revert WitnessNotSpent(currentWitness);
}
}
functiongetAndClearWitness() internalreturns (bytes32 witness) {
assembly ("memory-safe") {
witness := tload(_WITNESS_SLOT)
tstore(_WITNESS_SLOT, 0x00)
}
}
functionsetPayer(address payer) internal{
if (payer ==address(0)) {
revert ConfusedDeputy();
}
address oldPayer;
assembly ("memory-safe") {
oldPayer := tload(_PAYER_SLOT)
}
if (oldPayer !=address(0)) {
revert ReentrantPayer(oldPayer);
}
assembly ("memory-safe") {
tstore(_PAYER_SLOT, and(0xffffffffffffffffffffffffffffffffffffffff, payer))
}
}
functiongetPayer() internalviewreturns (address payer) {
assembly ("memory-safe") {
payer := tload(_PAYER_SLOT)
}
}
functionclearPayer(address expectedOldPayer) internal{
address oldPayer;
assembly ("memory-safe") {
oldPayer := tload(_PAYER_SLOT)
}
if (oldPayer != expectedOldPayer) {
revert PayerSpent();
}
assembly ("memory-safe") {
tstore(_PAYER_SLOT, 0x00)
}
}
}
abstractcontractPermit2PaymentBaseisSettlerAbstract{
usingRevertforbool;
/// @dev Permit2 address
ISignatureTransfer internalconstant _PERMIT2 = ISignatureTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);
function_isRestrictedTarget(address target) internalpurevirtualoverridereturns (bool) {
return target ==address(_PERMIT2);
}
function_msgSender() internalviewvirtualoverridereturns (address) {
return TransientStorage.getPayer();
}
/// @dev You must ensure that `target` is derived by hashing trusted initcode or another/// equivalent mechanism that guarantees "reasonable"ness. `target` must not be/// user-supplied or attacker-controlled. This is required for security and is not checked/// here. For example, it must not do something weird like modifying the spender (possibly/// setting it to itself). If the callback is expected to relay a/// `ISignatureTransfer.PermitTransferFrom` struct, then the computation of `target` using/// the trusted initcode (or equivalent) must ensure that that calldata is relayed/// unmodified. The library function `AddressDerivation.deriveDeterministicContract` is/// recommended.function_setOperatorAndCall(addresspayable target,
uint256 value,
bytesmemory data,
uint32 selector,
function (bytescalldata) internalreturns (bytesmemory) callback
) internalreturns (bytesmemory) {
TransientStorage.setOperatorAndCallback(target, selector, callback);
(bool success, bytesmemory returndata) = target.call{value: value}(data);
success.maybeRevert(returndata);
TransientStorage.checkSpentOperatorAndCallback();
return returndata;
}
function_setOperatorAndCall(address target,
bytesmemory data,
uint32 selector,
function (bytescalldata) internalreturns (bytesmemory) callback
) internaloverridereturns (bytesmemory) {
return _setOperatorAndCall(payable(target), 0, data, selector, callback);
}
function_invokeCallback(bytescalldata data) internalreturns (bytesmemory) {
// Retrieve callback and perform call with untrusted calldata
(bytes4 selector, function (bytescalldata) internalreturns (bytesmemory) callback, addressoperator) =
TransientStorage.getAndClearOperatorAndCallback();
require(bytes4(data) == selector);
require(msg.sender== operator);
return callback(data[4:]);
}
}
abstractcontractPermit2PaymentisPermit2PaymentBase{
fallback(bytescalldata data) externalvirtualreturns (bytesmemory) {
return _invokeCallback(data);
}
function_permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient)
internalpureoverridereturns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, address token, uint256 amount)
{
transferDetails.to = recipient;
transferDetails.requestedAmount = amount = permit.permitted.amount;
token = permit.permitted.token;
}
// This function is provided *EXCLUSIVELY* for use here and in RfqOrderSettlement. Any other use// of this function is forbidden. You must use the version that does *NOT* take a `from` or// `witness` argument.function_transferFromIKnowWhatImDoing(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails memory transferDetails,
addressfrom,
bytes32 witness,
stringmemory witnessTypeString,
bytesmemory sig,
bool isForwarded
) internaloverride{
if (isForwarded) revert ForwarderNotAllowed();
_PERMIT2.permitWitnessTransferFrom(permit, transferDetails, from, witness, witnessTypeString, sig);
}
// See comment in above overload; don't use this functionfunction_transferFromIKnowWhatImDoing(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails memory transferDetails,
addressfrom,
bytes32 witness,
stringmemory witnessTypeString,
bytesmemory sig
) internaloverride{
_transferFromIKnowWhatImDoing(permit, transferDetails, from, witness, witnessTypeString, sig, _isForwarded());
}
function_transferFrom(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails memory transferDetails,
bytesmemory sig
) internaloverride{
_transferFrom(permit, transferDetails, sig, _isForwarded());
}
}
abstractcontractPermit2PaymentTakerSubmittedisAllowanceHolderContext, Permit2Payment{
constructor() {
assert(!_hasMetaTxn());
}
function_isRestrictedTarget(address target) internalpurevirtualoverridereturns (bool) {
return target ==address(_ALLOWANCE_HOLDER) ||super._isRestrictedTarget(target);
}
function_operator() internalviewoverridereturns (address) {
return AllowanceHolderContext._msgSender();
}
function_msgSender()
internalviewvirtualoverride(Permit2PaymentBase, AllowanceHolderContext)
returns (address)
{
return Permit2PaymentBase._msgSender();
}
function_transferFrom(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails memory transferDetails,
bytesmemory sig,
bool isForwarded
) internaloverride{
if (isForwarded) {
if (sig.length!=0) revert InvalidSignatureLen();
if (permit.nonce !=0) Panic.panic(Panic.ARITHMETIC_OVERFLOW);
if (block.timestamp> permit.deadline) revert SignatureExpired(permit.deadline);
// we don't check `requestedAmount` because it's checked by AllowanceHolder itself
_allowanceHolderTransferFrom(
permit.permitted.token, _msgSender(), transferDetails.to, transferDetails.requestedAmount
);
} else {
_PERMIT2.permitTransferFrom(permit, transferDetails, _msgSender(), sig);
}
}
function_allowanceHolderTransferFrom(address token, address owner, address recipient, uint256 amount)
internaloverride{
// `owner` is always `_msgSender()`
_ALLOWANCE_HOLDER.transferFrom(token, owner, recipient, amount);
}
modifiertakerSubmitted() override{
address msgSender = _operator();
TransientStorage.setPayer(msgSender);
_;
TransientStorage.clearPayer(msgSender);
}
modifiermetaTx(address, bytes32) override{
revert();
_;
}
}
abstractcontractPermit2PaymentMetaTxnisContext, Permit2Payment{
constructor() {
assert(_hasMetaTxn());
}
function_operator() internalviewoverridereturns (address) {
return Context._msgSender();
}
function_msgSender() internalviewvirtualoverride(Permit2PaymentBase, Context) returns (address) {
return Permit2PaymentBase._msgSender();
}
// `string.concat` isn't recognized by solc as compile-time constant, but `abi.encodePacked` is// This is defined here as `private` and not in `SettlerAbstract` as `internal` because no other// contract/file should reference it. The *ONLY* approved way to make a transfer using this// witness string is by setting the witness with modifier `metaTx`stringprivateconstant _SLIPPAGE_AND_ACTIONS_WITNESS =string(
abi.encodePacked("SlippageAndActions slippageAndActions)", SLIPPAGE_AND_ACTIONS_TYPE, TOKEN_PERMISSIONS_TYPE)
);
function_transferFrom(
ISignatureTransfer.PermitTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails memory transferDetails,
bytesmemory sig,
bool isForwarded // must be false) internaloverride{
bytes32 witness = TransientStorage.getAndClearWitness();
if (witness ==bytes32(0)) {
revert ConfusedDeputy();
}
_transferFromIKnowWhatImDoing(
permit, transferDetails, _msgSender(), witness, _SLIPPAGE_AND_ACTIONS_WITNESS, sig, isForwarded
);
}
function_allowanceHolderTransferFrom(address, address, address, uint256) internalpureoverride{
revert ConfusedDeputy();
}
modifiertakerSubmitted() override{
revert();
_;
}
modifiermetaTx(address msgSender, bytes32 witness) override{
if (_isForwarded()) {
revert ForwarderNotAllowed();
}
TransientStorage.setWitness(witness);
TransientStorage.setPayer(msgSender);
_;
TransientStorage.clearPayer(msgSender);
// It should not be possible for this check to revert because the very first thing that a// metatransaction does is spend the witness.
TransientStorage.checkSpentWitness();
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20} from"../IERC20.sol";
import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
import {SettlerAbstract} from"../SettlerAbstract.sol";
import {SafeTransferLib} from"../vendor/SafeTransferLib.sol";
import {FullMath} from"../vendor/FullMath.sol";
abstractcontractRfqOrderSettlementisSettlerAbstract{
usingSafeTransferLibforIERC20;
usingFullMathforuint256;
structConsideration {
address token;
uint256 amount;
address counterparty;
bool partialFillAllowed;
}
stringinternalconstant CONSIDERATION_TYPE ="Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)";
// `string.concat` isn't recognized by solc as compile-time constant, but `abi.encodePacked` isstringinternalconstant CONSIDERATION_WITNESS =string(abi.encodePacked("Consideration consideration)", CONSIDERATION_TYPE, TOKEN_PERMISSIONS_TYPE));
bytes32internalconstant CONSIDERATION_TYPEHASH =0x7d806873084f389a66fd0315dead7adaad8ae6e8b6cf9fb0d3db61e5a91c3ffa;
stringinternalconstant RFQ_ORDER_TYPE ="RfqOrder(Consideration makerConsideration,Consideration takerConsideration)";
stringinternalconstant RFQ_ORDER_TYPE_RECURSIVE =string(abi.encodePacked(RFQ_ORDER_TYPE, CONSIDERATION_TYPE));
bytes32internalconstant RFQ_ORDER_TYPEHASH =0x49fa719b76f0f6b7e76be94b56c26671a548e1c712d5b13dc2874f70a7598276;
function_hashConsideration(Consideration memory consideration) internalpurereturns (bytes32 result) {
assembly ("memory-safe") {
let ptr :=sub(consideration, 0x20)
let oldValue :=mload(ptr)
mstore(ptr, CONSIDERATION_TYPEHASH)
result :=keccak256(ptr, 0xa0)
mstore(ptr, oldValue)
}
}
function_logRfqOrder(bytes32 makerConsiderationHash, bytes32 takerConsiderationHash, uint128 makerFilledAmount)
private{
assembly ("memory-safe") {
mstore(0x00, RFQ_ORDER_TYPEHASH)
mstore(0x20, makerConsiderationHash)
let ptr :=mload(0x40)
mstore(0x40, takerConsiderationHash)
let orderHash :=keccak256(0x00, 0x60)
mstore(0x40, ptr)
mstore(0x10, makerFilledAmount)
mstore(0x00, orderHash)
log0(0x00, 0x30)
}
}
constructor() {
assert(CONSIDERATION_TYPEHASH ==keccak256(bytes(CONSIDERATION_TYPE)));
assert(RFQ_ORDER_TYPEHASH ==keccak256(bytes(RFQ_ORDER_TYPE_RECURSIVE)));
}
/// @dev Settle an RfqOrder between maker and taker transfering funds directly between the counterparties. Either/// two Permit2 signatures are consumed, with the maker Permit2 containing a witness of the RfqOrder, or/// AllowanceHolder is supported for the taker payment. The Maker has signed the same order as the/// Taker. Submission may be directly by the taker or via a third party with the Taker signing a witness./// @dev if used, the taker's witness is not calculated nor verified here as calling function is trustedfunctionfillRfqOrderVIP(address recipient,
ISignatureTransfer.PermitTransferFrom memory makerPermit,
address maker,
bytesmemory makerSig,
ISignatureTransfer.PermitTransferFrom memory takerPermit,
bytesmemory takerSig
) internal{
(
ISignatureTransfer.SignatureTransferDetails memory makerTransferDetails,
address makerToken,
uint256 makerAmount
) = _permitToTransferDetails(makerPermit, recipient);
(
ISignatureTransfer.SignatureTransferDetails memory takerTransferDetails,
address takerToken,
uint256 takerAmount
) = _permitToTransferDetails(takerPermit, maker);
bytes32 witness = _hashConsideration(
Consideration({
token: takerToken,
amount: takerAmount,
counterparty: _msgSender(),
partialFillAllowed: false
})
);
_transferFrom(takerPermit, takerTransferDetails, takerSig);
_transferFromIKnowWhatImDoing(
makerPermit, makerTransferDetails, maker, witness, CONSIDERATION_WITNESS, makerSig, false
);
_logRfqOrder(
witness,
_hashConsideration(
Consideration({token: makerToken, amount: makerAmount, counterparty: maker, partialFillAllowed: false})
),
uint128(makerAmount)
);
}
/// @dev Settle an RfqOrder between maker and Settler retaining funds in this contract./// @dev pre-condition: msgSender has been authenticated against the requestor/// One Permit2 signature is consumed, with the maker Permit2 containing a witness of the RfqOrder.// In this variant, Maker pays recipient and Settler pays MakerfunctionfillRfqOrderSelfFunded(address recipient,
ISignatureTransfer.PermitTransferFrom memory permit,
address maker,
bytesmemory makerSig,
IERC20 takerToken,
uint256 maxTakerAmount
) internal{
// Compute witnesses. These are based on the quoted maximum amounts. We will modify them// later to adjust for the actual settled amount, which may be modified by encountered// slippage.
(ISignatureTransfer.SignatureTransferDetails memory transferDetails, address makerToken, uint256 makerAmount) =
_permitToTransferDetails(permit, recipient);
bytes32 takerWitness = _hashConsideration(
Consideration({token: makerToken, amount: makerAmount, counterparty: maker, partialFillAllowed: true})
);
bytes32 makerWitness = _hashConsideration(
Consideration({
token: address(takerToken),
amount: maxTakerAmount,
counterparty: _msgSender(),
partialFillAllowed: true
})
);
// Now we adjust the transfer amounts to compensate for encountered slippage. Rounding is// performed in the maker's favor.uint256 takerAmount = takerToken.balanceOf(address(this));
if (takerAmount > maxTakerAmount) {
takerAmount = maxTakerAmount;
}
transferDetails.requestedAmount = makerAmount = makerAmount.unsafeMulDiv(takerAmount, maxTakerAmount);
// Now that we have all the relevant information, make the transfers and log the order.
takerToken.safeTransfer(maker, takerAmount);
_transferFromIKnowWhatImDoing(
permit, transferDetails, maker, makerWitness, CONSIDERATION_WITNESS, makerSig, false
);
_logRfqOrder(makerWitness, takerWitness, uint128(makerAmount));
}
}
Contract Source Code
File 23 of 33: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.25;import {IERC20} from"../IERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
uint32privateconstant _TRANSFER_FROM_FAILED_SELECTOR =0x7939f424; // bytes4(keccak256("TransferFromFailed()"))uint32privateconstant _TRANSFER_FAILED_SELECTOR =0x90b8ec18; // bytes4(keccak256("TransferFailed()"))uint32privateconstant _APPROVE_FAILED_SELECTOR =0x3e3f8f73; // bytes4(keccak256("ApproveFailed()"))/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(addresspayable to, uint256 amount) internal{
assembly ("memory-safe") {
// Transfer the ETH and store if it succeeded or not.ifiszero(call(gas(), to, amount, 0, 0, 0, 0)) {
let freeMemoryPointer :=mload(0x40)
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
}
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 amount) internal{
assembly ("memory-safe") {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.ifiszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no// return data.ifiszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _TRANSFER_FROM_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
functionsafeTransfer(IERC20 token, address to, uint256 amount) internal{
assembly ("memory-safe") {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.ifiszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no// return data.ifiszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _TRANSFER_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
functionsafeApprove(IERC20 token, address to, uint256 amount) internal{
assembly ("memory-safe") {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.ifiszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)) {
returndatacopy(freeMemoryPointer, 0, returndatasize())
revert(freeMemoryPointer, returndatasize())
}
// We check that the call either returned exactly 1 (can't just be non-zero data), or had no// return data.ifiszero(or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()))) {
mstore(0, _APPROVE_FAILED_SELECTOR)
revert(0x1c, 0x04)
}
}
}
functionsafeApproveIfBelow(IERC20 token, address spender, uint256 amount) internal{
uint256 allowance = token.allowance(address(this), spender);
if (allowance < amount) {
if (allowance !=0) {
safeApprove(token, spender, 0);
}
safeApprove(token, spender, type(uint256).max);
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20, IERC20Meta} from"./IERC20.sol";
import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
import {Basic} from"./core/Basic.sol";
import {RfqOrderSettlement} from"./core/RfqOrderSettlement.sol";
import {UniswapV3Fork} from"./core/UniswapV3Fork.sol";
import {UniswapV2} from"./core/UniswapV2.sol";
import {SafeTransferLib} from"./vendor/SafeTransferLib.sol";
import {ISettlerActions} from"./ISettlerActions.sol";
import {TooMuchSlippage} from"./core/SettlerErrors.sol";
/// @dev This library omits index bounds/overflow checking when accessing calldata arrays for gas efficiencylibraryCalldataDecoder{
functiondecodeCall(bytes[] calldata data, uint256 i)
internalpurereturns (bytes4 selector, bytescalldata args)
{
assembly ("memory-safe") {
// initially, we set `args.offset` to the pointer to the length. this is 32 bytes before the actual start of data
args.offset:=add(
data.offset,
// We allow the indirection/offset to `calls[i]` to be negativecalldataload(
add(shl(5, i), data.offset) // can't overflow; we assume `i` is in-bounds
)
)
// now we load `args.length` and set `args.offset` to the start of data
args.length:=calldataload(args.offset)
args.offset:=add(args.offset, 0x20)
// slice off the first 4 bytes of `args` as the selector
selector :=calldataload(args.offset) // solidity cleans dirty bits automatically
args.length:=sub(args.length, 4)
args.offset:=add(args.offset, 4)
}
}
}
abstractcontractSettlerBaseisBasic, RfqOrderSettlement, UniswapV3Fork, UniswapV2{
usingSafeTransferLibforIERC20;
usingSafeTransferLibforaddresspayable;
receive() externalpayable{}
eventGitCommit(bytes20indexed);
// When you change this, you must make corresponding changes to// `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set// `constructor_args`.constructor(bytes20 gitCommit) {
assert((gitCommit ==bytes20(0)) == (block.chainid==31337));
emit GitCommit(gitCommit);
}
structAllowedSlippage {
address recipient;
IERC20 buyToken;
uint256 minAmountOut;
}
function_checkSlippageAndTransfer(AllowedSlippage calldata slippage) internal{
// This final slippage check effectively prohibits custody optimization on the// final hop of every swap. This is gas-inefficient. This is on purpose. Because// ISettlerActions.BASIC could interact with an intents-based settlement// mechanism, we must ensure that the user's want token increase is coming// directly from us instead of from some other form of exchange of value.
(address recipient, IERC20 buyToken, uint256 minAmountOut) =
(slippage.recipient, slippage.buyToken, slippage.minAmountOut);
if (minAmountOut !=0||address(buyToken) !=address(0)) {
if (buyToken == ETH_ADDRESS) {
uint256 amountOut =address(this).balance;
if (amountOut < minAmountOut) {
revert TooMuchSlippage(buyToken, minAmountOut, amountOut);
}
payable(recipient).safeTransferETH(amountOut);
} else {
uint256 amountOut = buyToken.balanceOf(address(this));
if (amountOut < minAmountOut) {
revert TooMuchSlippage(buyToken, minAmountOut, amountOut);
}
buyToken.safeTransfer(recipient, amountOut);
}
}
}
function_dispatch(uint256, bytes4 action, bytescalldata data) internalvirtualoverridereturns (bool) {
if (action == ISettlerActions.TRANSFER_FROM.selector) {
(address recipient, ISignatureTransfer.PermitTransferFrom memory permit, bytesmemory sig) =abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom, bytes));
(ISignatureTransfer.SignatureTransferDetails memory transferDetails,,) =
_permitToTransferDetails(permit, recipient);
_transferFrom(permit, transferDetails, sig);
} elseif (action == ISettlerActions.RFQ.selector) {
(
address recipient,
ISignatureTransfer.PermitTransferFrom memory permit,
address maker,
bytesmemory makerSig,
IERC20 takerToken,
uint256 maxTakerAmount
) =abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom, address, bytes, IERC20, uint256));
fillRfqOrderSelfFunded(recipient, permit, maker, makerSig, takerToken, maxTakerAmount);
} elseif (action == ISettlerActions.UNISWAPV3.selector) {
(address recipient, uint256 bps, bytesmemory path, uint256 amountOutMin) =abi.decode(data, (address, uint256, bytes, uint256));
sellToUniswapV3(recipient, bps, path, amountOutMin);
} elseif (action == ISettlerActions.UNISWAPV2.selector) {
(address recipient, address sellToken, uint256 bps, address pool, uint24 swapInfo, uint256 amountOutMin) =abi.decode(data, (address, address, uint256, address, uint24, uint256));
sellToUniswapV2(recipient, sellToken, bps, pool, swapInfo, amountOutMin);
} elseif (action == ISettlerActions.BASIC.selector) {
(IERC20 sellToken, uint256 bps, address pool, uint256 offset, bytesmemory _data) =abi.decode(data, (IERC20, uint256, address, uint256, bytes));
basicSellToPool(sellToken, bps, pool, offset, _data);
} elseif (action == ISettlerActions.POSITIVE_SLIPPAGE.selector) {
(address recipient, IERC20 token, uint256 expectedAmount) =abi.decode(data, (address, IERC20, uint256));
if (token == IERC20(ETH_ADDRESS)) {
uint256 balance =address(this).balance;
if (balance > expectedAmount) {
unchecked {
payable(recipient).safeTransferETH(balance - expectedAmount);
}
}
} else {
uint256 balance = token.balanceOf(address(this));
if (balance > expectedAmount) {
unchecked {
token.safeTransfer(recipient, balance - expectedAmount);
}
}
}
} else {
returnfalse;
}
returntrue;
}
}
Contract Source Code
File 27 of 33: SettlerErrors.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20} from"../IERC20.sol";
/// @notice Thrown when an offset is not the expected valueerrorInvalidOffset();
/// @notice Thrown when a validating a target contract to avoid certain types of targetserrorConfusedDeputy();
/// @notice Thrown when a target contract is invalid given the contexterrorInvalidTarget();
/// @notice Thrown when validating the caller against the expected callererrorInvalidSender();
/// @notice Thrown in cases when using a Trusted Forwarder / AllowanceHolder is not allowederrorForwarderNotAllowed();
/// @notice Thrown when a signature length is not the expected lengtherrorInvalidSignatureLen();
/// @notice Thrown when a slippage limit is exceedederrorTooMuchSlippage(IERC20 token, uint256 expected, uint256 actual);
/// @notice Thrown when a byte array that is supposed to encode a function from ISettlerActions is/// not recognized in context.errorActionInvalid(uint256 i, bytes4 action, bytes data);
/// @notice Thrown when the encoded fork ID as part of UniswapV3 fork path is not on the list of/// recognized forks for this chain.errorUnknownForkId(uint8 forkId);
/// @notice Thrown when an AllowanceHolder transfer's permit is past its deadlineerrorSignatureExpired(uint256 deadline);
/// @notice An internal error that should never be thrown. Thrown when a callback reenters the/// entrypoint and attempts to clobber the existing callback.errorReentrantCallback(uint256 callbackInt);
/// @notice An internal error that should never be thrown. This error can only be thrown by/// non-metatx-supporting Settler instances. Thrown when a callback-requiring liquidity/// source is called, but Settler never receives the callback.errorCallbackNotSpent(uint256 callbackInt);
/// @notice Thrown when a metatransaction has reentrancy.errorReentrantMetatransaction(bytes32 oldWitness);
/// @notice Thrown when any transaction has reentrancy, not just taker-submitted or metatransaction.errorReentrantPayer(address oldPayer);
/// @notice An internal error that should never be thrown. Thrown when a metatransaction fails to/// spend a coupon.errorWitnessNotSpent(bytes32 oldWitness);
/// @notice An internal error that should never be thrown. Thrown when the payer is unset/// unexpectedly.errorPayerSpent();
Contract Source Code
File 28 of 33: SettlerMetaTxn.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20, IERC20Meta} from"./IERC20.sol";
import {IERC721Owner} from"./IERC721Owner.sol";
import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
import {Permit2PaymentMetaTxn} from"./core/Permit2Payment.sol";
import {Context, AbstractContext} from"./Context.sol";
import {CalldataDecoder, SettlerBase} from"./SettlerBase.sol";
import {UnsafeMath} from"./utils/UnsafeMath.sol";
import {ISettlerActions} from"./ISettlerActions.sol";
import {ConfusedDeputy, ActionInvalid} from"./core/SettlerErrors.sol";
abstractcontractSettlerMetaTxnisPermit2PaymentMetaTxn, SettlerBase{
usingUnsafeMathforuint256;
usingCalldataDecoderforbytes[];
constructor() {
assert(
block.chainid==31337|| IERC721Owner(0x00000000000004533Fe15556B1E086BB1A72cEae).ownerOf(3) ==address(this)
);
}
function_hasMetaTxn() internalpureoverridereturns (bool) {
returntrue;
}
function_msgSender()
internalviewvirtual// Solidity inheritance is so stupidoverride(Permit2PaymentMetaTxn, AbstractContext)
returns (address)
{
returnsuper._msgSender();
}
function_hashArrayOfBytes(bytes[] calldata actions) internalpurereturns (bytes32 result) {
// This function deliberately does no bounds checking on `actions` for// gas efficiency. We assume that `actions` will get used elsewhere in// this context and any OOB or other malformed calldata will result in a// revert later.assembly ("memory-safe") {
let ptr :=mload(0x40)
let hashesLength :=shl(5, actions.length)
for {
let i := actions.offsetlet dst := ptr
let end :=add(i, hashesLength)
} lt(i, end) {
i :=add(i, 0x20)
dst :=add(dst, 0x20)
} {
let src :=add(actions.offset, calldataload(i))
let length :=calldataload(src)
calldatacopy(dst, add(src, 0x20), length)
mstore(dst, keccak256(dst, length))
}
result :=keccak256(ptr, hashesLength)
}
}
function_hashActionsAndSlippage(bytes[] calldata actions, AllowedSlippage calldata slippage)
internalpurereturns (bytes32 result)
{
// This function does not check for or clean any dirty bits that might// exist in `slippage`. We assume that `slippage` will be used elsewhere// in this context and that if there are dirty bits it will result in a// revert later.bytes32 arrayOfBytesHash = _hashArrayOfBytes(actions);
assembly ("memory-safe") {
let ptr :=mload(0x40)
mstore(ptr, SLIPPAGE_AND_ACTIONS_TYPEHASH)
calldatacopy(add(ptr, 0x20), slippage, 0x60)
mstore(add(ptr, 0x80), arrayOfBytesHash)
result :=keccak256(ptr, 0xa0)
}
}
function_dispatchVIP(bytes4 action, bytescalldata data, bytescalldata sig) internalvirtualreturns (bool) {
if (action == ISettlerActions.METATXN_RFQ_VIP.selector) {
// An optimized path involving a maker/taker in a single trade// The RFQ order is signed by both maker and taker, validation is// performed inside the RfqOrderSettlement so there is no need to// validate `sig` against `actions` here
(
address recipient,
ISignatureTransfer.PermitTransferFrom memory makerPermit,
address maker,
bytesmemory makerSig,
ISignatureTransfer.PermitTransferFrom memory takerPermit
) =abi.decode(
data,
(address, ISignatureTransfer.PermitTransferFrom, address, bytes, ISignatureTransfer.PermitTransferFrom)
);
fillRfqOrderVIP(recipient, makerPermit, maker, makerSig, takerPermit, sig);
} elseif (action == ISettlerActions.METATXN_TRANSFER_FROM.selector) {
(address recipient, ISignatureTransfer.PermitTransferFrom memory permit) =abi.decode(data, (address, ISignatureTransfer.PermitTransferFrom));
(ISignatureTransfer.SignatureTransferDetails memory transferDetails,,) =
_permitToTransferDetails(permit, recipient);
// We simultaneously transfer-in the taker's tokens and authenticate the// metatransaction.
_transferFrom(permit, transferDetails, sig);
} elseif (action == ISettlerActions.METATXN_UNISWAPV3_VIP.selector) {
(
address recipient,
bytesmemory path,
ISignatureTransfer.PermitTransferFrom memory permit,
uint256 amountOutMin
) =abi.decode(data, (address, bytes, ISignatureTransfer.PermitTransferFrom, uint256));
sellToUniswapV3VIP(recipient, path, permit, sig, amountOutMin);
} else {
returnfalse;
}
returntrue;
}
functionexecuteMetaTxn(
AllowedSlippage calldata slippage,
bytes[] calldata actions,
bytes32, /* zid & affiliate */address msgSender,
bytescalldata sig
) publicmetaTx(msgSender, _hashActionsAndSlippage(actions, slippage)) returns (bool) {
require(actions.length!=0);
{
(bytes4 action, bytescalldata data) = actions.decodeCall(0);
// By forcing the first action to be one of the witness-aware// actions, we ensure that the entire sequence of actions is// authorized. `msgSender` is the signer of the metatransaction.if (!_dispatchVIP(action, data, sig)) {
revert ActionInvalid(0, action, data);
}
}
for (uint256 i =1; i < actions.length; i = i.unsafeInc()) {
(bytes4 action, bytescalldata data) = actions.decodeCall(i);
if (!_dispatch(i, action, data)) {
revert ActionInvalid(i, action, data);
}
}
_checkSlippageAndTransfer(slippage);
returntrue;
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20} from"../IERC20.sol";
import {UnsafeMath} from"../utils/UnsafeMath.sol";
import {Panic} from"../utils/Panic.sol";
import {TooMuchSlippage} from"./SettlerErrors.sol";
interfaceIUniV2Pair{
functiontoken0() externalviewreturns (address);
functiontoken1() externalviewreturns (address);
functiongetReserves() externalviewreturns (uint112, uint112, uint32);
functionswap(uint256, uint256, address, bytescalldata) external;
}
abstractcontractUniswapV2{
usingUnsafeMathforuint256;
// bytes4(keccak256("getReserves()"))uint32privateconstant UNI_PAIR_RESERVES_SELECTOR =0x0902f1ac;
// bytes4(keccak256("swap(uint256,uint256,address,bytes)"))uint32privateconstant UNI_PAIR_SWAP_SELECTOR =0x022c0d9f;
// bytes4(keccak256("transfer(address,uint256)"))uint32privateconstant ERC20_TRANSFER_SELECTOR =0xa9059cbb;
// bytes4(keccak256("balanceOf(address)"))uint32privateconstant ERC20_BALANCEOF_SELECTOR =0x70a08231;
/// @dev Sell a token for another token using UniswapV2.functionsellToUniswapV2(address recipient,
address sellToken,
uint256 bps,
address pool,
uint24 swapInfo,
uint256 minBuyAmount
) internal{
// Preventing calls to Permit2 or AH is not explicitly required as neither of these contracts implement the `swap` nor `transfer` selector// |7|6|5|4|3|2|1|0| - bit positions in swapInfo (uint8)// |0|0|0|0|0|0|F|Z| - Z: zeroForOne flag, F: sellTokenHasFee flagbool zeroForOne = (swapInfo &1) ==1; // Extract the least significant bit (bit 0)bool sellTokenHasFee = (swapInfo &2) >>1==1; // Extract the second least significant bit (bit 1) and shift it rightuint256 feeBps = swapInfo >>8;
uint256 sellAmount;
uint256 buyAmount;
// If bps is zero we assume there are no funds within this contract, skip the updating sellAmount.// This case occurs if the pool is being chained, in which the funds have been sent directly to the poolif (bps !=0) {
// We don't care about phantom overflow here because reserves are// limited to 112 bits. Any token balance that would overflow here would// also break UniV2.// It is *possible* to set `bps` above the basis and therefore// cause an overflow on this multiplication. However, `bps` is// passed as authenticated calldata, so this is a GIGO error that we// do not attempt to fix.unchecked {
sellAmount = (IERC20(sellToken).balanceOf(address(this)) * bps).unsafeDiv(10_000);
}
}
assembly ("memory-safe") {
let ptr :=mload(0x40)
// transfer sellAmount (a non zero amount) of sellToken to the poolif sellAmount {
mstore(ptr, ERC20_TRANSFER_SELECTOR)
mstore(add(ptr, 0x20), pool)
mstore(add(ptr, 0x40), sellAmount)
// ...||ERC20_TRANSFER_SELECTOR|pool|sellAmount|ifiszero(call(gas(), sellToken, 0, add(ptr, 0x1c), 0x44, 0x00, 0x20)) { bubbleRevert(ptr) }
ifiszero(or(iszero(returndatasize()), and(iszero(lt(returndatasize(), 0x20)), eq(mload(0x00), 1)))) {
revert(0, 0)
}
}
// get pool reserveslet sellReserve
let buyReserve
mstore(0x00, UNI_PAIR_RESERVES_SELECTOR)
// ||UNI_PAIR_RESERVES_SELECTOR|ifiszero(staticcall(gas(), pool, 0x1c, 0x04, 0x00, 0x40)) { bubbleRevert(ptr) }
iflt(returndatasize(), 0x40) { revert(0, 0) }
{
let r :=shl(5, zeroForOne)
buyReserve :=mload(r)
sellReserve :=mload(xor(0x20, r))
}
// Update the sell amount in the following cases:// the funds are in the pool already (flagged by sellAmount being 0)// the sell token has a fee (flagged by sellTokenHasFee)ifor(iszero(sellAmount), sellTokenHasFee) {
// retrieve the sellToken balance of the poolmstore(0x00, ERC20_BALANCEOF_SELECTOR)
mstore(0x20, and(0xffffffffffffffffffffffffffffffffffffffff, pool))
// ||ERC20_BALANCEOF_SELECTOR|pool|ifiszero(staticcall(gas(), sellToken, 0x1c, 0x24, 0x00, 0x20)) { bubbleRevert(ptr) }
iflt(returndatasize(), 0x20) { revert(0, 0) }
let bal :=mload(0x00)
// determine real sellAmount by comparing pool's sellToken balance to reserve amountiflt(bal, sellReserve) {
mstore(0x00, 0x4e487b71) // selector for `Panic(uint256)`mstore(0x20, 0x11) // panic code for arithmetic underflowrevert(0x1c, 0x24)
}
sellAmount :=sub(bal, sellReserve)
}
// compute buyAmount based on sellAmount and reserveslet sellAmountWithFee :=mul(sellAmount, sub(10000, feeBps))
buyAmount :=div(mul(sellAmountWithFee, buyReserve), add(sellAmountWithFee, mul(sellReserve, 10000)))
let swapCalldata :=add(ptr, 0x1c)
// set up swap call selector and empty callback datamstore(ptr, UNI_PAIR_SWAP_SELECTOR)
mstore(add(ptr, 0x80), 0x80) // offset to length of datamstore(add(ptr, 0xa0), 0) // length of data// set amount0Out and amount1Out
{
// If `zeroForOne`, offset is 0x24, else 0x04let offset :=add(0x04, shl(5, zeroForOne))
mstore(add(swapCalldata, offset), buyAmount)
mstore(add(swapCalldata, xor(0x20, offset)), 0)
}
mstore(add(swapCalldata, 0x44), and(0xffffffffffffffffffffffffffffffffffffffff, recipient))
// ...||UNI_PAIR_SWAP_SELECTOR|amount0Out|amount1Out|recipient|data|// perform swap at the pool sending bought tokens to the recipientifiszero(call(gas(), pool, 0, swapCalldata, 0xa4, 0, 0)) { bubbleRevert(swapCalldata) }
// revert with the return data from the most recent callfunctionbubbleRevert(p) {
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
}
if (buyAmount < minBuyAmount) {
revert TooMuchSlippage(
IERC20(zeroForOne ? IUniV2Pair(pool).token1() : IUniV2Pair(pool).token0()), minBuyAmount, sellAmount
);
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.25;import {IERC20} from"../IERC20.sol";
import {ISignatureTransfer} from"permit2/src/interfaces/ISignatureTransfer.sol";
import {UnsafeMath} from"../utils/UnsafeMath.sol";
import {Panic} from"../utils/Panic.sol";
import {SafeTransferLib} from"../vendor/SafeTransferLib.sol";
import {AddressDerivation} from"../utils/AddressDerivation.sol";
import {SettlerAbstract} from"../SettlerAbstract.sol";
import {TooMuchSlippage} from"./SettlerErrors.sol";
interfaceIUniswapV3Pool{
/// @notice Swap token0 for token1, or token1 for token0/// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback/// @param recipient The address to receive the output of the swap/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive),/// or exact output (negative)/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this/// value after the swap. If one for zero, the price cannot be greater than this value after the swap/// @param data Any data to be passed through to the callback/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positivefunctionswap(address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytescalldata data
) externalreturns (int256 amount0, int256 amount1);
}
abstractcontractUniswapV3ForkisSettlerAbstract{
usingUnsafeMathforuint256;
usingSafeTransferLibforIERC20;
/// @dev Minimum size of an encoded swap path:/// sizeof(address(inputToken) | uint8(forkId) | uint24(poolId) | address(outputToken))uint256privateconstant SINGLE_HOP_PATH_SIZE =0x2c;
/// @dev How many bytes to skip ahead in an encoded path to start at the next hop:/// sizeof(address(inputToken) | uint8(forkId) | uint24(poolId))uint256privateconstant PATH_SKIP_HOP_SIZE =0x18;
/// @dev The size of the swap callback prefix data before the Permit2 data.uint256privateconstant SWAP_CALLBACK_PREFIX_DATA_SIZE =0x28;
/// @dev The offset from the pointer to the length of the swap callback prefix data to the start of the Permit2 data.uint256privateconstant SWAP_CALLBACK_PERMIT2DATA_OFFSET =0x48;
uint256privateconstant PERMIT_DATA_SIZE =0x60;
uint256privateconstant ISFORWARDED_DATA_SIZE =0x01;
/// @dev Minimum tick price sqrt ratio.uint160privateconstant MIN_PRICE_SQRT_RATIO =4295128739;
/// @dev Minimum tick price sqrt ratio.uint160privateconstant MAX_PRICE_SQRT_RATIO =1461446703485210103287273052203988822378723970342;
/// @dev Mask of lower 20 bytes.uint256privateconstant ADDRESS_MASK =0x00ffffffffffffffffffffffffffffffffffffffff;
/// @dev Mask of lower 3 bytes.uint256privateconstant UINT24_MASK =0xffffff;
/// @dev Sell a token for another token directly against uniswap v3./// @param encodedPath Uniswap-encoded path./// @param bps proportion of current balance of the first token in the path to sell./// @param minBuyAmount Minimum amount of the last token in the path to buy./// @param recipient The recipient of the bought tokens./// @return buyAmount Amount of the last token in the path bought.functionsellToUniswapV3(address recipient, uint256 bps, bytesmemory encodedPath, uint256 minBuyAmount)
internalreturns (uint256 buyAmount)
{
buyAmount = _uniV3ForkSwap(
recipient,
encodedPath,
// We don't care about phantom overflow here because reserves are// limited to 128 bits. Any token balance that would overflow here// would also break UniV3.
(IERC20(address(bytes20(encodedPath))).balanceOf(address(this)) * bps).unsafeDiv(10_000),
minBuyAmount,
address(this), // payernewbytes(SWAP_CALLBACK_PREFIX_DATA_SIZE)
);
}
/// @dev Sell a token for another token directly against uniswap v3. Payment is using a Permit2 signature (or AllowanceHolder)./// @param encodedPath Uniswap-encoded path./// @param minBuyAmount Minimum amount of the last token in the path to buy./// @param recipient The recipient of the bought tokens./// @param permit The PermitTransferFrom allowing this contract to spend the taker's tokens/// @param sig The taker's signature for Permit2/// @return buyAmount Amount of the last token in the path bought.functionsellToUniswapV3VIP(address recipient,
bytesmemory encodedPath,
ISignatureTransfer.PermitTransferFrom memory permit,
bytesmemory sig,
uint256 minBuyAmount
) internalreturns (uint256 buyAmount) {
bytesmemory swapCallbackData =newbytes(SWAP_CALLBACK_PREFIX_DATA_SIZE + PERMIT_DATA_SIZE + ISFORWARDED_DATA_SIZE + sig.length);
_encodePermit2Data(swapCallbackData, permit, sig, _isForwarded());
buyAmount = _uniV3ForkSwap(
recipient,
encodedPath,
permit.permitted.amount,
minBuyAmount,
address(0), // payer
swapCallbackData
);
}
// Executes successive swaps along an encoded uniswap path.function_uniV3ForkSwap(address recipient,
bytesmemory encodedPath,
uint256 sellAmount,
uint256 minBuyAmount,
address payer,
bytesmemory swapCallbackData
) internalreturns (uint256 buyAmount) {
if (sellAmount >uint256(type(int256).max)) {
Panic.panic(Panic.ARITHMETIC_OVERFLOW);
}
IERC20 outputToken;
while (true) {
bool isPathMultiHop = _isPathMultiHop(encodedPath);
bool zeroForOne;
IUniswapV3Pool pool;
uint32 callbackSelector;
{
(IERC20 token0, uint8 forkId, uint24 poolId, IERC20 token1) = _decodeFirstPoolInfoFromPath(encodedPath);
IERC20 sellToken = token0;
outputToken = token1;
if (!(zeroForOne = token0 < token1)) {
(token0, token1) = (token1, token0);
}
address factory;
bytes32 initHash;
(factory, initHash, callbackSelector) = _uniV3ForkInfo(forkId);
pool = _toPool(factory, initHash, token0, poolId, token1);
_updateSwapCallbackData(swapCallbackData, sellToken, payer);
}
int256 amount0;
int256 amount1;
if (isPathMultiHop) {
uint256 freeMemPtr;
assembly ("memory-safe") {
freeMemPtr :=mload(0x40)
}
(amount0, amount1) =abi.decode(
_setOperatorAndCall(
address(pool),
abi.encodeCall(
pool.swap,
(
// Intermediate tokens go to this contract.address(this),
zeroForOne,
int256(sellAmount),
zeroForOne ? MIN_PRICE_SQRT_RATIO +1 : MAX_PRICE_SQRT_RATIO -1,
swapCallbackData
)
),
callbackSelector,
_uniV3ForkCallback
),
(int256, int256)
);
assembly ("memory-safe") {
mstore(0x40, freeMemPtr)
}
} else {
(amount0, amount1) =abi.decode(
_setOperatorAndCall(
address(pool),
abi.encodeCall(
pool.swap,
(
recipient,
zeroForOne,
int256(sellAmount),
zeroForOne ? MIN_PRICE_SQRT_RATIO +1 : MAX_PRICE_SQRT_RATIO -1,
swapCallbackData
)
),
callbackSelector,
_uniV3ForkCallback
),
(int256, int256)
);
}
{
int256 _buyAmount =-(zeroForOne ? amount1 : amount0);
if (_buyAmount <0) {
Panic.panic(Panic.ARITHMETIC_OVERFLOW);
}
buyAmount =uint256(_buyAmount);
}
if (!isPathMultiHop) {
// Done.break;
}
// Continue with next hop.
payer =address(this); // Subsequent hops are paid for by us.
sellAmount = buyAmount;
// Skip to next hop along path.
encodedPath = _shiftHopFromPathInPlace(encodedPath);
assembly ("memory-safe") {
mstore(swapCallbackData, SWAP_CALLBACK_PREFIX_DATA_SIZE)
}
}
if (buyAmount < minBuyAmount) {
revert TooMuchSlippage(outputToken, minBuyAmount, buyAmount);
}
}
// Return whether or not an encoded uniswap path contains more than one hop.function_isPathMultiHop(bytesmemory encodedPath) privatepurereturns (bool) {
return encodedPath.length> SINGLE_HOP_PATH_SIZE;
}
function_decodeFirstPoolInfoFromPath(bytesmemory encodedPath)
privatepurereturns (IERC20 inputToken, uint8 forkId, uint24 poolId, IERC20 outputToken)
{
if (encodedPath.length< SINGLE_HOP_PATH_SIZE) {
Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
}
assembly ("memory-safe") {
// Solidity cleans dirty bits automatically
inputToken :=mload(add(encodedPath, 0x14))
forkId :=mload(add(encodedPath, 0x15))
poolId :=mload(add(encodedPath, 0x18))
outputToken :=mload(add(encodedPath, SINGLE_HOP_PATH_SIZE))
}
}
// Skip past the first hop of an encoded uniswap path in-place.function_shiftHopFromPathInPlace(bytesmemory encodedPath) privatepurereturns (bytesmemory) {
if (encodedPath.length< PATH_SKIP_HOP_SIZE) {
Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
}
assembly ("memory-safe") {
let length :=sub(mload(encodedPath), PATH_SKIP_HOP_SIZE)
encodedPath :=add(encodedPath, PATH_SKIP_HOP_SIZE)
mstore(encodedPath, length)
}
return encodedPath;
}
function_encodePermit2Data(bytesmemory swapCallbackData,
ISignatureTransfer.PermitTransferFrom memory permit,
bytesmemory sig,
bool isForwarded
) privatepure{
assembly ("memory-safe") {
mstore(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, swapCallbackData), mload(add(0x20, mload(permit))))
mcopy(add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, 0x20), swapCallbackData), add(0x20, permit), 0x40)
mstore8(add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, PERMIT_DATA_SIZE), swapCallbackData), isForwarded)
mcopy(
add(
add(add(SWAP_CALLBACK_PERMIT2DATA_OFFSET, PERMIT_DATA_SIZE), ISFORWARDED_DATA_SIZE),
swapCallbackData
),
add(0x20, sig),
mload(sig)
)
}
}
// Update `swapCallbackData` in place with new values.function_updateSwapCallbackData(bytesmemory swapCallbackData, IERC20 sellToken, address payer) privatepure{
assembly ("memory-safe") {
let length :=mload(swapCallbackData)
mstore(add(0x28, swapCallbackData), sellToken)
mstore(add(0x14, swapCallbackData), payer)
mstore(swapCallbackData, length)
}
}
// Compute the pool address given two tokens and a poolId.function_toPool(address factory, bytes32 initHash, IERC20 inputToken, uint24 poolId, IERC20 outputToken)
privatepurereturns (IUniswapV3Pool)
{
// address(keccak256(abi.encodePacked(// hex"ff",// factory,// keccak256(abi.encode(inputToken, outputToken, poolId)),// initHash// )))
(IERC20 token0, IERC20 token1) =
inputToken < outputToken ? (inputToken, outputToken) : (outputToken, inputToken);
bytes32 salt;
assembly ("memory-safe") {
token0 :=and(ADDRESS_MASK, token0)
token1 :=and(ADDRESS_MASK, token1)
poolId :=and(UINT24_MASK, poolId)
let ptr :=mload(0x40)
mstore(0x00, token0)
mstore(0x20, token1)
mstore(0x40, poolId)
salt :=keccak256(0x00, sub(0x60, shl(0x05, iszero(poolId))))
mstore(0x40, ptr)
}
return IUniswapV3Pool(AddressDerivation.deriveDeterministicContract(factory, salt, initHash));
}
function_uniV3ForkInfo(uint8 forkId) internalviewvirtualreturns (address, bytes32, uint32);
function_uniV3ForkCallback(bytescalldata data) privatereturns (bytesmemory) {
require(data.length>=0x80);
int256 amount0Delta;
int256 amount1Delta;
assembly ("memory-safe") {
amount0Delta :=calldataload(data.offset)
amount1Delta :=calldataload(add(0x20, data.offset))
data.offset:=add(data.offset, calldataload(add(0x40, data.offset)))
data.length:=calldataload(data.offset)
data.offset:=add(0x20, data.offset)
}
uniswapV3SwapCallback(amount0Delta, amount1Delta, data);
returnnewbytes(0);
}
/// @dev The UniswapV3 pool swap callback which pays the funds requested/// by the caller/pool to the pool. Can only be called by a valid/// UniswapV3 pool./// @param amount0Delta Token0 amount owed./// @param amount1Delta Token1 amount owed./// @param data Arbitrary data forwarded from swap() caller. A packed encoding of: payer, sellToken, (optionally: permit[0x20:], isForwarded, sig)functionuniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytescalldata data) private{
address payer =address(uint160(bytes20(data)));
data = data[0x14:];
uint256 sellAmount = amount0Delta >0 ? uint256(amount0Delta) : uint256(amount1Delta);
_pay(payer, sellAmount, data);
}
function_pay(address payer, uint256 amount, bytescalldata permit2Data) private{
if (payer ==address(this)) {
IERC20(address(uint160(bytes20(permit2Data)))).safeTransfer(msg.sender, amount);
} else {
assert(payer ==address(0));
ISignatureTransfer.PermitTransferFrom calldata permit;
bool isForwarded;
bytescalldata sig;
assembly ("memory-safe") {
// this is super dirty, but it works because although `permit` is aliasing in the// middle of `payer`, because `payer` is all zeroes, it's treated as padding for the// first word of `permit`, which is the sell token
permit :=sub(permit2Data.offset, 0x0c)
isForwarded :=and(0x01, calldataload(add(0x55, permit2Data.offset)))
sig.offset:=add(0x75, permit2Data.offset)
sig.length:=sub(permit2Data.length, 0x75)
}
_transferFrom(
permit,
ISignatureTransfer.SignatureTransferDetails({to: msg.sender, requestedAmount: amount}),
sig,
isForwarded
);
}
}
}