-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1631 from privacy-scaling-explorations/feat/regis…
…try-payout-extensions feat(contracts): add first registry and payout extensions
- Loading branch information
Showing
11 changed files
with
259 additions
and
7 deletions.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
packages/contracts/contracts/extensions/interfaces/ISimpleProjectRegistry.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
/// @title ISimpleProjectRegistry | ||
/// @notice An interface for a simple project registry | ||
interface ISimpleProjectRegistry { | ||
/// @notice A struct representing a single registry project. | ||
struct RegistryProject { | ||
// project metadata url | ||
bytes32 metadataUrl; | ||
// project address | ||
address project; | ||
} | ||
|
||
/// @notice Get a registry metadata url | ||
/// @return The metadata url in bytes32 format | ||
function getRegistryMetadataUrl() external view returns (bytes32); | ||
|
||
/// @notice Get a project | ||
/// @param index The index of the project | ||
/// @return The address of the project | ||
function getProject(uint256 index) external view returns (RegistryProject memory); | ||
|
||
/// @notice Get all projects | ||
/// @return The addresses of the projects | ||
function getProjects() external view returns (RegistryProject[] memory); | ||
|
||
/// @notice Get the number of projects | ||
/// @return The number of projects | ||
function getProjectsNumber() external view returns (uint256); | ||
} |
66 changes: 66 additions & 0 deletions
66
packages/contracts/contracts/extensions/registry/SimpleRecipientRegistry.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
import { ISimpleProjectRegistry } from "../interfaces/ISimpleProjectRegistry.sol"; | ||
|
||
/// @title SimpleProjectRegistry | ||
/// @notice This contract is a simple registry of projects | ||
/// @dev This does not constrain the number of projects | ||
/// which might be > vote options | ||
/// @dev it does not prevent duplicate addresses from being | ||
/// added as projects | ||
/// @notice it does not allow to remove projects either | ||
contract SimpleProjectRegistry is Ownable(msg.sender), ISimpleProjectRegistry { | ||
/// @notice simple storage of projects is an array of addresses with the the index being the position in the array | ||
RegistryProject[] internal projects; | ||
|
||
/// @notice registry metadata url | ||
bytes32 internal immutable metadataUrl; | ||
|
||
/// @notice Create a new instance of the registry contract | ||
/// @param _metadataUrl the registry metadata url | ||
constructor(bytes32 _metadataUrl) payable { | ||
metadataUrl = _metadataUrl; | ||
} | ||
|
||
/// @notice Add a project to the registry | ||
/// @param project The address of the project to add | ||
function addProject(RegistryProject calldata project) external onlyOwner { | ||
projects.push(project); | ||
} | ||
|
||
/// @notice Add multiple projects to the registry | ||
/// @param _projects The addresses of the projects to add | ||
function addProjects(RegistryProject[] calldata _projects) external onlyOwner { | ||
uint256 len = _projects.length; | ||
for (uint256 i = 0; i < len; ) { | ||
projects.push(_projects[i]); | ||
|
||
unchecked { | ||
i++; | ||
} | ||
} | ||
} | ||
|
||
/// @inheritdoc ISimpleProjectRegistry | ||
function getRegistryMetadataUrl() external view returns (bytes32) { | ||
return metadataUrl; | ||
} | ||
|
||
/// @inheritdoc ISimpleProjectRegistry | ||
function getProject(uint256 index) external view returns (RegistryProject memory) { | ||
return projects[index]; | ||
} | ||
|
||
/// @inheritdoc ISimpleProjectRegistry | ||
function getProjects() external view returns (RegistryProject[] memory) { | ||
return projects; | ||
} | ||
|
||
/// @inheritdoc ISimpleProjectRegistry | ||
function getProjectsNumber() external view returns (uint256) { | ||
return projects.length; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.10; | ||
|
||
/// @title ITally | ||
/// @notice Tally interface | ||
interface ITally { | ||
/// @notice Verify the number of spent voice credits per vote option from the tally.json | ||
/// @param _voteOptionIndex the index of the vote option where credits were spent | ||
/// @param _spent the spent voice credits for a given vote option index | ||
/// @param _spentProof proof generated for the perVOSpentVoiceCredits | ||
/// @param _spentSalt the corresponding salt given in the tally perVOSpentVoiceCredits object | ||
/// @param _voteOptionTreeDepth depth of the vote option tree | ||
/// @param _spentVoiceCreditsHash hashLeftRight(number of spent voice credits, spent salt) | ||
/// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) | ||
// in the tally.json file | ||
/// @return isValid Whether the provided proof is valid | ||
function verifyPerVOSpentVoiceCredits( | ||
uint256 _voteOptionIndex, | ||
uint256 _spent, | ||
uint256[][] calldata _spentProof, | ||
uint256 _spentSalt, | ||
uint8 _voteOptionTreeDepth, | ||
uint256 _spentVoiceCreditsHash, | ||
uint256 _resultCommitment | ||
) external view returns (bool); | ||
|
||
/// @notice Verify the number of spent voice credits from the tally.json | ||
/// @param _totalSpent spent field retrieved in the totalSpentVoiceCredits object | ||
/// @param _totalSpentSalt the corresponding salt in the totalSpentVoiceCredit object | ||
/// @param _resultCommitment hashLeftRight(merkle root of the results.tally, results.salt) in tally.json file | ||
/// @param _perVOSpentVoiceCreditsHash only for QV - hashLeftRight(merkle root of the no spent voice credits, salt) | ||
function verifySpentVoiceCredits( | ||
uint256 _totalSpent, | ||
uint256 _totalSpentSalt, | ||
uint256 _resultCommitment, | ||
uint256 _perVOSpentVoiceCreditsHash | ||
) external view returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
/// @title MockERC20 | ||
/// @notice A mock ERC20 contract that mints 100,000,000,000,000 tokens to the deployer | ||
contract MockERC20 is ERC20 { | ||
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { | ||
_mint(msg.sender, 100e18); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import { ITally } from "../interfaces/ITally.sol"; | ||
|
||
contract MockTally is ITally { | ||
bool public returnValue = true; | ||
|
||
/// @notice Flip the return value | ||
/// @dev used for mock testing | ||
function flipReturnValue() external { | ||
returnValue = !returnValue; | ||
} | ||
|
||
/// @inheritdoc ITally | ||
function verifyPerVOSpentVoiceCredits( | ||
uint256 _voteOptionIndex, | ||
uint256 _spent, | ||
uint256[][] calldata _spentProof, | ||
uint256 _spentSalt, | ||
uint8 _voteOptionTreeDepth, | ||
uint256 _spentVoiceCreditsHash, | ||
uint256 _resultCommitment | ||
) external view returns (bool) { | ||
return returnValue; | ||
} | ||
|
||
/// @inheritdoc ITally | ||
function verifySpentVoiceCredits( | ||
uint256 _totalSpent, | ||
uint256 _totalSpentSalt, | ||
uint256 _resultCommitment, | ||
uint256 _perVOSpentVoiceCreditsHash | ||
) external view returns (bool) { | ||
return returnValue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
packages/contracts/tests/extensions/SimpleProjectRegistry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { expect } from "chai"; | ||
import { encodeBytes32String, Signer } from "ethers"; | ||
|
||
import { deployContract, getSigners } from "../../ts"; | ||
import { SimpleProjectRegistry } from "../../typechain-types"; | ||
|
||
describe("SimpleProjecttRegistry", () => { | ||
let projectRegistry: SimpleProjectRegistry; | ||
let owner: Signer; | ||
let user: Signer; | ||
|
||
let ownerAddress: string; | ||
let userAddress: string; | ||
|
||
const metadataUrl = encodeBytes32String("url"); | ||
|
||
before(async () => { | ||
[owner, user] = await getSigners(); | ||
[ownerAddress, userAddress] = await Promise.all([owner.getAddress(), user.getAddress()]); | ||
projectRegistry = await deployContract("SimpleProjectRegistry", owner, true, metadataUrl); | ||
}); | ||
|
||
it("should allow the owner to add a project", async () => { | ||
await projectRegistry.addProject({ project: ownerAddress, metadataUrl }); | ||
}); | ||
|
||
it("should allow the owner to add multiple projects", async () => { | ||
await projectRegistry.addProjects([ | ||
{ project: ownerAddress, metadataUrl }, | ||
{ project: userAddress, metadataUrl }, | ||
{ project: ownerAddress, metadataUrl }, | ||
]); | ||
}); | ||
|
||
it("should throw if the caller is not the owner", async () => { | ||
await expect( | ||
projectRegistry.connect(user).addProject({ project: ownerAddress, metadataUrl }), | ||
).to.be.revertedWithCustomError(projectRegistry, "OwnableUnauthorizedAccount"); | ||
}); | ||
|
||
it("should return registry metadata url properly", async () => { | ||
const url = await projectRegistry.getRegistryMetadataUrl(); | ||
|
||
expect(url).to.equal(metadataUrl); | ||
}); | ||
|
||
it("should return the correct address given an index", async () => { | ||
const data = await projectRegistry.getProject(0n); | ||
|
||
expect(data.project).to.equal(ownerAddress); | ||
expect(data.metadataUrl).to.equal(metadataUrl); | ||
}); | ||
|
||
it("should return the full list of addresses", async () => { | ||
const projects = await projectRegistry.getProjects(); | ||
const data = projects.map((item) => ({ project: item.project, metadataUrl: item.metadataUrl })); | ||
|
||
expect(data).to.deep.equal([ | ||
{ project: ownerAddress, metadataUrl }, | ||
{ project: ownerAddress, metadataUrl }, | ||
{ project: userAddress, metadataUrl }, | ||
{ project: ownerAddress, metadataUrl }, | ||
]); | ||
}); | ||
}); |