Skip to content

Commit

Permalink
CodeHawks competition commit
Browse files Browse the repository at this point in the history
  • Loading branch information
alexroan committed Jul 23, 2023
0 parents commit fa9f20a
Show file tree
Hide file tree
Showing 18 changed files with 1,146 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: test

on: [workflow_dispatch, push, pull_request]

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

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

- name: Run Forge build
run: |
forge --version
forge build --sizes
forge fmt --check
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env

broadcast
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/Openzeppelin/openzeppelin-contracts
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-include .env

.PHONY: all test clean deploy fund help install snapshot format anvil

DEFAULT_ANVIL_KEY := 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

help:
@echo "Usage:"
@echo " make deploy [ARGS=...]\n example: make deploy ARGS=\"--network sepolia\""
@echo ""
@echo " make fund [ARGS=...]\n example: make deploy ARGS=\"--network sepolia\""

all: clean install update build

# Clean the repo
clean :; forge clean

install :; forge install

# Update Dependencies
update:; forge update

build:; forge build

test :; forge test

coverage :; forge coverage --report debug > coverage-report.txt

snapshot :; forge snapshot

format :; forge fmt

anvil :; anvil -m 'test test test test test test test test test test test junk' --steps-tracing --block-time 1

NETWORK_ARGS := --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) --broadcast

ifeq ($(findstring --network sepolia,$(ARGS)),--network sepolia)
NETWORK_ARGS := --rpc-url $(SEPOLIA_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv
endif

deploy:
@forge script script/DeployEscrowFactory.s.sol:DeployEscrowFactory $(NETWORK_ARGS)
212 changes: 212 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# CodeHawks Escrow Contract - Competition Details

<br/>
<p align="center">
<a href="https://codehawks.com" target="_blank">
<img src="https://res.cloudinary.com/droqoz7lg/image/upload/v1689080263/snhkgvtsidryjdtx0pce.png" width="400" alt="CodeHawks escrow contract">
</a>
</p>
<br/>

## Contest Details

- Total Prize Pool: $40,000
- HM Awards: $37,000
- LQAG Awards: $3,000
- Starts July 24, 2023
- Ends August 5th, 2023
- nSLOC: ~182
- Complexity Score: ~106

### Project Overview

**Actors**

* Buyer: The purchaser of services, in this scenario, a project purchasing an audit.
* Seller: The seller of services, in this scenario, an auditor willing to audit a project.
* Arbiter: An impartial, trusted actor who can resolve disputes between the Buyer and Seller.
**Design considerations**
* The Arbiter is only compensated the `arbiterFee` amount if a dispute occurs.
* Once a dispute has been initiated it can not be canceled.
* ERC777 tokens should not be used as tokens for the `Escrow` contract given that it enables a malicious buyer to DOS `Escrow::resolveDispute`
* In case a smart contract calls `EscrowFactory::newEscrow`, given that the caller of this contract is in control of the salt, frontrunning is a possibility.
### Workflows

**Create an `Escrow`**

1. Buyer approves the payment contract to be handled by `EscrowFactory`.
2. Buyer calls `EscrowFactory::newEscrow`, inputs:
1. The price.
2. The payment token.
3. The seller (auditor or person in charge of the audit).
4. Arbiter.
5. Arbiter fee: Fee to pay in case of a dispute is initialized.
6. Salt: for `create2` `Escrow` deployment.


**Expected sucessful workflow**

1. The buyer creates an `Escrow` contract through `EscrowFactory::newEscrow`, depositing the funds.
2. The seller sends the buyer the report (off-chain).
3. The buyer acknowledges this report on-chain by calling `Escrow::confirmReceipt`. This sends the funds to the seller.

**Expected dispute workflow**

1. The buyer creates an `Escrow` contract through `EscrowFactory::newEscrow`, depositing the funds.
2. For any reason, the buyer or the seller can initiate a dispute through `Escrow::initiateDispute`.
3. The arbiter confers with both parties offchain. Arbiter then calls `Escrow::resolveDispute`, reimbursing either side accordingly, emptying the `Escrow`.

## Submissions

- Submit to [CodeHawks](https://www.codehawks.com/contests/cljyfxlc40003jq082s0wemya)

## In Scope

All contracts in `src` are in scope.

*Note on `script` folder*:
The contracts in `script` are the scripts you can assume are going to be used to deploy and interact with the contracts. If they have an issue that will affect the overall security of the system, they are in scope. However, if they have a security issue that only affects the script and not the overall deployment of the stablecoin protocol, it is out of scope.

## Known Issues

* **Addresses other than the zero address (for example 0xdead) could prevent disputes from being resolved** - Before the `buyer` deploys a new `Escrow`, the `buyer` and `seller` should agree to the terms for the `Escrow`. If the `buyer` accidentally or maliciously deploys an `Escrow` with incorrect `arbiter` details, then the `seller` could refuse to provide their services. Given that the `buyer` is the actor deploying the new `Escrow` and locking the funds, it's in their best interest to deploy this correctly.
* **Large arbiter fee results in little/no `seller` payment** - In this scenario, the `seller` can decide to not perform the audit. If this is the case, the only way the `buyer` can receive any of their funds back is by initiating the dispute process, in which the `buyer` loses a large portion of their deposited funds to the `arbiter`. Therefore, the `buyer` is disincentivized to deploy a new `Escrow` in such a way.
* **Tokens with callbacks allow malicious sellers to DOS dispute resolutions** - Each supported token will be vetted to be supported. ERC777 should be discouraged.
* **`buyer` never calls `confirmReceipt`** - The terms of the `Escrow` are agreed upon by the `buyer` and `seller` before deploying it. The onus is on the `seller` to perform due diligence on the `buyer` and their off-chain identity/reputation before deciding to supply the `buyer` with their services.
* **`salt` input when creating an `Escrow` can be front-run**
* **`arbiter` is a trusted role**
* **User error such as `buyer` calling `confirmReceipt` too soon**
* **Non-`tokenAddress` funds locked**

# About

This project is meant to enable smart contract auditors (sellers) and smart contract protocols looking for audits (buyers) to connect using a credibly neutral option, with optional arbitration.

- [CodeHawks Escrow Contract - Competition Details](#codehawks-escrow-contract---competition-details)
- [Contest Details](#contest-details)
- [Project Overview](#project-overview)
- [Workflows](#workflows)
- [Submissions](#submissions)
- [In Scope](#in-scope)
- [Known Issues](#known-issues)
- [About](#about)
- [Getting Started](#getting-started)
- [Requirements](#requirements)
- [Quickstart](#quickstart)
- [Usage](#usage)
- [Testing](#testing)
- [Test Coverage](#test-coverage)
- [Start a local node](#start-a-local-node)
- [Deploy](#deploy)
- [Deploy - Other Network](#deploy---other-network)
- [Deployment to a testnet or mainnet](#deployment-to-a-testnet-or-mainnet)
- [Estimate gas](#estimate-gas)
- [Formatting](#formatting)
- [Acknowledgements](#acknowledgements)

# Getting Started

## Requirements

- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- You'll know you did it right if you can run `git --version` and you see a response like `git version x.x.x`
- [foundry](https://getfoundry.sh/)
- You'll know you did it right if you can run `forge --version` and you see a response like `forge 0.2.0 (816e00b 2023-03-16T00:05:26.396218Z)`

## Quickstart

```
git clone https://github.com/Cyfrin/2023-07-escrow
cd escrow
forge build
```

# Usage

## Testing

```
forge test
```

### Test Coverage

```
forge coverage
```

and for coverage based testing:

```
forge coverage --report debug
```


## Start a local node

```
make anvil
```

## Deploy

This will default to your local node. You need to have it running in another terminal in order for it to deploy.

```
make deploy
```

## Deploy - Other Network

[See below](#deployment-to-a-testnet-or-mainnet)



# Deployment to a testnet or mainnet

1. Setup environment variables

You'll want to set your `SEPOLIA_RPC_URL` and `PRIVATE_KEY` as environment variables. You can add them to a `.env` file, similar to what you see in `.env.example`.

- `PRIVATE_KEY`: The private key of your account (like from [metamask](https://metamask.io/)). **NOTE:** FOR DEVELOPMENT, PLEASE USE A KEY THAT DOESN'T HAVE ANY REAL FUNDS ASSOCIATED WITH IT.
- You can [learn how to export it here](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key).
- `SEPOLIA_RPC_URL`: This is url of the goerli testnet node you're working with. You can get setup with one for free from [Alchemy](https://alchemy.com/?a=673c802981)

Optionally, add your `ETHERSCAN_API_KEY` if you want to verify your contract on [Etherscan](https://etherscan.io/).

1. Get testnet ETH

Head over to [faucets.chain.link](https://faucets.chain.link/) and get some tesnet ETH. You should see the ETH show up in your metamask.

2. Deploy

```
make deploy ARGS="--network sepolia"
```

## Estimate gas

You can estimate how much gas things cost by running:

```
forge snapshot
```

And you'll see and output file called `.gas-snapshot`


# Formatting


To run code formatting:
```
forge fmt
```


# Acknowledgements
- Inspiration for the codebase from [Ross Campbell](https://www.linkedin.com/in/ross-campbell-058153aa/)
9 changes: 9 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
remappings = ['@openzeppelin/contracts=lib/openzeppelin-contracts/contracts']
solc_version = "0.8.18"
via_ir = true

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
Binary file added img/codehawks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at e8a047
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at e50c24
15 changes: 15 additions & 0 deletions script/DeployEscrowFactory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

import {Script} from "forge-std/Script.sol";
import {EscrowFactory} from "../src/EscrowFactory.sol";
import {Escrow} from "../src/Escrow.sol";

contract DeployEscrowFactory is Script {
function run() external returns (EscrowFactory) {
vm.startBroadcast();
EscrowFactory factory = new EscrowFactory();
vm.stopBroadcast();
return factory;
}
}
Loading

0 comments on commit fa9f20a

Please sign in to comment.