Repository: pcaversaccio/snekmate (language: python)

Issue:
💥 Multiproof Verification Merkle Tree
See here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol#L83. 


Discussion / hints:

Fix patch:
diff --git a/.gas-snapshot b/.gas-snapshot
new file mode 100644
index 00000000..be330023
--- /dev/null
+++ b/.gas-snapshot
@@ -0,0 +1,256 @@
+AccessControlTest:testGrantRoleAdminRoleSuccess() (gas: 43721)
+AccessControlTest:testGrantRoleMultipleTimesSuccess() (gas: 49245)
+AccessControlTest:testGrantRoleNonAdmin() (gas: 13732)
+AccessControlTest:testGrantRoleSuccess() (gas: 43852)
+AccessControlTest:testInitialSetup() (gas: 25522)
+AccessControlTest:testRenounceRoleAdminRoleSuccess() (gas: 20458)
+AccessControlTest:testRenounceRoleMultipleTimesSuccess() (gas: 45148)
+AccessControlTest:testRenounceRoleNonMsgSender() (gas: 9383)
+AccessControlTest:testRenounceRoleSuccess() (gas: 41411)
+AccessControlTest:testRevokeRoleAdminRoleSuccess() (gas: 24648)
+AccessControlTest:testRevokeRoleMultipleTimesSuccess() (gas: 41185)
+AccessControlTest:testRevokeRoleNonAdmin() (gas: 13788)
+AccessControlTest:testRevokeRoleSuccess() (gas: 39976)
+AccessControlTest:testSetRoleAdminPreviousAdminCallsGrantRole() (gas: 75958)
+AccessControlTest:testSetRoleAdminPreviousAdminCallsRevokeRole() (gas: 101559)
+AccessControlTest:testSetRoleAdminSuccess() (gas: 90393)
+AccessControlTest:testSupportsInterfaceGasCost() (gas: 5636)
+AccessControlTest:testSupportsInterfaceInvalidInterfaceId() (gas: 5502)
+AccessControlTest:testSupportsInterfaceSuccess() (gas: 6232)
+OwnableTest:testHasOwner() (gas: 9682)
+OwnableTest:testInitialSetup() (gas: 9697)
+OwnableTest:testRenounceOwnershipNonOwner() (gas: 10715)
+OwnableTest:testRenounceOwnershipSuccess() (gas: 15508)
+OwnableTest:testTransferOwnershipNonOwner() (gas: 11255)
+OwnableTest:testTransferOwnershipSuccess() (gas: 20757)
+OwnableTest:testTransferOwnershipToZeroAddress() (gas: 13338)
+Ownable2StepTest:testAcceptOwnershipNonPendingOwner() (gas: 43408)
+Ownable2StepTest:testAcceptOwnershipSuccess() (gas: 36812)
+Ownable2StepTest:testHasOwner() (gas: 9683)
+Ownable2StepTest:testInitialSetup() (gas: 12654)
+Ownable2StepTest:testPendingOwnerResetAfterRenounceOwnership() (gas: 36363)
+Ownable2StepTest:testRenounceOwnershipNonOwner() (gas: 10949)
+Ownable2StepTest:testRenounceOwnershipSuccess() (gas: 17815)
+Ownable2StepTest:testTransferOwnershipNonOwner() (gas: 11321)
+Ownable2StepTest:testTransferOwnershipSuccess() (gas: 41735)
+ERC20Test:testApproveExceedingBalanceCase1() (gas: 39384)
+ERC20Test:testApproveExceedingBalanceCase2() (gas: 46096)
+ERC20Test:testApproveFromZeroAddress() (gas: 9833)
+ERC20Test:testApproveSuccessCase1() (gas: 42487)
+ERC20Test:testApproveSuccessCase2() (gas: 49215)
+ERC20Test:testApproveToZeroAddress() (gas: 14688)
+ERC20Test:testBalanceOf() (gas: 17696)
+ERC20Test:testBurnFromExceedingBalance() (gas: 30528)
+ERC20Test:testBurnFromFromZeroAddress() (gas: 12301)
+ERC20Test:testBurnFromInsufficientAllowanceCase1() (gas: 40846)
+ERC20Test:testBurnFromInsufficientAllowanceCase2() (gas: 40922)
+ERC20Test:testBurnFromSuccessCase1() (gas: 38239)
+ERC20Test:testBurnFromSuccessCase2() (gas: 63862)
+ERC20Test:testBurnFromUnlimitedAllowance() (gas: 48205)
+ERC20Test:testBurnFromZeroAddress() (gas: 9354)
+ERC20Test:testBurnInvalidAmount() (gas: 15013)
+ERC20Test:testBurnSuccessCase1() (gas: 24561)
+ERC20Test:testBurnSuccessCase2() (gas: 30170)
+ERC20Test:testCachedDomainSeparator() (gas: 7862)
+ERC20Test:testDecreaseAllowanceExceedingBalanceCase1() (gas: 46516)
+ERC20Test:testDecreaseAllowanceExceedingBalanceCase2() (gas: 33054)
+ERC20Test:testDecreaseAllowanceFromZeroAddress() (gas: 12126)
+ERC20Test:testDecreaseAllowanceInvalidAmountCase1() (gas: 14212)
+ERC20Test:testDecreaseAllowanceInvalidAmountCase2() (gas: 45853)
+ERC20Test:testDecreaseAllowanceSuccessCase1() (gas: 49717)
+ERC20Test:testDecreaseAllowanceSuccessCase2() (gas: 35519)
+ERC20Test:testDecreaseAllowanceToZeroAddress() (gas: 17002)
+ERC20Test:testDomainSeparator() (gas: 9706)
+ERC20Test:testHasOwner() (gas: 10096)
+ERC20Test:testIncreaseAllowanceExceedingBalanceCase1() (gas: 39590)
+ERC20Test:testIncreaseAllowanceExceedingBalanceCase2() (gas: 46496)
+ERC20Test:testIncreaseAllowanceFromZeroAddress() (gas: 12137)
+ERC20Test:testIncreaseAllowanceSuccessCase1() (gas: 42759)
+ERC20Test:testIncreaseAllowanceSuccessCase2() (gas: 49657)
+ERC20Test:testIncreaseAllowanceToZeroAddress() (gas: 17081)
+ERC20Test:testInitialSetup() (gas: 27031)
+ERC20Test:testMintNonMinter() (gas: 11611)
+ERC20Test:testMintOverflow() (gas: 15915)
+ERC20Test:testMintSuccess() (gas: 49076)
+ERC20Test:testMintToZeroAddress() (gas: 13709)
+ERC20Test:testPermitBadChainId() (gas: 40065)
+ERC20Test:testPermitBadNonce() (gas: 39029)
+ERC20Test:testPermitExpiredDeadline() (gas: 15885)
+ERC20Test:testPermitOtherSignature() (gas: 40311)
+ERC20Test:testPermitReplaySignature() (gas: 72425)
+ERC20Test:testPermitSuccess() (gas: 68616)
+ERC20Test:testRenounceOwnershipNonOwner() (gas: 11125)
+ERC20Test:testRenounceOwnershipSuccess() (gas: 17881)
+ERC20Test:testSetMinterNonOwner() (gas: 11536)
+ERC20Test:testSetMinterRemoveOwnerAddress() (gas: 13951)
+ERC20Test:testSetMinterSuccess() (gas: 33014)
+ERC20Test:testSetMinterToZeroAddress() (gas: 13762)
+ERC20Test:testTotalSupply() (gas: 9296)
+ERC20Test:testTransferFromExceedingBalance() (gas: 30884)
+ERC20Test:testTransferFromFromZeroAddress() (gas: 14405)
+ERC20Test:testTransferFromInsufficientAllowanceCase1() (gas: 41278)
+ERC20Test:testTransferFromInsufficientAllowanceCase2() (gas: 41330)
+ERC20Test:testTransferFromSuccess() (gas: 60106)
+ERC20Test:testTransferFromToZeroAddress() (gas: 30332)
+ERC20Test:testTransferFromUnlimitedAllowance() (gas: 69632)
+ERC20Test:testTransferFromZeroAddress() (gas: 16928)
+ERC20Test:testTransferInvalidAmount() (gas: 14201)
+ERC20Test:testTransferOwnershipNonOwner() (gas: 11642)
+ERC20Test:testTransferOwnershipSuccess() (gas: 21523)
+ERC20Test:testTransferOwnershipToZeroAddress() (gas: 13705)
+ERC20Test:testTransferSuccess() (gas: 42042)
+ERC20Test:testTransferToZeroAddress() (gas: 14721)
+ERC20Test:testTransferZeroTokens() (gas: 24114)
+ERC721Test:testApproveClearingApprovalWithNoPriorApproval() (gas: 174628)
+ERC721Test:testApproveClearingApprovalWithPriorApproval() (gas: 182903)
+ERC721Test:testApproveFromApprovedAddress() (gas: 197003)
+ERC721Test:testApproveFromNonOwner() (gas: 169990)
+ERC721Test:testApproveFromOperatorAddress() (gas: 221443)
+ERC721Test:testApproveInvalidTokenId() (gas: 169187)
+ERC721Test:testApproveToOwner() (gas: 166869)
+ERC721Test:testApproveToZeroAddress() (gas: 182969)
+ERC721Test:testApproveWithNoPriorApproval() (gas: 194918)
+ERC721Test:testApproveWithPriorApprovalToDifferentAddress() (gas: 203234)
+ERC721Test:testApproveWithPriorApprovalToSameAddress() (gas: 202886)
+ERC721Test:testBalanceOfCase1() (gas: 327261)
+ERC721Test:testBalanceOfCase2() (gas: 10668)
+ERC721Test:testBalanceOfZeroAddress() (gas: 8936)
+ERC721Test:testBurnSuccess() (gas: 304702)
+ERC721Test:testBurnSuccessViaApproveAndSetApprovalForAll() (gas: 370086)
+ERC721Test:testCachedDomainSeparator() (gas: 8044)
+ERC721Test:testDomainSeparator() (gas: 9822)
+ERC721Test:testGetApprovedApprovedTokenId() (gas: 192477)
+ERC721Test:testGetApprovedInvalidTokenId() (gas: 11145)
+ERC721Test:testGetApprovedNotApprovedTokenId() (gas: 167125)
+ERC721Test:testHasOwner() (gas: 10166)
+ERC721Test:testInitialSetup() (gas: 18473)
+ERC721Test:testOwnerOf() (gas: 164838)
+ERC721Test:testOwnerOfInvalidTokenId() (gas: 11077)
+ERC721Test:testPermitBadChainId() (gas: 196802)
+ERC721Test:testPermitBadNonce() (gas: 195863)
+ERC721Test:testPermitExpiredDeadline() (gas: 168830)
+ERC721Test:testPermitOtherSignature() (gas: 197186)
+ERC721Test:testPermitReplaySignature() (gas: 230653)
+ERC721Test:testPermitSuccess() (gas: 226196)
+ERC721Test:testRenounceOwnershipNonOwner() (gas: 11311)
+ERC721Test:testRenounceOwnershipSuccess() (gas: 18070)
+ERC721Test:testSafeMintNonMinter() (gas: 12264)
+ERC721Test:testSafeMintOverflow() (gas: 12584)
+ERC721Test:testSafeMintReceiverContract() (gas: 392847)
+ERC721Test:testSafeMintReceiverContractFunctionNotImplemented() (gas: 116900)
+ERC721Test:testSafeMintReceiverContractInvalidReturnIdentifier() (gas: 345831)
+ERC721Test:testSafeMintReceiverContractRevertsWithMessage() (gas: 343097)
+ERC721Test:testSafeMintReceiverContractRevertsWithPanic() (gas: 343344)
+ERC721Test:testSafeMintReceiverContractRevertsWithoutMessage() (gas: 342725)
+ERC721Test:testSafeMintSuccess() (gas: 515414)
+ERC721Test:testSafeMintToZeroAddress() (gas: 36709)
+ERC721Test:testSafeMintTokenAlreadyMinted() (gas: 354883)
+ERC721Test:testSafeTransferFromNoData() (gas: 1153794)
+ERC721Test:testSafeTransferFromReceiverFunctionNotImplemented() (gas: 181607)
+ERC721Test:testSafeTransferFromReceiverInvalidReturnIdentifier() (gas: 410629)
+ERC721Test:testSafeTransferFromReceiverRevertsWithMessage() (gas: 407878)
+ERC721Test:testSafeTransferFromReceiverRevertsWithPanic() (gas: 408148)
+ERC721Test:testSafeTransferFromReceiverRevertsWithoutMessage() (gas: 407528)
+ERC721Test:testSafeTransferFromWithData() (gas: 1163104)
+ERC721Test:testSetApprovalForAllOperatorIsOwner() (gas: 166396)
+ERC721Test:testSetApprovalForAllSuccessCase1() (gas: 193835)
+ERC721Test:testSetApprovalForAllSuccessCase2() (gas: 187606)
+ERC721Test:testSetApprovalForAllSuccessCase3() (gas: 200667)
+ERC721Test:testSetMinterNonOwner() (gas: 11759)
+ERC721Test:testSetMinterRemoveOwnerAddress() (gas: 14130)
+ERC721Test:testSetMinterSuccess() (gas: 33472)
+ERC721Test:testSetMinterToZeroAddress() (gas: 13917)
+ERC721Test:testSupportsInterfaceGasCost() (gas: 5754)
+ERC721Test:testSupportsInterfaceInvalidInterfaceId() (gas: 5641)
+ERC721Test:testSupportsInterfaceSuccess() (gas: 9386)
+ERC721Test:testTokenByIndex() (gas: 558031)
+ERC721Test:testTokenByIndexOutOfBounds() (gas: 329731)
+ERC721Test:testTokenOfOwnerByIndex() (gas: 577757)
+ERC721Test:testTokenOfOwnerByIndexReverts() (gas: 550248)
+ERC721Test:testTokenURIAfterBurning() (gas: 138060)
+ERC721Test:testTokenURIDefault() (gas: 167876)
+ERC721Test:testTokenURIInvalidTokenId() (gas: 11585)
+ERC721Test:testTokenURINoBaseURI() (gas: 2522998)
+ERC721Test:testTokenURINoTokenUri() (gas: 124927)
+ERC721Test:testTotalSupply() (gas: 327358)
+ERC721Test:testTransferFrom() (gas: 554624)
+ERC721Test:testTransferOwnershipNonOwner() (gas: 11785)
+ERC721Test:testTransferOwnershipSuccess() (gas: 21752)
+ERC721Test:testTransferOwnershipToZeroAddress() (gas: 13891)
+BatchDistributorTest:testDistributeEtherMultipleAddressesSuccess() (gas: 145962)
+BatchDistributorTest:testDistributeEtherOneAddressSuccess() (gas: 74891)
+BatchDistributorTest:testDistributeEtherRevertWithInsufficientFunds() (gas: 76796)
+BatchDistributorTest:testDistributeEtherRevertWithNoFallbackFunctionForMsgSender() (gas: 152636)
+BatchDistributorTest:testDistributeEtherRevertWithNoFallbackFunctionForReceipt() (gas: 49020)
+BatchDistributorTest:testDistributeEtherSendsBackExcessiveEther() (gas: 179540)
+BatchDistributorTest:testDistributeTokenMultipleAddressesSuccess() (gas: 758684)
+BatchDistributorTest:testDistributeTokenOneAddressSuccess() (gas: 723705)
+BatchDistributorTest:testDistributeTokenRevertWithInsufficientAllowance() (gas: 713101)
+BatchDistributorTest:testDistributeTokenRevertWithInsufficientBalance() (gas: 715788)
+Create2AddressTest:testComputeAddress() (gas: 701549)
+Create2AddressTest:testComputeAddressSelf() (gas: 35725)
+CreateAddressTest:testComputeAddressNonce0x00() (gas: 14758)
+CreateAddressTest:testComputeAddressNonce0x7f() (gas: 680636)
+CreateAddressTest:testComputeAddressNonceUint16() (gas: 680733)
+CreateAddressTest:testComputeAddressNonceUint24() (gas: 680806)
+CreateAddressTest:testComputeAddressNonceUint32() (gas: 680811)
+CreateAddressTest:testComputeAddressNonceUint40() (gas: 680795)
+CreateAddressTest:testComputeAddressNonceUint48() (gas: 680779)
+CreateAddressTest:testComputeAddressNonceUint56() (gas: 680896)
+CreateAddressTest:testComputeAddressNonceUint64() (gas: 680921)
+CreateAddressTest:testComputeAddressNonceUint8() (gas: 680684)
+CreateAddressTest:testComputeAddressRevertTooHighNonce() (gas: 9346)
+CreateAddressTest:testComputeAddressSelfNonce0x7f() (gas: 12910)
+CreateAddressTest:testComputeAddressSelfNonceUint16() (gas: 13087)
+CreateAddressTest:testComputeAddressSelfNonceUint24() (gas: 13116)
+CreateAddressTest:testComputeAddressSelfNonceUint32() (gas: 13145)
+CreateAddressTest:testComputeAddressSelfNonceUint40() (gas: 13196)
+CreateAddressTest:testComputeAddressSelfNonceUint48() (gas: 13313)
+CreateAddressTest:testComputeAddressSelfNonceUint56() (gas: 13217)
+CreateAddressTest:testComputeAddressSelfNonceUint64() (gas: 13353)
+CreateAddressTest:testComputeAddressSelfNonceUint8() (gas: 12954)
+CreateAddressTest:testComputeAddressSelfRevertTooHighNonce() (gas: 9168)
+ECDSATest:testEthSignedMessageHash() (gas: 5802)
+ECDSATest:testRecoverWith0x00Value() (gas: 13956)
+ECDSATest:testRecoverWithArbitraryMessage() (gas: 19318)
+ECDSATest:testRecoverWithCorrectVersion() (gas: 20088)
+ECDSATest:testRecoverWithInvalidSignature() (gas: 13541)
+ECDSATest:testRecoverWithTooHighSValue() (gas: 11696)
+ECDSATest:testRecoverWithTooLongSignature() (gas: 9947)
+ECDSATest:testRecoverWithTooShortSignature() (gas: 9570)
+ECDSATest:testRecoverWithValidSignature() (gas: 19401)
+ECDSATest:testRecoverWithWrongMessage() (gas: 19396)
+ECDSATest:testRecoverWithWrongVersion() (gas: 13939)
+ECDSATest:testToTypedDataHash() (gas: 5882)
+EIP712DomainSeparatorTest:testCachedDomainSeparatorV4() (gas: 7575)
+EIP712DomainSeparatorTest:testDomainSeparatorV4() (gas: 9234)
+EIP712DomainSeparatorTest:testHashTypedDataV4() (gas: 10876)
+MerkleProofVerificationTest:testInvalidMerkleMultiProof() (gas: 412478916)
+MerkleProofVerificationTest:testInvalidMerkleProof() (gas: 33970262)
+MerkleProofVerificationTest:testInvalidMerkleProofLength() (gas: 33972496)
+MerkleProofVerificationTest:testInvalidMultiProof() (gas: 909634320)
+MerkleProofVerificationTest:testMultiProofEdgeCase1() (gas: 412458074)
+MerkleProofVerificationTest:testMultiProofEdgeCase2() (gas: 412458157)
+MerkleProofVerificationTest:testMultiProofVerify() (gas: 412483025)
+MerkleProofVerificationTest:testVerify() (gas: 67939287)
+MulticallsTest:testMulticallRevert() (gas: 545361187)
+MulticallsTest:testMulticallSelfRevert() (gas: 1090159014)
+MulticallsTest:testMulticallSelfSuccess() (gas: 1635485932)
+MulticallsTest:testMulticallSuccess() (gas: 545364135)
+MulticallsTest:testMulticallValueRevertCase1() (gas: 545904534)
+MulticallsTest:testMulticallValueRevertCase2() (gas: 545905772)
+MulticallsTest:testMulticallValueSelfCase1() (gas: 1091235768)
+MulticallsTest:testMulticallValueSelfCase2() (gas: 1091253522)
+MulticallsTest:testMulticallValueSelfSuccess() (gas: 1091257158)
+MulticallsTest:testMulticallValueSuccess() (gas: 545926196)
+MulticallsTest:testMultistaticcallRevert() (gas: 8937393460525250605)
+MulticallsTest:testMultistaticcallSuccess() (gas: 545336832)
+SignatureCheckerTest:testEIP1271WithInvalidSignature1() (gas: 25066)
+SignatureCheckerTest:testEIP1271WithInvalidSignature2() (gas: 15987)
+SignatureCheckerTest:testEIP1271WithInvalidSigner() (gas: 24995)
+SignatureCheckerTest:testEIP1271WithMaliciousWallet() (gas: 19035)
+SignatureCheckerTest:testEIP1271WithValidSignature() (gas: 25099)
+SignatureCheckerTest:testEOAWithInvalidSignature1() (gas: 16799)
+SignatureCheckerTest:testEOAWithInvalidSignature2() (gas: 14235)
+SignatureCheckerTest:testEOAWithInvalidSigner() (gas: 16810)
+SignatureCheckerTest:testEOAWithValidSignature() (gas: 13672)
diff --git a/src/tokens/ERC20.vy b/src/tokens/ERC20.vy
index b8105246..57b799b4 100644
--- a/src/tokens/ERC20.vy
+++ b/src/tokens/ERC20.vy
@@ -197,7 +197,7 @@ def __init__(name_: String[25], symbol_: String[5], initial_supply_: uint256, na
 
     self._transfer_ownership(msg.sender)
     self.is_minter[msg.sender] = True
-    if (initial_supply > 0):
+    if (initial_supply != empty(uint256)):
         self.balanceOf[msg.sender] = initial_supply
         self.totalSupply = initial_supply
 
diff --git a/src/tokens/ERC721.vy b/src/tokens/ERC721.vy
index d5561437..3280be63 100644
--- a/src/tokens/ERC721.vy
+++ b/src/tokens/ERC721.vy
@@ -260,7 +260,7 @@ def __init__(name_: String[25], symbol_: String[5], base_uri_: String[80], name_
            main version of the signing domain. Signatures
            from different versions are not compatible.
     """
-    self._counter = 0
+    self._counter = empty(uint256)
 
     name = name_
     symbol = symbol_
@@ -454,17 +454,17 @@ def tokenURI(token_id: uint256) -> String[512]:
     token_uri: String[432] = self._token_uris[token_id]
 
     # If there is no base URI, return the token URI.
-    if (len(_BASE_URI) == 0):
+    if (len(_BASE_URI) == empty(uint256)):
         return token_uri
 
     # If both are set, concatenate the base URI
     # and token URI.
-    if (len(token_uri) > 0):
+    if (len(token_uri) != empty(uint256)):
         return concat(_BASE_URI, token_uri)
 
     # If there is no token URI but a base URI,
     # concatenate the base URI and token ID.
-    if (len(_BASE_URI) > 0):
+    if (len(_BASE_URI) != empty(uint256)):
         return concat(_BASE_URI, uint2str(token_id))
     else:
         return ""
@@ -868,7 +868,7 @@ def _burn(token_id: uint256):
 
     # Checks whether a token-specific URI has been set for the token
     # and deletes the token URI from the storage mapping.
-    if (len(self._token_uris[token_id]) > 0):
+    if (len(self._token_uris[token_id]) != empty(uint256)):
         self._token_uris[token_id] = ""
 
 
@@ -1001,8 +1001,8 @@ def _remove_token_from_owner_enumeration(owner: address, token_id:uint256):
     
     # This also deletes the contents at the
     # last position of the array.
-    self._owned_tokens_index[token_id] = 0
-    self._owned_tokens[owner][last_token_index] = 0
+    self._owned_tokens_index[token_id] = empty(uint256)
+    self._owned_tokens[owner][last_token_index] = empty(uint256)
 
 
 @internal
@@ -1036,7 +1036,7 @@ def _remove_token_from_all_tokens_enumeration(token_id: uint256):
 
     # This also deletes the contents at the
     # last position of the array.
-    self._all_tokens_index[token_id] = 0
+    self._all_tokens_index[token_id] = empty(uint256)
     self._all_tokens.pop()
 
 
diff --git a/src/utils/BatchDistributor.vy b/src/utils/BatchDistributor.vy
index 3957d26e..27fec25d 100644
--- a/src/utils/BatchDistributor.vy
+++ b/src/utils/BatchDistributor.vy
@@ -57,7 +57,7 @@ def distribute_ether(data: Batch):
         # safety measure, a reentrancy guard is used.
         raw_call(txn.recipient, b"", value=txn.amount)
 
-    if (self.balance != 0):
+    if (self.balance != empty(uint256)):
         raw_call(msg.sender, b"", value=self.balance)
 
 
@@ -81,7 +81,7 @@ def distribute_token(token: ERC20, data: Batch):
            of tuples that contain each a recipient address &
            token amount.
     """
-    total: uint256 = 0
+    total: uint256 = empty(uint256)
     for txn in data.txns:
         total += txn.amount
 
diff --git a/src/utils/MerkleProofVerification.vy b/src/utils/MerkleProofVerification.vy
index 58fa443e..b15544b5 100644
--- a/src/utils/MerkleProofVerification.vy
+++ b/src/utils/MerkleProofVerification.vy
@@ -56,6 +56,31 @@ def verify(proof: DynArray[bytes32, max_value(uint16)], root: bytes32, leaf: byt
     return self._process_proof(proof, leaf) == root
 
 
+@external
+@pure
+def multi_proof_verify(proof: DynArray[bytes32, max_value(uint16)], proof_flags: DynArray[bool, max_value(uint16)],
+                       root: bytes32, leaves: DynArray[bytes32, max_value(uint16)]) -> bool:
+    """
+    @dev Returns `True` if it can be simultaneously proved that
+         `leaves` are part of a Merkle tree defined by `root`
+         and a given set of `proof_flags`.
+    @notice Note that not all Merkle trees allow for multiproofs.
+            See `_process_multi_proof` for further details.
+    @param proof The 32-byte array containing sibling hashes
+           on the branches from `leaves` to the `root` of the
+           Merkle tree.
+    @param proof_flags The Boolean array of flags indicating
+           whether another value from the "main queue" (merging
+           branches) or an element from the `proof` array is used
+           to calculate the next hash.
+    @param root The 32-byte Merkle root hash.
+    @param leaves The 32-byte array containing the leaf hashes.
+    @return bool The verification whether `leaves` are simultaneously
+            part of a Merkle tree defined by `root`.
+    """
+    return self._process_multi_proof(proof, proof_flags, leaves) == root
+
+
 @internal
 @pure
 def _process_proof(proof: DynArray[bytes32, max_value(uint16)], leaf: bytes32) -> bytes32:
@@ -77,6 +102,91 @@ def _process_proof(proof: DynArray[bytes32, max_value(uint16)], leaf: bytes32) -
     return computed_hash
 
 
+@internal
+@pure
+def _process_multi_proof(proof: DynArray[bytes32, max_value(uint16)], proof_flags: DynArray[bool, max_value(uint16)],
+                         leaves: DynArray[bytes32, max_value(uint16)]) -> bytes32:
+    """
+    @dev Returns the recovered hash obtained by traversing
+         a Merkle tree from `leaves` using `proof` and a
+         a given set of `proof_flags`.
+    @notice The reconstruction is performed by incrementally
+            reconstructing all inner nodes by combining a
+            leaf/inner node with either another leaf/inner node
+            or a proof sibling node, depending on whether each
+            `proof_flags` element is `True` or `False`.
+
+            IMPORTANT: Note that not all Merkle trees allow for
+            multiproofs. In order to use multiproofs, it is
+            sufficient to ensure that:
+            1) the Merkle tree is complete (but not necessarily
+               perfect),
+            2) the `leaves` to be proved are in the reverse order
+               in which they are in the Merkle tree (i.e. from right
+               to left, starting with the deepest layer and moving
+               on to the next layer). For the definition of the
+               generalised Merkle tree index, please visit:
+               https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md#generalized-merkle-tree-index.
+    @param proof The 32-byte array containing sibling hashes
+           on the branches from `leaves` to the `root` of the
+           Merkle tree.
+    @param proof_flags The Boolean array of flags indicating
+           whether another value from the "main queue" (merging
+           branches) or an element from the `proof` array is used
+           to calculate the next hash.
+    @param leaves The 32-byte array containing the leaf hashes.
+    @return bytes32 The 32-byte recovered hash by using `leaves`
+            and `proof` with a given set of `proof_flags`.
+    """
+    leaves_len: uint256 = len(leaves)
+    total_hashes: uint256 = len(proof_flags)
+
+    # Checks the validity of the proof. We do not check for an
+    # overflow (nor underflow) as `leaves_len`, `proof`, and
+    # `total_hashes` are bounded by the value `max_value(uint16)`
+    # and therefore cannot overflow the `uint256` type when they
+    # are added together or incremented by 1.
+    assert unsafe_add(leaves_len, len(proof)) == unsafe_add(total_hashes, 1), "MerkleProof: invalid multiproof"
+
+    hashes: DynArray[bytes32, max_value(uint16)] = []
+    leaf_pos: uint256 = empty(uint256)
+    hash_pos: uint256 = empty(uint256)
+    proof_pos: uint256 = empty(uint256)
+    a: bytes32 = empty(bytes32)
+    b: bytes32 = empty(bytes32)
+
+    # At each step, the next hash is calculated from two values:
+    # - a value from the "main queue". If not all leaves have been used,
+    #   the next leaf is picked up, otherwise the next hash.
+    # - depending on the flag, either another value from the "main queue"
+    #   (merging branches) or an element from the `proof` array.
+    for flag in proof_flags:
+        if (leaf_pos < leaves_len):
+            a = leaves[leaf_pos]
+            leaf_pos += 1
+        else:
+            a = hashes[hash_pos]
+            hash_pos += 1
+        if (flag):
+            if (leaf_pos < leaves_len):
+                b = leaves[leaf_pos]
+                leaf_pos += 1
+            else:
+                b = hashes[hash_pos]
+                hash_pos += 1
+        else:
+            b = proof[proof_pos]
+            proof_pos += 1
+        hashes.append(self._hash_pair(a, b))
+
+    if (total_hashes != empty(uint256)):
+        return hashes[total_hashes - 1]
+    elif (leaves_len != empty(uint256)):
+        return leaves[empty(uint256)]
+    else:
+        return proof[empty(uint256)]
+
+
 @internal
 @pure
 def _hash_pair(a: bytes32, b: bytes32) -> bytes32:
diff --git a/src/utils/Multicall.vy b/src/utils/Multicall.vy
index e07ddbc7..1cb9e341 100644
--- a/src/utils/Multicall.vy
+++ b/src/utils/Multicall.vy
@@ -74,7 +74,7 @@ def multicall(data: DynArray[Batch, max_value(uint8)]) -> DynArray[Result, max_v
     """
     results: DynArray[Result, max_value(uint8)] = []
     return_data: Bytes[max_value(uint8)] = b""
-    success: bool = False
+    success: bool = empty(bool)
     for batch in data:
         if (batch.allow_failure == False):
             return_data = raw_call(batch.target, batch.call_data, max_outsize=max_value(uint8))
@@ -99,10 +99,10 @@ def multicall_value(data: DynArray[BatchValue, max_value(uint8)]) -> DynArray[Re
     @param data The array of `BatchValue` structs.
     @return DynArray The array of `Result` structs.
     """
-    value_accumulator: uint256 = 0
+    value_accumulator: uint256 = empty(uint256)
     results: DynArray[Result, max_value(uint8)] = []
     return_data: Bytes[max_value(uint8)] = b""
-    success: bool = False
+    success: bool = empty(bool)
     for batch in data:
         msg_value: uint256 = batch.value
         value_accumulator = unsafe_add(value_accumulator, msg_value)
@@ -136,7 +136,7 @@ def multicall_self(data: DynArray[BatchSelf, max_value(uint8)]) -> DynArray[Resu
     """
     results: DynArray[Result, max_value(uint8)] = []
     return_data: Bytes[max_value(uint8)] = b""
-    success: bool = False
+    success: bool = empty(bool)
     for batch in data:
         if (batch.allow_failure == False):
             return_data = raw_call(self, batch.call_data, max_outsize=max_value(uint8), is_delegate_call=True)
@@ -167,10 +167,10 @@ def multicall_value_self(data: DynArray[BatchValueSelf, max_value(uint8)]) -> Dy
     @param data The array of `BatchValueSelf` structs.
     @return DynArray The array of `Result` structs.
     """
-    value_accumulator: uint256 = 0
+    value_accumulator: uint256 = empty(uint256)
     results: DynArray[Result, max_value(uint8)] = []
     return_data: Bytes[max_value(uint8)] = b""
-    success: bool = False
+    success: bool = empty(bool)
     for batch in data:
         msg_value: uint256 = batch.value
         value_accumulator = unsafe_add(value_accumulator, msg_value)
@@ -197,7 +197,7 @@ def multistaticcall(data: DynArray[Batch, max_value(uint8)]) -> DynArray[Result,
     """
     results: DynArray[Result, max_value(uint8)] = []
     return_data: Bytes[max_value(uint8)] = b""
-    success: bool = False
+    success: bool = empty(bool)
     for batch in data:
         if (batch.allow_failure == False):
             return_data = raw_call(batch.target, batch.call_data, max_outsize=max_value(uint8), is_static_call=True)


Test patch:
diff --git a/test/utils/MerkleProofVerification.t.sol b/test/utils/MerkleProofVerification.t.sol
index 0791406d..cce7152a 100644
--- a/test/utils/MerkleProofVerification.t.sol
+++ b/test/utils/MerkleProofVerification.t.sol
@@ -44,6 +44,102 @@ contract MerkleProofVerificationTest is Test {
         return proofDecoded;
     }
 
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * multiproof after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible 32-byte array type.
+     * @return bytes32[] The 32-byte array containing sibling hashes
+     * on the branches from `leaves` to the `root` of the Merkle tree.
+     */
+    function decodeCorrectMultiProofPayload()
+        internal
+        returns (bytes32[] memory)
+    {
+        bytes32[] memory multiProofDecoded = new bytes32[](8);
+        string[] memory cmdsMultiProof = new string[](2);
+        cmdsMultiProof[0] = "node";
+        cmdsMultiProof[1] = "test/utils/scripts/generate-multiproof.js";
+        bytes memory multiProof = vm.ffi(cmdsMultiProof);
+        (
+            bytes32 arg1,
+            bytes32 arg2,
+            bytes32 arg3,
+            bytes32 arg4,
+            bytes32 arg5,
+            bytes32 arg6,
+            bytes32 arg7,
+            bytes32 arg8
+        ) = abi.decode(
+                multiProof,
+                (
+                    bytes32,
+                    bytes32,
+                    bytes32,
+                    bytes32,
+                    bytes32,
+                    bytes32,
+                    bytes32,
+                    bytes32
+                )
+            );
+        multiProofDecoded[0] = arg1;
+        multiProofDecoded[1] = arg2;
+        multiProofDecoded[2] = arg3;
+        multiProofDecoded[3] = arg4;
+        multiProofDecoded[4] = arg5;
+        multiProofDecoded[5] = arg6;
+        multiProofDecoded[6] = arg7;
+        multiProofDecoded[7] = arg8;
+        return multiProofDecoded;
+    }
+
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * proof flags after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible Boolean array type.
+     * @return bool[] The Boolean array of flags indicating whether another
+     * value from the "main queue" (merging branches) or an element from the
+     * `proof` array is used to calculate the next hash.
+     */
+    function decodeCorrectMultiProofProofFlags()
+        internal
+        returns (bool[] memory)
+    {
+        bool[] memory multiProofProofFlagsDecoded = new bool[](10);
+        string[] memory cmdsMultiProofProofFlags = new string[](2);
+        cmdsMultiProofProofFlags[0] = "node";
+        cmdsMultiProofProofFlags[
+            1
+        ] = "test/utils/scripts/generate-multiproof-proof-flags.js";
+        bytes memory multiProofProofFlags = vm.ffi(cmdsMultiProofProofFlags);
+        (
+            bool arg1,
+            bool arg2,
+            bool arg3,
+            bool arg4,
+            bool arg5,
+            bool arg6,
+            bool arg7,
+            bool arg8,
+            bool arg9,
+            bool arg10
+        ) = abi.decode(
+                multiProofProofFlags,
+                (bool, bool, bool, bool, bool, bool, bool, bool, bool, bool)
+            );
+        multiProofProofFlagsDecoded[0] = arg1;
+        multiProofProofFlagsDecoded[1] = arg2;
+        multiProofProofFlagsDecoded[2] = arg3;
+        multiProofProofFlagsDecoded[3] = arg4;
+        multiProofProofFlagsDecoded[4] = arg5;
+        multiProofProofFlagsDecoded[5] = arg6;
+        multiProofProofFlagsDecoded[6] = arg7;
+        multiProofProofFlagsDecoded[7] = arg8;
+        multiProofProofFlagsDecoded[8] = arg9;
+        multiProofProofFlagsDecoded[9] = arg10;
+        return multiProofProofFlagsDecoded;
+    }
+
     /**
      * @dev An `internal` helper function that converts the JavaScript-based
      * proof after calling the foreign function interface (ffi) cheatcode
@@ -103,6 +199,78 @@ contract MerkleProofVerificationTest is Test {
         return proofDecoded;
     }
 
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * multiproof after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible 32-byte array type.
+     * @notice This `internal` helper function is used to decode a bad multiproof.
+     * @return bytes32[] The 32-byte array containing sibling hashes
+     * on the branches from `leaves` to the `root` of the Merkle tree.
+     */
+    function decodeBadMultiProofPayload() internal returns (bytes32[] memory) {
+        bytes32[] memory multiProofDecoded = new bytes32[](5);
+        string[] memory cmdsMultiProof = new string[](2);
+        cmdsMultiProof[0] = "node";
+        cmdsMultiProof[1] = "test/utils/scripts/generate-bad-multiproof.js";
+        bytes memory multiProof = vm.ffi(cmdsMultiProof);
+        (
+            bytes32 arg1,
+            bytes32 arg2,
+            bytes32 arg3,
+            bytes32 arg4,
+            bytes32 arg5
+        ) = abi.decode(
+                multiProof,
+                (bytes32, bytes32, bytes32, bytes32, bytes32)
+            );
+        multiProofDecoded[0] = arg1;
+        multiProofDecoded[1] = arg2;
+        multiProofDecoded[2] = arg3;
+        multiProofDecoded[3] = arg4;
+        multiProofDecoded[4] = arg5;
+        return multiProofDecoded;
+    }
+
+    /**
+     * @dev An `internal` helper function that converts the JavaScript-based
+     * proof flags after calling the foreign function interface (ffi) cheatcode
+     * `vm.ffi` into a Solidity-compatible Boolean array type.
+     * @notice This `internal` helper function is used to decode a bad multiproof
+     * proof flags array.
+     * @return bool[] The Boolean array of flags indicating whether another
+     * value from the "main queue" (merging branches) or an element from the
+     * `proof` array is used to calculate the next hash.
+     */
+    function decodeBadMultiProofProofFlags() internal returns (bool[] memory) {
+        bool[] memory multiProofProofFlagsDecoded = new bool[](7);
+        string[] memory cmdsMultiProofProofFlags = new string[](2);
+        cmdsMultiProofProofFlags[0] = "node";
+        cmdsMultiProofProofFlags[
+            1
+        ] = "test/utils/scripts/generate-bad-multiproof-proof-flags.js";
+        bytes memory multiProofProofFlags = vm.ffi(cmdsMultiProofProofFlags);
+        (
+            bool arg1,
+            bool arg2,
+            bool arg3,
+            bool arg4,
+            bool arg5,
+            bool arg6,
+            bool arg7
+        ) = abi.decode(
+                multiProofProofFlags,
+                (bool, bool, bool, bool, bool, bool, bool)
+            );
+        multiProofProofFlagsDecoded[0] = arg1;
+        multiProofProofFlagsDecoded[1] = arg2;
+        multiProofProofFlagsDecoded[2] = arg3;
+        multiProofProofFlagsDecoded[3] = arg4;
+        multiProofProofFlagsDecoded[4] = arg5;
+        multiProofProofFlagsDecoded[5] = arg6;
+        multiProofProofFlagsDecoded[6] = arg7;
+        return multiProofProofFlagsDecoded;
+    }
+
     function setUp() public {
         merkleProofVerification = IMerkleProofVerification(
             vyperDeployer.deployContract(
@@ -160,7 +328,7 @@ contract MerkleProofVerificationTest is Test {
         string[] memory cmdsCorrectRoot = new string[](2);
         cmdsCorrectRoot[0] = "node";
         cmdsCorrectRoot[1] = "test/utils/scripts/generate-root.js";
-        bytes memory root = vm.ffi(cmdsCorrectRoot);
+        bytes memory correctRoot = vm.ffi(cmdsCorrectRoot);
 
         string[] memory cmdsCorrectLeaf = new string[](2);
         cmdsCorrectLeaf[0] = "node";
@@ -172,7 +340,7 @@ contract MerkleProofVerificationTest is Test {
         assertTrue(
             !merkleProofVerification.verify(
                 badProofDecoded,
-                bytes32(root),
+                bytes32(correctRoot),
                 bytes32(leaf)
             )
         );
@@ -204,4 +372,172 @@ contract MerkleProofVerificationTest is Test {
             )
         );
     }
+
+    function testMultiProofVerify() public {
+        string[] memory cmdsRoot = new string[](2);
+        cmdsRoot[0] = "node";
+        cmdsRoot[1] = "test/utils/scripts/generate-root.js";
+        bytes memory root = vm.ffi(cmdsRoot);
+
+        string[] memory cmdsLeaves = new string[](2);
+        cmdsLeaves[0] = "node";
+        cmdsLeaves[1] = "test/utils/scripts/generate-multiproof-leaves.js";
+        bytes memory leaves = vm.ffi(cmdsLeaves);
+        bytes32[] memory leavesDecoded = new bytes32[](3);
+        (bytes32 arg1, bytes32 arg2, bytes32 arg3) = abi.decode(
+            leaves,
+            (bytes32, bytes32, bytes32)
+        );
+        leavesDecoded[0] = arg1;
+        leavesDecoded[1] = arg2;
+        leavesDecoded[2] = arg3;
+
+        bool[] memory proofFlags = decodeCorrectMultiProofProofFlags();
+        bytes32[] memory multiProofDecoded = decodeCorrectMultiProofPayload();
+
+        assertTrue(
+            merkleProofVerification.multi_proof_verify(
+                multiProofDecoded,
+                proofFlags,
+                bytes32(root),
+                leavesDecoded
+            )
+        );
+    }
+
+    function testInvalidMerkleMultiProof() public {
+        string[] memory cmdsCorrectRoot = new string[](2);
+        cmdsCorrectRoot[0] = "node";
+        cmdsCorrectRoot[1] = "test/utils/scripts/generate-root.js";
+        bytes memory correctRoot = vm.ffi(cmdsCorrectRoot);
+
+        string[] memory cmdsBadLeaves = new string[](2);
+        cmdsBadLeaves[0] = "node";
+        cmdsBadLeaves[
+            1
+        ] = "test/utils/scripts/generate-bad-multiproof-leaves.js";
+        bytes memory badLeaves = vm.ffi(cmdsBadLeaves);
+        bytes32[] memory badLeavesDecoded = new bytes32[](3);
+        (bytes32 arg1, bytes32 arg2, bytes32 arg3) = abi.decode(
+            badLeaves,
+            (bytes32, bytes32, bytes32)
+        );
+        badLeavesDecoded[0] = arg1;
+        badLeavesDecoded[1] = arg2;
+        badLeavesDecoded[2] = arg3;
+
+        bool[] memory badProofFlags = decodeBadMultiProofProofFlags();
+        bytes32[] memory badMultiProofDecoded = decodeBadMultiProofPayload();
+
+        assertTrue(
+            !merkleProofVerification.multi_proof_verify(
+                badMultiProofDecoded,
+                badProofFlags,
+                bytes32(correctRoot),
+                badLeavesDecoded
+            )
+        );
+    }
+
+    function testInvalidMultiProof() public {
+        string[] memory cmdsCorrectRoot = new string[](2);
+        cmdsCorrectRoot[0] = "node";
+        cmdsCorrectRoot[1] = "test/utils/scripts/generate-root.js";
+        bytes memory correctRoot = vm.ffi(cmdsCorrectRoot);
+
+        string[] memory cmdsBadLeaves = new string[](2);
+        cmdsBadLeaves[0] = "node";
+        cmdsBadLeaves[
+            1
+        ] = "test/utils/scripts/generate-bad-multiproof-leaves.js";
+        bytes memory badLeaves = vm.ffi(cmdsBadLeaves);
+        bytes32[] memory badLeavesDecoded = new bytes32[](3);
+        (bytes32 arg1, bytes32 arg2, bytes32 arg3) = abi.decode(
+            badLeaves,
+            (bytes32, bytes32, bytes32)
+        );
+        badLeavesDecoded[0] = arg1;
+        badLeavesDecoded[1] = arg2;
+        badLeavesDecoded[2] = arg3;
+
+        bool[] memory badProofFlags = decodeBadMultiProofProofFlags();
+        bytes32[] memory badMultiProofDecoded = decodeBadMultiProofPayload();
+
+        bool[] memory badProofFlagsSliced = new bool[](3);
+        badProofFlagsSliced[0] = badProofFlags[0];
+        badProofFlagsSliced[1] = badProofFlags[2];
+        badProofFlagsSliced[2] = badProofFlags[4];
+
+        bytes32[] memory badMultiProofDecodedSliced = new bytes32[](3);
+        badMultiProofDecodedSliced[0] = badMultiProofDecoded[0];
+        badMultiProofDecodedSliced[1] = badMultiProofDecoded[2];
+        badMultiProofDecodedSliced[2] = badMultiProofDecoded[4];
+
+        bytes32[] memory badLeavesDecodedSliced = new bytes32[](2);
+        badLeavesDecodedSliced[0] = badLeavesDecoded[0];
+        badLeavesDecodedSliced[1] = badLeavesDecoded[2];
+
+        vm.expectRevert(bytes("MerkleProof: invalid multiproof"));
+        merkleProofVerification.multi_proof_verify(
+            badMultiProofDecodedSliced,
+            badProofFlags,
+            bytes32(correctRoot),
+            badLeavesDecoded
+        );
+
+        vm.expectRevert(bytes("MerkleProof: invalid multiproof"));
+        merkleProofVerification.multi_proof_verify(
+            badMultiProofDecoded,
+            badProofFlagsSliced,
+            bytes32(correctRoot),
+            badLeavesDecoded
+        );
+
+        vm.expectRevert(bytes("MerkleProof: invalid multiproof"));
+        merkleProofVerification.multi_proof_verify(
+            badMultiProofDecoded,
+            badProofFlags,
+            bytes32(correctRoot),
+            badLeavesDecodedSliced
+        );
+    }
+
+    function testMultiProofEdgeCase1() public {
+        bytes32[] memory leaves = new bytes32[](1);
+        bytes32[] memory multiProof = new bytes32[](0);
+        bool[] memory proofFlags = new bool[](0);
+
+        leaves[0] = keccak256(bytes.concat(keccak256(abi.encode("a"))));
+
+        /// @dev Works for a Merkle tree containing a single leaf.
+        assertTrue(
+            merkleProofVerification.multi_proof_verify(
+                multiProof,
+                proofFlags,
+                leaves[0],
+                leaves
+            )
+        );
+    }
+
+    function testMultiProofEdgeCase2() public {
+        bytes32[] memory leaves = new bytes32[](0);
+        bytes32[] memory multiProof = new bytes32[](1);
+        bool[] memory proofFlags = new bool[](0);
+
+        bytes32 root = keccak256(
+            bytes.concat(keccak256(abi.encode("a", "b", "c")))
+        );
+        multiProof[0] = root;
+
+        /// @dev Can prove empty leaves.
+        assertTrue(
+            merkleProofVerification.multi_proof_verify(
+                multiProof,
+                proofFlags,
+                root,
+                leaves
+            )
+        );
+    }
 }
diff --git a/test/utils/interfaces/IMerkleProofVerification.sol b/test/utils/interfaces/IMerkleProofVerification.sol
index 4724835d..f87ab379 100644
--- a/test/utils/interfaces/IMerkleProofVerification.sol
+++ b/test/utils/interfaces/IMerkleProofVerification.sol
@@ -7,4 +7,11 @@ interface IMerkleProofVerification {
         bytes32 root,
         bytes32 leaf
     ) external pure returns (bool);
+
+    function multi_proof_verify(
+        bytes32[] memory proof,
+        bool[] memory proofFlags,
+        bytes32 root,
+        bytes32[] memory leaves
+    ) external pure returns (bool);
 }
diff --git a/test/utils/scripts/generate-bad-multiproof-leaves.js b/test/utils/scripts/generate-bad-multiproof-leaves.js
new file mode 100644
index 00000000..5290fed7
--- /dev/null
+++ b/test/utils/scripts/generate-bad-multiproof-leaves.js
@@ -0,0 +1,17 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const badElements = require("./multiproof-bad-elements.js");
+const merkleTree = StandardMerkleTree.of(
+  badElements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-bad-indices.js");
+const { leaves } = merkleTree.getMultiProof(idx);
+const hashedBadLeaves = leaves.map((c) => merkleTree.leafHash(c));
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(3).fill("bytes32"), hashedBadLeaves)
+);
diff --git a/test/utils/scripts/generate-bad-multiproof-proof-flags.js b/test/utils/scripts/generate-bad-multiproof-proof-flags.js
new file mode 100644
index 00000000..d208bd62
--- /dev/null
+++ b/test/utils/scripts/generate-bad-multiproof-proof-flags.js
@@ -0,0 +1,16 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const badElements = require("./multiproof-bad-elements.js");
+const merkleTree = StandardMerkleTree.of(
+  badElements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-bad-indices.js");
+const { proofFlags } = merkleTree.getMultiProof(idx);
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(7).fill("bool"), proofFlags)
+);
diff --git a/test/utils/scripts/generate-bad-multiproof.js b/test/utils/scripts/generate-bad-multiproof.js
new file mode 100644
index 00000000..58046020
--- /dev/null
+++ b/test/utils/scripts/generate-bad-multiproof.js
@@ -0,0 +1,16 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const badElements = require("./multiproof-bad-elements.js");
+const merkleTree = StandardMerkleTree.of(
+  badElements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-bad-indices.js");
+const { proof } = merkleTree.getMultiProof(idx);
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(5).fill("bytes32"), proof)
+);
diff --git a/test/utils/scripts/generate-multiproof-leaves.js b/test/utils/scripts/generate-multiproof-leaves.js
new file mode 100644
index 00000000..55edb36d
--- /dev/null
+++ b/test/utils/scripts/generate-multiproof-leaves.js
@@ -0,0 +1,17 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const elements = require("./elements.js");
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-indices.js");
+const { leaves } = merkleTree.getMultiProof(idx);
+const hashedLeaves = leaves.map((c) => merkleTree.leafHash(c));
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(3).fill("bytes32"), hashedLeaves)
+);
diff --git a/test/utils/scripts/generate-multiproof-proof-flags.js b/test/utils/scripts/generate-multiproof-proof-flags.js
new file mode 100644
index 00000000..cb6e8286
--- /dev/null
+++ b/test/utils/scripts/generate-multiproof-proof-flags.js
@@ -0,0 +1,16 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const elements = require("./elements.js");
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-indices.js");
+const { proofFlags } = merkleTree.getMultiProof(idx);
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(10).fill("bool"), proofFlags)
+);
diff --git a/test/utils/scripts/generate-multiproof.js b/test/utils/scripts/generate-multiproof.js
new file mode 100644
index 00000000..1a0be2ae
--- /dev/null
+++ b/test/utils/scripts/generate-multiproof.js
@@ -0,0 +1,16 @@
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+const ethers = require("ethers");
+
+const elements = require("./elements.js");
+const merkleTree = StandardMerkleTree.of(
+  elements.map((c) => [c]),
+  ["string"]
+);
+
+const idx = require("./multiproof-indices.js");
+const { proof } = merkleTree.getMultiProof(idx);
+
+// eslint-disable-next-line no-undef
+process.stdout.write(
+  ethers.utils.defaultAbiCoder.encode(Array(8).fill("bytes32"), proof)
+);
diff --git a/test/utils/scripts/generate-proof-no-such-leaf.js b/test/utils/scripts/generate-proof-no-such-leaf.js
index 8db70e01..b5c4b5bc 100644
--- a/test/utils/scripts/generate-proof-no-such-leaf.js
+++ b/test/utils/scripts/generate-proof-no-such-leaf.js
@@ -13,16 +13,5 @@ const proof = merkleTree.getHexProof(leaf);
 
 // eslint-disable-next-line no-undef
 process.stdout.write(
-  ethers.utils.defaultAbiCoder.encode(
-    [
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-      "bytes32",
-    ],
-    proof
-  )
+  ethers.utils.defaultAbiCoder.encode(Array(7).fill("bytes32"), proof)
 );
diff --git a/test/utils/scripts/generate-proof.js b/test/utils/scripts/generate-proof.js
index 04a08324..d44ec6ee 100644
--- a/test/utils/scripts/generate-proof.js
+++ b/test/utils/scripts/generate-proof.js
@@ -11,8 +11,5 @@ const proof = merkleTree.getProof([elements[0]]);
 
 // eslint-disable-next-line no-undef
 process.stdout.write(
-  ethers.utils.defaultAbiCoder.encode(
-    ["bytes32", "bytes32", "bytes32", "bytes32", "bytes32", "bytes32"],
-    proof
-  )
+  ethers.utils.defaultAbiCoder.encode(Array(6).fill("bytes32"), proof)
 );
diff --git a/test/utils/scripts/multiproof-bad-elements.js b/test/utils/scripts/multiproof-bad-elements.js
new file mode 100644
index 00000000..4a4a6202
--- /dev/null
+++ b/test/utils/scripts/multiproof-bad-elements.js
@@ -0,0 +1,1 @@
+module.exports = "$£!?àéèç@%&#=+-:;_()/`*°~".split("");
diff --git a/test/utils/scripts/multiproof-bad-indices.js b/test/utils/scripts/multiproof-bad-indices.js
new file mode 100644
index 00000000..fe424313
--- /dev/null
+++ b/test/utils/scripts/multiproof-bad-indices.js
@@ -0,0 +1,1 @@
+module.exports = [0, 2, 5];
diff --git a/test/utils/scripts/multiproof-indices.js b/test/utils/scripts/multiproof-indices.js
new file mode 100644
index 00000000..11f04d05
--- /dev/null
+++ b/test/utils/scripts/multiproof-indices.js
@@ -0,0 +1,1 @@
+module.exports = [0, 1, 4];