diff --git a/contracts/dev/TestOracle.sol b/contracts/dev/TestOracle.sol index 12eaaa27..6db4ff27 100644 --- a/contracts/dev/TestOracle.sol +++ b/contracts/dev/TestOracle.sol @@ -16,6 +16,6 @@ contract TestOracle { view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { - return (110680464442257311610, 190355094900, 1685339747, block.timestamp, 110680464442257311610); + return (110680464442257311610, price, 1685339747, block.timestamp, 110680464442257311610); } } diff --git a/contracts/paymaster/ERC20Paymaster.sol b/contracts/paymaster/ERC20Paymaster.sol index b26ab95a..0ba69100 100644 --- a/contracts/paymaster/ERC20Paymaster.sol +++ b/contracts/paymaster/ERC20Paymaster.sol @@ -37,6 +37,8 @@ contract ERC20Paymaster is BasePaymaster { address public immutable WALLET_FACTORY; + IOracle public nativeAssetOracle; + mapping(address => TokenSetting) public supportedToken; event ConfigUpdated(address token, address oracle, uint32 priceMarkup); @@ -48,6 +50,11 @@ contract ERC20Paymaster is BasePaymaster { WALLET_FACTORY = _walletFactory; } + function setNativeAssetOracle(address _oracle) external onlyOwner { + require(_oracle != address(0), "Paymaster: invalid oracle addr"); + nativeAssetOracle = IOracle(_oracle); + } + function setToken(address[] calldata _tokens, address[] calldata _tokenOracles, uint32[] calldata _priceMarkups) external onlyOwner @@ -66,7 +73,7 @@ contract ERC20Paymaster is BasePaymaster { require(IOracle(_tokenOracle).decimals() == 8, "Paymaster: token oracle decimals must be 8"); supportedToken[_token].priceMarkup = _priceMarkup; supportedToken[_token].tokenOracle = IOracle(_tokenOracle); - supportedToken[_token].tokenDecimals = IERC20Metadata(_token).decimals(); + supportedToken[_token].tokenDecimals = 10 ** IERC20Metadata(_token).decimals(); emit ConfigUpdated(_token, _tokenOracle, _priceMarkup); } } @@ -78,7 +85,9 @@ contract ERC20Paymaster is BasePaymaster { function updatePrice(address token) external { require(isSupportToken(token), "Paymaster: token not support"); uint192 tokenPrice = fetchPrice(supportedToken[token].tokenOracle); - supportedToken[token].previousPrice = tokenPrice; + uint192 nativeAssetPrice = fetchPrice(nativeAssetOracle); + supportedToken[token].previousPrice = + nativeAssetPrice * uint192(supportedToken[token].tokenDecimals) / tokenPrice; } function isSupportToken(address token) public view returns (bool) { @@ -104,12 +113,9 @@ contract ERC20Paymaster is BasePaymaster { uint256 cachedPrice = supportedToken[token].previousPrice; require(cachedPrice != 0, "Paymaster: price not set"); - uint256 exchangeRate = (cachedPrice * 10 ** supportedToken[token].tokenDecimals) / 10 ** 8; - // tokenRequiredPreFund = requiredPreFund * exchangeRate / 10^18 - uint256 costOfPost = userOp.gasPrice() * COST_OF_POST; - uint256 tokenRequiredPreFund = (requiredPreFund + costOfPost) * supportedToken[token].priceMarkup * exchangeRate + uint256 tokenRequiredPreFund = (requiredPreFund + costOfPost) * supportedToken[token].priceMarkup * cachedPrice / (1e18 * PRICE_DENOMINATOR); require(tokenRequiredPreFund <= maxCost, "Paymaster: maxCost too low"); @@ -128,7 +134,7 @@ contract ERC20Paymaster is BasePaymaster { sponsorWalletCreation = false; } - return (abi.encode(sender, token, costOfPost, exchangeRate, tokenRequiredPreFund, sponsorWalletCreation), 0); + return (abi.encode(sender, token, costOfPost, cachedPrice, tokenRequiredPreFund, sponsorWalletCreation), 0); } /* @@ -188,12 +194,12 @@ contract ERC20Paymaster is BasePaymaster { address sender, address payable token, uint256 costOfPost, - uint256 exchangeRate, + uint256 cachedPrice, uint256 tokenRequiredPreFund, bool sponsorWalletCreation ) = abi.decode(context, (address, address, uint256, uint256, uint256, bool)); uint256 tokenRequiredFund = - (actualGasCost + costOfPost) * supportedToken[token].priceMarkup * exchangeRate / (1e18 * PRICE_DENOMINATOR); + (actualGasCost + costOfPost) * supportedToken[token].priceMarkup * cachedPrice / (1e18 * PRICE_DENOMINATOR); if (sponsorWalletCreation) { // if sponsor during wallet creatation, charge the acutal amount IERC20Metadata(token).safeTransferFrom(sender, address(this), tokenRequiredPreFund); @@ -203,7 +209,9 @@ contract ERC20Paymaster is BasePaymaster { } // update oracle uint192 lasestTokenPrice = fetchPrice(supportedToken[token].tokenOracle); - supportedToken[token].previousPrice = lasestTokenPrice; + uint192 nativeAssetPrice = fetchPrice(nativeAssetOracle); + supportedToken[token].previousPrice = + nativeAssetPrice * uint192(supportedToken[token].tokenDecimals) / lasestTokenPrice; emit UserOperationSponsored(sender, token, tokenRequiredFund, actualGasCost); } diff --git a/script/PaymasterDeployer.s.sol b/script/PaymasterDeployer.s.sol index 4cc62b7d..b26954a9 100644 --- a/script/PaymasterDeployer.s.sol +++ b/script/PaymasterDeployer.s.sol @@ -92,7 +92,8 @@ contract PaymasterDeployer is Script, DeployHelper { function delpoyGoerli() private { address testUsdc = 0x07865c6E87B9F70255377e024ace6630C1Eaa37F; - address testOracle = 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e; + address nativeOracle = 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e; + address usdcOracle = 0xAb5c49580294Aff77670F839ea425f5b78ab3Ae7; address paymaster = deploy( "Paymaster", bytes.concat( @@ -102,13 +103,14 @@ contract PaymasterDeployer is Script, DeployHelper { address[] memory tokens = new address[](1); tokens[0] = testUsdc; address[] memory oracles = new address[](1); - oracles[0] = testOracle; + oracles[0] = usdcOracle; uint32[] memory priceMarkups = new uint32[](1); priceMarkups[0] = 1e6; vm.stopBroadcast(); // start broadcast using paymasterOwner vm.startBroadcast(paymasterOwnerPrivateKey); + ERC20Paymaster(paymaster).setNativeAssetOracle(address(nativeOracle)); IEntryPoint(ENTRYPOINT_ADDRESS).depositTo{value: 0.05 ether}(address(paymaster)); ERC20Paymaster(paymaster).addStake{value: 0.03 ether}(1); @@ -119,7 +121,8 @@ contract PaymasterDeployer is Script, DeployHelper { function delpoySepolia() private { address testUsdc = 0x94a9D9AC8a22534E3FaCa9F4e7F2E2cf85d5E4C8; - address testOracle = 0x694AA1769357215DE4FAC081bf1f309aDC325306; + address usdcOracle = 0xA2F78ab2355fe2f984D808B5CeE7FD0A93D5270E; + address nativeOracle = 0x694AA1769357215DE4FAC081bf1f309aDC325306; address paymaster = deploy( "Paymaster", bytes.concat( @@ -129,7 +132,7 @@ contract PaymasterDeployer is Script, DeployHelper { address[] memory tokens = new address[](1); tokens[0] = testUsdc; address[] memory oracles = new address[](1); - oracles[0] = testOracle; + oracles[0] = usdcOracle; uint32[] memory priceMarkups = new uint32[](1); priceMarkups[0] = 1e6; @@ -137,6 +140,8 @@ contract PaymasterDeployer is Script, DeployHelper { // start broadcast using paymasterOwner vm.startBroadcast(paymasterOwnerPrivateKey); + ERC20Paymaster(paymaster).setNativeAssetOracle(address(nativeOracle)); + IEntryPoint(ENTRYPOINT_ADDRESS).depositTo{value: 0.05 ether}(address(paymaster)); ERC20Paymaster(paymaster).addStake{value: 0.03 ether}(1); @@ -144,37 +149,7 @@ contract PaymasterDeployer is Script, DeployHelper { ERC20Paymaster(paymaster).updatePrice(address(testUsdc)); } - function delpoyArbGoerli() private { - address testUsdc_bridge = 0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892; - address testUsdc_circle = 0xfd064A18f3BF249cf1f87FC203E90D8f650f2d63; - address testOracle = 0x62CAe0FA2da220f43a51F86Db2EDb36DcA9A5A08; - address paymaster = deploy( - "Paymaster", - bytes.concat( - type(ERC20Paymaster).creationCode, abi.encode(ENTRYPOINT_ADDRESS, paymasterOwner, soulwalletFactory) - ) - ); - address[] memory tokens = new address[](2); - tokens[0] = testUsdc_bridge; - tokens[1] = testUsdc_circle; - address[] memory oracles = new address[](2); - oracles[0] = testOracle; - oracles[1] = testOracle; - uint32[] memory priceMarkups = new uint32[](2); - priceMarkups[0] = 1e6; - priceMarkups[1] = 1e6; - - vm.stopBroadcast(); - // start broadcast using paymasterOwner - vm.startBroadcast(paymasterOwnerPrivateKey); - - IEntryPoint(ENTRYPOINT_ADDRESS).depositTo{value: 0.03 ether}(address(paymaster)); - ERC20Paymaster(paymaster).addStake{value: 0.03 ether}(1); - - ERC20Paymaster(paymaster).setToken(tokens, oracles, priceMarkups); - ERC20Paymaster(paymaster).updatePrice(address(testUsdc_bridge)); - ERC20Paymaster(paymaster).updatePrice(address(testUsdc_circle)); - } + function delpoyArbGoerli() private {} function delpoyArbSepolia() private { address paymaster = deploy( @@ -204,37 +179,7 @@ contract PaymasterDeployer is Script, DeployHelper { ERC20Paymaster(paymaster).addStake{value: 0.03 ether}(1); } - function delpoyOpGoerli() private { - address testUsdc_bridge = 0xe05606174bac4A6364B31bd0eCA4bf4dD368f8C6; - address testUsdc_circle = 0x7E07E15D2a87A24492740D16f5bdF58c16db0c4E; - address testOracle = 0x57241A37733983F97C4Ab06448F244A1E0Ca0ba8; - address paymaster = deploy( - "Paymaster", - bytes.concat( - type(ERC20Paymaster).creationCode, abi.encode(ENTRYPOINT_ADDRESS, paymasterOwner, soulwalletFactory) - ) - ); - address[] memory tokens = new address[](2); - tokens[0] = testUsdc_bridge; - tokens[1] = testUsdc_circle; - address[] memory oracles = new address[](2); - oracles[0] = testOracle; - oracles[1] = testOracle; - uint32[] memory priceMarkups = new uint32[](2); - priceMarkups[0] = 1e6; - priceMarkups[1] = 1e6; - - vm.stopBroadcast(); - // start broadcast using paymasterOwner - vm.startBroadcast(paymasterOwnerPrivateKey); - - IEntryPoint(ENTRYPOINT_ADDRESS).depositTo{value: 0.03 ether}(address(paymaster)); - ERC20Paymaster(paymaster).addStake{value: 0.03 ether}(1); - - ERC20Paymaster(paymaster).setToken(tokens, oracles, priceMarkups); - ERC20Paymaster(paymaster).updatePrice(address(testUsdc_bridge)); - ERC20Paymaster(paymaster).updatePrice(address(testUsdc_circle)); - } + function delpoyOpGoerli() private {} function delpoylocalEntryPoint() private { ENTRYPOINT_ADDRESS = deploy("EntryPoint", type(EntryPoint).creationCode); diff --git a/test/paymaster/ERC20Paymaster.t.sol b/test/paymaster/ERC20Paymaster.t.sol index 36172353..c8d9a409 100644 --- a/test/paymaster/ERC20Paymaster.t.sol +++ b/test/paymaster/ERC20Paymaster.t.sol @@ -33,6 +33,7 @@ contract ERC20PaymasterTest is Test, UserOpHelper { address payable beneficiary; TokenERC20 token; TestOracle testOracle; + TestOracle nativeAssetOracle; HelloWorld helloWorld; function setUp() public { @@ -41,7 +42,8 @@ contract ERC20PaymasterTest is Test, UserOpHelper { beneficiary = payable(makeAddr("beneficiary")); token = new TokenERC20(6); - testOracle = new TestOracle(190355094900); + testOracle = new TestOracle(166590000); + nativeAssetOracle = new TestOracle(190355094900); helloWorld = new HelloWorld(); bundler = new Bundler(); bytes[] memory modules = new bytes[](0); @@ -68,6 +70,7 @@ contract ERC20PaymasterTest is Test, UserOpHelper { vm.deal(paymasterOwner, 10000e18); vm.startPrank(paymasterOwner); + paymaster.setNativeAssetOracle(address(nativeAssetOracle)); entryPoint.depositTo{value: 1000e18}(address(paymaster)); paymaster.addStake{value: 1000e18}(1); address[] memory tokens = new address[](1); diff --git a/test/paymaster/ERC20PaymasterActiveWallet.t.sol b/test/paymaster/ERC20PaymasterActiveWallet.t.sol index 36e3dff4..cd7aa0e4 100644 --- a/test/paymaster/ERC20PaymasterActiveWallet.t.sol +++ b/test/paymaster/ERC20PaymasterActiveWallet.t.sol @@ -41,6 +41,7 @@ contract ERC20PaymasterActiveWalletTest is Test, UserOpHelper { address payable beneficiary; TokenERC20 token; TestOracle testOracle; + TestOracle nativeAssetOracle; HelloWorld helloWorld; function setUp() public { @@ -50,7 +51,8 @@ contract ERC20PaymasterActiveWalletTest is Test, UserOpHelper { beneficiary = payable(makeAddr("beneficiary")); token = new TokenERC20(6); - testOracle = new TestOracle(190355094900); + testOracle = new TestOracle(166590000); + nativeAssetOracle = new TestOracle(190355094900); helloWorld = new HelloWorld(); bundler = new Bundler(); @@ -76,6 +78,7 @@ contract ERC20PaymasterActiveWalletTest is Test, UserOpHelper { vm.deal(paymasterOwner, 10000e18); vm.startPrank(paymasterOwner); + paymaster.setNativeAssetOracle(address(nativeAssetOracle)); entryPoint.depositTo{value: 1000e18}(address(paymaster)); paymaster.addStake{value: 1000e18}(1); address[] memory tokens = new address[](1);