Platform: Code4rena
Start Date: 06/06/2023
Pot Size: $60,500 USDC
Total HM: 5
Participants: 50
Period: 8 days
Judge: gzeon
Id: 246
League: ETH
Rank: 6/50
Findings: 3
Award: $3,069.12
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: auditor0517
2574.2156 USDC - $2,574.22
https://github.com/code-423n4/2023-06-llama/blob/aac904d31639c1b4b4e97f1c76b9c0f40b8e5cee/src/strategies/LlamaRelativeQuorum.sol#L282-L292 https://github.com/code-423n4/2023-06-llama/blob/aac904d31639c1b4b4e97f1c76b9c0f40b8e5cee/src/strategies/LlamaRelativeQuorum.sol#L199-L210 https://github.com/code-423n4/2023-06-llama/blob/aac904d31639c1b4b4e97f1c76b9c0f40b8e5cee/src/LlamaCore.sol#L565-L584
LlamaRelativeQuorum isActionApproved / isActionDisapproved check condition error: quantity > holders
.
The two cannot be compared. In general quantity > holder, so the approver was lower than expected.
diff --git a/src/strategies/LlamaRelativeQuorum.sol b/src/strategies/LlamaRelativeQuorum.sol index d796ae9..bfc49c3 100644 --- a/src/strategies/LlamaRelativeQuorum.sol +++ b/src/strategies/LlamaRelativeQuorum.sol @@ -284,6 +284,14 @@ contract LlamaRelativeQuorum is ILlamaStrategy, Initializable { return action.totalApprovals >= _getMinimumAmountNeeded(actionApprovalSupply[actionInfo.id], minApprovalPct); } + function isActionApprovedInfo(ActionInfo calldata actionInfo) public returns (bool) { + Action memory action = llamaCore.getAction(actionInfo.id); + emit ApproveInfo(action.totalApprovals, actionApprovalSupply[actionInfo.id], minApprovalPct, _getMinimumAmountNeeded(actionApprovalSupply[actionInfo.id], minApprovalPct)); + return action.totalApprovals >= _getMinimumAmountNeeded(actionApprovalSupply[actionInfo.id], minApprovalPct); + } + + event ApproveInfo(uint, uint, uint, uint); + /// @inheritdoc ILlamaStrategy function isActionDisapproved(ActionInfo calldata actionInfo) public view returns (bool) { Action memory action = llamaCore.getAction(actionInfo.id); diff --git a/test/LlamaStrategy.t.sol b/test/LlamaStrategy.t.sol index 136b00f..7a66896 100644 --- a/test/LlamaStrategy.t.sol +++ b/test/LlamaStrategy.t.sol @@ -243,7 +243,7 @@ contract LlamaStrategyTest is LlamaTestSetup { address _policyHolder = address(uint160(i + 100)); if (mpPolicy.balanceOf(_policyHolder) == 0) { vm.prank(address(mpExecutor)); - mpPolicy.setRoleHolder(uint8(Roles.TestRole1), _policyHolder, 1, type(uint64).max); + mpPolicy.setRoleHolder(uint8(Roles.TestRole1), _policyHolder, 10_000, type(uint64).max); } } } @@ -685,10 +685,9 @@ contract Initialize is LlamaStrategyTest { } contract IsActionApproved is LlamaStrategyTest { - function testFuzz_ReturnsTrueForPassedActions(uint256 _actionApprovals, uint256 _numberOfPolicies) public { - _numberOfPolicies = bound(_numberOfPolicies, 2, 100); - _actionApprovals = - bound(_actionApprovals, FixedPointMathLib.mulDivUp(_numberOfPolicies, 4000, 10_000), _numberOfPolicies); + function testFuzz_ReturnsTrueForPassedActions() public { + uint256 _numberOfPolicies = 100; + uint256 _actionApprovals = 40; ILlamaStrategy testStrategy = deployTestStrategy(); @@ -696,9 +695,9 @@ contract IsActionApproved is LlamaStrategyTest { ActionInfo memory actionInfo = createAction(testStrategy); - approveAction(_actionApprovals, actionInfo); + approveAction(1, actionInfo); - bool _isActionApproved = testStrategy.isActionApproved(actionInfo); + bool _isActionApproved = LlamaRelativeQuorum(address(testStrategy)).isActionApprovedInfo(actionInfo); assertEq(_isActionApproved, true); }
If you run the patch, you will find that emit ApproveInfo(: 10000, : 100, : 4000, : 40)
.
This means that a person has quantity = 10_000 > holders * 40% = 100 * 40% = 40
One person votes more than the total number of holders, the vote is successful.
Foundry
You might want to use getRoleSupplyAsQuantitySum
not getRoleSupplyAsNumberOfHolders
Context
#0 - c4-pre-sort
2023-06-19T11:51:17Z
0xSorryNotSorry marked the issue as duplicate of #203
#1 - c4-judge
2023-07-06T13:51:47Z
gzeon-c4 marked the issue as satisfactory
🌟 Selected for report: Rolezn
Also found by: 0xSmartContract, DavidGiladi, QiuhaoLi, Sathish9098, kutugu, libratus, matrix_0wl, minhquanym
25.6332 USDC - $25.63
ID | Title | Severity |
---|---|---|
[L-01] | Clones.cloneDeterministic can be frontrun | Low |
[L-02] | Malicious guard can break protocol | Low |
[L-03] | Guard may conflict when the action target and selector are same | Low |
[L-04] | NFT metadata name length has no limit, may destroy NFT display | Low |
If the initialization parameters and salt are in sync, frontrun can do nothing.
But in factory, salt relies only on name, and if a name has value, confusing, a malicious user may frontrun to register the name to confuse user to arbitrage.
Keep initialization parameters and salt are in sync
A malicious user may deploy guard through an upgradable contract or create2 + selfdestruct.
After action approved, the protocol can be corrupted by upgrading guard, especially those function with onlyLlama modifier, and the malicious guard will stop the protocol until guard is modified to a different address.
In particular, for LlamaCore.setGuard.selector
, if is a malicious guard that prevents the guard to modify again.
Consider prevent setGuard for LlamaCore.setGuard.selector
Guard only distinguishes between target and selector. When actions occur frequently, different actions may require different guards. When the guard is changed multiple times, conflicts may occur due to sequence problems.
Use data instead of selector to distinguish
NFT metadata name length has no limit, may destroy NFT display
Limit name length
#0 - c4-judge
2023-07-02T16:09:19Z
gzeon-c4 marked the issue as grade-b
🌟 Selected for report: 0xnev
Also found by: 0xSmartContract, K42, QiuhaoLi, VictoryGod, dirk_y, joestakey, ktg, kutugu, libratus, mahdirostami, neko_nyaa, peanuts, xuwinnie
469.2665 USDC - $469.27
Llama is a governance system for onchain organizations. It uses non-transferable NFTs to encode access control, features programmatic control of funds, and includes a modular framework to define action execution rules.
The video explainer provides a high-level overview of the Llama system and the docs describe the core components.
The core concept of LLAMA is to represent a unique user through the "Soulbound Token", note that the token can't be used as a DID and can still be attacked by witches. Therefore, LLAMA exercises various rights by granting role to tokenId, similar to the reputation system, and approximates the concept of DID in the form of tokenId
+ role
.
Different governance systems are isolated from each other, and the contract code is reused through clone
.
The system works through the decentralized voting mechanism for the execution of action
, for the execution of various things, support delegatecall
and call
.
According to the protocol, there are no privileged roles and powers, but LLAMA as a general framework, doesn't make clear specifications. The implementation depends on the specific governance system, see Specification
below.
As a general governance system, LLAMA implements the basic framework, so there are no clear specification. Including the following problems:
LlamaAbsolutePeerReview
is to dynamically change the creator's vote weights.LLAMA_POLICY_LOGIC
/ LLAMA_CORE_LOGIC
is immutable, if there is a bug, factory contract need be abandonedconstant
/ immutable
value is hardcoded and will be cloned synchronouslyname
could be used for fraud on NFTdelegatecall
: Whether the untrusted contract can be invoked to modify the contract storage and control the contract
LlamaExecutor
: no storage, works wellLlamaAccount
: can change storage and reset latercall
: Focus on contract function calls within this protocol, calling external contracts have no effect
onlyLlama
modifier.According to the documentation, LLAMA can be deployed on EVM compatible chains and Rollup. There are some related issues to be aware of:
push0
is still not supported by many chains, like Arbitrum and might be problematic for projects compiled with a version of Solidity >= 0.8.20
(when it was introduced).replay
/ malleability
attack, and they are handled well by LLAMAopcode
EIP712
and EIP721
.
tokenURI
is not implemented as EIP721
required.LLAMA had been audited before and needed to check if the bugs had been fixed correctly.
Scope of change:
ILlamaAccount
interfaceLlamaAccount
with ILlamaAccount
throughout the codebase.LlamaAccount
now implements ILlamaAccount
authorizedAccountLogics
mapping in LlamaFactory
LlamaFactory
as part of the
factory construction and authorizeAccountLogic()
function which sets
authorizedAccountLogics
mapping to true and emits
AccountLogicAuthorized
event._deployAccounts
in LlamaCore
now has a check to see if the Llama
Account logic contract is an authorized one and reverts with a
UnauthorizedAccountLogic
error if not.computeLlamaAccountAddress
function signature in
LlamaLens
.payable
for ILlamaAccount
ILlamaAccount
initialize()
function takes bytes memory config
as
input parameter instead of string memory name
to make it generalizable
for future Account Logic contracts.src/LlamaAccount.sol
-> src/accounts/LlamaAccount.sol
StrategyAuthorized
,
AccountCreated
and ScriptAuthorized
events which are unique and have
no need to be indexed.24 hours
#0 - c4-pre-sort
2023-06-19T13:20:13Z
0xSorryNotSorry marked the issue as high quality report
#1 - c4-judge
2023-07-02T15:46:38Z
gzeon-c4 marked the issue as grade-a