当前位置:网站首页>Voting implementation of sushitoken
Voting implementation of sushitoken
2022-07-21 21:02:00 【biakia0610】
SushiToken Inherit the standard ERC20, On this basis , It also realizes the voting function of tokens .
1、 data structure
/// @notice A record of each accounts delegate
mapping (address => address) internal _delegates;
/// @notice A checkpoint for marking number of votes from a given block
struct Checkpoint {
uint32 fromBlock;
uint256 votes;
}
/// @notice A record of votes checkpoints for each account, by index
mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
/// @notice The number of checkpoints for each account
mapping (address => uint32) public numCheckpoints;
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
/// @notice The EIP-712 typehash for the delegation struct used by the contract
bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
/// @notice A record of states for signing / validating signatures
mapping (address => uint) public nonces;
_delegates To represent an agent ,A->B Mapping , Express B Can replace A Exercise voting .
Checkpoint Is the data structure for recording votes , Similar to a snapshot , It records the number of votes held by users in a certain block .
numCheckpoints What is recorded is snapshot ID, Every time there is a new snapshot , This ID Will be on the .
checkpoints It is a snapshot record of all votes of users .
DOMAIN_TYPEHASH、DELEGATION_TYPEHASH and nonces And all EIP-712 of , This EIP The purpose is to improve the availability of message signatures on the chain , For details, see https://eips.ethereum.org/EIPS/eip-712
2、 Transfer vote
function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
// decrease old representative
uint32 srcRepNum = numCheckpoints[srcRep];
uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint256 srcRepNew = srcRepOld.sub(amount);
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
// increase new representative
uint32 dstRepNum = numCheckpoints[dstRep];
uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint256 dstRepNew = dstRepOld.add(amount);
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _writeCheckpoint(
address delegatee,
uint32 nCheckpoints,
uint256 oldVotes,
uint256 newVotes
)
internal
{
uint32 blockNumber = safe32(block.number, "SUSHI::_writeCheckpoint: block number exceeds 32 bits");
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
_moveDelegates Is to transfer votes among users ,srcRep Yes, the transfer out address will reduce the current number of votes ,dstRep Yes, the transfer in address will increase the current number of votes , Will eventually call _writeCheckpoint.
_writeCheckpoint Is a snapshot of recorded votes , Each time you enter, you will get the current block height , Then check whether the latest record is highly consistent with the current block , If they are unanimous, the number of votes will be directly covered , Otherwise, the snapshot ID Add one , Regenerate a snapshot object , Then write it down .
3、 Voting proxy
/**
* @notice Delegate votes from `msg.sender` to `delegatee`
* @param delegator The address to get delegatee for
*/
function delegates(address delegator)
external
view
returns (address)
{
return _delegates[delegator];
}
/**
* @notice Delegate votes from `msg.sender` to `delegatee`
* @param delegatee The address to delegate votes to
*/
function delegate(address delegatee) external {
return _delegate(msg.sender, delegatee);
}
/**
* @notice Delegates votes from signatory to `delegatee`
* @param delegatee The address to delegate votes to
* @param nonce The contract state required to match the signature
* @param expiry The time at which to expire the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function delegateBySig(
address delegatee,
uint nonce,
uint expiry,
uint8 v,
bytes32 r,
bytes32 s
)
external
{
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name())),
getChainId(),
address(this)
)
);
bytes32 structHash = keccak256(
abi.encode(
DELEGATION_TYPEHASH,
delegatee,
nonce,
expiry
)
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator,
structHash
)
);
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), "SUSHI::delegateBySig: invalid signature");
require(nonce == nonces[signatory]++, "SUSHI::delegateBySig: invalid nonce");
require(now <= expiry, "SUSHI::delegateBySig: signature expired");
return _delegate(signatory, delegatee);
}
function _delegate(address delegator, address delegatee)
internal
{
address currentDelegate = _delegates[delegator];
uint256 delegatorBalance = balanceOf(delegator); // balance of underlying SUSHIs (not scaled);
_delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
The user can call delegate Or call by signature delegateBySig, To appoint their own agents , Eventually these methods will call _delegate.
_delegate Get what you currently hold sushi The number of , And then through _moveDelegates To the agent delegatee Increase voting rights , At the same time, reduce the current agent currentDelegate The right to vote .
delegates The method is to check the user's agent .
4、 View votes
/**
* @notice Gets the current votes balance for `account`
* @param account The address to get votes balance
* @return The number of current votes for `account`
*/
function getCurrentVotes(address account)
external
view
returns (uint256)
{
uint32 nCheckpoints = numCheckpoints[account];
return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
/**
* @notice Determine the prior number of votes for an account as of a block number
* @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
* @param account The address of the account to check
* @param blockNumber The block number to get the vote balance at
* @return The number of votes the account had as of the given block
*/
function getPriorVotes(address account, uint blockNumber)
external
view
returns (uint256)
{
require(blockNumber < block.number, "SUSHI::getPriorVotes: not yet determined");
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
// First check most recent balance
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
// Next check implicit zero balance
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
getCurrentVotes Is to get the latest vote , Through the first numCheckpoints[account] Get the recently saved snapshot ID, Then get the number of votes for this snapshot .
getPriorVotes It is to get the historical voting number of users in a block , Internal binary search is used to find the closest snapshot .
5、mint function
function mint(address _to, uint256 _amount) public onlyOwner {
_mint(_to, _amount);
_moveDelegates(address(0), _delegates[_to], _amount);
}
SushiToken Rewrote mint function , Voting rights were allocated one to one .
6、 The problem is
a) SushiToken It's just mint Voting rights are allocated in the function , But no rewriting transfer/transferFrom, In this way, voting rights cannot be transferred , And there is no rewriting burn, Unable to destroy voting rights , A user assigned for the first time SushiToken after , Whether he sold these coins or destroyed them , There are still corresponding voting rights , This is not reasonable .
b) User pass delegate Method to the agent A Distribution of voting rights , Then after a period of time, the user holds sushi Tokens have increased , Call again at this time delegate Transfer to B, Now A The voting rights of are less than the voting rights to be transferred , Will cause the transfer to fail .
边栏推荐
- 【漏洞复现】redis未授权访问windows+linux利用方法
- 【漏洞复现】CVE-2022-22954 VMware Workspace ONE Access漏洞分析
- VOF phase transition equation in openfoam
- Solana Account 详解
- Picture horizontal waterfall flow
- Vulnhub-dc-4 target penetration record
- JWT realizes TP5 user login function
- 【内网渗透】域内信息收集(手工+AdFind工具)
- Typewriter typing, backspace effect
- PHP three lines of code to write test interface
猜你喜欢
web安全--文件包含(本地包含,远程包含)
Openfoam tips
火山引擎&搜款网:服装批发背后的智慧与“荐”行
Moher college webshell file upload analysis (questions 3-5)
【文件上传绕过】--二次渲染
Metaforce force Meta - Cosmos, pour vous apprendre à comprendre rapidement le mécanisme de glissement
Solve the problem of vendor JS file is too large (official processing scheme)
wap绿色传奇搭建(纯净版)
[reverse analysis] basic introduction - search program main function modification program
ECSHOP vulnerability recurrence
随机推荐
Pikachu character injection for Day1 POC and exp learning
Solve the problem of vendor JS file is too large (official processing scheme)
PHP basic syntax
【内网渗透】openssl反弹流量加密shell
Thinkphp6 learning experience
PHP extracts the phone number in the string and verifies whether it is 11 digits
Nodejs uses the post method to receive JS objects and write JSON files
Construction de l'environnement PHP (panneau de pagode recommandé)
BUUCTF(misc)
Introduction to PHP
Wide byte injection learning record
TP5 docking visa free FM payment interface
PHP基础语法
How is redis different from memcached
Express+ejs+swagger-ui-dist create timely and updated rest API online interface documents
[intranet penetration] intranet penetration of vulnstack II
One of the previous company's employment confirmation topics: merging cells
web安全--文件包含(本地包含,远程包含)
【权限提升】提权exp查找思路与利用方法
php百度人脸检测api测颜值评分(源码直接可用)