Transaction Hash:
Block:
17626837 at Jul-05-2023 09:38:35 AM +UTC
Transaction Fee:
0.003858517766605116 ETH
$8.76
Gas Used:
60,141 Gas / 64.157858476 Gwei
Emitted Events:
20 |
ChainportCongress.VoteCast( voter=[Sender] 0x894107b7b5051409f279e8300774b2f62febe057, proposalId=42, support=True )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x894107B7...62Febe057 | (ChainPort: Deployer 2) |
2.679820634244157193 Eth
Nonce: 313
|
2.675962116477552077 Eth
Nonce: 314
| 0.003858517766605116 | |
0xB6b4C7aC...6a83884a1 | |||||
0xDAFEA492...692c98Bc5
Miner
| (Flashbots: Builder) | 0.049521408928060378 Eth | 0.050321284228060378 Eth | 0.0007998753 |
Execution Trace
castVote[ChainportCongress (ln:170)]
_castVote[ChainportCongress (ln:177)]
File 1 of 2: ChainportCongress
File 2 of 2: ChainportCongressMembersRegistry
/** *Submitted for verification at BscScan.com on 2021-05-25 */ // Sources flattened with hardhat v2.3.0 https://hardhat.org // File contracts/interfaces/ICongressMembersRegistry.sol pragma solidity ^0.6.12; /** * IHordCongressMembersRegistry contract. * @author Nikola Madjarevic * Date created: 21.3.21. * Github: madjarevicn */ interface ICongressMembersRegistry { function isMember(address _address) external view returns (bool); function getMinimalQuorum() external view returns (uint256); } // File contracts/governance/ChainportCongress.sol pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; /** * ChainportCongress contract. * @author Nikola Madjarevic * Date created: 18.3.21. * Github: madjarevicn */ contract ChainportCongress { /// @notice The name of this contract string public constant name = "ChainportCongress"; // Members registry contract ICongressMembersRegistry membersRegistry; /// @notice The total number of proposals uint public proposalCount; struct Proposal { // Unique id for looking up a proposal uint id; // Creator of the proposal address proposer; // the ordered list of target addresses for calls to be made address[] targets; // The ordered list of values (i.e. msg.value) to be passed to the calls to be made uint[] values; // The ordered list of function signatures to be called string[] signatures; // The ordered list of calldata to be passed to each call bytes[] calldatas; // Current number of votes in favor of this proposal uint forVotes; // Current number of votes in opposition to this proposal uint againstVotes; // Flag marking whether the proposal has been canceled bool canceled; // Flag marking whether the proposal has been executed bool executed; // Timestamp when proposal is created uint timestamp; // Receipts of ballots for the entire set of voters mapping (address => Receipt) receipts; } /// @notice Ballot receipt record for a voter struct Receipt { // Whether or not a vote has been cast bool hasVoted; // Whether or not the voter supports the proposal bool support; } /// @notice The official record of all proposals ever proposed mapping (uint => Proposal) public proposals; /// @notice An event emitted when a new proposal is created event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, string description); /// @notice An event emitted when a vote has been cast on a proposal event VoteCast(address voter, uint proposalId, bool support); /// @notice An event emitted when a proposal has been canceled event ProposalCanceled(uint id); /// @notice An event emitted when a proposal has been executed event ProposalExecuted(uint id); /// @notice An event emitted everytime ether is received event ReceivedEther(address sender, uint amount); /// @notice Event which will fire every time transaction is executed event ExecuteTransaction(address indexed target, uint value, string signature, bytes data); modifier onlyMember { require(membersRegistry.isMember(msg.sender) == true, "Only ChainportCongress member can call this function"); _; } /// One time call function to set members registry contract function setMembersRegistry( address _membersRegistry ) external { require(address(membersRegistry) == address(0x0), "ChainportCongress:setMembersRegistry: membersRegistry is already set"); membersRegistry = ICongressMembersRegistry(_membersRegistry); } function propose( address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description ) external onlyMember returns (uint) { require( targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "ChainportCongress::propose: proposal function information arity mismatch" ); require(targets.length != 0, "ChainportCongress::propose: must provide actions"); proposalCount++; Proposal memory newProposal = Proposal({ id: proposalCount, proposer: msg.sender, targets: targets, values: values, signatures: signatures, calldatas: calldatas, forVotes: 0, againstVotes: 0, canceled: false, executed: false, timestamp: block.timestamp }); proposals[newProposal.id] = newProposal; emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, description); return newProposal.id; } function castVote( uint proposalId, bool support ) external onlyMember { return _castVote(msg.sender, proposalId, support); } function execute( uint proposalId ) external onlyMember payable { // load the proposal Proposal storage proposal = proposals[proposalId]; // Require that proposal is not previously executed neither cancelled require(proposal.executed == false && proposal.canceled == false); // Mark that proposal is executed proposal.executed = true; // Require that votes in favor of proposal are greater or equal to minimalQuorum require(proposal.forVotes >= membersRegistry.getMinimalQuorum()); for (uint i = 0; i < proposal.targets.length; i++) { bytes memory callData; if (bytes(proposal.signatures[i]).length == 0) { callData = proposal.calldatas[i]; } else { callData = abi.encodePacked(bytes4(keccak256(bytes(proposal.signatures[i]))), proposal.calldatas[i]); } // solium-disable-next-line security/no-call-value (bool success,) = proposal.targets[i].call{value:proposal.values[i]}(callData); // Require that transaction went through require(success, "ChainportCongress::executeTransaction: Transaction execution reverted."); // Emit event that transaction is being executed emit ExecuteTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i]); } // Emit event that proposal executed emit ProposalExecuted(proposalId); } function cancel(uint proposalId) external onlyMember { Proposal storage proposal = proposals[proposalId]; // Require that proposal is not previously executed neither cancelled require(proposal.executed == false && proposal.canceled == false, "ChainportCongress:cancel: Proposal already executed or canceled"); // 3 days after proposal can get cancelled require(block.timestamp >= proposal.timestamp + 259200, "ChainportCongress:cancel: Time lock hasn't ended yet"); // Proposal with reached minimalQuorum cant be cancelled require(proposal.forVotes < membersRegistry.getMinimalQuorum(), "ChainportCongress:cancel: Proposal already reached quorum"); // Set that proposal is cancelled proposal.canceled = true; // Emit event emit ProposalCanceled(proposalId); } function _castVote(address voter, uint proposalId, bool support) internal { Proposal storage proposal = proposals[proposalId]; Receipt storage receipt = proposal.receipts[voter]; require(receipt.hasVoted == false, "ChainportCongress::_castVote: voter already voted"); if (support) { proposal.forVotes = add256(proposal.forVotes, 1); } else { proposal.againstVotes = add256(proposal.againstVotes, 1); } receipt.hasVoted = true; receipt.support = support; emit VoteCast(voter, proposalId, support); } function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { Proposal storage p = proposals[proposalId]; return (p.targets, p.values, p.signatures, p.calldatas); } function getMembersRegistry() external view returns (address) { return address(membersRegistry); } function add256(uint256 a, uint256 b) internal pure returns (uint) { uint c = a + b; require(c >= a, "addition overflow"); return c; } receive() external payable { emit ReceivedEther(msg.sender, msg.value); } }
File 2 of 2: ChainportCongressMembersRegistry
/** *Submitted for verification at BscScan.com on 2021-05-25 */ // Sources flattened with hardhat v2.3.0 https://hardhat.org // File contracts/libraries/SafeMath.sol // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } // File contracts/governance/ChainportCongressMembersRegistry.sol pragma solidity ^0.6.12; /** * ChainportCongressMembersRegistry contract. * @author Nikola Madjarevic * Date created: 21.3.21. * Github: madjarevicn */ contract ChainportCongressMembersRegistry { using SafeMath for *; /// @notice The name of this contract string public constant name = "ChainportCongressMembersRegistry"; /// @notice Event to fire every time someone is added or removed from members event MembershipChanged(address member, bool isMember); /// @notice Chainport congress pointer address public chainportCongress; //The minimum number of voting members that must be in attendance uint256 minimalQuorum; // Mapping to check if the member is belonging to congress mapping (address => bool) isMemberInCongress; // Mapping address to member info mapping(address => Member) public address2Member; // Mapping to store all members addresses address[] public allMembers; struct Member { bytes32 name; uint memberSince; } modifier onlyChainportCongress { require(msg.sender == chainportCongress); _; } /** * @param initialCongressMembers is the array containing addresses of initial members */ constructor( address[] memory initialCongressMembers, bytes32[] memory initialCongressMemberNames, address _chainportCongress ) public { uint length = initialCongressMembers.length; for(uint i=0; i<length; i++) { addMemberInternal( initialCongressMembers[i], initialCongressMemberNames[i] ); } chainportCongress = _chainportCongress; } function changeMinimumQuorum( uint newMinimumQuorum ) external onlyChainportCongress { require(newMinimumQuorum > 0); minimalQuorum = newMinimumQuorum; } /** * Add member * * Make `targetMember` a member named `memberName` * * @param targetMember ethereum address to be added * @param memberName public name for that member */ function addMember( address targetMember, bytes32 memberName ) external onlyChainportCongress { addMemberInternal(targetMember, memberName); } function addMemberInternal( address targetMember, bytes32 memberName ) internal { //Require that this member is not already a member of congress require(isMemberInCongress[targetMember] == false); // Update basic member information address2Member[targetMember] = Member({ memberSince: block.timestamp, name: memberName }); // Add member to list of all members allMembers.push(targetMember); // Update minimum quorum minimalQuorum = allMembers.length.sub(1); // Mark that user is member in congress isMemberInCongress[targetMember] = true; // Fire an event emit MembershipChanged(targetMember, true); } /** * Remove member * * @notice Remove membership from `targetMember` * * @param targetMember ethereum address to be removed */ function removeMember( address targetMember ) external onlyChainportCongress { require(isMemberInCongress[targetMember] == true); uint length = allMembers.length; uint i=0; // Find selected member while(allMembers[i] != targetMember) { if(i == length) { revert(); } i++; } // Move the last member to this place allMembers[i] = allMembers[length-1]; // Remove the last member allMembers.pop(); //Remove him from state mapping isMemberInCongress[targetMember] = false; //Remove his state to empty member address2Member[targetMember] = Member({ memberSince: block.timestamp, name: "0x0" }); //Reduce 1 member from quorum minimalQuorum = minimalQuorum.sub(1); // Emit event that member is removed. emit MembershipChanged(targetMember, false); } /** * @notice Function which will be exposed and congress will use it as "modifier" * @param _address is the address we're willing to check if it belongs to congress * @return true/false depending if it is either a member or not */ function isMember( address _address ) external view returns (bool) { return isMemberInCongress[_address]; } /// @notice Getter for length for how many members are currently /// @return length of members function getNumberOfMembers() external view returns (uint) { return allMembers.length; } /// @notice Function to get addresses of all members in congress /// @return array of addresses function getAllMemberAddresses() external view returns (address[] memory) { return allMembers; } /// Get member information function getMemberInfo( address _member ) external view returns (address, bytes32, uint) { Member memory member = address2Member[_member]; return ( _member, member.name, member.memberSince ); } function getMinimalQuorum() external view returns (uint256) { return minimalQuorum; } }