Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 32 additions & 64 deletions contracts/src/RDEXHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ contract RDEXHook is BaseHook, Ownable {
uint24 internal immutable i_minimumFee;

// discountBasisPoints is a percentage of the fee that will be discounted 1 to 1000 1 is 0.001% and 1000 is 0.1%
mapping(uint256 claimTopic => uint16 discountBasisPoints)
internal s_topicToDiscount;
// mapping(uint256 claimTopic => uint16 discountBasisPoints)
// internal s_topicToDiscount;

uint256[] internal s_topicsWithDiscount;
// uint256[] internal s_topicsWithDiscount;

uint256 public s_reducedFeeTopic;

/* ==================== EVENTS ==================== */

Expand Down Expand Up @@ -153,9 +155,11 @@ contract RDEXHook is BaseHook, Ownable {
address _sender,
PoolKey calldata,
IPoolManager.SwapParams calldata,
bytes calldata
bytes calldata _hookData
) external override returns (bytes4, BeforeSwapDelta, uint24) {
uint24 fee = _calculateFee(_sender);
//ClaimData(usedIdentity, REDUCED_FEE_TOPIC, "2000");`
bool isReducedFee = abi.decode(_hookData, (bool));
uint24 fee = isReducedFee ? _calculateFee(_sender) : 0;
// poolManager.updateDynamicLPFee(_key, fee);
uint24 feeWithFlag = fee | LPFeeLibrary.OVERRIDE_FEE_FLAG;

Expand Down Expand Up @@ -195,36 +199,8 @@ contract RDEXHook is BaseHook, Ownable {
emit RefCurrencyClaimTrustedIssuerSet(_refCurrencyClaimTrustedIssuer);
}

/// @notice Sets the discount basis points for a specific topic
/// @dev Only the owner can call this function
/// @param _topic The topic for which the discount is being set
/// @param _discountBasisPoints The discount basis points to be set for the topic
function setTopicToDiscount(
uint256 _topic,
uint16 _discountBasisPoints
) external onlyOwner {
s_topicToDiscount[_topic] = _discountBasisPoints;
}

/// @notice Sets the topics that have discounts
/// @dev Only the owner can call this function
/// @param _topicsWithDiscount An array of topics that have discounts
function setTopicsWithDiscount(
uint256[] calldata _topicsWithDiscount
) external onlyOwner {
s_topicsWithDiscount = _topicsWithDiscount;
}

// TODO: Explore if we need this getters or we can directly use public variables
function topicsWithDiscount() external view returns (uint256[] memory) {
return s_topicsWithDiscount;
}

/// @notice Returns the discount basis points for a specific topic
/// @param _topic The topic for which the discount basis points are being queried
/// @return The discount basis points for the specified topic
function topicToDiscount(uint256 _topic) external view returns (uint16) {
return s_topicToDiscount[_topic];
function setReducedFeeTopic(uint16 _reducedFeeTopic) external onlyOwner {
s_reducedFeeTopic = _reducedFeeTopic;
}

/// @notice Returns the identity registry storage
Expand Down Expand Up @@ -284,44 +260,36 @@ contract RDEXHook is BaseHook, Ownable {
/// @notice Calculates the fee
/// @return The calculated fee
function _calculateFee(address _sender) internal view returns (uint24) {
//TODO: find way to let user say which topics it wants to be checked for discount during swap
//TODO: find way to test that discounts actually get applied
uint256 discountedFee = BASE_FEE;
uint256[] memory topicsWithDiscount = s_topicsWithDiscount;

IIdentity identity = IIdentity(
s_identityRegistryStorage.storedIdentity(_sender)
);
bytes32 claimId = keccak256(
abi.encode(s_refCurrencyClaimTrustedIssuer, s_reducedFeeTopic)
);

for (uint256 i = 0; i < topicsWithDiscount.length; i++) {
uint256 topic = topicsWithDiscount[i];
uint256 discountBasisPoints = s_topicToDiscount[topic];
bytes32 claimId = keccak256(
abi.encode(s_refCurrencyClaimTrustedIssuer, topic)
);
(, , , bytes memory sig, bytes memory data, ) = identity.getClaim(
claimId
);
if (
IClaimIssuer(s_refCurrencyClaimTrustedIssuer).isClaimValid(
identity,
s_reducedFeeTopic,
sig,
data
)
) {
uint256 decodedFeeDiscount = abi.decode(data, (uint256));
unchecked {
discountedFee = discountedFee - decodedFeeDiscount;
}

(, , , bytes memory sig, bytes memory data, ) = identity.getClaim(
claimId
);
if (
IClaimIssuer(s_refCurrencyClaimTrustedIssuer).isClaimValid(
identity,
topicsWithDiscount[i],
sig,
data
)
) {
unchecked {
discountedFee = discountedFee - discountBasisPoints;
}

if (discountedFee < i_minimumFee) {
return i_minimumFee;
}
if (discountedFee < i_minimumFee) {
return i_minimumFee;
}
}

return uint24(discountedFee);
}

/* ==================== PRIVATE ==================== */
}
68 changes: 64 additions & 4 deletions contracts/test/RDEXHookFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {IClaimIssuer} from "@onchain-id/solidity/contracts/interface/IClaimIssue
// RDEX Hook contracts
import {RDEXHook} from "src/RDEXHook.sol";
import {TREXSuite} from "./utils/TREXSuite.t.sol";
import {PoolSwapTest} from "v4-core/src/test/PoolSwapTest.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";

contract MockERC20Mint is MockERC20 {
function mint(address to, uint256 amount) public {
Expand Down Expand Up @@ -62,14 +65,16 @@ contract RDEXHookFeesTest is Test, TREXSuite, Deployers {
// Deploy Hook
address hookAddress = address(
(uint160(makeAddr("RDEXHook")) & ~Hooks.ALL_HOOK_MASK) |
Hooks.BEFORE_INITIALIZE_FLAG
Hooks.BEFORE_INITIALIZE_FLAG |
Hooks.BEFORE_SWAP_FLAG
);
deployCodeTo(
"RDEXHook.sol:RDEXHook",
abi.encode(manager, deployer, 3000),
hookAddress
);
hook = RDEXHook(hookAddress);
swapRouter = new PoolSwapTest(manager);

// Set the identity registry storage of the Hook
vm.startPrank(deployer);
Expand Down Expand Up @@ -221,13 +226,68 @@ contract RDEXHookFeesTest is Test, TREXSuite, Deployers {
);
}
function test_discountTopicsGetApplied() public {
// Set up our swap parameters
PoolSwapTest.TestSettings memory testSettings = PoolSwapTest
.TestSettings({takeClaims: false, settleUsingBurn: false});

IPoolManager.SwapParams memory params = IPoolManager.SwapParams({
zeroForOne: true,
amountSpecified: -0.00001 ether,
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});

uint256[] memory discountTopics = new uint256[](1);
discountTopics[0] = DISCOUNT_TOPIC;
vm.startPrank(deployer);
hook.setTopicsWithDiscount(discountTopics);
hook.setTopicToDiscount(DISCOUNT_TOPIC, MOCK_DISCOUNT);
// hook.setTopicsWithDiscount(discountTopics);
// hook.setDynamicFee(DISCOUNT_TOPIC, MOCK_DISCOUNT);

// uint256 balanceOfToken1Before = currency1.balanceOfSelf();

// hook.swap(key, params, testSettings, ZERO_BYTES);
// uint256 balanceOfToken1After = currency1.balanceOfSelf();
// uint256 outputFromBaseFeeSwap = balanceOfToken1After -
// balanceOfToken1Before;

// assertGt(balanceOfToken1After, balanceOfToken1Before);

// assertEq(outputFromBaseFeeSwap, MOCK_DISCOUNT);

// assertEq(hook.dynamicFee(DISCOUNT_TOPIC), MOCK_DISCOUNT);
vm.stopPrank();

// Swap happens with discount
}

function test_minimumFeeGetsAppliedIfDiscountTooBig() public {
// Set up our swap parameters
PoolSwapTest.TestSettings memory testSettings = PoolSwapTest
.TestSettings({takeClaims: false, settleUsingBurn: false});

IPoolManager.SwapParams memory params = IPoolManager.SwapParams({
zeroForOne: true,
amountSpecified: -0.00001 ether,
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});

uint256[] memory discountTopics = new uint256[](1);
discountTopics[0] = DISCOUNT_TOPIC;
vm.startPrank(deployer);
// hook.setTopicsWithDiscount(discountTopics);
//hook.setDynamicFee(DISCOUNT_TOPIC, 1000000);

// uint256 balanceOfToken1Before = currency1.balanceOfSelf();

// hook.swap(key, params, testSettings, ZERO_BYTES);
// uint256 balanceOfToken1After = currency1.balanceOfSelf();
// uint256 outputFromBaseFeeSwap = balanceOfToken1After -
// balanceOfToken1Before;

// assertGt(balanceOfToken1After, balanceOfToken1Before);

// assertEq(outputFromBaseFeeSwap, MINIMUM_FEE);

assertEq(hook.topicToDiscount(DISCOUNT_TOPIC), MOCK_DISCOUNT);
// assertEq(hook.dynamicFee(DISCOUNT_TOPIC), 1000);
vm.stopPrank();

// Swap happens with discount
Expand Down
Loading