Skip to content

Commit

Permalink
Merge pull request #65 from loreum-org/chore/deploy-tokens
Browse files Browse the repository at this point in the history
Fix bug in the promote function and rename private variables
  • Loading branch information
xhad authored Mar 7, 2024
2 parents 99e2508 + ff616f2 commit e4aa752
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 98 deletions.
189 changes: 96 additions & 93 deletions src/Chamber.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,114 +65,117 @@ contract Chamber is IChamber, Common {
}

/// @inheritdoc IChamber
function create(address[] memory _target, uint256[] memory _value, bytes[] memory _data) external {
function create(address[] memory targets, uint256[] memory values, bytes[] memory datas) external {
if(IERC721(memberToken).balanceOf(_msgSender()) < 1) revert insufficientBalance();
uint256[5] memory topFiveLeader;
for (uint256 i=0; i<5; i++){
topFiveLeader[i] = leaderboard[i];
}
nonce++;
proposals[nonce] = Proposal({
target: _target,
value: _value,
data: _data,
target: targets,
value: values,
data: datas,
voters: topFiveLeader,
approvals: 0,
nonce: nonce,
state: State.Initialized
});
emit ProposalCreated(nonce, _target, _value, _data, topFiveLeader, nonce);
emit ProposalCreated(nonce, targets, values, datas, topFiveLeader, nonce);
}

/// @inheritdoc IChamber
function approve(uint256 _proposalId, uint256 _tokenId, bytes memory _signature) external {
if(_msgSender() != IERC721(memberToken).ownerOf(_tokenId)) revert invalidApproval("Sender isn't NFT owner");
if(proposals[_proposalId].state != State.Initialized) revert invalidApproval("Proposal isn't Initialized");
if(voted[_proposalId][_tokenId]) revert invalidApproval("TokenID already voted");
function approve(uint256 proposalId, uint256 tokenId, bytes memory signature) external {
if(_msgSender() != IERC721(memberToken).ownerOf(tokenId)) revert invalidApproval("Sender isn't NFT owner");
if(proposals[proposalId].state != State.Initialized) revert invalidApproval("Proposal isn't Initialized");
if(voted[proposalId][tokenId]) revert invalidApproval("TokenID already voted");

require(verifySignature(_proposalId, _tokenId, _signature), "Invalid signature");
require(verifySignature(proposalId, tokenId, signature), "Invalid signature");

uint256[5] memory voters = proposals[_proposalId].voters;
uint256[5] memory voters = proposals[proposalId].voters;
bool onVoterList = false;

for (uint i = 0; i < voters.length; i++) {
if (_tokenId == voters[i]) onVoterList = true;
if (tokenId == voters[i]) onVoterList = true;
}

if (!onVoterList) revert invalidApproval("TokenId not on voter list");

voted[_proposalId][_tokenId] = true;
proposals[_proposalId].approvals += 1;
emit ProposalApproved(_proposalId, _tokenId, proposals[_proposalId].approvals);
voted[proposalId][tokenId] = true;
proposals[proposalId].approvals += 1;
emit ProposalApproved(proposalId, tokenId, proposals[proposalId].approvals);
}

/// @inheritdoc IChamber
function promote(uint256 _amt, uint256 _tokenId) public nonReentrant {
if(_amt == 0 && _tokenId == 0) revert invalidPromotion();
function promote(uint256 amount, uint256 tokenId) public nonReentrant {
if(amount == 0 && tokenId == 0) revert invalidPromotion();

totalDelegation[_tokenId] += _amt;
accountDelegation[_msgSender()][_tokenId] += _amt;
_updateLeaderboard(_tokenId);
totalDelegation[tokenId] += amount;
accountDelegation[_msgSender()][tokenId] += amount;
_updateLeaderboard(tokenId);

SafeERC20.safeTransferFrom(IERC20(govToken), _msgSender(), address(this), _amt);
emit Promoted(_msgSender(), _amt, _tokenId);
SafeERC20.safeIncreaseAllowance(IERC20(govToken), address(this), amount);
SafeERC20.safeTransferFrom(IERC20(govToken), _msgSender(), address(this), amount);
emit Promoted(_msgSender(), amount, tokenId);
}

/// @inheritdoc IChamber
function demote(uint256 _amt, uint256 _tokenId) public nonReentrant {
if(_amt == 0 && _tokenId == 0) revert invalidDemotion();
if(accountDelegation[_msgSender()][_tokenId] < _amt) revert invalidDemotion();
function demote(uint256 amount, uint256 tokenId) public nonReentrant {
if(amount == 0 && tokenId == 0) revert invalidDemotion();
if(accountDelegation[_msgSender()][tokenId] < amount) revert invalidDemotion();

totalDelegation[_tokenId] -= _amt;
accountDelegation[_msgSender()][_tokenId] -= _amt;
if (totalDelegation[_tokenId]== 0){
_removeFromLeaderboard(_tokenId);
}else{
_updateLeaderboard(_tokenId);
totalDelegation[tokenId] -= amount;
accountDelegation[_msgSender()][tokenId] -= amount;
if (totalDelegation[tokenId]== 0){
_removeFromLeaderboard(tokenId);
} else {
_updateLeaderboard(tokenId);
}

SafeERC20.safeTransfer(IERC20(govToken), _msgSender(), _amt);
emit Demoted(_msgSender(), _amt, _tokenId);
SafeERC20.safeTransfer(IERC20(govToken), _msgSender(), amount);
SafeERC20.safeDecreaseAllowance(IERC20(govToken), address(this), amount);

emit Demoted(_msgSender(), amount, tokenId);
}

/// @inheritdoc IChamber
function execute(uint256 _proposalId, uint256 _tokenId, bytes memory _signature) public noReentrancy{
function execute(uint256 proposalId, uint256 tokenId, bytes memory signature) public noReentrancy{

// TODO Implement Gas handling and Optimizations

if( _proposalId > 1 && !(_isCancellationProposal(_proposalId)) ){
require((proposals[_proposalId-1].state == State.Executed || proposals[_proposalId-1].state == State.Canceled), "Previous proposal must be resolved.");
if( proposalId > 1 && !(_isCancellationProposal(proposalId)) ){
require((proposals[proposalId-1].state == State.Executed || proposals[proposalId-1].state == State.Canceled), "Previous proposal must be resolved.");
}

require(proposals[_proposalId].approvals >= 3, "Not enough approvals"); // TODO: Make quorum dynamic
require(proposals[proposalId].approvals >= 3, "Not enough approvals"); // TODO: Make quorum dynamic

require(verifySignature(_proposalId, _tokenId, _signature), "Invalid signature");
require(verifySignature(proposalId, tokenId, signature), "Invalid signature");

bool validVoter = false;
for (uint256 i = 0 ; i < 5; i++){
if (_tokenId == proposals[_proposalId].voters[i]){
if (tokenId == proposals[proposalId].voters[i]){
validVoter = true;
}
}
require(validVoter, "Not a voter");

if(proposals[_proposalId].state != State.Initialized) revert invalidProposalState();
if(proposals[proposalId].state != State.Initialized) revert invalidProposalState();

Proposal memory proposalData = proposals[_proposalId];
proposals[_proposalId].state = State.Executed;
Proposal memory proposalData = proposals[proposalId];
proposals[proposalId].state = State.Executed;

address guard = getGuard();
if (guard != address (0)){
IGuard(guard).checkTransaction(
proposals[_proposalId].target,
proposals[_proposalId].value,
proposals[_proposalId].data,
proposals[_proposalId].voters,
proposals[_proposalId].state,
_signature,
proposals[proposalId].target,
proposals[proposalId].value,
proposals[proposalId].data,
proposals[proposalId].voters,
proposals[proposalId].state,
signature,
msg.sender,
_proposalId,
_tokenId
proposalId,
tokenId
);
}
bool finalSuccess = false;
Expand All @@ -183,10 +186,10 @@ contract Chamber is IChamber, Common {
}
{
if (guard != address(0)) {
IGuard(guard).checkAfterExecution(constructMessageHash(_proposalId, _tokenId), finalSuccess);
IGuard(guard).checkAfterExecution(constructMessageHash(proposalId, tokenId), finalSuccess);
}
}
emit ProposalExecuted(_proposalId);
emit ProposalExecuted(proposalId);
}

/// @notice Checks if the proposal corresponds to a cancellation request.
Expand All @@ -203,15 +206,15 @@ contract Chamber is IChamber, Common {
}

//// @inheritdoc IChamber
function cancel(uint256 _proposalId) external authorized {
require(proposals[_proposalId].state == State.Initialized, "Proposal is not initialized");
proposals[_proposalId].target = new address[](1);
proposals[_proposalId].value = new uint256[](1);
proposals[_proposalId].data = new bytes[](1);
function cancel(uint256 proposalId) external authorized {
require(proposals[proposalId].state == State.Initialized, "Proposal is not initialized");
proposals[proposalId].target = new address[](1);
proposals[proposalId].value = new uint256[](1);
proposals[proposalId].data = new bytes[](1);

proposals[_proposalId].state = State.Canceled;
proposals[proposalId].state = State.Canceled;

emit ProposalCanceled(_proposalId);
emit ProposalCanceled(proposalId);
}


Expand Down Expand Up @@ -268,13 +271,13 @@ contract Chamber is IChamber, Common {

/// @inheritdoc IChamber
function verifySignature(
uint256 _proposalId,
uint256 _tokenId,
bytes memory _signature
uint256 proposalId,
uint256 tokenId,
bytes memory signature
) public view returns (bool) {
bytes32 messageHash = constructMessageHash(_proposalId, _tokenId);
address signer = ECDSA.recover(messageHash, _signature);
return signer == IERC721(memberToken).ownerOf(_tokenId);
bytes32 messageHash = constructMessageHash(proposalId, tokenId);
address signer = ECDSA.recover(messageHash, signature);
return signer == IERC721(memberToken).ownerOf(tokenId);
}

/// @inheritdoc IChamber
Expand All @@ -287,48 +290,48 @@ contract Chamber is IChamber, Common {
}

function encodeData(
address[] memory _to,
uint256[] memory _value,
bytes[] memory _data,
uint256[5] memory _voters,
uint256 _approvals,
uint256 _nonce,
State _state,
uint256 _proposalId,
uint256 _tokenId
address[] memory to,
uint256[] memory value,
bytes[] memory data,
uint256[5] memory voters,
uint256 approvals,
uint256 _nonce,
State state,
uint256 proposalId,
uint256 tokenId
)internal view returns(bytes memory){
bytes32 txHash = keccak256(
abi.encode(
_to,
_value,
_data,
_voters,
_approvals,
to,
value,
data,
voters,
approvals,
_nonce,
_state,
_proposalId,
_tokenId
state,
proposalId,
tokenId
)
);
return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), txHash);
}

/// @inheritdoc IChamber
function constructMessageHash(
uint256 _proposalId,
uint256 _tokenId
uint256 proposalId,
uint256 tokenId
) public view returns (bytes32) {
return keccak256(
encodeData(
proposals[_proposalId].target,
proposals[_proposalId].value,
proposals[_proposalId].data,
proposals[_proposalId].voters,
proposals[_proposalId].approvals,
proposals[_proposalId].nonce,
proposals[_proposalId].state,
_proposalId,
_tokenId
proposals[proposalId].target,
proposals[proposalId].value,
proposals[proposalId].data,
proposals[proposalId].voters,
proposals[proposalId].approvals,
proposals[proposalId].nonce,
proposals[proposalId].state,
proposalId,
tokenId
)
);
}
Expand Down
10 changes: 5 additions & 5 deletions src/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,21 @@ contract Registry is IRegistry, Initializable, Ownable {
}

/// @inheritdoc IRegistry
function deploy(address _memberToken, address _govToken) external returns (address) {
function deploy(address erc721, address erc20) external returns (address) {

bytes memory data = abi.encodeWithSelector(IChamber.initialize.selector, _memberToken, _govToken);
bytes memory data = abi.encodeWithSelector(IChamber.initialize.selector, erc721, erc20);
MultiProxy chamber = new MultiProxy(chamberBeacon, data, msg.sender);

ChamberData memory chamberData = ChamberData({
chamber: address(chamber),
memberToken: _memberToken,
govToken: _govToken
memberToken: erc721,
govToken: erc20
});

chambers[totalChambers] = chamberData;
totalChambers++;

emit ChamberDeployed(address(chamber), totalChambers, msg.sender, _memberToken, _govToken);
emit ChamberDeployed(address(chamber), totalChambers, msg.sender, erc721, erc20);
return address(chamber);
}
}
Expand Down
1 change: 1 addition & 0 deletions test/performance/ChamberPerf.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ contract ChamberPerfTest is Test {
deal(address(LORE), bones, amount);
LORE.approve(address(chamber), amount);
chamber.promote(amount, i);
chamber.demote(amount, i);
}
}

Expand Down

0 comments on commit e4aa752

Please sign in to comment.