Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 944d4ee

Browse files
authored
feat: add support for different fees per dest domain to basic/percentage fee handler (#206)
1 parent 746d51e commit 944d4ee

18 files changed

+112
-224
lines changed

contracts/handlers/fee/BasicFeeHandler.sol

+14-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma solidity 0.8.11;
44

55
import "../../interfaces/IFeeHandler.sol";
66
import "../../utils/AccessControl.sol";
7+
import "../FeeHandlerRouter.sol";
78

89
/**
910
@title Handles deposit fees.
@@ -13,8 +14,8 @@ import "../../utils/AccessControl.sol";
1314
contract BasicFeeHandler is IFeeHandler, AccessControl {
1415
address public immutable _bridgeAddress;
1516
address public immutable _feeHandlerRouterAddress;
17+
mapping (uint8 => mapping(bytes32 => uint256)) public _domainResourceIDToFee;
1618

17-
uint256 public _fee;
1819

1920
event FeeChanged(
2021
uint256 newFee
@@ -71,8 +72,9 @@ contract BasicFeeHandler is IFeeHandler, AccessControl {
7172
@param feeData Additional data to be passed to the fee handler.
7273
*/
7374
function collectFee(address sender, uint8 fromDomainID, uint8 destinationDomainID, bytes32 resourceID, bytes calldata depositData, bytes calldata feeData) virtual payable external onlyBridgeOrRouter {
74-
if (msg.value != _fee) revert IncorrectFeeSupplied(msg.value);
75-
emit FeeCollected(sender, fromDomainID, destinationDomainID, resourceID, _fee, address(0));
75+
uint256 currentFee = _domainResourceIDToFee[destinationDomainID][resourceID];
76+
if (msg.value != currentFee) revert IncorrectFeeSupplied(msg.value);
77+
emit FeeCollected(sender, fromDomainID, destinationDomainID, resourceID, currentFee, address(0));
7678
}
7779

7880
/**
@@ -86,17 +88,20 @@ contract BasicFeeHandler is IFeeHandler, AccessControl {
8688
@return Returns the fee amount.
8789
*/
8890
function calculateFee(address sender, uint8 fromDomainID, uint8 destinationDomainID, bytes32 resourceID, bytes calldata depositData, bytes calldata feeData) virtual external view returns(uint256, address) {
89-
return (_fee, address(0));
91+
return (_domainResourceIDToFee[destinationDomainID][resourceID], address(0));
9092
}
9193

9294
/**
93-
@notice Sets new value of the fee.
95+
@notice Maps the {newFee} to {destinantionDomainID} to {resourceID} in {_domainResourceIDToFee}.
9496
@notice Only callable by admin.
95-
@param newFee Value {_fee} will be updated to.
97+
@param destinationDomainID ID of chain fee will be set.
98+
@param resourceID ResourceID for which fee will be set.
99+
@param newFee Value to which fee will be updated to for the provided {destinantionDomainID} and {resourceID}.
96100
*/
97-
function changeFee(uint256 newFee) external onlyAdmin {
98-
require(_fee != newFee, "Current fee is equal to new fee");
99-
_fee = newFee;
101+
function changeFee(uint8 destinationDomainID, bytes32 resourceID, uint256 newFee) external onlyAdmin {
102+
uint256 currentFee = _domainResourceIDToFee[destinationDomainID][resourceID];
103+
require(currentFee != newFee, "Current fee is equal to new fee");
104+
_domainResourceIDToFee[destinationDomainID][resourceID] = newFee;
100105
emit FeeChanged(newFee);
101106
}
102107

contracts/handlers/fee/PercentageERC20FeeHandlerEVM.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contract PercentageERC20FeeHandlerEVM is BasicFeeHandler, ERC20Safe {
1616
uint32 public constant HUNDRED_PERCENT = 1e8;
1717

1818
/**
19-
@notice _fee inherited from BasicFeeHandler in this implementation is
19+
@notice _domainResourceIDToFee[destinationDomainID][resourceID] inherited from BasicFeeHandler in this implementation is
2020
in BPS and should be multiplied by 10000 to avoid precision loss
2121
*/
2222
struct Bounds {
@@ -61,7 +61,7 @@ contract PercentageERC20FeeHandlerEVM is BasicFeeHandler, ERC20Safe {
6161

6262
(uint256 depositAmount) = abi.decode(depositData, (uint256));
6363

64-
fee = depositAmount * _fee / HUNDRED_PERCENT; // 10000 for BPS and 10000 to avoid precision loss
64+
fee = depositAmount * _domainResourceIDToFee[destinationDomainID][resourceID] / HUNDRED_PERCENT; // 10000 for BPS and 10000 to avoid precision loss
6565

6666
if (fee < bounds.lowerBound) {
6767
fee = bounds.lowerBound;

migrations/2_deploy_contracts.js

-65
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// The Licensed Work is (c) 2022 Sygma
22
// SPDX-License-Identifier: LGPL-3.0-only
33

4-
const Ethers = require("ethers");
5-
64
const Helpers = require("../test/helpers");
75
const Utils = require("./utils");
86

@@ -18,7 +16,6 @@ const PermissionedGenericHandlerContract = artifacts.require(
1816
);
1917
const FeeRouterContract = artifacts.require("FeeHandlerRouter");
2018
const BasicFeeHandlerContract = artifacts.require("BasicFeeHandler");
21-
const DynamicFeeHandlerContract = artifacts.require("DynamicERC20FeeHandlerEVM");
2219
const PercentageFeeHandler = artifacts.require("PercentageERC20FeeHandlerEVM");
2320

2421
module.exports = async function (deployer, network) {
@@ -68,41 +65,12 @@ module.exports = async function (deployer, network) {
6865
bridgeInstance.address,
6966
feeRouterInstance.address
7067
);
71-
const dynamicFeeHandlerInstance = await deployer.deploy(
72-
DynamicFeeHandlerContract,
73-
bridgeInstance.address,
74-
feeRouterInstance.address
75-
);
7668
const percentageFeeHandlerInstance = await deployer.deploy(
7769
PercentageFeeHandler,
7870
bridgeInstance.address,
7971
feeRouterInstance.address
8072
)
8173

82-
// setup fee router and fee handlers
83-
await bridgeInstance.adminChangeFeeHandler(feeRouterInstance.address);
84-
if(Object.keys(currentNetworkConfig.fee.oracle).length != 0){
85-
await dynamicFeeHandlerInstance.setFeeOracle(
86-
currentNetworkConfig.fee.oracle.address
87-
);
88-
await dynamicFeeHandlerInstance.setFeeProperties(
89-
currentNetworkConfig.fee.oracle.gasUsed,
90-
currentNetworkConfig.fee.oracle.feePercentage
91-
);
92-
}
93-
94-
if(Object.keys(currentNetworkConfig.fee.basic).length != 0) {
95-
await basicFeeHandlerInstance.changeFee(
96-
Ethers.utils.parseEther(currentNetworkConfig.fee.basic.fee).toString()
97-
);
98-
}
99-
100-
if(Object.keys(currentNetworkConfig.fee.percentage).length != 0) {
101-
await percentageFeeHandlerInstance.changeFee(
102-
currentNetworkConfig.fee.percentage.fee
103-
);
104-
}
105-
10674
console.table({
10775
"Deployer Address": deployerAddress,
10876
"Domain ID": currentNetworkConfig.domainID,
@@ -111,7 +79,6 @@ module.exports = async function (deployer, network) {
11179
"ERC721Handler Address": erc721HandlerInstance.address,
11280
"FeeRouterContract Address": feeRouterInstance.address,
11381
"BasicFeeHandler Address": basicFeeHandlerInstance.address,
114-
"DynamicFeeHandler Address": dynamicFeeHandlerInstance.address,
11582
"PercentageFeeHandler Address": percentageFeeHandlerInstance.address
11683
});
11784

@@ -123,22 +90,6 @@ module.exports = async function (deployer, network) {
12390
bridgeInstance,
12491
erc20HandlerInstance
12592
);
126-
await Utils.setupFee(
127-
networksConfig,
128-
feeRouterInstance,
129-
dynamicFeeHandlerInstance,
130-
basicFeeHandlerInstance,
131-
percentageFeeHandlerInstance,
132-
erc20
133-
);
134-
135-
if(Object.keys(currentNetworkConfig.fee.percentage).length != 0) {
136-
await percentageFeeHandlerInstance.changeFeeBounds(
137-
erc20.resourceID,
138-
Ethers.utils.parseEther(currentNetworkConfig.fee.percentage.lowerBound).toString(),
139-
Ethers.utils.parseEther(currentNetworkConfig.fee.percentage.upperBound).toString()
140-
);
141-
}
14293

14394
console.log(
14495
"-------------------------------------------------------------------------------"
@@ -159,14 +110,6 @@ module.exports = async function (deployer, network) {
159110
bridgeInstance,
160111
erc721HandlerInstance
161112
);
162-
await Utils.setupFee(
163-
networksConfig,
164-
feeRouterInstance,
165-
dynamicFeeHandlerInstance,
166-
basicFeeHandlerInstance,
167-
percentageFeeHandlerInstance,
168-
erc721
169-
);
170113

171114
console.log(
172115
"-------------------------------------------------------------------------------"
@@ -192,14 +135,6 @@ module.exports = async function (deployer, network) {
192135
bridgeInstance,
193136
permissionedGenericHandlerInstance
194137
);
195-
await Utils.setupFee(
196-
networksConfig,
197-
feeRouterInstance,
198-
dynamicFeeHandlerInstance,
199-
basicFeeHandlerInstance,
200-
percentageFeeHandlerInstance,
201-
generic
202-
);
203138

204139
console.log(
205140
"-------------------------------------------------------------------------------"

migrations/3_deploy_permissionlessGenericHandler.js

-22
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ const BridgeContract = artifacts.require("Bridge");
88
const PermissionlessGenericHandlerContract = artifacts.require(
99
"PermissionlessGenericHandler"
1010
);
11-
const FeeRouterContract = artifacts.require("FeeHandlerRouter");
12-
const BasicFeeHandlerContract = artifacts.require("BasicFeeHandler");
13-
const PercentageFeeHandler = artifacts.require("PercentageERC20FeeHandlerEVM");
14-
const DynamicGenericFeeHandlerEVMContract = artifacts.require("DynamicGenericFeeHandlerEVM");
1511

1612
module.exports = async function (deployer, network) {
1713
const networksConfig = Utils.getNetworksConfig();
@@ -25,23 +21,13 @@ module.exports = async function (deployer, network) {
2521
) {
2622
// fetch deployed contracts addresses
2723
const bridgeInstance = await BridgeContract.deployed();
28-
const feeRouterInstance = await FeeRouterContract.deployed();
29-
const basicFeeHandlerInstance = await BasicFeeHandlerContract.deployed();
30-
const percentageFeeHandlerInstance = await PercentageFeeHandler.deployed();
3124

3225
// deploy generic handler
3326
const permissionlessGenericHandlerInstance = await deployer.deploy(
3427
PermissionlessGenericHandlerContract,
3528
bridgeInstance.address
3629
);
3730

38-
// deploy generic dynamic fee handler
39-
const dynamicFeeHandlerInstance = await deployer.deploy(
40-
DynamicGenericFeeHandlerEVMContract,
41-
bridgeInstance.address,
42-
feeRouterInstance.address
43-
)
44-
4531
console.log(
4632
"-------------------------------------------------------------------------------"
4733
);
@@ -72,13 +58,5 @@ module.exports = async function (deployer, network) {
7258
bridgeInstance.address,
7359
genericHandlerSetResourceData
7460
);
75-
await Utils.setupFee(
76-
networksConfig,
77-
feeRouterInstance,
78-
dynamicFeeHandlerInstance,
79-
basicFeeHandlerInstance,
80-
percentageFeeHandlerInstance,
81-
currentNetworkConfig.permissionlessGeneric
82-
);
8361
}
8462
};

migrations/4_deploy_xc20_contracts.js

-17
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ const Utils = require("./utils");
55

66
const BridgeContract = artifacts.require("Bridge");
77
const XC20HandlerContract = artifacts.require("XC20Handler");
8-
const FeeRouterContract = artifacts.require("FeeHandlerRouter");
9-
const BasicFeeHandlerContract = artifacts.require("BasicFeeHandler");
10-
const DynamicFeeHandlerContract = artifacts.require("DynamicERC20FeeHandlerEVM");
11-
const PercentageFeeHandler = artifacts.require("PercentageERC20FeeHandlerEVM");
128

139

1410
module.exports = async function (deployer, network) {
@@ -27,11 +23,6 @@ module.exports = async function (deployer, network) {
2723

2824
// fetch deployed contracts addresses
2925
const bridgeInstance = await BridgeContract.deployed();
30-
const feeRouterInstance = await FeeRouterContract.deployed();
31-
const basicFeeHandlerInstance = await BasicFeeHandlerContract.deployed();
32-
const dynamicFeeHandlerInstance =
33-
await DynamicFeeHandlerContract.deployed();
34-
const percentageFeeHandlerInstance = await PercentageFeeHandler.deployed();
3526

3627
// deploy XC20 contracts
3728
await deployer.deploy(XC20HandlerContract, bridgeInstance.address);
@@ -40,14 +31,6 @@ module.exports = async function (deployer, network) {
4031
// setup xc20 tokens
4132
for (const xc20 of currentNetworkConfig.xc20) {
4233
await Utils.setupErc20(deployer, xc20, bridgeInstance, xc20HandlerInstance);
43-
await Utils.setupFee(
44-
networksConfig,
45-
feeRouterInstance,
46-
dynamicFeeHandlerInstance,
47-
basicFeeHandlerInstance,
48-
percentageFeeHandlerInstance,
49-
xc20
50-
);
5134

5235
console.log(
5336
"-------------------------------------------------------------------------------"

migrations/5_renounceAdmin.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const AccessControlSegregatorContract = artifacts.require(
88
);
99
const FeeRouterContract = artifacts.require("FeeHandlerRouter");
1010
const BasicFeeHandlerContract = artifacts.require("BasicFeeHandler");
11-
const DynamicFeeHandlerContract = artifacts.require("DynamicERC20FeeHandlerEVM");
11+
const PercentageFeeHandlerContract = artifacts.require("PercentageERC20FeeHandlerEVM");
1212

1313
module.exports = async function (deployer, network) {
1414
const networksConfig = Utils.getNetworksConfig();
@@ -21,8 +21,7 @@ module.exports = async function (deployer, network) {
2121
await AccessControlSegregatorContract.deployed();
2222
const feeRouterInstance = await FeeRouterContract.deployed();
2323
const basicFeeHandlerInstance = await BasicFeeHandlerContract.deployed();
24-
const dynamicFeeHandlerInstance =
25-
await DynamicFeeHandlerContract.deployed();
24+
const percentageFeeHandlerInstance = await PercentageFeeHandlerContract.deployed();
2625

2726
if (currentNetworkConfig.access.feeHandlerAdmin) {
2827
console.log(
@@ -33,7 +32,7 @@ module.exports = async function (deployer, network) {
3332
await basicFeeHandlerInstance.renounceAdmin(
3433
currentNetworkConfig.access.feeHandlerAdmin
3534
);
36-
await dynamicFeeHandlerInstance.renounceAdmin(
35+
await percentageFeeHandlerInstance.renounceAdmin(
3736
currentNetworkConfig.access.feeHandlerAdmin
3837
);
3938
}

migrations/utils.js

+3-40
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable max-len */
12
// The Licensed Work is (c) 2022 Sygma
23
// SPDX-License-Identifier: LGPL-3.0-only
34

@@ -17,12 +18,6 @@ const DEFAULT_CONFIG_PATH = "./migrations/local.json";
1718
const emptySetResourceData = "0x";
1819
const erc20TokenAmount = Ethers.utils.parseUnits("1000", 18);
1920

20-
const FeeType = {
21-
ORACLE: "oracle",
22-
BASIC: "basic",
23-
PERCENTAGE: "PERCENTAGE"
24-
}
25-
2621
function getNetworksConfig() {
2722
let path = parseArgs(process.argv.slice(2))["file"];
2823
if (path == undefined) {
@@ -32,37 +27,6 @@ function getNetworksConfig() {
3227
return JSON.parse(fs.readFileSync(path));
3328
}
3429

35-
async function setupFee(
36-
networksConfig,
37-
feeRouterInstance,
38-
dynamicFeeHandlerInstance,
39-
basicFeeHandlerInstance,
40-
percentageFeeHandlerInstance,
41-
token
42-
) {
43-
for await (const network of Object.values(networksConfig)) {
44-
if (token.feeType == FeeType.ORACLE) {
45-
await feeRouterInstance.adminSetResourceHandler(
46-
network.domainID,
47-
token.resourceID,
48-
dynamicFeeHandlerInstance.address
49-
);
50-
} else if (token.feeType == FeeType.BASIC) {
51-
await feeRouterInstance.adminSetResourceHandler(
52-
network.domainID,
53-
token.resourceID,
54-
basicFeeHandlerInstance.address
55-
);
56-
} else if (token.feeType == FeeType.PERCENTAGE) {
57-
await feeRouterInstance.adminSetResourceHandler(
58-
network.domainID,
59-
token.resourceID,
60-
percentageFeeHandlerInstance.address
61-
)
62-
}
63-
}
64-
}
65-
6630
async function setupErc20(
6731
deployer,
6832
erc20,
@@ -259,12 +223,11 @@ async function getDeployerAddress(deployer) {
259223
}
260224

261225
module.exports = {
262-
setupFee,
263226
setupErc20,
264227
setupErc721,
265228
setupGeneric,
266229
getNetworksConfig,
267230
migrateToNewTokenHandler,
268231
redeployHandler,
269-
getDeployerAddress
270-
};
232+
getDeployerAddress,
233+
}

0 commit comments

Comments
 (0)