diff --git a/package.json b/package.json
index ffa408b..01ed179 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,8 @@
     "package.json": "sort-package-json"
   },
   "dependencies": {
-    "@openzeppelin/contracts": "^5.0.1",
+    "@defi-wonderland/smock-foundry": "1.0.6",
+    "@openzeppelin/contracts": "5.0.1",
     "isolmate": "github:defi-wonderland/isolmate#59e1804"
   },
   "devDependencies": {
diff --git a/remappings.txt b/remappings.txt
index 83b675a..db861fd 100644
--- a/remappings.txt
+++ b/remappings.txt
@@ -4,4 +4,5 @@ isolmate/=node_modules/isolmate/src
 
 contracts/=solidity/contracts
 interfaces/=solidity/interfaces
+examples/=solidity/examples
 test/=solidity/test
\ No newline at end of file
diff --git a/solidity/contracts/governance/WonderGovernor.sol b/solidity/contracts/governance/WonderGovernor.sol
index f564ded..820d254 100644
--- a/solidity/contracts/governance/WonderGovernor.sol
+++ b/solidity/contracts/governance/WonderGovernor.sol
@@ -79,7 +79,7 @@ abstract contract WonderGovernor is
    */
   modifier validProposalType(uint8 proposalType) {
     if (!_isValidProposalType(proposalType)) {
-      revert InvalidProposalType(proposalType);
+      revert GovernorInvalidProposalType(proposalType);
     }
     _;
   }
diff --git a/solidity/examples/AliceGovernor.sol b/solidity/examples/AliceGovernor.sol
index 75b2930..2c2488d 100644
--- a/solidity/examples/AliceGovernor.sol
+++ b/solidity/examples/AliceGovernor.sol
@@ -7,13 +7,13 @@ import 'contracts/governance/utils/WonderVotes.sol';
 contract AliceGovernor is WonderGovernor {
   WonderVotes public votes;
   string internal _countingMode = 'support=bravo&quorum=bravo';
-  uint8[] internal __proposalTypes = [1, 2, 3];
+  uint8[] internal __proposalTypes = [0, 1, 2, 3];
 
-  mapping(uint256 proposalId => mapping(address => Receipt)) public receipts;
+  mapping(uint256 proposalId => mapping(address => BallotReceipt)) public receipts;
   mapping(uint256 proposalId => ProposalTrack) public proposalTracks;
 
   /// @notice Ballot receipt record for a voter
-  struct Receipt {
+  struct BallotReceipt {
     /// @notice Whether or not a vote has been cast
     bool hasVoted;
     /// @notice 0 = Against, 1 = For, 2 = Abstain
@@ -95,11 +95,11 @@ contract AliceGovernor is WonderGovernor {
       revert InvalidVoteType(_support);
     }
 
-    Receipt storage receipt = receipts[_proposalId][_account];
+    BallotReceipt storage _receipt = receipts[_proposalId][_account];
 
-    receipt.hasVoted = true;
-    receipt.support = _support;
-    receipt.votes = _weight;
+    _receipt.hasVoted = true;
+    _receipt.support = _support;
+    _receipt.votes = _weight;
   }
 
   function hasVoted(uint256 _proposalId, address _account) external view override returns (bool) {
diff --git a/solidity/interfaces/governance/IWonderGovernor.sol b/solidity/interfaces/governance/IWonderGovernor.sol
index 2e49684..eb8663f 100644
--- a/solidity/interfaces/governance/IWonderGovernor.sol
+++ b/solidity/interfaces/governance/IWonderGovernor.sol
@@ -105,7 +105,7 @@ interface IWonderGovernor is IERC165, IERC6372 {
   /**
    * @dev The proposalType is not supported by the governor.
    */
-  error InvalidProposalType(uint8 proposalType);
+  error GovernorInvalidProposalType(uint8 proposalType);
 
   /**
    * @dev Emitted when a proposal is created.
diff --git a/solidity/test/smock/SmockHelper.sol b/solidity/test/smock/SmockHelper.sol
new file mode 100644
index 0000000..7ef3520
--- /dev/null
+++ b/solidity/test/smock/SmockHelper.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {Test} from 'forge-std/Test.sol';
+
+contract SmockHelper is Test {
+  function deployMock(
+    string memory _label,
+    bytes memory _creationCode,
+    bytes memory _encodedArgs
+  ) internal returns (address _deployed) {
+    bytes memory _bytecode = abi.encodePacked(_creationCode, _encodedArgs);
+    assembly {
+      mstore(0x0, _creationCode)
+      _deployed := create2(0, add(_bytecode, 0x20), mload(_bytecode), 'Wonderland')
+    }
+    vm.label(_deployed, _label);
+    vm.allowCheatcodes(_deployed);
+  }
+}
diff --git a/solidity/test/smock/examples/MockAliceGovernor.sol b/solidity/test/smock/examples/MockAliceGovernor.sol
new file mode 100644
index 0000000..759c1a5
--- /dev/null
+++ b/solidity/test/smock/examples/MockAliceGovernor.sol
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {Test} from 'forge-std/Test.sol';
+import {
+  Address,
+  AliceGovernor,
+  Checkpoints,
+  Context,
+  DoubleEndedQueue,
+  ECDSA,
+  EIP712,
+  ERC165,
+  IERC1155Receiver,
+  IERC165,
+  IERC6372,
+  IERC721Receiver,
+  IWonderGovernor,
+  IWonderVotes,
+  Nonces,
+  SafeCast,
+  SignatureChecker,
+  Time,
+  WonderGovernor,
+  WonderVotes
+} from 'solidity/examples/AliceGovernor.sol';
+import 'solidity/contracts/governance/WonderGovernor.sol';
+import 'solidity/contracts/governance/utils/WonderVotes.sol';
+
+contract MockAliceGovernor is AliceGovernor, Test {
+  constructor(address _wonderToken) AliceGovernor(_wonderToken) {}
+
+  /// Mocked State Variables
+
+  function set_votes(WonderVotes _votes) public {
+    votes = _votes;
+  }
+
+  function mock_call_votes(WonderVotes _votes) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('votes()'), abi.encode(_votes));
+  }
+
+  function set__countingMode(string memory __countingMode) public {
+    _countingMode = __countingMode;
+  }
+
+  function set___proposalTypes(uint8[] memory ___proposalTypes) public {
+    __proposalTypes = ___proposalTypes;
+  }
+
+  function set_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public {
+    receipts[_key0][_key1] = _value;
+  }
+
+  function mock_call_receipts(uint256 _key0, address _key1, AliceGovernor.BallotReceipt memory _value) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('receipts(uint256,address)', _key0, _key1), abi.encode(_value));
+  }
+
+  function set_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public {
+    proposalTracks[_key0] = _value;
+  }
+
+  function mock_call_proposalTracks(uint256 _key0, AliceGovernor.ProposalTrack memory _value) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('proposalTracks(uint256)', _key0), abi.encode(_value));
+  }
+
+  /// Mocked External Functions
+
+  function mock_call_CLOCK_MODE(string memory _clockMode) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('CLOCK_MODE()'), abi.encode(_clockMode));
+  }
+
+  function mock_call_COUNTING_MODE(string memory _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('COUNTING_MODE()'), abi.encode(_return0));
+  }
+
+  function mock_call_clock(uint48 _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('clock()'), abi.encode(_return0));
+  }
+
+  function mock_call_votingPeriod(uint256 _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('votingPeriod()'), abi.encode(_return0));
+  }
+
+  function mock_call_votingDelay(uint256 _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('votingDelay()'), abi.encode(_return0));
+  }
+
+  function mock_call_quorum(uint256 _timepoint, uint8 _proposalType, uint256 _return0) public {
+    vm.mockCall(
+      address(this), abi.encodeWithSignature('quorum(uint256,uint8)', _timepoint, _proposalType), abi.encode(_return0)
+    );
+  }
+
+  function mock_call_hasVoted(uint256 _proposalId, address _account, bool _return0) public {
+    vm.mockCall(
+      address(this), abi.encodeWithSignature('hasVoted(uint256,address)', _proposalId, _account), abi.encode(_return0)
+    );
+  }
+
+  function mock_call_proposalThreshold(uint8 _proposalType, uint256 _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('proposalThreshold(uint8)', _proposalType), abi.encode(_return0));
+  }
+
+  function mock_call_isValidProposalType(uint8 _proposalType, bool _return0) public {
+    vm.mockCall(
+      address(this), abi.encodeWithSignature('isValidProposalType(uint8)', _proposalType), abi.encode(_return0)
+    );
+  }
+
+  /// Mocked Internal Functions
+
+  struct _getVotesOutput {
+    uint256 _returnParam0;
+  }
+
+  mapping(bytes32 => _getVotesOutput) private _getVotesOutputs;
+  bytes32[] private _getVotesInputHashes;
+
+  function mock_call__getVotes(
+    address _account,
+    uint8 _proposalType,
+    uint256 _timepoint,
+    bytes memory _params,
+    uint256 _returnParam0
+  ) public {
+    bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params));
+    _getVotesOutputs[_key] = _getVotesOutput(_returnParam0);
+    for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) {
+      if (_key == _getVotesInputHashes[_i]) {
+        return;
+      }
+    }
+    _getVotesInputHashes.push(_key);
+  }
+
+  function _getVotes(
+    address _account,
+    uint8 _proposalType,
+    uint256 _timepoint,
+    bytes memory _params
+  ) internal view override returns (uint256 _returnParam0) {
+    bytes32 _key = keccak256(abi.encode(_account, _proposalType, _timepoint, _params));
+    for (uint256 _i; _i < _getVotesInputHashes.length; ++_i) {
+      if (_key == _getVotesInputHashes[_i]) {
+        _getVotesOutput memory _output = _getVotesOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._getVotes(_account, _proposalType, _timepoint, _params);
+  }
+
+  struct _isValidProposalTypeOutput {
+    bool _returnParam0;
+  }
+
+  mapping(bytes32 => _isValidProposalTypeOutput) private _isValidProposalTypeOutputs;
+  bytes32[] private _isValidProposalTypeInputHashes;
+
+  function mock_call__isValidProposalType(uint8 _proposalType, bool _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode(_proposalType));
+    _isValidProposalTypeOutputs[_key] = _isValidProposalTypeOutput(_returnParam0);
+    for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) {
+      if (_key == _isValidProposalTypeInputHashes[_i]) {
+        return;
+      }
+    }
+    _isValidProposalTypeInputHashes.push(_key);
+  }
+
+  function _isValidProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) {
+    bytes32 _key = keccak256(abi.encode(_proposalType));
+    for (uint256 _i; _i < _isValidProposalTypeInputHashes.length; ++_i) {
+      if (_key == _isValidProposalTypeInputHashes[_i]) {
+        _isValidProposalTypeOutput memory _output = _isValidProposalTypeOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._isValidProposalType(_proposalType);
+  }
+
+  function mock_call__countVote(
+    uint256 _proposalId,
+    address _account,
+    uint8 _support,
+    uint256 _weight,
+    bytes memory _params
+  ) public {
+    vm.mockCall(
+      address(this),
+      abi.encodeWithSignature(
+        '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params
+      ),
+      abi.encode()
+    );
+  }
+
+  function _countVote(
+    uint256 _proposalId,
+    address _account,
+    uint8 _support,
+    uint256 _weight,
+    bytes memory _params
+  ) internal override {
+    (bool _success, bytes memory _data) = address(this).call(
+      abi.encodeWithSignature(
+        '_countVote(uint256,address,uint8,uint256,bytes)', _proposalId, _account, _support, _weight, _params
+      )
+    );
+    if (_success) return abi.decode(_data, ());
+    else return super._countVote(_proposalId, _account, _support, _weight, _params);
+  }
+
+  struct _quorumReachedOutput {
+    bool _returnParam0;
+  }
+
+  mapping(bytes32 => _quorumReachedOutput) private _quorumReachedOutputs;
+  bytes32[] private _quorumReachedInputHashes;
+
+  function mock_call__quorumReached(uint256 _proposalId, bool _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode(_proposalId));
+    _quorumReachedOutputs[_key] = _quorumReachedOutput(_returnParam0);
+    for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) {
+      if (_key == _quorumReachedInputHashes[_i]) {
+        return;
+      }
+    }
+    _quorumReachedInputHashes.push(_key);
+  }
+
+  function _quorumReached(uint256 _proposalId) internal view override returns (bool _returnParam0) {
+    bytes32 _key = keccak256(abi.encode(_proposalId));
+    for (uint256 _i; _i < _quorumReachedInputHashes.length; ++_i) {
+      if (_key == _quorumReachedInputHashes[_i]) {
+        _quorumReachedOutput memory _output = _quorumReachedOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._quorumReached(_proposalId);
+  }
+
+  struct _voteSucceededOutput {
+    bool _returnParam0;
+  }
+
+  mapping(bytes32 => _voteSucceededOutput) private _voteSucceededOutputs;
+  bytes32[] private _voteSucceededInputHashes;
+
+  function mock_call__voteSucceeded(uint256 _proposalId, bool _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode(_proposalId));
+    _voteSucceededOutputs[_key] = _voteSucceededOutput(_returnParam0);
+    for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) {
+      if (_key == _voteSucceededInputHashes[_i]) {
+        return;
+      }
+    }
+    _voteSucceededInputHashes.push(_key);
+  }
+
+  function _voteSucceeded(uint256 _proposalId) internal view override returns (bool _returnParam0) {
+    bytes32 _key = keccak256(abi.encode(_proposalId));
+    for (uint256 _i; _i < _voteSucceededInputHashes.length; ++_i) {
+      if (_key == _voteSucceededInputHashes[_i]) {
+        _voteSucceededOutput memory _output = _voteSucceededOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._voteSucceeded(_proposalId);
+  }
+}
diff --git a/solidity/test/smock/examples/MockRabbitToken.sol b/solidity/test/smock/examples/MockRabbitToken.sol
new file mode 100644
index 0000000..ded229b
--- /dev/null
+++ b/solidity/test/smock/examples/MockRabbitToken.sol
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {Test} from 'forge-std/Test.sol';
+import {AliceGovernor, EIP712, ERC20, RabbitToken, WonderERC20Votes} from 'solidity/examples/RabbitToken.sol';
+import {WonderERC20Votes} from 'solidity/contracts/token/ERC20/extensions/WonderERC20Votes.sol';
+import {AliceGovernor} from 'solidity/examples/AliceGovernor.sol';
+import {EIP712} from 'node_modules/@openzeppelin/contracts/utils/cryptography/EIP712.sol';
+import {ERC20} from 'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol';
+
+contract MockRabbitToken is RabbitToken, Test {
+  constructor(AliceGovernor _governor) RabbitToken(_governor) {}
+
+  /// Mocked State Variables
+
+  function set_governor(AliceGovernor _governor) public {
+    governor = _governor;
+  }
+
+  function mock_call_governor(AliceGovernor _governor) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('governor()'), abi.encode(_governor));
+  }
+
+  /// Mocked External Functions
+
+  function mock_call_proposalTypes(uint8[] memory _return0) public {
+    vm.mockCall(address(this), abi.encodeWithSignature('proposalTypes()'), abi.encode(_return0));
+  }
+
+  /// Mocked Internal Functions
+
+  struct _getProposalTypesOutput {
+    uint8[] _returnParam0;
+  }
+
+  mapping(bytes32 => _getProposalTypesOutput) private _getProposalTypesOutputs;
+  bytes32[] private _getProposalTypesInputHashes;
+
+  function mock_call__getProposalTypes(uint8[] memory _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode());
+    _getProposalTypesOutputs[_key] = _getProposalTypesOutput(_returnParam0);
+    for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) {
+      if (_key == _getProposalTypesInputHashes[_i]) {
+        return;
+      }
+    }
+    _getProposalTypesInputHashes.push(_key);
+  }
+
+  function _getProposalTypes() internal view override returns (uint8[] memory _returnParam0) {
+    bytes32 _key = keccak256(abi.encode());
+    for (uint256 _i; _i < _getProposalTypesInputHashes.length; ++_i) {
+      if (_key == _getProposalTypesInputHashes[_i]) {
+        _getProposalTypesOutput memory _output = _getProposalTypesOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._getProposalTypes();
+  }
+
+  struct _maxDelegatesOutput {
+    uint8 _returnParam0;
+  }
+
+  mapping(bytes32 => _maxDelegatesOutput) private _maxDelegatesOutputs;
+  bytes32[] private _maxDelegatesInputHashes;
+
+  function mock_call__maxDelegates(uint8 _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode());
+    _maxDelegatesOutputs[_key] = _maxDelegatesOutput(_returnParam0);
+    for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) {
+      if (_key == _maxDelegatesInputHashes[_i]) {
+        return;
+      }
+    }
+    _maxDelegatesInputHashes.push(_key);
+  }
+
+  function _maxDelegates() internal view override returns (uint8 _returnParam0) {
+    bytes32 _key = keccak256(abi.encode());
+    for (uint256 _i; _i < _maxDelegatesInputHashes.length; ++_i) {
+      if (_key == _maxDelegatesInputHashes[_i]) {
+        _maxDelegatesOutput memory _output = _maxDelegatesOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._maxDelegates();
+  }
+
+  struct _validProposalTypeOutput {
+    bool _returnParam0;
+  }
+
+  mapping(bytes32 => _validProposalTypeOutput) private _validProposalTypeOutputs;
+  bytes32[] private _validProposalTypeInputHashes;
+
+  function mock_call__validProposalType(uint8 _proposalType, bool _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode(_proposalType));
+    _validProposalTypeOutputs[_key] = _validProposalTypeOutput(_returnParam0);
+    for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) {
+      if (_key == _validProposalTypeInputHashes[_i]) {
+        return;
+      }
+    }
+    _validProposalTypeInputHashes.push(_key);
+  }
+
+  function _validProposalType(uint8 _proposalType) internal view override returns (bool _returnParam0) {
+    bytes32 _key = keccak256(abi.encode(_proposalType));
+    for (uint256 _i; _i < _validProposalTypeInputHashes.length; ++_i) {
+      if (_key == _validProposalTypeInputHashes[_i]) {
+        _validProposalTypeOutput memory _output = _validProposalTypeOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._validProposalType(_proposalType);
+  }
+
+  struct _weightNormalizerOutput {
+    uint256 _returnParam0;
+  }
+
+  mapping(bytes32 => _weightNormalizerOutput) private _weightNormalizerOutputs;
+  bytes32[] private _weightNormalizerInputHashes;
+
+  function mock_call__weightNormalizer(uint256 _returnParam0) public {
+    bytes32 _key = keccak256(abi.encode());
+    _weightNormalizerOutputs[_key] = _weightNormalizerOutput(_returnParam0);
+    for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) {
+      if (_key == _weightNormalizerInputHashes[_i]) {
+        return;
+      }
+    }
+    _weightNormalizerInputHashes.push(_key);
+  }
+
+  function _weightNormalizer() internal view override returns (uint256 _returnParam0) {
+    bytes32 _key = keccak256(abi.encode());
+    for (uint256 _i; _i < _weightNormalizerInputHashes.length; ++_i) {
+      if (_key == _weightNormalizerInputHashes[_i]) {
+        _weightNormalizerOutput memory _output = _weightNormalizerOutputs[_key];
+        return (_output._returnParam0);
+      }
+    }
+    return super._weightNormalizer();
+  }
+}
diff --git a/solidity/test/unit/WonderGovernor.t.sol b/solidity/test/unit/WonderGovernor.t.sol
new file mode 100644
index 0000000..fa480e7
--- /dev/null
+++ b/solidity/test/unit/WonderGovernor.t.sol
@@ -0,0 +1,771 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import 'forge-std/Test.sol';
+
+import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol';
+import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol';
+import {AliceGovernor} from 'examples/AliceGovernor.sol';
+import {MockRabbitToken} from '../smock/examples/MockRabbitToken.sol';
+import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol';
+import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol';
+import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol';
+
+contract GovernorForTest is AliceGovernor {
+  constructor(address _wonderToken) AliceGovernor(_wonderToken) {}
+
+  function getProposal(uint256 _proposalId) public view returns (ProposalCore memory) {
+    return _getProposal(_proposalId);
+  }
+}
+
+contract BaseTest is Test {
+  address deployer = makeAddr('deployer');
+  address hatter = makeAddr('hatter');
+  address cat = makeAddr('cat');
+
+  IWonderGovernor governor;
+  MockRabbitToken rabbit;
+
+  function _mockGetPastVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal {
+    vm.mockCall(
+      address(rabbit),
+      abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, _account, _proposalType, _timePoint),
+      abi.encode(_votes)
+    );
+  }
+
+  function setUp() public {
+    vm.startPrank(deployer);
+
+    address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1);
+    governor = new GovernorForTest(tokenAddress);
+    rabbit = new MockRabbitToken(AliceGovernor(payable(address(governor))));
+
+    vm.stopPrank();
+  }
+
+  function _expectEmit(address _contract) internal {
+    vm.expectEmit(true, true, true, true, _contract);
+  }
+
+  function _createProposal(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes
+  ) internal returns (uint256) {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    vm.prank(hatter);
+    return governor.propose(_proposalType, _targets, _values, _calldatas, _description);
+  }
+}
+
+contract Unit_Propose is BaseTest {
+  event ProposalCreated(
+    uint256 proposalId,
+    uint8 proposalType,
+    address proposer,
+    address[] targets,
+    uint256[] values,
+    string[] signatures,
+    bytes[] calldatas,
+    uint256 voteStart,
+    uint256 voteEnd,
+    string description
+  );
+
+  function test_Emit_ProposalCreated(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    // hatter will pass the proposal threshold limit
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    uint256 _precomputedProposalId =
+      governor.hashProposal(_proposalType, _targets, _values, _calldatas, keccak256(bytes(_description)));
+    _expectEmit(address(governor));
+
+    emit ProposalCreated(
+      _precomputedProposalId,
+      _proposalType,
+      address(hatter),
+      _targets,
+      _values,
+      new string[](1),
+      _calldatas,
+      block.number + 1,
+      block.number + governor.votingPeriod() + 1,
+      _description
+    );
+
+    vm.prank(hatter);
+    uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, _description);
+  }
+
+  function test_Stores_New_Proposal(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    vm.prank(hatter);
+    uint256 _proposeId = governor.propose(_proposalType, _targets, _values, _calldatas, _description);
+
+    WonderGovernor.ProposalCore memory _proposal = GovernorForTest(payable(address(governor))).getProposal(_proposeId);
+
+    assertEq(_proposal.proposer, hatter);
+    assertEq(_proposal.proposalType, _proposalType);
+    assertEq(_proposal.voteStart, block.number + 1);
+    assertEq(_proposal.voteDuration, governor.votingPeriod());
+    assertEq(_proposal.executed, false);
+    assertEq(_proposal.canceled, false);
+    assertEq(_proposal.etaSeconds, 0);
+  }
+
+  function test_Call_IWonderVotes_GetVotes(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    // hatter will pass the proposal threshold limit
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    vm.expectCall(
+      address(rabbit),
+      abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, hatter, _proposalType, block.number - 1),
+      1
+    );
+
+    vm.prank(hatter);
+    governor.propose(_proposalType, _targets, _values, _calldatas, _description);
+  }
+
+  function test_Revert_GovernorInvalidProposalType(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata
+  ) public {
+    vm.assume(_proposalType >= governor.proposalTypes().length);
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType));
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    vm.prank(hatter);
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderGovernor.GovernorInvalidProposalType.selector, _proposalType));
+    governor.propose(_proposalType, _targets, _values, _calldatas, '');
+  }
+
+  function test_Revert_GovernorInsufficientProposerVotes(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    uint256 _proposerVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    uint256 _votesThreshold = governor.proposalThreshold(_proposalType);
+    vm.assume(_proposerVotes < _votesThreshold);
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+
+    address[] memory _targets = new address[](1);
+    _targets[0] = _target;
+
+    uint256[] memory _values = new uint256[](1);
+    _values[0] = _value;
+
+    bytes[] memory _calldatas = new bytes[](1);
+    _calldatas[0] = _calldata;
+
+    vm.prank(hatter);
+
+    vm.expectRevert(
+      abi.encodeWithSelector(
+        IWonderGovernor.GovernorInsufficientProposerVotes.selector, hatter, _proposerVotes, _votesThreshold
+      )
+    );
+    governor.propose(_proposalType, _targets, _values, _calldatas, '');
+  }
+
+  function test_Revert_GovernorInvalidProposalLength(
+    uint8 _proposalType,
+    address[] memory _targets,
+    uint256[] memory _values,
+    bytes[] memory _calldatas
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_targets.length != _values.length || _targets.length != _calldatas.length || _targets.length == 0);
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, governor.proposalThreshold(_proposalType));
+
+    vm.prank(hatter);
+    vm.expectRevert(
+      abi.encodeWithSelector(
+        IWonderGovernor.GovernorInvalidProposalLength.selector, _targets.length, _calldatas.length, _values.length
+      )
+    );
+
+    governor.propose(_proposalType, _targets, _values, _calldatas, '');
+  }
+}
+
+contract Unit_CastVote is BaseTest {
+  event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
+
+  function test_Emit_VoteCast(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_support < 2);
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    _expectEmit(address(governor));
+    emit VoteCast(cat, _proposalId, _support, _voterVotes, '');
+
+    vm.prank(cat);
+    governor.castVote(_proposalId, _support);
+  }
+
+  function test_Call_GetVotes(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_voterVotes > 0);
+    vm.assume(_support < 2);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.expectCall(
+      address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1
+    );
+
+    vm.prank(cat);
+    governor.castVote(_proposalId, _support);
+  }
+
+  function test_Count_VoteFor(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVote(_proposalId, 1);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, _voterVotes);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAgainst(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVote(_proposalId, 0);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, _voterVotes);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAbstain(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVote(_proposalId, 2);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, _voterVotes);
+  }
+}
+
+contract Unit_CastVoteWithReason is BaseTest {
+  event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
+
+  function test_Emit_VoteCast(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_support < 2);
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    _expectEmit(address(governor));
+    emit VoteCast(cat, _proposalId, _support, _voterVotes, _reason);
+
+    vm.prank(cat);
+    governor.castVoteWithReason(_proposalId, _support, _reason);
+  }
+
+  function test_Call_GetVotes(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_voterVotes > 0);
+    vm.assume(_support < 2);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.expectCall(
+      address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1
+    );
+
+    vm.prank(cat);
+    governor.castVoteWithReason(_proposalId, _support, _reason);
+  }
+
+  function test_Count_VoteFor(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReason(_proposalId, 1, _reason);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, _voterVotes);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAgainst(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReason(_proposalId, 0, _reason);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, _voterVotes);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAbstain(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReason(_proposalId, 2, _reason);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, _voterVotes);
+  }
+}
+
+contract Unit_CastVoteWithReasonAndParams is BaseTest {
+  event VoteCastWithParams(
+    address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params
+  );
+
+  function test_Emit_VoteCastWithParams(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason,
+    bytes memory _params
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_support < 2);
+    vm.assume(_params.length > 0);
+
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, block.number + governor.votingDelay(), _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    _expectEmit(address(governor));
+    emit VoteCastWithParams(cat, _proposalId, _support, _voterVotes, _reason, _params);
+
+    vm.prank(cat);
+    governor.castVoteWithReasonAndParams(_proposalId, _support, _reason, _params);
+  }
+
+  function test_Call_GetVotes(
+    uint8 _proposalType,
+    uint8 _support,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    bytes memory _params
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_voterVotes > 0);
+    vm.assume(_support < 2);
+    vm.assume(_params.length > 0);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.expectCall(
+      address(rabbit), abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, cat, _proposalType, _voteStart), 1
+    );
+
+    vm.prank(cat);
+    governor.castVoteWithReasonAndParams(_proposalId, _support, '', _params);
+  }
+
+  function test_Count_VoteFor(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason,
+    bytes memory _params
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_params.length > 0);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReasonAndParams(_proposalId, 1, _reason, _params);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, _voterVotes);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAgainst(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason,
+    bytes memory _params
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_params.length > 0);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReasonAndParams(_proposalId, 0, _reason, _params);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, _voterVotes);
+    assertEq(_abstainVotes, 0);
+  }
+
+  function test_Count_VoteAbstain(
+    uint8 _proposalType,
+    address _target,
+    uint256 _value,
+    bytes memory _calldata,
+    string memory _description,
+    uint256 _proposerVotes,
+    uint256 _voterVotes,
+    string memory _reason,
+    bytes memory _params
+  ) public {
+    vm.assume(_proposalType < governor.proposalTypes().length);
+    vm.assume(_proposerVotes >= governor.proposalThreshold(_proposalType));
+    vm.assume(_params.length > 0);
+
+    uint256 _voteStart = block.number + governor.votingDelay();
+    _mockGetPastVotes(hatter, _proposalType, block.number - 1, _proposerVotes);
+    _mockGetPastVotes(cat, _proposalType, _voteStart, _voterVotes);
+
+    uint256 _proposalId = _createProposal(_proposalType, _target, _value, _calldata, _description, _proposerVotes);
+
+    vm.roll(block.number + governor.votingDelay() + 1);
+
+    vm.prank(cat);
+    governor.castVoteWithReasonAndParams(_proposalId, 2, _reason, _params);
+
+    (uint256 _id, uint256 _votes, uint256 _forVotes, uint256 _againstVotes, uint256 _abstainVotes) =
+      AliceGovernor(payable(address(governor))).proposalTracks(_proposalId);
+
+    assertEq(_forVotes, 0);
+    assertEq(_againstVotes, 0);
+    assertEq(_abstainVotes, _voterVotes);
+  }
+}
diff --git a/solidity/test/unit/WonderVotes.t.sol b/solidity/test/unit/WonderVotes.t.sol
new file mode 100644
index 0000000..8b7c1c8
--- /dev/null
+++ b/solidity/test/unit/WonderVotes.t.sol
@@ -0,0 +1,1013 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import 'forge-std/Test.sol';
+
+import {WonderGovernor} from 'contracts/governance/WonderGovernor.sol';
+import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol';
+import {IWonderVotes} from 'interfaces/governance/utils/IWonderVotes.sol';
+import {IWonderGovernor} from 'interfaces/governance/IWonderGovernor.sol';
+import {WonderVotes} from 'contracts/governance/utils/WonderVotes.sol';
+import {RabbitToken} from 'examples/RabbitToken.sol';
+import {MockAliceGovernor} from '../smock/examples/MockAliceGovernor.sol';
+import {AliceGovernor} from 'examples/AliceGovernor.sol';
+
+contract WonderVotesForTest is RabbitToken {
+  constructor(AliceGovernor _governor) RabbitToken(_governor) {}
+
+  function mint(address _account, uint256 _amount) public {
+    _mint(_account, _amount);
+  }
+
+  function burn(uint256 _amount) public {
+    _burn(msg.sender, _amount);
+  }
+}
+
+contract BaseTest is Test {
+  address deployer = makeAddr('deployer');
+  address hatter = makeAddr('hatter');
+  address cat = makeAddr('cat');
+
+  MockAliceGovernor governor;
+  RabbitToken rabbitToken;
+
+  event DelegateVotesChanged(address indexed delegate, uint8 proposalType, uint256 previousVotes, uint256 newVotes);
+
+  function _mockGetPastVotes(address _account, uint8 _proposalType, uint256 _timePoint, uint256 _votes) internal {
+    vm.mockCall(
+      address(rabbitToken),
+      abi.encodeWithSelector(IWonderVotes.getPastVotes.selector, _account, _proposalType, _timePoint),
+      abi.encode(_votes)
+    );
+  }
+
+  function setUp() public virtual {
+    vm.startPrank(deployer);
+
+    address tokenAddress = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1);
+    governor = new MockAliceGovernor(tokenAddress);
+    rabbitToken = new WonderVotesForTest(AliceGovernor(payable(address(governor))));
+
+    vm.stopPrank();
+  }
+
+  function _expectEmit(address _contract) internal {
+    vm.expectEmit(true, true, true, true, _contract);
+  }
+}
+
+contract Unit_Delegate_Simple is BaseTest {
+  function test_Minting_WithoutTracking_Add_Zero(uint128 _amount) public {
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+    }
+  }
+
+  function test_Minting_SelfDelegate_Before(uint128 _amount) public {
+    // To start tracking votes the account delegates himself
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), _amount);
+    }
+  }
+
+  function test_Minting_SelfDelegate_After(uint128 _amount) public {
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // If the account does not have delegates it will not track votes
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+    }
+
+    // To start tracking votes the account delegates himself
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), _amount);
+    }
+  }
+
+  function test_SelfDelegate_Changes(uint128 _amount) public {
+    // To start tracking votes the account delegates himself
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), _amount);
+    }
+
+    vm.prank(hatter);
+    rabbitToken.delegate(cat);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(cat, _proposalTypes[i]), _amount);
+    }
+  }
+
+  function test_SelfDelegate_Burns(uint128 _amount) public {
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    vm.prank(hatter);
+    WonderVotesForTest(address(rabbitToken)).burn(_amount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+    }
+  }
+
+  function test_Emit_DelegateVotesChanged(uint128 _amount) public {
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _expectEmit(address(rabbitToken));
+      emit DelegateVotesChanged(hatter, _proposalTypes[i], 0, _amount);
+    }
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+  }
+}
+
+contract Unit_Delegate_Smart is BaseTest {
+  function test_Minting_SmartDelegation_Before(uint128 _amount) public {
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // Will define one delegate per proposal type
+    address[] memory _delegates = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+      rabbitToken.delegate(_delegates[i], _proposalTypes[i]);
+    }
+    vm.stopPrank();
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), _amount);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_delegates[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_Minting_SmartDelegation_After(uint128 _amount) public {
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    // Will define one delegate per proposal type
+    address[] memory _delegates = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+      rabbitToken.delegate(_delegates[i], _proposalTypes[i]);
+    }
+    vm.stopPrank();
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), _amount);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_delegates[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_Minting_SmartDelegation_Changes(uint128 _amount) public {
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // Will define one delegate per proposal type
+    address[] memory _delegates = new address[](_proposalTypes.length);
+    address[] memory _delegatesChange = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+      rabbitToken.delegate(_delegates[i], _proposalTypes[i]);
+    }
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), _amount);
+    }
+
+    // Delegates changes
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegatesChange[i] = makeAddr(string(abi.encodePacked('delegateChange', i)));
+
+      // 100% voting power to the delegate for the proposalType
+      rabbitToken.delegate(_delegatesChange[i], _proposalTypes[i]);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegatesChange[i], _proposalTypes[i]), _amount);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_delegates[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_delegatesChange[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_Revert_InvalidProposalType(uint8 _proposalType) public {
+    vm.assume(_proposalType >= rabbitToken.proposalTypes().length);
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderVotes.InvalidProposalType.selector, _proposalType));
+
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter, _proposalType);
+  }
+
+  function test_Emit_DelegateVotesChanged(uint128 _amount) public {
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    address[] memory _delegates = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+
+      _expectEmit(address(rabbitToken));
+      emit DelegateVotesChanged(_delegates[i], _proposalTypes[i], 0, _amount);
+
+      rabbitToken.delegate(_delegates[i], _proposalTypes[i]);
+    }
+    vm.stopPrank();
+  }
+}
+
+contract Unit_Delegate_SmartAndPartial is BaseTest {
+  function test_Minting_SmartAndPartialDelegation_Before(uint128 _amount) public {
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // To simply we will divide the voting power into 2 delegates 50% each
+    // We can add a more complex test of this further
+    uint256 _weightNormalizer = rabbitToken.weightNormalizer();
+    uint256 _weight = _weightNormalizer / 2;
+
+    address[] memory _delegates = new address[](_proposalTypes.length);
+    address[] memory _delegates2 = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+      _delegates2[i] = makeAddr(string(abi.encodePacked('delegate2', i)));
+
+      IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: _delegates[i], weight: _weight});
+      IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: _delegates2[i], weight: _weight});
+      IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](2);
+      _delegatesStruct[0] = _delegate;
+      _delegatesStruct[1] = _delegate2;
+
+      rabbitToken.delegate(_delegatesStruct, _proposalTypes[i]);
+    }
+
+    vm.stopPrank();
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), _amount / 2);
+      assertEq(rabbitToken.getVotes(_delegates2[i], _proposalTypes[i]), _amount / 2);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_delegates[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_delegates2[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_Minting_SmartAndPartialDelegation_After(uint128 _amount) public {
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // To simply we will divide the voting power into 2 delegates 50% each
+    // We can add a more complex test of this further
+    uint256 _weightNormalizer = rabbitToken.weightNormalizer();
+    uint256 _weight = _weightNormalizer / 2;
+
+    address[] memory _delegates = new address[](_proposalTypes.length);
+    address[] memory _delegates2 = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+      _delegates2[i] = makeAddr(string(abi.encodePacked('delegate2', i)));
+
+      IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: _delegates[i], weight: _weight});
+      IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: _delegates2[i], weight: _weight});
+      IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](2);
+      _delegatesStruct[0] = _delegate;
+      _delegatesStruct[1] = _delegate2;
+
+      rabbitToken.delegate(_delegatesStruct, _proposalTypes[i]);
+    }
+    vm.stopPrank();
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), 0);
+
+      assertEq(rabbitToken.getVotes(_delegates[i], _proposalTypes[i]), _amount / 2);
+      assertEq(rabbitToken.getVotes(_delegates2[i], _proposalTypes[i]), _amount / 2);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_delegates[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_delegates2[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_Revert_InvalidProposalType(uint8 _proposalType) public {
+    vm.assume(_proposalType >= rabbitToken.proposalTypes().length);
+
+    IWonderVotes.Delegate[] memory _delegates = new IWonderVotes.Delegate[](1);
+    _delegates[0] = IWonderVotes.Delegate({account: makeAddr('delegate'), weight: rabbitToken.weightNormalizer()});
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderVotes.InvalidProposalType.selector, _proposalType));
+
+    vm.prank(hatter);
+    rabbitToken.delegate(_delegates, _proposalType);
+  }
+
+  function test_Emit_DelegateVotesChanged(uint128 _amount) public {
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _amount);
+
+    // To simply we will divide the voting power into 2 delegates 50% each
+    // We can add a more complex test of this further
+    uint256 _weightNormalizer = rabbitToken.weightNormalizer();
+    uint256 _weight = _weightNormalizer / 2;
+
+    address[] memory _delegates = new address[](_proposalTypes.length);
+    address[] memory _delegates2 = new address[](_proposalTypes.length);
+
+    vm.startPrank(hatter);
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+      _delegates2[i] = makeAddr(string(abi.encodePacked('delegate2', i)));
+
+      IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: _delegates[i], weight: _weight});
+      IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: _delegates2[i], weight: _weight});
+      IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](2);
+      _delegatesStruct[0] = _delegate;
+      _delegatesStruct[1] = _delegate2;
+
+      _expectEmit(address(rabbitToken));
+
+      emit DelegateVotesChanged(_delegates[i], _proposalTypes[i], 0, _amount / 2);
+      emit DelegateVotesChanged(_delegates2[i], _proposalTypes[i], 0, _amount / 2);
+
+      rabbitToken.delegate(_delegatesStruct, _proposalTypes[i]);
+    }
+
+    vm.stopPrank();
+  }
+
+  function test_Revert_ZeroWeight(uint8 _proposalType) public {
+    vm.assume(_proposalType < rabbitToken.proposalTypes().length);
+
+    IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](1);
+    _delegatesStruct[0] = IWonderVotes.Delegate({account: makeAddr('delegate'), weight: 0});
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderVotes.ZeroWeight.selector));
+
+    vm.prank(hatter);
+    rabbitToken.delegate(_delegatesStruct, _proposalType);
+  }
+
+  function test_Revert_InvalidWeightSum_LessThan_WeighNormalizer(uint8 _proposalType, uint256 _weightSum) public {
+    vm.assume(_proposalType < rabbitToken.proposalTypes().length);
+    vm.assume(_weightSum > 0 && (_weightSum > rabbitToken.weightNormalizer()));
+
+    IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](1);
+    _delegatesStruct[0] = IWonderVotes.Delegate({account: makeAddr('delegate'), weight: _weightSum});
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderVotes.InvalidWeightSum.selector, _weightSum));
+
+    vm.prank(hatter);
+    rabbitToken.delegate(_delegatesStruct, _proposalType);
+  }
+
+  function test_Revert_InvalidWeightSum_MoreThan_WeighNormalizer(uint8 _proposalType, uint256 _weightSum) public {
+    vm.assume(_proposalType < rabbitToken.proposalTypes().length);
+    vm.assume(_weightSum > 0 && (_weightSum > rabbitToken.weightNormalizer()));
+
+    IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](1);
+    _delegatesStruct[0] = IWonderVotes.Delegate({account: makeAddr('delegate'), weight: _weightSum});
+
+    vm.expectRevert(abi.encodeWithSelector(IWonderVotes.InvalidWeightSum.selector, _weightSum));
+
+    vm.prank(hatter);
+    rabbitToken.delegate(_delegatesStruct, _proposalType);
+  }
+}
+
+contract Unit_TransferVotes is BaseTest {
+  function setUp() public override {
+    super.setUp();
+
+    // To start tracking votes the accounts delegates themselves
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+    vm.prank(cat);
+    rabbitToken.delegate(cat);
+  }
+
+  function test_TransferVotes_SimpleDelegation(uint128 _balance, uint128 _transferAmount) public {
+    vm.assume(_balance >= _transferAmount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _balance);
+
+    vm.prank(hatter);
+    rabbitToken.transfer(cat, _transferAmount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(hatter, _proposalTypes[i]), _balance - _transferAmount);
+      assertEq(rabbitToken.getVotes(cat, _proposalTypes[i]), _transferAmount);
+    }
+  }
+
+  function test_TransferVotes_SimpleDelegation_Emit_DelegateVotesChanged(
+    uint128 _balance,
+    uint128 _transferAmount
+  ) public {
+    vm.assume(_balance >= _transferAmount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _balance);
+
+    _expectEmit(address(rabbitToken));
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      emit DelegateVotesChanged(hatter, _proposalTypes[i], _balance, _balance - _transferAmount);
+      emit DelegateVotesChanged(cat, _proposalTypes[i], 0, _transferAmount);
+    }
+
+    vm.prank(hatter);
+    rabbitToken.transfer(cat, _transferAmount);
+  }
+
+  function test_TransferVotes_SmartDelegation(uint128 _balance, uint128 _transferAmount) public {
+    vm.assume(_balance >= _transferAmount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // Will define one delegate per proposal type
+    address[] memory _hatterDelegates = new address[](_proposalTypes.length);
+    address[] memory _catDelegates = new address[](_proposalTypes.length);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _hatterDelegates[i] = makeAddr(string(abi.encodePacked('hatterDelegate', i)));
+      _catDelegates[i] = makeAddr(string(abi.encodePacked('catDelegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+
+      vm.prank(hatter);
+      rabbitToken.delegate(_hatterDelegates[i], _proposalTypes[i]);
+
+      vm.prank(cat);
+      rabbitToken.delegate(_catDelegates[i], _proposalTypes[i]);
+    }
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _balance);
+
+    vm.prank(hatter);
+    rabbitToken.transfer(cat, _transferAmount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getVotes(_hatterDelegates[i], _proposalTypes[i]), _balance - _transferAmount);
+      assertEq(rabbitToken.getVotes(_catDelegates[i], _proposalTypes[i]), _transferAmount);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _catDelegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_hatterDelegates[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_catDelegates[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+
+  function test_TransferVotes_SmartDelegation_Emits_DelegateVotesChanged(
+    uint128 _balance,
+    uint128 _transferAmount
+  ) public {
+    vm.assume(_balance >= _transferAmount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    // Will define one delegate per proposal type
+    address[] memory _hatterDelegates = new address[](_proposalTypes.length);
+    address[] memory _catDelegates = new address[](_proposalTypes.length);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _hatterDelegates[i] = makeAddr(string(abi.encodePacked('hatterDelegate', i)));
+      _catDelegates[i] = makeAddr(string(abi.encodePacked('catDelegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+
+      vm.prank(hatter);
+      rabbitToken.delegate(_hatterDelegates[i], _proposalTypes[i]);
+
+      vm.prank(cat);
+      rabbitToken.delegate(_catDelegates[i], _proposalTypes[i]);
+    }
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _balance);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      emit DelegateVotesChanged(_hatterDelegates[i], _proposalTypes[i], _balance, _balance - _transferAmount);
+      emit DelegateVotesChanged(_catDelegates[i], _proposalTypes[i], 0, _transferAmount);
+    }
+
+    vm.prank(hatter);
+    rabbitToken.transfer(cat, _transferAmount);
+  }
+
+  function _partialDelegate(
+    string memory _nameHash,
+    address _account,
+    uint8[] memory _proposalTypes
+  ) internal returns (address[] memory, address[] memory) {
+    // To simply we will divide the voting power into 2 delegates 50% each
+    uint256 _weightNormalizer = rabbitToken.weightNormalizer();
+    uint256 _weight = _weightNormalizer / 2;
+
+    address[] memory _delegates = new address[](_proposalTypes.length);
+    address[] memory _delegates2 = new address[](_proposalTypes.length);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      // smart partial delegation
+      _delegates[i] = makeAddr(string(abi.encodePacked('1', _nameHash, i)));
+      _delegates2[i] = makeAddr(string(abi.encodePacked('2', _nameHash, i)));
+
+      IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: _delegates[i], weight: _weight});
+      IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: _delegates2[i], weight: _weight});
+      IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](2);
+      _delegatesStruct[0] = _delegate;
+      _delegatesStruct[1] = _delegate2;
+
+      vm.prank(_account);
+      rabbitToken.delegate(_delegatesStruct, _proposalTypes[i]);
+    }
+
+    return (_delegates, _delegates2);
+  }
+
+  function test_TransferVotes_SmartAndPartialDelegation(uint128 _balance, uint128 _transferAmount) public {
+    vm.assume(_balance >= _transferAmount);
+    uint8[] memory _proposalTypes = rabbitToken.proposalTypes();
+
+    (address[] memory _hatterDelegates1, address[] memory _hatterDelegates2) =
+      _partialDelegate('hatter', hatter, _proposalTypes);
+    (address[] memory _catDelegates1, address[] memory _catDelegates2) = _partialDelegate('cat', cat, _proposalTypes);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _balance);
+
+    vm.prank(hatter);
+    rabbitToken.transfer(cat, _transferAmount);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertApproxEqAbs(
+        rabbitToken.getVotes(_hatterDelegates1[i], _proposalTypes[i]), (_balance - _transferAmount) / 2, 1, ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getVotes(_hatterDelegates2[i], _proposalTypes[i]), (_balance - _transferAmount) / 2, 1, ''
+      );
+
+      assertApproxEqAbs(rabbitToken.getVotes(_catDelegates1[i], _proposalTypes[i]), _transferAmount / 2, 1, '');
+      assertApproxEqAbs(rabbitToken.getVotes(_catDelegates2[i], _proposalTypes[i]), _transferAmount / 2, 1, '');
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _catDelegates1.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getVotes(_hatterDelegates1[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_hatterDelegates2[j], _proposalTypes[i]), 0);
+
+          assertEq(rabbitToken.getVotes(_catDelegates1[j], _proposalTypes[i]), 0);
+          assertEq(rabbitToken.getVotes(_catDelegates2[j], _proposalTypes[i]), 0);
+        }
+      }
+    }
+  }
+}
+
+contract Unit_GetPastVotes is BaseTest {
+  uint8[] internal _proposalTypes;
+
+  // 2 delegates 50% each
+  uint256 internal _weightNormalizer;
+  uint256 internal _weight;
+
+  address[] internal _delegates;
+  address[] internal _delegates2;
+
+  function setUp() public override {
+    super.setUp();
+
+    // To track votes, if not it will be always 0
+    vm.prank(hatter);
+    rabbitToken.delegate(hatter);
+    vm.prank(cat);
+    rabbitToken.delegate(cat);
+
+    _proposalTypes = rabbitToken.proposalTypes();
+
+    // 2 delegates 50% each
+    _weightNormalizer = rabbitToken.weightNormalizer();
+    _weight = _weightNormalizer / 2;
+
+    _delegates = new address[](_proposalTypes.length);
+    _delegates2 = new address[](_proposalTypes.length);
+  }
+
+  // Simple delegation
+  function test_GetPastVotes_After_Mint(uint128 _previousBalance, uint128 _addBalance) public {
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(
+        rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), uint256(_previousBalance) + _addBalance
+      );
+    }
+  }
+
+  function test_GetPastVotes_After_Burn(uint128 _previousBalance, uint128 _subsBalance) public {
+    vm.assume(_previousBalance >= _subsBalance);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    vm.prank(hatter);
+    WonderVotesForTest(address(rabbitToken)).burn(_subsBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), _previousBalance - _subsBalance);
+    }
+  }
+
+  function test_GetPastVotes_After_Transfer(uint128 _previousBalance, uint128 _addBalance) public {
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    WonderVotesForTest(address(rabbitToken)).mint(cat, _addBalance);
+
+    vm.roll(block.number + 1);
+
+    vm.prank(cat);
+    rabbitToken.transfer(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(
+        rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), uint256(_previousBalance) + _addBalance
+      );
+
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance);
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0);
+    }
+  }
+
+  function _smartDelegate() internal {
+    vm.startPrank(hatter);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+
+      // 100% voting power to the delegate for the proposalType
+      rabbitToken.delegate(_delegates[i], _proposalTypes[i]);
+    }
+    vm.stopPrank();
+  }
+
+  // Smart Delegation
+  function test_GetPastVotes_After_Mint_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public {
+    _smartDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+
+      assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1),
+        uint256(_previousBalance) + _addBalance
+      );
+    }
+  }
+
+  function test_GetPastVotes_After_Burn_SmartDelegation(uint128 _previousBalance, uint128 _subsBalance) public {
+    vm.assume(_previousBalance >= _subsBalance);
+
+    _smartDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    vm.prank(hatter);
+    WonderVotesForTest(address(rabbitToken)).burn(_subsBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0);
+
+      assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1), _previousBalance - _subsBalance
+      );
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0);
+        }
+      }
+    }
+  }
+
+  function test_GetPastVotes_After_Transfer_SmartDelegation(uint128 _previousBalance, uint128 _addBalance) public {
+    _smartDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    WonderVotesForTest(address(rabbitToken)).mint(cat, _addBalance);
+
+    vm.roll(block.number + 1);
+
+    vm.prank(cat);
+    rabbitToken.transfer(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0);
+
+      assertEq(rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance);
+      assertEq(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1),
+        uint256(_previousBalance) + _addBalance
+      );
+
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance);
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0);
+        }
+      }
+    }
+  }
+
+  function _smartAndPartialDelegate() internal {
+    vm.startPrank(hatter);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      _delegates[i] = makeAddr(string(abi.encodePacked('delegate', i)));
+      _delegates2[i] = makeAddr(string(abi.encodePacked('delegate2', i)));
+
+      IWonderVotes.Delegate memory _delegate = IWonderVotes.Delegate({account: _delegates[i], weight: _weight});
+      IWonderVotes.Delegate memory _delegate2 = IWonderVotes.Delegate({account: _delegates2[i], weight: _weight});
+      IWonderVotes.Delegate[] memory _delegatesStruct = new IWonderVotes.Delegate[](2);
+      _delegatesStruct[0] = _delegate;
+      _delegatesStruct[1] = _delegate2;
+
+      rabbitToken.delegate(_delegatesStruct, _proposalTypes[i]);
+    }
+    vm.stopPrank();
+  }
+
+  // Smart and partial delegation
+  function test_GetPastVotes_After_Mint_SmartAndPartialDelegation(uint128 _previousBalance, uint128 _addBalance) public {
+    _smartAndPartialDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0);
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) + _addBalance) / 2,
+        1,
+        ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) + _addBalance) / 2,
+        1,
+        ''
+      );
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0);
+
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0);
+        }
+      }
+    }
+  }
+
+  function test_GetPastVotes_After_Burn_SmartAndPartialDelegation(
+    uint128 _previousBalance,
+    uint128 _subsBalance
+  ) public {
+    vm.assume(_previousBalance >= _subsBalance);
+
+    _smartAndPartialDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    vm.roll(block.number + 1);
+
+    vm.prank(hatter);
+    WonderVotesForTest(address(rabbitToken)).burn(_subsBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0);
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) - _subsBalance) / 2,
+        1,
+        ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) - _subsBalance) / 2,
+        1,
+        ''
+      );
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0);
+
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0);
+        }
+      }
+    }
+  }
+
+  function test_GetPastVotes_After_Transfer_SmartAndPartialDelegation(
+    uint128 _previousBalance,
+    uint128 _addBalance
+  ) public {
+    _smartAndPartialDelegate();
+
+    WonderVotesForTest(address(rabbitToken)).mint(hatter, _previousBalance);
+    WonderVotesForTest(address(rabbitToken)).mint(cat, _addBalance);
+
+    vm.roll(block.number + 1);
+
+    vm.prank(cat);
+    rabbitToken.transfer(hatter, _addBalance);
+    vm.roll(block.number + 1);
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 2), 0);
+      assertEq(rabbitToken.getPastVotes(hatter, _proposalTypes[i], block.number - 1), 0);
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 2), _previousBalance / 2, 1, ''
+      );
+
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) + _addBalance) / 2,
+        1,
+        ''
+      );
+      assertApproxEqAbs(
+        rabbitToken.getPastVotes(_delegates2[i], _proposalTypes[i], block.number - 1),
+        (uint256(_previousBalance) + _addBalance) / 2,
+        1,
+        ''
+      );
+
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 2), _addBalance);
+      assertEq(rabbitToken.getPastVotes(cat, _proposalTypes[i], block.number - 1), 0);
+    }
+
+    for (uint256 i = 0; i < _proposalTypes.length; i++) {
+      for (uint256 j = 0; j < _delegates.length; j++) {
+        if (j != i) {
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates[j], _proposalTypes[i], block.number - 1), 0);
+
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 2), 0);
+          assertEq(rabbitToken.getPastVotes(_delegates2[j], _proposalTypes[i], block.number - 1), 0);
+        }
+      }
+    }
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index a80fc74..6d6371a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -192,6 +192,14 @@
   dependencies:
     "@jridgewell/trace-mapping" "0.3.9"
 
+"@defi-wonderland/smock-foundry@1.0.6":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@defi-wonderland/smock-foundry/-/smock-foundry-1.0.6.tgz#25cef951d3ab4d038581a64d9d7cb55538bee53e"
+  integrity sha512-TEvPrkTBao1bMUnmIasBPdDQ46AC/pUFVrcvtvJ9Mc3krZkeZWGP+UDW786B9lGoGWkXFTWoiR6fZuoekczM1w==
+  dependencies:
+    handlebars "4.7.7"
+    yargs "17.7.2"
+
 "@jridgewell/resolve-uri@^3.0.3":
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
@@ -231,7 +239,7 @@
     "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
-"@openzeppelin/contracts@^5.0.1":
+"@openzeppelin/contracts@5.0.1":
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890"
   integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w==
@@ -1192,6 +1200,18 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
 
+handlebars@4.7.7:
+  version "4.7.7"
+  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
+  integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
+  dependencies:
+    minimist "^1.2.5"
+    neo-async "^2.6.0"
+    source-map "^0.6.1"
+    wordwrap "^1.0.0"
+  optionalDependencies:
+    uglify-js "^3.1.4"
+
 hard-rejection@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
@@ -1701,7 +1721,7 @@ minimist-options@4.1.0:
     is-plain-obj "^1.1.0"
     kind-of "^6.0.3"
 
-minimist@^1.2.6:
+minimist@^1.2.5, minimist@^1.2.6:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -1728,6 +1748,11 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
 
+neo-async@^2.6.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+  integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@@ -2262,6 +2287,11 @@ sort-package-json@1.53.1:
     is-plain-obj "2.1.0"
     sort-object-keys "^1.1.3"
 
+source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
 spdx-correct@^3.0.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
@@ -2534,6 +2564,11 @@ type-fest@^1.0.2:
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43"
   integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==
 
+uglify-js@^3.1.4:
+  version "3.17.4"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"
+  integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==
+
 undici-types@~5.26.4:
   version "5.26.5"
   resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
@@ -2588,6 +2623,11 @@ word-wrap@~1.2.3:
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
   integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
 
+wordwrap@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+  integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
+
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -2643,7 +2683,7 @@ yargs-parser@^21.1.1:
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
   integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
 
-yargs@^17.0.0:
+yargs@17.7.2, yargs@^17.0.0:
   version "17.7.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
   integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==