From ed637a6527b0166aa7320011aa74a2b5d0efa5d3 Mon Sep 17 00:00:00 2001 From: Carlos Juarez Date: Sat, 9 Nov 2024 10:16:36 +0700 Subject: [PATCH] feat: adding delegation wall contract --- script/Deploy.s.sol | 8 +++--- src/DelegationWall.sol | 47 ++++++++++++++++++++++++++++++++ src/Foo.sol | 8 ------ test/DelegationWall.t.sol | 23 ++++++++++++++++ test/Foo.t.sol | 56 --------------------------------------- 5 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 src/DelegationWall.sol delete mode 100644 src/Foo.sol create mode 100644 test/DelegationWall.t.sol delete mode 100644 test/Foo.t.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 498db52..803605a 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.25 <0.9.0; -import { Foo } from "../src/Foo.sol"; +import { DelegationWall } from "../src/DelegationWall.sol"; import { BaseScript } from "./Base.s.sol"; /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting contract Deploy is BaseScript { - function run() public broadcast returns (Foo foo) { - foo = new Foo(); - } + function run() public broadcast returns (DelegationWall delegationWall) { + delegationWall = new DelegationWall(); + } } diff --git a/src/DelegationWall.sol b/src/DelegationWall.sol new file mode 100644 index 0000000..bc566b1 --- /dev/null +++ b/src/DelegationWall.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.25; + +/// @title DelegationWall - Release 1, Build 1 +/// @author Aragon Association - 2024 +/// @notice A smart contract where any wallet can announce itself as a delegate by providing an IPFS Id +contract DelegationWall { + struct Candidate { + bytes contentUrl; + } + + /// @dev Stores the data registered by the delegate candidates + mapping(address => Candidate) public candidates; + + /// @dev Keeps track of the addresses that have been already registered, used to enumerate. + address[] public candidateAddresses; + + /// @notice Emitted when a wallet registers as a candidate + event CandidateRegistered(address indexed candidate, bytes contentUrl); + + /// @notice Raised when a delegate registers with an empty contentUrl + error EmptyContent(); + + /// @notice Registers the given data as a new delegation candidate + function register(bytes memory _contentUrl) public { + if (_contentUrl.length == 0) revert EmptyContent(); + + if (candidates[msg.sender].contentUrl.length == 0) { + candidateAddresses.push(msg.sender); + } + + candidates[msg.sender].contentUrl = _contentUrl; + + emit CandidateRegistered(msg.sender, _contentUrl); + } + + /// @notice Returns the list of candidate addresses registered + /// @dev Use this function to get all addresses in a single call. You can still call candidateAddresses[idx] to resolve them one by one. + function getCandidateAddresses() public view returns (address[] memory) { + return candidateAddresses; + } + + /// @notice Returns the number of candidate entries available + function candidateCount() public view returns (uint256) { + return candidateAddresses.length; + } +} diff --git a/src/Foo.sol b/src/Foo.sol deleted file mode 100644 index 7483070..0000000 --- a/src/Foo.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.25; - -contract Foo { - function id(uint256 value) external pure returns (uint256) { - return value; - } -} diff --git a/test/DelegationWall.t.sol b/test/DelegationWall.t.sol new file mode 100644 index 0000000..cfb4e28 --- /dev/null +++ b/test/DelegationWall.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import { Test } from "forge-std/src/Test.sol"; +import { console2 } from "forge-std/src/console2.sol"; + +import { DelegationWall } from "../src/DelegationWall.sol"; + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); +} + +/// @dev If this is your first time with Forge, read this tutorial in the Foundry Book: +/// https://book.getfoundry.sh/forge/writing-tests +contract DelegationWallTest is Test { + DelegationWall internal delegationWall; + + /// @dev A function invoked before each test case is run. + function setUp() public virtual { + // Instantiate the contract-under-test. + delegationWall = new DelegationWall(); + } +} diff --git a/test/Foo.t.sol b/test/Foo.t.sol deleted file mode 100644 index 727337a..0000000 --- a/test/Foo.t.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.25 <0.9.0; - -import { Test } from "forge-std/src/Test.sol"; -import { console2 } from "forge-std/src/console2.sol"; - -import { Foo } from "../src/Foo.sol"; - -interface IERC20 { - function balanceOf(address account) external view returns (uint256); -} - -/// @dev If this is your first time with Forge, read this tutorial in the Foundry Book: -/// https://book.getfoundry.sh/forge/writing-tests -contract FooTest is Test { - Foo internal foo; - - /// @dev A function invoked before each test case is run. - function setUp() public virtual { - // Instantiate the contract-under-test. - foo = new Foo(); - } - - /// @dev Basic test. Run it with `forge test -vvv` to see the console log. - function test_Example() external view { - console2.log("Hello World"); - uint256 x = 42; - assertEq(foo.id(x), x, "value mismatch"); - } - - /// @dev Fuzz test that provides random values for an unsigned integer, but which rejects zero as an input. - /// If you need more sophisticated input validation, you should use the `bound` utility instead. - /// See https://twitter.com/PaulRBerg/status/1622558791685242880 - function testFuzz_Example(uint256 x) external view { - vm.assume(x != 0); // or x = bound(x, 1, 100) - assertEq(foo.id(x), x, "value mismatch"); - } - - /// @dev Fork test that runs against an Ethereum Mainnet fork. For this to work, you need to set `API_KEY_ALCHEMY` - /// in your environment You can get an API key for free at https://alchemy.com. - function testFork_Example() external { - // Silently pass this test if there is no API key. - string memory alchemyApiKey = vm.envOr("API_KEY_ALCHEMY", string("")); - if (bytes(alchemyApiKey).length == 0) { - return; - } - - // Otherwise, run the test against the mainnet fork. - vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 16_428_000 }); - address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address holder = 0x7713974908Be4BEd47172370115e8b1219F4A5f0; - uint256 actualBalance = IERC20(usdc).balanceOf(holder); - uint256 expectedBalance = 196_307_713.810457e6; - assertEq(actualBalance, expectedBalance); - } -}