Skip to content

Commit 0d1fbc0

Browse files
authored
Merge pull request #1 from maple-labs/release-v2
feat: Release v2.0.0
2 parents fce0115 + c80d8ab commit 0d1fbc0

18 files changed

+203
-38
lines changed

.github/workflows/forge-pr.yaml

+25-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ name: Forge Tests (PR)
33
on: [pull_request]
44

55
jobs:
6-
run-ci:
6+
test:
7+
name: Test with "deep" profile
78
runs-on: ubuntu-latest
89
steps:
910
- uses: actions/checkout@v2
@@ -18,8 +19,8 @@ jobs:
1819
git config --global url."https://github.com/".insteadOf "[email protected]:"
1920
git submodule update --init --recursive
2021
21-
- name: Run forge tests
22-
run: forge test
22+
- name: Run Forge tests
23+
run: ./scripts/test.sh -p deep
2324

2425
coverage_report:
2526
name: Generate coverage report
@@ -39,6 +40,8 @@ jobs:
3940
- name: Generate coverage report
4041
run: |
4142
forge coverage --report lcov
43+
sudo apt-get install lcov
44+
lcov --remove lcov.info -o lcov.info 'tests/*'
4245
- name: Report code coverage
4346
uses: zgosalvez/github-actions-report-lcov@v1
4447
with:
@@ -47,3 +50,22 @@ jobs:
4750
artifact-name: code-coverage-report
4851
github-token: ${{ secrets.GITHUB_TOKEN }}
4952
working-directory: ./
53+
54+
size_check:
55+
name: Check contracts sizes
56+
runs-on: ubuntu-latest
57+
steps:
58+
- uses: actions/checkout@v2
59+
60+
- name: Install Foundry
61+
uses: onbjerg/foundry-toolchain@v1
62+
with:
63+
version: nightly
64+
65+
- name: Install submodules
66+
run: |
67+
git config --global url."https://github.com/".insteadOf "[email protected]:"
68+
git submodule update --init --recursive
69+
70+
- name: Check contract sizes
71+
run: ./scripts/check-sizes.sh

.github/workflows/forge.yml

+26-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ name: Forge Tests
22

33
on:
44
push:
5-
branches:
6-
- main
5+
branches: [main]
76

87
jobs:
9-
run-ci:
8+
test:
9+
name: Test with "deep" profile
1010
runs-on: ubuntu-latest
1111
steps:
1212
- uses: actions/checkout@v2
@@ -21,8 +21,8 @@ jobs:
2121
git config --global url."https://github.com/".insteadOf "[email protected]:"
2222
git submodule update --init --recursive
2323
24-
- name: Run forge tests
25-
run: forge test
24+
- name: Run Forge tests
25+
run: ./scripts/test.sh -p super_deep
2626

2727
coverage_report:
2828
name: Generate coverage report
@@ -42,6 +42,8 @@ jobs:
4242
- name: Generate coverage report
4343
run: |
4444
forge coverage --report lcov
45+
sudo apt-get install lcov
46+
lcov --remove lcov.info -o lcov.info 'tests/*'
4547
- name: Report code coverage
4648
uses: zgosalvez/github-actions-report-lcov@v1
4749
with:
@@ -50,3 +52,22 @@ jobs:
5052
artifact-name: code-coverage-report
5153
github-token: ${{ secrets.GITHUB_TOKEN }}
5254
working-directory: ./
55+
56+
size_check:
57+
name: Check contracts sizes
58+
runs-on: ubuntu-latest
59+
steps:
60+
- uses: actions/checkout@v2
61+
62+
- name: Install Foundry
63+
uses: onbjerg/foundry-toolchain@v1
64+
with:
65+
version: nightly
66+
67+
- name: Install submodules
68+
run: |
69+
git config --global url."https://github.com/".insteadOf "[email protected]:"
70+
git submodule update --init --recursive
71+
72+
- name: Check contract sizes
73+
run: ./scripts/check-sizes.sh

Makefile

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
1-
install:
2-
@git submodule update --init --recursive
3-
4-
update:
5-
@forge update
6-
7-
# Build and test
8-
9-
profile ?=default
10-
111
build:
12-
@FOUNDRY_PROFILE=production forge build
2+
@scripts/build.sh -p production
133

144
release:
155
@scripts/release.sh
166

17-
test:
18-
forge test
7+
size:
8+
@scripts/check-sizes.sh
199

20-
clean:
21-
@forge clean
10+
test:
11+
@scripts/test.sh -p default

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ forge install
7070
| Spearbit Auditors via Cantina | [`2023-06-05 - Cantina Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/11667848/cantina-maple.pdf) |
7171
| Three Sigma | [`2023-04-10 - Three Sigma Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/11663546/maple-v2-audit_three-sigma_2023.pdf) |
7272

73+
### August 2024 Release
74+
| Auditor | Report Link |
75+
|---|---|
76+
| 0xMacro | [`2024-08-14 - 0xMacro Report`](https://github.com/maple-labs/syrup-utils/blob/main/audits/0xMacro-Maple-Finance-Aug-2024.pdf) |
77+
| Three Sigma | [`2024-08-23 - Three Sigma Report`](https://github.com/maple-labs/syrup-utils/blob/main/audits/ThreeSigma-Maple-Finance-Aug-2024.pdf) |
7378

7479
## Bug Bounty
7580

configs/package.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ packages:
88
contractName: MapleLoanFactory
99
- path: contracts/MapleLoanInitializer.sol
1010
contractName: MapleLoanInitializer
11-
- path: contracts/MapleLoanStorage.sol
12-
contractName: MapleLoanStorage
1311
- path: contracts/MapleRefinancer.sol
1412
contractName: MapleRefinancer
1513
customDescription: Maple Open Term Loan Artifacts and ABIs

contracts/MapleLoan.sol

+12-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { MapleLoanStorage } from "./MapleLoanStorage.sol";
2121
██║ ╚═╝ ██║██║ ██║██║ ███████╗███████╗
2222
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝
2323
24+
2425
██████╗ ██████╗ ███████╗███╗ ██╗ ████████╗███████╗██████╗ ███╗ ███╗ ██╗ ██████╗ █████╗ ███╗ ██╗ ██╗ ██╗ ██╗
2526
██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║ ██║ ██╔═══██╗██╔══██╗████╗ ██║ ██║ ██║███║
2627
██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██║ █████╗ ██████╔╝██╔████╔██║ ██║ ██║ ██║███████║██╔██╗ ██║ ██║ ██║╚██║
@@ -84,6 +85,14 @@ contract MapleLoan is IMapleLoan, MapleProxiedInternals, MapleLoanStorage {
8485
emit BorrowerAccepted(borrower = msg.sender);
8586
}
8687

88+
function acceptLoanTerms() external override whenNotPaused onlyBorrower {
89+
require(!loanTermsAccepted, "ML:ALT:ALREADY_ACCEPTED");
90+
91+
loanTermsAccepted = true;
92+
93+
emit LoanTermsAccepted();
94+
}
95+
8796
function acceptNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_)
8897
external override whenNotPaused onlyBorrower returns (bytes32 refinanceCommitment_)
8998
{
@@ -266,8 +275,9 @@ contract MapleLoan is IMapleLoan, MapleProxiedInternals, MapleLoanStorage {
266275
}
267276

268277
function fund() external override whenNotPaused onlyLender returns (uint256 fundsLent_, uint40 paymentDueDate_, uint40 defaultDate_) {
269-
require(dateFunded == 0, "ML:F:LOAN_ACTIVE");
270-
require(principal != 0, "ML:F:LOAN_CLOSED");
278+
require(loanTermsAccepted, "ML:F:TERMS_NOT_ACCEPTED");
279+
require(dateFunded == 0, "ML:F:LOAN_ACTIVE");
280+
require(principal != 0, "ML:F:LOAN_CLOSED");
271281

272282
dateFunded = _uint40(block.timestamp);
273283

contracts/MapleLoanStorage.sol

+2
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ abstract contract MapleLoanStorage is IMapleLoanStorage {
3232
uint64 public override lateInterestPremiumRate; // The amount to increase the interest rate by for late payments.
3333
uint64 public override platformServiceFeeRate; // The annualized platform service fee rate.
3434

35+
bool public override loanTermsAccepted; // The state of the borrower's acceptance of the loan terms.
36+
3537
}

contracts/MapleRefinancer.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { MapleLoanStorage } from "./MapleLoanStorage.sol";
1818
1919
*/
2020

21-
/// @title Refinancer uses storage from a MapleLoan defined by MapleLoanStorage.
21+
/// @title MapleRefinancer uses storage from a MapleLoan defined by MapleLoanStorage.
2222
contract MapleRefinancer is IMapleRefinancer, MapleLoanStorage {
2323

2424
function decreasePrincipal(uint256 amount_) external override {

contracts/interfaces/IMapleLoan.sol

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ interface IMapleLoan is IMapleProxied, IMapleLoanEvents, IMapleLoanStorage {
2323
*/
2424
function acceptLender() external;
2525

26+
/**
27+
* @dev Accept the loan terms, must be called by the borrower.
28+
*/
29+
function acceptLoanTerms() external;
30+
2631
/**
2732
* @dev Accept the proposed terms and trigger refinance execution.
2833
* @param refinancer_ The address of the refinancer contract.

contracts/interfaces/IMapleLoanEvents.sol

+5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ interface IMapleLoanEvents {
7171
*/
7272
event LenderAccepted(address indexed lender_);
7373

74+
/**
75+
* @dev The loan terms were accepted by the borrower.
76+
*/
77+
event LoanTermsAccepted();
78+
7479
/**
7580
* @dev The terms of the refinance proposal were accepted.
7681
* @param refinanceCommitment_ The hash of the refinancer, deadline, and calls proposed.

contracts/interfaces/IMapleLoanStorage.sol

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ interface IMapleLoanStorage {
7373
*/
7474
function lender() external view returns (address lender_);
7575

76+
/**
77+
* @dev Whether the loan terms have been accepted by the borrower.
78+
*/
79+
function loanTermsAccepted() external view returns (bool loanTermsAccepted_);
80+
7681
/**
7782
* @dev The amount of time the borrower has, after the loan is called, to make a payment, paying back the called principal.
7883
*/

foundry.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ contracts = 'contracts' # The contract directory
33
test = 'tests' # The test directory
44
libs = ['modules'] # A list of library directories
55
solc_version = '0.8.7' # Override for the solc version (setting this ignores `auto_detect_solc`)
6-
optimizer = true # Enable or disable the solc optimizer
7-
optimizer_runs = 200 # The number of optimizer runs
6+
optimizer = false # Enable or disable the solc optimizer
87
verbosity = 3 # The verbosity of tests
98
block_timestamp = 1_622_400_000 # Timestamp for tests (non-zero)
9+
fuzz_runs = 100 # Number of fuzz runs
1010

1111
[profile.deep]
1212
fuzz_runs = 1000
1313

1414
[profile.super_deep]
1515
fuzz_runs = 50000
1616

17-
[profile.release]
18-
optimizer = true # Enable or disable the solc optimizer
19-
optimizer_runs = 200 # The number of optimizer runs
17+
[profile.production]
18+
optimizer = true # Enable or disable the solc optimizer
19+
optimizer_runs = 200 # The number of optimizer runs

package.yaml

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: open-term-loan
2-
version: 0.0.1
2+
version: 1.0.1
33
source: contracts
44
packages:
55
- path: contracts/MapleLoan.sol
@@ -8,8 +8,6 @@ packages:
88
contractName: MapleLoanFactory
99
- path: contracts/MapleLoanInitializer.sol
1010
contractName: MapleLoanInitializer
11-
- path: contracts/MapleLoanStorage.sol
12-
contractName: MapleLoanStorage
1311
- path: contracts/MapleRefinancer.sol
1412
contractName: MapleRefinancer
15-
customDescription: Maple Loan Artifacts and ABIs
13+
customDescription: Maple Open Term Loan Artifacts and ABIs

scripts/check-sizes.sh

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
3+
while getopts p: flag
4+
do
5+
case "${flag}" in
6+
p) profile=${OPTARG};;
7+
esac
8+
done
9+
10+
export FOUNDRY_PROFILE=production
11+
12+
sizes=$(forge build --sizes)
13+
14+
names=($(cat ./configs/package.yaml | grep " contractName:" | sed -r 's/.{18}//'))
15+
16+
fail=false
17+
18+
for i in "${!names[@]}"; do
19+
line=$(echo "$sizes" | grep -w "${names[i]}")
20+
21+
if [[ $line == *"-"* ]]; then
22+
echo "${names[i]} is too large"
23+
fail=true
24+
fi
25+
done
26+
27+
if $fail
28+
then
29+
echo "Contract size check failed"
30+
exit 1
31+
else
32+
echo "Contract size check passed"
33+
fi

scripts/release.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ version=$(cat ./configs/package.yaml | grep "version: " | sed -r 's/.{9}//')
55
name=$(cat ./configs/package.yaml | grep "name: " | sed -r 's/.{6}//')
66
customDescription=$(cat ./configs/package.yaml | grep "customDescription: " | sed -r 's/.{19}//')
77

8-
./scripts/build.sh -p release
8+
./scripts/build.sh -p production
99

1010
rm -rf ./package
1111
mkdir -p package

tests/AcceptLoanTerms.t.sol

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity 0.8.7;
3+
4+
import { Test } from "../modules/forge-std/src/Test.sol";
5+
6+
import { MapleLoanHarness } from "./utils/Harnesses.sol";
7+
import { MockFactory, MockGlobals } from "./utils/Mocks.sol";
8+
9+
contract AcceptLoanTermsTests is Test {
10+
11+
event LoanTermsAccepted();
12+
13+
address account = makeAddr("account");
14+
15+
MapleLoanHarness loan = new MapleLoanHarness();
16+
MockFactory factory = new MockFactory();
17+
MockGlobals globals = new MockGlobals();
18+
19+
function setUp() external {
20+
factory.__setGlobals(address(globals));
21+
22+
loan.__setFactory(address(factory));
23+
loan.__setBorrower(account);
24+
}
25+
26+
function test_acceptLoanTerms_paused() external {
27+
globals.__setFunctionPaused(true);
28+
29+
vm.expectRevert("ML:PAUSED");
30+
loan.acceptLoanTerms();
31+
}
32+
33+
function test_acceptLoanTerms_notBorrower() external {
34+
vm.expectRevert("ML:NOT_BORROWER");
35+
loan.acceptLoanTerms();
36+
}
37+
38+
function test_acceptLoanTerms_alreadyAccepted() external {
39+
loan.__setLoanTermsAccepted(true);
40+
41+
vm.prank(account);
42+
vm.expectRevert("ML:ALT:ALREADY_ACCEPTED");
43+
loan.acceptLoanTerms();
44+
}
45+
46+
function test_acceptLoanTerms_success() external {
47+
assertTrue(!loan.loanTermsAccepted());
48+
49+
vm.expectEmit();
50+
emit LoanTermsAccepted();
51+
52+
vm.prank(account);
53+
loan.acceptLoanTerms();
54+
55+
assertTrue(loan.loanTermsAccepted());
56+
}
57+
58+
}

0 commit comments

Comments
 (0)