|
| 1 | +# Escrow System |
| 2 | + |
| 3 | +## About |
| 4 | +It’s functionality is similar to normal freelancing platforms. |
| 5 | +On a freelancing platform, a client can create a project. And hire several developers on the project. After discussion with a developer, the client creates a milestone and deposit funds to escrow. And once the developer delivers work, they release the fund in escrow to the developer. And a client can file a dispute in a certain period. |
| 6 | +We’re going to implement this system using a smart contract. |
| 7 | +On our project, let’s define some words: `originator` (same as client) and `participant`(same as developer) |
| 8 | +We use cryptocurrency, and once the fund is transferred to a participant, we can’t dispute it. So funds will be transferred to a participant through a vesting contract. Before it’s being released, clients can file a dispute. |
| 9 | + |
| 10 | +## Core contract |
| 11 | +EscrowFactory: Originators can create escrows here |
| 12 | + |
| 13 | +Escrow: Originators can create milestones and manage funds. Participants will use this contract to get paid. |
| 14 | + |
| 15 | +Locker: If originators release funds, fund is transferred to vesting contract and originators can claim after some time. |
| 16 | + |
| 17 | +## Project Specs |
| 18 | + |
| 19 | +### EscrowFactory |
| 20 | + - This contract has a variable named `createFee`, `feeRecipient` and `feePercent`. |
| 21 | + When an originator create an escrow, they should pay a certain amount of ETH. It will be transferred to `feeRecipient`. |
| 22 | + When originator releases milestone fund, some percent will be transferred to `feeRecipient` address. |
| 23 | + - This contract has a function named `createEscrow`. |
| 24 | + It will receive `uri`(string) as a parameter and create an escrow contract. And uri is saved on `escrow` contract. |
| 25 | + The front-end also need to get a list of escrows owned by a certain wallet. |
| 26 | + The front-end also need to get a list of all available escrows. |
| 27 | + - It has operators registration. Operators can assets milestone dispute if originator and participants don’t agree each other. |
| 28 | + - It has locker(address) and lockDuration(uint256). |
| 29 | + Locker is the address of locker contract. LockDuration is the duration of fund lock when the originator releases the fund. |
| 30 | + - This contract has a function named `destroy`. This will be called from escrow contract to remove `escrow` from `factory`. |
| 31 | + |
| 32 | +_You can define and use other necessary variables and functions._ |
| 33 | + |
| 34 | +### Escrow |
| 35 | + - Flow of a `milestone`. |
| 36 | + - Originator create a milestone |
| 37 | + - Originator can create a milestone info before participant agrees |
| 38 | + - Participant can agree with a milestone |
| 39 | + - Originator deposits funds to agreed milestone |
| 40 | + - Originator can released fund, or if they don’t release it, participant can request fund after dueDate of the milestone |
| 41 | + - Originator can file a dispute for released milestones (only when milestone fund is released, but it’s in Locker contract and it’s locked. => block.timestamp – released timestamp < lockDuration) |
| 42 | + If milestone fund in Locker is available to claim and participant doesn’t claim yet, originator can’t file a dispute. |
| 43 | + - Participant can accept dispute, and then fund in Locker contract is immediately sent back to originator. |
| 44 | + - Originator can cancel a dispute. |
| 45 | + - Also, operator can accept/cancel a dispute if participant and originator don’t agree each other. |
| 46 | + - It will have `overview` info. (count of created milestones, deposited milestones, released milestones, disputed milestones) |
| 47 | + - `updateMeta` function is to update metadata of an escrow |
| 48 | + - `createMilestone(address token, address participant, uint256 amount, uint256 timestamp, string memory meta)`: timestamp is unix timestamp of dueDate. Meta is just a string value of milestone description. And only called by originator |
| 49 | + - `updateMilestone(uint256 _mId, token, participant, amount, timestamp, meta)`: It can be called before milestone is agreed by participant. And only called by originator |
| 50 | + - `agreeMilestone(mId)`: only called by participant. It’s to accept a certain milestone. |
| 51 | + - `depositMilestone(mId)`: only called by originator, and deposit fund of milestone to this contract |
| 52 | + - `requestMilestone(mId)`: only called by participant when it’s not released and it’s over dueDate |
| 53 | + - `releaseMilestone(mId)`: only called by originator and release fund. Fund is transferred to Lock contract |
| 54 | + - `createDispute(mId)`: only called by originator. |
| 55 | + - `resolveDispute(mId)`: only called by participant or operator. |
| 56 | + - `cancelDispute(mId)`: only called by originator or operator. |
| 57 | + - `destroy()`: only called by originator, when all milestones are released. It will call factory.destroy() and destroy the current escrow contract |
| 58 | + |
| 59 | +_You can define and use other necessary variables and functions._ |
| 60 | + |
| 61 | +### Locker |
| 62 | + - `Create(_token, amount, beneficiary, mId, unlockTimestamp)`: Called by escrow contracts only. |
| 63 | + - `Release(lockId)`: Release fund to `beneficiary` address of `lockId` (if lockDuration is passed) |
| 64 | + |
| 65 | +_You can define and use other necessary variables and functions._ |
| 66 | + |
| 67 | +## Example |
| 68 | +### About dispute flow: |
| 69 | +LockDuration is one week. |
| 70 | +Originator released milestoneA to Participant on 1th. |
| 71 | +Originator can file a dispute for milestoneA before 8th. |
| 72 | +After 8th, participant can release the fund. |
| 73 | +After 8th, originator can’t file a dispute even if participant doesn’t release it. |
| 74 | + |
| 75 | +Let’s assume that originator filed a dispute on 6th. |
| 76 | +Then participant can’t release fund on 8th or 9 th… |
| 77 | +If that dispute is cancelled on 7th, participant can release after 8th. |
| 78 | + |
| 79 | +## Reference: |
| 80 | +When deploying a new contract, `don’t use new`. In that case, code size of factory contract is bigger, and it’s a bad practice. |
| 81 | +Use [Clones.clone](https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones) and [factory](https://github.com/pancakeswap/pancake-smart-contracts/blob/master/projects/farms-pools/contracts/SmartChefFactory.sol) and [initialize](https://github.com/pancakeswap/pancake-smart-contracts/blob/master/projects/farms-pools/contracts/SmartChefInitializable.sol) model. |
| 82 | +When destorying a contract, use [selfdestruct](https://solidity-by-example.org/hacks/self-destruct/) |
| 83 | + |
| 84 | +## Command |
| 85 | +This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract. |
| 86 | + |
| 87 | +Try running some of the following tasks: |
| 88 | + |
| 89 | +```shell |
| 90 | +npx hardhat help |
| 91 | +npx hardhat test |
| 92 | +GAS_REPORT=true npx hardhat test |
| 93 | +npx hardhat node |
| 94 | +npx hardhat run scripts/deploy.ts |
| 95 | +``` |
0 commit comments