Debt DAO contest - tnevler's results

A cryptonative credit marketplace for fully anon and trustless loans to DAOs.

General Information

Platform: Code4rena

Start Date: 03/11/2022

Pot Size: $115,500 USDC

Total HM: 17

Participants: 120

Period: 7 days

Judge: LSDan

Total Solo HM: 1

Id: 174

League: ETH

Debt DAO

Findings Distribution

Researcher Performance

Rank: 30/120

Findings: 2

Award: $669.35

QA:
grade-a
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Report

Non-Critical Issues

[N-1]: Function defines a named return variable but then it uses return statements

Context:

  1. return id; L453
  2. return state.claimRevenue(revenueContract, token, data); L74
  3. return state.claimEscrow(token); L90
  4. return (c, principal, interest); L97
  5. return credit; L160
  6. return claimed; L57
  7. return claimed; L101
  8. return claimed; L121

Recommendation:

Choose named return variable or return statement. It is unnecessary to use both.

[N-2]: Wrong order of functions

Context:

  1. modifier whileActive (modifier definition can not go after internal function)
  2. SpigotedLine.unused (external function can not go after internal function)
  3. SpigotedLine.receive (receive function must be after constructor and before external functions)
  4. SecuredLine.rollover (external function can not go after internal function)
  5. Spigot.claimRevenue (external function can not go after external view function)
  6. Escrow.isLiquidatable (external function can not go after external view function)
  7. EscrowLib.addCollateral (external function can not go after public function)
  8. SpigotLib.operate (external function can not go after public function)

Description:

According to official solidity documentation functions should be grouped according to their visibility and ordered:

  • constructor

  • receive function (if exists)

  • fallback function (if exists)

  • external

  • public

  • internal

  • private

Within a grouping, place the view and pure functions last.

Recommendation:

Put the functions in the correct order according to the documentation.

[N-3]: Require() statements should have descriptive reason string

Context:

  1. require(uint(status) >= uint( LineLib.STATUS.ACTIVE)); L112
  2. require(interestRate.setRate(id, drate, frate)); L241
  3. require(interestRate.setRate(id, drate, frate)); L259
  4. require(amount <= credit.principal + credit.interestAccrued); L326
  5. require(defaultRevenueSplit_ <= SpigotedLineLib.MAX_SPLIT); L62
  6. require(amount <= unusedTokens[credit.token]); L143
  7. require(msg.sender == borrower); L160
  8. require(msg.sender == arbiter); L239
  9. require(escrow_.liquidate(amount, targetToken, to)); L64
  10. require(escrow.updateLine(newLine)); L90
  11. require(amount > 0); L91
  12. require(msg.sender == ILineOfCredit(self.line).arbiter()); L105
  13. require(amount > 0); L161
  14. require(amount > 0); L198
  15. require(msg.sender == self.line); L216
  16. require(LineLib.sendOutTokenOrETH(token, self.treasury, claimed - escrowedAmount)); L96
  17. require(revenueContract != address(this)); L128
  18. require(self.settings[revenueContract].transferOwnerFunction == bytes4(0)); L130
  19. require(success); L155
  20. require(newOwner != address(0)); L180
  21. require(newOperator != address(0)); L189
  22. require(newTreasury != address(0)); L201
  23. require(ISpigot(spigot).updateOwner(newLine)); L147

[N-4]: NatSpec is missing

Context:

  1. function init() external virtual returns(LineLib.STATUS) { L64
  2. function _init() internal virtual returns(LineLib.STATUS) { L69
  3. modifier whileActive() { L78
  4. modifier whileBorrowing() { L83
  5. modifier onlyBorrower() { L88
  6. function counts() external view returns (uint256, uint256) { L117
  7. function _healthcheck() internal virtual returns (LineLib.STATUS) { L121
  8. function declareInsolvent() external whileBorrowing returns(bool) { L143
  9. function _canDeclareInsolvent() internal virtual returns(bool) { L157
  10. function updateOutstandingDebt() external override returns (uint256, uint256) { L163
  11. function _updateOutstandingDebt() L167
  12. function accrueInterest() external override returns(bool) { L200
  13. function addCredit( L223
  14. function setRates( L247
  15. function increaseCredit(bytes32 id, uint256 amount) L265
  16. function depositAndClose() L292
  17. function depositAndRepay(uint256 amount) L315
  18. function borrow(bytes32 id, uint256 amount) L340
  19. function withdraw(bytes32 id, uint256 amount) L370
  20. function close(bytes32 id) external payable override returns (bool) { L388
  21. function unused(address token) external view returns (uint256) { L78
  22. function claimAndRepay(address claimToken, bytes calldata zeroExTradeData) L93
  23. function useAndRepay(uint256 amount) external whileBorrowing returns(bool) { L137
  24. function claimAndTrade(address claimToken, bytes calldata zeroExTradeData) L154
  25. function updateOwnerSplit(address revenueContract) external returns (bool) { L213
  26. function addSpigot( L223
  27. function updateWhitelist(bytes4 func, bool allowed) L235
  28. function releaseSpigot(address to) external returns (bool) { L244
  29. function sweep(address to, address token) external nonReentrant returns (uint256) { L255
  30. constructor( L13
  31. function rollover(address newLine) L48
  32. function _healthcheck() internal override(EscrowedLine, LineOfCredit) returns(LineLib.STATUS) { L97
  33. constructor(address _escrow) { L16
  34. function owner() external view returns (address) { L41
  35. function operator() external view returns (address) { L45
  36. function treasury() external view returns (address) { L49
  37. function updateOwnerSplit(address revenueContract, uint8 ownerSplit) L146
  38. function getSetting(address revenueContract) L220
  39. constructor(address _registry) { L15
  40. function accrueInterest( L34
  41. function _accrueInterest( L42
  42. function setRate( L74
  43. constructor( L20
  44. function deployEscrow( L42
  45. function deploySpigot( L51
  46. function deploySecuredLine(address borrower, uint256 ttl) L59
  47. function deploySecuredLineWithConfig(CoreLineParams calldata coreParams) L87
  48. event AddCredit( L16
  49. function getOutstandingDebt( L73
  50. function addCollateral(EscrowState storage self, address oracle, uint256 amount, address token) L87
  51. function enableCollateral(EscrowState storage self, address oracle, address token) external returns (bool) { L104
  52. function releaseCollateral( L152
  53. function getCollateralRatio(EscrowState storage self, address oracle) external returns (uint256) { L182
  54. function getCollateralValue(EscrowState storage self, address oracle) external returns (uint256) { L187
  55. function liquidate( L192
  56. function isLiquidatable(EscrowState storage self, address oracle, uint256 minimumCollateralRatio) external returns(bool) { L210
  57. function updateLine(EscrowState storage self, address _line) external returns(bool) { L215
  58. function _claimRevenue(SpigotState storage self, address revenueContract, address token, bytes calldata data) L29
  59. function operate(SpigotState storage self, address revenueContract, bytes calldata data) external returns (bool) { L61
  60. function claimRevenue(SpigotState storage self, address revenueContract, address token, bytes calldata data) L83
  61. function claimEscrow(SpigotState storage self, address token) L105
  62. function addSpigot(SpigotState storage self, address revenueContract, ISpigot.Setting memory setting) external returns (bool) { L125
  63. function removeSpigot(SpigotState storage self, address revenueContract) L143
  64. function updateOwnerSplit(SpigotState storage self, address revenueContract, uint8 ownerSplit) L164
  65. function updateOwner(SpigotState storage self, address newOwner) external returns (bool) { L178
  66. function updateOperator(SpigotState storage self, address newOperator) external returns (bool) { L187
  67. function updateTreasury(SpigotState storage self, address newTreasury) external returns (bool) { L196
  68. function updateWhitelistedFunction(SpigotState storage self, bytes4 func, bool allowed) external returns (bool) { L208
  69. function getEscrowed(SpigotState storage self, address token) external view returns (uint256) { L216
  70. function isWhitelisted(SpigotState storage self, bytes4 func) external view returns(bool) { L221
  71. function getSetting(SpigotState storage self, address revenueContract) L227
  72. function trade( L120
  73. function canDeclareInsolvent(address spigot, address arbiter) external view returns (bool) { L151
  74. function _mutualConsent(address _signerOne, address _signerTwo) internal returns(bool) { L38
  75. function _getNonCaller(address _signerOne, address _signerTwo) internal view returns(address) { L65
  76. event DeployedSecuredLine( L8
  77. event DeployedSpigot( L16
  78. event DeployedEscrow( L23
  79. function transferModulesToLine(address line, address spigot, address escrow) external { L57
  80. function deploySecuredLine( L77

[N-5]: Typos

Context:

  1. // Line Financials aggregated accross all existing Credit L37 (Change accross to across)
  2. * @param arbiter_ - A neutral party with some special priviliges on behalf of Borrower and Lender. L45 (Change priviliges to privileges)
  3. * @dev - updates `status` variable in storage if current status is diferent from existing status L107 (Change diferent to different)
  4. @notice - accrues token demoninated interest on a lender's position. L213 (Change demoninated to denominated)
  5. // ensure that borrowing doesnt cause Line to be LIQUIDATABLE L355 (Change doesnt to doesn't)
  6. // ensure all money owed is accounted for. Accrue facility fee since prinicpal was paid off L395 (Change prinicpal to principal)
  7. // Internal funcs // L412 (Change funcs to functions)
  8. * @notice - updates `status` variable in storage if current status is diferent from existing status. L416 (Change diferent to different)
  9. * @notice - Insert `p` into the next availble FIFO position in the repayment queue L510 (Change availble to available)
  10. * @param arbiter_ - neutral party with some special priviliges on behalf of borrower and lender L46 (Change priviliges to privileges)
  11. * @notice requires Spigot contract itselgf to be transfered to Arbiter and sold off to a 3rd party before declaring insolvent L84 (Change itselgf to itself)
  12. * @notice requires Spigot contract itselgf to be transfered to Arbiter and sold off to a 3rd party before declaring insolvent L84 (Change transfered to transferred)
  13. spigot.claimEscrow(claimToken) : // same asset. dont trade L107 (Change dont to don't)
  14. spigot.claimEscrow(claimToken) : // same asset. dont trade L164 (Change dont to don't)
  15. * @dev - priviliged internal function L180 (Change priviliged to privileged)
  16. // we dont use revenue after this so can store now L204 (Change dont to don't)
  17. // we dont check borrower is same on both lines because borrower might want new address managing new line L58 (Change dont to don't)
  18. * @return isInsolvent - if the entire Line including all collateral sources is fuly insolvent. L110 (Change fuly to fully)
  19. * @notice helper function to allow borrower to easily swithc collateral to a new Line after repyment L84 (Change swithc to switch)
  20. * @notice helper function to allow borrower to easily swithc collateral to a new Line after repyment L84 (Change repyment to repayment)
  21. // ##### Claimoooor ##### L54 (Change Claimoooor to Claimor)
  22. // ##### OPERATOOOR ##### L96 (Change OPERATOOOR to OPERATOR)
  23. // ##### OPERATOOOR ##### L97 (Change OPERATOOOR to OPERATOR)
  24. // ##### Maintainooor ##### L115 (Change to Maintainor)
  25. * @dev - revenuContract's transfer func MUST only accept one paramteter which is the new owner. L133 (Change paramteter to parameter)
  26. * @dev - Used if we setup Escrow before Line exists. Line has no way to interface with this function so once transfered `line` is set forever L71 (Change transfered to transferred)
  27. * @notice - allows the lines arbiter to enable thdeposits of an asset L94 (Change thdeposits to the deposits)
  28. * - gives better risk segmentation forlenders L95 (Change forlenders to for lenders)
  29. * @notice Interest rate / acrrued interest calculation contract for Line of Credit contracts L17 (Change acrrued to accrued)
  30. * @param oracle - interset rate contract used by line that will calculate interest owed L123 (Change interset to interest)
  31. * @param credit - The lender position that is being bwithdrawn from L200 (Change bwithdrawn to withdrawn)
  32. // emit events before seeting to 0 L221 (Change seeting to setting)
  33. * @param interest - interset rate contract used by line that will calculate interest owed L237 (Change interset to interest)
  34. // get token demoninated interest accrued L250 (Change demoninated to denominated)
  35. // cap so uint doesnt overflow in split calculations. L53 (Change doesnt to doesn't)
  36. * @dev priviliged internal function L44 (Change priviliged to privileged)
  37. * @param spigot - The Spigot to claim from. Must be owned by adddress(this) L48 (Change adddress(this) to address(this))
  38. * @notice - Transfers ownership of the entire Spigot and its revenuw streams from its then Owner to either L188 (Change revenuw to revenue)

[N-6]: TODO

Context:

  1. // TODO: test L140
  2. // TODO: test L145

[N-7]: Natspec is incomplete

Context:

  1. function _updateStatus(LineLib.STATUS status_) internal returns(LineLib.STATUS) { L421 (param tag status_ is missing)
  2. function _createCredit( L435 (return tag id is missing)
  3. function _repay(Credit memory credit, bytes32 id, uint256 amount) L465 (param tag credit is missing)
  4. function _close(Credit memory credit, bytes32 id) internal virtual returns (bool) { L483 (param tags credit and id are missing)
  5. function liquidate( L80 (return tag is missing)
  6. function _init() internal virtual returns(LineLib.STATUS) { L25 (return tag is missing)
  7. function _healthcheck() virtual internal returns(LineLib.STATUS) { L34 (return tag is missing)
  8. function _rollover(address newLine) internal virtual returns(bool) { L89 (param tag newLine is missing)
  9. function claimRevenue(address revenueContract, address token, bytes calldata data) L70 (param tag token is missing)
  10. function operate(address revenueContract, bytes calldata data) external returns (bool) { L108 (return tag is missing)
  11. function addSpigot(address revenueContract, Setting memory setting) external returns (bool) { L125 (return tag is missing)
  12. function removeSpigot(address revenueContract) L137 (return tag is missing)
  13. function updateOwner(address newOwner) external returns (bool) { L159 (return tag is missing)
  14. function updateOperator(address newOperator) external returns (bool) { L170 (return tag is missing)
  15. function updateTreasury(address newTreasury) external returns (bool) { L181 (return tag is missing)
  16. function updateWhitelistedFunction(bytes4 func, bool allowed) external returns (bool) { L194 (return tag is missing)
  17. function getEscrowed(address token) external view returns (uint256) { L206 (return tag is missing)
  18. function isWhitelisted(bytes4 func) external view returns(bool) { L216 (return tag is missing)
  19. function line() external view override returns(address) { L57 (return tag is missing)
  20. function updateLine(address _line) external returns(bool) { L74 (param tag _line is missing)
  21. function enableCollateral(address token) external returns (bool) { L100 (return tag is missing)
  22. function getLatestAnswer(address token) external returns (int) { L22 (param tag token is missing)
  23. function deploySecuredLineWithModules( L135 (params tag coreParams, mSpigot, mEscrow are missing)
  24. function deploySecuredLineWithModules( L135 (return tag line is missing)
  25. function create( L125 (param tags are missing)
  26. function create( L125 (return tag is missing)
  27. function repay( L168 (param tags are missing)
  28. function withdraw( L202 (param tags are missing)
  29. function accrue( L239 (param tags are missing)
  30. function _getLatestCollateralRatio(EscrowState storage self, address oracle) public returns (uint256) { L34 (param self is missing)
  31. function rollover(address spigot, address newLine) external returns(bool) { L146 (param tags are missing)
  32. function updateSplit(address spigot, address revenueContract, LineLib.STATUS status, uint8 defaultSplit) external returns (bool) { L169 (param tags spigot, status, defaultSplit are missing)
  33. function releaseSpigot(address spigot, LineLib.STATUS status, address borrower, address arbiter, address to) external returns (bool) { L194 (param tags are missing)
  34. function sweep(address to, address token, uint256 amount, LineLib.STATUS status, address borrower, address arbiter) external returns (bool) { L217 (param tags are missing)
  35. modifier mutualConsent(address _signerOne, address _signerTwo) { L31 (param tags are missing)
  36. function rolloverSecuredLine( L41 (param tag oracle is missing)

[N-8]: Line is too long

Context:

  1. mapping(bytes32 => Credit) public credits; // id -> Reference ID for a credit line provided by a single Lender for a given token on a Line of Credit L35
  2. * @dev - A Borrower and a first Lender agree on terms. Then the Borrower deploys the contract using the constructor below. L42
  3. * Later, both Lender and Borrower must call _mutualConsent() during addCredit() to actually enable funds to be deposited. L43
  4. * @param ttl_ - The time to live for all credit lines for the Line of Credit facility (sets the maturity/term of the Line of Credit) L47
  5. deadline = block.timestamp + ttl_; //the deadline is the term/maturity/expiry date of the Line of Credit facility L58
  6. /// @notice exchange aggregator (mainly 0x router) to trade revenue tokens from a Spigot for credit tokens owed to lenders L28
  7. * @notice - excess unsold revenue claimed from Spigot to be sold later or excess credit tokens bought from revenue but not yet used to repay debt L36
  8. * - needed because the Line of Credit might have the same token being lent/borrower as being bought/sold so need to separate accounting. L37
  9. * @param swapTarget_ - 0x protocol exchange address to send calldata for trades to exchange revenue tokens for credit tokens L49
  10. * @param ttl_ - time to live for line of credit contract across all lenders set at deployment in order to set the term/expiry date L50
  11. * @param defaultRevenueSplit_ - The % of Revenue Tokens that the Spigot escrows for debt repayment if the Line is healthy. L51
  12. * @notice requires Spigot contract itselgf to be transfered to Arbiter and sold off to a 3rd party before declaring insolvent L84
  13. ```* @param targetToken - The credit token that needs to be bought in order to pat down debt. Always `credits[ids[0]].token```` L182
  14. * - current implementation just sends "liquidated" tokens to Arbiter to sell off how the deem fit and then manually repay with DepositAndRepay L72
  15. * @notice - a contract allowing the revenue stream of a smart contract to be split between two parties, Owner and Treasury L12
  16. * @param _owner - An address that controls the Spigot and owns rights to some or all tokens earned by owned revenue contracts L26
  17. * @param _treasury - A non-active address for non-Owner that receives revenue tokens that aren't allocated and escrowed for the Owner L27
  18. * @param _operator - An active address for non-Owner that can execute whitelisted functions to manage and maintain product operations L28
  19. * @notice - Claims revenue tokens from the Spigot (push and pull payments) and escrows them for the Owner withdraw later. L59
  20. ```* @return claimed - The amount of revenue tokens claimed from revenueContract and split between owner and `treasury```` L68
  21. * @notice - uses predefined function in revenueContract settings to transfer complete control and ownership from this Spigot to the Operator L131
  22. * @notice - Ownable contract that allows someone to deposit ERC20 and ERC4626 tokens as collateral to back a Line of Credit L17
  23. * @param _minimumCollateralRatio - In bps, 3 decimals. Cratio threshold where liquidations begin. see Escrow.isLiquidatable() L36
  24. * @param _line - Initial owner of Escrow contract. May be non-Line contract at construction before transferring to a Line. L38
  25. * @param _borrower - borrower on the _line contract. Cannot pull from _line because _line might not be a Line at construction. L40
  26. * @notice - Checks Line's outstanding debt value and current Escrow collateral value to compute collateral ratio and checks that against minimum. L62
  27. * @dev - Used if we setup Escrow before Line exists. Line has no way to interface with this function so once transfered `line` is set forever L71
  28. * @notice - simple contract that wraps Chainlink's Feed Registry to get asset prices for any tokens without needing to know the specific oracle address L10
  29. @notice sets up new line based of config of old line. Old line does not need to have REPAID status for this call to succeed. L171
  30. /// @notice After accrueInterest runs, emits the amount of interest added to a Borrower's outstanding balance of interest due L31
  31. (N.B. results in an increase in interestRepaid, i.e. interest not yet withdrawn by a Lender). There is no corresponding function L43
  32. * @dev - Creates a deterministic hash id for a credit line provided by a single Lender for a given token on a Line of Credit facility L55
  33. * @notice - Calculates value of tokens. Used for calculating the USD value of principal and of interest during getOutstandingDebt() L101
  34. * @dev assumes that `id` of a single credit line within the Line of Credit facility (same lender/token) is stored only once in the `positions` array L12
  35. * This means cleanup on _close() and checks on addCredit() are CRITICAL. If `id` is duplicated then the position can't be closed L14
  36. * @return newPositions - all active credit lines on the Line of Credit facility after the `id` has been removed [Bob - consider renaming to newIds L17
  37. function isLiquidatable(EscrowState storage self, address oracle, uint256 minimumCollateralRatio) external returns(bool) { L210
  38. // Configurations for revenue contracts related to the split of revenue, access control to claiming revenue tokens and transfer of Spigot ownership L15
  39. // cant claim revenue via operate() because that fucks up accounting logic. Owner shouldn't whitelist it anyway but just in case L69
  40. function addSpigot(SpigotState storage self, address revenueContract, ISpigot.Setting memory setting) external returns (bool) { L125
  41. * @notice Allows revenue tokens in 'escrowed' to be traded for credit tokens that aren't yet used to repay debt. L39
  42. The newly exchanged credit tokens are held in 'unusedTokens' ready for a Lender to withdraw using useAndRepay L40
  43. This feature allows a Borrower to take advantage of an increase in the value of the revenue token compared L41
  44. to the credit token and to in effect use less revenue tokens to be later used to repay the same amount of debt. L42
  45. ```* @param targetToken - The credit token that needs to be bought in order to pat down debt. Always `credits[ids[0]].token```` L46
  46. * @notice Changes the revenue split between a Borrower's treasury and the LineOfCredit based on line health, runs with updateOwnerSplit() L164
  47. function updateSplit(address spigot, address revenueContract, LineLib.STATUS status, uint8 defaultSplit) external returns (bool) { L169
  48. function releaseSpigot(address spigot, LineLib.STATUS status, address borrower, address arbiter, address to) external returns (bool) { L194
  49. * @notice - Sends any remaining tokens (revenue or credit tokens) in the Spigot to the Borrower after the loan has been repaid. L212
  50. - In case of a Borrower default (loan status = liquidatable), this is a fallback mechanism to withdraw all the tokens and send them to the Arbiter L213
  51. function sweep(address to, address token, uint256 amount, LineLib.STATUS status, address borrower, address arbiter) external returns (bool) { L217
  52. @notice sets up new line based of config of old line. Old line does not need to have REPAID status for this call to succeed. L34

Description:

Maximum suggested line length is 120 characters.

#0 - c4-judge

2022-12-06T21:39:58Z

dmvt marked the issue as grade-a

Awards

49.2315 USDC - $49.23

Labels

bug
G (Gas Optimization)
grade-b
G-16

External Links

Report

Gas Optimizations

[G-1]: The increment in for loop post condition can be made unchecked

Context:

  1. for (uint256 i; i < len; ++i) { L179
  2. for (uint256 i; i < len; ++i) { L203
  3. for (uint256 i; i <= lastSpot; ++i) { L520
  4. for(uint256 i; i < len; ++i) { L23
  5. for(uint i = 1; i < len; ++i) { L51
  6. for (uint256 i; i < length; ++i) { L57

Description:

This can save 30-40 gas per loop iteration.

Recommendation:

Example how to fix. Change:

for (uint256 i = 0; i < orders.length; ++i) { // Do the thing }

To:

for (uint256 i = 0; i < orders.length;) { // Do the thing unchecked { ++i; } }

[G-2]: Place subtractions where the operands can't underflow in unchecked {} block

Context:

  1. unusedTokens[credit.token] -= repaid - newTokens; L122 (repaid - newTokens)
  2. unusedTokens[credit.token] += newTokens - repaid; L125 (newTokens - repaid)
  3. unusedTokens[credit.token] -= amount; L144
  4. ids[i - 1] = ids[i]; // could also clean arr here like in _SortIntoQ L52 (i-1)
  5. self.deposited[token].amount -= amount; L164
  6. self.deposited[token].amount -= amount; L202
  7. require(LineLib.sendOutTokenOrETH(token, self.treasury, claimed - escrowedAmount)); L96 (claimed - escrowedAmount)
  8. uint256 diff = oldClaimTokens - newClaimTokens; L101
  9. unused - diff L109

Description:

Some gas can be saved by using an unchecked {} block if an underflow isn't possible because of a previous require() or if-statement.

[G-3]: If internal function called only once it can be inlined to save gas

Context:

MutualConsent._getNonCaller

Description:

If they are not inlined, they cost an additional 20 to 40 gas because of 2 extra jump instructions and additional stack operations needed for function calls.

[G-4]: Cache a value from a mapping/array in local memory

Context:

https://github.com/debtdao/Line-of-Credit/blob/audit/code4rena-2022-11-03/contracts/utils/SpigotLib.sol#L34-L49 (self.settings[revenueContract].claimFunction)

Description:

If you read value from mapping/array more than once within a function then it is cheaper to cache it in local memory and then read it from memory wnen it is neaded. This will save about 40 gas.

Recommendation:

Example how to fix. Change:

if(self.settings[revenueContract].claimFunction == bytes4(0)) { // push payments // claimed = total balance - already accounted for balance claimed = existingBalance - self.escrowed[token]; // underflow revert ensures we have more tokens than we started with and actually claimed revenue } else { // pull payments if(bytes4(data) != self.settings[revenueContract].claimFunction) { revert BadFunction(); } (bool claimSuccess,) = revenueContract.call(data); if(!claimSuccess) { revert ClaimFailed(); } // claimed = total balance - existing balance claimed = LineLib.getBalance(token) - existingBalance; // underflow revert ensures we have more tokens than we started with and actually claimed revenue }

to:

bytes4 _claimFunction = self.settings[revenueContract].claimFunction; if(_claimFunction == bytes4(0)) { // push payments // claimed = total balance - already accounted for balance claimed = existingBalance - self.escrowed[token]; // underflow revert ensures we have more tokens than we started with and actually claimed revenue } else { // pull payments if(bytes4(data) != _claimFunction) { revert BadFunction(); } (bool claimSuccess,) = revenueContract.call(data); if(!claimSuccess) { revert ClaimFailed(); } // claimed = total balance - existing balance claimed = LineLib.getBalance(token) - existingBalance; // underflow revert ensures we have more tokens than we started with and actually claimed revenue }

[G-5]: Use calldata instead of memory

Context:

  1. function _accrue(Credit memory credit, bytes32 id) internal returns(Credit memory) { L218 (credit)
  2. function _close(Credit memory credit, bytes32 id) internal virtual returns (bool) { L483 (credit)
  3. function addSpigot(address revenueContract, Setting memory setting) external returns (bool) { L125 (setting)
  4. ILineOfCredit.Credit memory credit, L74
  5. function addSpigot(SpigotState storage self, address revenueContract, ISpigot.Setting memory setting) external returns (bool) { L125 (setting)

Description:

If a reference type function parameter is read-only, it is recommended to use calldata instead of memory because this provides significant gas savings. Since Solidity v0.6.9, memory and calldata are allowed in all functions regardless of their visibility type (ie external, public, etc).

#0 - c4-judge

2022-11-17T22:57:22Z

dmvt marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter