-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(contracts): add example implementation and tests for BaseChecker…
… contract
- Loading branch information
Showing
23 changed files
with
278 additions
and
3 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1,8 +1,9 @@ | ||
node_modules | ||
.env | ||
/cache | ||
|
||
# Hardhat files | ||
/cache | ||
/cache-hh | ||
/artifacts | ||
|
||
# Forge files | ||
|
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
21 changes: 21 additions & 0 deletions
21
packages/contracts/contracts/src/test/BaseERC721Checker.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,21 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.27; | ||
|
||
import {BaseChecker} from "../../src/BaseChecker.sol"; | ||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
|
||
contract BaseERC721Checker is BaseChecker { | ||
IERC721 public immutable NFT; | ||
|
||
constructor(IERC721 _nft) { | ||
NFT = IERC721(_nft); | ||
} | ||
|
||
function _check(address subject, bytes memory evidence) internal view override returns (bool) { | ||
// Decode the tokenId from the evidence. | ||
uint256 tokenId = abi.decode(evidence, (uint256)); | ||
|
||
// Return true if the subject is the owner of the tokenId, false otherwise. | ||
return NFT.ownerOf(tokenId) == subject; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
packages/contracts/contracts/src/test/BaseERC721Policy.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,17 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.27; | ||
|
||
import {BasePolicy} from "../../src/BasePolicy.sol"; | ||
import {BaseERC721Checker} from "./BaseERC721Checker.sol"; | ||
|
||
contract BaseERC721Policy is BasePolicy { | ||
BaseERC721Checker public immutable CHECKER; | ||
|
||
constructor(BaseERC721Checker _checker) BasePolicy(_checker) { | ||
CHECKER = BaseERC721Checker(_checker); | ||
} | ||
|
||
function trait() external pure returns (string memory) { | ||
return "BaseERC721"; | ||
} | ||
} |
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,44 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.27; | ||
|
||
import {BaseERC721Policy} from "./BaseERC721Policy.sol"; | ||
|
||
contract BaseVoting { | ||
event Registered(address voter); | ||
event Voted(address voter, uint8 option); | ||
|
||
error NotRegistered(); | ||
error AlreadyVoted(); | ||
error InvalidOption(); | ||
|
||
BaseERC721Policy public immutable POLICY; | ||
|
||
// Mapping to track if an address has voted | ||
mapping(address => bool) public hasVoted; | ||
// Mapping to count votes for each option | ||
mapping(uint8 => uint256) public voteCounts; | ||
|
||
constructor(BaseERC721Policy _policy) { | ||
POLICY = _policy; | ||
} | ||
|
||
// Function to register a voter using a the policy enforcement. | ||
function register(uint256 tokenId) external { | ||
bytes memory evidence = abi.encode(tokenId); | ||
POLICY.enforce(msg.sender, evidence); | ||
|
||
emit Registered(msg.sender); | ||
} | ||
|
||
// Function to cast a vote for a given option. | ||
function vote(uint8 option) external { | ||
if (!POLICY.enforced(address(this), msg.sender)) revert NotRegistered(); | ||
if (hasVoted[msg.sender]) revert AlreadyVoted(); | ||
if (option >= 2) revert InvalidOption(); | ||
|
||
hasVoted[msg.sender] = true; | ||
voteCounts[option]++; | ||
|
||
emit Voted(msg.sender, option); | ||
} | ||
} |
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,15 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
|
||
contract NFT is ERC721 { | ||
uint256 private _tokenIdCounter; | ||
|
||
constructor() ERC721("NFT", "NFT") {} | ||
|
||
function mint(address to) external { | ||
_safeMint(to, _tokenIdCounter); | ||
_tokenIdCounter++; | ||
} | ||
} |
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,87 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.27; | ||
|
||
import {Test} from "forge-std/src/Test.sol"; | ||
import {NFT} from "../src/test/NFT.sol"; | ||
import {BaseERC721Checker} from "../src/test/BaseERC721Checker.sol"; | ||
import {BaseERC721CheckerHarness} from "./wrappers/BaseERC721CheckerHarness.sol"; | ||
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; | ||
|
||
contract BaseChecker is Test { | ||
NFT internal nft; | ||
BaseERC721Checker internal checker; | ||
BaseERC721CheckerHarness internal checkerHarness; | ||
|
||
address public deployer = vm.addr(0x1); | ||
address public target = vm.addr(0x2); | ||
address public subject = vm.addr(0x3); | ||
address public notOwner = vm.addr(0x4); | ||
|
||
function setUp() public virtual { | ||
vm.startPrank(deployer); | ||
|
||
nft = new NFT(); | ||
checker = new BaseERC721Checker(nft); | ||
checkerHarness = new BaseERC721CheckerHarness(nft); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check_internal_RevertWhen_ERC721NonexistentToken() public { | ||
vm.startPrank(target); | ||
|
||
vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, uint256(0))); | ||
checkerHarness.exposed__check(subject, abi.encode(0)); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check_internal_return_False() public { | ||
vm.startPrank(target); | ||
|
||
nft.mint(subject); | ||
|
||
assert(!checkerHarness.exposed__check(notOwner, abi.encode(0))); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check_Internal() public { | ||
vm.startPrank(target); | ||
|
||
nft.mint(subject); | ||
|
||
assert(checkerHarness.exposed__check(subject, abi.encode(0))); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check_RevertWhen_ERC721NonexistentToken() public { | ||
vm.startPrank(target); | ||
|
||
vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, uint256(0))); | ||
checker.check(subject, abi.encode(0)); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check_return_False() public { | ||
vm.startPrank(target); | ||
|
||
nft.mint(subject); | ||
|
||
assert(!checker.check(notOwner, abi.encode(0))); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function test_check() public { | ||
vm.startPrank(target); | ||
|
||
nft.mint(subject); | ||
|
||
assert(checker.check(subject, abi.encode(0))); | ||
|
||
vm.stopPrank(); | ||
} | ||
} |
Empty file.
18 changes: 18 additions & 0 deletions
18
packages/contracts/contracts/test/wrappers/BaseERC721CheckerHarness.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,18 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.27; | ||
|
||
import {BaseERC721Checker} from "../../src/test/BaseERC721Checker.sol"; | ||
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; | ||
|
||
// This contract is a harness for testing the BaseERC721Checker contract. | ||
// Deploy this contract and call its methods to test the internal methods of BaseERC721Checker. | ||
contract BaseERC721CheckerHarness is BaseERC721Checker { | ||
constructor(IERC721 _nft) BaseERC721Checker(_nft) {} | ||
|
||
/// @notice Exposes the internal `_check` method for testing purposes. | ||
/// @param subject The address to be checked. | ||
/// @param evidence The data associated with the check. | ||
function exposed__check(address subject, bytes calldata evidence) public view returns (bool) { | ||
return _check(subject, evidence); | ||
} | ||
} |
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.
Empty file.
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,70 @@ | ||
import { expect } from "chai" | ||
import { ethers } from "hardhat" | ||
import { AbiCoder, Signer } from "ethers" | ||
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers" | ||
import { BaseERC721Checker, BaseERC721Checker__factory, NFT, NFT__factory } from "../typechain-types" | ||
|
||
describe("BaseChecker", () => { | ||
async function deployBaseCheckerFixture() { | ||
const [deployer, subject, target, notOwner]: Signer[] = await ethers.getSigners() | ||
const subjectAddress: string = await subject.getAddress() | ||
const notOwnerAddress: string = await notOwner.getAddress() | ||
|
||
const NFTFactory: NFT__factory = await ethers.getContractFactory("NFT") | ||
const BaseERC721CheckerFactory: BaseERC721Checker__factory = | ||
await ethers.getContractFactory("BaseERC721Checker") | ||
|
||
const nft: NFT = await NFTFactory.deploy() | ||
const checker: BaseERC721Checker = await BaseERC721CheckerFactory.connect(deployer).deploy( | ||
await nft.getAddress() | ||
) | ||
|
||
// mint 0 for subject. | ||
await nft.connect(deployer).mint(subjectAddress) | ||
|
||
// encoded token ids. | ||
const validNFTId = AbiCoder.defaultAbiCoder().encode(["uint256"], [0]) | ||
const invalidNFTId = AbiCoder.defaultAbiCoder().encode(["uint256"], [1]) | ||
|
||
return { | ||
nft, | ||
checker, | ||
target, | ||
subjectAddress, | ||
notOwnerAddress, | ||
validNFTId, | ||
invalidNFTId | ||
} | ||
} | ||
|
||
describe("constructor()", () => { | ||
it("Should deploy the checker contract correctly", async () => { | ||
const { checker } = await loadFixture(deployBaseCheckerFixture) | ||
|
||
expect(checker).to.not.eq(undefined) | ||
}) | ||
}) | ||
|
||
describe("check()", () => { | ||
it("should revert the check when the evidence is not meaningful", async () => { | ||
const { nft, checker, target, subjectAddress, invalidNFTId } = await loadFixture(deployBaseCheckerFixture) | ||
|
||
await expect(checker.connect(target).check(subjectAddress, invalidNFTId)).to.be.revertedWithCustomError( | ||
nft, | ||
"ERC721NonexistentToken" | ||
) | ||
}) | ||
|
||
it("should return false when the subject is not the owner of the evidenced token", async () => { | ||
const { checker, target, notOwnerAddress, validNFTId } = await loadFixture(deployBaseCheckerFixture) | ||
|
||
expect(await checker.connect(target).check(notOwnerAddress, validNFTId)).to.be.equal(false) | ||
}) | ||
|
||
it("should check", async () => { | ||
const { checker, target, subjectAddress, validNFTId } = await loadFixture(deployBaseCheckerFixture) | ||
|
||
expect(await checker.connect(target).check(subjectAddress, validNFTId)).to.be.equal(true) | ||
}) | ||
}) | ||
}) |