Skip to content

Commit

Permalink
Merge pull request #9 from xudean/feature/exchange-volume-hooks
Browse files Browse the repository at this point in the history
Add function to update pool fee
  • Loading branch information
fksyuan authored Jan 20, 2025
2 parents 411163c + 1348465 commit 6c502ff
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 65 deletions.
9 changes: 2 additions & 7 deletions script/attestation/AttestationRegistry.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ contract DeployAttestationRegistry is Script {
address payable feeRecipient = payable(vm.envAddress("FEE_RECIPIENT")); // fee recipient

// deploy AttestationRegistry
AttestationRegistry attestationRegistry = new AttestationRegistry(
primusZKTLS,
submissionFee,
feeRecipient
);
AttestationRegistry attestationRegistry = new AttestationRegistry(primusZKTLS, submissionFee, feeRecipient);
string[] memory cexUrls = new string[](4);
cexUrls[0] = "https://www.okx.com/v3/users/fee/trading-volume-progress";
cexUrls[1] = "https://www.bitget.com/v1/mix/vip/need";
Expand All @@ -37,11 +33,10 @@ contract DeployAttestationRegistry is Script {
cexNames[1] = "bitget";
cexNames[2] = "binance";
cexNames[3] = "bybit";
attestationRegistry.setCexAndJsonPath(cexUrls,cexNames,cexJsonPaths);
attestationRegistry.setCexAndJsonPath(cexUrls, cexNames, cexJsonPaths);

console.log("AttestationRegistry deployed at:", address(attestationRegistry));

vm.stopBroadcast();
}
}

49 changes: 14 additions & 35 deletions src/BaseFeeDiscountHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@ abstract contract BaseFeeDiscountHook is Ownable {
event BeforeAddLiquidity(address indexed sender);
event BeforeSwap(address indexed sender);

uint24 private defaultFee = 3000;
uint24 public defaultFee = 3000;

uint24 private baseValue = 10000;
uint24 public baseValue = 10000;

uint24 private durationOfAttestation = 7;
uint24 public durationOfAttestation = 7;

PoolId[] public poolsInitialized;

mapping(PoolId => uint24) public poolFeeMapping;

// mapping(PoolId => uint24) public poolFeeMapping;
// AttestationRegistry
IAttestationRegistry internal iAttestationRegistry;
IAttestationRegistry public iAttestationRegistry;

constructor(IAttestationRegistry _iAttestationRegistry, address initialOwner) Ownable(initialOwner) {
iAttestationRegistry = _iAttestationRegistry;
}

function getFeeDiscount(address sender, PoolKey memory poolKey) internal view returns (uint24) {
uint24 poolFee = poolFeeMapping[poolKey.toId()];
if (_checkAttestations(sender)) {
return (defaultFee / 2) | LPFeeLibrary.OVERRIDE_FEE_FLAG;
return (poolFee / 2) | LPFeeLibrary.OVERRIDE_FEE_FLAG;
}
return defaultFee;
return poolFee;
}

/*
Expand All @@ -49,14 +53,6 @@ abstract contract BaseFeeDiscountHook is Ownable {
defaultFee = fee;
}

/*
@dev Get default fee
@return uint24
*/
function getDefaultFee() public view returns (uint24) {
return defaultFee;
}

/*
@dev Set baseValue
@param _baseValue
Expand All @@ -66,14 +62,6 @@ abstract contract BaseFeeDiscountHook is Ownable {
baseValue = _baseValue;
}

/*
@dev Get baseValue
@return uint24
*/
function getBaseValue() public view returns (uint24) {
return baseValue;
}

/*
@dev Set durationOfAttestation
@param _durationOfAttestation
Expand All @@ -84,19 +72,10 @@ abstract contract BaseFeeDiscountHook is Ownable {
}

/*
@dev Get durationOfAttestation
@return uint24
@dev Set attestationRegistry
*/
function getDurationOfAttestation() external view returns (uint24) {
return durationOfAttestation;
}

/*
@dev Get attestationRegistry
@return IAttestationRegistry
*/
function getAttestationRegistry() external view returns (IAttestationRegistry) {
return iAttestationRegistry;
function setAttestationRegistry(IAttestationRegistry _iAttestationRegistry) external onlyOwner {
iAttestationRegistry = _iAttestationRegistry;
}

/*
Expand Down
30 changes: 19 additions & 11 deletions src/attestation/AttestationRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ pragma solidity ^0.8.24;

import {Attestation as PrimusAttestation, IPrimusZKTLS} from "zkTLS-contracts/src/IPrimusZKTLS.sol";
import {Attestation} from "../types/Common.sol";
import {IAttestationRegistry} from '../IAttestationRegistry.sol';
import {IAttestationRegistry} from "../IAttestationRegistry.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";
import {UintString} from "forge-gas-snapshot/src/utils/UintString.sol";

import {JsonParser} from "../utils/JsonParser.sol";

struct CexInfo {
struct CexInfo {
// The cex name
string cexName;
// jsonPath to get
Expand All @@ -20,10 +20,11 @@ contract AttestationRegistry is Ownable, IAttestationRegistry {
using JsonParser for string;
using UintString for string;
// Attestation with address mapping

mapping(address => Attestation[]) public attestationsOfAddress;
// Cex info mapping cex url => CexInfo
mapping(string => CexInfo) public cexInfoMapping;
// IPrimusZKTLS contract
// IPrimusZKTLS contract
IPrimusZKTLS internal primusZKTLS;
// submission fee
uint256 public submissionFee;
Expand Down Expand Up @@ -57,18 +58,22 @@ contract AttestationRegistry is Ownable, IAttestationRegistry {
* @param _cexNames The cex names such as "binance" "okx" etc.
* @param _jsonPaths The json paths
*/
function setCexAndJsonPath(string[] memory _cexUrls, string[] memory _cexNames, string[] memory _jsonPaths) external onlyOwner {
function setCexAndJsonPath(string[] memory _cexUrls, string[] memory _cexNames, string[] memory _jsonPaths)
external
onlyOwner
{
require(_cexUrls.length == _cexNames.length && _cexNames.length == _jsonPaths.length, "Array length mismatch");
for (uint256 i = 0; i < _cexUrls.length; ++i) {
cexInfoMapping[_cexUrls[i]] = CexInfo({cexName: _cexNames[i], parsePath: _jsonPaths[i]});
}
}

/**
* @dev getCexInfoDetail
* @param _cexUrl The cex URL address
* @return CexInfo
* */
* @dev getCexInfoDetail
* @param _cexUrl The cex URL address
* @return CexInfo
*
*/
function getCexInfoDetail(string memory _cexUrl) external view returns (CexInfo memory) {
require(bytes(cexInfoMapping[_cexUrl].cexName).length > 0, "URL not found");
return cexInfoMapping[_cexUrl];
Expand All @@ -80,7 +85,10 @@ contract AttestationRegistry is Ownable, IAttestationRegistry {
* @param _cexName The cex name such as "binance" "okx" etc.
* @param _jsonPath The parsing path
*/
function addUrlToCexInfo(string memory _cexUrl, string memory _cexName, string memory _jsonPath) external onlyOwner {
function addUrlToCexInfo(string memory _cexUrl, string memory _cexName, string memory _jsonPath)
external
onlyOwner
{
cexInfoMapping[_cexUrl] = CexInfo({cexName: _cexName, parsePath: _jsonPath});
emit UrlToCexInfoAdded(_cexUrl, _cexName);
}
Expand All @@ -105,7 +113,7 @@ contract AttestationRegistry is Ownable, IAttestationRegistry {

// send fee to feeRecipient
if (submissionFee > 0) {
(bool sent, ) = feeRecipient.call{value: msg.value}("");
(bool sent,) = feeRecipient.call{value: msg.value}("");
require(sent, "Failed to send fee");
emit FeeReceived(msg.sender, msg.value);
}
Expand Down Expand Up @@ -151,7 +159,7 @@ contract AttestationRegistry is Ownable, IAttestationRegistry {
* @return Attestation[] memory
*/
function getAttestationByRecipient(address recipient) public view returns (Attestation[] memory) {
require(recipient!= address(0), "Invalid address");
require(recipient != address(0), "Invalid address");
return attestationsOfAddress[recipient];
}

Expand Down
32 changes: 31 additions & 1 deletion src/pool-bin/volume/BinExchangeVolumeHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import "pancake-v4-core/src/pool-cl/interfaces/ICLHooks.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "pancake-v4-core/src/types/BeforeSwapDelta.sol";
import {BinBaseHook} from "../BinBaseHook.sol";
import {BinPoolManager} from "pancake-v4-core/src/pool-bin/BinPoolManager.sol";
import {IPoolManager} from "pancake-v4-core/src/interfaces/IPoolManager.sol";
import {Currency} from "pancake-v4-core/src/types/Currency.sol";
import {IHooks} from "pancake-v4-core/src/interfaces/IHooks.sol";
import {IBinPoolManager} from "pancake-v4-core/src/pool-bin/interfaces/IBinPoolManager.sol";
import {LPFeeLibrary} from "pancake-v4-core/src/libraries/LPFeeLibrary.sol";
import {PoolId, PoolIdLibrary} from "pancake-v4-core/src/types/PoolId.sol";
Expand Down Expand Up @@ -43,18 +45,46 @@ contract BinExchangeVolumeHook is BinBaseHook, BaseFeeDiscountHook {
function afterInitialize(address sender, PoolKey calldata key, uint24 activeId)
external
override
poolManagerOnly
returns (bytes4)
{
poolManager.updateDynamicLPFee(key, getDefaultFee());
poolManager.updateDynamicLPFee(key, defaultFee);
poolFeeMapping[key.toId()] = defaultFee;
poolsInitialized.push(key.toId());
return (this.afterInitialize.selector);
}

function beforeSwap(address, PoolKey calldata key, bool, int128, bytes calldata)
external
override
poolManagerOnly
returns (bytes4, BeforeSwapDelta, uint24)
{
uint24 fee = getFeeDiscount(tx.origin, key);
return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee);
}

/*
@dev Update fee for pool by poolKey
@param fee
@return
*/
function updatePoolFeeByPoolKey(PoolKey memory poolKey, uint24 newBaseFee) external onlyOwner {
poolManager.updateDynamicLPFee(poolKey, newBaseFee);
poolFeeMapping[poolKey.toId()] = newBaseFee;
}

/*
@dev Update fee for pool by poolId
@param fee
@return
*/
function updatePoolFeeByPoolId(PoolId[] memory poolIds, uint24 newBaseFee) external onlyOwner {
for (uint256 i = 0; i < poolIds.length; i++) {
(Currency currency0, Currency currency1, IHooks hooks, IPoolManager manager, uint24 fee, bytes32 parameters)
= poolManager.poolIdToPoolKey(poolIds[i]);
poolManager.updateDynamicLPFee(PoolKey(currency0, currency1, hooks, manager, fee, parameters), newBaseFee);
poolFeeMapping[poolIds[i]] = newBaseFee;
}
}
}
34 changes: 32 additions & 2 deletions src/pool-cl/volume/CLExchangeVolumeHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ pragma solidity ^0.8.24;

import {PoolKey} from "pancake-v4-core/src/types/PoolKey.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "pancake-v4-core/src/types/BeforeSwapDelta.sol";
import {PoolIdLibrary} from "pancake-v4-core/src/types/PoolId.sol";
import {PoolIdLibrary, PoolId} from "pancake-v4-core/src/types/PoolId.sol";
import {ICLPoolManager} from "pancake-v4-core/src/pool-cl/interfaces/ICLPoolManager.sol";
import {IPoolManager} from "pancake-v4-core/src/interfaces/IPoolManager.sol";
import {Currency} from "pancake-v4-core/src/types/Currency.sol";
import {IHooks} from "pancake-v4-core/src/interfaces/IHooks.sol";
import {CLBaseHook} from "../CLBaseHook.sol";
import {IAttestationRegistry} from "../../IAttestationRegistry.sol";
import {BaseFeeDiscountHook} from "../../BaseFeeDiscountHook.sol";
Expand Down Expand Up @@ -44,9 +47,12 @@ contract CLExchangeVolumeHook is CLBaseHook, BaseFeeDiscountHook {
function afterInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, int24 tick)
external
override
poolManagerOnly
returns (bytes4)
{
poolManager.updateDynamicLPFee(key, getDefaultFee());
poolManager.updateDynamicLPFee(key, defaultFee);
poolFeeMapping[key.toId()] = defaultFee;
poolsInitialized.push(key.toId());
return (this.afterInitialize.selector);
}

Expand All @@ -59,4 +65,28 @@ contract CLExchangeVolumeHook is CLBaseHook, BaseFeeDiscountHook {
uint24 fee = getFeeDiscount(tx.origin, key);
return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, fee);
}

/*
@dev Set default fee for pool
@param fee
@return
*/
function updatePoolFeeByPoolKey(PoolKey memory poolKey, uint24 newBaseFee) external onlyOwner {
poolManager.updateDynamicLPFee(poolKey, newBaseFee);
poolFeeMapping[poolKey.toId()] = newBaseFee;
}

/*
@dev Update fee for pool by poolId
@param fee
@return
*/
function updatePoolFeeByPoolId(PoolId[] memory poolIds, uint24 newBaseFee) external onlyOwner {
for (uint256 i = 0; i < poolIds.length; i++) {
(Currency currency0, Currency currency1, IHooks hooks, IPoolManager manager, uint24 fee, bytes32 parameters)
= poolManager.poolIdToPoolKey(poolIds[i]);
poolManager.updateDynamicLPFee(PoolKey(currency0, currency1, hooks, manager, fee, parameters), newBaseFee);
poolFeeMapping[poolIds[i]] = newBaseFee;
}
}
}
8 changes: 4 additions & 4 deletions src/types/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ pragma solidity ^0.8.24;
struct Attestation {
// The address of the user who made the attestation
address recipient;
// The cex name(such as binance, okex, ) of the attestation
string exchange;
// The value of the attestation
uint32 value;
// The cex name(such as binance, okex, ) of the attestation
string exchange;
// The value of the attestation
uint32 value;
// The timestamp of the attestation
uint256 timestamp;
}
18 changes: 13 additions & 5 deletions test/pool-cl/CLExchangeVolumeHookTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity ^0.8.24;

import "./util/MockAttestationRegistry.t.sol";
import {Constants} from "pancake-v4-core/test/pool-cl/helpers/Constants.sol";
import {Currency} from "pancake-v4-core/src/types/Currency.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "pancake-v4-core/src/types/BeforeSwapDelta.sol";
import {CLExchangeVolumeHook} from "../../src/pool-cl/volume/CLExchangeVolumeHook.sol";
Expand Down Expand Up @@ -58,9 +59,9 @@ contract CLExchangeVolumeHookTest is Test {
currency0: Currency.wrap(address(0)), // Replace with actual token address
currency1: Currency.wrap(address(1)), // Replace with actual token address
hooks: IHooks(address(clExchangeVolumeHook)),
poolManager: IPoolManager(address(0)),
fee: 3000, // Example fee, replace with actual value
parameters: keccak256(abi.encodePacked("1"))
poolManager: clPoolManager,
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG, // Example fee, replace with actual value
parameters: bytes32(uint256(IHooks(address(clExchangeVolumeHook)).getHooksRegistrationBitmap())).setTickSpacing(10)
});

// Define valid SwapParams (adjust fields as per actual definition)
Expand All @@ -69,6 +70,13 @@ contract CLExchangeVolumeHookTest is Test {
sqrtPriceLimitX96: 0, // Replace with appropriate value
zeroForOne: true // Example direction
});

clPoolManager.initialize(poolKey,Constants.SQRT_RATIO_1_4 );

//update fee
vm.prank(clExchangeVolumeHook.owner());
clExchangeVolumeHook.updatePoolFeeByPoolKey(poolKey,3000);

console.logString("start swap");
vm.prank(address(clPoolManager));
(bytes4 selector1, BeforeSwapDelta beforeSwapDelta1, uint24 fee1) =
Expand Down Expand Up @@ -99,7 +107,7 @@ contract CLExchangeVolumeHookTest is Test {
clExchangeVolumeHook.setDefaultFee(5000);

// Verify the state update
uint256 updatedFee = clExchangeVolumeHook.getDefaultFee();
uint256 updatedFee = clExchangeVolumeHook.defaultFee();
assertEq(updatedFee, 5000);
}

Expand All @@ -113,7 +121,7 @@ contract CLExchangeVolumeHookTest is Test {
vm.prank(owner); // Mock the caller as the owner
clExchangeVolumeHook.setBaseValue(20000);
// Verify the state update
uint256 updatedFee = clExchangeVolumeHook.getBaseValue();
uint256 updatedFee = clExchangeVolumeHook.baseValue();
assertEq(updatedFee, 20000);
}

Expand Down

0 comments on commit 6c502ff

Please sign in to comment.