Skip to content

Commit

Permalink
feat: add deployment scripts for protocolFeeController (#221)
Browse files Browse the repository at this point in the history
* feat: add deployment scripts for protocolFeeController

* chore: redeployment

* fix: adjust the test error tolerance to 0.05%

* fix: excluded out extreme case for fuzz test
  • Loading branch information
chefburger authored Dec 3, 2024
1 parent d48491a commit 2b79b8b
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 13 deletions.
2 changes: 1 addition & 1 deletion script/01_DeployVault.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract DeployVaultScript is BaseScript {

/// @dev prepare the payload to transfer ownership from deployer to real owner
bytes memory afterDeploymentExecutionPayload =
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("owner"));
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("poolOwner"));

address vault = factory.deploy(
getDeploymentSalt(),
Expand Down
2 changes: 1 addition & 1 deletion script/02_DeployCLPoolManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract DeployCLPoolManagerScript is BaseScript {

/// @dev prepare the payload to transfer ownership from deployer to real owner
bytes memory afterDeploymentExecutionPayload =
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("owner"));
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("poolOwner"));

address clPoolManager = factory.deploy(
getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0
Expand Down
2 changes: 1 addition & 1 deletion script/03_DeployBinPoolManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract DeployBinPoolManagerScript is BaseScript {

/// @dev prepare the payload to transfer ownership from deployer to real owner
bytes memory afterDeploymentExecutionPayload =
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("owner"));
abi.encodeWithSelector(Ownable.transferOwnership.selector, getAddressFromConfig("poolOwner"));

address binPoolManager = factory.deploy(
getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0
Expand Down
57 changes: 57 additions & 0 deletions script/04a_DeployCLProtocolFeeController.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";
import {ProtocolFeeController} from "../src/ProtocolFeeController.sol";
import {Create3Factory} from "pancake-create3-factory/src/Create3Factory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/**
* Step 1: Deploy
* forge script script/04a_DeployCLProtocolFeeController.s.sol:DeployCLProtocolFeeControllerScript -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow
*
* Step 2: Get the ABI-encoded form of the constructor arguments
* cast abi-encode "Constructor(address)" <clPoolManager_addr>
*
* Step 3: Verify
* forge verify-contract <address> ProtocolFeeController --watch --chain <chain_id> \
* --constructor-args <constructor_args_from_step2>
*
* Step 4: Proceed to deploy contract and call protocolFeeController.acceptOwnership
*/
contract DeployCLProtocolFeeControllerScript is BaseScript {
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-CORE/CLProtocolFeeController/0.91");
}

function run() public {
Create3Factory factory = Create3Factory(getAddressFromConfig("create3Factory"));

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

address clPoolManager = getAddressFromConfig("clPoolManager");
console.log("clPoolManager address: ", address(clPoolManager));

/// @dev append the clPoolManager address to the creationCode
bytes memory creationCode =
abi.encodePacked(type(ProtocolFeeController).creationCode, abi.encode(clPoolManager));

/// @dev prepare the payload to transfer ownership from deployer to real owner
bytes memory afterDeploymentExecutionPayload = abi.encodeWithSelector(
Ownable.transferOwnership.selector, getAddressFromConfig("protocolFeeControllerOwner")
);

address clProtocolFeeController = factory.deploy(
getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0
);

console.log("CLProtocolFeeController contract deployed at ", clProtocolFeeController);

vm.stopBroadcast();
}
}
39 changes: 39 additions & 0 deletions script/04b_SetProtocolFeeControllerForCLPool.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";
import {ProtocolFeeController} from "../src/ProtocolFeeController.sol";
import {IProtocolFees} from "../src/interfaces/IProtocolFees.sol";

/**
* Step 1: Set ProtocolFeeController for CLPool
* forge script script/04b_SetProtocolFeeControllerForCLPool.s.sol:SetProtocolFeeControllerForCLPoolScript -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow
*/
contract SetProtocolFeeControllerForCLPoolScript is BaseScript {
function run() public {
// @dev this should be the private key of the poolManager owner instead of the deployer
uint256 ownerPrivateKey = vm.envUint("POOL_OWNER_PRIVATE_KEY");
vm.startBroadcast(ownerPrivateKey);

IProtocolFees clPoolManager = IProtocolFees(getAddressFromConfig("clPoolManager"));
console.log("clPoolManager address: ", address(clPoolManager));

ProtocolFeeController clProtocolFeeController =
ProtocolFeeController(getAddressFromConfig("clProtocolFeeController"));
console.log("clProtocolFeeController address: ", address(clProtocolFeeController));

if (clProtocolFeeController.poolManager() != address(clPoolManager)) {
revert("PoolManager mismatch");
}

IProtocolFees(clPoolManager).setProtocolFeeController(
ProtocolFeeController(getAddressFromConfig("clProtocolFeeController"))
);

vm.stopBroadcast();
}
}
57 changes: 57 additions & 0 deletions script/05a_DeployBinProtocolFeeController.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";
import {ProtocolFeeController} from "../src/ProtocolFeeController.sol";
import {Create3Factory} from "pancake-create3-factory/src/Create3Factory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/**
* Step 1: Deploy
* forge script script/05a_DeployBinProtocolFeeController.s.sol:DeployBinProtocolFeeControllerScript -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow
*
* Step 2: Get the ABI-encoded form of the constructor arguments
* cast abi-encode "Constructor(address)" <binPoolManager_addr>
*
* Step 3: Verify
* forge verify-contract <address> ProtocolFeeController --watch --chain <chain_id> \
* --constructor-args <constructor_args_from_step2>
*
* Step 4: Proceed to deploy contract and call protocolFeeController.acceptOwnership
*/
contract DeployBinProtocolFeeControllerScript is BaseScript {
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-CORE/BinProtocolFeeController/0.91");
}

function run() public {
Create3Factory factory = Create3Factory(getAddressFromConfig("create3Factory"));

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

address binPoolManager = getAddressFromConfig("binPoolManager");
console.log("binPoolManager address: ", address(binPoolManager));

/// @dev append the binPoolManager address to the creationCode
bytes memory creationCode =
abi.encodePacked(type(ProtocolFeeController).creationCode, abi.encode(binPoolManager));

/// @dev prepare the payload to transfer ownership from deployer to real owner
bytes memory afterDeploymentExecutionPayload = abi.encodeWithSelector(
Ownable.transferOwnership.selector, getAddressFromConfig("protocolFeeControllerOwner")
);

address binProtocolFeeController = factory.deploy(
getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0
);

console.log("BinProtocolFeeController contract deployed at ", binProtocolFeeController);

vm.stopBroadcast();
}
}
39 changes: 39 additions & 0 deletions script/05b_SetProtocolFeeControllerForBinPool.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import {BaseScript} from "./BaseScript.sol";
import {ProtocolFeeController} from "../src/ProtocolFeeController.sol";
import {IProtocolFees} from "../src/interfaces/IProtocolFees.sol";

/**
* Step 1: Set ProtocolFeeController for BinPool
* forge script script/05b_SetProtocolFeeControllerForBinPool.s.sol:SetProtocolFeeControllerForBinPoolScript -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow
*/
contract SetProtocolFeeControllerForBinPoolScript is BaseScript {
function run() public {
// @dev this should be the private key of the poolManager owner instead of the deployer
uint256 ownerPrivateKey = vm.envUint("POOL_OWNER_PRIVATE_KEY");
vm.startBroadcast(ownerPrivateKey);

IProtocolFees binPoolManager = IProtocolFees(getAddressFromConfig("binPoolManager"));
console.log("binPoolManager address: ", address(binPoolManager));

ProtocolFeeController binProtocolFeeController =
ProtocolFeeController(getAddressFromConfig("binProtocolFeeController"));
console.log("binProtocolFeeController address: ", address(binProtocolFeeController));

if (binProtocolFeeController.poolManager() != address(binPoolManager)) {
revert("PoolManager mismatch");
}

IProtocolFees(binPoolManager).setProtocolFeeController(
ProtocolFeeController(getAddressFromConfig("binProtocolFeeController"))
);

vm.stopBroadcast();
}
}
4 changes: 3 additions & 1 deletion script/BaseScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ abstract contract BaseScript is Script {

/// @notice must be implemented by the inheriting contract to make sure eth deployment salt is unique
/// since the deployment salt will be the only factor to decide the address of the newly deployed contract
function getDeploymentSalt() public view virtual returns (bytes32);
function getDeploymentSalt() public pure virtual returns (bytes32) {
revert("Deployment salt not set !");
}
}
7 changes: 5 additions & 2 deletions script/config/bsc-testnet.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"create3Factory": "0x38Ab3f2CE00973A51d3A2A04d634C9bcbf20e4e1",
"owner": "0x42571B8414c68B63A2729146CE93F23639d25399",
"poolOwner": "0x42571B8414c68B63A2729146CE93F23639d25399",
"vault": "0xd557753bde3f0EaF32626F8681Ac6d8c1EBA2BBa",
"clPoolManager": "0x70890E308DCE727180ac1B9550928fED342dea52",
"binPoolManager": "0x68554d088F3640Bd2A7B38b43AE70FDcc16ef197"
"binPoolManager": "0x68554d088F3640Bd2A7B38b43AE70FDcc16ef197",
"protocolFeeControllerOwner": "0x42571B8414c68B63A2729146CE93F23639d25399",
"clProtocolFeeController": "0xa3b9Af6E83B5161AC008cafb76712857bE54536D",
"binProtocolFeeController": "0x4eE90A76f4C0b32bce3C941b6Fa409E6c4DCE98A"
}
7 changes: 5 additions & 2 deletions script/config/ethereum-mainnet.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"create3Factory": "0x",
"owner": "0x",
"poolOwner": "0x",
"vault": "0x",
"clPoolManager": "0x",
"binPoolManager": "0x"
"binPoolManager": "0x",
"protocolFeeControllerOwner": "0x",
"clProtocolFeeController": "0x",
"binProtocolFeeController": "0x"
}
7 changes: 5 additions & 2 deletions script/config/ethereum-sepolia.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"create3Factory": "0x38Ab3f2CE00973A51d3A2A04d634C9bcbf20e4e1",
"owner": "0x",
"poolOwner": "0x",
"vault": "0x",
"clPoolManager": "0x",
"binPoolManager": "0x"
"binPoolManager": "0x",
"protocolFeeControllerOwner": "0x",
"clProtocolFeeController": "0x",
"binProtocolFeeController": "0x"
}
56 changes: 53 additions & 3 deletions test/ProtocolFeeController.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,60 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
}
}

function testGetLPFeeFromTotalFee() public {
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
// common case1: totalFee=1%, splitRatio=33%
{
uint24 totalFee = 10000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
assertEq(lpFee, 6722);
}

// common case2: totalFee=0.5%, splitRatio=33%
{
uint24 totalFee = 5000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
assertEq(lpFee, 3355);
}

// common case3: totalFee=0.1%, splitRatio=33%
{
uint24 totalFee = 1000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
assertEq(lpFee, 670);
}

controller.setProtocolFeeSplitRatio(500000);

// common case4: totalFee=1%, splitRatio=50%
{
uint24 totalFee = 10000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
// protocol fee is capped at 0.4% so lpFee will be 0.6% in this case
assertEq(lpFee, 6024);
}

// common case5: totalFee=0.5%, splitRatio=50%
{
uint24 totalFee = 5000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
assertEq(lpFee, 2506);
}

// common case6: totalFee=0.1%, splitRatio=50%
{
uint24 totalFee = 1000;
uint24 lpFee = controller.getLPFeeFromTotalFee(totalFee);
assertEq(lpFee, 500);
}
}

function testGetLPFeeFromTotalFee(uint24 totalFee, uint24 splitRatio) public {
totalFee = uint24(bound(totalFee, 0, LPFeeLibrary.ONE_HUNDRED_PERCENT_FEE));
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
splitRatio = uint24(bound(splitRatio, 0, controller.ONE_HUNDRED_PERCENT_RATIO()));

// ignore extreme case where splitRatio is over 90% to avoid precision loss
splitRatio = uint24(bound(splitRatio, 0, controller.ONE_HUNDRED_PERCENT_RATIO() * 9 / 10));
controller.setProtocolFeeSplitRatio(splitRatio);

// try to simulate the calculation the process of FE initialization pool
Expand All @@ -140,8 +190,8 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
assertApproxEqAbs(
totalFee,
protocolFeeZeroForOne.calculateSwapFee(lpFee),
// keeping the error within 0.01% (can't avoid due to precision loss)
100,
// keeping the error within 0.05% (can't avoid due to precision loss)
500,
"totalFee should be equal to protocolFee + (1 - protocolFee) * lpFee"
);
}
Expand Down

0 comments on commit 2b79b8b

Please sign in to comment.