Skip to content

Commit 99c9002

Browse files
committed
add hh task to print protocol withheld fees
1 parent d550f6c commit 99c9002

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

hardhat/tasks/feeSharingCollector.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,135 @@ task(
2727
await setLoanTokenWrbtcAddress(hre, signer, true);
2828
});
2929

30+
task(
31+
"feeSharingCollector:getWithheldFees",
32+
"Print protocol withheld fees by token: symbol | address | amount (4 decimals)"
33+
).setAction(async (_, hre) => {
34+
const { ethers } = hre;
35+
36+
const toCommaSeparated = (value) => {
37+
return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
38+
};
39+
40+
const formatRoundedAmount = (amount, decimals) => {
41+
const tenThousand = ethers.BigNumber.from(10000);
42+
const ten = ethers.BigNumber.from(10);
43+
const decimalsNum = Number(decimals);
44+
45+
let amountInTenThousandths;
46+
if (decimalsNum >= 4) {
47+
const factor = ten.pow(decimalsNum - 4);
48+
amountInTenThousandths = amount.add(factor.div(2)).div(factor);
49+
} else {
50+
const factor = ten.pow(4 - decimalsNum);
51+
amountInTenThousandths = amount.mul(factor);
52+
}
53+
54+
const whole = amountInTenThousandths.div(tenThousand).toString();
55+
const fraction = amountInTenThousandths.mod(tenThousand).toString().padStart(4, "0");
56+
return `${toCommaSeparated(whole)}.${fraction}`;
57+
};
58+
59+
const readTokenMeta = async (tokenAddress) => {
60+
const rbtcSpecialAddress = "0xeabd29be3c3187500df86a2613c6470e12f2d77d";
61+
if (tokenAddress.toLowerCase() === rbtcSpecialAddress) {
62+
return { symbol: "RBTC", decimals: 18 };
63+
}
64+
65+
let symbol = "UNKNOWN";
66+
let decimals;
67+
68+
try {
69+
const tokenStringSymbol = await ethers.getContractAt(
70+
["function symbol() view returns (string)"],
71+
tokenAddress
72+
);
73+
symbol = await tokenStringSymbol.symbol();
74+
} catch (e) {
75+
try {
76+
const tokenBytesSymbol = await ethers.getContractAt(
77+
["function symbol() view returns (bytes32)"],
78+
tokenAddress
79+
);
80+
const rawSymbol = await tokenBytesSymbol.symbol();
81+
symbol = ethers.utils.parseBytes32String(rawSymbol);
82+
} catch (_ignored) {}
83+
}
84+
85+
try {
86+
const tokenDecimals = await ethers.getContractAt(
87+
["function decimals() view returns (uint8)"],
88+
tokenAddress
89+
);
90+
decimals = await tokenDecimals.decimals();
91+
} catch (e) {
92+
throw new Error(
93+
`Could not read decimals() for token ${tokenAddress}. ERC20 token must implement decimals().`
94+
);
95+
}
96+
97+
return { symbol, decimals };
98+
};
99+
100+
const protocol = await ethers.getContract("ISovryn");
101+
const fscAddress = await protocol.feesController();
102+
if (!ethers.utils.isAddress(fscAddress)) {
103+
throw new Error(
104+
`Invalid FeeSharingCollector address from ISovryn.feesController(): ${fscAddress}`
105+
);
106+
}
107+
const feeSharingCollector = await ethers.getContractAt("FeeSharingCollector", fscAddress);
108+
const network = await ethers.provider.getNetwork();
109+
const tokenAddresses = await feeSharingCollector.getProtocolWithholdTokensList();
110+
111+
if (tokenAddresses.length === 0) {
112+
logger.info(
113+
`No tokens found in protocol withhold list. network=${network.chainId} fsc=${fscAddress}`
114+
);
115+
return;
116+
}
117+
118+
const rows = [];
119+
for (const tokenAddress of tokenAddresses) {
120+
const { symbol, decimals } = await readTokenMeta(tokenAddress);
121+
const rawWithheldAmount = await feeSharingCollector.getProtocolWithheldFees(tokenAddress);
122+
rows.push({
123+
symbol,
124+
address: tokenAddress,
125+
amount: formatRoundedAmount(rawWithheldAmount, decimals),
126+
});
127+
}
128+
129+
const headers = {
130+
symbol: "TOKEN",
131+
address: "ADDRESS",
132+
amount: "WITHHELD",
133+
};
134+
135+
const symbolWidth = Math.max(headers.symbol.length, ...rows.map((r) => r.symbol.length));
136+
const addressWidth = Math.max(headers.address.length, ...rows.map((r) => r.address.length));
137+
const amountWidth = Math.max(headers.amount.length, ...rows.map((r) => r.amount.length));
138+
139+
const headerLine = `${headers.symbol.padEnd(symbolWidth)} | ${headers.address.padEnd(
140+
addressWidth
141+
)} | ${headers.amount.padStart(amountWidth)}`;
142+
const dividerLine = `${"-".repeat(symbolWidth)}-+-${"-".repeat(
143+
addressWidth
144+
)}-+-${"-".repeat(amountWidth)}`;
145+
146+
console.log(`\nnetwork: ${network.chainId}`);
147+
console.log(`feeSharingCollector: ${fscAddress}\n`);
148+
console.log(headerLine);
149+
console.log(dividerLine);
150+
rows.forEach((row) => {
151+
console.log(
152+
`${row.symbol.padEnd(symbolWidth)} | ${row.address.padEnd(
153+
addressWidth
154+
)} | ${row.amount.padStart(amountWidth)}`
155+
);
156+
});
157+
});
158+
30159
const initializeFeeSharingCollector = async (hre, signer) => {
31160
const {
32161
deployments: { get },

0 commit comments

Comments
 (0)