diff --git a/contracts/SmartRoute/DODOGasProxy.sol b/contracts/SmartRoute/DODOGasProxy.sol index b1f08c0..30f8621 100644 --- a/contracts/SmartRoute/DODOGasProxy.sol +++ b/contracts/SmartRoute/DODOGasProxy.sol @@ -21,9 +21,13 @@ contract DODOGasProxy is InitializableOwnable { // DODOApproveProxy address for token claiming address public immutable _DODO_APPROVE_PROXY_; uint256 public nonce; + uint256 public totalGasFee; // Total transferred in gas fee collected + uint256 public totalBridgeGasFee; // Total bridge gas fee collected // Gas fee mapping for each target chain mapping(uint64 => uint256) public chainGasFee; + // Gas fee mapping for each cross-chain bridge + mapping(uint64 => uint256) public bridgeGasFee; // Whitelist mapping for approved contracts mapping(address => bool) public isWhiteListedContract; // Whitelist mapping for approved approve contracts @@ -34,15 +38,32 @@ contract DODOGasProxy is InitializableOwnable { // Pause state bool public paused; + + // For readability + struct ChainGasFee { + uint64 chainId; + uint256 gasFee; + } + + struct BridgeGasFee { + uint64 chainId; + uint256 gasFee; + } + + uint64[] internal chainIndexs; + uint64[] internal bridgeIndexs; // ============ Events ============ event GasFeeChanged(uint64 chainId, uint256 newFee); + event BridgeGasFeeChanged(uint64 chainId, uint256 newFee); event WhiteListChanged(address indexed target, bool isWhiteListed); event WhiteListApproveChanged(address indexed target, bool isWhiteListed); event BotChanged(address indexed newBot); event GasFeePaid(address payer, uint64 chainId, uint256 gasFee, bytes32 externalID); + event BridgeGasFeePaid(address payer, uint64 chainId, uint256 gasFee, bytes32 externalID); event PausedStateChanged(bool newState); + event NativeDrip(address payer, uint64 chainId, uint256 amount, bytes32 externalID); // ============ Modifiers ============ @@ -99,10 +120,27 @@ contract DODOGasProxy is InitializableOwnable { for (uint256 i = 0; i < chainIds.length; i++) { chainGasFee[chainIds[i]] = gasFees[i]; + bridgeIndexs.push(chainIds[i]); emit GasFeeChanged(chainIds[i], gasFees[i]); } } + /// @notice Batch set gas fees for cross-chain bridges + /// @param chainIds Array of chain IDs + /// @param gasFees Array of corresponding gas fees + function batchSetBridgeGasFee( + uint64[] calldata chainIds, + uint256[] calldata gasFees + ) external onlyBot { + require(chainIds.length == gasFees.length, "DODOGasProxy: LENGTH_MISMATCH"); + + for (uint256 i = 0; i < chainIds.length; i++) { + bridgeGasFee[chainIds[i]] = gasFees[i]; + bridgeIndexs.push(chainIds[i]); + emit BridgeGasFeeChanged(chainIds[i], gasFees[i]); + } + } + /// @notice Batch set contract whitelist status /// @param targets Array of target addresses /// @param isWhiteListed Array of whitelist status @@ -142,6 +180,8 @@ contract DODOGasProxy is InitializableOwnable { /// @param approveTarget Address to approve tokens for /// @param executeTarget Contract to execute call on /// @param transferInGasFee Extra amount of gas fee transferred in + /// @param transferbridgeGasFee Extra amount of gas fee for bridge transferred in + /// @param nativeDrip Extra amount of native token to drip to target contract /// @param callData Call data for target contract function proxyCall( uint64 chainId, @@ -150,6 +190,8 @@ contract DODOGasProxy is InitializableOwnable { address approveTarget, address executeTarget, uint256 transferInGasFee, + uint256 transferbridgeGasFee, + uint256 nativeDrip, bytes calldata callData ) external payable whenNotPaused returns (bytes memory) { // Check if target contract is whitelisted @@ -157,15 +199,16 @@ contract DODOGasProxy is InitializableOwnable { require(isWhiteListedContract[executeTarget], "DODOGasProxy: NOT_WHITELISTED"); // Check and collect gas fee - uint256 requiredGasFee = chainGasFee[chainId]; - require(requiredGasFee > 0, "DODOGasProxy: GAS_FEE_NOT_SET"); - require(transferInGasFee > requiredGasFee, "DODOGasProxy: INSUFFICIENT_TRANSFER_IN_GAS_FEE"); + require(chainGasFee[chainId] > 0 && bridgeGasFee[chainId] > 0, "DODOGasProxy: GAS_FEE_NOT_SET"); + require(transferInGasFee >= chainGasFee[chainId], "DODOGasProxy: INSUFFICIENT_TRANSFER_IN_GAS_FEE"); + require(transferbridgeGasFee >= bridgeGasFee[chainId], "DODOGasProxy: INSUFFICIENT_BRIDGE_GAS_FEE"); + // Transfer user tokens if(fromToken == _ETH_ADDRESS_) { - require(msg.value >= fromTokenAmount + transferInGasFee, "DODOGasProxy: INVALID_ETH_AMOUNT"); + require(msg.value >= fromTokenAmount + transferInGasFee + transferbridgeGasFee + nativeDrip, "DODOGasProxy: INVALID_ETH_AMOUNT"); } else { - require(msg.value >= transferInGasFee, "DODOGasProxy: INVALID_GAS_FEE"); + require(msg.value >= transferInGasFee + transferbridgeGasFee + nativeDrip, "DODOGasProxy: INVALID_GAS_FEE"); // Use DODOApproveProxy to claim tokens instead of transferFrom IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens( fromToken, @@ -181,18 +224,41 @@ contract DODOGasProxy is InitializableOwnable { // generate externalID nonce++; - bytes32 externalID = keccak256(abi.encodePacked(block.timestamp, msg.sender, chainId, transferInGasFee, nonce)); - emit GasFeePaid(msg.sender, chainId, requiredGasFee, externalID); + bytes32 externalID = keccak256(abi.encodePacked(block.timestamp, msg.sender, chainId, transferInGasFee, transferbridgeGasFee, nonce)); + totalGasFee += transferInGasFee; + totalBridgeGasFee += transferbridgeGasFee; + emit GasFeePaid(msg.sender, chainId, transferInGasFee, externalID); + emit BridgeGasFeePaid(msg.sender, chainId, transferbridgeGasFee, externalID); // Call target contract (bool success, bytes memory result) = executeTarget.call{ - value: fromToken == _ETH_ADDRESS_ ? fromTokenAmount : 0 + value: fromToken == _ETH_ADDRESS_ ? fromTokenAmount + nativeDrip : nativeDrip }(callData); + + if(nativeDrip > 0) { + emit NativeDrip(msg.sender, chainId, nativeDrip, externalID); + } require(success, "DODOGasProxy: CALL_FAILED"); return result; } + function getChainGasFee() public view returns (ChainGasFee[] memory) { + ChainGasFee[] memory list = new ChainGasFee[](chainIndexs.length); + for (uint256 i = 0; i < chainIndexs.length; i++) { + list[i] = ChainGasFee(chainIndexs[i], chainGasFee[chainIndexs[i]]); + } + return list; + } + + function getBridgeGasFee() public view returns (BridgeGasFee[] memory) { + BridgeGasFee[] memory list = new BridgeGasFee[](bridgeIndexs.length); + for (uint256 i = 0; i < bridgeIndexs.length; i++) { + list[i] = BridgeGasFee(bridgeIndexs[i], bridgeGasFee[bridgeIndexs[i]]); + } + return list; + } + receive() external payable {} fallback() external payable {} } diff --git a/contracts/SmartRoute/DOODGasDistributor.sol b/contracts/SmartRoute/DOODGasDistributor.sol index c35a42a..68fbcd3 100644 --- a/contracts/SmartRoute/DOODGasDistributor.sol +++ b/contracts/SmartRoute/DOODGasDistributor.sol @@ -50,7 +50,8 @@ contract DOODGasDistributor is InitializableOwnable { emit Distribution(to, distributionAmount, externalId); } - function setMaxAmount(uint256 _maxAmount) external onlyOwner { + function setMaxAmount(uint256 _maxAmount) external { + require(msg.sender == bot || msg.sender == _OWNER_, "Only bot or owner"); maxAmount = _maxAmount; emit MaxAmountUpdated(_maxAmount); }