RobinhoodPrismNativeRouter

0x47563c2cfd0b3f90ac0aab48cfea9df2945e3180

Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
13,676 bytes
ABI entries
33
5 read · 6 write
License
none

Contract information

Address
0x47563c2cfd0b3f90ac0aab48cfea9df2945e3180
Chain
Robinhood Chain (4663)
Compiler
v0.8.28+commit.7893614a
Optimization
Enabled
Creation tx
0x5b8e8eab2b…9d52978c44

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (5)

PERMIT2()address
POOL_MANAGER()address
PRISM_BUYER()address
REGISTRY()address
UNIVERSAL_ROUTER()address

Events (2)

PrismNativeLaunchPrismNativeTrade

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPrismNativeRouterRegistry",
        "name": "registry_",
        "type": "address"
      },
      {
        "internalType": "contract IPrismNativeRouterBuyer",
        "name": "prismBuyer_",
        "type": "address"
      },
      {
        "internalType": "contract IRobinhoodHelperUniversalRouter",
        "name": "universalRouter_",
        "type": "address"
      },
      {
        "internalType": "contract IRobinhoodHelperPermit2",
        "name": "permit2_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "target",
        "type": "address"
      }
    ],
    "name": "AddressEmptyCode",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "AddressInsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FailedInnerCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidDependency",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidMinimumOutput",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidNativePairPool",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPoolKey",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSwapAmount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSwapDependency",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidToken",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidValue",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ManagerNotApproved",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NativeTransferFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OutputTooLow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "maximum",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "actual",
        "type": "uint256"
      }
    ],
    "name": "SwapInputTooHigh",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "minimum",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "actual",
        "type": "uint256"
      }
    ],
    "name": "SwapOutputTooLow",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "manager",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "nativeIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "pairTokenIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "name": "PrismNativeLaunch",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "manager",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "isBuy",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "postBond",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "nativeAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "pairAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      }
    ],
    "name": "PrismNativeTrade",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "PERMIT2",
    "outputs": [
      {
        "internalType": "contract IRobinhoodHelperPermit2",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POOL_MANAGER",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PRISM_BUYER",
    "outputs": [
      {
        "internalType": "contract IPrismNativeRouterBuyer",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "REGISTRY",
    "outputs": [
      {
        "internalType": "contract IPrismNativeRouterRegistry",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "UNIVERSAL_ROUTER",
    "outputs": [
      {
        "internalType": "contract IRobinhoodHelperUniversalRouter",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenId",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "tokenAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minPairOut",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minFinalOut",
            "type": "uint256"
          },
          {
            "components": [
              {
                "internalType": "Currency",
                "name": "currency0",
                "type": "address"
              },
              {
                "internalType": "Currency",
                "name": "currency1",
                "type": "address"
              },
              {
                "internalType": "uint24",
                "name": "fee",
                "type": "uint24"
              },
              {
                "internalType": "int24",
                "name": "tickSpacing",
                "type": "int24"
              },
              {
                "internalType": "contract IHooks",
                "name": "hooks",
                "type": "address"
              }
            ],
            "internalType": "struct PoolKey",
            "name": "nativePairPool",
            "type": "tuple"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct RobinhoodPrismNativeRouter.TradeRequest",
        "name": "request",
        "type": "tuple"
      }
    ],
    "name": "buyWithNative",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "pairSpent",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint32",
            "name": "a",
            "type": "uint32"
          },
          {
            "internalType": "uint8",
            "name": "b",
            "type": "uint8"
          },
          {
            "internalType": "uint128",
            "name": "curveScaler",
            "type": "uint128"
          },
          {
            "internalType": "uint8",
            "name": "creatorFeeBasisPoints",
            "type": "uint8"
          },
          {
            "internalType": "address",
            "name": "tokenCreatorAddress",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenSplit",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "enableHolderRewards",
            "type": "bool"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          }
        ],
        "internalType": "struct IPrismNativeRouterBuyer.CreationParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "nativePairPool",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minPairOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minTokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "createAndBuyWithNative",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint32",
            "name": "a",
            "type": "uint32"
          },
          {
            "internalType": "uint8",
            "name": "b",
            "type": "uint8"
          },
          {
            "internalType": "uint128",
            "name": "curveScaler",
            "type": "uint128"
          },
          {
            "internalType": "uint8",
            "name": "creatorFeeBasisPoints",
            "type": "uint8"
          },
          {
            "internalType": "address",
            "name": "tokenCreatorAddress",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenSplit",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "enableHolderRewards",
            "type": "bool"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          }
        ],
        "internalType": "struct IPrismNativeRouterBuyer.CreationParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "nativePairPool",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minPairOut",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "address[]",
            "name": "addresses",
            "type": "address[]"
          },
          {
            "internalType": "uint256",
            "name": "startTsOffset",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "duration",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "transferLimit",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "balanceLimit",
            "type": "uint256"
          }
        ],
        "internalType": "struct IRobinhoodLaunchToken.Whitelist",
        "name": "whitelist",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "minTokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "createAndBuyWithNativeWithWhitelist",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint32",
            "name": "a",
            "type": "uint32"
          },
          {
            "internalType": "uint8",
            "name": "b",
            "type": "uint8"
          },
          {
            "internalType": "uint128",
            "name": "curveScaler",
            "type": "uint128"
          },
          {
            "internalType": "uint8",
            "name": "creatorFeeBasisPoints",
            "type": "uint8"
          },
          {
            "internalType": "address",
            "name": "tokenCreatorAddress",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenSplit",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "enableHolderRewards",
            "type": "bool"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          }
        ],
        "internalType": "struct IPrismNativeRouterBuyer.CreationParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "nativePairPool",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "minPairOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minPostBondTokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "launchAndBuyWithNative",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint32",
            "name": "a",
            "type": "uint32"
          },
          {
            "internalType": "uint8",
            "name": "b",
            "type": "uint8"
          },
          {
            "internalType": "uint128",
            "name": "curveScaler",
            "type": "uint128"
          },
          {
            "internalType": "uint8",
            "name": "creatorFeeBasisPoints",
            "type": "uint8"
          },
          {
            "internalType": "address",
            "name": "tokenCreatorAddress",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenSplit",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "enableHolderRewards",
            "type": "bool"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          }
        ],
        "internalType": "struct IPrismNativeRouterBuyer.CreationParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "nativePairPool",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "minPairOut",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "address[]",
            "name": "addresses",
            "type": "address[]"
          },
          {
            "internalType": "uint256",
            "name": "startTsOffset",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "duration",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "transferLimit",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "balanceLimit",
            "type": "uint256"
          }
        ],
        "internalType": "struct IRobinhoodLaunchToken.Whitelist",
        "name": "whitelist",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "minPostBondTokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "launchAndBuyWithNativeWithWhitelist",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "manager",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenId",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "tokenAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minPairOut",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minFinalOut",
            "type": "uint256"
          },
          {
            "components": [
              {
                "internalType": "Currency",
                "name": "currency0",
                "type": "address"
              },
              {
                "internalType": "Currency",
                "name": "currency1",
                "type": "address"
              },
              {
                "internalType": "uint24",
                "name": "fee",
                "type": "uint24"
              },
              {
                "internalType": "int24",
                "name": "tickSpacing",
                "type": "int24"
              },
              {
                "internalType": "contract IHooks",
                "name": "hooks",
                "type": "address"
              }
            ],
            "internalType": "struct PoolKey",
            "name": "nativePairPool",
            "type": "tuple"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct RobinhoodPrismNativeRouter.TradeRequest",
        "name": "request",
        "type": "tuple"
      }
    ],
    "name": "sellToNative",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "nativeOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "pairOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";

import {IRobinhoodLaunchToken} from "../robinhood/interfaces/IRobinhoodLaunchToken.sol";
import {IArenaPoolDeployer} from "../robinhood/interfaces/IArenaPoolDeployer.sol";
import {
    IRobinhoodArenaManagerHelper,
    IRobinhoodHelperPermit2,
    IRobinhoodHelperUniversalRouter
} from "../helpers/RobinhoodHelperInterfaces.sol";
import {RobinhoodV4SwapExecutor} from "../helpers/RobinhoodV4SwapExecutor.sol";

interface IPrismNativeRouterRegistry {
    function isApprovedManager(address manager) external view returns (bool);
    function pairTokenForManager(address manager) external view returns (address);
}

interface IPrismNativeRouterBuyer {
    struct CreationParams {
        address manager;
        uint32 a;
        uint8 b;
        uint128 curveScaler;
        uint8 creatorFeeBasisPoints;
        address tokenCreatorAddress;
        uint256 tokenSplit;
        bool enableHolderRewards;
        string name;
        string symbol;
    }

    function bondAndBuyFromLpOnCreation(
        CreationParams calldata params,
        uint256 pairTokenToSpend,
        uint256 minPostBondTokenOut,
        uint256 deadline
    ) external returns (address token, uint256 tokenOut);

    function bondAndBuyFromLpOnCreationWithWhitelist(
        CreationParams calldata params,
        uint256 pairTokenToSpend,
        IRobinhoodLaunchToken.Whitelist calldata whitelist,
        uint256 minPostBondTokenOut,
        uint256 deadline
    ) external returns (address token, uint256 tokenOut);
}

/// @notice ETH entry/exit router for every registry-approved Prism pair.
/// @dev A manager must authorize this router as its native helper for pre-bond
///      buys/sells. Post-bond and atomic-launch routes need no manager custody.
contract RobinhoodPrismNativeRouter is RobinhoodV4SwapExecutor, ReentrancyGuard {
    using SafeERC20 for IERC20;

    struct TradeRequest {
        address manager;
        uint256 tokenId;
        uint256 tokenAmount;
        uint256 minPairOut;
        uint256 minFinalOut;
        PoolKey nativePairPool;
        uint256 deadline;
    }

    IPrismNativeRouterRegistry public immutable REGISTRY;
    IPrismNativeRouterBuyer public immutable PRISM_BUYER;

    error InvalidDependency();
    error ManagerNotApproved();
    error InvalidNativePairPool();
    error InvalidToken();
    error InvalidValue();
    error OutputTooLow();
    error NativeTransferFailed();

    event PrismNativeTrade(
        address indexed user,
        address indexed manager,
        address indexed token,
        uint256 tokenId,
        bool isBuy,
        bool postBond,
        uint256 nativeAmount,
        uint256 pairAmount,
        uint256 tokenAmount
    );

    event PrismNativeLaunch(
        address indexed user,
        address indexed manager,
        address indexed token,
        uint256 nativeIn,
        uint256 pairTokenIn,
        uint256 tokenOut
    );

    /// @notice Creates a Prism token from native ETH and buys an arbitrary
    ///         amount on the curve. If that purchase graduates the token, any
    ///         paired-token remainder is bought through the newly created pool.
    function createAndBuyWithNative(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 tokenAmount,
        uint256 minPairOut,
        uint256 minTokenOut,
        uint256 deadline
    ) external payable nonReentrant returns (address token, uint256 tokenOut) {
        IRobinhoodLaunchToken.Whitelist memory whitelist;
        return _createAndBuy(
            params, nativePairPool, tokenAmount, minPairOut, minTokenOut,
            deadline, whitelist, false
        );
    }

    function createAndBuyWithNativeWithWhitelist(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 tokenAmount,
        uint256 minPairOut,
        IRobinhoodLaunchToken.Whitelist calldata whitelist,
        uint256 minTokenOut,
        uint256 deadline
    ) external payable nonReentrant returns (address token, uint256 tokenOut) {
        return _createAndBuy(
            params, nativePairPool, tokenAmount, minPairOut, minTokenOut,
            deadline, whitelist, true
        );
    }

    constructor(
        IPrismNativeRouterRegistry registry_,
        IPrismNativeRouterBuyer prismBuyer_,
        IRobinhoodHelperUniversalRouter universalRouter_,
        IRobinhoodHelperPermit2 permit2_
    ) RobinhoodV4SwapExecutor(universalRouter_, permit2_) {
        if (
            address(registry_) == address(0) || address(registry_).code.length == 0
                || address(prismBuyer_) == address(0) || address(prismBuyer_).code.length == 0
        ) revert InvalidDependency();
        REGISTRY = registry_;
        PRISM_BUYER = prismBuyer_;
    }

    function buyWithNative(TradeRequest calldata request)
        external
        payable
        nonReentrant
        returns (uint256 tokenOut, uint256 pairSpent)
    {
        if (msg.value == 0) revert InvalidValue();
        (IRobinhoodArenaManagerHelper manager, IERC20 pairToken, address token) =
            _validatedTrade(request);
        _validateNativePairPool(request.nativePairPool, address(pairToken));

        uint256 pairBefore = pairToken.balanceOf(address(this));
        uint256 pairOut = _swapExactInputSingle(
            request.nativePairPool,
            _zeroForOne(address(0), address(pairToken), request.nativePairPool),
            msg.value,
            request.minPairOut,
            request.deadline
        );

        uint256 tokenBefore = IERC20(token).balanceOf(address(this));
        bool postBond = manager.getTokenParameters(request.tokenId).lpDeployed;
        if (postBond) {
            tokenOut = _swapExactInputSingle(
                _launchPoolKey(manager, address(pairToken), token),
                address(pairToken) < token,
                pairOut,
                request.minFinalOut,
                request.deadline
            );
        } else {
            pairToken.forceApprove(address(manager), pairOut);
            manager.buyAndCreateLpIfPossibleWithUser(
                request.tokenAmount, request.tokenId, msg.sender, pairOut
            );
            tokenOut = IERC20(token).balanceOf(address(this)) - tokenBefore;
            pairToken.forceApprove(address(manager), 0);
            if (tokenOut < request.minFinalOut) revert OutputTooLow();
        }

        IERC20(token).safeTransfer(msg.sender, tokenOut);
        uint256 pairRefund = pairToken.balanceOf(address(this)) - pairBefore;
        pairSpent = pairOut - pairRefund;
        if (pairRefund != 0) pairToken.safeTransfer(msg.sender, pairRefund);
        emit PrismNativeTrade(
            msg.sender, address(manager), token, request.tokenId, true,
            postBond, msg.value, pairSpent, tokenOut
        );
    }

    function sellToNative(TradeRequest calldata request)
        external
        nonReentrant
        returns (uint256 nativeOut, uint256 pairOut)
    {
        (IRobinhoodArenaManagerHelper manager, IERC20 pairToken, address token) =
            _validatedTrade(request);
        _validateNativePairPool(request.nativePairPool, address(pairToken));
        IERC20(token).safeTransferFrom(msg.sender, address(this), request.tokenAmount);

        bool postBond = manager.getTokenParameters(request.tokenId).lpDeployed;
        if (postBond) {
            pairOut = _swapExactInputSingle(
                _launchPoolKey(manager, address(pairToken), token),
                token < address(pairToken),
                request.tokenAmount,
                request.minPairOut,
                request.deadline
            );
        } else {
            IERC20(token).forceApprove(address(manager), request.tokenAmount);
            uint256 pairBefore = pairToken.balanceOf(address(this));
            manager.sellWithUser(request.tokenAmount, request.tokenId, msg.sender, request.minPairOut);
            pairOut = pairToken.balanceOf(address(this)) - pairBefore;
            IERC20(token).forceApprove(address(manager), 0);
        }

        nativeOut = _swapExactInputSingle(
            request.nativePairPool,
            _zeroForOne(address(pairToken), address(0), request.nativePairPool),
            pairOut,
            request.minFinalOut,
            request.deadline
        );
        (bool success,) = payable(msg.sender).call{value: nativeOut}("");
        if (!success) revert NativeTransferFailed();
        emit PrismNativeTrade(
            msg.sender, address(manager), token, request.tokenId, false,
            postBond, nativeOut, pairOut, request.tokenAmount
        );
    }

    function launchAndBuyWithNative(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 minPairOut,
        uint256 minPostBondTokenOut,
        uint256 deadline
    ) external payable nonReentrant returns (address token, uint256 tokenOut) {
        IRobinhoodLaunchToken.Whitelist memory whitelist;
        return _launch(params, nativePairPool, minPairOut, minPostBondTokenOut, deadline, whitelist, false);
    }

    function launchAndBuyWithNativeWithWhitelist(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 minPairOut,
        IRobinhoodLaunchToken.Whitelist calldata whitelist,
        uint256 minPostBondTokenOut,
        uint256 deadline
    ) external payable nonReentrant returns (address token, uint256 tokenOut) {
        return _launch(params, nativePairPool, minPairOut, minPostBondTokenOut, deadline, whitelist, true);
    }

    function _launch(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 minPairOut,
        uint256 minPostBondTokenOut,
        uint256 deadline,
        IRobinhoodLaunchToken.Whitelist memory whitelist,
        bool withWhitelist
    ) internal returns (address token, uint256 tokenOut) {
        if (msg.value == 0) revert InvalidValue();
        IERC20 pairToken = IERC20(_approvedPairToken(params.manager));
        _validateNativePairPool(nativePairPool, address(pairToken));
        uint256 pairBefore = pairToken.balanceOf(address(this));
        uint256 pairOut = _swapExactInputSingle(
            nativePairPool,
            _zeroForOne(address(0), address(pairToken), nativePairPool),
            msg.value,
            minPairOut,
            deadline
        );
        pairToken.forceApprove(address(PRISM_BUYER), pairOut);
        if (withWhitelist) {
            (token, tokenOut) = PRISM_BUYER.bondAndBuyFromLpOnCreationWithWhitelist(
                params, pairOut, whitelist, minPostBondTokenOut, deadline
            );
        } else {
            (token, tokenOut) = PRISM_BUYER.bondAndBuyFromLpOnCreation(
                params, pairOut, minPostBondTokenOut, deadline
            );
        }
        pairToken.forceApprove(address(PRISM_BUYER), 0);
        IERC20(token).safeTransfer(msg.sender, tokenOut);
        uint256 refund = pairToken.balanceOf(address(this)) - pairBefore;
        if (refund != 0) pairToken.safeTransfer(msg.sender, refund);
        emit PrismNativeLaunch(msg.sender, params.manager, token, msg.value, pairOut - refund, tokenOut);
    }

    function _createAndBuy(
        IPrismNativeRouterBuyer.CreationParams calldata params,
        PoolKey calldata nativePairPool,
        uint256 tokenAmount,
        uint256 minPairOut,
        uint256 minTokenOut,
        uint256 deadline,
        IRobinhoodLaunchToken.Whitelist memory whitelist,
        bool withWhitelist
    ) internal returns (address token, uint256 tokenOut) {
        if (msg.value == 0 || tokenAmount == 0) revert InvalidValue();
        IERC20 pairToken = IERC20(_approvedPairToken(params.manager));
        _validateNativePairPool(nativePairPool, address(pairToken));
        IRobinhoodArenaManagerHelper manager = IRobinhoodArenaManagerHelper(params.manager);
        manager.setNextLaunchHolderRewards(params.enableHolderRewards);
        uint256 tokenId = manager.tokenIdentifier();
        uint256 pairBefore = pairToken.balanceOf(address(this));
        uint256 pairOut = _swapExactInputSingle(
            nativePairPool,
            _zeroForOne(address(0), address(pairToken), nativePairPool),
            msg.value,
            minPairOut,
            deadline
        );
        pairToken.forceApprove(params.manager, pairOut);
        if (withWhitelist) {
            manager.createTokenWithWL(
                params.a, params.b, params.curveScaler,
                params.creatorFeeBasisPoints, params.tokenCreatorAddress,
                params.tokenSplit, params.name, params.symbol, tokenAmount,
                whitelist
            );
        } else {
            manager.createToken(
                params.a, params.b, params.curveScaler,
                params.creatorFeeBasisPoints, params.tokenCreatorAddress,
                params.tokenSplit, params.name, params.symbol, tokenAmount
            );
        }
        pairToken.forceApprove(params.manager, 0);
        IRobinhoodArenaManagerHelper.TokenParameters memory created =
            manager.getTokenParameters(tokenId);
        token = created.tokenContractAddress;
        tokenOut = IERC20(token).balanceOf(address(this));
        uint256 pairRemainder = pairToken.balanceOf(address(this)) - pairBefore;
        if (created.lpDeployed && pairRemainder != 0) {
            tokenOut += _swapExactInputSingle(
                _launchPoolKey(manager, address(pairToken), token),
                address(pairToken) < token,
                pairRemainder,
                1,
                deadline
            );
            pairRemainder = pairToken.balanceOf(address(this)) - pairBefore;
        }
        if (tokenOut < minTokenOut) revert OutputTooLow();
        IERC20(token).safeTransfer(msg.sender, tokenOut);
        if (pairRemainder != 0) pairToken.safeTransfer(msg.sender, pairRemainder);
        emit PrismNativeLaunch(
            msg.sender, params.manager, token, msg.value,
            pairOut - pairRemainder, tokenOut
        );
    }

    function _validatedTrade(TradeRequest calldata request)
        internal
        view
        returns (IRobinhoodArenaManagerHelper manager, IERC20 pairToken, address token)
    {
        address pair = _approvedPairToken(request.manager);
        manager = IRobinhoodArenaManagerHelper(request.manager);
        if (manager.ARENA_ADDRESS() != pair) revert ManagerNotApproved();
        pairToken = IERC20(pair);
        token = manager.getTokenParameters(request.tokenId).tokenContractAddress;
        if (token == address(0) || token.code.length == 0) revert InvalidToken();
    }

    function _approvedPairToken(address manager) internal view returns (address pairToken) {
        if (!REGISTRY.isApprovedManager(manager)) revert ManagerNotApproved();
        pairToken = REGISTRY.pairTokenForManager(manager);
        if (pairToken == address(0)) revert ManagerNotApproved();
    }

    function _validateNativePairPool(PoolKey calldata key, address pairToken) internal pure {
        address currency0 = Currency.unwrap(key.currency0);
        address currency1 = Currency.unwrap(key.currency1);
        if (!((currency0 == address(0) && currency1 == pairToken) || (currency1 == address(0) && currency0 == pairToken))) {
            revert InvalidNativePairPool();
        }
    }

    function _zeroForOne(address input, address output, PoolKey calldata key)
        internal pure returns (bool)
    {
        return Currency.unwrap(key.currency0) == input
            && Currency.unwrap(key.currency1) == output;
    }

    function _launchPoolKey(
        IRobinhoodArenaManagerHelper manager,
        address pairToken,
        address launchToken
    ) internal view returns (PoolKey memory) {
        IArenaPoolDeployer.PoolInitParams memory config =
            manager.getV4PoolInitParams().poolInitParams;
        address token0 = pairToken < launchToken ? pairToken : launchToken;
        address token1 = pairToken < launchToken ? launchToken : pairToken;
        return PoolKey({
            currency0: Currency.wrap(token0),
            currency1: Currency.wrap(token1),
            fee: config.fee,
            tickSpacing: config.tickSpacing,
            hooks: IHooks(config.hookContract)
        });
    }
}
Chain explorer2259msChain node84ms