Skip to content

Commit

Permalink
[TokenPaymaster] Support chains without WETH9 & Other Improvements (#670
Browse files Browse the repository at this point in the history
)

* [TokenPaymaster] Support chains without WETH9

For chains like Celo we can swap directly to it without unwrapping

* Ability to update Oracle config & better cache price updates

* expose _setOracleConfiguration

* make configs public

* Switch to swap-router-contracts interface

* Create swap-router-contracts

* move price update to validate fn
  • Loading branch information
0xFirekeeper authored Nov 26, 2024
1 parent cf51bfa commit 389f945
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@
[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/uniswap/v3-core
[submodule "lib/swap-router-contracts"]
path = lib/swap-router-contracts
url = https://github.com/Uniswap/swap-router-contracts
12 changes: 10 additions & 2 deletions contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
IERC20Metadata _token,
IEntryPoint _entryPoint,
IERC20 _wrappedNative,
ISwapRouter _uniswap,
IV3SwapRouter _uniswap,
TokenPaymasterConfig memory _tokenPaymasterConfig,
OracleHelperConfig memory _oracleHelperConfig,
UniswapHelperConfig memory _uniswapHelperConfig,
Expand Down Expand Up @@ -98,6 +98,10 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
_setUniswapHelperConfiguration(_uniswapHelperConfig);
}

function setOracleConfiguration(OracleHelperConfig memory _oracleHelperConfig) external onlyOwner {
_setOracleConfiguration(_oracleHelperConfig);
}

/// @notice Allows the contract owner to withdraw a specified amount of tokens from the contract.
/// @param to The address to transfer the tokens to.
/// @param amount The amount of tokens to transfer.
Expand All @@ -123,6 +127,10 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
uint256 refundPostopCost = tokenPaymasterConfig.refundPostopCost;
require(refundPostopCost < userOp.unpackPostOpGasLimit(), "TPM: postOpGasLimit too low");
uint256 preChargeNative = requiredPreFund + (refundPostopCost * maxFeePerGas);

bool forceUpdate = (block.timestamp - cachedPriceTimestamp) > tokenPaymasterConfig.priceMaxAge;
updateCachedPrice(forceUpdate);

// note: as price is in native-asset-per-token and we want more tokens increasing it means dividing it by markup
uint256 cachedPriceWithMarkup = (cachedPrice * PRICE_DENOMINATOR) / priceMarkup;
if (dataLength == 32) {
Expand Down Expand Up @@ -161,7 +169,7 @@ contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper {
unchecked {
uint256 priceMarkup = tokenPaymasterConfig.priceMarkup;
(uint256 preCharge, address userOpSender) = abi.decode(context, (uint256, address));
uint256 _cachedPrice = updateCachedPrice(false);
uint256 _cachedPrice = cachedPrice;
// note: as price is in native-asset-per-token and we want more tokens increasing it means dividing it by markup
uint256 cachedPriceWithMarkup = (_cachedPrice * PRICE_DENOMINATOR) / priceMarkup;
// Refund tokens based on actual gas cost
Expand Down
4 changes: 2 additions & 2 deletions contracts/prebuilts/account/utils/OracleHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ abstract contract OracleHelper {
/// @notice The timestamp of a block when the cached price was updated
uint48 public cachedPriceTimestamp;

OracleHelperConfig private oracleHelperConfig;
OracleHelperConfig public oracleHelperConfig;

/// @notice The "10^(tokenOracle.decimals)" value used for the price calculation
uint128 private tokenOracleDecimalPower;
Expand All @@ -54,7 +54,7 @@ abstract contract OracleHelper {
_setOracleConfiguration(_oracleHelperConfig);
}

function _setOracleConfiguration(OracleHelperConfig memory _oracleHelperConfig) private {
function _setOracleConfiguration(OracleHelperConfig memory _oracleHelperConfig) internal {
oracleHelperConfig = _oracleHelperConfig;
require(_oracleHelperConfig.priceUpdateThreshold <= PRICE_DENOMINATOR, "TPM: update threshold too high");
tokenOracleDecimalPower = uint128(10 ** oracleHelperConfig.tokenOracle.decimals());
Expand Down
15 changes: 9 additions & 6 deletions contracts/prebuilts/account/utils/UniswapHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pragma solidity ^0.8.23;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol";

abstract contract UniswapHelper {
Expand All @@ -19,23 +19,24 @@ abstract contract UniswapHelper {
uint256 minSwapAmount;
uint24 uniswapPoolFee;
uint8 slippage;
bool wethIsNativeAsset;
}

/// @notice The Uniswap V3 SwapRouter contract
ISwapRouter public immutable uniswap;
IV3SwapRouter public immutable uniswap;

/// @notice The ERC20 token used for transaction fee payments
IERC20Metadata public immutable token;

/// @notice The ERC-20 token that wraps the native asset for current chain
IERC20 public immutable wrappedNative;

UniswapHelperConfig private uniswapHelperConfig;
UniswapHelperConfig public uniswapHelperConfig;

constructor(
IERC20Metadata _token,
IERC20 _wrappedNative,
ISwapRouter _uniswap,
IV3SwapRouter _uniswap,
UniswapHelperConfig memory _uniswapHelperConfig
) {
_token.approve(address(_uniswap), type(uint256).max);
Expand Down Expand Up @@ -85,6 +86,9 @@ abstract contract UniswapHelper {
}

function unwrapWeth(uint256 amount) internal {
if (uniswapHelperConfig.wethIsNativeAsset) {
return;
}
IPeripheryPayments(address(uniswap)).unwrapWETH9(amount, address(this));
}

Expand All @@ -96,12 +100,11 @@ abstract contract UniswapHelper {
uint256 amountOutMin,
uint24 fee
) internal returns (uint256 amountOut) {
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams(
tokenIn, //tokenIn
tokenOut, //tokenOut
fee,
address(uniswap),
block.timestamp, //deadline
amountIn,
amountOutMin,
0
Expand Down
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ out = 'artifacts_forge'
remappings = [
'@uniswap/v3-core/contracts=lib/v3-core/contracts',
'@uniswap/v3-periphery/contracts=lib/v3-periphery/contracts',
'@uniswap/swap-router-contracts/contracts=lib/swap-router-contracts/contracts',
'@chainlink/=lib/chainlink/',
'@openzeppelin/contracts=lib/openzeppelin-contracts/contracts',
'@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/',
Expand Down
1 change: 1 addition & 0 deletions lib/swap-router-contracts
Submodule swap-router-contracts added at c696aa
7 changes: 4 additions & 3 deletions src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/Acco
import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol";
import { TokenPaymaster, IERC20Metadata } from "contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol";
import { OracleHelper, IOracle } from "contracts/prebuilts/account/utils/OracleHelper.sol";
import { UniswapHelper, ISwapRouter } from "contracts/prebuilts/account/utils/UniswapHelper.sol";
import { UniswapHelper, IV3SwapRouter } from "contracts/prebuilts/account/utils/UniswapHelper.sol";

/// @dev This is a dummy contract to test contract interactions with Account.
contract Number {
Expand Down Expand Up @@ -115,14 +115,15 @@ contract TokenPaymasterTest is BaseTest {
UniswapHelper.UniswapHelperConfig memory uniswapHelperConfig = UniswapHelper.UniswapHelperConfig({
minSwapAmount: 1,
slippage: 5,
uniswapPoolFee: 3
uniswapPoolFee: 3,
wethIsNativeAsset: false
});

paymaster = new TokenPaymaster(
IERC20Metadata(address(token)),
entrypoint,
weth,
ISwapRouter(address(testUniswap)),
IV3SwapRouter(address(testUniswap)),
tokenPaymasterConfig,
oracleHelperConfig,
uniswapHelperConfig,
Expand Down

0 comments on commit 389f945

Please sign in to comment.