Polemos Lending Smart Contract Security Audit Report Halborn Final
Polemos Lending Smart Contract Security Audit Report Halborn Final
Polemos Lending Smart Contract Security Audit Report Halborn Final
Description 27
Code Location 28
Risk Level 30
Recommendation 30
Remediation Plan 30
Description 32
Code Location 33
Proof of Concept 35
Risk Level 36
Recommendation 37
Remediation Plan 37
1
3.3 (HAL-03) CHECKPOLICY BYPASS FOR SAFEBATCHTRANSFERFROM ERC1155
IS POSSIBLE - CRITICAL 38
Description 38
Code Location 39
Proof of Concept 42
Risk Level 43
Recommendation 44
Remediation Plan 44
Description 45
Code Location 45
Proof of Concept 47
Risk Level 50
Recommendation 50
Remediation Plan 50
Description 51
Code Location 52
Proof of Concept 54
Risk Level 56
Recommendation 56
Remediation Plan 56
Description 57
2
Code Location 58
Proof of Concept 60
Risk Level 62
Recommendation 62
Remediation Plan 62
Description 63
Code Location 63
Proof of Concept 64
Risk Level 64
Recommendation 65
Remediation Plan 65
Description 66
Code Location 67
Proof of Concept 71
Risk Level 72
Recommendation 72
Remediation Plan 72
3.9 (HAL-09) DIAMOND PROXY DOES NOT SET THE ESSENTIAL VARIABLES IN
THE CONSTRUCTOR - MEDIUM 73
Description 73
Code Location 73
3
Risk Level 74
Recommendation 74
Remediation Plan 74
Description 75
Code Location 75
Risk Level 76
Recommendation 76
Remediation Plan 77
Description 78
Code Location 78
Risk Level 80
Recommendation 80
Remediation Plan 80
Description 81
Risk Level 81
Recommendation 81
Remediation Plan 82
Description 83
Risk Level 85
Recommendation 85
Remediation Plan 85
4
3.14 (HAL-14) STAKEASSET CAN BE CALLED WITHOUT RENTAL WALLET ADDED -
LOW 86
Description 86
Code Location 86
Risk Level 87
Recommendation 87
Remediation Plan 87
Description 88
Code Location 88
Risk Level 91
Recommendation 91
Remediation Plan 91
Description 92
Code Location 92
Risk Level 93
Recommendation 93
Remediation Plan 93
Description 94
Risk Level 94
Recommendation 94
5
Remediation Plan 94
Description 95
Risk Level 95
Recommendation 95
Remediation Plan 95
Description 96
Risk Level 97
Recommendation 98
Remediation Plan 98
Description 99
Recommendation 101
3.21 (HAL-21) THE ADMIN LIST IS MISSING INPUT VALIDATION - LOW 102
Description 102
Recommendation 104
3.22 (HAL-22) SCHOLAR AUTOMATOR PROXY LACKS THE ISPAUSED CHECK - LOW
106
Description 106
6
Risk Level 107
Recommendation 107
Description 108
Recommendation 110
Description 111
Recommendation 112
Description 113
Recommendation 116
Description 117
7
Code Location 117
Recommendation 118
Description 119
Recommendation 120
Description 121
Recommendation 122
Description 123
Recommendation 124
8
4.2 Diamond storage 140
Description 148
Results 148
5.2 AUTOMATED SECURITY SCAN 156
Description 156
Results 156
9
DOCUMENT REVISION HISTORY
Remediation Plan
1.1 03/30/2023 Grzegorz Trawinski
Updates
10
3.0 Remediation Plan 04/21/2023 Grzegorz Trawinski
CONTACTS
Grzegorz
Halborn [email protected]
Trawinski
11
EXECUTIVE OVERVIEW
12
1.1 INTRODUCTION
Polemos is a GameFi platform that is the first decentralized combination
of its asset lending library, Play to Own education hub, and gaming com-
munity. The Lending solution was also improved with upgrade capabilities
by Diamond Proxy pattern.
13
ones were related to the checkPolicy() functionality. Also, several
other significant findings were identified and immediately solved by the
Polemos team on March 30th, 2023.
Polemos requested an audit of the updated code on April 6th, 2023. Halborn
identified some improvements to reduce the likelihood and impact of risks
related to the upgradability pattern, which should be addressed by the
Polemos team
On April 21st, Polemos provided the updated code with remediation applied.
The majority of findings were addressed and fixed. Only two findings were
considered as accepted risk.
14
RISK METHODOLOGY:
The risk level is then calculated using a sum of these two values, creating
a value of 10 to 1 with 10 being the highest level of security risk.
10 - CRITICAL
9 - 8 - HIGH
7 - 6 - MEDIUM
5 - 4 - LOW
15
3 - 1 - VERY LOW AND INFORMATIONAL
EXECUTIVE OVERVIEW
16
1.4 SCOPE
Code repositories:
1. Lending
• Repository: polemos-assets-keeper
• Commit ID: e784b55ee91a10bda3643ea381a267ce457e817d
• Branch: halborn1
• Smart contracts in scope:
1. scholar-wallet/types/Exports.sol
2. scholar-wallet/types/Types.sol
3. scholar-wallet/utils/ScholarAssetHandler.sol
4. scholar-wallet/utils/Exports.sol
5. scholar-wallet/utils/libs/LibScholarParamValidator.sol
6. scholar-wallet/utils/libs/Exports.sol
7. scholar-wallet/utils/libs/LibScholarAssetManagement.sol
8. scholar-wallet/utils/libs/IterableMapping.sol
9. scholar-wallet/utils/libs/LibScholarTransferUtils.sol
10. scholar-wallet/utils/libs/helpers/LibErc20Helper.sol
11. scholar-wallet/utils/libs/helpers/LibErc1155Helper.sol
12. scholar-wallet/utils/libs/helpers/LibErc721Helper.sol
EXECUTIVE OVERVIEW
13. scholar-wallet/ScholarAutomatorBase.sol
14. scholar-wallet/ScholarAutomator.sol
15. scholar-wallet/eip712/Exports.sol
16. scholar-wallet/eip712/types/SigningParams.sol
17. scholar-wallet/eip712/constants/OperationIds.sol
18. scholar-wallet/eip712/constants/TypeHashDefinitions.sol
19. scholar-wallet/eip712/libs/LibScholarSignatureVerification.sol
20. scholar-wallet/eip712/ScholarRecoverable.sol
21. scholar-wallet/eip712/interfaces/Interfaces.sol
22. scholar-wallet/policy/types/Exports.sol
23. scholar-wallet/policy/types/PolicyArguments.sol
24. scholar-wallet/policy/libs/LibScholarPolicyCheck.sol
25. scholar-wallet/policy/ScholarPolicyHandler.sol
26. scholar-wallet/interfaces/Interfaces.sol
17
27. rental-wallet/mocks/AutomatorProductionMock.sol
28. rental-wallet/mocks/AutomatorValidationsMock.sol
29. rental-wallet/types/Exports.sol
30. rental-wallet/types/Types.sol
31. rental-wallet/RentalAutomator.sol
32. rental-wallet/utils/RentalAssetHandler.sol
33. rental-wallet/utils/libs/LibRentalParamValidator.sol
34. rental-wallet/utils/libs/Exports.sol
35. rental-wallet/utils/libs/LibRentalTransferUtils.sol
36. rental-wallet/utils/libs/LibRentalAssetManagement.sol
37. rental-wallet/utils/libs/helpers/LibErc20Helper.sol
38. rental-wallet/utils/libs/helpers/LibErc1155Helper.sol
39. rental-wallet/utils/libs/helpers/LibErc721Helper.sol
40. rental-wallet/RentalAutomatorBase.sol
41. rental-wallet/eip712/Exports.sol
42. rental-wallet/eip712/types/SigningParams.sol
43. rental-wallet/eip712/constants/OperationIds.sol
44. rental-wallet/eip712/constants/TypeHashDefinitions.sol
45. rental-wallet/eip712/RentalRecoverable.sol
46. rental-wallet/eip712/libs/LibRentalSignatureVerification.sol
47. rental-wallet/eip712/interfaces/Interfaces.sol
48. rental-wallet/policy/types/Exports.sol
49. rental-wallet/policy/types/PolicyArguments.sol
50. rental-wallet/policy/libs/LibRentalPolicyCheck.sol
EXECUTIVE OVERVIEW
51. rental-wallet/policy/RentalPolicyHandler.sol
52. rental-wallet/interfaces/Interfaces.sol
53. common/types/Exports.sol
54. common/types/CommonTypes.sol
55. common/constants/Errors.sol
56. common/constants/RentalConstants.sol
57. common/constants/ScholarConstants.sol
58. common/utils/ownable/BasicOwnable.sol
59. common/utils/PublicUtils.sol
60. common/utils/SignatureValidator.sol
61. common/eip712/Exports.sol
62. common/eip712/types/SharedSigningParams.sol
63. common/eip712/libs/LibTypeHasher.sol
18
64. common/eip712/Eip712Config.sol
65. common/eip712/interfaces/SharedInterfaces.sol
66. common/interfaces/Interfaces.sol
Out-of-scope:
- third-party libraries and dependencies
- economic attacks
2. Lending - retest
• Repository: polemos-assets-keeper
• Commit ID: 4bbbcb7941556e7486b691cb58b811733a02d2d1
• Commit ID: cf0d83cd8d859d41e0059954d2d9be1bd2a2dd7e
• Commit ID: e8b6e08011c7f0985a64972deeb0d95c29e88cdf
• Branch: halborn3
• Repository: polemos-assets-keeper
• Commit ID: 95a0ab1191857ab7802033eb2e7a9804daef5640
• Branch: halborn4
• Smart contracts in scope:
EXECUTIVE OVERVIEW
19
12. upgrade-diamond/facets/inheritance/Eip712ConfigFacet.sol
13. upgrade-diamond/facets/RentalAssetHandlerFacet.sol
14. upgrade-diamond/facets/RentalDiamondLoupeFacet.sol
15. upgrade-diamond/facets/PrettyBasicModules.sol
16. upgrade-diamond/facets/RentalAuthenticatedUpgradabilityFacet.sol
17. upgrade-diamond/utils/RentalPausable.sol
18. upgrade-diamond/utils/Helpers.sol
19. upgrade-diamond/libs/Exports.sol
20. upgrade-diamond/libs/LibRentalDiamond.sol
21. upgrade-diamond/libs/LibRentalStorage.sol
22. upgrade-diamond/interfaces/Exports.sol
23. upgrade-diamond/interfaces/IDiamondCut.sol
24. upgrade-diamond/interfaces/IDiamondLoupe.sol
25. upgrade-diamond/interfaces/Interfaces.sol
34. upgrade-diamond/facets/inheritance/ScholarSignatureValidatorFacet.sol
35. upgrade-diamond/facets/inheritance/ScholarDiamondCutInternal.sol
36. upgrade-diamond/facets/inheritance/ScholarAutomatorBaseFacet.sol
37. upgrade-diamond/facets/ScholarAssetHandlerFacet.sol
38. upgrade-diamond/facets/ScholarAuthenticatedUpgradabilityFacet.sol
39. upgrade-diamond/facets/ScholarDiamondLoupeFacet.sol
40. upgrade-diamond/facets/PrettyBasicModules.sol
41. upgrade-diamond/utils/ScholarPausable.sol
42. upgrade-diamond/utils/Helpers.sol
43. upgrade-diamond/libs/Exports.sol
44. upgrade-diamond/libs/LibScholarDiamond.sol
45. upgrade-diamond/libs/LibScholarStorage.sol
46. upgrade-diamond/interfaces/Exports.sol
47. upgrade-diamond/interfaces/IDiamondCut.sol
20
48. upgrade-diamond/interfaces/IDiamondLoupe.sol
49. upgrade-diamond/interfaces/Interfaces.sol
• Repository: polemos-assets-keeper
• Commit ID: daba98517e5f3cfc70c38272a6eb16ee701d50b4
• Branch: halborn5
EXECUTIVE OVERVIEW
21
2. ASSESSMENT SUMMARY & FINDINGS
OVERVIEW
5 0 4 13 7
EXECUTIVE OVERVIEW
22
LIKELIHOOD
(HAL-01)
(HAL-02)
(HAL-07)
(HAL-08) (HAL-03)
(HAL-09)
(HAL-04)
(HAL-05)
(HAL-10)
(HAL-11)
(HAL-14)
IMPACT
(HAL-15)
(HAL-16)
(HAL-12)
(HAL-18)
(HAL-13)
(HAL-19)
(HAL-20)
(HAL-21)
(HAL-17)
(HAL-06)
(HAL-22)
EXECUTIVE OVERVIEW
(HAL-23)
(HAL-24)
(HAL-25)
(HAL-26)
(HAL-27)
(HAL-28)
(HAL-29)
23
SECURITY ANALYSIS RISK LEVEL REMEDIATION DATE
(HAL-08) AUTHENTICATED
UPGRADEABILITY FACET IS NOT Medium SOLVED - 04/21/2023
INDEPENDENT
(HAL-09) DIAMOND PROXY DOES NOT SET
THE ESSENTIAL VARIABLES IN THE Medium RISK ACCEPTED
EXECUTIVE OVERVIEW
CONSTRUCTOR
(HAL-10) STAKE ASSET LACKS TOKENS
Low RISK ACCEPTED
WHITELISTING
24
(HAL-15) STAKEASSET CAN BE
Low RISK ACCEPTED
FRONT-RUN TO BLOCK INTERNAL ASSET ID
Informational ACKNOWLEDGED
INCLUDE ASSET PRICE
(HAL-26) SETFEETOKENCONTRACTADDRESS
MAY REVERT FOR ERC20 WITH FALLBACK Informational ACKNOWLEDGED
IMPLEMENTATION
(HAL-27) REDUNDANT INITIALIZATION
Informational ACKNOWLEDGED
OF UINT256 VARIABLES TO 0
25
FINDINGS & TECH
DETAILS
26
3.1 (HAL-01) CHECKPOLICY IS
VULNERABLE TO
TIME-OF-CHECK-TO-TIME-OF-USE -
CRITICAL
Description:
The solution implements rental wallets that are under the custody of the
Polemos team. Each rental wallet is assigned to each borrower individ-
ually. After a successful asset claim, the rented asset is transferred
to the rental wallet. Furthermore, the borrower can transfer owned to-
kens to the rental wallet. The borrower can manage the rental wallet
off-chain, however, the list of possible actions is strictly limited.
E.g., the borrower can transfer out any owned tokens from the rental
wallet, but transferring the rented asset is forbidden. To check which
action is allowed or prohibited, in before any action, the solution
calls the checkPolicy() function from the RentalAutomator contract. The
checkPolicy() function makes necessary checks, including the rental wal-
FINDINGS & TECH DETAILS
27
3. The borrower triggers an attempt to transfer 50 owned tokens to the
external wallet twice, then:
Code Location:
28
265 false ,
266 PolicyCheckResponseCode .
ë Not_Checked_When_Erc20_Operation
267 );
268 }
269 } else {
270 // Allow to " transfer " / " transfer - from " for Staked or
ë Unstaked asset
271 // or for unknown asset for Automator .
272 // ( this means success when user tries to transfer own
ë tokens , tokens he is approved to spend ,
273 // or tokens not intersting for Automator )
274 return
275 RentalCheckResult (
276 true ,
277 PolicyCheckResponseCode .
ë Checked_When_Erc20_Operation_With_Own_Tokens
278 );
279 }
280 }
281
282 function _checkErc1155Transfer (
283 uint256 totalBalance ,
284 uint256 claimedBalance ,
FINDINGS & TECH DETAILS
29
300 false ,
301 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
302 );
303 }
304 } else {
305 // Allow to " safe - batch - transfer - from " / " safe -
ë transfer - from " for Staked or Unstaked asset
306 // or for unknown asset for Automator .
307 // ( this means success when user tries to transfer own
ë tokens , tokens he is approved to spend ,
308 // or tokens not intersting for Automator )
309 return
310 RentalCheckResult (
311 true ,
312 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
313 );
314 }
315 }
Risk Level:
FINDINGS & TECH DETAILS
Likelihood - 5
Impact - 5
Recommendation:
Remediation Plan:
30
or ERC1155 tokens. Instead, a new transferFromRentalWallet() function is
introduced, that handles both checkPolicy() functionality and transfer
of tokens in a single transaction.
FINDINGS & TECH DETAILS
31
3.2 (HAL-02) CLAIMASSET CAN BE
FRONT-RUN WITH CHECKPOLICY FOR
TOKENS TRANSFER - CRITICAL
Description:
The solution implements rental wallets that are under the custody of the
Polemos team. Each rental wallet is assigned to each borrower individu-
ally. After a successful asset claim, the rented asset is transferred to
the rental wallet. Furthermore, the borrower can transfer owned tokens
to the rental wallet. The borrower can manage the rental wallet off-
chain, however, the list of possible actions is strictly limited. E.g.,
the borrower can transfer out any owned tokens from the rental wallet,
but transferring the rented asset is forbidden. To check which action
is allowed or prohibited, in before any action, the solution calls the
checkPolicy() function from RentalAutomator. The checkPolicy() function
makes necessary checks, including the rental wallet tokens’ balances and
rental balances from RentedStats structure, to decide e.g., if a transfer
FINDINGS & TECH DETAILS
32
As an example, consider the below scenario:
Code Location:
33
260 PolicyCheckResponseCode .
ë Checked_When_Erc20_Operation_With_Own_Tokens
261 );
262 } else {
263 return
264 RentalCheckResult (
265 false ,
266 PolicyCheckResponseCode .
ë Not_Checked_When_Erc20_Operation
267 );
268 }
269 } else {
270 // Allow to " transfer " / " transfer - from " for Staked or
ë Unstaked asset
271 // or for unknown asset for Automator .
272 // ( this means success when user tries to transfer own
ë tokens , tokens he is approved to spend ,
273 // or tokens not intersting for Automator )
274 return
275 RentalCheckResult (
276 true ,
277 PolicyCheckResponseCode .
ë Checked_When_Erc20_Operation_With_Own_Tokens
278 );
FINDINGS & TECH DETAILS
279 }
280 }
281
282 function _checkErc1155Transfer (
283 uint256 totalBalance ,
284 uint256 claimedBalance ,
285 uint256 txBalance
286 ) private pure returns ( RentalCheckResult memory ) {
287 // status : the given operation is applied to Claimed or
ë Reclaimed asset
288 if ( claimedBalance > 0) {
289 // allow to " safeBatchTransferFrom " / "
ë safeTransferFrom " when user has enough own tokens to cover tx -
ë amount
290 // ( this means success when asset is Claimed /
ë Reclaimed and user has enough non - rented tokens )
291 if ( totalBalance - claimedBalance >= txBalance ) {
292 return
293 RentalCheckResult (
294 true ,
34
295 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
296 );
297 } else {
298 return
299 RentalCheckResult (
300 false ,
301 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
302 );
303 }
304 } else {
305 // Allow to " safe - batch - transfer - from " / " safe -
ë transfer - from " for Staked or Unstaked asset
306 // or for unknown asset for Automator .
307 // ( this means success when user tries to transfer own
ë tokens , tokens he is approved to spend ,
308 // or tokens not intersting for Automator )
309 return
310 RentalCheckResult (
311 true ,
312 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
313 );
FINDINGS & TECH DETAILS
314 }
315 }
Proof of Concept:
Listing 3: HalbornERC20CheckPolicyTest.t.sol
1 function
ë test_checkPolicy_for_transfer_ERC20_with_empty_balance () public {
2 uint256 [] memory Ids = new uint256 [](1) ;
3 uint256 [] memory ammounts = new uint256 [](1) ;
4 Ids [0] = 0;
5 ammounts [0] = 10 e10 ;
6 RentalPolicyArguments memory rentalPolicyArguments =
ë RentalPolicyArguments (
35
7 address ( mockErc20Token ) ,
8 Ids ,
9 borrower1 ,
10 borrowerExternal1 ,
11 ammounts ,
12 address (0) ,
13 true ,
14 address (0) ,
15 OperationType . Transfer_Erc20
16 );
17
18 ( RentalCheckResult memory rentalCheckResult ) =
ë rentalAutomator . checkPolicy ( rentalPolicyArguments , AssetType . EIP20
ë );
19 assertEq ( rentalCheckResult . success , true );
20 assertEq ( uint ( rentalCheckResult . resultCode ) , uint (
ë PolicyCheckResponseCode .
ë Checked_When_Erc20_Operation_With_Own_Tokens ) ) ;
21 }
FINDINGS & TECH DETAILS
Risk Level:
Likelihood - 5
36
Impact - 5
Recommendation:
Remediation Plan:
37
3.3 (HAL-03) CHECKPOLICY BYPASS FOR
SAFEBATCHTRANSFERFROM ERC1155 IS
POSSIBLE - CRITICAL
Description:
The solution implements rental wallets that are under the custody of the
Polemos team. Each rental wallet is assigned to each borrower individ-
ually. After a successful asset claim, the rented asset is transferred
to the rental wallet. Furthermore, the borrower can transfer owned to-
kens to the rental wallet. The borrower can manage the rental wallet
off-chain, however, the list of possible actions is strictly limited.
E.g., the borrower can transfer out any owned tokens from the rental
wallet, but transferring the rented asset is forbidden. To check which
action is allowed or prohibited, in before any action, the solution calls
the checkPolicy() function from RentalAutomator. The checkPolicy() makes
necessary checks, including the rental wallet tokens’ balances and rental
balances from RentedStats structure, to decide e.g., if a transfer of
FINDINGS & TECH DETAILS
38
As an example, consider the below scenario:
Code Location:
39
177 }
178 return
179 RentalCheckResult (
180 false ,
181 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
182 );
183 }
184
185 // this is required to return the most accurate result in
ë case of safe - transfer operation
186 if ( erc1155Operation == OperationType .
ë SafeTransferFrom_Erc1155 ) {
187 return
188 _checkErc1155Transfer (
189 rentalWalletTotalBalances [0] ,
190 rentalWalletClaimedBalances [0] ,
191 args . amounts [0]
192 );
193 }
194
195 for ( uint256 i = 0; i < args . tokenIds . length ; i ++) {
196 RentalCheckResult memory check = _checkErc1155Transfer
ë (
FINDINGS & TECH DETAILS
197 rentalWalletTotalBalances [ i ],
198 rentalWalletClaimedBalances [i ] ,
199 args . amounts [ i]
200 );
201
202 // if there is not_checked result , then we should
ë break the cycle and return with suitable result
203 if ( check . resultCode == PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation ) {
204 return
205 RentalCheckResult (
206 false ,
207 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
208 );
209 }
210 }
211 return
212 RentalCheckResult (
213 true ,
40
214 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
215 );
216 }
294 true ,
295 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
296 );
297 } else {
298 return
299 RentalCheckResult (
300 false ,
301 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
302 );
303 }
304 } else {
305 // Allow to " safe - batch - transfer - from " / " safe -
ë transfer - from " for Staked or Unstaked asset
306 // or for unknown asset for Automator .
307 // ( this means success when user tries to transfer own
ë tokens , tokens he is approved to spend ,
308 // or tokens not intersting for Automator )
309 return
310 RentalCheckResult (
41
311 true ,
312 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
313 );
314 }
315 }
Proof of Concept:
Listing 6: HalbornERC1155CheckPolicyTest.t.sol
1 function
ë test_checkPolicy_ERC1155_SafeBatchTransferFrom_with_same_tokenId ()
ë public {
2 claimAssetFromLenderToBorrowerERC1155 () ;
3
FINDINGS & TECH DETAILS
42
24
25 vm . prank ( borrower1 );
26 ( RentalCheckResult memory rentalCheckResult ) =
ë rentalAutomator . checkPolicy ( rentalPolicyArguments , AssetType .
ë EIP1155 );
27 assertEq ( rentalCheckResult . success , true );
28 assertEq ( uint ( rentalCheckResult . resultCode ) , uint (
ë PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens )) ;
29
30 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 1) , 20) ;
31 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 1) ,
ë 0) ;
32 bytes memory data ;
33 vm . prank ( borrower1 );
34 mockErc1155Token . safeBatchTransferFrom ( borrower1 ,
ë borrowerExternal1 , Ids , ammounts , data ) ;
35 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 1) , 0) ;
36 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 1) ,
ë 20) ;
37 }
FINDINGS & TECH DETAILS
Risk Level:
Likelihood - 5
Impact - 5
43
Recommendation:
Remediation Plan:
44
3.4 (HAL-04) DIAMOND PROXY
INITIALIZE FUNCTIONS CAN BE CALLED
MULTIPLE TIMES - CRITICAL
Description:
Code Location:
FINDINGS & TECH DETAILS
Listing 7: RentalAuthenticatedUpgradabilityFacet.sol
69 function initalizeAuthenticatedUpgradabilityModule ( address []
ë calldata adminUserList ) external {
70 DiamondStorage storage ds = state () ;
71
72 RentalAuthenticatedUpgradabilityFacetStorage
73 storage facetStorage = getAuthentificationFacetStorage
ë () ;
74 require (! facetStorage . isInitialized ,
ë AUTHENTICATED_UPGRADABILITY_FACET_ALREADY_INITIALIZED ) ;
75 require ( adminUserList . length == MAX_ADMINS_COUNT ,
ë INVALID_ADMIN_LIST_LENGTH_ERR );
76 _validateListContainsUniqueItemsWithErr ( adminUserList ,
ë DUPLICATED_ADMIN_USERS_ERR );
77
78 for ( uint8 i ; i < MAX_ADMINS_COUNT ; ++ i) {
79 address currentAdmin = adminUserList [ i ];
80 ds . adminList . _isAdmin [ currentAdmin ] = true ;
81 ds . adminList . addresses . push ( currentAdmin ) ;
45
82 }
83 }
Listing 8: RentalAdminFacet.sol
68 function initializeRentalAdminFacet () external {
69 RentalAdminFacetStorage storage facetStorage =
ë getAdminFacetStorage () ;
70 require (! facetStorage . isInitialized ,
ë ADMIN_FACET_ALREADY_INITIALIZED );
71
72 DiamondStorage storage ds = state () ;
73 ds . adminAddress = msg . sender ;
74 }
45
46 LibRentalParamValidator . validateAddressNotNullWithError (
47 _feeTokenContractAddress ,
48 USDC_ADDRESS_IS_NILL_ERR
49 );
50 validateContractSupportsErc20Interface (
51 publicUtils () ,
52 _feeTokenContractAddress ,
53 address ( this )
54 );
55
56 ds . feeTokenContractInstance = IERC20 (
ë _feeTokenContractAddress ) ;
57 ds . defaultReclaimPeriod = DEFAULT_RECLAIM_PERIOD ;
58 ds . minRentalPeriod = MIN_RENTAL_PERIOD ;
59 ds . maxRentalPeriod = MAX_RENTAL_PERIOD ;
60
61 // note : make sure this is invoked after recoverable facet
ë is appended and inited
62 _initEip712Config ( _eip721DomainName , IBaseRecoverable (
ë address ( this ))) ;
46
63 }
Proof of Concept:
47
9 rentalPolicyHandlerFacet = new RentalPolicyHandlerFacet () ;
10 rentalAutomatorFacet = new RentalAutomatorFacet () ;
11 rentalAssetHandlerFacet = new RentalAssetHandlerFacet () ;
12 rentalAuthenticatedUpgradabilityFacet = new
ë RentalAuthenticatedUpgradabilityFacet () ;
13
14 FacetCutWithoutInitData [] memory cut = new
ë FacetCutWithoutInitData [](8) ;
15
16 cut [0] = FacetCutWithoutInitData (
17 address ( rentalAdminFacet ) ,
18 FacetCutAction . Add ,
19 generateSelectors (" RentalAdminFacet " )
20 );
21 cut [1] = FacetCutWithoutInitData (
22 address ( rentalDiamondLoupeFacet ) ,
23 FacetCutAction . Add ,
24 generateSelectors (" RentalDiamondLoupeFacet ")
25 );
26 cut [2] = FacetCutWithoutInitData (
27 address ( rentalRecoverableFacet ) ,
28 FacetCutAction . Add ,
29 generateSelectors (" RentalRecoverableFacet " )
30 );
FINDINGS & TECH DETAILS
48
51 cut [7] = FacetCutWithoutInitData (
52 address ( rentalAuthenticatedUpgradabilityFacet ) ,
53 FacetCutAction . Add ,
54 generateSelectors ("
ë RentalAuthenticatedUpgradabilityFacet ")
55 );
56
57 rentalAutomatorProxy = new RentalAutomatorProxy ( cut ) ;
58
59 RentalAutomatorFacet ( address ( rentalAutomatorProxy )) .
ë initializeRentalAutomator ( address ( mockUSDToken ) , domainName ) ;
60
61 address [] memory temp = new address [](3) ;
62
63 temp [0] = address ( owner );
64 temp [1] = address ( owner2 );
65 temp [2] = address ( owner3 );
66
67 RentalAuthenticatedUpgradabilityFacet ( address (
ë rentalAutomatorProxy )) . initalizeAuthenticatedUpgradabilityModule (
ë temp );
68
69 RentalAdminFacet ( address ( rentalAutomatorProxy ) ) .
ë initializeRentalAdminFacet () ;
FINDINGS & TECH DETAILS
70 (...)
71 }
72
73 function test_diamond_proxy_initializeRentalAutomator_twice ()
ë public {
74 vm . prank ( attacker1 );
75 vm . expectRevert ( bytes (" AC -012 ") ) ;
76 RentalAutomatorFacet ( address ( rentalAutomatorProxy )) .
ë initializeRentalAutomator ( address ( mockUSDToken ) , domainName ) ;
77 }
78
79 function
ë test_diamond_proxy_initalizeAuthenticatedUpgradabilityModule_twice
ë () public {
80 address [] memory temp = new address [](3) ;
81
82 temp [0] = address ( attacker1 );
83 temp [1] = address ( attacker2 );
84 temp [2] = address ( attacker3 );
85
49
86 vm . prank ( attacker1 );
87 RentalAuthenticatedUpgradabilityFacet ( address (
ë rentalAutomatorProxy )) . initalizeAuthenticatedUpgradabilityModule (
ë temp );
88 }
89
90 function test_diamond_proxy_initializeRentalAdminFacet_twice ()
ë public {
91 vm . prank ( attacker1 );
92 RentalAdminFacet ( address ( rentalAutomatorProxy ) ) .
ë initializeRentalAdminFacet () ;
93 }
Risk Level:
Likelihood - 5
Impact - 5
FINDINGS & TECH DETAILS
Recommendation:
Remediation Plan:
50
3.5 (HAL-05)
TRANSFERFROMRENTALWALLET BYPASS FOR
SAFETRANSFERFROM ERC1155 IS
POSSIBLE - CRITICAL
Description:
The solution implements rental wallets that are under the custody of the
Polemos team. Each rental wallet is assigned to each borrower individu-
ally. After a successful asset claim, the rented asset is transferred to
the rental wallet. Furthermore, the borrower can transfer owned tokens to
the rental wallet. The borrower can manage the rental wallet off-chain,
however, the list of possible actions is strictly limited. E.g., the
borrower can transfer out any owned tokens from the rental wallet, but
transferring the rented asset is forbidden. The transferFromRentalWallet
() function is now available to the user, to manage owned and rental
tokens within the rental wallet.
FINDINGS & TECH DETAILS
51
Code Location:
181 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
182 );
183 }
184
185 // this is required to return the most accurate result in
ë case of safe - transfer operation
186 if ( erc1155Operation == OperationType .
ë SafeTransferFrom_Erc1155 ) {
187 return
188 _checkErc1155Transfer (
189 rentalWalletTotalBalances [0] ,
190 rentalWalletClaimedBalances [0] ,
191 args . amounts [0]
192 );
193 }
194
195 for ( uint256 i ; i < args . tokenIds . length ; i ++) {
196 RentalCheckResult memory check = _checkErc1155Transfer
ë (
197 rentalWalletTotalBalances [ i ],
52
198 rentalWalletClaimedBalances [i ] ,
199 args . amounts [ i]
200 );
201
202 // if there is not_checked result , then we should
ë break the cycle and return with suitable result
203 if ( check . resultCode == PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation ) {
204 return
205 RentalCheckResult (
206 false ,
207 PolicyCheckResponseCode .
ë Not_Checked_When_Erc1155_Operation
208 );
209 }
210 }
211 return
212 RentalCheckResult (
213 true ,
214 PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens
215 );
216 }
FINDINGS & TECH DETAILS
53
121 );
122 }
123 }
Proof of Concept:
ë rentalAutomatorProxy ) , true ) ;
6
7 uint256 [] memory Ids = new uint256 [](2) ;
8 uint256 [] memory ammounts = new uint256 [](2) ;
9 Ids [0] = 2;
10 ammounts [0] = 0;
11 Ids [1] = 1;
12 ammounts [1] = 10;
13
14
15 bytes32 structHash = keccak256 (
16 abi . encode (
17 TRANSFER_FROM_RENTAL_TYPEHASH ,
18 mockErc1155Token ,
19 borrower1 ,
20 borrowerExternal1 ,
21 keccak256 ( abi . encodePacked ( Ids )) ,
22 keccak256 ( abi . encodePacked ( ammounts )) ,
23 rentalAutomatorProxy , // params . operatorOrSpender ,
24 true , // params . approved ,
54
25 address (0) , // params . verifyingContract ,
26 uint256 ( OperationType . SafeTransferFrom_Erc1155 ) ,
27 0,
28 keccak256 ( bytes ( TRANSFER_FROM_RENTAL ) )
29 )
30 );
31
32 bytes32 domainSeparator = rentalRecoverable .
ë buildDomainSeparator ( domainName , VERSION , address (
ë rentalAutomatorProxy )) ;
33 bytes32 readyForSigningHash = Hasher . hashTypedDataV4 (
ë structHash , domainSeparator ) ;
34
35 ( uint8 v , bytes32 r , bytes32 s) = vm . sign ( ownerPrivateKey ,
ë readyForSigningHash );
36 bytes memory signature = abi . encodePacked (r ,s , v) ;
37
38 RentalPolicyArguments memory rentalPolicyArguments =
ë RentalPolicyArguments (
39 address ( mockErc1155Token ) ,
40 Ids ,
41 borrower1 ,
42 borrowerExternal1 ,
43 ammounts ,
FINDINGS & TECH DETAILS
44 address ( rentalAutomatorProxy ) ,
45 true ,
46 address (0) ,
47 OperationType . SafeTransferFrom_Erc1155
48 );
49
50 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 1) , 10) ;
51 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 2) , 0) ;
52 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 1) ,
ë 0) ;
53 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 2) ,
ë 0) ;
54
55 vm . prank ( borrower1 );
56 ( RentalCheckResult memory rentalCheckResult ) =
ë RentalAutomatorFacet ( address ( rentalAutomatorProxy )) .
ë transferFromRentalWallet ( rentalPolicyArguments , AssetType . EIP1155 ,
ë 0, signature ) ;
57
58 assertEq ( rentalCheckResult . success , true );
55
59 assertEq ( uint ( rentalCheckResult . resultCode ) , uint (
ë PolicyCheckResponseCode .
ë Checked_When_Erc1155_Operation_With_Own_Tokens )) ;
60
61 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 1) , 0) ;
62 assertEq ( mockErc1155Token . balanceOf ( borrower1 , 2) , 0) ;
63 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 1) ,
ë 10) ;
64 assertEq ( mockErc1155Token . balanceOf ( borrowerExternal1 , 2) ,
ë 0) ;
65 }
Risk Level:
Likelihood - 5
Impact - 5
Recommendation:
FINDINGS & TECH DETAILS
Remediation Plan:
56
3.6 (HAL-06) CHECKPOLICY REVERTS
FOR APPROVE ERC20 OPERATION TYPE -
MEDIUM
Description:
The solution implements rental wallets that are under the custody of the
Polemos team. Each rental wallet is assigned to each borrower individ-
ually. After a successful asset claim, the rented asset is transferred
to the rental wallet. Furthermore, the borrower can transfer owned to-
kens to the rental wallet. The borrower can manage the rental wallet
off-chain, however, the list of possible actions is strictly limited.
E.g. the borrower can transfer out any owned tokens from the rental
wallet, but transferring the rented asset is forbidden. To check which
action is allowed or prohibited, in before any action, the solution
calls the checkPolicy() function from RentalAutomator. The checkPolicy()
makes necessary checks, including the rental wallet tokens’ balances and
rental balances from RentedStats structure, to decide e.g. if a transfer
FINDINGS & TECH DETAILS
57
Code Location:
58
728 tokenContract ,
729 tokenIds [ i ]
730 );
731 }
732 }
59
59 rentalWalletTotalBalances [0] ,
60 rentalWalletClaimedBalances [0] ,
61 args ,
62 automatorAddress
63 );
64 }
65
66 if (
67 operation >= OperationType . TransferFrom_Erc721 &&
68 operation <= OperationType . SetApprovalForAll_Erc721
69 ) {
70 return
71 _applyErc721Rules (
72 operation ,
73 rentalWalletTotalBalances ,
74 rentalWalletClaimedBalances ,
75 args ,
76 automatorAddress
77 );
78 }
79
80 if (
81 operation >= OperationType . SafeTransferFrom_Erc1155 &&
82 operation <= OperationType . SetApprovalForAll_Erc1155
FINDINGS & TECH DETAILS
83 ) {
84 return
85 _applyErc1155Rules (
86 operation ,
87 rentalWalletTotalBalances ,
88 rentalWalletClaimedBalances ,
89 args ,
90 automatorAddress
91 );
92 }
93
94 revert ( UNKNOWN_RENTAL_OPERATION_TYPE ) ;
95 }
Proof of Concept:
The below unit tests confirm the checkPolicy() reverts for the
Approve_Erc20 operation type.
60
Listing 17: HalbornERC20CheckPolicyTest.t.sol
1 function test_checkPolicy_ERC20_Approve_reverts () public {
2 claimAssetFromLenderToBorrowerERC20 () ;
3
4 uint256 [] memory Ids = new uint256 [](1) ;
5 uint256 [] memory ammounts = new uint256 [](1) ;
6 Ids [0] = 0;
7 ammounts [0] = 0;
8 RentalPolicyArguments memory rentalPolicyArguments =
ë RentalPolicyArguments (
9 address ( mockErc20Token ) ,
10 Ids ,
11 borrower1 ,
12 borrowerExternal1 ,
13 ammounts ,
14 address ( rentalAutomator ) ,
15 true ,
16 address (0) ,
17 OperationType . Approve_Erc20
18 );
19
20 vm . prank ( borrower1 );
21 ( RentalCheckResult memory rentalCheckResult ) =
ë rentalAutomator . checkPolicy ( rentalPolicyArguments , AssetType . EIP20
FINDINGS & TECH DETAILS
ë );
22 assertEq ( rentalCheckResult . success , true );
23 assertEq ( uint ( rentalCheckResult . resultCode ) , uint (
ë PolicyCheckResponseCode . Checked_When_Erc20_Operation ) ) ;
24 }
61
Risk Level:
Likelihood - 5
Impact - 2
Recommendation:
Remediation Plan:
62
3.7 (HAL-07) VALID SIGNATURE CAN BE
REJECTED DUE TO INTEGER UNDERFLOW -
MEDIUM
Description:
Code Location:
FINDINGS & TECH DETAILS
63
Listing 19: LibScholarParamValidator.sol (Line 86)
80 function validateSignatureTimestamp ( uint256 timestamp )
ë internal view {
81 /* solhint - disable - next - line not - rely - on - time */
82 uint256 blockTimestamp = block . timestamp ;
83 uint256 timestampWithTreshold = blockTimestamp +
ë SIGNATURE_TIMESTAMP_FUTURE_TRESHOLD ;
84 require ( timestamp < timestampWithTreshold ,
ë SIGNATURE_TIMESTAMP_IN_FUTURE_ERR ) ;
85 require (
86 blockTimestamp - timestamp <=
ë SIGNATURE_VALIDITY_PERIOD ,
87 SIGNATURE_VALIDITY_PERIOD_ERR
88 );
89 }
Proof of Concept:
Risk Level:
Likelihood - 1
Impact - 5
64
Recommendation:
Remediation Plan:
65
3.8 (HAL-08) AUTHENTICATED
UPGRADEABILITY FACET IS NOT
INDEPENDENT - MEDIUM
Description:
in RentalRecoverableFacet.
• _initEip712Config() initialize function must be called in prior to
initializing domain separator, otherwise the getDomainSeparator()
returns 0x0.
66
Code Location:
90 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
91
92 signatories [i ] = currentAdmin ;
93 }
94
95 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
96
97 FacetCut [] memory facetCut = new FacetCut [](1) ;
98 facetCut [0] = FacetCut (
99 params . facetAddress ,
100 FacetCutAction . Add ,
101 params . selectors ,
102 params . initData
103 );
104 diamondCut ( facetCut ) ;
105
106 emit AddModules ( params , adminDatas ) ;
107 }
67
108
109 function removeModules (
110 RemoveModulesParams memory params ,
111 AdminData [] memory adminDatas
112 ) external {
113 uint8 length = uint8 ( adminDatas . length );
114 _validateParamsLength ( length ) ;
115
116 address [] memory signatories = new address []( length ) ;
117
118 for ( uint8 i ; i < length - 1; ++ i) {
119 AdminData memory currentData = adminDatas [ i ];
120
121 validateSignatureTimestamp ( currentData . timestamp );
122
123 address currentAdmin = recoverable () .
ë recoverSignatoryRemoveModules (
124 params ,
125 currentData ,
126 REMOVE_MODULES ,
127 eip712Config () . getDomainSeparator ()
128 );
129 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
FINDINGS & TECH DETAILS
130
131 signatories [i ] = currentAdmin ;
132 }
133
134 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
135
136 FacetCut [] memory facetCut = new FacetCut [](1) ;
137 facetCut [0] = FacetCut ( params . facetAddress , FacetCutAction
ë . Remove , params . selectors , hex " " );
138 diamondCut ( facetCut ) ;
139
140 emit RemoveModules ( params , adminDatas ) ;
141 }
142
143 function replaceModules (
144 ReplaceModulesParams memory params ,
145 AdminData [] memory adminDatas
146 ) external {
147 uint8 length = uint8 ( adminDatas . length );
68
148 _validateParamsLength ( length ) ;
149
150 address [] memory signatories = new address []( length ) ;
151
152 for ( uint8 i ; i < length - 1; ++ i) {
153 AdminData memory currentData = adminDatas [ i ];
154
155 validateSignatureTimestamp ( currentData . timestamp );
156
157 address currentAdmin = recoverable () .
ë recoverSignatoryReplaceModules (
158 params ,
159 currentData ,
160 REPLACE_MODULES ,
161 eip712Config () . getDomainSeparator ()
162 );
163 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
164
165 signatories [i ] = currentAdmin ;
166 }
167
168 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
FINDINGS & TECH DETAILS
169
170 FacetCut [] memory facetCut = new FacetCut [](1) ;
171 facetCut [0] = FacetCut (
172 params . facetAddress ,
173 FacetCutAction . Replace ,
174 params . selectors ,
175 params . initData
176 );
177 diamondCut ( facetCut ) ;
178
179 emit ReplaceModules ( params , adminDatas );
180 }
181
182 function replaceAdmin (
183 ReplaceAdminApproveParams memory approveParams ,
184 AdminData [] memory adminDatas ,
185 ReplaceAdminConfirmParams memory confirmParams
186 ) external {
187 require ( approveParams . nonce == confirmParams . nonce ,
ë NONCE_MISMATCH_ERR );
69
188 DiamondStorage storage ds = state () ;
189 uint8 length = uint8 ( adminDatas . length );
190 _validateParamsLength ( length ) ;
191
192 address [] memory signatories = new address []( length ) ;
193
194 _validateAdminAddressWithErr ( confirmParams .
ë currentAdminAddress , USER_NOT_OLD_ADMIN_ERR );
195 validateSignatureTimestamp ( confirmParams . timestamp ) ;
196
197 for ( uint8 i ; i < length - 1; ++ i) {
198 AdminData memory currentData = adminDatas [ i ];
199
200 validateSignatureTimestamp ( currentData . timestamp );
201
202 address currentAdmin = recoverable () .
ë recoverReplaceAdminApprove (
203 approveParams ,
204 currentData ,
205 REPLACE_ADMIN_APPROVE ,
206 eip712Config () . getDomainSeparator ()
207 );
208 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
FINDINGS & TECH DETAILS
209
210 signatories [i ] = currentAdmin ;
211 }
212
213 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
214
215 address newAdmin = recoverable () .
ë recoverReplaceAdminConfirm (
216 confirmParams ,
217 REPLACE_ADMIN_CONFIRM ,
218 eip712Config () . getDomainSeparator ()
219 );
220 require (
221 newAdmin == approveParams . newAdminAddress ,
222 SIGNATORY_NOT_NEW_UPGRADABILITY_ADMIN_ERR
223 );
224
225 ds . adminList . _isAdmin [ approveParams . currentAdminAddress ] =
ë false ;
70
226 ds . adminList . _isAdmin [ approveParams . newAdminAddress ] =
ë true ;
227 emit ReplaceAdmin ( approveParams , confirmParams , adminDatas
ë );
228 }
Proof of Concept:
71
Risk Level:
Likelihood - 2
Impact - 5
Recommendation:
Remediation Plan:
72
3.9 (HAL-09) DIAMOND PROXY DOES NOT
SET THE ESSENTIAL VARIABLES IN THE
CONSTRUCTOR - MEDIUM
Description:
Code Location:
73
27 unchecked {
28 facetIndex ++;
29 }
30 }
31
32 LibDiamond . diamondCut ( cutWithInitData ) ;
33 _setSupportedInterfacesByDefault () ;
34 }
Risk Level:
Likelihood - 1
Impact - 5
Recommendation:
Remediation Plan:
RISK ACCEPTED: The Polemos team accepted the risk of this finding. Instead
of setting the aforementioned variables in the constructor, the deployment
script is going to handle the deployment and initialize functionality.
Then, a manual process of verification will be executed.
74
3.10 (HAL-10) STAKE ASSET LACKS
TOKENS WHITELISTING - LOW
Description:
Code Location:
75
192 LibRentalParamValidator . validateAddressNotNullWithError (
193 params . assetContract ,
194 ASSET_CONTRACT_IS_NIL_ERR
195 );
196 LibRentalParamValidator . validateAddressIsContractWithError
ë (
197 params . assetContract ,
198 ADDRESS_NOT_CONTRACT_ERR
199 );
200
201 IERC721 tokenContract = IERC721 ( params . assetContract );
202
203 LibRentalParamValidator . validateErc721AssetOwnership (
204 tokenContract ,
205 params . lender ,
206 params . tokenId ,
207 LENDER_NOT_ASSET_OWNER_ERR
208 );
209 LibRentalParamValidator . validateErc721Approval (
210 tokenContract ,
211 params . lender ,
212 automator ,
213 NO_ERC721_LENDER_APPROVAL_ERR
214 );
FINDINGS & TECH DETAILS
215 }
Risk Level:
Likelihood - 1
Impact - 4
Recommendation:
76
Remediation Plan:
RISK ACCEPTED: The Polemos team accepted the risk associated with this
finding.
FINDINGS & TECH DETAILS
77
3.11 (HAL-11) INCOMPATIBILITY WITH
NON-STANDARD ERC20 TOKENS - LOW
Description:
Code Location:
78
Listing 25: LibRentalTransferUtils.sol (Line 118)
95 function _transferAssetToOriginalOwnerErc20 (
96 RentalAssetDescription storage gameAsset
97 ) private returns ( bool ) {
98 uint256 amount = gameAsset . amount ;
99 address currentOwner = gameAsset . currentOwner ;
100 address originalOwner = gameAsset . originalOwner ;
101 IERC20 tokenContract = IERC20 ( gameAsset . assetContract ) ;
102
103 LibRentalParamValidator . validateErc20Ownership (
104 tokenContract ,
105 currentOwner ,
106 amount ,
107 BORROWER_NOT_ERC20_ASSET_OWNER_ERR
108 );
109 LibRentalParamValidator . validateErc20Approval (
110 tokenContract ,
111 amount ,
112 currentOwner ,
113 address ( this ) ,
114 NO_ERC20_BORROWER_APPROVAL_ERR
FINDINGS & TECH DETAILS
115 );
116
117 gameAsset . currentOwner = originalOwner ;
118 bool isTransfered = tokenContract . transferFrom (
ë currentOwner , originalOwner , amount );
119 require ( isTransfered , TRANSFER_TO_LENDER_ERR ) ;
120 return isTransfered ;
121 }
79
186 tokenContract ,
187 originalOwner ,
188 amount ,
189 LENDER_NOT_ERC20_OWNER_ERR
190 );
191 LibRentalParamValidator . validateErc20Approval (
192 tokenContract ,
193 amount ,
194 originalOwner ,
195 address ( this ) ,
196 NO_ERC20_LENDER_APPROVAL_ERR
197 );
198
199 bool isTransfered = tokenContract . transferFrom (
ë originalOwner , borrower , amount );
200 require ( isTransfered , TRANSFER_TO_LENDER_ERR ) ;
201 return isTransfered ;
202 }
Risk Level:
Likelihood - 1
FINDINGS & TECH DETAILS
Impact - 4
Recommendation:
Remediation Plan:
RISK ACCEPTED: The Polemos team accepted the risk of this finding, but
it is not expected to support any tokens with legacy implementation of
transfer/transferFrom. The Polemos team plans to initially collect fees
in USDC.
80
3.12 (HAL-12) IMPLEMENTATIONS CAN
BE INITIALIZED - LOW
Description:
Risk Level:
Likelihood - 2
Impact - 3
Recommendation:
81
Remediation Plan:
RISK ACCEPTED: The Polemos team is aware of the finding, but it is not
going to apply any contracts amendments. The current design of diamond
and facets is considered secured enough and fixed for rental solution.
Also, presently, the standalone facets are rather useless and represent a
small attack surface for the attacker. However, the proposed remediation
might be applied in the future for new facets and scholar solution.
FINDINGS & TECH DETAILS
82
3.13 (HAL-13) IMMUTABLE FUNCTIONS
ARE NOT REGISTERED - LOW
Description:
83
27 unchecked {
28 facetIndex ++;
29 }
30 }
31
32 LibDiamond . diamondCut ( cutWithInitData ) ;
33 _setSupportedInterfacesByDefault () ;
34 }
ë HardhatRuntimeEnvironment ) {
20 const { deployments , getUnnamedAccounts , network , ethers } = hre
ë ;
21 const [ account1 ] = await getUnnamedAccounts () ;
22 const { deploy , log , get } = deployments ;
23
24 const facetsToInclude : FacetCut [] = [];
25
26 for ( let facetName of FACETS ) {
27 const { address : facetAddress } = await get ( facetName );
28 const { interface : contractInterface } = await ethers .
ë getContractAt (
29 facetName ,
30 facetAddress ,
31 );
32
33 facetsToInclude . push ({
34 action : 0, // FacetCutAction . Add
35 facetAddress ,
36 functionSelectors :
37 getFunctionSelectorsFromContractInterface (
84
ë contractInterface ) ,
38 }) ;
39 }
40
41 const args = [ facetsToInclude ];
42 const deployResult = await deploy ( ' RentalAutomatorProxy ', {
43 from : account1 ,
44 log : true ,
45 autoMine : true ,
46 ...( await getGasPrice ( network . name ) ) ,
47 args ,
48 }) ;
49
50 if ( deployResult . newlyDeployed ) {
51 log ( ` RentalAutomatorProxy is deployed `) ;
52 }
53 };
Risk Level:
Likelihood - 2
Impact - 3
FINDINGS & TECH DETAILS
Recommendation:
Remediation Plan:
85
3.14 (HAL-14) STAKEASSET CAN BE
CALLED WITHOUT RENTAL WALLET
ADDED - LOW
Description:
Code Location:
ë . lender ];
336 // note : allow only for non locked wallet
337 require (! knownRentalWallets [ rentalWallet ]. isLocked ,
ë RENTAL_WALLET_LOCKED_STAKE_ERR ) ;
338 validate ( params . signature ) ;
339 address signatory = recoverable . recoverSignatoryStakeAsset
ë (
340 params ,
341 STAKE_ASSET ,
342 getDomainSeparator ()
343 );
344
345 RentalAssetDescription storage gameAsset = assets [ params .
ë internalAssetId ];
346 RentalAssetDescription memory result = assetHandler .
ë stakeAsset (
347 gameAsset ,
348 params ,
349 signatory ,
350 address ( this )
86
351 );
352 _copyToStorage ( result , gameAsset );
353
354 _addHistoryRecord ( gameAsset , HistoryOperationType . Stake );
355 emit Staked (
356 gameAsset . tokenId ,
357 gameAsset . assetContract ,
358 gameAsset . amount ,
359 gameAsset . internalId ,
360 uint8 ( gameAsset . assetType ) ,
361 gameAsset . originalOwner ,
362 gameAsset . currentOwner ,
363 assetHandler . getCurrentBlockTimeStamp ()
364 );
365 }
Risk Level:
Likelihood - 1
Impact - 3
FINDINGS & TECH DETAILS
Recommendation:
Remediation Plan:
87
3.15 (HAL-15) STAKEASSET CAN BE
FRONT-RUN TO BLOCK INTERNAL ASSET
ID - LOW
Description:
The stakeAsset() function allows the lender to make an offer of asset lend-
ing. The function assumes that the lender provides the internalAssetId
value within the StakeAssetParams structure. The internalAssetId param-
eter must be unique, and it represents the status of the asset record
within the lending/borrowing life cycle. The assessment did not reveal
any possibility to stake another asset under the same internalAssetId
parameter. Since this value must be provided by the user, it is pos-
sible to front-run the stakeAsset() function by the malicious user to
reserve internalAssetId value, ultimately, preventing the legitimate user
to stake the asset. Any subsequent call to the stakeAsset() function with
the same internalAssetId value will revert, as the underlying record’s
status will be different from Status.None.
FINDINGS & TECH DETAILS
Code Location:
88
Listing 32: RentalAutomator.sol (Line 345)
334 function stakeAsset ( StakeAssetParams memory params ) external {
335 address rentalWallet = externalWalletToRentalWallet [ params
ë . lender ];
336 // note : allow only for non locked wallet
337 require (! knownRentalWallets [ rentalWallet ]. isLocked ,
ë RENTAL_WALLET_LOCKED_STAKE_ERR ) ;
338 validate ( params . signature ) ;
339 address signatory = recoverable . recoverSignatoryStakeAsset
ë (
340 params ,
341 STAKE_ASSET ,
342 getDomainSeparator ()
343 );
344
345 RentalAssetDescription storage gameAsset = assets [ params .
ë internalAssetId ];
346 RentalAssetDescription memory result = assetHandler .
ë stakeAsset (
347 gameAsset ,
348 params ,
349 signatory ,
350 address ( this )
351 );
FINDINGS & TECH DETAILS
89
173 StakeAssetParams memory params ,
174 address signatory ,
175 address automator
176 ) private view {
177 // note : the following is required to prevent re - use of '
ë internalId ' value for staking the same or new asset
178 LibRentalParamValidator . validateAssetHasStatus (
179 gameAsset . status ,
180 Status . None ,
181 ASSET_STATUS_IS_NOT_NONE_ERR
182 );
183 // note : part of significant validations are left in
ë RentalAutomator due to reduce - contract - codebase reason
184
185 LibRentalParamValidator . validateInternalId ( params .
ë internalAssetId );
186 LibRentalParamValidator . validateSignatureTimestamp ( params .
ë timestamp );
187 LibRentalParamValidator . validateAddressesMatchWithError (
188 signatory ,
189 params . lender ,
190 SIGNATORY_NOT_LENDER_ERR
191 );
192 LibRentalParamValidator . validateAddressNotNullWithError (
FINDINGS & TECH DETAILS
90
212 automator ,
213 NO_ERC721_LENDER_APPROVAL_ERR
214 );
215 }
Risk Level:
Likelihood - 1
Impact - 3
Recommendation:
Remediation Plan:
RISK ACCEPTED: The Polemos team accepted the risk associated with this
finding.
FINDINGS & TECH DETAILS
91
3.16 (HAL-16) RECLAIMASSET CAN BE
CALLED AFTER
RENTALORRECLAIMPERIOD - LOW
Description:
Code Location:
FINDINGS & TECH DETAILS
92
Risk Level:
Likelihood - 1
Impact - 3
Recommendation:
Remediation Plan:
93
3.17 (HAL-17) LACK OF EMERGENCY
STOP PATTERN - LOW
Description:
The current solution does not implement any kind of emergency stop pat-
tern. Such a pattern allows the project team to pause crucial functional-
ities, while being in the state of emergency, e.g., being under adversary
attack. The most prevalent application of the emergency stop pattern is
the Pausable contract from the OpenZeppelin’s library.
Risk Level:
Likelihood - 2
Impact - 2
FINDINGS & TECH DETAILS
Recommendation:
Remediation Plan:
94
3.18 (HAL-18) DIAMOND PROXY
INITIALIZE FUNCTIONS CAN BE
FRONT-RUN - LOW
Description:
Risk Level:
Likelihood - 1
FINDINGS & TECH DETAILS
Impact - 3
Recommendation:
Remediation Plan:
RISK ACCEPTED: The Polemos team accepted the risk associated with this
finding.
95
3.19 (HAL-19) SIGNATURES USED IN
AUTHENTICATED UPGRADABILITY FACET
CAN BE REPLAYED - LOW
Description:
186 ) external {
187 require ( approveParams . nonce == confirmParams . nonce ,
ë NONCE_MISMATCH_ERR );
188 DiamondStorage storage ds = state () ;
189 uint8 length = uint8 ( adminDatas . length );
190 _validateParamsLength ( length ) ;
191
192 address [] memory signatories = new address []( length ) ;
193
194 _validateAdminAddressWithErr ( confirmParams .
ë currentAdminAddress , USER_NOT_OLD_ADMIN_ERR );
195 validateSignatureTimestamp ( confirmParams . timestamp ) ;
196
197 for ( uint8 i ; i < length - 1; ++ i) {
198 AdminData memory currentData = adminDatas [ i ];
199
200 validateSignatureTimestamp ( currentData . timestamp );
201
202 address currentAdmin = recoverable () .
ë recoverReplaceAdminApprove (
96
203 approveParams ,
204 currentData ,
205 REPLACE_ADMIN_APPROVE ,
206 eip712Config () . getDomainSeparator ()
207 );
208 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
209
210 signatories [i ] = currentAdmin ;
211 }
212
213 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
214
215 address newAdmin = recoverable () .
ë recoverReplaceAdminConfirm (
216 confirmParams ,
217 REPLACE_ADMIN_CONFIRM ,
218 eip712Config () . getDomainSeparator ()
219 );
220 require (
221 newAdmin == approveParams . newAdminAddress ,
222 SIGNATORY_NOT_NEW_UPGRADABILITY_ADMIN_ERR
223 );
FINDINGS & TECH DETAILS
224
225 ds . adminList . _isAdmin [ approveParams . currentAdminAddress ] =
ë false ;
226 ds . adminList . _isAdmin [ approveParams . newAdminAddress ] =
ë true ;
227 emit ReplaceAdmin ( approveParams , confirmParams , adminDatas
ë );
228 }
Risk Level:
Likelihood - 1
Impact - 3
97
Recommendation:
Remediation Plan:
98
3.20 (HAL-20) THE AUTHENTICATED
UPGRADABILITY FACET SHARES ITS
STORAGE ADDRESS SLOT WITH OTHER
FACETS - LOW
Description:
99
29 mapping ( bytes32 => bool ) isSignatureApplied ;
30 string domainName ;
31 bytes32 cachedDomainSeparator ;
32 uint256 defaultReclaimPeriod ;
33 uint256 minRentalPeriod ;
34 uint256 maxRentalPeriod ;
35 address feeAddress ;
36 address adminAddress ;
37 bool isPaused ;
38 uint256 historyIndex ; // id of the latest history record
39 address priceOracle ; // trusted oracle that supplies asset
ë price
40 IERC20 feeTokenContractInstance ;
41 mapping ( bytes32 => RentalAssetDescription ) assets ;
42 // Rental wallet the Automator instance of familar of
43 // Note that it is Admin user responsibility to include ,
ë exclude rental wallet addresses to / from the list
44 mapping ( address => KnownRentalWalletRecord ) knownRentalWallets
ë ;
45 // Mapping required to track an Asset related activity
46 mapping ( uint256 => RentalHistoryRecord ) historyRecords ;
47 // mappings for storing amounts of rented tokens for each user
48 RentedStats rentedStats ;
49 StakedStats stakedStats ;
FINDINGS & TECH DETAILS
50 AuthenticationData adminList ;
51 }
100
Risk Level:
Likelihood - 1
Impact - 3
Recommendation:
Remediation Plan:
101
3.21 (HAL-21) THE ADMIN LIST IS
MISSING INPUT VALIDATION - LOW
Description:
disabled.
102
64
65 for ( uint8 i ; i < MAX_ADMINS_COUNT ; ++ i) {
66 address currentAdmin = adminUserList [ i ];
67 ds . adminList . _isAdmin [ currentAdmin ] = true ;
68 ds . adminList . addresses . push ( currentAdmin ) ;
69 }
70 facetStorage . isInitialized = true ;
71 }
193
194 _validateAdminAddressWithErr ( confirmParams .
ë currentAdminAddress , USER_NOT_OLD_ADMIN_ERR );
195 validateSignatureTimestamp ( confirmParams . timestamp ) ;
196
197 for ( uint8 i ; i < length - 1; ++ i) {
198 AdminData memory currentData = adminDatas [ i ];
199
200 validateSignatureTimestamp ( currentData . timestamp );
201
202 address currentAdmin = recoverable () .
ë recoverReplaceAdminApprove (
203 approveParams ,
204 currentData ,
205 REPLACE_ADMIN_APPROVE ,
206 eip712Config () . getDomainSeparator ()
207 );
208 _validateAdminAddressWithErr ( currentAdmin ,
ë USER_NOT_ADMIN_ERR );
209
210 signatories [i ] = currentAdmin ;
103
211 }
212
213 _validateListContainsUniqueItemsWithErr ( signatories ,
ë DUPLICATED_ADMIN_SIGNATORIES_ERR );
214
215 address newAdmin = recoverable () .
ë recoverReplaceAdminConfirm (
216 confirmParams ,
217 REPLACE_ADMIN_CONFIRM ,
218 eip712Config () . getDomainSeparator ()
219 );
220 require (
221 newAdmin == approveParams . newAdminAddress ,
222 SIGNATORY_NOT_NEW_UPGRADABILITY_ADMIN_ERR
223 );
224
225 ds . adminList . _isAdmin [ approveParams . currentAdminAddress ] =
ë false ;
226 ds . adminList . _isAdmin [ approveParams . newAdminAddress ] =
ë true ;
227 emit ReplaceAdmin ( approveParams , confirmParams , adminDatas
ë );
228 }
FINDINGS & TECH DETAILS
Risk Level:
Likelihood - 1
Impact - 3
Recommendation:
104
Remediation Plan:
105
3.22 (HAL-22) SCHOLAR AUTOMATOR
PROXY LACKS THE ISPAUSED CHECK - LOW
Description:
50 assembly {
51 ds . slot := position
52 }
53 // get facet from function selector
54 address facet = address ( bytes20 ( ds . facets [ msg . sig ]) ) ;
55 require ( facet != address (0) , FUNCTION_NOT_EXISTS_ERR );
56 // Execute external function from facet using delegatecall
ë and return any value .
57 // solhint - disable - next - line no - inline - assembly
58 assembly {
59 // copy function selector and any arguments
60 calldatacopy (0 , 0 , calldatasize () )
61 // execute function call using the facet
62 let result := delegatecall ( gas () , facet , 0 ,
ë calldatasize () , 0 , 0)
63 // get any return value
64 returndatacopy (0 , 0 , returndatasize () )
65 // return any return value or error back to the caller
66 switch result
67 case 0 {
106
68 revert (0 , returndatasize () )
69 }
70 default {
71 return (0 , returndatasize () )
72 }
73 }
74 }
Risk Level:
Likelihood - 2
Impact - 2
Recommendation:
Remediation Plan:
FINDINGS & TECH DETAILS
107
3.23 (HAL-23) REDUNDANT VALIDATION
FOR ERC721 ASSET CLAIM -
INFORMATIONAL
Description:
The claimAsset() function allows the borrower to borrow an asset from the
lender. The function contains multiple input validation checks for both
borrowers and price oracle’s parameters. However, some checks appear to
be redundant.
Code Location:
108
221 KnownRentalWalletRecord memory record ,
222 address oracleSignatory ,
223 address signatory ,
224 AutomatorConfig memory config
225 ) private view {
226 LibRentalParamValidator . validateAssetHasStatus (
227 gameAsset . status ,
228 Status . Staked ,
229 ASSET_IS_NOT_STAKED_ERR
230 );
231
232 // note : params . borrower is validated in ' addRentalWallet '
ë method
233 LibRentalParamValidator . validateIsRentalWallet ( record .
ë known ) ;
234 LibRentalParamValidator . validateAddressesMatchWithError (
235 oracleSignatory ,
236 config . oraclePriceAddress ,
237 SIGNATORY_NOT_ORACLE_ERR
238 );
239 LibRentalParamValidator . validateAddressesMatchWithError (
240 signatory ,
241 record . externalWallet ,
242 SIGNATORY_NOT_BORROWER_EW_ERR
FINDINGS & TECH DETAILS
243 );
244 LibRentalParamValidator . validateSignatureTimestamp ( params .
ë timestamp );
245 LibRentalParamValidator . validateSignatureTimestamp (
ë oracleParams . oracleTimestamp ) ;
246
247 // note : do note validate ' game . internalId ' == ' params .
ë internalAssetId ', because it is used in Automator
248 // to retrieve the value of RentalAssetDescription type
ë related to ' params . internalAssetId '.
249 // if supplied ' game . internalId ' does not exists , then its
ë status is Status . Unstaked , therefore first - line of the method
ë fails
250 LibRentalParamValidator . validateUintsMatchWithError (
251 uint256 ( params . internalAssetId ) ,
252 uint256 ( oracleParams . internalAssetId ) ,
253 INTERNAL_IDS_NOT_MATCH_ERR
254 );
255 LibRentalParamValidator . validateAddressesMatchWithError (
256 gameAsset . assetContract ,
109
257 oracleParams . contractAddress ,
258 TOKEN_CONTRACTS_NOT_MATCH_ERR
259 );
260 LibRentalParamValidator . validateUintsMatchWithError (
261 gameAsset . tokenId ,
262 oracleParams . tokenId ,
263 TOKEN_IDS_NOT_MATCH_ERR
264 );
265 LibRentalParamValidator . validateUintsMatchWithError (
266 gameAsset . amount ,
267 oracleParams . amount ,
268 AMOUNTS_NOT_MATCH_ERR
269 );
270
271 LibRentalParamValidator . validateUintsMatch (
272 params . rentalPeriodInSeconds ,
273 oracleParams . rentalPeriodInSeconds
274 );
275 LibRentalParamValidator . validateBorrowingPeriod (
276 params . rentalPeriodInSeconds ,
277 config . minRentalPeriod ,
278 config . maxRentalPeriod
279 );
280 }
FINDINGS & TECH DETAILS
Risk Level:
Likelihood - 1
Impact - 1
Recommendation:
Remediation Plan:
110
3.24 (HAL-24) NONCE IMPLEMENTATION
IS REDUNDANT - INFORMATIONAL
Description:
Code Location:
111
Risk Level:
Likelihood - 1
Impact - 1
Recommendation:
Remediation Plan:
112
3.25 (HAL-25) CLAIMASSETPARAMS CAN
INCLUDE ASSET PRICE - INFORMATIONAL
Description:
The claimAsset() function allows the borrower to borrow an asset from the
lender. To authorize the function, two signatures must be present: first
calculated by the borrower, second calculated by the price oracle. The
OraclePriceFeedParams signed by the price oracle contains the assetPrice
parameter, whereas the ClaimAssetParams parameters signed by the borrower
does not. This difference makes the agreement one-sided. As a result,
the implementation poses a risk to the borrower that the price might be
changed before the transaction finalization on-chain. However, still,
the borrower should be protected by the token’s allowance set in prior
to the transaction. Furthermore, the risk of price manipulation exists
off-chain, and it would be related either to human error, business logic
error, or price oracle’s wallet private key hijacking.
FINDINGS & TECH DETAILS
Code Location:
113
100 uint256 assetPrice ;
101 uint256 oracleTimestamp ;
102 uint256 rentalPeriodInSeconds ;
103 bytes signature ;
104 }
378 oracleParams ,
379 ORACLE_PRICE_FEED ,
380 getDomainSeparator ()
381 );
382 address signatory = recoverable . recoverSignatoryClaimAsset
ë (
383 params ,
384 CLAIM_ASSET ,
385 getDomainSeparator ()
386 );
387
388 RentalAssetDescription storage gameAsset = assets [ params .
ë internalAssetId ];
389 AutomatorConfig memory config = getConfigWithSafetyCheck ()
ë ;
390 RentalAssetDescription memory result = assetHandler .
ë claimAsset (
391 oracleParams ,
392 params ,
393 gameAsset ,
394 record ,
114
395 oracleSignatory ,
396 signatory ,
397 config
398 );
399 LibRentalTransferUtils . transferAssetToBorrower ( gameAsset ,
ë params . borrower );
400 _copyToStorage ( result , gameAsset );
401 // note : it deducts fees from external wallet of borrower
ë ( so called ' borrowerSupervisor ')
402 LibRentalTransferUtils . payRentalFees (
403 IERC20 ( config . feeTokenContractAddress ) ,
404 record . externalWallet ,
405 config . feeAddress ,
406 oracleParams . assetPrice
407 );
408
409 _updateRentedAssetRecord (
410 gameAsset . assetType ,
411 params . borrower , // rental wallet of borrower
412 gameAsset . originalOwner , // external wallet of lender
413 gameAsset . assetContract ,
414 gameAsset . tokenId ,
415 gameAsset . amount ,
416 true
FINDINGS & TECH DETAILS
417 );
418
419 _addHistoryRecord ( gameAsset , HistoryOperationType . Claim );
420 emit Claimed (
421 gameAsset . internalId ,
422 gameAsset . originalOwner ,
423 gameAsset . currentOwner ,
424 gameAsset . currentOwnerSupervisor ,
425 assetHandler . getCurrentBlockTimeStamp () ,
426 oracleParams . assetPrice ,
427 params . rentalPeriodInSeconds
428 );
429 }
Risk Level:
Likelihood - 1
Impact - 1
115
Recommendation:
Remediation Plan:
116
3.26 (HAL-26)
SETFEETOKENCONTRACTADDRESS MAY
REVERT FOR ERC20 WITH FALLBACK
IMPLEMENTATION - INFORMATIONAL
Description:
Code Location:
117
37 if ( erc165Instance . supportsInterface ( ERC20_INTERFACE_ID ))
ë {
38 result = DetectionResult . Erc20 ;
39 } else if ( erc165Instance . supportsInterface (
ë ERC721_INTERFACE_ID ) ) {
40 result = DetectionResult . Erc721 ;
41 } else if ( erc165Instance . supportsInterface (
ë ERC1155_INTERFACE_ID )) {
42 result = DetectionResult . Erc1155 ;
43 }
44 }
Risk Level:
Likelihood - 1
Impact - 1
Recommendation:
Remediation Plan:
118
3.27 (HAL-27) REDUNDANT
INITIALIZATION OF UINT256 VARIABLES
TO 0 - INFORMATIONAL
Description:
Code Location:
PublicUtils.sol
- Line 18: for (uint256 i = 0; i < length; ++i){
RentalAutomator.sol
- Line 718: for (uint256 i = 0; i < tokenIds.length; i++){
RentalAutomatorBase.sol
FINDINGS & TECH DETAILS
LibRentalPolicyCheck.sol
- Line 195: for (uint256 i = 0; i < args.tokenIds.length; i++){
ScholarAutomator.sol
- Line 161: for (uint256 i = 0; i < list.size(); i++){
ScholarAutomatorBase.sol
- Line 88: index = 0;
Risk Level:
Likelihood - 1
Impact - 1
119
Recommendation:
Remediation Plan:
120
3.28 (HAL-28) GAS OVER-CONSUMPTION
IN LOOPS - INFORMATIONAL
Description:
Code Location:
RentalAutomator.sol
- Line 718: for (uint256 i = 0; i < tokenIds.length; i++){
LibRentalPolicyCheck.sol
- Line 195: for (uint256 i = 0; i < args.tokenIds.length; i++){
ScholarAutomator.sol
- Line 161: for (uint256 i = 0; i < list.size(); i++){
FINDINGS & TECH DETAILS
Risk Level:
Likelihood - 1
Impact - 1
Proof of Concept:
121
8 }
9 function preiincrement ( uint256 iterations ) public {
10 for ( uint256 i = 0; i < iterations ; ++ i) {
11 }
12 }
13 }
Recommendation:
Remediation Plan:
122
3.29 (HAL-29) IMMUTABILITY CAN BE
INTRODUCED - INFORMATIONAL
Description:
Code Location:
Risk Level:
Likelihood - 1
Impact - 1
123
Recommendation:
Remediation Plan:
124
CONTRACT
UPGRADABILITY
125
4.1 Solution structure
The team at Halborn analyzed the structure of the smart contracts in
scope to make sure all future upgrades are secure. The chosen solution
by the Polemos team is the Diamond Proxy pattern. The solution is based
on diamond-2-hardhat.
BaseFacet.sol
CONTRACT UPGRADABILITY
126
VersionFacet.sol
Eip712ConfigFacet.sol
CONTRACT UPGRADABILITY
127
PrettyBasicModules.sol
CONTRACT UPGRADABILITY
128
RentalAssetHandlerFacet.sol
CONTRACT UPGRADABILITY
129
SignatureValidatorFacet.sol
CONTRACT UPGRADABILITY
130
RentalAdminFacet.sol
CONTRACT UPGRADABILITY
131
RentalDiamondCutInternal.sol
CONTRACT UPGRADABILITY
132
RentalAuthenticatedUpgradabilityFacet.sol
CONTRACT UPGRADABILITY
133
RentalAutomatorBaseFacet.sol
CONTRACT UPGRADABILITY
134
RentalAutomatorFacet.sol
CONTRACT UPGRADABILITY
135
RentalDiamondLoupeFacet.sol
CONTRACT UPGRADABILITY
136
CONTRACT UPGRADABILITY
RentalPausable.sol
137
RentalAutomatorProxy.sol
CONTRACT UPGRADABILITY
138
CONTRACT UPGRADABILITY
139
4.2 Diamond storage
The solution follows the diamond storage pattern presented in diamond-
Storage. No possibility of storage collisions between the proxies and
implementations was identified. The solution uses several namespaces to
select address space for storage’s structs:
- polemos.diamond.standard.diamond.storage
- polemos.diamond.standard.admin.storage
- polemos.diamond.standard.authentication.storage
- polemos.diamond.standard.eip712.config.storage
This approach does not require reserving some space for future variables
(e.g. by means of __gap parameter).
The Rental and Scholar solutions use the same namespaces for the storage
purposes; however, they are not meant to be deployed within a single
proxy.
4.3 Initialization
CONTRACT UPGRADABILITY
140
Listing 53: RentalAuthenticatedUpgradabilityFacet.sol (Line 70)
56 function initalizeAuthenticatedUpgradabilityModule ( address []
ë calldata adminUserList ) external {
57 DiamondStorage storage ds = state () ;
58
59 RentalAuthenticatedUpgradabilityFacetStorage
60 storage facetStorage = getAuthentificationFacetStorage
ë () ;
61 require (! facetStorage . isInitialized ,
ë AUTHENTICATED_UPGRADABILITY_FACET_ALREADY_INITIALIZED ) ;
62 require ( adminUserList . length == MAX_ADMINS_COUNT ,
ë INVALID_ADMIN_LIST_LENGTH_ERR );
63 _validateListContainsUniqueItemsWithErr ( adminUserList ,
ë DUPLICATED_ADMIN_USERS_ERR );
64
65 for ( uint8 i ; i < MAX_ADMINS_COUNT ; ++ i) {
66 address currentAdmin = adminUserList [ i ];
67 ds . adminList . _isAdmin [ currentAdmin ] = true ;
68 ds . adminList . addresses . push ( currentAdmin ) ;
69 }
70 facetStorage . isInitialized = true ;
71 }
CONTRACT UPGRADABILITY
141
36 }
77 }
142
33 );
34
35 facetStorage . isInitialized = true ;
36 }
RentalAdminFacet
- RentalAdminFacet [X]
- SignatureValidatorFacet [X] (no init function)
- BaseFacet [X] (no init function)
RentalAuthenticatedUpgradabilityFacet
- RentalAuthenticatedUpgradabilityFacet [X]
- RentalDiamondCutInternal [X] (no init function)
- BaseFacet [X] (no init function)
RentalAutomatorFacet
- RentalAutomatorFacet [X]
- Eip712ConfigFacet[X]
- RentalAutomatorBaseFacet [X] (no init function)
CONTRACT UPGRADABILITY
RentalAssetHandlerFacet
- RentalAssetHandlerFacet [X] (no init function)
RentalRecoverableFacet
- RentalRecoverableFacet [X] (no init function)
PublicUtilsFacet
- PublicUtilsFacet [X] (no init function)
RentalPolicyHandlerFacet
- RentalPolicyHandlerFacet [X] (no init function)
143
RentalDiamondLoupeFacet
- RentalDiamondLoupeFacet [X] (no init function)
- BaseFacet [X] (no init function)
4.4 Deployment
The current deployment procedure is split into the several steps. Firstly,
the facets are deployed. Secondly, the proxy is deployed with facets’
addresses as input. Thirdly, the facets are initialized. This ap-
proach makes it vulnerable to front-runs and missing initialize func-
tion calls. The weakness was reported in HAL-09 - DIAMOND PROXY DOES
CONTRACT UPGRADABILITY
144
Proxy deployment - 010_deploy_rental_automator_proxy.ts
CONTRACT UPGRADABILITY
145
Initialize functions calls - 010_init_rental_automator_step_a.ts
CONTRACT UPGRADABILITY
146
AUTOMATED TESTING
147
5.1 STATIC ANALYSIS REPORT
Description:
Results:
RentalAutomator.sol
AUTOMATED TESTING
148
AUTOMATED TESTING
149
AUTOMATED TESTING
150
AUTOMATED TESTING
151
ScholarAutomator.sol
AUTOMATED TESTING
152
AUTOMATED TESTING
153
RentalAutomatorProxy.sol
ScholarAutomatorProxy.sol
AUTOMATED TESTING
154
• Re-entrancy issues are false positives.
• Usage of timestamp for comparisons is false positive.
• Vast number of findings are false positives.
• Multiple informational issues related to solidity naming convention
were identified.
• Some findings were reported, e.g. IMMUTABILITY CAN BE INTRODUCED
• No major issues were found by Slither.
AUTOMATED TESTING
155
5.2 AUTOMATED SECURITY SCAN
Description:
Results:
RentalAutomator.sol
AUTOMATED TESTING
156
AUTOMATED TESTING
157
AUTOMATED TESTING
158
ScholarAutomator.sol
AUTOMATED TESTING
159
RentalAutomatorProxy.sol
ScholarAutomatorProxy.sol
160
THANK YOU FOR CHOOSING