Skip to content

Commit

Permalink
Simple streaming grants (#154)
Browse files Browse the repository at this point in the history
* add superfluid finance

* basic grants contract

* very basic, rm superfluid pkg for now

* mapping of approved recipients

* add snapshot block on init

* calculate voting power

* voting

* Update revolution_contracts.yml

* setters

* let anyone make a grants app w/enough voting power!

* prettier

* move to initializer

* basic tests

* Update revolution_contracts.yml

* deploy grantos

* new grants deploy

* fix

* Update 8453.txt

* ensure unit updates work

* add more pool util functions

* try scaling units back

* try connecting pool?

* fix token vote weight

* grants bb

* new flow rate boom

* try this

* Update revolution_contracts.yml
  • Loading branch information
rocketman-21 authored May 13, 2024
1 parent fb0d9e8 commit c517edf
Show file tree
Hide file tree
Showing 42 changed files with 7,345 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-points-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cobuild/revolution": minor
---

Add basic Grants program using Superfluid streaming
2 changes: 1 addition & 1 deletion .github/workflows/revolution_contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ jobs:
uses: ./.github/workflows/coverage.yml
with:
package: "revolution"
files_to_ignore: '"*lib*" "*base*" "*governance*" "*protocol-rewards*" "*interfaces*" "*extensions*"'
files_to_ignore: '"*lib*" "*base*" "*governance*" "*protocol-rewards*" "*grants*" "*interfaces*" "*extensions*" "*superfluid*"'
2 changes: 2 additions & 0 deletions packages/revolution/deploys/grants/8453.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Grants: 0xa9c7256e654b004df9968b4575cf2fe9949548ca
GrantsImpl: 0x74b9304102cffb7af22a7f5635559f1057ee674a
1 change: 0 additions & 1 deletion packages/revolution/remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ solmate=node_modules/solmate
@cobuild/protocol-rewards/src/=node_modules/@cobuild/protocol-rewards/src/
@cobuild/splits/src/=node_modules/@cobuild/splits/src/
@cobuild/utility-contracts/src/=node_modules/@cobuild/utility-contracts/src/

75 changes: 75 additions & 0 deletions packages/revolution/script/grants/DeployGrants.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.22;

import { console2 } from "forge-std/console2.sol";
import { Script } from "forge-std/Script.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { RevolutionGrants } from "../../src/grants/RevolutionGrants.sol";
import { ERC1967Proxy } from "@cobuild/utility-contracts/src/proxy/ERC1967Proxy.sol";
import { IRevolutionGrants } from "../../src/interfaces/IRevolutionGrants.sol";

contract DeployGrants is Script {
using Strings for uint256;

address grants;
address grantsImpl;

function run() public {
uint256 chainID = vm.envUint("CHAIN_ID");
uint256 key = vm.envUint("PRIVATE_KEY");
address manager = vm.envAddress("MANAGER_OWNER");
address votingPower = vm.envAddress("VOTING_POWER");
address superToken = vm.envAddress("SUPER_TOKEN");

address deployerAddress = vm.addr(key);

vm.startBroadcast(deployerAddress);

IRevolutionGrants.GrantsParams memory params = IRevolutionGrants.GrantsParams({
tokenVoteWeight: 1000 * 1e18,
pointsVoteWeight: 1, // Example points vote weight
quorumVotesBPS: 0, // Example quorum votes in basis points (50%)
minVotingPowerToVote: 1e18, // Minimum voting power required to vote
minVotingPowerToCreate: 100 * 1e18 // Minimum voting power required to create a grant
});

grantsImpl = address(new RevolutionGrants(manager));
grants = address(new ERC1967Proxy(grantsImpl, ""));

IRevolutionGrants(grants).initialize({
votingPower: votingPower,
superToken: superToken,
initialOwner: manager,
grantsParams: params
});

vm.stopBroadcast();

writeDeploymentDetailsToFile(chainID);
}

function writeDeploymentDetailsToFile(uint256 chainID) private {
string memory filePath = string(abi.encodePacked("deploys/grants/", chainID.toString(), ".txt"));

vm.writeFile(filePath, "");
vm.writeLine(filePath, string(abi.encodePacked("Grants: ", addressToString(grants))));
vm.writeLine(filePath, string(abi.encodePacked("GrantsImpl: ", addressToString(grantsImpl))));
}

function addressToString(address _addr) private pure returns (string memory) {
bytes memory s = new bytes(40);
for (uint256 i = 0; i < 20; i++) {
bytes1 b = bytes1(uint8(uint256(uint160(_addr)) / (2 ** (8 * (19 - i)))));
bytes1 hi = bytes1(uint8(b) / 16);
bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
s[2 * i] = char(hi);
s[2 * i + 1] = char(lo);
}
return string(abi.encodePacked("0x", string(s)));
}

function char(bytes1 b) private pure returns (bytes1 c) {
if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
else return bytes1(uint8(b) + 0x57);
}
}
200 changes: 200 additions & 0 deletions packages/revolution/src/base/erc777/IERC777.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC777/IERC777.sol)

pragma solidity ^0.8.22;

/**
* @dev Interface of the ERC777Token standard as defined in the EIP.
*
* This contract uses the
* https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
* token holders and recipients react to token movements by using setting implementers
* for the associated interfaces in said registry. See {IERC1820Registry} and
* {ERC1820Implementer}.
*/
interface IERC777 {
/**
* @dev Emitted when `amount` tokens are created by `operator` and assigned to `to`.
*
* Note that some additional user `data` and `operatorData` can be logged in the event.
*/
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

/**
* @dev Emitted when `operator` destroys `amount` tokens from `account`.
*
* Note that some additional user `data` and `operatorData` can be logged in the event.
*/
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);

/**
* @dev Emitted when `operator` is made operator for `tokenHolder`
*/
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);

/**
* @dev Emitted when `operator` is revoked its operator status for `tokenHolder`
*/
event RevokedOperator(address indexed operator, address indexed tokenHolder);

/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);

/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() external view returns (string memory);

/**
* @dev Returns the smallest part of the token that is not divisible. This
* means all token operations (creation, movement and destruction) must have
* amounts that are a multiple of this number.
*
* For most token contracts, this value will equal 1.
*/
function granularity() external view returns (uint256);

/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);

/**
* @dev Returns the amount of tokens owned by an account (`owner`).
*/
function balanceOf(address owner) external view returns (uint256);

/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* If send or receive hooks are registered for the caller and `recipient`,
* the corresponding functions will be called with `data` and empty
* `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
*
* Emits a {Sent} event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the {IERC777Recipient}
* interface.
*/
function send(address recipient, uint256 amount, bytes calldata data) external;

/**
* @dev Destroys `amount` tokens from the caller's account, reducing the
* total supply.
*
* If a send hook is registered for the caller, the corresponding function
* will be called with `data` and empty `operatorData`. See {IERC777Sender}.
*
* Emits a {Burned} event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
*/
function burn(uint256 amount, bytes calldata data) external;

/**
* @dev Returns true if an account is an operator of `tokenHolder`.
* Operators can send and burn tokens on behalf of their owners. All
* accounts are their own operator.
*
* See {operatorSend} and {operatorBurn}.
*/
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);

/**
* @dev Make an account an operator of the caller.
*
* See {isOperatorFor}.
*
* Emits an {AuthorizedOperator} event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/
function authorizeOperator(address operator) external;

/**
* @dev Revoke an account's operator status for the caller.
*
* See {isOperatorFor} and {defaultOperators}.
*
* Emits a {RevokedOperator} event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/
function revokeOperator(address operator) external;

/**
* @dev Returns the list of default operators. These accounts are operators
* for all token holders, even if {authorizeOperator} was never called on
* them.
*
* This list is immutable, but individual holders may revoke these via
* {revokeOperator}, in which case {isOperatorFor} will return false.
*/
function defaultOperators() external view returns (address[] memory);

/**
* @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
* be an operator of `sender`.
*
* If send or receive hooks are registered for `sender` and `recipient`,
* the corresponding functions will be called with `data` and
* `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
*
* Emits a {Sent} event.
*
* Requirements
*
* - `sender` cannot be the zero address.
* - `sender` must have at least `amount` tokens.
* - the caller must be an operator for `sender`.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the {IERC777Recipient}
* interface.
*/
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;

/**
* @dev Destroys `amount` tokens from `account`, reducing the total supply.
* The caller must be an operator of `account`.
*
* If a send hook is registered for `account`, the corresponding function
* will be called with `data` and `operatorData`. See {IERC777Sender}.
*
* Emits a {Burned} event.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
* - the caller must be an operator for `account`.
*/
function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external;

event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
}
3 changes: 0 additions & 3 deletions packages/revolution/src/culture-index/CultureIndex.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ import { IRevolutionVotingPower } from "../interfaces/IRevolutionVotingPower.sol
import { ICultureIndex } from "../interfaces/ICultureIndex.sol";
import { RevolutionVersion } from "../version/RevolutionVersion.sol";

import { ERC20VotesUpgradeable } from "../base/erc20/ERC20VotesUpgradeable.sol";
import { MaxHeap } from "./MaxHeap.sol";
import { CultureIndexStorageV1 } from "./storage/CultureIndexStorageV1.sol";

import { ERC721CheckpointableUpgradeable } from "../base/ERC721CheckpointableUpgradeable.sol";

contract CultureIndex is
ICultureIndex,
RevolutionVersion,
Expand Down
Loading

0 comments on commit c517edf

Please sign in to comment.