Skip to content

Commit

Permalink
feat(qa): tests, docs, wrapup
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbeckers committed Jun 4, 2024
1 parent b3fc749 commit 2988fa7
Show file tree
Hide file tree
Showing 21 changed files with 205 additions and 181 deletions.
144 changes: 112 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,59 @@
# CookieJar

CookieJar is a collection of smart contracts that allow users to deposit ERC20 and ERC721 tokens and earn interest on their deposits. The contracts are designed to be modular and extensible, allowing developers to easily add new features and functionality.
The cookie jar is a protocol for distributing tokens to users over time. It is designed to be flexible and extensible,
allowing developers to create custom cookie jars with different distribution rules and token types.

The CookieJar protocol supports various types of distributions, including Baal Cookie Jars for distributing tokens to
`Moloch DAO` members, `ERC20` and `ERC721` Cookie Jars for distributing tokens based on users' token balances, `List`
Cookie Jars for
distributing tokens to a pre-approved list of users, `Open` Cookie Jars for distributing tokens to any user who claims
them, and [`Hats`](https://www.hatsprotocol.xyz/) Cookie Jars for distributing tokens based on users' Hat wearingness.

# Architecture

The protocol consists of a set of composable modules to establish multiple types of cookie jars. The core module is
the `CookieJarCore` contract that represents the cookie jar which configures the rate limiting and ownership. By adding
a `Giver` module tokens held by the cookie jar can be distributed from a `6551` smart wallet or a `Safe` contract. Based
on the allowlist, users can claim tokens from the cookie jar.

## Rate limiting

By defining the `amount` and `period` of the cookie jar, the rate of token distribution can be controlled. The `amount`
is the number of tokens distributed per `period`. The `period` is the time interval in seconds between each
distribution.

## Allow lists

Allow lists gate access to the cookie jar. Currently we support `Baal` (MolochV3), `ERC20`, `ERC721`, `List`, `Open`,
and `Hats` gating.

Baal cookie jars are gated by the MolochV3 DAO. The `ERC20` cookie jar is gated by the balance of an ERC20 token.
The `ERC721` cookie jar is gated by the balance of an ERC721 token. The `List` cookie jar is gated by a list of
addresses. The `Open` cookie jar is open to all addresses. The `Hats` cookie jar is gated by the HatID.

## Givers

Givers are the source of tokens for the cookie jar. Currently we support `6551` and `Safe` givers.

The `6551` giver is a smart wallet that holds tokens. The `Safe` giver is a Safe contract that holds tokens.

## Example happy flow

```mermaid
sequenceDiagram
participant User as Allowlisted user
participant CJC as Cookie Jar
participant GB as Giver
participant AB as Allowlist
User->>CJC: reachInJar("I like cookies")
CJC->>AB: _isAllowList(User)
AB-->>CJC: true
CJC->>CJC: _isValidClaimPeriod(User)
CJC-->>CJC: true
CJC->>GB: _giveCookie(User, cookieAmount, cookieToken)
GB-->>CJC: cookieUid
CJC-->>User: emit GiveCookie(cookieUid, User, cookieAmount, reason)
```

# Deployment

Expand Down Expand Up @@ -65,7 +118,7 @@ The following table lists the addresses of the deployed contracts:
## Sepolia

| Contract | Address |
| ------------------- | -------------------------------------------- |
|---------------------|----------------------------------------------|
| Baal Cookie Jar | `0x1540F8eb71264EF41c7B12490f1D4556778a9E4f` |
| ERC20 Cookie Jar | `0x4ab3Ced6b45Ef849e5A69716b35a6F54a3b75e1c` |
| ERC721 Cookie Jar | `0x9036A0D0EB7C4400CDBAb840CD87B717Bb95Dd0E` |
Expand All @@ -77,31 +130,33 @@ The following table lists the addresses of the deployed contracts:
| Account | `0xB91594c1DBc9Fc2fF75eBF4Dcae8B9099D6e088A` |
| Cookie Jar NFT | `0xa16B24f39d90002C71914D055A65143720C1D5A7` |


## 6551 Tokenbound Registry

The Tokenbound Registry for 6551 tokens is deployed on the following chains:

| Chain | Address |
| -------- | -------------------------------------------- |
|----------|----------------------------------------------|
| Arbitrum | `0x02101dfB77FDE026414827Fdc604ddAF224F0921` |
| Gnosis | `0x02101dfB77FDE026414827Fdc604ddAF224F0921` |
| Optimism | `0x02101dfB77FDE026414827Fdc604ddAF224F0921` |
| Sepolia | `0x02101dfB77FDE026414827Fdc604ddAF224F0921` |

## Sustainability Safe

To fund further development we implemented sustainability fees when claiming cookies and the option to donate native tokens or ERC20s when summoning a CookieJar. Donations are optional; summon the CookieJar with `donationToken` = address(0) and `donationAmount` = 0;
To fund further development we implemented sustainability fees when claiming cookies and the option to donate native
tokens or ERC20s when summoning a CookieJar. Donations are optional; summon the CookieJar with `donationToken` =
address(0) and `donationAmount` = 0;

| Chain | Address |
| -------- | -------------------------------------------- |
|----------|----------------------------------------------|
| Gnosis | `0x1cE42BA793BA1E9Bf36c8b3f0aDDEe6c89D9a9fc` |
| Mainnet | `0x1cE42BA793BA1E9Bf36c8b3f0aDDEe6c89D9a9fc` |
| Optimism | `0x1cE42BA793BA1E9Bf36c8b3f0aDDEe6c89D9a9fc` |

## Installation

To run the CookieJar contracts, you will need to have foundry installed on your system. You can then run the following command:
To run the CookieJar contracts, you will need to have foundry installed on your system. You can then run the following
command:

`forge compile`

Expand All @@ -117,14 +172,16 @@ To run the tests for the Cookie Jar contracts, you can run the following command

### Cookie Jar Initialization Parameters

The Cookie Jar contract can be initialized with a set of parameters that define the behavior of the contract. The parameters are passed to the contract's constructor as an array of values.
The Cookie Jar contract can be initialized with a set of parameters that define the behavior of the contract. The
parameters are passed to the contract's constructor as an array of values.

The parameters are different depending on the type of Cookie Jar being created. There are three types of Cookie Jars: Baal, ERC20, and ERC721. The parameters for each type of Cookie Jar are as follows:
The parameters are different depending on the type of Cookie Jar being created. There are three types of Cookie Jars:
Baal, ERC20, and ERC721. The parameters for each type of Cookie Jar are as follows:

#### Baal Cookie Jar

| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------------------------- |
|-----------|---------|------------------------------------------------------------|
| 0 | address | The address of the owner or safe target of the Cookie Jar. |
| 1 | uint256 | The length of the period for the Cookie Jar. |
| 2 | uint256 | The amount of cookies to be distributed per period. |
Expand All @@ -137,7 +194,7 @@ The parameters are different depending on the type of Cookie Jar being created.
#### ERC20 Cookie Jar

| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------------------------- |
|-----------|---------|------------------------------------------------------------|
| 0 | address | The address of the owner or safe target of the Cookie Jar. |
| 1 | uint256 | The length of the period for the Cookie Jar. |
| 2 | uint256 | The amount of cookies to be distributed per period. |
Expand All @@ -148,7 +205,7 @@ The parameters are different depending on the type of Cookie Jar being created.
#### ERC721 Cookie Jar

| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------------------------- |
|-----------|---------|------------------------------------------------------------|
| 0 | address | The address of the owner or safe target of the Cookie Jar. |
| 1 | uint256 | The length of the period for the Cookie Jar. |
| 2 | uint256 | The amount of cookies to be distributed per period. |
Expand All @@ -159,7 +216,7 @@ The parameters are different depending on the type of Cookie Jar being created.
#### Hats Cookie Jar

| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------------------------- |
|-----------|---------|------------------------------------------------------------|
| 0 | address | The address of the owner or safe target of the Cookie Jar. |
| 1 | uint256 | The length of the period for the Cookie Jar. |
| 2 | uint256 | The amount of cookies to be distributed per period. |
Expand All @@ -169,50 +226,72 @@ The parameters are different depending on the type of Cookie Jar being created.
#### Mapping Cookie Jar

| Parameter | Type | Description |
| --------- | --------- | ---------------------------------------------------------- |
|-----------|-----------|------------------------------------------------------------|
| 0 | address | The address of the owner or safe target of the Cookie Jar. |
| 1 | uint256 | The length of the period for the Cookie Jar. |
| 2 | uint256 | The amount of cookies to be distributed per period. |
| 3 | address | The address of the cookie token contract. |
| 4 | address[] | The list of addresses allowed to claim cookies. |

The parameters for each type of Cookie Jar are passed to the summonCookieJar function as a byte array. The byte array is constructed by encoding the parameters in the order listed above.

## Poster schema

The Poster contract is used to emit events on chain as a way to store more complex information on chain in a gas-efficient manner.
The parameters for each type of Cookie Jar are passed to the summonCookieJar function as a byte array. The byte array is
constructed by encoding the parameters in the order listed above.

### Generating IDs

The CookieUtils library provides utility functions for generating unique identifiers (UIDs) for cookies and cookie jars.

#### getCookieJarUid

The getCookieJarUid function is used to generate a unique identifier for a cookie jar. The function takes an address parameter cookieJarAddr and returns a string that represents the unique identifier.
The getCookieJarUid function is used to generate a unique identifier for a cookie jar. The function takes an address
parameter cookieJarAddr and returns a string that represents the unique identifier.

For example, to generate a unique identifier for a new cookie jar, you can call the getCookieJarUid function with the address of the new cookie jar as the input parameter:
For example, to generate a unique identifier for a new cookie jar, you can call the getCookieJarUid function with the
address of the new cookie jar as the input parameter:

```js
function getCookieJarUid(address cookieJarAddr) internal view returns (string memory) {
return string(abi.encodePacked(Strings.toHexString(uint160(cookieJarAddr)), Strings.toHexString(block.chainid)));
}
function getCookieJarUid(address

cookieJarAddr
)
internal
view
returns(string
memory
)
{
return string(abi.encodePacked(Strings.toHexString(uint160(cookieJarAddr)), Strings.toHexString(block.chainid)));
}
```

The unique identifier is generated by concatenating the hexadecimal representation of the cookieJarAddr parameter with the hexadecimal representation of the current blockchain chain ID.
The unique identifier is generated by concatenating the hexadecimal representation of the cookieJarAddr parameter with
the hexadecimal representation of the current blockchain chain ID.

#### getCookieUid

The getCookieUid function is used to generate a unique identifier for a cookie. The function takes a string parameter cookieJarUid and returns a string that represents the unique identifier.
The getCookieUid function is used to generate a unique identifier for a cookie. The function takes a string parameter
cookieJarUid and returns a string that represents the unique identifier.

Similarly, to generate a unique identifier for a new cookie, you can call the getCookieUid function with the unique identifier of the cookie jar as the input parameter:
Similarly, to generate a unique identifier for a new cookie, you can call the getCookieUid function with the unique
identifier of the cookie jar as the input parameter:

```js
function getCookieUid(string memory cookieJarUid) internal view returns (string memory) {
return bytes32ToString(keccak256(abi.encodePacked(cookieJarUid, msg.sender, block.timestamp)));
}
function getCookieUid(string

memory
cookieJarUid
)
internal
view
returns(string
memory
)
{
return bytes32ToString(keccak256(abi.encodePacked(cookieJarUid, msg.sender, block.timestamp)));
}
```

The unique identifier is generated by concatenating the cookieJarUid parameter with the hexadecimal representation of the current user's address and the current block timestamp.
The unique identifier is generated by concatenating the cookieJarUid parameter with the hexadecimal representation of
the current user's address and the current block timestamp.

### Details schema

Expand Down Expand Up @@ -242,7 +321,8 @@ The post claim reason event is used to store a reason for a claim made by a user

### Assess Reason

The assess reason event is used to store an assessment of a reason for a claim made by a user. The event has the following schema:
The assess reason event is used to store an assessment of a reason for a claim made by a user. The event has the
following schema:

```json
{
Expand Down
2 changes: 0 additions & 2 deletions src/ERC6551/CookieJar6551.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,5 @@ abstract contract CookieJar6551 is Giver6551 {
*/
function setUp(bytes memory _initializationParams) public virtual override {
super.setUp(_initializationParams);

transferOwnership(target);
}
}
2 changes: 0 additions & 2 deletions src/SafeModule/ZodiacCookieJar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,5 @@ abstract contract ZodiacCookieJar is GiverZodiac {
*/
function setUp(bytes memory _initializationParams) public virtual override {
super.setUp(_initializationParams);

transferOwnership(target);
}
}
8 changes: 4 additions & 4 deletions src/core/CookieJarCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import { AllowlistBase } from "./allowlists/AllowlistBase.sol";

/**
* @title CookieJarCore
* @notice A base contract for a cookie jar that distributes tokens to members of a DAO.
* @notice A base contract for a cookie jar that distributes tokens according to an allow list.
* @dev This contract is intended to be inherited by a derived contract that implements the logic for distributing
* tokens to members of a DAO. The derived contract should implement the giveCookie function to handle the logic of
* tokens according to checks against an allowlist. The derived contract should implement the giveCookie function to handle the logic of
* transferring the specified amount of cookies to the given address and return a unique identifier for the cookie.
*/
abstract contract CookieJarCore is GiverBase, AllowlistBase, ICookieJar, Initializable, OwnableUpgradeable {
Expand Down Expand Up @@ -44,14 +44,14 @@ abstract contract CookieJarCore is GiverBase, AllowlistBase, ICookieJar, Initial
* @param _initializationParams The initialization parameters, encoded as a bytes array.
*/
function setUp(bytes memory _initializationParams) public virtual initializer {
(, uint256 _periodLength, uint256 _cookieAmount, address _cookieToken) =
(address target, uint256 _periodLength, uint256 _cookieAmount, address _cookieToken) =
abi.decode(_initializationParams, (address, uint256, uint256, address));

periodLength = _periodLength;
cookieAmount = _cookieAmount;
cookieToken = _cookieToken;

__Ownable_init();
_transferOwnership(target);

emit Setup(_initializationParams);
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/givers/Giver6551.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { CookieJarCore } from "src/core/CookieJarCore.sol";

/**
* @title Giver6551
* @dev This contract extends GiverBase and provides a mechanism for giving cookies.
* @dev This contract extends GiverBase and provides a mechanism for giving cookies from a smart account.
*/
abstract contract Giver6551 is CookieJarCore {
/// @notice The target address for the contract.
Expand Down
4 changes: 2 additions & 2 deletions src/core/givers/GiverZodiac.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ abstract contract GiverZodiac is Module, CookieJarCore {
function __Giver_init(bytes memory _initializationParams) public virtual override {
(address _safeTarget) = abi.decode(_initializationParams, (address));

setAvatar(_safeTarget);
setTarget(_safeTarget);
avatar = _safeTarget;
target = _safeTarget;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/interfaces/IHats.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

interface IHats {
function isWearerOfHat(address _user, uint256 _hatId) external view returns (bool isWearer);

function isAdminOfHat(address _user, uint256 _hatId) external view returns (bool isAdmin);

function isInGoodStanding(address _wearer, uint256 _hatId) external view returns (bool standing);
}
4 changes: 0 additions & 4 deletions test/CookieJarCore.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ contract MockCookieJarCore is CookieJarCore {
return _isAllowList(user);
}

function reachInJar(string calldata reason) public override {
// Do nothing
}

function _isAllowList(address user) internal view override returns (bool) {
return true;
}
Expand Down
4 changes: 0 additions & 4 deletions test/ERC6551/CookieJar6551.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ contract MockCookieJar6551 is CookieJar6551 {
return _isAllowList(user);
}

function reachInJar(string calldata reason) public override {
// Do nothing
}

function _isAllowList(address user) internal view override returns (bool) {
return true;
}
Expand Down
Loading

0 comments on commit 2988fa7

Please sign in to comment.