From e747ec72eca61a9a1fc67dacb68e10fc848a1b7b Mon Sep 17 00:00:00 2001 From: Shivaansh Kapoor Date: Mon, 9 Sep 2024 12:27:01 +0400 Subject: [PATCH] added price expiry duration --- .../common/BiconomyTokenPaymasterErrors.sol | 5 +++ .../interfaces/IBiconomyTokenPaymaster.sol | 9 ++-- contracts/token/BiconomyTokenPaymaster.sol | 42 +++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/contracts/common/BiconomyTokenPaymasterErrors.sol b/contracts/common/BiconomyTokenPaymasterErrors.sol index 48de59d..6a3235a 100644 --- a/contracts/common/BiconomyTokenPaymasterErrors.sol +++ b/contracts/common/BiconomyTokenPaymasterErrors.sol @@ -41,4 +41,9 @@ contract BiconomyTokenPaymasterErrors { * @notice Throws when oracle returns invalid price */ error OraclePriceNotPositive(); + + /** + * @notice Throws when oracle price hasn't been updated for a duration of time the owner is comfortable with + */ + error OraclePriceExpired(); } diff --git a/contracts/interfaces/IBiconomyTokenPaymaster.sol b/contracts/interfaces/IBiconomyTokenPaymaster.sol index 724e2b4..50189f0 100644 --- a/contracts/interfaces/IBiconomyTokenPaymaster.sol +++ b/contracts/interfaces/IBiconomyTokenPaymaster.sol @@ -10,9 +10,10 @@ interface IBiconomyTokenPaymaster { uint8 decimals; } - event UnaccountedGasChanged(uint256 indexed oldValue, uint256 indexed newValue); - event FixedDynamicAdjustmentChanged(uint256 indexed oldValue, uint256 indexed newValue); - event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector, address indexed actor); + event UpdatedUnaccountedGas(uint256 indexed oldValue, uint256 indexed newValue); + event UpdatedFixedDynamicAdjustment(uint256 indexed oldValue, uint256 indexed newValue); + event UpdatedFeeCollector(address indexed oldFeeCollector, address indexed newFeeCollector, address indexed actor); + event UpdatedPriceExpiryDuration(uint256 indexed oldValue, uint256 indexed newValue); event GasDeposited(address indexed paymasterId, uint256 indexed value); event GasWithdrawn(address indexed paymasterId, address indexed to, uint256 indexed value); event GasBalanceDeducted(address indexed paymasterId, uint256 indexed charge, bytes32 indexed userOpHash); @@ -27,5 +28,7 @@ interface IBiconomyTokenPaymaster { function setDynamicAdjustment(uint256 _newUnaccountedGas) external payable; + function setPriceExpiryDuration(uint256 _newPriceExpiryDuration) external payable; + function setTokenInfo(address _tokenAddress, IOracle _oracle, uint8 _decimals) external payable; } diff --git a/contracts/token/BiconomyTokenPaymaster.sol b/contracts/token/BiconomyTokenPaymaster.sol index 7a62cb0..af3dc4c 100644 --- a/contracts/token/BiconomyTokenPaymaster.sol +++ b/contracts/token/BiconomyTokenPaymaster.sol @@ -17,10 +17,12 @@ import "@account-abstraction/contracts/core/Helpers.sol"; * @author ShivaanshK * @author livingrockrises * @notice Token Paymaster for Entry Point v0.7 - * @dev A paymaster that allows user to pay gas fees in ERC20 tokens. The paymaster owner chooses which tokens to - * accept. The payment manager (usually the owner) first deposits native gas into the EntryPoint. Then, for each - * transaction, it takes the gas fee from the user's ERC20 token balance. The exchange rate between ETH and the token is - * calculated using 1 of three methods: external price source, off-chain oracle, or a TWAP oracle. + * @dev A paymaster that allows user to pay gas fees in ERC20 tokens. The paymaster uses the precharge and refund model + * to handle gas remittances. For fair and "always available" operation, it relies on price oracles which + * implement the IOracle interface to calculate the gas cost of the transaction in a supported token. The owner has full + * discretion over the supported tokens, premium and discounts applied (if any), and how to manage the assets + * received by the paymaster. The properties described above, make the paymaster self-relaint: independent of any + * offchain service for use. */ contract BiconomyTokenPaymaster is BasePaymaster, @@ -34,13 +36,14 @@ contract BiconomyTokenPaymaster is address public feeCollector; uint256 public unaccountedGas; uint256 public dynamicAdjustment; + uint256 public priceExpiryDuration; IOracle public nativeOracle; // ETH -> USD price mapping(address => TokenInfo) tokenDirectory; // Limit for unaccounted gas cost uint256 private constant UNACCOUNTED_GAS_LIMIT = 50_000; uint256 private constant PRICE_DENOMINATOR = 1e6; - uint256 private constant MAX_DYNAMIC_ADJUSTMENT = 2e6; + uint256 private constant MAX_DYNAMIC_ADJUSTMENT = 2e6; // 100% premium on price (2e6/PRICE_DENOMINATOR) constructor( address _owner, @@ -48,6 +51,7 @@ contract BiconomyTokenPaymaster is uint256 _unaccountedGas, uint256 _dynamicAdjustment, IOracle _nativeOracle, + uint256 _priceExpiryDuration, address[] memory _tokens, // Array of token addresses uint8[] memory _decimals, // Array of corresponding token decimals IOracle[] memory _oracles // Array of corresponding oracle addresses @@ -176,7 +180,7 @@ contract BiconomyTokenPaymaster is assembly ("memory-safe") { sstore(feeCollector.slot, _newFeeCollector) } - emit FeeCollectorChanged(oldFeeCollector, _newFeeCollector, msg.sender); + emit UpdatedFeeCollector(oldFeeCollector, _newFeeCollector, msg.sender); } /** @@ -192,12 +196,12 @@ contract BiconomyTokenPaymaster is assembly ("memory-safe") { sstore(unaccountedGas.slot, _newUnaccountedGas) } - emit UnaccountedGasChanged(oldUnaccountedGas, _newUnaccountedGas); + emit UpdatedUnaccountedGas(oldUnaccountedGas, _newUnaccountedGas); } /** * @dev Set a new dynamicAdjustment value. - * @param _newDynamicAdjustment The new value to be set as the unaccounted gas value + * @param _newDynamicAdjustment The new value to be set as the dynamic adjustment * @notice only to be called by the owner of the contract. */ function setDynamicAdjustment(uint256 _newDynamicAdjustment) external payable override onlyOwner { @@ -208,7 +212,20 @@ contract BiconomyTokenPaymaster is assembly ("memory-safe") { sstore(dynamicAdjustment.slot, _newDynamicAdjustment) } - emit FixedDynamicAdjustmentChanged(oldDynamicAdjustment, _newDynamicAdjustment); + emit UpdatedFixedDynamicAdjustment(oldDynamicAdjustment, _newDynamicAdjustment); + } + + /** + * @dev Set a new dynamicAdjustment value. + * @param _newPriceExpiryDuration The new value to be set as the unaccounted gas value + * @notice only to be called by the owner of the contract. + */ + function setPriceExpiryDuration(uint256 _newPriceExpiryDuration) external payable override onlyOwner { + uint256 oldPriceExpiryDuration = priceExpiryDuration; + assembly ("memory-safe") { + sstore(priceExpiryDuration.slot, _newPriceExpiryDuration) + } + emit UpdatedPriceExpiryDuration(oldPriceExpiryDuration, _newPriceExpiryDuration); } /** @@ -229,6 +246,7 @@ contract BiconomyTokenPaymaster is onlyOwner { tokenDirectory[_tokenAddress] = TokenInfo(_oracle, _decimals); + emit UpdatedTokenDirectory(_tokenAddress, _oracle, _decimals); } /** @@ -293,9 +311,9 @@ contract BiconomyTokenPaymaster is if (answer <= 0) { revert OraclePriceNotPositive(); } - // if (updatedAt < block.timestamp - stalenessThreshold) { - // revert OraclePriceStale(); - // } + if (updatedAt < block.timestamp - priceExpiryDuration) { + revert OraclePriceExpired(); + } price = uint192(int192(answer)); } }