Skip to content

Commit

Permalink
WIP: Check new price calculation function; use 1e26 as 'price denomin…
Browse files Browse the repository at this point in the history
…ator'
  • Loading branch information
forshtat committed May 25, 2023
1 parent 63cf750 commit 39a1e41
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 59 deletions.
18 changes: 9 additions & 9 deletions contracts/samples/TokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
event Received(address indexed sender, uint256 value);

/// @notice All 'price' variables are multiplied by this value to avoid rounding up
uint256 private constant PRICE_DENOMINATOR = 1e6;
uint256 private constant PRICE_DENOMINATOR = 1e26;

This comment has been minimized.

Copy link
@livingrockrises

livingrockrises Jun 3, 2023

Contributor

1e26?


/// @notice Estimated gas cost for refunding tokens after the transaction is completed
uint256 public constant REFUND_POSTOP_COST = 40000;
Expand Down Expand Up @@ -72,13 +72,13 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
_entryPoint
)
OracleHelper(
_oracleHelperConfig,
10 ** _token.decimals()
_oracleHelperConfig
)
UniswapHelper(
_token,
_wrappedNative,
_uniswap,
10 ** _token.decimals(),
_uniswapHelperConfig
)
{
Expand All @@ -91,8 +91,8 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
function setTokenPaymasterConfig(
TokenPaymasterConfig memory _tokenPaymasterConfig
) public onlyOwner {
require(_tokenPaymasterConfig.priceMarkup <= 1900000, "TPM: price markup too high");
require(_tokenPaymasterConfig.priceMarkup >= 1e6, "TPM: price markup too low");
require(_tokenPaymasterConfig.priceMarkup <= 2 * PRICE_DENOMINATOR, "TPM: price markup too high");
require(_tokenPaymasterConfig.priceMarkup >= PRICE_DENOMINATOR, "TPM: price markup too low");
tokenPaymasterConfig = _tokenPaymasterConfig;
emit ConfigUpdated(_tokenPaymasterConfig);
}
Expand All @@ -103,7 +103,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
_setOracleConfiguration(_oracleHelperConfig);
}

function setOracleConfiguration(
function setUniswapConfiguration(
UniswapHelperConfig memory _uniswapHelperConfig
) external onlyOwner {
_setUniswapHelperConfiguration(_uniswapHelperConfig);
Expand Down Expand Up @@ -140,7 +140,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
cachedPriceWithMarkup = clientSuppliedPrice;
}
}
uint256 tokenAmount = weiToToken(preChargeNative, cachedPriceWithMarkup, false);
uint256 tokenAmount = weiToToken(preChargeNative, cachedPriceWithMarkup);
SafeERC20.safeTransferFrom(token, userOp.sender, address(this), tokenAmount);
context = abi.encodePacked(tokenAmount, userOp.maxFeePerGas, userOp.sender);
validationResult = 0;
Expand Down Expand Up @@ -171,7 +171,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
uint256 cachedPriceWithMarkup = _cachedPrice * PRICE_DENOMINATOR / priceMarkup;
// Refund tokens based on actual gas cost
uint256 actualChargeNative = actualGasCost + REFUND_POSTOP_COST * maxFeePerGas;
uint256 actualTokenNeeded = weiToToken(actualChargeNative, cachedPriceWithMarkup, false);
uint256 actualTokenNeeded = weiToToken(actualChargeNative, cachedPriceWithMarkup);
if (preCharge > actualTokenNeeded) {
// If the initially provided token amount is greater than the actual amount needed, refund the difference
SafeERC20.safeTransfer(
Expand Down Expand Up @@ -201,7 +201,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
if (
currentEntryPointBalance < tokenPaymasterConfig.minEntryPointBalance
) {
uint256 swappedWeth = _maybeSwapTokenToWeth(token, _cachedPrice, false);
uint256 swappedWeth = _maybeSwapTokenToWeth(token, _cachedPrice);
unwrapWeth(swappedWeth);
entryPoint.depositTo{value: address(this).balance}(address(this));
}
Expand Down
55 changes: 43 additions & 12 deletions contracts/samples/utils/OracleHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ abstract contract OracleHelper {

event TokenPriceUpdated(uint256 currentPrice, uint256 previousPrice);

uint256 private constant PRICE_DENOMINATOR = 1e6;

/// @notice Actually equals 10^(token.decimals) value used for the price calculation
uint256 private immutable tokenDecimalPower;
uint256 private constant PRICE_DENOMINATOR = 1e26;

struct OracleHelperConfig {
/// @notice The Oracle contract used to fetch the latest token prices
Expand All @@ -32,10 +29,10 @@ abstract contract OracleHelper {
/// @notice If 'false' we will use nativeOracle to establish a token price through a shared third currency
bool tokenToNativeOracle;

/// @notice 'true' if price is dollars-per-token, 'false' if price is tokens-per-dollar
/// @notice 'true' if price is dollars-per-token (or ether-per-token), 'false' if price is tokens-per-dollar
bool tokenOracleReverse;

/// @notice 'true' if price is dollars-per-ether, 'false' if price is ether-per-dollar
/// @notice 'false' if price is dollars-per-ether, 'true' if price is ether-per-dollar
bool nativeOracleReverse;

/// @notice The price update threshold percentage that triggers a price update (1e6 = 100%)
Expand All @@ -53,12 +50,16 @@ abstract contract OracleHelper {

OracleHelperConfig private oracleHelperConfig;

/// @notice The "10^(tokenOracle.decimals)" value used for the price calculation
uint256 private tokenOracleDecimalPower;

/// @notice The "10^(nativeOracle.decimals)" value used for the price calculation
uint256 private nativeOracleDecimalPower;

constructor (
OracleHelperConfig memory _oracleHelperConfig,
uint256 _tokenDecimalPower
OracleHelperConfig memory _oracleHelperConfig
) {
cachedPrice = type(uint256).max; // initialize the storage slot to invalid value
tokenDecimalPower = _tokenDecimalPower;
_setOracleConfiguration(
_oracleHelperConfig
);
Expand All @@ -69,6 +70,8 @@ abstract contract OracleHelper {
) internal {
oracleHelperConfig = _oracleHelperConfig;
require(_oracleHelperConfig.priceUpdateThreshold <= 1e6, "TPM: update threshold too high");
tokenOracleDecimalPower = 10 ** oracleHelperConfig.tokenOracle.decimals();
nativeOracleDecimalPower = 10 ** oracleHelperConfig.nativeOracle.decimals();
}

/// @notice Updates the token price by fetching the latest price from the Oracle.
Expand All @@ -84,8 +87,17 @@ abstract contract OracleHelper {
}
uint256 _cachedPrice = cachedPrice;
uint256 tokenPrice = fetchPrice(tokenOracle);
uint256 nativeAssetPrice = fetchPrice(nativeOracle);
uint256 price = nativeAssetPrice * tokenDecimalPower / tokenPrice;
uint256 nativeAssetPrice = 1;
// If the 'TokenOracle' returns the price in the native asset units there is no need to fetch native asset price
if (!oracleHelperConfig.tokenToNativeOracle) {
nativeAssetPrice = fetchPrice(nativeOracle);
}
uint256 price = calculatePrice(
tokenPrice,
nativeAssetPrice,
oracleHelperConfig.tokenOracleReverse,
oracleHelperConfig.nativeOracleReverse
);
uint256 priceNewByOld = price * PRICE_DENOMINATOR / _cachedPrice;

bool updateRequired = force ||
Expand All @@ -95,12 +107,31 @@ abstract contract OracleHelper {
return _cachedPrice;
}
uint256 previousPrice = _cachedPrice;
_cachedPrice = nativeAssetPrice * tokenDecimalPower / tokenPrice;
_cachedPrice = price;
cachedPrice = _cachedPrice;
emit TokenPriceUpdated(_cachedPrice, previousPrice);
return _cachedPrice;
}

function calculatePrice(
uint256 tokenPrice,
uint256 nativeAssetPrice,
bool tokenOracleReverse,
bool nativeOracleReverse
) private view returns (uint256){
if (tokenOracleReverse) {
tokenPrice = PRICE_DENOMINATOR * tokenOracleDecimalPower / tokenPrice;
} else {
tokenPrice = PRICE_DENOMINATOR * tokenPrice / tokenOracleDecimalPower;
}

if (nativeOracleReverse) {
return nativeAssetPrice * tokenPrice / nativeOracleDecimalPower;
} else {
return tokenPrice * nativeOracleDecimalPower / nativeAssetPrice;
}
}

/// @notice Fetches the latest price from the given Oracle.
/// @dev This function is used to get the latest price from the tokenOracle or nativeOracle.
/// @param _oracle The Oracle contract to fetch the price from.
Expand Down
21 changes: 10 additions & 11 deletions contracts/samples/utils/UniswapHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol";
abstract contract UniswapHelper {
event UniswapReverted(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin);

uint256 private constant PRICE_DENOMINATOR = 1e6;
uint256 private constant PRICE_DENOMINATOR = 1e26;

struct UniswapHelperConfig {
/// @notice Minimum native asset amount to receive from a single swap
Expand All @@ -33,26 +33,31 @@ abstract contract UniswapHelper {

UniswapHelperConfig private uniswapHelperConfig;

/// @notice The "10^(token.decimals)" value used for the price calculation
uint256 private immutable tokenDecimalPower;

constructor(
IERC20 _token,
IERC20 _wrappedNative,
ISwapRouter _uniswap,
uint256 _tokenDecimalPower,
UniswapHelperConfig memory _uniswapHelperConfig
){
_token.approve(address(_uniswap), type(uint256).max);
token = _token;
wrappedNative = _wrappedNative;
uniswap = _uniswap;
tokenDecimalPower = _tokenDecimalPower;
_setUniswapHelperConfiguration(_uniswapHelperConfig);
}

function _setUniswapHelperConfiguration(UniswapHelperConfig memory _uniswapHelperConfig) internal {
uniswapHelperConfig = _uniswapHelperConfig;
}

function _maybeSwapTokenToWeth(IERC20 tokenIn, uint256 quote, bool reverseQuote) internal returns (uint256) {
function _maybeSwapTokenToWeth(IERC20 tokenIn, uint256 quote) internal returns (uint256) {
uint256 tokenBalance = tokenIn.balanceOf(address(this));
uint256 amountOutMin = addSlippage(tokenToWei(tokenBalance, quote, reverseQuote), uniswapHelperConfig.slippage);
uint256 amountOutMin = addSlippage(tokenToWei(tokenBalance, quote), uniswapHelperConfig.slippage);
if (amountOutMin < uniswapHelperConfig.minSwapAmount) {
return 0;
}
Expand All @@ -71,17 +76,11 @@ abstract contract UniswapHelper {
}


function tokenToWei(uint256 amount, uint256 price, bool reverse) internal view returns (uint256) {
if (reverse) {
return weiToToken(amount, price, false);
}
function tokenToWei(uint256 amount, uint256 price) public pure returns (uint256) {
return amount * price / PRICE_DENOMINATOR;
}

function weiToToken(uint256 amount, uint256 price, bool reverse) internal view returns (uint256) {
if (reverse) {
return tokenToWei(amount, price, false);
}
function weiToToken(uint256 amount, uint256 price) public pure returns (uint256) {
return amount * PRICE_DENOMINATOR / price;
}

Expand Down
13 changes: 10 additions & 3 deletions contracts/test/TestOracle2.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "../samples/utils/IOracle.sol";

contract TestOracle2 is IOracle {
int256 public price;
uint8 private _decimals_;

constructor(int256 _price) {
constructor(int256 _price, uint8 _decimals) {
price = _price;
_decimals_ = _decimals;
}

function setPrice(int256 _price) external {
price = _price;
}

function decimals() external pure override returns (uint8) {
return 8;
function setDecimals(uint8 _decimals) external {
_decimals_ = _decimals;
}

function decimals() external view override returns (uint8) {
return _decimals_;
}

function latestRoundData()
Expand Down
Loading

0 comments on commit 39a1e41

Please sign in to comment.