# Commit Milestones
**Goals:**
- Commits can be created with milestones (daily, x times a week, every month, etc)
- Partial stake can be recovered based on milestone completion (% of stake)
- Commits can be joined, and fulfilled without paying any gas (this means removing join fee and possibly create fee)
Milestones are defined with the calendar format [RRULE](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html). This has the benefit of enabling highly flexible milestones and repeats as can be seen in calendar apps.


Milestone checkins happen by creating an attestation every time the participants submits proof. Proofs can be things like Strava service, sleep apps, farcaster posts etc.
Finally when the commit period has ended, the backend service finalize the commit by calculating the stake rewards for each participants and generates a merkle tree to send to the contract.
This makes for a simple architecture where much of the logic is handled by a trusted backend api.
## Milestones and verifiers
### Photo
Participants submit photos that are approved (by whom?).
AI algorithm detects features in photo based on criteria.
##### Use-cases
- Touching Grass
- Gym session
### Manual
Participants are verified by one of many admins that call `/:commitId/verify` endpoint with a signed message. Backend verifies signature and creates an Attestation.
##### Use-cases
- Classes where teacher manually approves attendance.
##### Data
```ts
verification = {
type: "manual",
data: ["0x...123", "0x...456"]
}
```
##### UI
Creator adds admin addresses in a select field.
### Code sketches
```solidity
contract CommitProtocol is AccessControl, ERC1155 {
struct Commit {
string metadataURI; // Metadata describing the commit.
address creator; // Commit creator.
uint256 startsAt; // Deadline for participants to join.
uint256 endsAt; // Deadline for verifications.
uint256 maxParticipants; // Maximum allowed participants (0 = unlimited).
address token; // ERC20 token used for stake.
uint256 stake; // Amount each participant must stake.
string rrule; // Recurrence rule as a string (e.g. "FREQ=DAILY;COUNT=10"). This will be parsed by the trusted backend service
Verification verification;
}
struct Verification {
string type; // erc20, strava, etc
bytes data; // Encode data used for verification (eg. length of daily run, amount of erc20 token)
}
// Trusted backend service
bytes32 public constant TRUSTED_ROLE = keccak256("TRUSTED_ROLE");
function create(Commit memory config) public {
// Create the commit
}
function join(uint256 commitId) external {
// Join commit, transfer stake, and mint NFT
}
function finalize(
uint256 commitId,
bytes32 merkleRoot,
string distributionURI
) external onlyRole(TRUSTED_ROLE) {
/*
Trusted web2 backend service does the following:
1. counts all the participants' verified milestones (by indexing EAS GraphQL)
2. calculates stake rewards
3. creates a merkle tree and uploads to ipfs (so indexer and participants can easily check their reward amounts)
4. calls contract with merkle tree and ipfs uri of distribution
*/
}
function claim(uint256 commitId, address participant, uint256 amount, bytes32[] calldata proof) external {
// Participant claims stake + reward with merkle proof
}
}
```
```ts
async function handleMilestoneCheckin({ proof, commitId, milestoneIndex, chain }) {
if (await verifyProof(proof)) {
// Create EAS Attestation with data: (commitId, milestoneIndex)
}
}
type MilestoneProof = {
type: "strava" | "farcaster_post" | "erc20" | "erc721"
data: "..."
}
function verifyProof(proof: MilestoneProof) {
// Handle different proofs
switch (proof.type) {
case "farcaster_post":
// Verify proof.data
case "strava":
case "er20"
// Verify proof.data.amount >= commitSDK.getMilestoneData(commitId)
case ...
}
}
```
---
Previous sketches:
Sketchbook for what milestones in Commit could look like.
Ideal goals:
- Little changes to the contracts
- Flexibility in describing the recurrence rules
- Ability to verify each milestone with ease
- Flexibility in how milestones can be verified
- Partially completed Commits should still be rewarded
---
Ideas:
- Define as a [Calendar Recurrance Rule](https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html) (rrule) (e.g. `FREQ=DAILY;COUNT=5` for 5 times a week). This makes it easy and flexible to define recurring events/tasks in a standardized way. However, it requires off-chain parsing.
- Whenever a participant completes a task, create an EAS attestation.
- A trusted backend-service
1. fetch all the created attestations
2. get the timestamps
3. check if they fulfill the `rrule`
- Partially completed Commits require changes to the way we verify today. Instead of returning a `bool` we could return `0.0 - 1.0` that indicate a percentage of how completed the Commit is. However this needs to happen once all tasks have been completed.
- Release of funds mid-commit requires additional changes to the contract.
```tsx
async function verifyMilestones() {
// Fetch verifications for participant (and commitId on network)
const verifications = await fetchAttestations(milestoneSchema, {
where: { recipient, commitId, network }
})
// Parse the rrule
const rule = rrulestr(commit.milestones.rule);
const expectedWithinWindow = rule.between(
commit.joinBefore,
commit.verifyBefore
);
// Get the dates for the participant verifications
const actualDates = verifications.map(v => new Date(v.timestamp));
// Get the dates within the commit time periods
const actualWithinWindow = actualDates.filter(date =>
date >= commit.joinBefore && date <= commit.verifyBefore
);
// Count the occurences
if (actualWithinWindow.length >= expectedWithinWindow.length) {
console.log('Milestone verified: enough occurrences within the window.');
// Verify the participant
await commitSDK.verify(commitId, participant)
} else {
console.error('Milestone not met.');
}
}
```