-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial check-in
- Loading branch information
Showing
8 changed files
with
397 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {Script, console} from "forge-std/Script.sol"; | ||
import {AfterSwapDonationHook} from "../src/HookDonation.sol"; | ||
|
||
contract CounterScript is Script { | ||
AfterSwapDonationHook public donationHook; | ||
|
||
function setUp() public {} | ||
|
||
function run() public { | ||
vm.startBroadcast(); | ||
|
||
// donationHook = new AfterSwapDonationHook(); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {BaseHook} from "lib/v4-periphery/src/base/hooks/BaseHook.sol"; | ||
import {PoolKey} from "lib/v4-periphery/lib/v4-core/src/types/PoolKey.sol"; | ||
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; | ||
import {BalanceDelta} from "lib/v4-periphery/lib/v4-core/src/types/BalanceDelta.sol"; | ||
import {Hooks} from "lib/v4-periphery/lib/v4-core/src/libraries/Hooks.sol"; | ||
import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; | ||
|
||
contract AfterSwapDonationHook is BaseHook { | ||
using CurrencyLibrary for Currency; | ||
struct DonationMapping { | ||
bool enabled; | ||
address payable recipient; | ||
uint256 percent; // how much to donate | ||
} | ||
address public owner; | ||
address public pool; | ||
mapping(address => DonationMapping) donationMap; | ||
|
||
// -------------- begin donation associated functions --------------- | ||
function disableDonation() public { | ||
// Reset the value to the default value. | ||
delete donationMap[msg.sender]; | ||
} | ||
|
||
function enableDonation(address recipient, uint256 percent) public { | ||
DonationMapping memory local; | ||
local.recipient = payable(recipient); | ||
local.percent = percent; | ||
|
||
donationMap[msg.sender] = local; | ||
} | ||
|
||
// the following should all have internal view, not public | ||
// but have been changed to public view for testing | ||
|
||
function donationEnabled(address payee) public view returns (bool) { | ||
return (donationMap[payee].recipient != payable(0x0)); | ||
} | ||
|
||
function donationPercent(address payee) public view returns (uint256) { | ||
return (donationMap[payee].percent); | ||
} | ||
|
||
function donationRecipient(address payee) public view returns (address) { | ||
return (donationMap[payee].recipient); | ||
} | ||
// -------------- end donation associated functions --------------- | ||
|
||
constructor(IPoolManager _poolManager) BaseHook(_poolManager) { | ||
owner = msg.sender; | ||
} | ||
|
||
// Modifier to restrict access to the owner | ||
modifier onlyOwner() { | ||
require(msg.sender == owner, "Not authorized"); | ||
_; | ||
} | ||
|
||
/// @notice The hook called after a swap | ||
/// @param sender The initial msg.sender for the swap call | ||
/// @param key The key for the pool | ||
/// @param swapParams The parameters for the swap | ||
/// @param delta The amount owed to the caller (positive) or owed to the pool (negative) | ||
/// @param ...Arbitrary data handed into the PoolManager by the swapper to be passed on to the hook | ||
/// @return bytes4 The function selector for the hook | ||
/// @return int128 The hook's delta in unspecified currency. Positive: the hook is owed/took currency, negative: the hook owes/sent currency | ||
function afterSwap( | ||
address sender, | ||
PoolKey calldata key, | ||
IPoolManager.SwapParams calldata swapParams, | ||
BalanceDelta delta, | ||
bytes calldata | ||
) external override returns (bytes4, int128) { | ||
require(msg.sender == address(pool), "Unauthorized caller"); | ||
|
||
// Check that donation is enabled for the sender | ||
if (!donationEnabled(sender)) | ||
return (this.afterSwap.selector, 0); | ||
|
||
uint256 spendAmount = swapParams.amountSpecified < 0 | ||
? uint256(-swapParams.amountSpecified) | ||
: uint256(int256(-delta.amount0())); | ||
|
||
uint256 donationAmount = (spendAmount * donationPercent(sender)) / 100; | ||
address recipient = donationRecipient(sender); | ||
|
||
key.currency0.transfer(recipient, donationAmount); | ||
|
||
return (this.afterSwap.selector, 0); | ||
} | ||
|
||
// Function to update the pool address if needed | ||
function updatePool(address _newPool) external onlyOwner { | ||
pool = _newPool; | ||
} | ||
|
||
// Only for other apps. Uniswap doesn't call this. | ||
function getHookPermissions() public pure override returns (Hooks.Permissions memory) { | ||
return | ||
Hooks.Permissions({ | ||
beforeInitialize: false, | ||
afterInitialize: false, | ||
beforeAddLiquidity: false, | ||
beforeRemoveLiquidity: false, | ||
afterAddLiquidity: false, | ||
afterRemoveLiquidity: false, | ||
beforeSwap: false, | ||
afterSwap: true, | ||
beforeDonate: false, | ||
afterDonate: false, | ||
beforeSwapReturnDelta: false, | ||
afterSwapReturnDelta: false, | ||
afterAddLiquidityReturnDelta: false, | ||
afterRemoveLiquidityReturnDelta: false | ||
}); | ||
} | ||
|
||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.26; | ||
|
||
// forge-std/=lib/v4-periphery/lib/v4-core/lib/forge-std/src/ | ||
import "lib/v4-periphery/lib/v4-core/lib/forge-std/src/Test.sol"; | ||
import "lib/v4-periphery/lib/v4-core/lib/forge-std/src/console.sol"; | ||
import {Hooks} from "lib/v4-core/src/libraries/Hooks.sol"; | ||
import {Deployers} from "lib/v4-core/test/utils/Deployers.sol"; | ||
import {IPoolManager} from "lib/v4-core/src/interfaces/IPoolManager.sol"; | ||
import {AfterSwapDonationHook} from "../src/HookDonation.sol"; | ||
import {Currency, CurrencyLibrary} from "lib/v4-core/src/types/Currency.sol"; | ||
// solmate/=lib/v4-core/lib/solmate/ | ||
import {MockERC20} from "lib/v4-core/lib/solmate/src/test/utils/mocks/MockERC20.sol"; | ||
import {PoolKey} from "lib/v4-core/src/types/PoolKey.sol"; | ||
import {IHooks} from "lib/v4-core/src/interfaces/IHooks.sol"; | ||
|
||
contract DonationTest is Test, Deployers { | ||
using CurrencyLibrary for Currency; | ||
|
||
struct DonationMapping { | ||
bool enabled; | ||
address payable recipient; | ||
uint256 percent; // how much to donate | ||
} | ||
mapping(address => DonationMapping) donationMap; | ||
|
||
// address constant USDT_MOCK_ADDRESS = address(0xEce6af52f8eDF69dd2C216b9C3f184e5b31750e9); // mock address | ||
// address constant USDC_MOCK_ADDRESS = address(0x63ba29cAF4c40DaDA8a61D10AB5D2728c806b61f); // mock address | ||
|
||
AfterSwapDonationHook donationHook; | ||
// The two currencies (tokens) from the pool | ||
Currency token0; | ||
Currency token1; | ||
PoolKey globalKey; | ||
address constant RECIPIENT = address(0x01); | ||
|
||
event HookAddress(address indexed hookAddress); | ||
|
||
function setUp() public { | ||
Deployers.deployFreshManagerAndRouters(); | ||
(currency0, currency1) = Deployers.deployMintAndApprove2Currencies(); | ||
(token0, token1) = (currency0, currency1); | ||
|
||
// Deploy the hook to an address with the correct flags | ||
uint160 flags = uint160( | ||
Hooks.AFTER_SWAP_FLAG | ||
); | ||
|
||
address hookAddress = address(flags); | ||
deployCodeTo( | ||
"HookDonation.sol", | ||
abi.encode(manager, ""), | ||
hookAddress | ||
); | ||
emit HookAddress(hookAddress); | ||
donationHook = AfterSwapDonationHook(hookAddress); | ||
console.log("setUp Hook Address: ", hookAddress); | ||
console.log("donation Hook: ", address(donationHook)); | ||
|
||
// Approve our hook address to spend these tokens as well | ||
MockERC20(Currency.unwrap(token0)).approve( | ||
address(donationHook), | ||
type(uint256).max | ||
); | ||
MockERC20(Currency.unwrap(token1)).approve( | ||
address(donationHook), | ||
type(uint256).max | ||
); | ||
// Approve swapRouter to spend these tokens as well | ||
MockERC20(Currency.unwrap(token0)).approve( | ||
address(swapRouter), | ||
type(uint256).max | ||
); | ||
MockERC20(Currency.unwrap(token1)).approve( | ||
address(swapRouter), | ||
type(uint256).max | ||
); | ||
|
||
// Initialize a pool with these two tokens | ||
(key, ) = initPool(token0, token1, IHooks(hookAddress), 3000, SQRT_PRICE_1_1, ZERO_BYTES); | ||
globalKey = key; | ||
} | ||
|
||
function donationEnabled(address payee) public view returns (bool) { | ||
return (donationMap[payee].recipient != payable(0x0)); | ||
} | ||
|
||
function donationRecipient(address payee) public view returns (address) { | ||
return (donationMap[payee].recipient); | ||
} | ||
|
||
function enableDonation(address recipient, uint256 percent) public { | ||
DonationMapping memory local; | ||
local.recipient = payable(recipient); | ||
local.percent = percent; | ||
|
||
donationMap[msg.sender] = local; | ||
} | ||
|
||
function test_internalEnableDonation() public { | ||
address payee = msg.sender; | ||
bool enabled = donationEnabled(payee); | ||
address recipient = donationRecipient(payee); | ||
console.log("Before enabling donation"); | ||
console.log("--------------------------------------------------------------------------------"); | ||
console.log("enabled: %s", enabled); | ||
console.log("recipient: %s", recipient); | ||
console.log(); | ||
|
||
enableDonation(RECIPIENT, 10); // recipient = 0x01, 10 percent | ||
console.log("After enabling donation"); | ||
console.log("--------------------------------------------------------------------------------"); | ||
enabled = donationEnabled(payee); | ||
recipient = donationRecipient(payee); | ||
console.log("enabled: %s", enabled); | ||
console.log("recipient: %s", recipient); | ||
} | ||
|
||
function test_enableDonation() public { | ||
address payee = msg.sender; | ||
bool enabled = donationHook.donationEnabled(payee); | ||
address recipient = donationHook.donationRecipient(payee); | ||
console.log("Before enabling donation"); | ||
console.log("--------------------------------------------------------------------------------"); | ||
console.log("enabled: %s", enabled); | ||
console.log("recipient: %s", recipient); | ||
console.log(); | ||
|
||
donationHook.enableDonation(RECIPIENT, 10); // recipient = 0x01, 10 percent | ||
console.log("After enabling donation"); | ||
console.log("--------------------------------------------------------------------------------"); | ||
enabled = donationHook.donationEnabled(payee); | ||
recipient = donationHook.donationRecipient(payee); | ||
console.log("enabled: %s", enabled); | ||
console.log("recipient: %s", recipient); | ||
} | ||
|
||
function test_Donation() public { | ||
bool zeroForOne = true; | ||
// PoolKey memory pool = PoolKey( | ||
// token0, token1, 3000, 60, IHooks(address(donationHook)) | ||
// ); | ||
PoolKey memory pool = globalKey; | ||
bytes memory data = abi.encode(msg.sender); | ||
address recipient = address(0x01); | ||
console.log("Donation not enabled"); | ||
console.log("Donation enabled for %s: %s", msg.sender, donationHook.donationEnabled(msg.sender)); | ||
console.log("Donation recipient for %s: %s", msg.sender, donationHook.donationRecipient(msg.sender)); | ||
|
||
console.log("Donation enabled: 10%%"); | ||
donationHook.enableDonation(RECIPIENT, 10); // recipient = 0x01, 10 percent | ||
console.log("Test Donation sender: ", msg.sender); | ||
|
||
console.log("Donation enabled for %s: %s", msg.sender, donationHook.donationEnabled(msg.sender)); | ||
console.log("Donation recipient for %s: %s", msg.sender, donationHook.donationRecipient(msg.sender)); | ||
|
||
console.log("beforeSwap Balance token0: ", token0.balanceOf(msg.sender)); | ||
console.log("beforeSwap Balance token1: ", token1.balanceOf(msg.sender)); | ||
console.log(); | ||
console.log("beforeSwap Balance token0: ", token0.balanceOf(recipient)); | ||
console.log("beforeSwap Balance token1: ", token1.balanceOf(recipient)); | ||
|
||
|
||
int256 amountSpecified = 10; | ||
Deployers.swap(pool, zeroForOne, amountSpecified, data); | ||
|
||
console.log(" afterSwap Balance token0: ", token0.balanceOf(msg.sender)); | ||
console.log(" afterSwap Balance token1: ", token1.balanceOf(msg.sender)); | ||
console.log(" afterSwap Balance token0: ", token0.balanceOf(recipient)); | ||
console.log(" afterSwap Balance token1: ", token1.balanceOf(recipient)); | ||
} | ||
|
||
} |
Oops, something went wrong.