5/5
_Follow along with this video:_ --- ### Proving Minting NFTs With a couple sanity checks out of the way, let's try something much harder and validate that `minting mints one NFT`. We'll set this up as a `rule` in our `NftMock.spec` file. We'll also have to add the `mint` function to our methods block, don't forget! ```js methods { function totalSupply() external returns uint256 envfree; function mint() external; } rule minting_mints_one_nft() { // Arrange // Act // Assert } ``` For this test, we're not going to configure things as `envfree`. This will afford us an opportunity to practice working with environment variables in `Certora`. We're going to set the `msg.value to 0` and assure that the address calling our mint function is always an expected `minter` address. ```js methods { function totalSupply() external returns uint256 envfree; function mint() external; } rule minting_mints_one_nft() { // Arrange env e; address minter; require e.msg.value == 0; require e.msg.sender == minter; // Act // Assert } ``` Now, in order to assure only 1 NFT is minted, we'll need to check the balance before and after minting. We can do this by adding balanceOf to our methods block and calling it in our test as demonstrated below. ```js methods { function totalSupply() external returns uint256 envfree; function mint() external; function balanceOf(address) external returns uint256 envfree; } rule minting_mints_one_nft() { // Arrange env e; address minter; require e.msg.value == 0; require e.msg.sender == minter; uint256 balanceBefore = balanceOf(minter); // Act // Assert } ``` It's time to act and finally mint the NFT, any time we want to reference the current contract that `Certora` is configured to verify, we can do so using `currentContract`. We'll then `assert` that the balance of `minter` _after_ the mint call is equal to the `balanceBefore` + 1. ```js methods { function totalSupply() external returns uint256 envfree; function mint() external; function balanceOf(address) external returns uint256 envfree; } rule minting_mints_one_nft() { // Arrange env e; address minter; require e.msg.value == 0; require e.msg.sender == minter; uint256 balanceBefore = balanceOf(minter); // Act currentContract.mint(e); // Assert assert(balanceOf(minter) == balanceBefore + 1, "Minting should only mint 1 NFT."); } ``` The above would absolutely work for our purposes, but it's worth pointing out that `uint256`, as we know, has a max value, and adding to that value is going to cause `overflow` and result in potential issues with our test. We can avoid this by instead typing `balanceBefore`, and the result of `balanceOf` in our assert statement, as `mathint`. ```js methods { function totalSupply() external returns uint256 envfree; function mint() external; function balanceOf(address) external returns uint256 envfree; } rule minting_mints_one_nft() { // Arrange env e; address minter; require e.msg.value == 0; require e.msg.sender == minter; mathint balanceBefore = balanceOf(minter); // Act currentContract.mint(e); // Assert assert(to_mathint(balanceOf(minter)) == balanceBefore + 1, "Minting should only mint 1 NFT."); } ``` ### Running the Prover With that, we can comment out our totalSupply invariant and run our prover to see what Certora can find. ```bash certoraRun ./certora/conf/NftMock.conf ``` And, after a brief wait for the server/process, we should see a successfully verification.  Good job!
Follow along with this video:
With a couple sanity checks out of the way, let's try something much harder and validate that minting mints one NFT. We'll set this up as a rule in our NftMock.spec file. We'll also have to add the mint function to our methods block, don't forget!
For this test, we're not going to configure things as envfree. This will afford us an opportunity to practice working with environment variables in Certora. We're going to set the msg.value to 0 and assure that the address calling our mint function is always an expected minter address.
Now, in order to assure only 1 NFT is minted, we'll need to check the balance before and after minting. We can do this by adding balanceOf to our methods block and calling it in our test as demonstrated below.
It's time to act and finally mint the NFT, any time we want to reference the current contract that Certora is configured to verify, we can do so using currentContract. We'll then assert that the balance of minter after the mint call is equal to the balanceBefore + 1.
The above would absolutely work for our purposes, but it's worth pointing out that uint256, as we know, has a max value, and adding to that value is going to cause overflow and result in potential issues with our test. We can avoid this by instead typing balanceBefore, and the result of balanceOf in our assert statement, as mathint.
With that, we can comment out our totalSupply invariant and run our prover to see what Certora can find.
And, after a brief wait for the server/process, we should see a successfully verification.

Good job!
In this lesson we set up and run tests to verify that minting does in fact only mint one NFT.
Previous lesson
Previous
Next lesson
Next
Course Overview
About the course
Assembly
Writing smart contracts using Huff and Yul
Ethereum Virtual Machine OPCodes
Formal verification testing
Smart contract invariant testing
Halmos, Certora, Kontrol
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Guest lecturers:
Last updated on August 11, 2025
Duration: 30min
Duration: 4h 39min
Duration: 3h 58min
Duration: 1h 56min
Course Overview
About the course
Assembly
Writing smart contracts using Huff and Yul
Ethereum Virtual Machine OPCodes
Formal verification testing
Smart contract invariant testing
Halmos, Certora, Kontrol
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Guest lecturers:
Last updated on August 11, 2025