Skip to content

Commit

Permalink
Cointags Contracts Docs (#993)
Browse files Browse the repository at this point in the history
Docs for cointags contracts.

Added a script to generate images from plantuml in the docs
  • Loading branch information
oveddan authored Dec 24, 2024
1 parent 839e006 commit bc137f3
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 6 deletions.
26 changes: 26 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,29 @@ Then, from the `docs` directory, start the dev server:
```bash
pnpm run dev
```

## Generating UML Diagrams

The documentation includes UML diagrams generated from PlantUML source files. To generate these diagrams:

1. Ensure you have Docker installed and running (used for PlantUML generation)

2. PlantUML source files are located in the `docs/uml` directory with `.puml` extension

3. Generate the diagrams by running:

```bash
pnpm run generate-uml
```

This will:

- Process all `.puml` files in the `docs/uml` directory
- Generate SVG diagrams in `public/uml`
- Use Docker to handle loading of the PlantUML server

The generated diagrams can be referenced in documentation using:

```markdown
![Diagram Name](/uml/diagram-name.svg)
```
8 changes: 5 additions & 3 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build": "tsc --noEmit",
"build:site": "vocs build",
"preview": "vocs preview",
"copy-changelog": "pnpm tsx scripts/copy-changelogs.ts"
"copy-changelog": "tsx scripts/copy-changelogs.ts",
"generate-uml": "tsx scripts/generateUml.ts"
},
"dependencies": {
"@0xsplits/splits-sdk": "^4.0.2",
Expand All @@ -23,16 +24,17 @@
"react": "latest",
"react-dom": "latest",
"typescript": "^5.2.2",
"viem": "^2.21.21",
"vite-plugin-radar": "^0.9.6",
"vite-plugin-vercel": "^9.0.2",
"vite-tsconfig-paths": "^4.3.2",
"viem": "^2.21.21",
"vocs": "^1.0.0-alpha.55",
"wagmi": "^2.12.17",
"yaml": "2.3.4"
},
"devDependencies": {
"@reservoir0x/relay-sdk": "^1.3.3",
"@zoralabs/tsconfig": "workspace:*"
"@zoralabs/tsconfig": "workspace:*",
"tsx": "^4.19.2"
}
}
2 changes: 1 addition & 1 deletion docs/pages/changelogs/protocol-deployments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

### Minor Changes

- [fc8e86a8](https://github.com/ourzora/zora-protocol/commit/fc8e86a8): - Added Cointags addresses and ABIs.
- [fc8e86a8](https://github.com/ourzora/zora-protocol/commit/fc8e86a8): - Added Cointags addresses and abis.
- Added IUniswapV3Pool ABI.

## 0.3.11
Expand Down
69 changes: 69 additions & 0 deletions docs/pages/contracts/Cointags.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Cointags

Cointags lets creators connect their posts with onchain communities through by tagging their posts with their coins. When someone mints a creator's post, a portion of the creator rewards automatically:
- Goes to buying and burning an ERC20 token of their choice
- Goes to the creator as their reward

For more details about using Cointags as a creator, see the [Cointags Support Article](https://support.zora.co/en/articles/4185217).

> **Note**: Cointags use UniswapV3Pools to buy the corresponding ERC20 token for burning. Currently Cointags only work with Uniswap V3 pools. The pool must have WETH as one of its tokens.
## Contract Architecture

The protocol consists of two main contracts:

| Contract | Description |
| ----- | ------- |
| [`CointagFactoryImpl.sol`](https://github.com/ourzora/zora-protocol/blob/main/packages/cointags/src/CointagFactoryImpl.sol) | CointagFactory implementation contract for deterministically deploying `Cointag` contracts|
| [`CointagImpl.sol`](https://github.com/ourzora/zora-protocol/blob/main/packages/cointags/src/CointagImpl.sol) | Cointag implementation contract |

### Deterministic Deployment

The `CointagFactory` is deployed deterministically at the address `0x7777777BbD0b88aD5F3b5f4c89C6B60D74b9774F` on [Base](https://basescan.org/address/0x7777777BbD0b88aD5F3b5f4c89C6B60D74b9774F) and [Zora Network](https://explorer.zora.energy/address/0x7777777BbD0b88aD5F3b5f4c89C6B60D74b9774F?).

The `CointagFactory` uses `solady`'s [CREATE3](https://github.com/Vectorized/solady/blob/main/src/utils/CREATE3.sol) for deterministically deploy `Cointag` contracts, ensuring that:
- Each combination of creator, pool, and burn percentage results in the same address across all chains.
- Deployed `Cointag` addresses are consistent regardless of implementation or code versions.
- `Cointag` addresses can be predicted before deployment.

## Protocol Flow

The following diagram illustrates the sequence of interactions in the protocol:

![Cointag Sequence Diagram](/uml/cointag-sequence.svg)

### Sequence Breakdown
- **Setting Up a Cointag and 1155 Post**:
- The creator deploys a new `Cointag` instance through the `CointagFactory`.
- The creator sets their 1155 post's reward recipient to the `Cointag` address.

- **Pulling to Buy, Burn, and Distribute Rewards**:
- When someone mints a post, creator rewards are deposited into the `ProtocolRewards` contract, with the `Cointag` as the recipient.
- Anyone can trigger the distribution of accumulated rewards by calling `pull()` on the `Cointag` contract; in the `pull()` function, the `Cointag`:
- Withdraws ETH from the protocol rewards.
- Wraps the buy/burn percentage as WETH.
- Swaps the WETH for the target ERC20.
- Burns the received ERC20 tokens.
- Deposits the remaining ETH back to the protocol rewards for the creator.

- **Creator Reward Withdrawal**:
- The creator can withdraw their share of rewards that were deposited in the `pull()` step from the protocol rewards contract at any time.

The following class diagram illustrates the contract relationships in the protocol:
![Cointag Class Diagram](/uml/cointag-objects.svg)


### Auto-pulling bot

Zora has a bot that automatically searches for `Cointag`s with an outstanding protocol rewards balance and pulls them.

## Error Handling

If any step in the buy & burn process fails, all ETH is sent to the creator.

### Upgradeability

`Cointag`s are upgradeable using the UUPS (Universal Upgradeable Proxy Standard) pattern with additional safety checks:
- Only the owner (creator) can initiate upgrades
- Similar to the Zora 1155 contract's upgradeability, new implementations must be registered in the `UpgradeGate`, which is controlled by a Zora team multisig to prevent malicious upgrades.

1 change: 1 addition & 0 deletions docs/public/uml/class-diagram-timedsale.svg
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 docs/public/uml/cointag-objects.svg
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 docs/public/uml/cointag-sequence.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions docs/scripts/generateUml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// scripts/generateUML.ts
import { exec } from "child_process";
import fs from "fs";
import path from "path";

// Function to wrap exec in a promise
const execPromise = (command: string) => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(`Error: ${error.message}`);
} else if (stderr) {
reject(`Error output: ${stderr}`);
} else {
resolve(stdout);
}
});
});
};

const inputDir = "uml";
const outputDir = "public/uml";

// Ensure the output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

// Read all files in the input directory
const files = fs.readdirSync(inputDir);

const promises = files.map(async (file) => {
if (file.endsWith(".puml")) {
const inputFilePath = path.join(inputDir, file);
const outputFilePath = path.join(outputDir, file.replace(".puml", ".svg"));

// Log input and output file information
console.log(`Processing: ${inputFilePath} -> ${outputFilePath}`);

// Execute the Docker command to generate the UML diagram
const command = `cat ${inputFilePath} | docker run --rm -i think/plantuml > ${outputFilePath}`;
await execPromise(command);
console.log(`Generated UML diagram for ${file} at ${outputFilePath}`);
}
});

// Wait for all promises to complete
await Promise.all(promises);
console.log("All UML diagrams generated successfully!");
Loading

0 comments on commit bc137f3

Please sign in to comment.