@@ -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+
30159const initializeFeeSharingCollector = async ( hre , signer ) => {
31160 const {
32161 deployments : { get } ,
0 commit comments