Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* Fix a `metaTx` malleability bug in `CrossChainReceiverFactory`
(contract is not deployed; no funds at risk) after a report in
Immunefi bug 78645
* Add Orvex CL PancakeInfinity fork to RobinHood chain
* Add `PANCAKE_INFINITY`, `PANCAKE_INFINITY_VIP`, and `METATXN_PANCAKE_INFINITY_VIP`

## 2026-07-27

Expand Down
17 changes: 17 additions & 0 deletions src/chains/Base/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import {IPoolManager} from "../../core/UniswapV4Types.sol";
import {EulerSwap, IEVC, IEulerSwap} from "../../core/EulerSwap.sol";
import {BalancerV3} from "../../core/BalancerV3.sol";
import {PancakeInfinity} from "../../core/PancakeInfinity.sol";
import {
pancakeInfinityVault,
pancakeInfinityClManager,
pancakeInfinityBinManager
} from "../../core/pancakeInfinityForks/PancakeInfinity.sol";
import {Renegade, BASE_SELECTOR} from "../../core/Renegade.sol";
import {Bebop} from "../../core/Bebop.sol";
import {Hanji} from "../../core/Hanji.sol";
Expand Down Expand Up @@ -227,6 +232,18 @@ abstract contract BaseMixin is
return BASE_POOL_MANAGER;
}

function _PANCAKE_INFINITY_VAULT() internal pure override returns (address) {
return pancakeInfinityVault;
}

function _PANCAKE_INFINITY_CL_MANAGER() internal pure override returns (address) {
return pancakeInfinityClManager;
}

function _PANCAKE_INFINITY_BIN_MANAGER() internal pure override returns (address) {
return pancakeInfinityBinManager;
}

/*
function _EVC() internal pure override returns (IEVC) {
return IEVC(0x5301c7dD20bD945D2013b48ed0DEE3A284ca8989);
Expand Down
17 changes: 17 additions & 0 deletions src/chains/Bnb/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import {DodoV2, IDodoV2} from "../../core/DodoV2.sol";
import {UniswapV4} from "../../core/UniswapV4.sol";
import {IPoolManager} from "../../core/UniswapV4Types.sol";
import {PancakeInfinity} from "../../core/PancakeInfinity.sol";
import {
pancakeInfinityVault,
pancakeInfinityClManager,
pancakeInfinityBinManager
} from "../../core/pancakeInfinityForks/PancakeInfinity.sol";
import {EulerSwap, IEVC, IEulerSwap} from "../../core/EulerSwap.sol";
import {Bebop} from "../../core/Bebop.sol";

Expand Down Expand Up @@ -177,6 +182,18 @@ abstract contract BnbMixin is
return BNB_POOL_MANAGER;
}

function _PANCAKE_INFINITY_VAULT() internal pure override returns (address) {
return pancakeInfinityVault;
}

function _PANCAKE_INFINITY_CL_MANAGER() internal pure override returns (address) {
return pancakeInfinityClManager;
}

function _PANCAKE_INFINITY_BIN_MANAGER() internal pure override returns (address) {
return pancakeInfinityBinManager;
}

function _EVC() internal pure override returns (IEVC) {
return IEVC(0xb2E5a73CeE08593d1a076a2AE7A6e02925a640ea);
}
Expand Down
26 changes: 22 additions & 4 deletions src/chains/RobinHood/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {Hanji} from "../../core/Hanji.sol";

import {ISettlerActions} from "../../ISettlerActions.sol";
import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol";
import {revertUnknownForkId} from "../../core/SettlerErrors.sol";
import {revertUnknownForkId, revertUnknownPoolManagerId} from "../../core/SettlerErrors.sol";

import {
uniswapV3RobinhoodFactory,
Expand All @@ -35,14 +35,16 @@ import {swapHoodV3Factory, swapHoodV3InitHash, swapHoodV3ForkId} from "../../cor
import {gigaDexV3Factory, gigaDexV3InitHash, gigaDexV3ForkId} from "../../core/univ3forks/GigaDexV3.sol";
import {IAlgebraCallback} from "../../core/univ3forks/Algebra.sol";
import {ROBINHOOD_POOL_MANAGER} from "../../core/UniswapV4Addresses.sol";
import {PancakeInfinity} from "../../core/PancakeInfinity.sol";
import {orvexVault, orvexClManager} from "../../core/pancakeInfinityForks/OrvexCL.sol";

import {FastLogic} from "../../utils/FastLogic.sol";

// Solidity inheritance is stupid
import {SettlerSwapAbstract} from "../../SettlerAbstract.sol";
import {Permit2PaymentAbstract} from "../../core/Permit2PaymentAbstract.sol";

abstract contract RobinHoodMixin is FreeMemory, SettlerBase, UniswapV4, EkuboV3, Hanji {
abstract contract RobinHoodMixin is FreeMemory, SettlerBase, UniswapV4, EkuboV3, Hanji, PancakeInfinity {
using FastLogic for bool;

constructor() {
Expand All @@ -59,7 +61,8 @@ abstract contract RobinHoodMixin is FreeMemory, SettlerBase, UniswapV4, EkuboV3,
if (super._dispatch(i, action, data, slippage)) {
return true;
} else if ((action == uint32(ISettlerActions.UNISWAPV4.selector))
.or(action == uint32(ISettlerActions.EKUBOV3.selector))) {
.or(action == uint32(ISettlerActions.EKUBOV3.selector))
.or(action == uint32(ISettlerActions.PANCAKE_INFINITY.selector))) {
(
address recipient,
IERC20 sellToken,
Expand All @@ -73,8 +76,10 @@ abstract contract RobinHoodMixin is FreeMemory, SettlerBase, UniswapV4, EkuboV3,

if (action == uint32(ISettlerActions.UNISWAPV4.selector)) {
sellToUniswapV4(recipient, sellToken, bps, feeOnTransfer, hashMul, hashMod, fills, amountOutMin);
} else { // if (action == uint32(ISettlerActions.EKUBOV3.selector))
} else if (action == uint32(ISettlerActions.EKUBOV3.selector)) {
sellToEkuboV3(recipient, sellToken, bps, feeOnTransfer, hashMul, hashMod, fills, amountOutMin);
} else { // if (action == uint32(ISettlerActions.PANCAKE_INFINITY.selector))
sellToPancakeInfinity(recipient, sellToken, bps, feeOnTransfer, hashMul, hashMod, fills, amountOutMin);
}
} else if (action == uint32(ISettlerActions.HANJI.selector)) {
(
Expand Down Expand Up @@ -142,6 +147,19 @@ abstract contract RobinHoodMixin is FreeMemory, SettlerBase, UniswapV4, EkuboV3,
return ROBINHOOD_POOL_MANAGER;
}

function _PANCAKE_INFINITY_VAULT() internal pure override returns (address) {
return orvexVault;
}

function _PANCAKE_INFINITY_CL_MANAGER() internal pure override returns (address) {
return orvexClManager;
}

// Orvex does not have a Bin pool manager
function _PANCAKE_INFINITY_BIN_MANAGER() internal pure override returns (address) {
revertUnknownPoolManagerId(1);
}

// I hate Solidity inheritance
function _fallback(bytes calldata data)
internal
Expand Down
7 changes: 5 additions & 2 deletions src/chains/RobinHood/MetaTxn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ contract RobinHoodSettlerMetaTxn is SettlerMetaTxn, RobinHoodMixin {
if (super._dispatchVIP(action, data, sig)) {
return true;
} else if ((action == uint32(ISettlerActions.METATXN_UNISWAPV4_VIP.selector))
.or(action == uint32(ISettlerActions.METATXN_EKUBOV3_VIP.selector))) {
.or(action == uint32(ISettlerActions.METATXN_EKUBOV3_VIP.selector))
.or(action == uint32(ISettlerActions.METATXN_PANCAKE_INFINITY_VIP.selector))) {
(
address recipient,
ISignatureTransfer.PermitTransferFrom memory permit,
Expand All @@ -47,8 +48,10 @@ contract RobinHoodSettlerMetaTxn is SettlerMetaTxn, RobinHoodMixin {

if (action == uint32(ISettlerActions.METATXN_UNISWAPV4_VIP.selector)) {
sellToUniswapV4VIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
} else { // if (action == uint32(ISettlerActions.METATXN_EKUBOV3_VIP.selector))
} else if (action == uint32(ISettlerActions.METATXN_EKUBOV3_VIP.selector)) {
sellToEkuboV3VIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
} else { // if (action == uint32(ISettlerActions.METATXN_PANCAKE_INFINITY_VIP.selector))
sellToPancakeInfinityVIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
}
} else {
return false;
Expand Down
7 changes: 5 additions & 2 deletions src/chains/RobinHood/TakerSubmitted.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ contract RobinHoodSettler is Settler, RobinHoodMixin {
if (super._dispatchVIP(action, data)) {
return true;
} else if ((action == uint32(ISettlerActions.UNISWAPV4_VIP.selector))
.or(action == uint32(ISettlerActions.EKUBOV3_VIP.selector))) {
.or(action == uint32(ISettlerActions.EKUBOV3_VIP.selector))
.or(action == uint32(ISettlerActions.PANCAKE_INFINITY_VIP.selector))) {
(
address recipient,
ISignatureTransfer.PermitTransferFrom memory permit,
Expand All @@ -41,8 +42,10 @@ contract RobinHoodSettler is Settler, RobinHoodMixin {

if (action == uint32(ISettlerActions.UNISWAPV4_VIP.selector)) {
sellToUniswapV4VIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
} else { // if (action == uint32(ISettlerActions.EKUBOV3_VIP.selector))
} else if (action == uint32(ISettlerActions.EKUBOV3_VIP.selector)) {
sellToEkuboV3VIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
} else { // if (action == uint32(ISettlerActions.PANCAKE_INFINITY_VIP.selector))
sellToPancakeInfinityVIP(recipient, feeOnTransfer, hashMul, hashMod, fills, permit, sig, amountOutMin);
}
} else {
return false;
Expand Down
44 changes: 23 additions & 21 deletions src/core/PancakeInfinity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {SettlerSwapAbstract} from "../SettlerAbstract.sol";
import {UnsafeMath} from "../utils/UnsafeMath.sol";
import {Panic} from "../utils/Panic.sol";
import {Ternary} from "../utils/Ternary.sol";
import {ZeroSellAmount, UnknownPoolManagerId} from "./SettlerErrors.sol";
import {ZeroSellAmount, UnknownPoolManagerId, revertUnknownPoolManagerId} from "./SettlerErrors.sol";

import {CreditDebt, Encoder, NotePtr, NotesLib, State, Decoder, Take} from "./FlashAccountingCommon.sol";
import {BalanceDelta} from "./UniswapV4Types.sol";
Expand Down Expand Up @@ -37,8 +37,6 @@ interface IPancakeInfinityVault {
function lock(bytes calldata data) external returns (bytes memory);
}

IPancakeInfinityVault constant VAULT = IPancakeInfinityVault(0x238a358808379702088667322f80aC48bAd5e6c4);

/// @notice Interface for the callback executed when an address locks the vault
interface IPancakeInfinityLockCallback {
/// @notice Called by the pool manager on `msg.sender` when a lock is acquired
Expand Down Expand Up @@ -92,9 +90,6 @@ interface IPancakeInfinityCLPoolManager is IPancakeInfinityPoolManager {
returns (BalanceDelta delta);
}

IPancakeInfinityCLPoolManager constant CL_MANAGER =
IPancakeInfinityCLPoolManager(0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b);

interface IPancakeInfinityBinPoolManager is IPancakeInfinityPoolManager {
/// @notice Peform a swap to a pool
/// @param key The pool key
Expand Down Expand Up @@ -194,9 +189,6 @@ library UnsafePancakeInfinityBinPoolManager {
}
}

IPancakeInfinityBinPoolManager constant BIN_MANAGER =
IPancakeInfinityBinPoolManager(0xC697d2898e0D09264376196696c51D7aBbbAA4a9);

abstract contract PancakeInfinity is SettlerSwapAbstract {
using UnsafeMath for uint256;
using UnsafeMath for int256;
Expand All @@ -214,6 +206,14 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {
assert(address(ETH_ADDRESS) == NotesLib.ETH_ADDRESS);
}

function _PANCAKE_INFINITY_VAULT() internal pure virtual returns (address);

// A chain hosting a fork that lacks CL_MANAGER must unconditionally call `revertUnknownPoolManagerId(0)`
function _PANCAKE_INFINITY_CL_MANAGER() internal pure virtual returns (address);

// A chain hosting a fork that lacks BIN_MANAGER must unconditionally call `revertUnknownPoolManagerId(1)`
function _PANCAKE_INFINITY_BIN_MANAGER() internal pure virtual returns (address);

//// How to generate `fills` for Pancake Infinity:
////
//// Linearize your DAG of fills by doing a topological sort on the tokens involved. In the
Expand Down Expand Up @@ -279,7 +279,10 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {
amountOutMin
);
bytes memory encodedBuyAmount = _setOperatorAndCall(
address(VAULT), data, uint32(IPancakeInfinityLockCallback.lockAcquired.selector), _pancakeInfinityCallback
_PANCAKE_INFINITY_VAULT(),
data,
uint32(IPancakeInfinityLockCallback.lockAcquired.selector),
_pancakeInfinityCallback
);
// buyAmount = abi.decode(abi.decode(encodedBuyAmount, (bytes)), (uint256));
assembly ("memory-safe") {
Expand Down Expand Up @@ -313,7 +316,10 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {
amountOutMin
);
bytes memory encodedBuyAmount = _setOperatorAndCall(
address(VAULT), data, uint32(IPancakeInfinityLockCallback.lockAcquired.selector), _pancakeInfinityCallback
_PANCAKE_INFINITY_VAULT(),
data,
uint32(IPancakeInfinityLockCallback.lockAcquired.selector),
_pancakeInfinityCallback
);
// buyAmount = abi.decode(abi.decode(encodedBuyAmount, (bytes)), (uint256));
assembly ("memory-safe") {
Expand Down Expand Up @@ -475,7 +481,7 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {
}

{
uint8 poolManagerId;
uint256 poolManagerId;
assembly ("memory-safe") {
poolManagerId := shr(0xf8, calldataload(data.offset))
data.offset := add(0x01, data.offset)
Expand Down Expand Up @@ -506,17 +512,17 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {

Decoder.overflowCheck(data);

if (uint256(poolManagerId) == 0) {
poolKey.poolManager = CL_MANAGER;
if (poolManagerId == 0) {
poolKey.poolManager = IPancakeInfinityPoolManager(_PANCAKE_INFINITY_CL_MANAGER());

_pancakeInfinitySettleDelta(
state,
IPancakeInfinityCLPoolManager(address(poolKey.poolManager))
.unsafeSwap(poolKey, zeroForOne, amountSpecified, sqrtPriceLimitX96, hookData),
zeroForOne
);
} else if (uint256(poolManagerId) == 1) {
poolKey.poolManager = BIN_MANAGER;
} else if (poolManagerId == 1) {
poolKey.poolManager = IPancakeInfinityPoolManager(_PANCAKE_INFINITY_BIN_MANAGER());
if (amountSpecified >> 127 != amountSpecified >> 128) {
Panic.panic(Panic.ARITHMETIC_OVERFLOW);
}
Expand All @@ -527,11 +533,7 @@ abstract contract PancakeInfinity is SettlerSwapAbstract {
zeroForOne
);
} else {
assembly ("memory-safe") {
mstore(0x00, 0x0a9a7da6) // selector for `UnknownPoolManagerId(uint8)`
mstore(0x20, and(0xff, poolManagerId))
revert(0x1c, 0x24)
}
revertUnknownPoolManagerId(poolManagerId);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/core/SettlerErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ error NotConverged();
/// the list of recognized pool managers.
error UnknownPoolManagerId(uint8 poolManagerId);

function revertUnknownPoolManagerId(uint256 poolManagerId) pure {
assembly ("memory-safe") {
mstore(0x00, 0x0a9a7da6) // selector for `UnknownPoolManagerId(uint8)`
mstore(0x20, and(0xff, poolManagerId))
revert(0x1c, 0x24)
}
}

/// @notice Thrown when the `msg.value` is less than the minimum expected value.
error Underpayment(uint256 msgValueMin, uint256 msgValueActual);

Expand Down
6 changes: 6 additions & 0 deletions src/core/pancakeInfinityForks/OrvexCL.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

// Orvex is a fork of PancakeSwap Infinity without the Bin pool manager
address constant orvexVault = 0xFe7E25dE55e5cBbEcCcb661F3679F873f72B9b0D;
address constant orvexClManager = 0xd01C774d4A66408326Bc65728Ac5Ae5aAf004032;
6 changes: 6 additions & 0 deletions src/core/pancakeInfinityForks/PancakeInfinity.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

address constant pancakeInfinityVault = 0x238a358808379702088667322f80aC48bAd5e6c4;
address constant pancakeInfinityClManager = 0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b;
address constant pancakeInfinityBinManager = 0xC697d2898e0D09264376196696c51D7aBbbAA4a9;
28 changes: 12 additions & 16 deletions test/integration/PancakeInfinity.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@ import {tmp} from "src/utils/512Math.sol";

import {SettlerMetaTxnPairTest} from "./SettlerMetaTxnPairTest.t.sol";
import {AllowanceHolderPairTest} from "./AllowanceHolderPairTest.t.sol";
import {PoolKey, PoolId, IPancakeInfinityPoolManager} from "src/core/PancakeInfinity.sol";
import {
PancakeInfinity,
PoolKey,
VAULT,
CL_MANAGER,
BIN_MANAGER,
PoolId,
IPancakeInfinityPoolManager
} from "src/core/PancakeInfinity.sol";
pancakeInfinityVault,
pancakeInfinityClManager,
pancakeInfinityBinManager
} from "src/core/pancakeInfinityForks/PancakeInfinity.sol";

interface IPancakeInfinityCLPoolManagerSlot0 {
function getSlot0(bytes32 poolId) external view returns (uint160 sqrtPriceX96);
Expand Down Expand Up @@ -90,9 +87,9 @@ abstract contract PancakeInfinityTest is AllowanceHolderPairTest, SettlerMetaTxn
}

function _setPancakeInfinityLabels() private {
vm.label(address(VAULT), "Vault");
vm.label(address(CL_MANAGER), "CLPoolManager");
vm.label(address(BIN_MANAGER), "BINPoolManager");
vm.label(pancakeInfinityVault, "Vault");
vm.label(pancakeInfinityClManager, "CLPoolManager");
vm.label(pancakeInfinityBinManager, "BINPoolManager");
}

function _readSlot0Cold(bytes32 poolId_) private view returns (uint160 sqrtPriceX96) {
Expand All @@ -105,7 +102,7 @@ abstract contract PancakeInfinityTest is AllowanceHolderPairTest, SettlerMetaTxn
}

function _readSlot0AndRevert(bytes32 poolId_) external view {
uint160 sqrtPriceX96 = IPancakeInfinityCLPoolManagerSlot0(address(CL_MANAGER)).getSlot0(poolId_);
uint160 sqrtPriceX96 = IPancakeInfinityCLPoolManagerSlot0(pancakeInfinityClManager).getSlot0(poolId_);
assembly ("memory-safe") {
mstore(0x00, sqrtPriceX96)
revert(0x00, 0x20)
Expand Down Expand Up @@ -138,10 +135,9 @@ abstract contract PancakeInfinityTest is AllowanceHolderPairTest, SettlerMetaTxn
function pancakeInfinityFills(IERC20 fromToken, IERC20 toToken) internal view virtual returns (bytes memory) {
bytes32 poolId_ = poolId();
uint8 managerId = poolManagerId();
PoolKey memory poolKey = (managerId == 0
? IPancakeInfinityPoolManager(CL_MANAGER)
: IPancakeInfinityPoolManager(BIN_MANAGER))
.poolIdToPoolKey(PoolId.wrap(poolId_));
PoolKey memory poolKey = IPancakeInfinityPoolManager(
managerId == 0 ? pancakeInfinityClManager : pancakeInfinityBinManager
).poolIdToPoolKey(PoolId.wrap(poolId_));

return abi.encodePacked(
uint16(10_000),
Expand Down