Decision of the minimum gas price
The chain-wide minimum gas price is a consensus parameter, which is the lower bound of the minimum gas prices set by different validators. This consensus parameter is modified by the validators.
After the XHedge upgrade, validators (or their corresponding rewardTo account) can send transactions to the staking contract (address: 0x2710) to anticipant the decision of minimum gas price.
The staking contract is a system contract implemented natively using golang, instead of solidity. However, we provide a pseudo-code at the end of this document to show how the logic of gas price decisions, hoping it can help your understanding.

The logic of gas price decisions

There are two hard-coded constants: the upper bound and lower bound of the minimum gas price. The decided minimum gas price cannot exceed these bounds.
The minimum gas price is decided in a voting process:
  1. 1.
    A validator starts a voting process by calling proposal. This function's argument is the target minimum gas price this validator votes for.
  2. 2.
    The other validators call vote to specify the target minimum gas price of each one.
  3. 3.
    The voting process will last for 24 hours. When it ends, anyone can call executeProposal to make the decision effective.
The decided minimum gas price is the middle of the arithmetic mean value and the geometric mean value of all the voted targets.

Psuedo code

1
contract MinGasPriceVote {
2
uint constant MinGasPriceDeltaRate = 5;
3
uint constant MinGasPriceUpperBound = 500000000000; //500gwei
4
uint constant MinGasPriceLowerBound = 10000000; //0.01gwei
5
uint constant DefaultProposalDuration = 60 * 60 * 24; //24hour
6
​
7
uint minGasPrice public;
8
uint endVotingTime public;
9
address[] voters public;
10
mapping(address => uint) voteMap public;
11
​
12
function proposal(uint target) external {
13
require(isValidator(msg.sender), "not-a-validator");
14
uint64 votingPower = getVotingPower(msg.sender);
15
require(votingPower != 0, "inactive-validator");
16
require(endVotingTime == 0, "is-still-voting");
17
checkTarget(minGasPrice, target);
18
endVotingTime = block.timestamp + DefaultProposalDuration;
19
voteMap[msg.sender] = (target<<64) + uint(votingPower);
20
addVoter(msg.sender);
21
}
22
​
23
function checkTarget(uint lastMinGasPrice, uint target) private {
24
require(MinGasPriceLowerBound < target, "target-too-small");
25
require(target < MinGasPriceUpperBound, "target-too-large");
26
if(lastMinGasPrice != 0) {
27
require(lastMinGasPrice/MinGasPriceDeltaRate <= target &&
28
target <= lastMinGasPrice*MinGasPriceDeltaRate, "target-outof-range");
29
}
30
}
31
​
32
function addVoter(address voter) private {
33
for(uint i=0; i<voters.length; i++) {
34
if(voters[i] == voter) return;
35
}
36
voters.push(voter);
37
}
38
​
39
function vote(uint target) external {
40
require(isValidator(msg.sender), "not-a-validator");
41
uint64 votingPower = getVotingPower(msg.sender);
42
require(votingPower != 0, "inactive-validator");
43
require(endVotingTime != 0, "not-in-voting");
44
require(block.timestamp < endVotingTime, "voting-finished");
45
if(target == 0) {
46
target = minGasPrice;
47
} else {
48
checkTarget(minGasPrice, target);
49
}
50
voteMap[msg.sender] = (target<<64) + uint(votingPower);
51
addVoter(msg.sender);
52
}
53
​
54
function executeProposal() external {
55
require(endVotingTime != 0, "not-in-voting");
56
require(endVotingTime < block.timestamp, "voting-not-finished");
57
minGasPrice = calculateMinGasPrice();
58
endVotingTime = 0;
59
uint index = voters.length-1;
60
do {
61
address voter = voters[index];
62
delete voteMap[voter];
63
voters.pop();
64
index--;
65
} while(index!=0);
66
}
67
​
68
function calculateMinGasPrice() private returns (uint) {
69
uint sumPower = 0;
70
uint sumTargets = 0;
71
uint[] memory targets = new uint[](voters.length);
72
for(uint i=0; i<voters.length; i++) {
73
address voter = voters[i];
74
uint vote = voteMap[voter];
75
(uint target, uint votingPower) = (uint(vote>>64), uint(uint64(vote)));
76
sumPower += votingPower;
77
sumTargets += target*votingPower;
78
targets[i] = target;
79
}
80
uint t1 = calculateMeidan(targets);
81
uint t2 = sumTargets / sumPower;
82
return (t1+t2)/2;
83
}
84
​
85
function calculateMeidan(uint[] memory targets) private pure returns (uint) {
86
uint index = targets.length/2; 4/2=2
87
if(index*2 == targets.length) {
88
returns (targets[index-1] + targets[index]) / 2
89
}
90
return targets[index];
91
}
92
​
93
function sort(uint[] memory arr) private pure {
94
if (arr.length > 0)
95
quickSort(arr, 0, arr.length - 1);
96
}
97
98
function quickSort(uint[] memory arr, uint left, uint right) private pure {
99
if (left >= right)
100
return;
101
uint p = arr[(left + right) / 2]; // p = the pivot element
102
uint i = left;
103
uint j = right;
104
while (i < j) {
105
while (arr[i] < p) ++i;
106
while (arr[j] > p) --j; // arr[j] > p means p still to the left, so j > 0
107
if (arr[i] > arr[j])
108
(arr[i], arr[j]) = (arr[j], arr[i]);
109
else
110
++i;
111
}
112
113
// Note --j was only done when a[j] > p. So we know: a[j] == p, a[<j] <= p, a[>j] > p
114
if (j > left)
115
quickSort(arr, left, j - 1); // j > left, so j > 0
116
quickSort(arr, j + 1, right);
117
}
118
}
Copied!