5/5
## Verifying Smart Contract Reverts: Testing Minimum Funding Requirements Ensuring smart contracts function correctly under ideal conditions is only half the battle. Robust contracts must also behave predictably when conditions *aren't* met. This lesson focuses on verifying failure conditions, specifically how to test a `revert` triggered by an `assert` statement designed to enforce a minimum funding amount in USD. Consider a Vyper smart contract with a `payable` function, let's call it `fund`, designed to accept Ether (ETH). A common requirement is to ensure users send a minimum value, not just in ETH, but in its equivalent US Dollar (USD) value. In our example, the contract requires a minimum of $5 USD, configured during deployment. Vyper provides the `assert` statement to enforce conditions that *must* be true for execution to continue. If the condition evaluates to `false`, the transaction reverts immediately, undoing any state changes made up to that point. The core logic we're testing looks like this: ```vyper # Inside the @payable fund function # Convert the received ETH (msg.value in Wei) to its USD equivalent usd_value_of_eth: uint256 = self.get_eth_to_usd_rate(msg.value) # Assert that the USD value meets or exceeds the minimum requirement assert usd_value_of_eth >= self.minimum_usd, "You must spend more ETH!" # ... rest of the function logic (e.g., updating balances) ``` Here, `usd_value_of_eth` (calculated from the incoming `msg.value` using an internal price conversion function, likely leveraging a Chainlink Price Feed via `self.get_eth_to_usd_rate`) is compared against `self.minimum_usd` (pre-set to $5, potentially stored with 18 decimals for precision). If the sent value is insufficient, the `assert` fails, the transaction reverts, and the provided error message ('You must spend more ETH!') is triggered (though the exact display might vary in tools like Remix). A key challenge in testing this logic lies in units and conversion. Smart contracts typically receive ETH values via `msg.value` in Wei (1 ETH = 10^18 Wei). However, our requirement is in USD. Therefore, the contract needs an internal mechanism to convert the incoming Wei amount to its USD equivalent, considering the current ETH/USD price. When testing, you'll need to calculate the correct Wei amount corresponding to specific USD values (e.g., just above and just below $5). Tools like `eth-converter.com` are invaluable for this, helping translate between ETH, Wei, and providing a USD estimate. Pay close attention to decimals; both the minimum USD value (`self.minimum_usd`) and the calculated USD value often use a fixed-point representation (e.g., 18 decimals) for on-chain calculations. To test the contract, we'll perform two main checks using a development environment like Remix connected to a testnet or local fork (e.g., via Tenderly RPC and Metamask): **1. Testing the Success Case (Sufficient Funds):** * Determine the current approximate ETH/USD rate. * Calculate an ETH amount slightly *above* the $5 minimum (e.g., $6 worth). * Use a converter (`eth-converter.com`) to find the Wei equivalent for this ETH amount (e.g., `2000000000000000` Wei if $6 ≈ 0.002 ETH). * Optionally, use the contract's view function (`get_eth_to_usd_rate` in our example) to double-check the contract's expected USD value for that Wei amount. * Call the `fund` function via Remix, sending the calculated Wei amount in the `value` field. * Observe the transaction: It should succeed (e.g., green checkmark in Remix terminal), and any expected state changes (like an increase in the contract's ETH balance) should occur. **2. Testing the Failure Case (Insufficient Funds - The Revert):** This is the crucial test to ensure the `assert` is working correctly. * Using the same ETH/USD rate, calculate an ETH amount *below* the $5 minimum (e.g., $3 worth). * Convert this ETH amount to its Wei equivalent (e.g., `1000000000000000` Wei if $3 ≈ 0.001 ETH). * Optionally, verify this amount is indeed below the threshold using the contract's view function (`get_eth_to_usd_rate`). * Call the `fund` function via Remix, sending this *lower* Wei amount in the `value` field. * Observe the outcome: * The IDE (like Remix) might predict the failure, often showing a 'Gas estimation failed' warning, indicating a likely revert before you even send the transaction. * Submit the transaction anyway (using Metamask). * The transaction will appear in the transaction list but will be marked as failed or reverted (e.g., with a red 'X' in Remix). * Crucially, check that the intended state changes *did not* happen (e.g., the contract's ETH balance remains unchanged). This confirms the `assert` statement successfully prevented the state update due to insufficient funds. A critical aspect of reverts is that **reverted transactions still consume gas**. Although the state changes are undone (as if the transaction never ran its course), the computational effort expended up to the point of the revert must be paid for by the sender. Your testing tools will show a `transaction cost` (in gas units) even for failed transactions. This reinforces why validating failure conditions is vital – you want reverts to happen only when genuinely necessary, preventing unintended state changes while being mindful of user gas costs. This testing process typically involves tools like: * **Remix IDE:** For compiling, deploying, and interacting with the contract. * **Metamask:** As the wallet interface to sign and send transactions. * **Test Environment:** A local blockchain, a public testnet, or a forked environment (like using a Tenderly RPC) to avoid spending real assets during development. * **ETH/Wei Converter:** An external tool (`eth-converter.com`) for calculating appropriate `msg.value` amounts. Thoroughly testing smart contract failure conditions, like reverts triggered by `assert` statements, is non-negotiable for building secure and reliable decentralized applications. By systematically verifying that your contract correctly handles invalid inputs or unmet conditions, you ensure it behaves predictably and protects its state integrity, even when users attempt to interact with it incorrectly.
An essential walkthrough to Verifying Smart Contract Reverts: Testing Minimum Funding Requirements - Discover how to test smart contract reverts caused by insufficient USD funding, focusing on Vyper's `assert`, required Wei calculations, and verifying failure conditions using development tools.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025
Duration: 2h 08min
Duration: 2h 32min
Duration: 24min
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025