Contract Name:
WrappedNative
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./interfaces/IWrappedNative.sol";
import "./interfaces/IWrappedNativeExtended.sol";
string constant VERSION = "1";
string constant NAME = "Wrapped Native";
string constant SYMBOL = "WNATIVE";
uint8 constant DECIMALS = 18;
bytes32 constant UPPER_BIT_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 constant ZERO = 0;
uint256 constant ONE = 1;
uint256 constant INFRASTRUCTURE_TAX_THRESHOLD = 9; // When convenienceFeeBps is greater than 9, we apply infrastructure tax to convenience fee.
uint256 constant INFRASTRUCTURE_TAX_BPS = 10_00;
uint256 constant FEE_DENOMINATOR = 100_00;
uint256 constant WITHDRAWAL_EVENT_TOPIC_0 = 0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65;
uint256 constant DEPOSIT_EVENT_TOPIC_0 = 0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c;
uint256 constant APPROVAL_EVENT_TOPIC_0 = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
uint256 constant TRANSFER_EVENT_TOPIC_0 = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
uint256 constant PERMIT_NONCE_INVALIDATED_EVENT_TOPIC_0 = 0x8dc5a0b2e80f26187d38744e9559150e3bd6e06fccefbe737fd33411cfb15151;
uint256 constant MASTER_NONCE_INVALIDATED_EVENT_TOPIC_0 = 0x9614574d6542397172c19ba2bf4588434feeb977576e92b7b59b38242ab59609;
bytes32 constant PERMIT_TRANSFER_TYPEHASH =
keccak256("PermitTransfer(address operator,uint256 amount,uint256 nonce,uint256 expiration,uint256 masterNonce)");
bytes32 constant PERMIT_WITHDRAWAL_TYPEHASH =
keccak256("PermitWithdrawal(address operator,uint256 amount,uint256 nonce,uint256 expiration,uint256 masterNonce,address to,address convenienceFeeReceiver,uint256 convenienceFeeBps)");
bytes4 constant SELECTOR_IS_NONCE_USED = IWrappedNativeExtended.isNonceUsed.selector;
bytes4 constant SELECTOR_MASTER_NONCES = IWrappedNativeExtended.masterNonces.selector;
bytes4 constant SELECTOR_TOTAL_SUPPLY = IWrappedNative.totalSupply.selector;
bytes4 constant SELECTOR_DOMAIN_SEPARATOR_V4 = IWrappedNativeExtended.domainSeparatorV4.selector;
bytes4 constant SELECTOR_NAME = IWrappedNative.name.selector;
bytes4 constant SELECTOR_SYMBOL = IWrappedNative.symbol.selector;
bytes4 constant SELECTOR_DECIMALS = IWrappedNative.decimals.selector;
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./Constants.sol";
import "./utils/EIP712.sol";
import "./utils/Math.sol";
/**
* @title WrappedNative
* @author Limit Break, Inc.
* @notice A contract that wraps native tokens (e.g. Ether) into an ERC-20 token. Designed as a
* canonical replacement for WETH9 that can be deployed to a consistent, deterministic address on all chains.
*
* @notice WrappedNative features the following improvements over WETH9:
*
* @notice - **Deterministically Deployable By Anyone To A Consistent Address On Any Chain!**
* @notice - **More Gas Efficient Operations Than WETH9!**
* @notice - **`approve` and `transfer` functions are payable** - will auto-deposit when `msg.value > 0`. This feature
* will allow a user to wrap and approve a protocol in a single action instead of two, improving UX and saving gas.
* @notice - **`depositTo`** - allows a depositor to specify the address to give WNATIVE to.
* Much more gas efficient for operations such as native refunds from protocols compared to `deposit + transfer`.
* @notice - **`withdrawToAccount`** - allows a withdrawer to withdraw to a different address.
* @notice - **`withdrawSplit`** - allows a withdrawer to withdraw and send native tokens to several addresses at once.
* @notice - **Permit Functions** - allows for transfers and withdrawals to be approved to spenders/operators gaslessly using EIP-712 signatures.
* Permitted withdrawals allow gas sponsorship to unwrap wrapped native tokens on the user's behalf, for a small convenience fee specified by the app.
* This is useful when user has no native tokens on a new chain but they have received wrapped native tokens.
*/
contract WrappedNative is EIP712 {
/// @dev Storage of user master nonces for permit processing.
mapping (address => uint256) private _masterNonces;
/// @dev Storage of permit nonces for permit processing. Uses bitmaps for gas-efficient storage.
mapping (address => mapping (uint256 => uint256)) private _permitNonces;
/// @notice Stores the wrapped native token balance of each user.
mapping (address => uint256) public balanceOf;
/// @notice Stores the wrapped native token allowance for each user/spender pair.
mapping (address => mapping (address => uint)) public allowance;
/// @notice Address that will receive infrastructure fee taxes on permit transfers.
address immutable ADDRESS_INFRASTRUCTURE_TAX;
constructor(address infrastructureTaxRecipient) EIP712(NAME, VERSION) {
ADDRESS_INFRASTRUCTURE_TAX = infrastructureTaxRecipient;
}
//=================================================
//== Deposit / Fallback Function Implementations ==
//=================================================
/**
* @notice Fallback function to deposit funds into the contract, or to call various view functions.
* If the `msg.value` is greater than zero, the function will deposit the funds into the
* `msg.sender` account. If the `msg.value` is zero, the function will check the `msg.sig`
* to determine which view function is being called. If a matching function selector is found
* the function will execute and return the appropriate value. If no matching function selector is found,
* the function will revert.
*
* @notice The reason seldom-used view functions have been implemented via fallback is to save gas costs
* elsewhere in the contract in common operations that have a runtime gas cost.
*
* @notice The following function selectors are implemented via fallback:
*
* @notice - **function isNonceUsed(address account, uint256 nonce) external view returns (bool)**
* @notice - **function masterNonces(address account) external view returns (uint256)**
* @notice - **function totalSupply() external view returns (uint256)**
* @notice - **function domainSeparatorV4() external view returns (bytes32)**
* @notice - **function name() external view returns (string)**
* @notice - **function symbol() external view returns (string)**
* @notice - **function decimals() external view returns (uint8)**
*
* @dev Throws when `msg.value` == 0 and the `msg.sig` does not match any of the implemented view functions.
*/
fallback() external payable {
if (msg.value > 0) {
deposit();
} else {
if (msg.sig == SELECTOR_IS_NONCE_USED) { // isNonceUsed(address account, uint256 nonce)
(address account, uint256 nonce) = abi.decode(msg.data[4:], (address,uint256));
bool isUsed = ((_permitNonces[account][uint248(nonce >> 8)] >> uint8(nonce)) & ONE) == ONE;
assembly {
mstore(0x00, isUsed)
return(0x00, 0x20)
}
} else if (msg.sig == SELECTOR_MASTER_NONCES) { // masterNonces(address account)
assembly {
if lt(calldatasize(), 0x24) {
revert(0,0)
}
mstore(0x00, shr(0x60, shl(0x60, calldataload(0x04))))
mstore(0x20, _masterNonces.slot)
mstore(0x00, sload(keccak256(0x00, 0x40)))
return(0x00, 0x20)
}
} else if (msg.sig == SELECTOR_TOTAL_SUPPLY) { // totalSupply()
assembly {
mstore(0x00, selfbalance())
return(0x00, 0x20)
}
} else if (msg.sig == SELECTOR_DOMAIN_SEPARATOR_V4) { // domainSeparatorV4()
bytes32 domainSeparator = _domainSeparatorV4();
assembly {
mstore(0x00, domainSeparator)
return(0x00, 0x20)
}
} else if (msg.sig == SELECTOR_NAME) { // name()
bytes memory nameReturnValue = abi.encode(NAME);
assembly {
return(add(nameReturnValue, 0x20), mload(nameReturnValue))
}
} else if (msg.sig == SELECTOR_SYMBOL) { // symbol()
bytes memory symbolReturnValue = abi.encode(SYMBOL);
assembly {
return(add(symbolReturnValue, 0x20), mload(symbolReturnValue))
}
} else if (msg.sig == SELECTOR_DECIMALS) { // decimals()
assembly {
mstore(0x00, 0x12) // 18
return(0x00, 0x20)
}
} else {
revert();
}
}
}
/**
* @notice Deposits `msg.value` funds into the `msg.sender` account, increasing their wrapped native token balance.
* @notice This function is triggered when native funds are sent to this contract with no calldata.
*/
receive() external payable {
deposit();
}
//=================================================
//========== Basic Deposits / Withdrawals =========
//=================================================
/**
* @notice Deposits `msg.value` funds into the `msg.sender` account, increasing their wrapped native token balance.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has increased by `msg.value`.
* @dev 2. The `msg.sender`'s native token balance has decreased by `msg.value`.
* @dev 3. The `msg.sender`'s wrapped native token balance has increased by `msg.value`.
* @dev 4. A `Deposit` event has been emitted. The `msg.sender` address is logged in the event.
*/
function deposit() public payable {
depositTo(msg.sender);
}
/**
* @notice Deposits `msg.value` funds into specified user's account, increasing their wrapped native token balance.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has increased by `msg.value`.
* @dev 2. The `msg.sender`'s native token balance has decreased by `msg.value`.
* @dev 3. The `to` account's wrapped native token balance has increased by `msg.value`.
* @dev 4. A `Deposit` event has been emitted. Caveat: The `to` address is logged in the event, not `msg.sender`.
*
* @param to The address that receives wrapped native tokens.
*/
function depositTo(address to) public payable {
assembly {
mstore(0x00, to)
mstore(0x20, balanceOf.slot)
let balanceSlot := keccak256(0x00, 0x40)
sstore(balanceSlot, add(sload(balanceSlot), callvalue()))
mstore(0x00, callvalue())
log2(0x00, 0x20, DEPOSIT_EVENT_TOPIC_0, to)
}
}
/**
* @notice Withdraws `amount` funds from the `msg.sender` account, decreasing their wrapped native token balance.
*
* @dev Throws when the `msg.sender`'s wrapped native token balance is less than `amount` to withdraw.
* @dev Throws when the unwrapped native funds cannot be transferred to the `msg.sender` account.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has decreased by `amount`.
* @dev 2. The `msg.sender`'s wrapped native token balance has decreased by `amount`.
* @dev 3. The `msg.sender`'s native token balance has increased by `amount`.
* @dev 4. A `Withdrawal` event has been emitted. The `msg.sender` address is logged in the event.
*
* @param amount The amount of wrapped native tokens to withdraw.
*/
function withdraw(uint256 amount) public {
withdrawToAccount(msg.sender, amount);
}
/**
* @notice Withdraws `amount` funds from the `msg.sender` account, decreasing their wrapped native token balance.
*
* @dev Throws when the `msg.sender`'s wrapped native token balance is less than `amount` to withdraw.
* @dev Throws when the unwrapped native funds cannot be transferred to the `to` account.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has decreased by `amount`.
* @dev 2. The `msg.sender`'s wrapped native token balance has decreased by `amount`.
* @dev 3. The `to` account's native token balance has increased by `amount`.
* @dev 4. A `Withdrawal` event has been emitted. Caveat: The `msg.sender` address is logged in the event, not `to`.
*
* @param to The address that receives the unwrapped native tokens.
* @param amount The amount of wrapped native tokens to withdraw.
*/
function withdrawToAccount(address to, uint256 amount) public {
_withdrawFromAccount(msg.sender, to, amount);
}
/**
* @notice Withdraws funds from the `msg.sender` and splits the funds between multiple receiver addresses.
*
* @dev Throws when the `msg.sender`'s wrapped native token balance is less than the sum of `amounts` to withdraw.
* @dev Throws when the unwrapped native funds cannot be transferred to one or more of the receiver addresses.
* @dev Throws when the `toAddresses` and `amounts` arrays are not the same length.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has decreased by the sum of `amounts`.
* @dev 2. The `msg.sender`'s wrapped native token balance has decreased by the sum of `amounts`.
* @dev 3. The receiver addresses' native token balances have increased by the corresponding amounts in `amounts`.
* @dev 4. A `Withdrawal` event has been emitted for each receiver address. Caveat: The `msg.sender` address is
* logged in the events, not the receiver address.
*
* @param toAddresses The addresses that receive the unwrapped native tokens.
* @param amounts The amounts of wrapped native tokens to withdraw for each receiver address.
*/
function withdrawSplit(address[] calldata toAddresses, uint256[] calldata amounts) external {
if (toAddresses.length != amounts.length) {
revert();
}
for (uint256 i = 0; i < toAddresses.length;) {
withdrawToAccount(toAddresses[i], amounts[i]);
unchecked {
++i;
}
}
}
//=================================================
//========== ERC-20 Approvals & Transfers =========
//=================================================
/**
* @notice Approves `spender` to spend/transfer `amount` of the `msg.sender`'s wrapped native tokens.
* When `amount` is set to `type(uint256).max`, the approval is unlimited.
*
* @notice Unlike a typical ERC-20 token, this function is payable, allowing for a `deposit` and approval to be
* executed simultaneously. If `msg.value` is greater than zero, the function will deposit the funds
* into the `msg.sender` account before approving the `spender` to spend/transfer the funds.
* If `msg.value` is zero, the function will only approve the `spender` to spend/transfer the funds.
* This feature is intended to improve the UX of users using wrapped native tokens so that users don't have
* to perform two transactions to first deposit, then approve the spending of their tokens, saving gas in
* the process.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. The `spender` is approved to spend/transfer `amount` of the `msg.sender`'s wrapped native tokens.
* @dev 2. A `Approval` event has been emitted. The `msg.sender` address, `spender` address, and `amount` of the
* updated approval are logged in the event.
* @dev 3. If `msg.value` is greater than zero, the `msg.sender`'s wrapped native token balance has increased by
* `msg.value`.
* @dev 4. If `msg.value` is greater than zero, a `Deposit` event has been emitted. The `msg.sender` address is
* logged in the event.
*
* @param spender The address that is approved to spend/transfer the `msg.sender`'s wrapped native tokens.
* @param amount The amount of wrapped native tokens that the `spender` is approved to spend/transfer. Approved
* spending is unlimited when this values is set to `type(uint256).max`.
*
* @return Always returns `true`.
*/
function approve(address spender, uint256 amount) public payable returns (bool) {
if (msg.value > 0) {
deposit();
}
assembly {
mstore(0x00, caller())
mstore(0x20, allowance.slot)
mstore(0x20, keccak256(0x00, 0x40))
mstore(0x00, spender)
let allowanceSlot := keccak256(0x00, 0x40)
sstore(allowanceSlot, amount)
mstore(0x00, amount)
log3(0x00, 0x20, APPROVAL_EVENT_TOPIC_0, caller(), spender)
mstore(0x00, 0x01)
return(0x00, 0x20)
}
}
/**
* @notice Transfers an `amount` of wrapped native tokens from the `msg.sender` to the `to` address.
*
* @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `msg.sender` account
* before transferring the wrapped funds. Otherwise, the function will only transfer the funds.
*
* @dev Throws when the `msg.sender` has an insufficient balance to transfer `amount` of wrapped native tokens.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
* @dev 2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
* @dev 3. When `msg.value` is greater than zero, the `msg.sender`'s wrapped native token balance has increased by `msg.value`.
* @dev 4. When `msg.value` is greater than zero, a `Deposit` event has been emitted. The `msg.sender` address is logged in the event.
* @dev 5. The `amount` of wrapped native tokens has been transferred from the `msg.sender` account to the `to` account.
* @dev 6. A `Transfer` event has been emitted. The `msg.sender` address, `to` address, and `amount` are logged in the event.
*
* @param to The address that receives the wrapped native tokens.
* @param amount The amount of wrapped native tokens to transfer.
*
* @return Always returns `true`.
*/
function transfer(address to, uint256 amount) public payable returns (bool) {
return transferFrom(msg.sender, to, amount);
}
/**
* @notice Transfers an `amount` of wrapped native tokens from the `from` to the `to` address.
*
* @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `from` account
* before transferring the wrapped funds. Otherwise, the function will only transfer the funds.
* @notice **As a reminder, the `msg.sender`'s native tokens will be deposited and the `from` (not the `msg.sender`)
* address will be credited before the transfer. Integrating spender/operator protocols MUST be aware that
* deposits made during transfers will not credit their own account.**
*
* @dev Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
* @dev Throws when the `msg.sender` is not the `from` address, and the `msg.sender` has not been approved
* by `from` for an allowance greater than or equal to `amount`.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
* @dev 2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
* @dev 3. When `msg.value` is greater than zero, the `from` account's wrapped native token balance has increased by `msg.value`.
* @dev 4. When `msg.value` is greater than zero, a `Deposit` event has been emitted. The `from` address is logged in the event.
* @dev 5. The `amount` of wrapped native tokens has been transferred from the `from` account to the `to` account.
* @dev 6. A `Transfer` event has been emitted. The `from` address, `to` address, and `amount` are logged in the event.
*
* @param from The address that transfers the wrapped native tokens.
* @param to The address that receives the wrapped native tokens.
* @param amount The amount of wrapped native tokens to transfer.
*
* @return Always returns `true`.
*/
function transferFrom(address from, address to, uint256 amount) public payable returns (bool) {
if (msg.value > 0) {
depositTo(from);
}
assembly {
mstore(0x00, from)
mstore(0x20, balanceOf.slot)
let balanceSlotFrom := keccak256(0x00, 0x40)
let balanceValFrom := sload(balanceSlotFrom)
if lt(balanceValFrom, amount) {
revert(0,0)
}
sstore(balanceSlotFrom, sub(balanceValFrom, amount))
mstore(0x00, to)
let balanceSlotTo := keccak256(0x00, 0x40)
sstore(balanceSlotTo, add(sload(balanceSlotTo), amount))
if iszero(eq(from, caller())) {
mstore(0x00, from)
mstore(0x20, allowance.slot)
mstore(0x20, keccak256(0x00, 0x40))
mstore(0x00, caller())
let allowanceSlot := keccak256(0x00, 0x40)
let allowanceVal := sload(allowanceSlot)
if iszero(eq(allowanceVal, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) {
if lt(allowanceVal, amount) {
revert(0,0)
}
sstore(allowanceSlot, sub(allowanceVal, amount))
}
}
mstore(0x00, amount)
log3(0x00, 0x20, TRANSFER_EVENT_TOPIC_0, from, to)
mstore(0x00, 0x01)
return(0x00, 0x20)
}
}
//=================================================
//======= Permitted Transfers / Withdrawals =======
//=================================================
/**
* @notice Allows the `msg.sender` to revoke/cancel all prior permitted transfer and withdrawal signatures.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. The `msg.sender`'s master nonce has been incremented by `1` in contract storage, rendering all signed
* permits using the prior master nonce unusable.
* @dev 2. A `MasterNonceInvalidated` event has been emitted.
*/
function revokeMyOutstandingPermits() external {
assembly {
mstore(0x00, caller())
mstore(0x20, _masterNonces.slot)
let masterNonceSlot := keccak256(0x00, 0x40)
let invalidatedNonce := sload(masterNonceSlot)
sstore(masterNonceSlot, add(0x01, invalidatedNonce))
log3(0x00, 0x00, MASTER_NONCE_INVALIDATED_EVENT_TOPIC_0, caller(), invalidatedNonce)
}
}
/**
* @notice Allows the `msg.sender` to revoke/cancel a single, previously signed permitted transfer or withdrawal
* signature by specifying the nonce of the individual permit.
*
* @dev Throws when the `msg.sender` has already revoked the permit nonce.
* @dev Throws when the permit nonce was already used successfully.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. The specified `nonce` for the `msg.sender` has been revoked and can
* no longer be used to execute a permitted transfer or withdrawal.
* @dev 2. A `PermitNonceInvalidated` event has been emitted.
*
* @param nonce The nonce that was signed in the permitted transfer or withdrawal.
*/
function revokeMyNonce(uint256 nonce) external {
_checkAndInvalidateNonce(msg.sender, nonce);
}
/**
* @notice Allows a spender/operator to transfer wrapped native tokens from the `from` account to the `to` account
* using a gasless signature from the `from` account so that the `from` account does not need to pay gas
* to set an on-chain allowance.
*
* @notice If the `msg.value` is greater than zero, the function will deposit the funds into the `from` account
* before transferring the wrapped funds. Otherwise, the function will only transfer the funds.
* @notice **As a reminder, the `msg.sender`'s native tokens will be deposited and the `from` (not the `msg.sender`)
* address will be credited before the transfer. Integrating spender/operator protocols MUST be aware that
* deposits made during transfers will not credit their own account.**
*
* @dev Throws when the `from` account is the zero address.
* @dev Throws when the `msg.sender` does not match the operator/spender from the signed transfer permit.
* @dev Throws when the permitAmount does not match the signed transfer permit.
* @dev Throws when the nonce does not match the signed transfer permit.
* @dev Throws when the expiration does not match the signed transfer permit.
* @dev Throws when the permit has expired.
* @dev Throws when the requested transfer amount exceeds the maximum permitted transfer amount.
* @dev Throws when the permit nonce has already been used or revoked/cancelled.
* @dev Throws when the master nonce has been revoked/cancelled since the permit was signed.
* @dev Throws when the permit signature is invalid, or was not signed by the `from` account.
* @dev Throws when the `from` account has an insufficient balance to transfer `transferAmount` of wrapped native tokens.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. When `msg.value` is greater than zero, this contract's native token balance has increased by `msg.value`.
* @dev 2. When `msg.value` is greater than zero, the `msg.sender`'s native token balance has decreased by `msg.value`.
* @dev 3. When `msg.value` is greater than zero, the `from` account's wrapped native token balance has increased by `msg.value`.
* @dev 4. When `msg.value` is greater than zero, a `Deposit` event has been emitted. The `from` address is logged in the event.
* @dev 5. `nonce` for `from` account is invalidated.
* @dev 6. A `PermitNonceInvalidated` event has been emitted.
* @dev 7. The `transferAmount` of wrapped native tokens has been transferred from the `from` account to the `to` account.
* @dev 8. A `Transfer` event has been emitted. The `from` address, `to` address, and `transferAmount` are logged in the event.
*
* @param from The address that transfers the wrapped native tokens.
* @param to The address that receives the wrapped native tokens.
* @param transferAmount The amount of wrapped native tokens to transfer.
* @param permitAmount The maximum amount of wrapped native tokens that can be transferred, signed in permit.
* @param nonce The nonce, signed in permit.
* @param expiration The expiration timestamp, signed in permit.
* @param signedPermit The signature of the permit.
*/
function permitTransfer(
address from,
address to,
uint256 transferAmount,
uint256 permitAmount,
uint256 nonce,
uint256 expiration,
bytes calldata signedPermit
) external payable {
if (msg.value > 0) {
depositTo(from);
}
if (block.timestamp > expiration ||
transferAmount > permitAmount ||
from == address(0)) {
revert();
}
_checkAndInvalidateNonce(from, nonce);
_verifyPermitSignature(
from,
_hashTypedDataV4(
keccak256(
abi.encode(
PERMIT_TRANSFER_TYPEHASH,
msg.sender,
permitAmount,
nonce,
expiration,
_masterNonces[from]
)
)
),
signedPermit
);
_balanceTransfer(from, to, transferAmount);
}
/**
* @notice Allows a spender/operator to withdraw wrapped native tokens from the `from` account to the `to` account
* using a gasless signature signed by the `from` account to prove authorization of the withdrawal.
*
* @dev Throws when the `from` account is the zero address.
* @dev Throws when the `msg.sender` does not match the operator/spender from the signed withdrawal permit.
* @dev Throws when the permit has expired.
* @dev Throws when the amount does not match the signed withdrawal permit.
* @dev Throws when the nonce does not match the signed withdrawal permit.
* @dev Throws when the expiration does not match the signed withdrawal permit.
* @dev Throws when the convenience fee reciever and fee does not match the signed withdrawal permit.
* @dev Throws when the `to` address does not match the signed withdrawal permit.
* @dev Throws when the permit nonce has already been used or revoked/cancelled.
* @dev Throws when the master nonce has been revoked/cancelled since the permit was signed.
* @dev Throws when the permit signature is invalid, or was not signed by the `from` account.
* @dev Throws when the `from` account has an insufficient balance to transfer `transferAmount` of wrapped native tokens.
*
* @dev <h4>Postconditions:</h4>
* @dev 1. This contract's native token balance has decreased by `amount`, less convenience and infrastructure
* fees that remain wrapped.
* @dev 2. The `from` account's wrapped native token balance has decreased by `amount`.
* @dev 3. The `to` account's native token balance has increased by `amount`, less convenience and/or infrastructure fees.
* @dev 4. The `convenienceFeeReceiver` account's wrapped native token balance has increased by the convenience fee.
* @dev 5. The infrastructure tax account's wrapped native token balance has increased by the infrastructure fee.
* @dev 6. `nonce` for `from` account is invalidated.
* @dev 7. A `PermitNonceInvalidated` event has been emitted.
* @dev 8. A `Withdrawal` event has been emitted. Caveat: The `from` address is logged in the event, not `to` or `msg.sender`.
*
* @param from The address that from which funds are withdrawn.
* @param to The address that receives the withdrawn funds.
* @param amount The amount of wrapped native tokens to withdraw.
* @param nonce The nonce, signed in permit.
* @param expiration The expiration timestamp, signed in permit.
* @param convenienceFeeReceiver The address that receives the convenience fee.
* @param convenienceFeeBps The basis points of the convenience fee.
* @param signedPermit The signature of the permit.
*/
function doPermittedWithdraw(
address from,
address to,
uint256 amount,
uint256 nonce,
uint256 expiration,
address convenienceFeeReceiver,
uint256 convenienceFeeBps,
bytes calldata signedPermit
) external {
if (block.timestamp > expiration ||
from == address(0)) {
revert();
}
_checkAndInvalidateNonce(from, nonce);
_verifyPermitSignature(
from,
_hashTypedDataV4(
keccak256(
abi.encode(
PERMIT_WITHDRAWAL_TYPEHASH,
msg.sender,
amount,
nonce,
expiration,
_masterNonces[from],
to,
convenienceFeeReceiver,
convenienceFeeBps
)
)
),
signedPermit
);
(
uint256 userAmount,
uint256 convenienceFee,
uint256 infrastructureFee
) = _computeWithdrawalSplits(amount, convenienceFeeReceiver, convenienceFeeBps);
if (convenienceFee > 0) {
_balanceTransfer(from, convenienceFeeReceiver, convenienceFee);
}
if (infrastructureFee > 0) {
_balanceTransfer(from, ADDRESS_INFRASTRUCTURE_TAX, infrastructureFee);
}
_withdrawFromAccount(from, to, userAmount);
}
//=================================================
//========= Miscellaneous Helper Functions ========
//=================================================
/**
* @dev Helper function that transfers wrapped native token balance between accounts.
*
* @dev Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
*
* @param from The address from which the wrapped native tokens is transferred.
* @param to The address to which the wrapped native tokens are transferred.
* @param amount The amount of wrapped native tokens to transfer.
*/
function _balanceTransfer(address from, address to, uint256 amount) private {
assembly {
mstore(0x00, from)
mstore(0x20, balanceOf.slot)
let balanceSlotFrom := keccak256(0x00, 0x40)
let balanceValFrom := sload(balanceSlotFrom)
if lt(balanceValFrom, amount) {
revert(0,0)
}
sstore(balanceSlotFrom, sub(balanceValFrom, amount))
mstore(0x00, to)
let balanceSlotTo := keccak256(0x00, 0x40)
sstore(balanceSlotTo, add(sload(balanceSlotTo), amount))
mstore(0x00, amount)
log3(0x00, 0x20, TRANSFER_EVENT_TOPIC_0, from, to)
}
}
/**
* @dev Helper function that withdraws wrapped native tokens from an account to another account.
*
* @dev Throws when the `from` account has an insufficient balance to transfer `amount` of wrapped native tokens.
* @dev Throws when the unwrapped native funds cannot be transferred to the `to` account.
*
* @param from The address from which the wrapped native tokens are withdrawn.
* @param to The address to which the native tokens are transferred.
* @param amount The amount of wrapped native tokens to withdraw.
*/
function _withdrawFromAccount(address from, address to, uint256 amount) private {
assembly {
mstore(0x00, from)
mstore(0x20, balanceOf.slot)
let balanceSlot := keccak256(0x00, 0x40)
let balanceVal := sload(balanceSlot)
let updatedBalance := sub(balanceVal, amount)
sstore(balanceSlot, updatedBalance)
mstore(0x00, amount)
log2(0x00, 0x20, WITHDRAWAL_EVENT_TOPIC_0, from)
if or(gt(updatedBalance, balanceVal), iszero(call(gas(), to, amount, 0, 0, 0, 0))) {
revert(0,0)
}
}
}
/**
* @dev Helper function that checks and invalidates a permit nonce.
*
* @dev Throws when the permit nonce has already been used or revoked/cancelled.
*
* @param account The account that signed the permit.
* @param nonce The nonce that was signed in the permit.
*/
function _checkAndInvalidateNonce(address account, uint256 nonce) private {
unchecked {
if (uint256(_permitNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) &
(ONE << uint8(nonce)) == ZERO) {
revert();
}
}
assembly {
log3(0x00, 0x00, PERMIT_NONCE_INVALIDATED_EVENT_TOPIC_0, account, nonce)
}
}
//=================================================
//============= Fee Split Calculations ============
//=================================================
/**
* @dev Helper function that computes the withdrawal fee split amounts.
*
* @param amount The amount of wrapped native tokens to split.
* @param convenienceFeeReceiver The address that receives the convenience fee.
* @param convenienceFeeBps The basis points of the convenience fee.
*/
function _computeWithdrawalSplits(
uint256 amount,
address convenienceFeeReceiver,
uint256 convenienceFeeBps
) private pure returns (uint256 userAmount, uint256 convenienceFee, uint256 convenienceFeeInfrastructure) {
if (convenienceFeeBps > FEE_DENOMINATOR) {
revert();
}
if (amount > type(uint240).max) {
revert();
}
if (convenienceFeeReceiver == address(0)) {
convenienceFeeBps = 0;
}
unchecked {
if (convenienceFeeBps > INFRASTRUCTURE_TAX_THRESHOLD) {
convenienceFee = amount * convenienceFeeBps / FEE_DENOMINATOR;
convenienceFeeInfrastructure = convenienceFee * INFRASTRUCTURE_TAX_BPS / FEE_DENOMINATOR;
convenienceFee -= convenienceFeeInfrastructure;
userAmount = amount - convenienceFee - convenienceFeeInfrastructure;
} else if (convenienceFeeBps > 0) {
convenienceFeeInfrastructure = amount / FEE_DENOMINATOR;
convenienceFee = amount * (convenienceFeeBps - ONE) / FEE_DENOMINATOR;
userAmount = amount - convenienceFee - convenienceFeeInfrastructure;
} else {
convenienceFeeInfrastructure = amount / FEE_DENOMINATOR;
userAmount = amount - convenienceFeeInfrastructure;
}
}
}
//=================================================
//============ Signature Verification =============
//=================================================
/**
* @notice Verifies a permit signature based on the bytes length of the signature provided.
*
* @dev Throws when -
* @dev The bytes signature length is 64 or 65 bytes AND
* @dev The ECDSA recovered signer is not the expectedSigner AND
* @dev The expectedSigner's code length is zero OR the expectedSigner does not return a valid EIP-1271 response
* @dev
* @dev OR
* @dev
* @dev The bytes signature length is not 64 or 65 bytes AND
* @dev The expectedSigner's code length is zero OR the expectedSigner does not return a valid EIP-1271 response
*/
function _verifyPermitSignature(
address expectedSigner,
bytes32 digest,
bytes calldata signature
) private view {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// Divide the signature in r, s and v variables
/// @solidity memory-safe-assembly
assembly {
r := calldataload(signature.offset)
s := calldataload(add(signature.offset, 32))
v := byte(0, calldataload(add(signature.offset, 64)))
}
(bool isError, address signer) = _ecdsaRecover(digest, v, r, s);
if (expectedSigner != signer || isError) {
_verifyEIP1271Signature(expectedSigner, digest, signature);
}
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// Divide the signature in r and vs variables
/// @solidity memory-safe-assembly
assembly {
r := calldataload(signature.offset)
vs := calldataload(add(signature.offset, 32))
}
(bool isError, address signer) = _ecdsaRecover(digest, r, vs);
if (expectedSigner != signer || isError) {
_verifyEIP1271Signature(expectedSigner, digest, signature);
}
} else {
_verifyEIP1271Signature(expectedSigner, digest, signature);
}
}
/**
* @notice Verifies an EIP-1271 signature.
*
* @dev Throws when `signer` code length is zero OR the EIP-1271 call does not
* @dev return the correct magic value.
*
* @param signer The signer address to verify a signature with
* @param hash The hash digest to verify with the signer
* @param signature The signature to verify
*/
function _verifyEIP1271Signature(address signer, bytes32 hash, bytes calldata signature) private view {
if(signer.code.length == 0) {
revert();
}
if (!_safeIsValidSignature(signer, hash, signature)) {
revert();
}
}
/**
* @notice Overload of the `_ecdsaRecover` function to unpack the `v` and `s` values
*
* @param digest The hash digest that was signed
* @param r The `r` value of the signature
* @param vs The packed `v` and `s` values of the signature
*
* @return isError True if the ECDSA function is provided invalid inputs
* @return signer The recovered address from ECDSA
*/
function _ecdsaRecover(bytes32 digest, bytes32 r, bytes32 vs) private pure returns (bool isError, address signer) {
unchecked {
bytes32 s = vs & UPPER_BIT_MASK;
uint8 v = uint8(uint256(vs >> 255)) + 27;
(isError, signer) = _ecdsaRecover(digest, v, r, s);
}
}
/**
* @notice Recovers the signer address using ECDSA
*
* @dev Does **NOT** revert if invalid input values are provided or `signer` is recovered as address(0)
* @dev Returns an `isError` value in those conditions that is handled upstream
*
* @param digest The hash digest that was signed
* @param v The `v` value of the signature
* @param r The `r` value of the signature
* @param s The `s` value of the signature
*
* @return isError True if the ECDSA function is provided invalid inputs
* @return signer The recovered address from ECDSA
*/
function _ecdsaRecover(bytes32 digest, uint8 v, bytes32 r, bytes32 s) private pure returns (bool isError, address signer) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
// Invalid signature `s` value - return isError = true and signer = address(0) to check EIP-1271
return (true, address(0));
}
signer = ecrecover(digest, v, r, s);
isError = (signer == address(0));
}
/**
* @notice A gas efficient, and fallback-safe way to call the isValidSignature function for EIP-1271.
*
* @param signer The EIP-1271 signer to call to check for a valid signature.
* @param hash The hash digest to verify with the EIP-1271 signer.
* @param signature The supplied signature to verify.
*
* @return isValid True if the EIP-1271 signer returns the EIP-1271 magic value.
*/
function _safeIsValidSignature(
address signer,
bytes32 hash,
bytes calldata signature
) private view returns(bool isValid) {
assembly {
function _callIsValidSignature(_signer, _hash, _signatureOffset, _signatureLength) -> _isValid {
let ptr := mload(0x40)
// store isValidSignature(bytes32,bytes) selector
mstore(ptr, hex"1626ba7e")
// store bytes32 hash value in abi encoded location
mstore(add(ptr, 0x04), _hash)
// store abi encoded location of the bytes signature data
mstore(add(ptr, 0x24), 0x40)
// store bytes signature length
mstore(add(ptr, 0x44), _signatureLength)
// copy calldata bytes signature to memory
calldatacopy(add(ptr, 0x64), _signatureOffset, _signatureLength)
// calculate data length based on abi encoded data with rounded up signature length
let dataLength := add(0x64, and(add(_signatureLength, 0x1F), not(0x1F)))
// update free memory pointer
mstore(0x40, add(ptr, dataLength))
// static call _signer with abi encoded data
// skip return data check if call failed or return data size is not at least 32 bytes
if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _signer, ptr, dataLength, 0x00, 0x20)) {
// check if return data is equal to isValidSignature magic value
_isValid := eq(mload(0x00), hex"1626ba7e")
leave
}
}
isValid := _callIsValidSignature(signer, hash, signature.offset, signature.length)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
interface IWrappedNative {
// ERC20 Specific
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external payable returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external payable returns (bool);
function transferFrom(address from, address to, uint256 value) external payable returns (bool);
// ERC20 Metadata Specific
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
// Wrapped Native Specific
event Deposit(address indexed to, uint256 amount);
event Withdrawal(address indexed from, uint256 amount);
function deposit() external payable;
function withdraw(uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "./IWrappedNative.sol";
interface IWrappedNativeExtended is IWrappedNative {
// Wrapped Native Permit Specific
event PermitNonceInvalidated(address indexed account,uint256 indexed nonce);
event MasterNonceInvalidated(address indexed account, uint256 indexed nonce);
// Enhancements for Deposits and Withdrawals
function depositTo(address to) external payable;
function withdrawToAccount(address to, uint256 amount) external;
function withdrawSplit(address[] calldata toAddresses, uint256[] calldata amounts) external;
// Permit Processing
function domainSeparatorV4() external view returns (bytes32);
function isNonceUsed(address account, uint256 nonce) external view returns (bool);
function masterNonces(address account) external view returns (uint256);
function revokeMyOutstandingPermits() external;
function revokeMyNonce(uint256 nonce) external;
function permitTransfer(
address from,
address to,
uint256 transferAmount,
uint256 permitAmount,
uint256 nonce,
uint256 expiration,
bytes calldata signedPermit
) external payable;
function doPermittedWithdraw(
address from,
address to,
uint256 amount,
uint256 nonce,
uint256 expiration,
address convenienceFeeReceiver,
uint256 convenienceFeeBps,
bytes calldata signedPermit
) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import "./MessageHashUtils.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712 {
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// 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 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, 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 and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// 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)
}
// Shift in bits from prod1 into prod0.
prod0 |= 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 for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the 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.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // 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 preconditions 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.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "./Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./Math.sol";
import {SignedMath} from "./SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}