Skip to content

Commit

Permalink
Init forge and add few tests to LockContract
Browse files Browse the repository at this point in the history
* Init forge and some tests

Co-authored-by: Kamil Koczurek <[email protected]>
  • Loading branch information
scx1332 and kamirr authored Jun 13, 2024
1 parent 2b7f059 commit 11520ec
Show file tree
Hide file tree
Showing 31 changed files with 22,788 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Foundry test suite
on: [push]

jobs:
solidity-tests:
name: Run solidity tests in foundry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Run tests
run: cd gnt2-contracts && forge test -vvv

- name: Run snapshot
run: cd gnt2-contracts && forge snapshot

- name: Upload snapshot
uses: actions/upload-artifact@v4
with:
name: gas-snapshot.txt
path: gnt2-contracts/.gas-snapshot



1 change: 1 addition & 0 deletions gnt2-contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ build/
dist/
/rinkeby-deployment.json
cache/
/out
66 changes: 66 additions & 0 deletions gnt2-contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Foundry

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**

Foundry consists of:

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.

## Documentation

https://book.getfoundry.sh/

## Usage

### Build

```shell
$ forge build
```

### Test

```shell
$ forge test
```

### Format

```shell
$ forge fmt
```

### Gas Snapshots

```shell
$ forge snapshot
```

### Anvil

```shell
$ anvil
```

### Deploy

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```

### Cast

```shell
$ cast <subcommand>
```

### Help

```shell
$ forge --help
$ anvil --help
$ cast --help
```
9 changes: 9 additions & 0 deletions gnt2-contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[profile.default]
# do not include all contracts
src = "src/dummy"
# Inlucde only contracts in the following paths
include_paths = ["test", "src/contracts/GNT2", "src/contracts/LockPayment", "src/contracts/MultiTransfer", "src/contracts/WrapperCall"]
out = "out"
remappings = ["@openzeppelin/=node_modules/@openzeppelin/"]
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
ignored_warnings_from = ["src/contracts/currentlyDeployed/GNT/Token.sol", "src/contracts/currentlyDeployed/MultiSigWallet/MultiSigWallet.sol"]
144 changes: 144 additions & 0 deletions gnt2-contracts/test/LockPayment.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
pragma solidity ^0.8.13;

import "forge-std/console.sol";
import "forge-std/Test.sol";
import "forge-std/mocks/MockERC20.sol";
import {ILockPayment, LockPayment, IERC20 as L_IERC20} from "../src/contracts/LockPayment/LockPayment.sol";

contract MockToken is MockERC20 {
constructor(string memory name, string memory symbol, uint8 decimals) {
initialize(name, symbol, decimals);
}

function mint(address to, uint256 value) public virtual {
_mint(to, value);
}

function burn(address from, uint256 value) public virtual {
_burn(from, value);
}
}

contract LockPaymentTest is Test {
MockToken internal token;
LockPayment internal testee;

function setUp() public {
token = new MockToken("Test Golem Network Token", "tGLM", 18);
testee = new LockPayment(L_IERC20(address(token)));
}

function test_DepositCreate() public {
helper_funds(msg.sender, 3, true);

uint256 expectedId = uint256(uint160(msg.sender)) << 96;
address expectedSpender = address(2);
vm.expectEmit();
emit LockPayment.DepositCreated(expectedId, expectedSpender);

vm.prank(msg.sender);
uint256 depositId = testee.createDeposit(0, address(2), 2, 1, 0);

assertEq(depositId, expectedId);
assertEq(token.balanceOf(msg.sender), 0);
assertEq(token.balanceOf(address(testee)), 3);

ILockPayment.DepositView memory deposit = testee.getDeposit(depositId);
assertEq(deposit.id, depositId);
assertEq(deposit.funder, msg.sender);
assertEq(deposit.spender, expectedSpender);
assertEq(deposit.amount, 2);
assertEq(deposit.validTo, 0);
}

function test_DepositCreate_NonceReused() public {
helper_funds(msg.sender, 6, true);

vm.prank(msg.sender);
testee.createDeposit(12, address(2), 2, 1, 0);

assertEq(token.balanceOf(msg.sender), 3);
assertEq(token.balanceOf(address(testee)), 3);

vm.prank(msg.sender);
vm.expectRevert("deposits[id].spender == address(0)");
testee.createDeposit(12, address(2), 2, 1, 0);

assertEq(token.balanceOf(msg.sender), 3);
assertEq(token.balanceOf(address(testee)), 3);
}

function test_DepositCreate_ZeroAmount() public {
helper_funds(msg.sender, 3, true);

vm.prank(msg.sender);
vm.expectRevert("amount > 0");
testee.createDeposit(0, address(2), 0, 1, 0);

assertEq(token.balanceOf(msg.sender), 3);
assertEq(token.balanceOf(address(testee)), 0);
}

function test_DepositCreate_NullSpender() public {
helper_funds(msg.sender, 3, true);

vm.prank(msg.sender);
vm.expectRevert("spender cannot be null address");
testee.createDeposit(0, address(0), 2, 1, 0);

assertEq(token.balanceOf(msg.sender), 3);
assertEq(token.balanceOf(address(testee)), 0);
}

function test_DepositCreate_SelfSpender() public {
helper_funds(msg.sender, 3, true);

vm.prank(msg.sender);
vm.expectRevert("spender cannot be funder");
testee.createDeposit(0, msg.sender, 2, 1, 0);

assertEq(token.balanceOf(msg.sender), 3);
assertEq(token.balanceOf(address(testee)), 0);
}

function test_CloseDeposit() public {
address spender = address(21);

helper_funds(msg.sender, 3, true);

vm.prank(msg.sender);
uint256 depositId = testee.createDeposit(12, spender, 2, 1, 0);
assertEq(token.balanceOf(msg.sender), 0);
assertEq(token.balanceOf(spender), 0);
assertEq(token.balanceOf(address(testee)), 3);

vm.expectEmit();
emit LockPayment.DepositFeeTransfer(depositId, spender, 1);
vm.expectEmit();
emit LockPayment.DepositClosed(depositId, spender);

vm.prank(spender);
testee.closeDeposit(depositId);

assertEq(token.balanceOf(msg.sender), 2);
assertEq(token.balanceOf(spender), 1);
assertEq(token.balanceOf(address(testee)), 0);

ILockPayment.DepositView memory deposit = testee.getDeposit(depositId);
assertEq(deposit.id, depositId);
assertEq(deposit.funder, msg.sender);
assertEq(deposit.spender, address(0xBad));
assertEq(deposit.amount, 0);
assertEq(deposit.validTo, 0);
}

function helper_funds(address addr, uint256 amount, bool approve_testee) internal {
token.mint(addr, amount);
assertEq(token.balanceOf(addr), amount);

if(approve_testee) {
vm.prank(msg.sender);
token.approve(address(testee), amount);
}
}
}
35 changes: 35 additions & 0 deletions gnt2-contracts/test/forge-std/Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;

import {StdStorage} from "./StdStorage.sol";
import {Vm, VmSafe} from "./Vm.sol";

abstract contract CommonBase {
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
// console.sol and console2.sol work by executing a staticcall to this address.
address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
// Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy.
address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
// Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38.
address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))));
// Address of the test contract, deployed by the DEFAULT_SENDER.
address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
// Deterministic deployment address of the Multicall3 contract.
address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
// The order of the secp256k1 curve.
uint256 internal constant SECP256K1_ORDER =
115792089237316195423570985008687907852837564279074904382605163141518161494337;

uint256 internal constant UINT256_MAX =
115792089237316195423570985008687907853269984665640564039457584007913129639935;

Vm internal constant vm = Vm(VM_ADDRESS);
StdStorage internal stdstore;
}

abstract contract TestBase is CommonBase {}

abstract contract ScriptBase is CommonBase {
VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS);
}
27 changes: 27 additions & 0 deletions gnt2-contracts/test/forge-std/Script.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;

// 💬 ABOUT
// Forge Std's default Script.

// 🧩 MODULES
import {console} from "./console.sol";
import {console2} from "./console2.sol";
import {safeconsole} from "./safeconsole.sol";
import {StdChains} from "./StdChains.sol";
import {StdCheatsSafe} from "./StdCheats.sol";
import {stdJson} from "./StdJson.sol";
import {stdMath} from "./StdMath.sol";
import {StdStorage, stdStorageSafe} from "./StdStorage.sol";
import {StdStyle} from "./StdStyle.sol";
import {StdUtils} from "./StdUtils.sol";
import {VmSafe} from "./Vm.sol";

// 📦 BOILERPLATE
import {ScriptBase} from "./Base.sol";

// ⭐️ SCRIPT
abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils {
// Note: IS_SCRIPT() must return true.
bool public IS_SCRIPT = true;
}
Loading

0 comments on commit 11520ec

Please sign in to comment.