diff --git a/.gitmodules b/.gitmodules index 43557e2ee..db5ccba93 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol b/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol index f2705f6f4..c7104c763 100644 --- a/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol +++ b/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol @@ -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, @@ -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. @@ -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) { @@ -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 diff --git a/contracts/prebuilts/account/utils/OracleHelper.sol b/contracts/prebuilts/account/utils/OracleHelper.sol index b7fa334d1..89ce08843 100644 --- a/contracts/prebuilts/account/utils/OracleHelper.sol +++ b/contracts/prebuilts/account/utils/OracleHelper.sol @@ -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; @@ -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()); diff --git a/contracts/prebuilts/account/utils/UniswapHelper.sol b/contracts/prebuilts/account/utils/UniswapHelper.sol index 39b541621..30f5031ae 100644 --- a/contracts/prebuilts/account/utils/UniswapHelper.sol +++ b/contracts/prebuilts/account/utils/UniswapHelper.sol @@ -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 { @@ -19,10 +19,11 @@ 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; @@ -30,12 +31,12 @@ abstract contract UniswapHelper { /// @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); @@ -85,6 +86,9 @@ abstract contract UniswapHelper { } function unwrapWeth(uint256 amount) internal { + if (uniswapHelperConfig.wethIsNativeAsset) { + return; + } IPeripheryPayments(address(uniswap)).unwrapWETH9(amount, address(this)); } @@ -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 diff --git a/foundry.toml b/foundry.toml index 13a9e8619..464eeb8d6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -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/', diff --git a/lib/swap-router-contracts b/lib/swap-router-contracts new file mode 160000 index 000000000..c696aada4 --- /dev/null +++ b/lib/swap-router-contracts @@ -0,0 +1 @@ +Subproject commit c696aada49b33c8e764e6f0bd0a0a56bd8aa455f diff --git a/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol b/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol index fed3bf3d5..e6b3507ef 100644 --- a/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol +++ b/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol @@ -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 { @@ -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,