Repository: pcaversaccio/snekmate (language: python)

Issue:
💥 Add `wad_ln` and `wad_exp` Functions to `Math` Contract
Add two new functions to the `Math` contract:

- `wad_ln`:  Calculates the _natural logarithm_ of a signed integer with a precision of 1e18.
- `wad_exp`: Calculates the _natural exponential function_ of a signed integer with a precision of 1e18.


Discussion / hints:

Fix patch:
diff --git a/.eslintignore b/.eslintignore
index 7e97ca09..12234ceb 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,7 @@
 node_modules
 lib/murky
+lib/solady
+lib/solmate
 lib/prb-test
 lib/forge-std
 lib/create-util
diff --git a/.gas-snapshot b/.gas-snapshot
index 6416d665..0a780bff 100644
--- a/.gas-snapshot
+++ b/.gas-snapshot
@@ -59,8 +59,8 @@ BatchDistributorTest:testDistributeTokenMultipleAddressesSuccess() (gas: 764652)
 BatchDistributorTest:testDistributeTokenOneAddressSuccess() (gas: 727265)
 BatchDistributorTest:testDistributeTokenRevertWithInsufficientAllowance() (gas: 721109)
 BatchDistributorTest:testDistributeTokenRevertWithInsufficientBalance() (gas: 723840)
-BatchDistributorTest:testFuzzDistributeEtherMultipleAddressesSuccess(((address,uint256)[]),uint256) (runs: 256, μ: 988506, ~: 925621)
-BatchDistributorTest:testFuzzDistributeTokenMultipleAddressesSuccess(((address,uint256)[]),address,uint256) (runs: 256, μ: 1465572, ~: 1430877)
+BatchDistributorTest:testFuzzDistributeEtherMultipleAddressesSuccess(((address,uint256)[]),uint256) (runs: 256, μ: 988403, ~: 925621)
+BatchDistributorTest:testFuzzDistributeTokenMultipleAddressesSuccess(((address,uint256)[]),address,uint256) (runs: 256, μ: 1465322, ~: 1430877)
 Create2AddressTest:testComputeAddress() (gas: 702919)
 Create2AddressTest:testComputeAddressSelf() (gas: 711250)
 Create2AddressTest:testFuzzComputeAddress(bytes32,address) (runs: 256, μ: 703452, ~: 703452)
@@ -86,32 +86,32 @@ CreateAddressTest:testComputeAddressSelfNonceUint56() (gas: 688545)
 CreateAddressTest:testComputeAddressSelfNonceUint64() (gas: 688641)
 CreateAddressTest:testComputeAddressSelfNonceUint8() (gas: 688290)
 CreateAddressTest:testComputeAddressSelfRevertTooHighNonce() (gas: 8810)
-CreateAddressTest:testFuzzComputeAddressNonce0x7f(uint64,address) (runs: 256, μ: 687744, ~: 687905)
+CreateAddressTest:testFuzzComputeAddressNonce0x7f(uint64,address) (runs: 256, μ: 687743, ~: 687905)
 CreateAddressTest:testFuzzComputeAddressNonceUint16(uint64,address) (runs: 256, μ: 687241, ~: 687368)
-CreateAddressTest:testFuzzComputeAddressNonceUint24(uint64,address) (runs: 256, μ: 687369, ~: 687472)
-CreateAddressTest:testFuzzComputeAddressNonceUint32(uint64,address) (runs: 256, μ: 687368, ~: 687501)
-CreateAddressTest:testFuzzComputeAddressNonceUint40(uint64,address) (runs: 256, μ: 687473, ~: 687571)
-CreateAddressTest:testFuzzComputeAddressNonceUint48(uint64,address) (runs: 256, μ: 687472, ~: 687577)
+CreateAddressTest:testFuzzComputeAddressNonceUint24(uint64,address) (runs: 256, μ: 687370, ~: 687472)
+CreateAddressTest:testFuzzComputeAddressNonceUint32(uint64,address) (runs: 256, μ: 687367, ~: 687501)
+CreateAddressTest:testFuzzComputeAddressNonceUint40(uint64,address) (runs: 256, μ: 687469, ~: 687571)
+CreateAddressTest:testFuzzComputeAddressNonceUint48(uint64,address) (runs: 256, μ: 687476, ~: 687577)
 CreateAddressTest:testFuzzComputeAddressNonceUint56(uint64,address) (runs: 256, μ: 687477, ~: 687584)
 CreateAddressTest:testFuzzComputeAddressNonceUint64(uint64,address) (runs: 256, μ: 687505, ~: 687688)
-CreateAddressTest:testFuzzComputeAddressNonceUint8(uint64,address) (runs: 256, μ: 687243, ~: 687346)
-CreateAddressTest:testFuzzComputeAddressRevertTooHighNonce(uint256,address) (runs: 256, μ: 13375, ~: 13329)
-CreateAddressTest:testFuzzComputeAddressSelfNonce0x7f(uint64) (runs: 256, μ: 693590, ~: 693705)
-CreateAddressTest:testFuzzComputeAddressSelfNonceUint16(uint64) (runs: 256, μ: 692871, ~: 692778)
+CreateAddressTest:testFuzzComputeAddressNonceUint8(uint64,address) (runs: 256, μ: 687244, ~: 687346)
+CreateAddressTest:testFuzzComputeAddressRevertTooHighNonce(uint256,address) (runs: 256, μ: 13377, ~: 13329)
+CreateAddressTest:testFuzzComputeAddressSelfNonce0x7f(uint64) (runs: 256, μ: 693589, ~: 693705)
+CreateAddressTest:testFuzzComputeAddressSelfNonceUint16(uint64) (runs: 256, μ: 692872, ~: 692778)
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint24(uint64) (runs: 256, μ: 693218, ~: 693303)
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint32(uint64) (runs: 256, μ: 693217, ~: 693311)
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint40(uint64) (runs: 256, μ: 693299, ~: 693382)
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint48(uint64) (runs: 256, μ: 693259, ~: 693369)
-CreateAddressTest:testFuzzComputeAddressSelfNonceUint56(uint64) (runs: 256, μ: 693350, ~: 693439)
-CreateAddressTest:testFuzzComputeAddressSelfNonceUint64(uint64) (runs: 256, μ: 693418, ~: 693569)
-CreateAddressTest:testFuzzComputeAddressSelfNonceUint8(uint64) (runs: 256, μ: 692930, ~: 693006)
+CreateAddressTest:testFuzzComputeAddressSelfNonceUint56(uint64) (runs: 256, μ: 693349, ~: 693439)
+CreateAddressTest:testFuzzComputeAddressSelfNonceUint64(uint64) (runs: 256, μ: 693416, ~: 693569)
+CreateAddressTest:testFuzzComputeAddressSelfNonceUint8(uint64) (runs: 256, μ: 692926, ~: 693006)
 CreateAddressTest:testFuzzComputeAddressSelfRevertTooHighNonce(uint256) (runs: 256, μ: 13210, ~: 13168)
 ECDSATest:testEthSignedMessageHash() (gas: 5859)
 ECDSATest:testFuzzEthSignedMessageHash(string) (runs: 256, μ: 6452, ~: 6455)
 ECDSATest:testFuzzRecoverWithInvalidSignature(bytes,string) (runs: 256, μ: 15181, ~: 15182)
 ECDSATest:testFuzzRecoverWithTooLongSignature(bytes,string) (runs: 256, μ: 13144, ~: 13142)
 ECDSATest:testFuzzRecoverWithValidSignature(string,string) (runs: 256, μ: 21790, ~: 21796)
-ECDSATest:testFuzzRecoverWithWrongMessage(string,string,bytes32) (runs: 256, μ: 21773, ~: 21789)
+ECDSATest:testFuzzRecoverWithWrongMessage(string,string,bytes32) (runs: 256, μ: 21777, ~: 21789)
 ECDSATest:testFuzzToDataWithIntendedValidatorHash(address,bytes) (runs: 256, μ: 7380, ~: 7366)
 ECDSATest:testFuzzToDataWithIntendedValidatorHashSelf(bytes) (runs: 256, μ: 9379, ~: 9363)
 ECDSATest:testFuzzToTypedDataHash(string,string) (runs: 256, μ: 7105, ~: 7108)
@@ -130,7 +130,7 @@ ECDSATest:testToDataWithIntendedValidatorHashSelf() (gas: 9535)
 ECDSATest:testToTypedDataHash() (gas: 5973)
 EIP712DomainSeparatorTest:testCachedDomainSeparatorV4() (gas: 7586)
 EIP712DomainSeparatorTest:testDomainSeparatorV4() (gas: 9376)
-EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 9491, ~: 9517)
+EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 9488, ~: 9517)
 EIP712DomainSeparatorTest:testFuzzHashTypedDataV4(address,address,uint256,uint256,uint64) (runs: 256, μ: 7763, ~: 7763)
 EIP712DomainSeparatorTest:testHashTypedDataV4() (gas: 13105)
 ERC1155Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3479)
@@ -160,18 +160,18 @@ ERC1155Test:testExistsAfterBatchMint() (gas: 143972)
 ERC1155Test:testExistsAfterSingleBurn() (gas: 129963)
 ERC1155Test:testExistsAfterSingleMint() (gas: 114087)
 ERC1155Test:testExistsBeforeMint() (gas: 7770)
-ERC1155Test:testFuzzBurnBatchSuccess(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 133574, ~: 133610)
-ERC1155Test:testFuzzBurnSuccess(address,address,uint256) (runs: 256, μ: 132453, ~: 132463)
+ERC1155Test:testFuzzBurnBatchSuccess(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 133576, ~: 133608)
+ERC1155Test:testFuzzBurnSuccess(address,address,uint256) (runs: 256, μ: 132443, ~: 132463)
 ERC1155Test:testFuzzRenounceOwnershipNonOwner(address) (runs: 256, μ: 14270, ~: 14270)
 ERC1155Test:testFuzzRenounceOwnershipSuccess(address) (runs: 256, μ: 49312, ~: 49296)
 ERC1155Test:testFuzzSafeBatchTransferFromByApprovedOperator(address,address,address,uint256,uint256,uint256,uint256,bytes) (runs: 256, μ: 226308, ~: 226270)
 ERC1155Test:testFuzzSafeBatchTransferFromEOAReceiver(address,address,uint256,uint256,uint256,uint256,bytes) (runs: 256, μ: 191164, ~: 191139)
 ERC1155Test:testFuzzSafeBatchTransferFromNoData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 572169, ~: 572179)
-ERC1155Test:testFuzzSafeBatchTransferFromWithData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 572692, ~: 572277)
+ERC1155Test:testFuzzSafeBatchTransferFromWithData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 572685, ~: 572277)
 ERC1155Test:testFuzzSafeMintBatchEOAReceiver(address,address,uint256,uint256,uint256,uint256,bytes) (runs: 256, μ: 153564, ~: 153557)
 ERC1155Test:testFuzzSafeMintBatchNoData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 528841, ~: 528851)
 ERC1155Test:testFuzzSafeMintBatchNonMinter(address) (runs: 256, μ: 41249, ~: 41249)
-ERC1155Test:testFuzzSafeMintBatchWithData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 529272, ~: 529055)
+ERC1155Test:testFuzzSafeMintBatchWithData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 529268, ~: 529055)
 ERC1155Test:testFuzzSafeMintEOAReceiver(address,address,uint256,uint256,uint256,uint256,bytes) (runs: 256, μ: 146764, ~: 146739)
 ERC1155Test:testFuzzSafeMintNoData(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 523047, ~: 523057)
 ERC1155Test:testFuzzSafeMintNonMinter(address) (runs: 256, μ: 33621, ~: 33621)
@@ -182,13 +182,13 @@ ERC1155Test:testFuzzSafeTransferFromNoData(address,uint256,uint256,uint256,uint2
 ERC1155Test:testFuzzSafeTransferFromWithData(address,uint256,uint256,uint256,uint256,bytes) (runs: 256, μ: 604586, ~: 603784)
 ERC1155Test:testFuzzSetApprovalForAllRevoke(address,address) (runs: 256, μ: 32083, ~: 32052)
 ERC1155Test:testFuzzSetApprovalForAllSuccess(address,address) (runs: 256, μ: 45488, ~: 45488)
-ERC1155Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16143, ~: 16161)
-ERC1155Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34559, ~: 34552)
+ERC1155Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16145, ~: 16161)
+ERC1155Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34561, ~: 34552)
 ERC1155Test:testFuzzSetUriNonMinter(address) (runs: 256, μ: 14606, ~: 14606)
-ERC1155Test:testFuzzTotalSupplyAfterBatchBurn(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 127511, ~: 127547)
-ERC1155Test:testFuzzTotalSupplyAfterBatchMint(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 142052, ~: 142006)
+ERC1155Test:testFuzzTotalSupplyAfterBatchBurn(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 127513, ~: 127545)
+ERC1155Test:testFuzzTotalSupplyAfterBatchMint(address,uint256,uint256,uint256,uint256) (runs: 256, μ: 142055, ~: 142006)
 ERC1155Test:testFuzzTotalSupplyAfterSingleBurn(address,uint256,bytes) (runs: 256, μ: 130287, ~: 130252)
-ERC1155Test:testFuzzTotalSupplyAfterSingleMint(uint256,uint256,bytes) (runs: 256, μ: 85895, ~: 114770)
+ERC1155Test:testFuzzTotalSupplyAfterSingleMint(uint256,uint256,bytes) (runs: 256, μ: 84962, ~: 84986)
 ERC1155Test:testFuzzTransferOwnershipNonOwner(address,address) (runs: 256, μ: 14366, ~: 14366)
 ERC1155Test:testFuzzTransferOwnershipSuccess(address,address) (runs: 256, μ: 76430, ~: 76400)
 ERC1155Test:testHasOwner() (gas: 10214)
@@ -262,8 +262,8 @@ ERC1155Test:testUriBaseAndTokenUriNotSet() (gas: 2954554)
 ERC1155Test:testUriBaseAndTokenUriSet() (gas: 65676)
 ERC1155Test:testUriNoBaseURI() (gas: 3004736)
 ERC1155Test:testUriNoTokenUri() (gas: 17533)
-ERC20Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3132)
-ERC20Invariants:invariantTotalSupply() (runs: 256, calls: 3840, reverts: 3132)
+ERC20Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3135)
+ERC20Invariants:invariantTotalSupply() (runs: 256, calls: 3840, reverts: 3135)
 ERC20Test:testApproveExceedingBalanceCase1() (gas: 40595)
 ERC20Test:testApproveExceedingBalanceCase2() (gas: 47293)
 ERC20Test:testApproveFromZeroAddress() (gas: 13278)
@@ -294,21 +294,21 @@ ERC20Test:testDecreaseAllowanceToZeroAddress() (gas: 19094)
 ERC20Test:testDomainSeparator() (gas: 11839)
 ERC20Test:testFuzzApproveSuccess(address,uint256) (runs: 256, μ: 39919, ~: 40775)
 ERC20Test:testFuzzBurnFromInsufficientAllowance(address,uint256,uint8) (runs: 256, μ: 180043, ~: 180222)
-ERC20Test:testFuzzBurnFromSuccess(address,uint256) (runs: 256, μ: 291042, ~: 291634)
+ERC20Test:testFuzzBurnFromSuccess(address,uint256) (runs: 256, μ: 291104, ~: 291634)
 ERC20Test:testFuzzBurnInvalidAmount(address,uint256) (runs: 256, μ: 16530, ~: 16530)
 ERC20Test:testFuzzBurnSuccessCase(uint256) (runs: 256, μ: 266128, ~: 265944)
 ERC20Test:testFuzzDecreaseAllowanceInvalidAmount(address,uint256,uint256) (runs: 256, μ: 41286, ~: 42997)
 ERC20Test:testFuzzDecreaseAllowanceSuccess(address,uint256,uint256) (runs: 256, μ: 47507, ~: 48025)
 ERC20Test:testFuzzDomainSeparator(uint8) (runs: 256, μ: 11916, ~: 11943)
 ERC20Test:testFuzzIncreaseAllowanceSuccess(address,uint256) (runs: 256, μ: 44377, ~: 44976)
-ERC20Test:testFuzzMintNonMinter(string,uint256) (runs: 256, μ: 13431, ~: 13448)
+ERC20Test:testFuzzMintNonMinter(string,uint256) (runs: 256, μ: 13435, ~: 13448)
 ERC20Test:testFuzzMintSuccess(string,uint256) (runs: 256, μ: 56114, ~: 56981)
-ERC20Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 46367, ~: 46398)
-ERC20Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 72181, ~: 72220)
+ERC20Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 46373, ~: 46404)
+ERC20Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 72188, ~: 72220)
 ERC20Test:testFuzzRenounceOwnershipNonOwner(address) (runs: 256, μ: 14203, ~: 14203)
-ERC20Test:testFuzzRenounceOwnershipSuccess(address) (runs: 256, μ: 49111, ~: 49150)
+ERC20Test:testFuzzRenounceOwnershipSuccess(address) (runs: 256, μ: 49166, ~: 49150)
 ERC20Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16083, ~: 16095)
-ERC20Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34433, ~: 34424)
+ERC20Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34432, ~: 34424)
 ERC20Test:testFuzzTransferFromInsufficientAllowance(address,address,uint256,uint8) (runs: 256, μ: 178255, ~: 178641)
 ERC20Test:testFuzzTransferFromSuccess(address,address,uint256) (runs: 256, μ: 207081, ~: 207955)
 ERC20Test:testFuzzTransferInvalidAmount(address,address,uint256) (runs: 256, μ: 16898, ~: 16898)
@@ -366,10 +366,10 @@ ERC4626VaultTest:testEmptyVaultDeposit() (gas: 567583)
 ERC4626VaultTest:testEmptyVaultMint() (gas: 568930)
 ERC4626VaultTest:testEmptyVaultRedeem() (gas: 205359)
 ERC4626VaultTest:testEmptyVaultwithdraw() (gas: 217329)
-ERC4626VaultTest:testFail_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 563152, ~: 565519)
-ERC4626VaultTest:testFail_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 564811, ~: 566995)
-ERC4626VaultTest:testFuzzDomainSeparator(uint8) (runs: 256, μ: 11783, ~: 11811)
-ERC4626VaultTest:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 46284, ~: 46310)
+ERC4626VaultTest:testFail_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 562627, ~: 565519)
+ERC4626VaultTest:testFail_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 564285, ~: 566995)
+ERC4626VaultTest:testFuzzDomainSeparator(uint8) (runs: 256, μ: 11782, ~: 11811)
+ERC4626VaultTest:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 46283, ~: 46312)
 ERC4626VaultTest:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 72496, ~: 72529)
 ERC4626VaultTest:testInitialSetup() (gas: 5780926)
 ERC4626VaultTest:testMintWithNoApproval() (gas: 22564)
@@ -389,30 +389,30 @@ ERC4626VaultTest:testVaultInteractionsForSomeoneElse() (gas: 227230)
 ERC4626VaultTest:testWithdrawInsufficientAllowance() (gas: 124243)
 ERC4626VaultTest:testWithdrawInsufficientAssets() (gas: 119518)
 ERC4626VaultTest:testWithdrawWithNoAssets() (gas: 21755)
-ERC4626VaultTest:test_RT_deposit_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 478669, ~: 480665)
-ERC4626VaultTest:test_RT_deposit_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480309, ~: 481858)
+ERC4626VaultTest:test_RT_deposit_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 478456, ~: 480665)
+ERC4626VaultTest:test_RT_deposit_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480096, ~: 481858)
 ERC4626VaultTest:test_RT_mint_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 478972, ~: 480777)
 ERC4626VaultTest:test_RT_mint_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480513, ~: 481935)
-ERC4626VaultTest:test_RT_redeem_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 479199, ~: 480698)
-ERC4626VaultTest:test_RT_redeem_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 479368, ~: 480808)
-ERC4626VaultTest:test_RT_withdraw_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480645, ~: 481871)
-ERC4626VaultTest:test_RT_withdraw_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 481087, ~: 482019)
-ERC4626VaultTest:test_asset((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430227, ~: 432832)
-ERC4626VaultTest:test_convertToAssets((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 436036, ~: 436916)
-ERC4626VaultTest:test_convertToShares((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 435886, ~: 436896)
-ERC4626VaultTest:test_deposit((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 476911, ~: 479542)
-ERC4626VaultTest:test_maxDeposit((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 429962, ~: 432568)
-ERC4626VaultTest:test_maxMint((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430042, ~: 432648)
-ERC4626VaultTest:test_maxRedeem((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430252, ~: 432857)
-ERC4626VaultTest:test_maxWithdraw((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 431487, ~: 434062)
-ERC4626VaultTest:test_mint((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 477390, ~: 479583)
-ERC4626VaultTest:test_previewDeposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 471319, ~: 473182)
+ERC4626VaultTest:test_RT_redeem_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 478923, ~: 480698)
+ERC4626VaultTest:test_RT_redeem_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 479092, ~: 480808)
+ERC4626VaultTest:test_RT_withdraw_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480432, ~: 481871)
+ERC4626VaultTest:test_RT_withdraw_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 480660, ~: 482019)
+ERC4626VaultTest:test_asset((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430440, ~: 432832)
+ERC4626VaultTest:test_convertToAssets((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 435609, ~: 436914)
+ERC4626VaultTest:test_convertToShares((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 435458, ~: 436896)
+ERC4626VaultTest:test_deposit((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 476706, ~: 479542)
+ERC4626VaultTest:test_maxDeposit((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430175, ~: 432568)
+ERC4626VaultTest:test_maxMint((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430255, ~: 432648)
+ERC4626VaultTest:test_maxRedeem((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430465, ~: 432857)
+ERC4626VaultTest:test_maxWithdraw((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 431700, ~: 434062)
+ERC4626VaultTest:test_mint((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 477184, ~: 479583)
+ERC4626VaultTest:test_previewDeposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 471105, ~: 473182)
 ERC4626VaultTest:test_previewMint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 471644, ~: 473316)
-ERC4626VaultTest:test_previewRedeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 473742, ~: 475054)
-ERC4626VaultTest:test_previewWithdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 475007, ~: 476263)
-ERC4626VaultTest:test_redeem((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 480148, ~: 481652)
-ERC4626VaultTest:test_totalAssets((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430422, ~: 433027)
-ERC4626VaultTest:test_withdraw((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 481287, ~: 482810)
+ERC4626VaultTest:test_previewRedeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 473529, ~: 475054)
+ERC4626VaultTest:test_previewWithdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 256, μ: 474794, ~: 476263)
+ERC4626VaultTest:test_redeem((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 479918, ~: 481652)
+ERC4626VaultTest:test_totalAssets((address[4],uint256[4],uint256[4],int256)) (runs: 256, μ: 430635, ~: 433027)
+ERC4626VaultTest:test_withdraw((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 256, μ: 481064, ~: 482810)
 ERC721Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3449)
 ERC721Invariants:invariantTotalSupply() (runs: 256, calls: 3840, reverts: 3449)
 ERC721Test:testApproveClearingApprovalWithNoPriorApproval() (gas: 177844)
@@ -440,20 +440,20 @@ ERC721Test:testFuzzApproveFromOperatorAddress(address,address,address) (runs: 25
 ERC721Test:testFuzzApproveWithNoPriorApproval(address,address) (runs: 256, μ: 197329, ~: 197339)
 ERC721Test:testFuzzApproveWithPriorApproval(address,address) (runs: 256, μ: 204594, ~: 204604)
 ERC721Test:testFuzzBurnSuccess(address) (runs: 256, μ: 306252, ~: 306244)
-ERC721Test:testFuzzDomainSeparator(uint8) (runs: 256, μ: 10074, ~: 10104)
+ERC721Test:testFuzzDomainSeparator(uint8) (runs: 256, μ: 10073, ~: 10104)
 ERC721Test:testFuzzGetApprovedApprovedTokenId(address,address) (runs: 256, μ: 194889, ~: 194899)
-ERC721Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 203496, ~: 203528)
-ERC721Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 229658, ~: 229698)
+ERC721Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 203493, ~: 203528)
+ERC721Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 229657, ~: 229698)
 ERC721Test:testFuzzRenounceOwnershipNonOwner(address) (runs: 256, μ: 14408, ~: 14408)
 ERC721Test:testFuzzRenounceOwnershipSuccess(address) (runs: 256, μ: 49498, ~: 49482)
 ERC721Test:testFuzzSafeMintNonMinter(address) (runs: 256, μ: 16197, ~: 16197)
-ERC721Test:testFuzzSafeMintSuccess(address[]) (runs: 256, μ: 21442712, ~: 21387096)
+ERC721Test:testFuzzSafeMintSuccess(address[]) (runs: 256, μ: 21442449, ~: 21387096)
 ERC721Test:testFuzzSafeTransferFromWithData(address,address,address,bytes) (runs: 256, μ: 1413017, ~: 1413721)
 ERC721Test:testFuzzSetApprovalForAllSuccess(address,address) (runs: 256, μ: 190821, ~: 190821)
-ERC721Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16191, ~: 16210)
-ERC721Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34778, ~: 34772)
-ERC721Test:testFuzzTokenByIndex(address,string[]) (runs: 256, μ: 24114869, ~: 24639072)
-ERC721Test:testFuzzTotalSupply(address,string[]) (runs: 256, μ: 23944247, ~: 24464036)
+ERC721Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16194, ~: 16210)
+ERC721Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34777, ~: 34772)
+ERC721Test:testFuzzTokenByIndex(address,string[]) (runs: 256, μ: 24113779, ~: 24639072)
+ERC721Test:testFuzzTotalSupply(address,string[]) (runs: 256, μ: 23943156, ~: 24464036)
 ERC721Test:testFuzzTransferFrom(address,address,address) (runs: 256, μ: 557408, ~: 557392)
 ERC721Test:testFuzzTransferOwnershipNonOwner(address,address) (runs: 256, μ: 14485, ~: 14485)
 ERC721Test:testFuzzTransferOwnershipSuccess(address,address) (runs: 256, μ: 77186, ~: 77167)
@@ -515,38 +515,44 @@ ERC721Test:testTransferFrom() (gas: 568289)
 ERC721Test:testTransferOwnershipNonOwner() (gas: 13015)
 ERC721Test:testTransferOwnershipSuccess() (gas: 55029)
 ERC721Test:testTransferOwnershipToZeroAddress() (gas: 16064)
-MathTest:testCbrtRoundDown() (gas: 48611)
-MathTest:testCbrtRoundUp() (gas: 49315)
-MathTest:testCeilDiv() (gas: 12862)
-MathTest:testFuzzCbrt(uint256,bool) (runs: 256, μ: 19758, ~: 19455)
-MathTest:testFuzzCeilDiv(uint256,uint256) (runs: 256, μ: 9026, ~: 9043)
-MathTest:testFuzzInt256Average(int256,int256) (runs: 256, μ: 5970, ~: 5970)
-MathTest:testFuzzIsNegative(int256) (runs: 256, μ: 5799, ~: 5799)
-MathTest:testFuzzLog10(uint256,bool) (runs: 256, μ: 7135, ~: 7362)
-MathTest:testFuzzLog2(uint256,bool) (runs: 256, μ: 7045, ~: 7169)
-MathTest:testFuzzLog256(uint256,bool) (runs: 256, μ: 7100, ~: 7322)
-MathTest:testFuzzMulDiv(uint256,uint256,uint256) (runs: 256, μ: 12956, ~: 12706)
-MathTest:testFuzzMulDivDomain(uint256,uint256,uint256) (runs: 256, μ: 10425, ~: 10500)
-MathTest:testFuzzUint256Average(uint256,uint256) (runs: 256, μ: 5778, ~: 5778)
-MathTest:testFuzzWadCbrt(uint256) (runs: 256, μ: 19477, ~: 19153)
-MathTest:testInt256Average() (gas: 12459)
-MathTest:testIsNegative() (gas: 11618)
-MathTest:testLog10RoundDown() (gas: 19030)
-MathTest:testLog10RoundUp() (gas: 20097)
-MathTest:testLog256RoundDown() (gas: 15983)
-MathTest:testLog256RoundUp() (gas: 16771)
-MathTest:testLog2RoundDown() (gas: 18022)
-MathTest:testLog2RoundUp() (gas: 18857)
-MathTest:testMulDivDivisionByZero() (gas: 10325)
-MathTest:testMulDivOverflow() (gas: 10659)
-MathTest:testMulDivRoundDownLargeValues() (gas: 12614)
-MathTest:testMulDivRoundDownSmallValues() (gas: 7597)
-MathTest:testMulDivRoundUpLargeValues() (gas: 12742)
-MathTest:testMulDivRoundUpSmallValues() (gas: 7839)
-MathTest:testUint256Average() (gas: 8359)
-MathTest:testWadCbrt() (gas: 46895)
-MerkleProofVerificationTest:testFuzzMultiProofVerifySingleLeaf(bytes32[],uint256) (runs: 256, μ: 1649977823, ~: 1649974519)
-MerkleProofVerificationTest:testFuzzVerify(bytes32[],uint256) (runs: 256, μ: 135976478, ~: 135973145)
+MathTest:testCbrtRoundDown() (gas: 46452)
+MathTest:testCbrtRoundUp() (gas: 47200)
+MathTest:testCeilDiv() (gas: 12728)
+MathTest:testFuzzCbrt(uint256,bool) (runs: 256, μ: 19624, ~: 19287)
+MathTest:testFuzzCeilDiv(uint256,uint256) (runs: 256, μ: 9025, ~: 9042)
+MathTest:testFuzzInt256Average(int256,int256) (runs: 256, μ: 5598, ~: 5598)
+MathTest:testFuzzIsNegative(int256) (runs: 256, μ: 5705, ~: 5705)
+MathTest:testFuzzLog10(uint256,bool) (runs: 256, μ: 7195, ~: 7419)
+MathTest:testFuzzLog2(uint256,bool) (runs: 256, μ: 7126, ~: 7256)
+MathTest:testFuzzLog256(uint256,bool) (runs: 256, μ: 7183, ~: 7401)
+MathTest:testFuzzMulDiv(uint256,uint256,uint256) (runs: 256, μ: 13052, ~: 12802)
+MathTest:testFuzzMulDivDomain(uint256,uint256,uint256) (runs: 256, μ: 10652, ~: 10728)
+MathTest:testFuzzUint256Average(uint256,uint256) (runs: 256, μ: 5782, ~: 5782)
+MathTest:testFuzzWadCbrt(uint256) (runs: 256, μ: 19322, ~: 19019)
+MathTest:testFuzzWadExp(int256) (runs: 256, μ: 15473, ~: 15578)
+MathTest:testFuzzWadLn(int256) (runs: 256, μ: 17111, ~: 16739)
+MathTest:testInt256Average() (gas: 12140)
+MathTest:testIsNegative() (gas: 10674)
+MathTest:testLog10RoundDown() (gas: 19351)
+MathTest:testLog10RoundUp() (gas: 20396)
+MathTest:testLog256RoundDown() (gas: 16126)
+MathTest:testLog256RoundUp() (gas: 17045)
+MathTest:testLog2RoundDown() (gas: 19000)
+MathTest:testLog2RoundUp() (gas: 19813)
+MathTest:testMulDivDivisionByZero() (gas: 10561)
+MathTest:testMulDivOverflow() (gas: 10906)
+MathTest:testMulDivRoundDownLargeValues() (gas: 13007)
+MathTest:testMulDivRoundDownSmallValues() (gas: 7917)
+MathTest:testMulDivRoundUpLargeValues() (gas: 13224)
+MathTest:testMulDivRoundUpSmallValues() (gas: 8115)
+MathTest:testUint256Average() (gas: 8288)
+MathTest:testWadCbrt() (gas: 44758)
+MathTest:testWadExp() (gas: 28737)
+MathTest:testWadExpOverflow() (gas: 10691)
+MathTest:testWadLn() (gas: 28106)
+MathTest:testWadLnNegativeValues() (gas: 10583)
+MerkleProofVerificationTest:testFuzzMultiProofVerifySingleLeaf(bytes32[],uint256) (runs: 256, μ: 1649977830, ~: 1649974648)
+MerkleProofVerificationTest:testFuzzVerify(bytes32[],uint256) (runs: 256, μ: 135976486, ~: 135973274)
 MerkleProofVerificationTest:testFuzzVerifyMultiProofMultipleLeaves(bool,bool,bool) (runs: 256, μ: 412472500, ~: 412472432)
 MerkleProofVerificationTest:testInvalidMerkleMultiProof() (gas: 412478563)
 MerkleProofVerificationTest:testInvalidMerkleProof() (gas: 33970453)
@@ -610,8 +616,8 @@ SignatureCheckerTest:testEOAWithInvalidSignature2() (gas: 20267)
 SignatureCheckerTest:testEOAWithInvalidSigner() (gas: 20496)
 SignatureCheckerTest:testEOAWithTooHighSValue() (gas: 17256)
 SignatureCheckerTest:testEOAWithValidSignature() (gas: 19591)
-SignatureCheckerTest:testFuzzEIP1271WithInvalidSigner(string,string) (runs: 256, μ: 35896, ~: 35902)
+SignatureCheckerTest:testFuzzEIP1271WithInvalidSigner(string,string) (runs: 256, μ: 35892, ~: 35902)
 SignatureCheckerTest:testFuzzEIP1271WithValidSignature(string) (runs: 256, μ: 34429, ~: 34431)
 SignatureCheckerTest:testFuzzEOAWithInvalidSignature(bytes,string) (runs: 256, μ: 16901, ~: 16896)
-SignatureCheckerTest:testFuzzEOAWithInvalidSigner(string,string) (runs: 256, μ: 21577, ~: 21583)
-SignatureCheckerTest:testFuzzEOAWithValidSignature(string,string) (runs: 256, μ: 20688, ~: 20694)
\ No newline at end of file
+SignatureCheckerTest:testFuzzEOAWithInvalidSigner(string,string) (runs: 256, μ: 21574, ~: 21583)
+SignatureCheckerTest:testFuzzEOAWithValidSignature(string,string) (runs: 256, μ: 20685, ~: 20694)
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 687815b4..77490818 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -19,3 +19,9 @@
 [submodule "lib/erc4626-tests"]
 	path = lib/erc4626-tests
 	url = https://github.com/a16z/erc4626-tests.git
+[submodule "lib/solady"]
+	path = lib/solady
+	url = https://github.com/vectorized/solady.git
+[submodule "lib/solmate"]
+	path = lib/solmate
+	url = https://github.com/transmissions11/solmate
diff --git a/.prettierignore b/.prettierignore
index 7e97ca09..12234ceb 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,5 +1,7 @@
 node_modules
 lib/murky
+lib/solady
+lib/solmate
 lib/prb-test
 lib/forge-std
 lib/create-util
diff --git a/.solhintignore b/.solhintignore
index 7e97ca09..12234ceb 100644
--- a/.solhintignore
+++ b/.solhintignore
@@ -1,5 +1,7 @@
 node_modules
 lib/murky
+lib/solady
+lib/solmate
 lib/prb-test
 lib/forge-std
 lib/create-util
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18e5016a..436c7217 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,27 +1,39 @@
 # 🕓 Changelog
 
+## `0.0.2` (Unreleased)
+
+### 💥 New Features
+
+- **Utility Functions**
+  - [`Math`](https://github.com/pcaversaccio/snekmate/blob/v0.0.2/src/utils/Math.vy): Add `wad_ln` and `wad_exp` to the standard mathematical utility functions. ([#91](https://github.com/pcaversaccio/snekmate/pull/91))
+
+### ♻️ Refactoring
+
+- **Utility Functions**
+  - [`Math`](https://github.com/pcaversaccio/snekmate/blob/v0.0.2/src/utils/Math.vy): Use directly 🐍 snekmate's [`log_2`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/Math.vy#L202) function in the internal calculation of `wad_cbrt`. ([#91](https://github.com/pcaversaccio/snekmate/pull/91))
+
 ## `0.0.1` (06-03-2023)
 
-### New Features
+### 💥 New Features
 
 - **Authentication**
-  - `Ownable`: Owner-based access control functions.
-  - `Ownable2Step`: 2-step ownership transfer functions.
-  - `AccessControl`: Multi-role-based access control functions.
+  - [`Ownable`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/auth/Ownable.vy): Owner-based access control functions.
+  - [`Ownable2Step`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/auth/Ownable2Step.vy): 2-step ownership transfer functions.
+  - [`AccessControl`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/auth/AccessControl.vy): Multi-role-based access control functions.
 - **Extensions**
-  - `ERC4626`: Modern and gas-efficient ERC-4626 tokenised vault implementation. ([#74](https://github.com/pcaversaccio/snekmate/pull/74))
+  - [`ERC4626`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/extensions/ERC4626.vy): Modern and gas-efficient ERC-4626 tokenised vault implementation. ([#74](https://github.com/pcaversaccio/snekmate/pull/74))
 - **Tokens**
-  - `ERC20`: Modern and gas-efficient ERC-20 + EIP-2612 implementation. ([#17](https://github.com/pcaversaccio/snekmate/pull/17))
-  - `ERC721`: Modern and gas-efficient ERC-721 + EIP-4494 implementation. ([#20](https://github.com/pcaversaccio/snekmate/pull/20))
-  - `ERC1155`: Modern and gas-efficient ERC-1155 implementation. ([#31](https://github.com/pcaversaccio/snekmate/pull/31))
+  - [`ERC20`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/tokens/ERC20.vy): Modern and gas-efficient ERC-20 + EIP-2612 implementation. ([#17](https://github.com/pcaversaccio/snekmate/pull/17))
+  - [`ERC721`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/tokens/ERC721.vy): Modern and gas-efficient ERC-721 + EIP-4494 implementation. ([#20](https://github.com/pcaversaccio/snekmate/pull/20))
+  - [`ERC1155`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/tokens/ERC1155.vy): Modern and gas-efficient ERC-1155 implementation. ([#31](https://github.com/pcaversaccio/snekmate/pull/31))
 - **Utility Functions**
-  - `Base64`: Base64 encoding and decoding functions. ([#47](https://github.com/pcaversaccio/snekmate/pull/47))
-  - `BatchDistributor`: Batch sending both native and ERC-20 tokens.
-  - `CreateAddress`: `CREATE` EVM opcode utility function for address calculation.
-  - `Create2Address`: `CREATE2` EVM opcode utility functions for address calculations.
-  - `ECDSA`: Elliptic curve digital signature algorithm (ECDSA) functions.
-  - `SignatureChecker`: ECDSA and EIP-1271 signature verification functions.
-  - `EIP712DomainSeparator`: EIP-712 domain separator.
-  - `Math`: Standard mathematical utility functions. ([#74](https://github.com/pcaversaccio/snekmate/pull/74), [#77](https://github.com/pcaversaccio/snekmate/pull/77), [#86](https://github.com/pcaversaccio/snekmate/pull/86))
-  - `MerkleProofVerification`: Merkle tree proof verification functions. ([#30](https://github.com/pcaversaccio/snekmate/pull/30))
-  - `Multicall`: Multicall functions.
+  - [`Base64`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/Base64.vy): Base64 encoding and decoding functions. ([#47](https://github.com/pcaversaccio/snekmate/pull/47))
+  - [`BatchDistributor`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/BatchDistributor.vy): Batch sending both native and ERC-20 tokens.
+  - [`CreateAddress`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/CreateAddress.vy): `CREATE` EVM opcode utility function for address calculation.
+  - [`Create2Address`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/Create2Address.vy): `CREATE2` EVM opcode utility functions for address calculations.
+  - [`ECDSA`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/ECDSA.vy): Elliptic curve digital signature algorithm (ECDSA) functions.
+  - [`SignatureChecker`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/SignatureChecker.vy): ECDSA and EIP-1271 signature verification functions.
+  - [`EIP712DomainSeparator`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/EIP712DomainSeparator.vy): EIP-712 domain separator.
+  - [`Math`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/Math.vy): Standard mathematical utility functions. ([#74](https://github.com/pcaversaccio/snekmate/pull/74), [#77](https://github.com/pcaversaccio/snekmate/pull/77), [#86](https://github.com/pcaversaccio/snekmate/pull/86))
+  - [`MerkleProofVerification`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/MerkleProofVerification.vy): Merkle tree proof verification functions. ([#30](https://github.com/pcaversaccio/snekmate/pull/30))
+  - [`Multicall`](https://github.com/pcaversaccio/snekmate/blob/v0.0.1/src/utils/Multicall.vy): Multicall functions.
diff --git a/lib/forge-std b/lib/forge-std
index dc1901fa..73a504d2 160000
--- a/lib/forge-std
+++ b/lib/forge-std
@@ -1,1 +1,1 @@
-Subproject commit dc1901fa900fc2ceabb4aae91d8a3d6c0c2e0392
+Subproject commit 73a504d2cf6f37b7ce285b479f4c681f76e95f1b
diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts
index 4fb6833e..f8e3c375 160000
--- a/lib/openzeppelin-contracts
+++ b/lib/openzeppelin-contracts
@@ -1,1 +1,1 @@
-Subproject commit 4fb6833e325658946c2185862b8e57e32f3683bc
+Subproject commit f8e3c375d19bd12f54222109dd0801c0e0b60dd2
diff --git a/lib/solady b/lib/solady
new file mode 160000
index 00000000..2d0251dc
--- /dev/null
+++ b/lib/solady
@@ -0,0 +1,1 @@
+Subproject commit 2d0251dc8e55a127df1bc8619fe51e48f5ecb56e
diff --git a/lib/solmate b/lib/solmate
new file mode 160000
index 00000000..1b3adf67
--- /dev/null
+++ b/lib/solmate
@@ -0,0 +1,1 @@
+Subproject commit 1b3adf677e7e383cc684b5d5bd441da86bf4bf1c
diff --git a/remappings.txt b/remappings.txt
index edddc5cc..9b6bb5c2 100644
--- a/remappings.txt
+++ b/remappings.txt
@@ -1,5 +1,7 @@
 utils/=lib/utils/
 murky/=lib/murky/src/
+solady/=lib/solady/src/
+solmate/=lib/solmate/src/
 prb/test/=lib/prb-test/src/
 forge-std/=lib/forge-std/src/
 erc4626-tests/=lib/erc4626-tests/
diff --git a/src/utils/Math.vy b/src/utils/Math.vy
index bbaf5fa0..467cb1d3 100644
--- a/src/utils/Math.vy
+++ b/src/utils/Math.vy
@@ -7,7 +7,22 @@
 @notice These functions implement standard mathematical utility
         functions that are missing in the Vyper language. If a
         function is inspired by an existing implementation, it
-        is properly referenced in the function docstring.
+        is properly referenced in the function docstring. The
+        following functions have been added for convenience:
+        - `uint256_average` (`external` `pure` function),
+        - `int256_average` (`external` `pure` function),
+        - `ceil_div` (`external` `pure` function),
+        - `is_negative` (`external` `pure` function),
+        - `mul_div` (`external` `pure` function),
+        - `log_2` (`external` `pure` function),
+        - `log_10` (`external` `pure` function),
+        - `log_256` (`external` `pure` function),
+        - `wad_ln` (`external` `pure` function),
+        - `wad_exp` (`external` `pure` function),
+        - `cbrt` (`external` `pure` function),
+        - `wad_cbrt` (`external` `pure` function),
+        - `_log_2` (`internal` `pure` function),
+        - `_wad_cbrt` (`internal` `pure` function).
 """
 
 
@@ -22,6 +37,71 @@ def __init__():
     pass
 
 
+@external
+@pure
+def uint256_average(x: uint256, y: uint256) -> uint256:
+    """
+    @dev Returns the average of two 32-byte unsigned integers.
+    @notice Note that the result is rounded towards zero. For
+            more details on finding the average of two unsigned
+            integers without an overflow, please refer to:
+            https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223.
+    @param x The first 32-byte unsigned integer of the data set.
+    @param y The second 32-byte unsigned integer of the data set.
+    @return uint256 The 32-byte average (rounded towards zero) of
+            `x` and `y`.
+    """
+    return unsafe_add(x & y, shift(x ^ y, -1))
+
+
+@external
+@pure
+def int256_average(x: int256, y: int256) -> int256:
+    """
+    @dev Returns the average of two 32-byte signed integers.
+    @notice Note that the result is rounded towards infinity.
+            For more details on finding the average of two signed
+            integers without an overflow, please refer to:
+            https://patents.google.com/patent/US6007232A/en.
+    @param x The first 32-byte signed integer of the data set.
+    @param y The second 32-byte signed integer of the data set.
+    @return int256 The 32-byte average (rounded towards infinity)
+            of `x` and `y`.
+    """
+    return unsafe_add(unsafe_add(shift(x, -1), shift(y, -1)), x & y & 1)
+
+
+@external
+@pure
+def ceil_div(x: uint256, y: uint256) -> uint256:
+    """
+    @dev Calculates "ceil(x / y)" for any strictly positive `y`.
+    @notice The implementation is inspired by OpenZeppelin's
+            implementation here:
+            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
+    @param x The 32-byte numerator.
+    @param y The 32-byte denominator.
+    @return uint256 The 32-byte rounded up result of "x/y".
+    """
+    assert y != empty(uint256), "Math: ceil_div division by zero"
+    if (x == empty(uint256)):
+        return empty(uint256)
+    else:
+        return unsafe_add(unsafe_div(x - 1, y), 1)
+
+
+@external
+@pure
+def is_negative(x: int256) -> bool:
+    """
+    @dev Returns `True` if a 32-byte signed integer is negative.
+    @notice Note that this function returns `False` for 0.
+    @param x The 32-byte signed integer variable.
+    @return bool The verification whether `x` is negative or not.
+    """
+    return (x ^ 1 < empty(int256))
+
+
 @external
 @pure
 def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint256:
@@ -144,59 +224,6 @@ def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint
     return result
 
 
-@external
-@pure
-def uint256_average(x: uint256, y: uint256) -> uint256:
-    """
-    @dev Returns the average of two 32-byte unsigned integers.
-    @notice Note that the result is rounded towards zero. For
-            more details on finding the average of two unsigned
-            integers without an overflow, please refer to:
-            https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223.
-    @param x The first 32-byte unsigned integer of the data set.
-    @param y The second 32-byte unsigned integer of the data set.
-    @return uint256 The 32-byte average (rounded towards zero) of
-            `x` and `y`.
-    """
-    return unsafe_add(x & y, shift(x ^ y, -1))
-
-
-@external
-@pure
-def int256_average(x: int256, y: int256) -> int256:
-    """
-    @dev Returns the average of two 32-byte signed integers.
-    @notice Note that the result is rounded towards infinity.
-            For more details on finding the average of two signed
-            integers without an overflow, please refer to:
-            https://patents.google.com/patent/US6007232A/en.
-    @param x The first 32-byte signed integer of the data set.
-    @param y The second 32-byte signed integer of the data set.
-    @return uint256 The 32-byte average (rounded towards infinity)
-            of `x` and `y`.
-    """
-    return unsafe_add(unsafe_add(shift(x, -1), shift(y, -1)), x & y & 1)
-
-
-@external
-@pure
-def ceil_div(x: uint256, y: uint256) -> uint256:
-    """
-    @dev Calculates "ceil(x / y)" for any strictly positive `y`.
-    @notice The implementation is inspired by OpenZeppelin's
-            implementation here:
-            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
-    @param x The 32-byte numerator.
-    @param y The 32-byte denominator.
-    @return uint256 The 32-byte rounded up result of "x/y".
-    """
-    assert y != empty(uint256), "Math: ceil_div division by zero"
-    if (x == empty(uint256)):
-        return empty(uint256)
-    else:
-        return unsafe_add(unsafe_div(x - 1, y), 1)
-
-
 @external
 @pure
 def log_2(x: uint256, roundup: bool) -> uint256:
@@ -211,44 +238,12 @@ def log_2(x: uint256, roundup: bool) -> uint256:
            to round up or not. The default `False` is round down.
     @return uint256 The 32-byte calculation result.
     """
-    value: uint256 = x
-    result: uint256 = empty(uint256)
-
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
     if (x == empty(uint256)):
-        # For the special case `x == 0` we already return 0 here in order
-        # not to iterate through the remaining code.
         return empty(uint256)
 
-    # The following lines cannot overflow because we have the well-known
-    # decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`.
-    if (shift(x, -128) != empty(uint256)):
-        value = shift(x, -128)
-        result = 128
-    if (shift(value, -64) != empty(uint256)):
-        value = shift(value, -64)
-        result = unsafe_add(result, 64)
-    if (shift(value, -32) != empty(uint256)):
-        value = shift(value, -32)
-        result = unsafe_add(result, 32)
-    if (shift(value, -16) != empty(uint256)):
-        value = shift(value, -16)
-        result = unsafe_add(result, 16)
-    if (shift(value, -8) != empty(uint256)):
-        value = shift(value, -8)
-        result = unsafe_add(result, 8)
-    if (shift(value, -4) != empty(uint256)):
-        value = shift(value, -4)
-        result = unsafe_add(result, 4)
-    if (shift(value, -2) != empty(uint256)):
-        value = shift(value, -2)
-        result = unsafe_add(result, 2)
-    if (shift(value, -1) != empty(uint256)):
-        result = unsafe_add(result, 1)
-
-    if (roundup and (shift(1, convert(result, int256)) < x)):
-        result = unsafe_add(result, 1)
-
-    return result
+    return self._log_2(x, roundup)
 
 
 @external
@@ -268,9 +263,9 @@ def log_10(x: uint256, roundup: bool) -> uint256:
     value: uint256 = x
     result: uint256 = empty(uint256)
 
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
     if (x == empty(uint256)):
-        # For the special case `x == 0` we already return 0 here in order
-        # not to iterate through the remaining code.
         return empty(uint256)
 
     # The following lines cannot overflow because we have the well-known
@@ -321,9 +316,9 @@ def log_256(x: uint256, roundup: bool) -> uint256:
     value: uint256 = x
     result: uint256 = empty(uint256)
 
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
     if (x == empty(uint256)):
-        # For the special case `x == 0` we already return 0 here in order
-        # not to iterate through the remaining code.
         return empty(uint256)
 
     # The following lines cannot overflow because we have the well-known
@@ -350,11 +345,151 @@ def log_256(x: uint256, roundup: bool) -> uint256:
 
 
 @external
-@view
+@pure
+def wad_ln(x: int256) -> int256:
+    """
+    @dev Calculates the natural logarithm of a signed integer with a
+         precision of 1e18.
+    @notice Note that it returns 0 if given 0. Furthermore, this function
+            consumes about 1,400 to 1,650 gas units depending on the value
+            of `x`. The implementation is inspired by Remco Bloemen's
+            implementation under the MIT license here:
+            https://xn--2-umb.com/22/exp-ln.
+    @param x The 32-byte variable.
+    @return int256 The 32-byte calculation result.
+    """
+    value: int256 = x
+
+    assert x >= empty(int256), "Math: wad_ln undefined"
+
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
+    if (x == empty(int256)):
+        return empty(int256)
+
+    # We want to convert `x` from "10 ** 18" fixed point to "2 ** 96"
+    # fixed point. We do this by multiplying by "2 ** 96 / 10 ** 18".
+    # But since "ln(x * C) = ln(x) + ln(C)" holds, we can just do nothing
+    # here and add "ln(2 ** 96 / 10 ** 18)" at the end.
+
+    # Reduce the range of `x` to "(1, 2) * 2 ** 96".
+    # Also remember that "ln(2 ** k * x) = k * ln(2) + ln(x)" holds.
+    k: int256 = unsafe_sub(convert(self._log_2(convert(x, uint256), False), int256), 96)
+    # Note that to circumvent Vyper's safecast feature for the potentially
+    # negative expression `value <<= uint256(159 - k)`, we first convert the
+    # expression `value <<= uint256(159 - k)` to `bytes32` and subsequently
+    # to `uint256`. Remember that the EVM default behaviour is to use two's
+    # complement representation to handle signed integers.
+    value = convert(shift(convert(convert(shift(value, unsafe_sub(159, k)), bytes32), uint256), -159), int256)
+
+    # Evaluate using a "(8, 8)"-term rational approximation. Since `p` is monic,
+    # we will multiply by a scaling factor later.
+    p: int256 = unsafe_add(shift(unsafe_mul(unsafe_add(value, 3273285459638523848632254066296), value), -96), 24828157081833163892658089445524)
+    p = unsafe_add(shift(unsafe_mul(p, value), -96), 43456485725739037958740375743393)
+    p = unsafe_sub(shift(unsafe_mul(p, value), -96), 11111509109440967052023855526967)
+    p = unsafe_sub(shift(unsafe_mul(p, value), -96), 45023709667254063763336534515857)
+    p = unsafe_sub(shift(unsafe_mul(p, value), -96), 14706773417378608786704636184526)
+    p = unsafe_sub(unsafe_mul(p, value), shift(795164235651350426258249787498, 96))
+
+    # We leave `p` in the "2 ** 192" base so that we do not have to scale it up
+    # again for the division. Note that `q` is monic by convention.
+    q: int256 = unsafe_add(shift(unsafe_mul(unsafe_add(value, 5573035233440673466300451813936), value), -96), 71694874799317883764090561454958)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 283447036172924575727196451306956)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 401686690394027663651624208769553)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 204048457590392012362485061816622)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 31853899698501571402653359427138)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 909429971244387300277376558375)
+
+    # It is known that the polynomial `q` has no zeros in the domain.
+    # No scaling is required, as `p` is already "2 ** 96" too large. Also,
+    # `r` is in the range "(0, 0.125) * 2 ** 96" after the division.
+    r: int256 = unsafe_div(p, q)
+
+    # To finalise the calculation, we have to proceed with the following steps:
+    #   - multiply by the scaling factor "s = 5.549...",
+    #   - add "ln(2 ** 96 / 10 ** 18)",
+    #   - add "k * ln(2)", and
+    #   - multiply by "10 ** 18 / 2 ** 96 = 5 ** 18 >> 78".
+    # In order to perform the most gas-efficient calculation, we carry out all
+    # these steps in one expression.
+    return shift(unsafe_add(unsafe_add(unsafe_mul(r, 1677202110996718588342820967067443963516166),\
+                 unsafe_mul(k, 16597577552685614221487285958193947469193820559219878177908093499208371)),\
+                 600920179829731861736702779321621459595472258049074101567377883020018308), -174)
+
+
+@external
+@pure
+def wad_exp(x: int256) -> int256:
+    """
+    @dev Calculates the natural exponential function of a signed integer with
+         a precision of 1e18.
+    @notice Note that this function consumes about 810 gas units. The implementation
+            is inspired by Remco Bloemen's implementation under the MIT license here:
+            https://xn--2-umb.com/22/exp-ln.
+    @param x The 32-byte variable.
+    @return int256 The 32-byte calculation result.
+    """
+    value: int256 = x
+
+    # If the result is `< 0.5`, we return zero. This happens when we have the following:
+    # "x <= floor(log(0.5e18) * 1e18) ~ -42e18".
+    if (x <= -42139678854452767551):
+        return empty(int256)
+
+    # When the result is "> (2 ** 255 - 1) / 1e18" we cannot represent it as a signed integer.
+    # This happens when "x >= floor(log((2 ** 255 - 1) / 1e18) * 1e18) ~ 135".
+    assert x < 135305999368893231589, "Math: wad_exp overflow"
+
+    # `x` is now in the range "(-42, 136) * 1e18". Convert to "(-42, 136) * 2 ** 96" for higher
+    # intermediate precision and a binary base. This base conversion is a multiplication with
+    # "1e18 / 2 ** 96 = 5 ** 18 / 2 ** 78".
+    value = unsafe_div(shift(x, 78), 5 ** 18)
+
+    # Reduce the range of `x` to "(-½ ln 2, ½ ln 2) * 2 ** 96" by factoring out powers of two
+    # so that "exp(x) = exp(x') * 2 ** k", where `k` is a signer integer. Solving this gives
+    # "k = round(x / log(2))" and "x' = x - k * log(2)". Thus, `k` is in the range "[-61, 195]".
+    k: int256 = shift(unsafe_add(unsafe_div(shift(value, 96), 54916777467707473351141471128), 2 ** 95), -96)
+    value = unsafe_sub(value, unsafe_mul(k, 54916777467707473351141471128))
+
+    # Evaluate using a "(6, 7)"-term rational approximation. Since `p` is monic,
+    # we will multiply by a scaling factor later.
+    y: int256 = unsafe_add(shift(unsafe_mul(unsafe_add(value, 1346386616545796478920950773328), value), -96), 57155421227552351082224309758442)
+    p: int256 = unsafe_add(unsafe_mul(unsafe_add(shift(unsafe_mul(unsafe_sub(unsafe_add(y, value), 94201549194550492254356042504812), y), -96),\
+                           28719021644029726153956944680412240), value), shift(4385272521454847904659076985693276, 96))
+
+    # We leave `p` in the "2 ** 192" base so that we do not have to scale it up
+    # again for the division.
+    q: int256 = unsafe_add(shift(unsafe_mul(unsafe_sub(value, 2855989394907223263936484059900), value), -96), 50020603652535783019961831881945)
+    q = unsafe_sub(shift(unsafe_mul(q, value), -96), 533845033583426703283633433725380)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 3604857256930695427073651918091429)
+    q = unsafe_sub(shift(unsafe_mul(q, value), -96), 14423608567350463180887372962807573)
+    q = unsafe_add(shift(unsafe_mul(q, value), -96), 26449188498355588339934803723976023)
+
+    # The polynomial `q` has no zeros in the range because all its roots are complex.
+    # No scaling is required, as `p` is already "2 ** 96" too large. Also,
+    # `r` is in the range "(0.09, 0.25) * 2**96" after the division.
+    r: int256 = unsafe_div(p, q)
+
+    # To finalise the calculation, we have to multiply `r` by:
+    #   - the scale factor "s = ~6.031367120",
+    #   - the factor "2 ** k" from the range reduction, and
+    #   - the factor "1e18 / 2 ** 96" for the base conversion.
+    # We do this all at once, with an intermediate result in "2**213" base,
+    # so that the final right shift always gives a positive value.
+
+    # Note that to circumvent Vyper's safecast feature for the potentially
+    # negative parameter value `r`, we first convert `r` to `bytes32` and
+    # subsequently to `uint256`. Remember that the EVM default behaviour is
+    # to use two's complement representation to handle signed integers.
+    return convert(shift(unsafe_mul(convert(convert(r, bytes32), uint256), 3822833074963236453042738258902158003155416615667), -unsafe_sub(195, k)), int256)
+
+
+@external
+@pure
 def cbrt(x: uint256, roundup: bool) -> uint256:
     """
     @dev Calculates the cube root of an unsigned integer.
-    @notice Note that this function consumes about 1,950 to 2,050 gas units
+    @notice Note that this function consumes about 1,600 to 1,800 gas units
             depending on the value of `x` and `roundup`. The implementation is
             inspired by Curve Finance's implementation under the MIT license here:
             https://github.com/curvefi/tricrypto-ng/blob/main/contracts/CurveCryptoMathOptimized3.vy.
@@ -363,9 +498,9 @@ def cbrt(x: uint256, roundup: bool) -> uint256:
            to round up or not. The default `False` is round down.
     @return The 32-byte cube root of `x`.
     """
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
     if (x == empty(uint256)):
-        # For the special case `x == 0` we already return 0 here in order
-        # not to iterate through the remaining code.
         return empty(uint256)
 
     y: uint256 = unsafe_div(self._wad_cbrt(x), 10 ** 12)
@@ -382,31 +517,68 @@ def wad_cbrt(x: uint256) -> uint256:
     """
     @dev Calculates the cube root of an unsigned integer with a precision
          of 1e18.
-    @notice Note that this function consumes about 1,850 to 1,950 gas units
+    @notice Note that this function consumes about 1,500 to 1,700 gas units
             depending on the value of `x`. The implementation is inspired
             by Curve Finance's implementation under the MIT license here:
             https://github.com/curvefi/tricrypto-ng/blob/main/contracts/CurveCryptoMathOptimized3.vy.
     @param x The 32-byte variable from which the cube root is calculated.
     @return The 32-byte cubic root of `x` with a precision of 1e18.
     """
+    # For the special case `x == 0` we already return 0 here in order
+    # not to iterate through the remaining code.
     if (x == empty(uint256)):
-        # For the special case `x == 0` we already return 0 here in order
-        # not to iterate through the remaining code.
         return empty(uint256)
 
     return self._wad_cbrt(x)
 
 
-@external
+@internal
 @pure
-def is_negative(x: int256) -> bool:
+def _log_2(x: uint256, roundup: bool) -> uint256:
     """
-    @dev Returns `True` if a 32-byte signed integer is negative.
-    @notice Note that this function returns `False` for 0.
-    @param x The 32-byte signed integer variable.
-    @return bool The verification whether `x` is negative or not.
+    @dev An `internal` helper function that returns the log in base 2
+         of `x`, following the selected rounding direction.
+    @notice Note that it returns 0 if given 0. The implementation is
+            inspired by OpenZeppelin's implementation here:
+            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
+    @param x The 32-byte variable.
+    @param roundup The Boolean variable that specifies whether
+           to round up or not. The default `False` is round down.
+    @return uint256 The 32-byte calculation result.
     """
-    return (x ^ 1 < empty(int256))
+    value: uint256 = x
+    result: uint256 = empty(uint256)
+
+    # The following lines cannot overflow because we have the well-known
+    # decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`.
+    if (shift(x, -128) != empty(uint256)):
+        value = shift(x, -128)
+        result = 128
+    if (shift(value, -64) != empty(uint256)):
+        value = shift(value, -64)
+        result = unsafe_add(result, 64)
+    if (shift(value, -32) != empty(uint256)):
+        value = shift(value, -32)
+        result = unsafe_add(result, 32)
+    if (shift(value, -16) != empty(uint256)):
+        value = shift(value, -16)
+        result = unsafe_add(result, 16)
+    if (shift(value, -8) != empty(uint256)):
+        value = shift(value, -8)
+        result = unsafe_add(result, 8)
+    if (shift(value, -4) != empty(uint256)):
+        value = shift(value, -4)
+        result = unsafe_add(result, 4)
+    if (shift(value, -2) != empty(uint256)):
+        value = shift(value, -2)
+        result = unsafe_add(result, 2)
+    if (shift(value, -1) != empty(uint256)):
+        result = unsafe_add(result, 1)
+
+    if (roundup and (shift(1, convert(result, int256)) < x)):
+        result = unsafe_add(result, 1)
+
+    return result
 
 
 @internal
@@ -415,7 +587,7 @@ def _wad_cbrt(x: uint256) -> uint256:
     """
     @dev An `internal` helper function that calculates the cube root of an
          unsigned integer with a precision of 1e18.
-    @notice Note that this function consumes about 1,800 to 1,850 gas units
+    @notice Note that this function consumes about 1,450 to 1,650 gas units
             depending on the value of `x`. The implementation is inspired
             by Curve Finance's implementation under the MIT license here:
             https://github.com/curvefi/tricrypto-ng/blob/main/contracts/CurveCryptoMathOptimized3.vy.
@@ -425,38 +597,16 @@ def _wad_cbrt(x: uint256) -> uint256:
     # Since this cube root is for numbers with base 1e18, we have to scale
     # the input by 1e36 to increase the precision. This leads to an overflow
     # for very large numbers. So we conditionally sacrifice precision.
-    xx: uint256 = empty(uint256)
+    value: uint256 = empty(uint256)
     if (x >= unsafe_mul(unsafe_div(max_value(uint256), 10 ** 36), 10 ** 18)):
-        xx = x
+        value = x
     elif (x >= unsafe_div(max_value(uint256), 10 ** 36)):
-        xx = unsafe_mul(x, 10 ** 18)
+        value = unsafe_mul(x, 10 ** 18)
     else:
-        xx = unsafe_mul(x, 10 ** 36)
-
-    # Compute the binary logarithm of `xx`. This approach was inspired by Sean
-    # Eron Anderson's "Bit Twiddling Hacks" from Stanford:
-    # https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog.
-    # Further inspiration stems from solmate:
-    # https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol.
-    # A detailed mathematical explanation by Remco Bloemen can be found here:
-    # https://xn--2-umb.com/22/exp-ln.
-    log2x: uint256 = empty(uint256)
-    if (xx > max_value(uint128)):
-        log2x = 128
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > max_value(uint64)):
-        log2x = log2x | 64
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > max_value(uint32)):
-        log2x = log2x | 32
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > max_value(uint16)):
-        log2x = log2x | 16
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > max_value(uint8)):
-        log2x = log2x | 8
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > 15):
-        log2x = log2x | 4
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > 3):
-        log2x = log2x | 2
-    if (unsafe_div(xx, shift(2, convert(log2x, int256))) > 1):
-        log2x = log2x | 1
+        value = unsafe_mul(x, 10 ** 36)
+
+    # Compute the binary logarithm of `value`.
+    log2x: uint256 = self._log_2(value, False)
 
     # If we divide log2x by 3, the remainder is "log2x % 3". So if we simply
     # multiply "2 ** (log2x/3)" and discard the remainder to calculate our guess,
@@ -476,18 +626,18 @@ def _wad_cbrt(x: uint256) -> uint256:
     # and 8 would be one iteration too many. Without initial values, the iteration
     # number can be up to 20 or more. The iterations are unrolled. This reduces the
     # gas cost, but requires more bytecode.
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
-    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(xx, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
+    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)
 
     # Since we scaled up, we have to scale down accordingly.
     if (x >= unsafe_mul(unsafe_div(max_value(uint256), 10 ** 36), 10 ** 18)):
         return unsafe_mul(y, 10 ** 12)
-    elif x >= unsafe_div(max_value(uint256), 10 ** 36):
+    elif (x >= unsafe_div(max_value(uint256), 10 ** 36)):
         return unsafe_mul(y, 10 ** 6)
     else:
         return y


Test patch:
diff --git a/test/utils/Math.t.sol b/test/utils/Math.t.sol
index 392202fa..753ed6b5 100644
--- a/test/utils/Math.t.sol
+++ b/test/utils/Math.t.sol
@@ -4,6 +4,10 @@ pragma solidity ^0.8.19;
 import {Test} from "forge-std/Test.sol";
 import {VyperDeployer} from "utils/VyperDeployer.sol";
 
+import {Math} from "openzeppelin/utils/math/Math.sol";
+import {wadExp, wadLn} from "solmate/utils/SignedWadMath.sol";
+import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
+
 import {IMath} from "./interfaces/IMath.sol";
 
 contract MathTest is Test {
@@ -109,6 +113,57 @@ contract MathTest is Test {
         math = IMath(vyperDeployer.deployContract("src/utils/", "Math"));
     }
 
+    function testUint256Average() public {
+        assertEq(math.uint256_average(83219, 219713), 151466);
+        assertEq(math.uint256_average(73220, 419712), 246466);
+        assertEq(math.uint256_average(83219, 419712), 251465);
+        assertEq(math.uint256_average(73220, 219713), 146466);
+        assertEq(
+            math.uint256_average(type(uint256).max, type(uint256).max),
+            type(uint256).max
+        );
+    }
+
+    function testInt256Average() public {
+        assertEq(math.int256_average(83219, 219713), 151466);
+        assertEq(math.int256_average(-83219, -219713), -151466);
+
+        assertEq(math.int256_average(-73220, 419712), 173246);
+        assertEq(math.int256_average(73220, -419712), -173246);
+
+        assertEq(math.int256_average(83219, -419712), -168247);
+        assertEq(math.int256_average(-83219, 419712), 168246);
+
+        assertEq(math.int256_average(73220, 219713), 146466);
+        assertEq(math.int256_average(-73220, -219713), -146467);
+
+        assertEq(
+            math.int256_average(type(int256).min, type(int256).min),
+            type(int256).min
+        );
+        assertEq(math.int256_average(type(int256).min, type(int256).max), -1);
+    }
+
+    function testCeilDiv() public {
+        assertEq(math.ceil_div(0, 8), 0);
+        assertEq(math.ceil_div(12, 6), 2);
+        assertEq(math.ceil_div(123, 17), 8);
+        assertEq(math.ceil_div(type(uint256).max, 2), 1 << 255);
+        assertEq(math.ceil_div(type(uint256).max, 1), type(uint256).max);
+        vm.expectRevert(bytes("Math: ceil_div division by zero"));
+        math.ceil_div(1, 0);
+    }
+
+    function testIsNegative() public {
+        assertEq(math.is_negative(0), false);
+        assertEq(math.is_negative(-1), true);
+        assertEq(math.is_negative(-1 * -1), false);
+        assertEq(math.is_negative(-1 * 100), true);
+        assertEq(math.is_negative(0 * -1), false);
+        assertEq(math.is_negative(int256(type(int16).min) * 2), true);
+        assertEq(math.is_negative(type(int256).min + type(int16).max), true);
+    }
+
     function testMulDivDivisionByZero() public {
         vm.expectRevert(bytes("Math: mul_div division by zero"));
         math.mul_div(1, 1, 0, false);
@@ -174,47 +229,6 @@ contract MathTest is Test {
         );
     }
 
-    function testUint256Average() public {
-        assertEq(math.uint256_average(83219, 219713), 151466);
-        assertEq(math.uint256_average(73220, 419712), 246466);
-        assertEq(math.uint256_average(83219, 419712), 251465);
-        assertEq(math.uint256_average(73220, 219713), 146466);
-        assertEq(
-            math.uint256_average(type(uint256).max, type(uint256).max),
-            type(uint256).max
-        );
-    }
-
-    function testInt256Average() public {
-        assertEq(math.int256_average(83219, 219713), 151466);
-        assertEq(math.int256_average(-83219, -219713), -151466);
-
-        assertEq(math.int256_average(-73220, 419712), 173246);
-        assertEq(math.int256_average(73220, -419712), -173246);
-
-        assertEq(math.int256_average(83219, -419712), -168247);
-        assertEq(math.int256_average(-83219, 419712), 168246);
-
-        assertEq(math.int256_average(73220, 219713), 146466);
-        assertEq(math.int256_average(-73220, -219713), -146467);
-
-        assertEq(
-            math.int256_average(type(int256).min, type(int256).min),
-            type(int256).min
-        );
-        assertEq(math.int256_average(type(int256).min, type(int256).max), -1);
-    }
-
-    function testCeilDiv() public {
-        assertEq(math.ceil_div(0, 8), 0);
-        assertEq(math.ceil_div(12, 6), 2);
-        assertEq(math.ceil_div(123, 17), 8);
-        assertEq(math.ceil_div(type(uint256).max, 2), 1 << 255);
-        assertEq(math.ceil_div(type(uint256).max, 1), type(uint256).max);
-        vm.expectRevert(bytes("Math: ceil_div division by zero"));
-        math.ceil_div(1, 0);
-    }
-
     function testLog2RoundDown() public {
         assertEq(math.log_2(0, false), 0);
         assertEq(math.log_2(1, false), 0);
@@ -301,6 +315,62 @@ contract MathTest is Test {
         assertEq(math.log_256(type(uint256).max, true), 32);
     }
 
+    function testWadLn() public {
+        assertEq(math.wad_ln(0), 0);
+        assertEq(math.wad_ln(10 ** 18), 0);
+        assertEq(math.wad_ln(1), -41446531673892822313);
+        assertEq(math.wad_ln(42), -37708862055609454007);
+        assertEq(math.wad_ln(10 ** 4), -32236191301916639577);
+        assertEq(math.wad_ln(10 ** 9), -20723265836946411157);
+        assertEq(math.wad_ln(2718281828459045235), 999999999999999999);
+        assertEq(math.wad_ln(11723640096265400935), 2461607324344817918);
+        assertEq(math.wad_ln(2 ** 128), 47276307437780177293);
+        assertEq(math.wad_ln(2 ** 170), 76388489021297880288);
+        assertEq(math.wad_ln(type(int256).max), 135305999368893231589);
+    }
+
+    function testWadLnNegativeValues() public {
+        vm.expectRevert(bytes("Math: wad_ln undefined"));
+        math.wad_ln(-1);
+        vm.expectRevert(bytes("Math: wad_ln undefined"));
+        math.wad_ln(type(int256).min);
+    }
+
+    function testWadExp() public {
+        assertEq(math.wad_exp(-42139678854452767551), 0);
+        assertEq(math.wad_exp(-3 * 10 ** 18), 49787068367863942);
+        assertEq(math.wad_exp(-2 * 10 ** 18), 135335283236612691);
+        assertEq(math.wad_exp(-1 * 10 ** 18), 367879441171442321);
+        assertEq(math.wad_exp(-0.5 * 10 ** 18), 606530659712633423);
+        assertEq(math.wad_exp(-0.3 * 10 ** 18), 740818220681717866);
+        assertEq(math.wad_exp(0), 10 ** 18);
+        assertEq(math.wad_exp(0.3 * 10 ** 18), 1349858807576003103);
+        assertEq(math.wad_exp(0.5 * 10 ** 18), 1648721270700128146);
+        assertEq(math.wad_exp(1 * 10 ** 18), 2718281828459045235);
+        assertEq(math.wad_exp(2 * 10 ** 18), 7389056098930650227);
+        assertEq(math.wad_exp(3 * 10 ** 18), 20085536923187667741);
+        assertEq(math.wad_exp(10 * 10 ** 18), 22026465794806716516980);
+        assertEq(
+            math.wad_exp(50 * 10 ** 18),
+            5184705528587072464148529318587763226117
+        );
+        assertEq(
+            math.wad_exp(100 * 10 ** 18),
+            26881171418161354484134666106240937146178367581647816351662017
+        );
+        assertEq(
+            math.wad_exp(135305999368893231588),
+            57896044618658097650144101621524338577433870140581303254786265309376407432913
+        );
+    }
+
+    function testWadExpOverflow() public {
+        vm.expectRevert(bytes("Math: wad_exp overflow"));
+        math.wad_exp(135305999368893231589);
+        vm.expectRevert(bytes("Math: wad_exp overflow"));
+        math.wad_exp(type(int256).max);
+    }
+
     function testCbrtRoundDown() public {
         assertEq(math.cbrt(0, false), 0);
         assertEq(math.cbrt(1, false), 1);
@@ -370,14 +440,45 @@ contract MathTest is Test {
         );
     }
 
-    function testIsNegative() public {
-        assertEq(math.is_negative(0), false);
-        assertEq(math.is_negative(-1), true);
-        assertEq(math.is_negative(-1 * -1), false);
-        assertEq(math.is_negative(-1 * 100), true);
-        assertEq(math.is_negative(0 * -1), false);
-        assertEq(math.is_negative(int256(type(int16).min) * 2), true);
-        assertEq(math.is_negative(type(int256).min + type(int16).max), true);
+    /**
+     * @notice We use the `average` function of OpenZeppelin as a benchmark:
+     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
+     */
+    function testFuzzUint256Average(uint256 x, uint256 y) public {
+        assertEq(math.uint256_average(x, y), Math.average(x, y));
+    }
+
+    /**
+     * @notice We use the `avg` function of solady as a benchmark:
+     * https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol.
+     */
+    function testFuzzInt256Average(int256 x, int256 y) public {
+        assertEq(math.int256_average(x, y), FixedPointMathLib.avg(x, y));
+    }
+
+    /**
+     * @notice Forked and adjusted accordingly from here:
+     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/utils/math/Math.t.sol.
+     */
+    function testFuzzCeilDiv(uint256 x, uint256 y) public {
+        vm.assume(y > 0);
+        uint256 result = math.ceil_div(x, y);
+        if (result == 0) {
+            assertEq(x, 0);
+        } else {
+            uint256 maxDiv = type(uint256).max / y;
+            bool overflow = maxDiv * y < x;
+            assertTrue(x > y * (result - 1));
+            assertTrue(overflow ? result == maxDiv + 1 : x <= y * result);
+        }
+    }
+
+    function testFuzzIsNegative(int256 x) public {
+        if (x >= 0) {
+            assertEq(math.is_negative(x), false);
+        } else {
+            assertEq(math.is_negative(x), true);
+        }
     }
 
     /**
@@ -441,42 +542,6 @@ contract MathTest is Test {
         } catch {}
     }
 
-    /**
-     * @notice We use the `average` function of OpenZeppelin as a benchmark:
-     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.
-     */
-    function testFuzzUint256Average(uint256 x, uint256 y) public {
-        assertEq(math.uint256_average(x, y), (x & y) + ((x ^ y) / 2));
-    }
-
-    /**
-     * @notice We use the `avg` function of solady as a benchmark:
-     * https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol.
-     */
-    function testFuzzInt256Average(int256 x, int256 y) public {
-        assertEq(
-            math.int256_average(x, y),
-            (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1)
-        );
-    }
-
-    /**
-     * @notice Forked and adjusted accordingly from here:
-     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/utils/math/Math.t.sol.
-     */
-    function testFuzzCeilDiv(uint256 x, uint256 y) public {
-        vm.assume(y > 0);
-        uint256 result = math.ceil_div(x, y);
-        if (result == 0) {
-            assertEq(x, 0);
-        } else {
-            uint256 maxDiv = type(uint256).max / y;
-            bool overflow = maxDiv * y < x;
-            assertTrue(x > y * (result - 1));
-            assertTrue(overflow ? result == maxDiv + 1 : x <= y * result);
-        }
-    }
-
     /**
      * @notice Forked and adjusted accordingly from here:
      * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/utils/math/Math.t.sol.
@@ -534,6 +599,32 @@ contract MathTest is Test {
         }
     }
 
+    /**
+     * @notice We use the `lnWad` function of solady as a benchmark:
+     * https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol,
+     * as well as the function `wadLn` of solmate:
+     * https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol.
+     */
+    function testFuzzWadLn(int256 x) public {
+        x = bound(x, 1, type(int256).max);
+        int256 result = math.wad_ln(x);
+        assertEq(result, FixedPointMathLib.lnWad(x));
+        assertEq(result, wadLn(x));
+    }
+
+    /**
+     * @notice We use the `expWad` function of solady as a benchmark:
+     * https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol,
+     * as well as the function `wadExp` of solmate:
+     * https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol.
+     */
+    function testFuzzWadExp(int256 x) public {
+        x = bound(x, type(int256).min, 135305999368893231588);
+        int256 result = math.wad_exp(x);
+        assertEq(result, FixedPointMathLib.expWad(x));
+        assertEq(result, wadExp(x));
+    }
+
     function testFuzzCbrt(uint256 x, bool roundup) public {
         uint256 result = math.cbrt(x, roundup);
         uint256 floor = floorCbrt(x);
@@ -553,12 +644,4 @@ contract MathTest is Test {
         );
         assertEq(result / 10 ** 12, floor);
     }
-
-    function testFuzzIsNegative(int256 x) public {
-        if (x >= 0) {
-            assertEq(math.is_negative(x), false);
-        } else {
-            assertEq(math.is_negative(x), true);
-        }
-    }
 }
diff --git a/test/utils/interfaces/IMath.sol b/test/utils/interfaces/IMath.sol
index a041ffec..cfeef419 100644
--- a/test/utils/interfaces/IMath.sol
+++ b/test/utils/interfaces/IMath.sol
@@ -2,13 +2,6 @@
 pragma solidity ^0.8.19;
 
 interface IMath {
-    function mul_div(
-        uint256 x,
-        uint256 y,
-        uint256 denominator,
-        bool roundup
-    ) external pure returns (uint256);
-
     function uint256_average(
         uint256 x,
         uint256 y
@@ -18,15 +11,26 @@ interface IMath {
 
     function ceil_div(uint256 x, uint256 y) external pure returns (uint256);
 
+    function is_negative(int256 x) external pure returns (bool);
+
+    function mul_div(
+        uint256 x,
+        uint256 y,
+        uint256 denominator,
+        bool roundup
+    ) external pure returns (uint256);
+
     function log_2(uint256 x, bool roundup) external pure returns (uint256);
 
     function log_10(uint256 x, bool roundup) external pure returns (uint256);
 
     function log_256(uint256 x, bool roundup) external pure returns (uint256);
 
+    function wad_ln(int256 x) external pure returns (int256);
+
+    function wad_exp(int256 x) external pure returns (int256);
+
     function cbrt(uint256 x, bool roundup) external pure returns (uint256);
 
     function wad_cbrt(uint256 x) external pure returns (uint256);
-
-    function is_negative(int256 x) external pure returns (bool);
 }