🪝 Solhooks: Hooks for Ethereum made simple
MIT License
Solhooks is a powerful and flexible Solidity library that provides pre and post hooks for function calls to other contracts. It allows you to execute custom code before or after a function call, either safely or unsafely.
🛡️ Safety First: Execute hooks securely with safePreHook
and safePostHook
.
⚡ Flexibility: With both preHook
and postHook
, integrate hooks as per your needs.
🌐 Universal Compatibility: Designed specifically for Solidity, but adaptable for various contract programming languages.
Install with Foundry:
forge install 0xfuturistic/solhooks
Dive into the world of hooks with four powerful modifiers:
preHook
: Executes an unsafe hook before the function it modifies.postHook
: Executes an unsafe hook after the function it modifies.safePreHook
: Executes a safe hook before the function it modifies.safePostHook
: Executes a safe hook after the function it modifies.Using preHook
:
function transfer(address from, address to, uint256 amount)
public
preHook(from, "beforeTokenTransfer(address,uint256)", abi.encode(to, amount))
{
// Your function code here
}
In this example, the transfer
function will execute the _unsafeHook
function in the Hooks
contract before executing the function code. The _unsafeHook
function will call the from
contract's beforeTokenTransfer
function with the encoded from
and amount
arguments.
Using postHook
:
function transfer(address from, address to, uint256 amount)
public
postHook(from, "afterTokenTransfer(address,uint256)", abi.encode(to, amount))
{
// Your function code here
}
For adding hooks to already defined methods:
function transfer(address from, address to, uint256 amount)
public
postHook(from, "afterTokenTransfer(address,uint256)", abi.encode(to, amount))
override
{
super.transfer(from, amount);
}
For chaining multiple operations:
function multiStepOperation()
public
preHook(target1, "step1", callData1)
postHook(target2, "step2", callData2)
{
// Core operation logic
}
In the realm of Ethereum smart contracts, there are times when you might want to sequence multiple operations or even call functions from various contracts in a specific order. This is where Solhooks truly shines.
Imagine you have a set of operations that need to be executed in a specific order:
Typically, you'd have to manually ensure this sequence within the body of your function. With Solhooks, you can elegantly sequence these operations using preHook
and postHook
.
Using Solhooks, you can achieve this as follows:
function sendAndRecord(address from, uint256 amount) public
preHook(tokenContract, "balanceOf(address)", abi.encode(from))
postHook(ledgerContract, "recordTransaction(address,uint256)", abi.encode(from, amount))
{
// Main function: send tokens
token.transfer(from, amount);
}
In the above example:
The preHook
ensures that the balance is checked in the token contract before making the transfer.
The postHook
records the transaction details in the ledger contract after the transfer.
Modularity: Each operation remains a separate module, enhancing code readability, maintainability, and security.
Reusability: Common operations (like balance checks) can be reused across different functions or contracts.
Flexibility: Easily change the sequence of operations or add/remove operations as needed without restructuring the entire function.
Complex DApps: In decentralized applications with multiple contracts interacting, Solhooks can ensure the correct sequence of calls between them.
Upgrades & Migrations: If you're migrating from one contract version to another, hooks can help in sequencing data transfers and updates.
Security: By adding checks or validations as pre-hooks, you can enhance the security of your main function.
In essence, Solhooks provides a powerful framework for developers to seamlessly stitch together intricate workflows in their Ethereum smart contracts, making the development process both efficient and elegant.
With Solhooks, developers can add arbitrary invariants and enforce constraints as pre and post hooks. This ensures:
Preconditions: Before the execution of the main function, the preHook can be used to check certain conditions (preconditions) that must be satisfied.
Postconditions: After the function's execution, the postHook can validate that the function maintained certain conditions (postconditions).
Imagine a simple pool contract where users can deposit and withdraw tokens. An invariant might be that the total balance of the pool should never be negative.
We can inherit this contract and add a preHook
modifier to the functions we want to add invariants to:
function withdraw(uint256 amount) public
preHook(address(this), "ensureSufficientBalance(uint256)", abi.encode(amount))
override
{
super.withdraw(amount);
}
Here, the ensureSufficientBalance
function (called via a preHook
) might look something like:
function ensureSufficientBalance(uint256 amount) internal view {
require(address(this).balance >= amount, "Insufficient balance for withdrawal");
}
preHook
calls it to ensure the invariant that the pool has enough balance for the withdrawal.
xy=k
) are maintained after each swap.In summary, Solhooks provides a structured and efficient way to embed invariants into Ethereum smart contracts, ensuring that they operate within defined boundaries, enhancing their reliability and security.
If you'd like to contribute to Solhooks, please fork the repository and make changes as you'd like. Pull requests are welcome!
Solhooks is licensed under the MIT License. See the LICENSE file for more information.