Impact: Medium severity—trapped ETH could lead to loss of user funds, though not exploitable for theft.
function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
require(
msg.value == s_currentMintPrice,
WeatherNft__InvalidAmountSent()
);
s_currentMintPrice += s_stepIncreasePerMint;
if (_registerKeeper) {
IERC20(s_link).safeTransferFrom(
msg.sender,
address(this),
_initLinkDeposit
);
}
_reqId = _sendFunctionsWeatherFetchRequest(_pincode, _isoCode);
emit WeatherNFTMintRequestSent(msg.sender, _pincode, _isoCode, _reqId);
s_funcReqIdToUserMintReq[_reqId] = UserMintRequest({
user: msg.sender,
pincode: _pincode,
isoCode: _isoCode,
registerKeeper: _registerKeeper,
heartbeat: _heartbeat,
initLinkDeposit: _initLinkDeposit
});
}
## Risk
**Likelihood**:
Users send excess ETH when minting an NFT, as the require only checks for equality but doesn’t refund overpayments.
The minting process fails after ETH is sent, such as if _sendFunctionsWeatherFetchRequest reverts, leaving ETH in the contract.
**Impact**:
Users lose access to ETH sent in excess or during failed mints, as there’s no way to retrieve it.
The contract owner cannot manage or refund trapped ETH, potentially leading to user disputes or loss of trust.
## Proof of Concept
```
function attack() external payable {
WeatherNft(weatherNftAddress).requestMintWeatherNFT{value: 0.2 ether}(
"12345",
"US",
false,
0,
0
);
}
```
As we can see, Result: 0.1 ETH is trapped since `require` passes but excess isn’t refunded
## Recommended Mitigation
Add a `withdrawEther` function to allow the contract owner to retrieve trapped ETH, ensuring funds aren’t permanently locked.
```
+
+ function withdrawEther() external onlyOwner {
+ payable(msg.sender).transfer(address(this).balance);
+ }
```
Alternatively, refund excess ETH directly in requestMintWeatherNFT:
```diff
function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
- require(
- msg.value == s_currentMintPrice,
- WeatherNft__InvalidAmountSent()
- );
+ require(
+ msg.value >= s_currentMintPrice,
+ WeatherNft__InvalidAmountSent()
+ );
+ if (msg.value > s_currentMintPrice) {
+ payable(msg.sender).transfer(msg.value - s_currentMintPrice);
+ }
s_currentMintPrice += s_stepIncreasePerMint;
}
```