Contract detail
NativeRFQPool
NativeRFQPool is a verified Robinhood Chain smart contract at 0x42dd6739…a38c7e2d with 1 indexed transactions.
- Address type
- Smart contract
- Transactions
- 1
- First indexed block
- #60122
- Latest indexed block
- #60122
- ERC-20 transfers
- 1
- Emitted events
- 2
- Verification
- Verified
- Compiler
- v0.8.28+commit.7893614a
- Verification match
- full
- Creation transaction
- 0x62b6479d…0108e034
- Created at block
- #60122
- Language
- solidity
- EVM version
- cancun
- License
- none
- Compiler optimization
- Enabled · 1500 runs
Related activity
Transactions
| Transaction | Block | From | To | Value (wei) |
|---|---|---|---|---|
| 0x3b682cf7…f9ed4b47 | #60122 | 0xca22062b…22ab6b4e | 0x42dd6739…a38c7e2d | 0 |
Verified contract
Contract source code
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {EIP712, ECDSA} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "./libraries/ConstantsLib.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {WrappedNLP} from "./WrappedNLP.sol";
import {IWETH9} from "./interfaces/IWETH9.sol";
import {ICreditVault} from "./interfaces/ICreditVault.sol";
import {INativeRFQPool} from "./interfaces/INativeRFQPool.sol";
contract NativeRFQPool is INativeRFQPool, Ownable2Step, EIP712 {
using SafeERC20 for IERC20;
using SafeCast for int256;
using SafeCast for uint256;
using Address for address payable;
/// @notice Flag to indicate if this pool uses credit vault funds for market making
/// @dev If true, market maker uses credit vault funds for quotes. If false, market maker uses their own funds
bool public immutable isCreditPool;
/// @notice The Pool name
/// @dev Every market maker has their own pool if they use their own funds to quote
string public name;
/// @notice Address of the router contract that can execute trades
address public router;
/// @dev Address of the Wrapped Ether (WETH9) contract
address public immutable WETH9;
/// @notice Address that holds and manages tokens - points to CreditVault if isCreditPool is true, otherwise points to market maker's own account
address public treasury;
/// @notice Mapping to track used nonces for preventing replay attacks
mapping(uint256 => bool) public nonces;
/// @notice Mapping of authorized market makers who can sign RFQ quotes
mapping(address => bool) public rfqSigners;
/// @notice Mapping to track wrapped token status (e.g., WrappedNLP)
mapping(address => bool) public isWrapToken;
/*//////////////////////////////////////////////////////////////////////////
RECEIVE FUNCTION
//////////////////////////////////////////////////////////////////////////*/
receive() external payable {
require(msg.sender == WETH9, ErrorsLib.OnlyWETH9());
}
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
constructor(
bool _isCreditPool,
address _WETH9,
address _router,
address _treasury,
string memory _name
) EIP712("Native RFQ Pool", "1") {
require(
_WETH9 != address(0) && _router != address(0) && _treasury != address(0) && bytes(_name).length > 0,
ErrorsLib.ZeroInput()
);
isCreditPool = _isCreditPool;
name = _name;
router = _router;
WETH9 = _WETH9;
treasury = _treasury;
}
/*//////////////////////////////////////////////////////////////////////////
RFQ TRADING
//////////////////////////////////////////////////////////////////////////*/
/// @notice See NativeRouter.tradeRFQT for detailed documentation
/// @dev Called by NativeRouter to execute RFQ trades in the pool
function tradeRFQT(uint256 effectiveSellerTokenAmount, address wNLPIn, RFQTQuote memory quote) external override {
require(msg.sender == router, ErrorsLib.OnlyNativeRouter());
// Prevent replay attacks
require(!nonces[quote.nonce], ErrorsLib.NonceUsed());
// Mark nonce as used
nonces[quote.nonce] = true;
// Store original buyerToken address to handle ETH unwrapping if buyerToken is zero address
address originalBuyerToken = quote.buyerToken;
// Handle ETH case: convert zero address to WETH9 for buyer or seller token
quote.buyerToken = quote.buyerToken == address(0) ? WETH9 : quote.buyerToken;
quote.sellerToken = quote.sellerToken == address(0) ? WETH9 : quote.sellerToken;
// Verify market maker signature
_verifyPMMSignature(quote);
uint256 buyerTokenAmount = effectiveSellerTokenAmount != quote.sellerTokenAmount
? (effectiveSellerTokenAmount * quote.buyerTokenAmount) / quote.sellerTokenAmount
: quote.buyerTokenAmount;
if (quote.confidenceExtractedValueE > 0 && block.timestamp > quote.confidenceExtractedValueT) {
uint256 boundaryValue =
quote.confidenceExtractedValueN == 0 ? quote.deadlineTimestamp : quote.confidenceExtractedValueN;
uint256 confidence = ((Math.min(block.timestamp, boundaryValue) - quote.confidenceExtractedValueT) * 1e6)
/ (boundaryValue - quote.confidenceExtractedValueT);
uint256 amplifiedRatio = confidence ** quote.confidenceExtractedValueE;
uint256 baseAmplifier = 1e6 ** quote.confidenceExtractedValueE;
uint256 varianceMetric =
(amplifiedRatio * quote.confidenceExtractedValueM * 10_000) / (baseAmplifier * 10_000);
buyerTokenAmount =
buyerTokenAmount * (10_000 - Math.min(varianceMetric, quote.confidenceExtractedValueM)) / 10_000;
}
require(
buyerTokenAmount >= quote.amountOutMinimum,
ErrorsLib.NotEnoughAmountOut(buyerTokenAmount, quote.amountOutMinimum)
);
_transferAndCallback(
quote.signer,
quote.recipient,
quote.buyerToken,
originalBuyerToken,
wNLPIn,
quote.sellerToken,
effectiveSellerTokenAmount,
buyerTokenAmount
);
emit RFQTrade(
quote.recipient,
quote.sellerToken,
quote.buyerToken,
effectiveSellerTokenAmount,
buyerTokenAmount,
quote.quoteId,
quote.signer
);
}
/*//////////////////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Updates the treasury address that receives token
/// @param newTreasury The new treasury address
function setTreasury(address newTreasury) public onlyOwner {
require(newTreasury != address(0), ErrorsLib.ZeroAddress());
treasury = newTreasury;
emit TreasurySet(newTreasury);
}
/// @notice Adds or removes a market maker's authorization to sign quotes
/// @param signer The market maker's address
/// @param isSigner True to authorize, false to revoke
function setSigner(address signer, bool isSigner) external onlyOwner {
require(signer != address(0), ErrorsLib.ZeroAddress());
rfqSigners[signer] = isSigner;
emit SignerUpdated(signer, isSigner);
}
/// @notice Updates wrap token status
/// @param token The address of the wrap token
/// @param isWrap Whether the token is a wrap token
function setWrapToken(address token, bool isWrap) external onlyOwner {
require(token != address(0), ErrorsLib.ZeroAddress());
isWrapToken[token] = isWrap;
emit WrapTokenUpdated(token, isWrap);
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
function _transferAndCallback(
address signer,
address recipient,
address buyerToken,
address originalBuyerToken,
address wNLPIn,
address sellerToken,
uint256 effectiveSellerTokenAmount,
uint256 buyerTokenAmount
) internal {
if (isCreditPool) {
ICreditVault(treasury).swapCallback(
signer, sellerToken, effectiveSellerTokenAmount.toInt256(), buyerToken, buyerTokenAmount.toInt256()
);
}
if (wNLPIn != address(0)) {
require(
(isWrapToken[wNLPIn] && address(WrappedNLP(wNLPIn).underlying()) == buyerToken),
ErrorsLib.InvalidWrapToken()
);
_transferFromTreasury(buyerToken, address(this), buyerTokenAmount);
// Approve WrappedNLP contract to spend underlying tokens
IERC20(buyerToken).safeApprove(address(wNLPIn), buyerTokenAmount);
// Deposit underlying tokens and wrap to WrappedNLP tokens for recipient
WrappedNLP(wNLPIn).depositAndWrap(recipient, buyerTokenAmount);
} else {
// Direct transfer to recipient without wrapping
_transferFromTreasury(originalBuyerToken, recipient, buyerTokenAmount);
}
}
/// @dev Helper function to transfer buyerToken from external account.
function _transferFromTreasury(address token, address receiver, uint256 value) private {
if (token == address(0)) {
// slither-disable-next-line arbitrary-send-erc20
IERC20(WETH9).safeTransferFrom(treasury, address(this), value);
IWETH9(WETH9).withdraw(value);
// Skip ETH initial balance check for saving gas
payable(receiver).sendValue(value);
} else {
// slither-disable-next-line arbitrary-send-erc20
IERC20(token).safeTransferFrom(treasury, receiver, value);
}
}
function _verifyPMMSignature(RFQTQuote memory quote) internal view {
require(rfqSigners[quote.signer], ErrorsLib.InvalidSigner());
bytes32 digest = _hashTypedDataV4(
keccak256(
abi.encode(
ORDER_SIGNATURE_HASH,
quote.nonce,
quote.signer,
quote.buyerToken,
quote.sellerToken,
quote.buyerTokenAmount,
quote.sellerTokenAmount,
quote.deadlineTimestamp,
quote.confidenceExtractedValueT,
quote.confidenceExtractedValueN,
quote.confidenceExtractedValueE,
quote.confidenceExtractedValueM,
quote.quoteId
)
)
);
require(quote.signer == ECDSA.recover(digest, quote.signature), ErrorsLib.InvalidSignature());
}
}