# Allo SimpleGrants
## EAS + Offchain Approach
#### Create Grant
This is how Optimism create projects. It contains of several steps:
- Create a Project attestation
- Upload Project Metadata
- Upload Avatar
- Upload Cover
- Create Project Metadata attestation with ref to Project
- Upload Application Metadata
- Create Application attestation
These attestations are they pieced together to create something like this:
```tsx
ProjectOrg = {
type: "Project"
project: {
name: "Project Name",
category: "Category",
// Project can have multiple metadata and each application references one of them
metadatas: [{
name,
description,
projectAvatarUrl,
...
applications: [{
round: 6,
...
}]
}, {
name,
description,
projectAvatarUrl,
...
applications: [{
round: 5,
...
}]
}],
}
}
```
<details>
<summary>Code block for how this is created on EAS</summary>
<div>
```tsx
// Sign In With Farcaster
const farcasterID = "...";
// OP RetroFunding Project or Organization
const projectRefUID = await eas.attest({
schema: schemas.projectOrOrg,
data: {
farcasterID,
type: "Project",
},
});
// Create Project
const projectAvatarUrl = await uploadMetadata(ProjectAvatar);
const projectCoverImageUrl = await uploadMetadata(ProjectCover);
const projectMetadata = {
name: "Superfluid",
description: "...",
projectAvatarUrl: "https://",
projectCoverImageUrl: "https://",
category: "Social",
osoSlug: "Superfluid",
socialLinks: {
website: ["http://"],
farcaster: ["https://"],
twitter: "https://",
mirror: "https://",
},
team: ["637061"], // Farcaster IDs
github: [{ url: "https://", name: null, description: null }],
packages: [],
contracts: [],
grantsAndFunding: {
ventureFunding: [],
grants: [],
revenue: [],
retroFunding: [],
},
pricingModel: null,
pricingModelDetails: null,
links: [],
};
const metadataUrl = await uploadMetadata(projectMetadata);
// OP RetroFunding Project Metadata
const metadataSnapshotRefUID = await eas.attest({
schema: schemas.projectMetadata,
data: {
farcasterID,
projectRefUID,
name: projectMetadata.name,
category: projectMetadata.category,
parentProjectRefUID: null,
metadataType: 2,
metadataUrl,
},
});
// Create Application
const applicationMetadata = {
round: 6,
category: "Governance Analytics",
subcategory: [
"Governance Infrastructure::Technical infrastructure which powers the voting process within Optimism Governance",
"Governance Tooling::Tools which are used by Delegates or Citizens participate in Optimism Governance tools which are used by Delegates and/or Citizens asuch as voting clients. Tools which support delegates and citizens insights into governance performance and history",
"Grants Tooling::Tools which support the Token House mission process, including the operation of the grants council. Tools which power or support the Retro Funding process.",
],
impactStatement: [{ question: "?", answer: "!" }],
};
const applicationMetadataUrl = await uploadMetadata(applicationMetadata);
// OP RetroFunding Application
const applicationRefUID = await eas.attest({
schema: schemas.application,
data: {
farcasterID,
round: applicationMetadata.round,
metadataSnapshotRefUID,
metadataType: 2,
metadataUrl: applicationMetadataUrl,
},
});
```
</div>
</details>
#### Browse Grants
In Optimisms case, Agora has created an Indexer on top of EAS that pieces together all the data and stores in a Web2 database (Postgres). This enables them to more easily search and query the data but it comes with the complexity of the indexer they built.
### Allocation
When a voter / funder has selected Grants to support, they navigate to a Checkout page.
It is possible to send directly to grantees with `ERC20.transfer(grantee, amount)` but the funder would need to sign each transaction individually.
There is a multicall3 contract but mainly it explicitly warns about approving it to spend tokens:
> Never approve Multicall3 to spend your tokens. If you do, anyone can steal your tokens. There are likely bots that monitor for this as well. - It is not recommended to inherit from this contract if your contract will hold funds. But if you must, be sure you know what you're doing and protect all state changing methods with an onlyOwner modifier or similar so funds cannot be stolen.
https://github.com/mds1/multicall?tab=readme-ov-file#security
In addition, integrating protocols like Drips and Superfluid or implementing token or attestation gating would be very difficult (if possible at all).
The other option is to transfer tokens to a Round/Strategy contract with:
`round.allocateMany(recipients, amounts)`
This round contract allocate function is flexible in how the Strategy builder choose to implement it.
Here are some examples of what different implementations could look like depending on the RFP details:
```solidity
// NFT gating
function allocate(address recipient, uint256 amount) onlyNFT(recipient) {}
// Only with a balance of > 10 Allo
function allocate(address recipient, uint256 amount) onlyERC20(alloTokenAddress, 10**18) {}
// Attestation with a specific schema and attester (defined in round.init)
function allocate(address recipient, uint256 amount) onlyEAS(schema, attester) {}
// Superfluid - start stream
function allocate(address recipient, uint256 amount) {
(, , uint256 currentUnitsHeld) =
superToken.getSubscription(address(this), SUPERTOKEN_INDEX_ID, recipient);
superToken.updateSubscriptionUnits(SUPERTOKEN_INDEX_ID, recipient, currentUnitsHeld + amount);
}
// Drips
function allocate(address recipient, uint256 amount) {
uint256 accountId = dripsDriver.calcAccountId(recipient);
dripsDriver.give(accountId, token, amount);
}
```
### Distribution
#### Off-chain
Calculations can be done off-chain in a node script / backend API. This is useful when calculations are complex or when external data is desired.
For example:
- Quadratic calculation on 1000 projects
##### How does it work?
1. Call trigger.dev workflow (UI/Frontend is notified when done)
2. Call `round.addMerkle(root)`
3. Grantees can now generate a proof and call `round.claim(granteeAddress, amount, proof)`