Solidity Exercise - Donations

Posted by Bourne's Blog - A Full-stack & Web3 Developer on May 14, 2024

Donations

Design a contract to receive donations from users, but remember a user can donate multiple times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract Donations {
    mapping(address => uint256) public amountDonated;

    receive() external payable {
        // your code here
        // amountDonated should store the amount
        // the person donated
        // don't forget a person can donate
        // multiple times!
    }
}

Problem Analysis

Variable amountDonated is a mapping store user’s donations. We need the donation amount can be accumulated since a same user can donate multiple times.

Coding

1
2
3
4
5
6
7
8
    receive() external payable {
        // your code here
        // amountDonated should store the amount
        // the person donated
        // don't forget a person can donate
        // multiple times!
        amountDonated[msg.sender] += msg.value;
    }

Test

1
2
3
4
5
6
7
8
9
10
11
➜  Donations git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 795.32ms
Compiler run successful!

Ran 1 test for test/Donations.t.sol:DonationsTest
[PASS] testDonate() (gas: 108731)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 877.58µs (90.75µs CPU time)

Ran 1 test suite in 179.55ms (877.58µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Go Deeper

Let’s go a little deeper.

Let see the test case. It only assign 1 ether to an address and assert the result to be 1 ether.

Let’s add case to test if the accumulated donation correct.

First we donate 1 eth, and then donate 2.5 eth, then we assert the total donation will be 3.5 eth.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    function testAccumulate() external{
        vm.deal(address(this), 5 ether);
        (bool success, ) = address(donations).call{value: 1 ether}("");
        require(success, "Send ether failed");

        (success, ) = address(donations).call{value: 2.5 ether}("");
        require(success, "Send ether failed");
        assertEq(
            donations.amountDonated(address(this)),
            3.5 ether,
            "expected amountDonated by address(this) to be 1 ether"
        );
    }

Test Again

1
2
3
4
5
6
7
8
9
10
11
12
➜  Donations git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 805.23ms
Compiler run successful!

Ran 2 tests for test/Donations.t.sol:DonationsTest
[PASS] testAccumulate() (gas: 46492)
[PASS] testDonate() (gas: 69435)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.72ms (1.08ms CPU time)

Ran 1 test suite in 194.03ms (4.72ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)

Great, we add a new test case and passed the test!

Reference: Solidity Exercise - Donations