@gooddollar/goodprotocol 1.0.16-beta.0 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/contracts/DAOStackInterfaces.sol/Avatar.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/Controller.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/GlobalConstraintInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/IntVoteInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/ReputationInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/SchemeRegistrar.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/AggregatorV3Interface.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ERC20.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IAaveIncentivesController.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IAdminWallet.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IDonationStaking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IERC2917.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IFirstClaimPool.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IGoodDollar.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IGoodStaking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IHasRouter.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IIdentity.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ILendingPool.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/INameService.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IUBIScheme.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ProxyAdmin.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Reserve.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Staking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Uniswap.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/UniswapFactory.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/UniswapPair.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/cERC20.dbg.json +1 -1
- package/artifacts/contracts/governance/ClaimersDistribution.sol/ClaimersDistribution.dbg.json +1 -1
- package/artifacts/contracts/governance/ClaimersDistribution.sol/ClaimersDistribution.json +2 -2
- package/artifacts/contracts/governance/CompoundVotingMachine.sol/CompoundVotingMachine.dbg.json +1 -1
- package/artifacts/contracts/governance/GReputation.sol/GReputation.dbg.json +1 -1
- package/artifacts/contracts/governance/GReputation.sol/GReputation.json +3 -10
- package/artifacts/contracts/governance/GovernanceStaking.sol/GovernanceStaking.dbg.json +1 -1
- package/artifacts/contracts/governance/GovernanceStaking.sol/GovernanceStaking.json +41 -3
- package/artifacts/contracts/governance/MultiBaseGovernanceShareField.sol/MultiBaseGovernanceShareField.dbg.json +1 -1
- package/artifacts/contracts/governance/Reputation.sol/Reputation.dbg.json +1 -1
- package/artifacts/contracts/governance/StakersDistribution.sol/StakersDistribution.dbg.json +1 -1
- package/artifacts/contracts/governance/StakersDistribution.sol/StakersDistribution.json +2 -2
- package/artifacts/contracts/mocks/AaveMock.sol/AaveMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/DAIMock.sol/DAIMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/DecimalsMock.sol/DecimalsMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/GoodCompoundStakingTest.sol/GoodCompoundStakingTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/GoodCompoundStakingTest.sol/GoodCompoundStakingTest.json +2 -2
- package/artifacts/contracts/mocks/GoodFundManagerTest.sol/GoodFundManagerTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/IncentiveControllerMock.sol/IncentiveControllerMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/LendingPoolMock.sol/LendingPoolMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/OverMintTester.sol/OverMintTester.dbg.json +1 -1
- package/artifacts/contracts/mocks/OverMintTester.sol/OverMintTester.json +2 -2
- package/artifacts/contracts/mocks/OverMintTesterRegularStake.sol/OverMintTesterRegularStake.dbg.json +1 -1
- package/artifacts/contracts/mocks/OverMintTesterRegularStake.sol/OverMintTesterRegularStake.json +2 -2
- package/artifacts/contracts/mocks/SixteenDecimalsTokenMock.sol/SixteenDecimalsTokenMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/SwapHelperTest.sol/SwapHelperTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/TwentyDecimalsTokenMock.sol/TwentyDecimalsTokenMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock2.dbg.json +1 -1
- package/artifacts/contracts/mocks/UsdcMock.sol/USDCMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cBATMock.sol/cBATMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAILowWorthMock.sol/cDAILowWorthMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAIMock.sol/cDAIMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAINonMintableMock.sol/cDAINonMintableMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDecimalsMock.sol/cDecimalsMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cSDTMock.sol/cSDTMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cUSDCMock.sol/cUSDCMock.dbg.json +1 -1
- package/artifacts/contracts/reserve/ExchangeHelper.sol/ExchangeHelper.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodMarketMaker.sol/GoodMarketMaker.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodReserveCDai.sol/ContributionCalc.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodReserveCDai.sol/GoodReserveCDai.dbg.json +1 -1
- package/artifacts/contracts/staking/BaseShareField.sol/BaseShareField.dbg.json +1 -1
- package/artifacts/contracts/staking/BaseShareFieldV2.sol/BaseShareFieldV2.dbg.json +1 -1
- package/artifacts/contracts/staking/DonationsStaking.sol/DonationsStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/DonationsStaking.sol/DonationsStaking.json +2 -2
- package/artifacts/contracts/staking/GoodFundManager.sol/GoodFundManager.dbg.json +1 -1
- package/artifacts/contracts/staking/SimpleStaking.sol/SimpleStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/SimpleStakingV2.sol/SimpleStakingV2.dbg.json +1 -1
- package/artifacts/contracts/staking/UniswapV2SwapHelper.sol/UniswapV2SwapHelper.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/AaveStakingFactory.sol/AaveStakingFactory.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/AaveStakingFactory.sol/AaveStakingFactory.json +2 -2
- package/artifacts/contracts/staking/aave/GoodAaveStaking.sol/GoodAaveStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/GoodAaveStaking.sol/GoodAaveStaking.json +2 -2
- package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.json +2 -2
- package/artifacts/contracts/staking/compound/CompoundStakingFactory.sol/CompoundStakingFactory.dbg.json +1 -1
- package/artifacts/contracts/staking/compound/CompoundStakingFactory.sol/CompoundStakingFactory.json +2 -2
- package/artifacts/contracts/staking/compound/GoodCompoundStaking.sol/GoodCompoundStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/compound/GoodCompoundStaking.sol/GoodCompoundStaking.json +2 -2
- package/artifacts/contracts/staking/compound/GoodCompoundStakingV2.sol/GoodCompoundStakingV2.dbg.json +1 -1
- package/artifacts/contracts/staking/compound/GoodCompoundStakingV2.sol/GoodCompoundStakingV2.json +2 -2
- package/artifacts/contracts/ubi/UBIScheme.sol/UBIScheme.dbg.json +1 -1
- package/artifacts/contracts/ubi/UBIScheme.sol/UBIScheme.json +2 -2
- package/artifacts/contracts/unaudited-foundation/FuseFaucet.sol/FuseFaucet.dbg.json +1 -1
- package/artifacts/contracts/unaudited-foundation/InvitesV1.sol/InvitesV1.dbg.json +1 -1
- package/artifacts/contracts/utils/BancorFormula.sol/BancorFormula.dbg.json +1 -1
- package/artifacts/contracts/utils/BulkProof.sol/BulkProof.dbg.json +4 -0
- package/artifacts/contracts/utils/BulkProof.sol/BulkProof.json +41 -0
- package/artifacts/contracts/utils/DAOContract.sol/DAOContract.dbg.json +1 -1
- package/artifacts/contracts/utils/DAOUpgradeableContract.sol/DAOUpgradeableContract.dbg.json +1 -1
- package/artifacts/contracts/utils/DSMath.sol/DSMath.dbg.json +1 -1
- package/artifacts/contracts/utils/DataTypes.sol/DataTypes.dbg.json +1 -1
- package/artifacts/contracts/utils/NameService.sol/NameService.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgrade.sol/OldMarketMaker.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgrade.sol/ProtocolUpgrade.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgradeFuse.sol/ProtocolUpgradeFuse.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgradeFuseRecover.sol/ProtocolUpgradeFuseRecover.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgradeRecover.sol/ProtocolUpgradeRecover.dbg.json +1 -1
- package/artifacts/contracts/utils/ReputationTestHelper.sol/ReputationTestHelper.dbg.json +1 -1
- package/contracts/governance/GReputation.sol +10 -31
- package/contracts/governance/GovernanceStaking.sol +15 -2
- package/contracts/utils/BulkProof.sol +45 -0
- package/hardhat.config.ts +27 -3
- package/package.json +1 -1
- package/releases/deployment.json +2 -2
- package/scripts/bulkProof.ts +207 -0
- package/scripts/governance/airdropCalculationRecover.ts +406 -0
- package/test/governance/GReputation.test.ts +16 -3
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { chunk } from "lodash";
|
|
3
|
+
import { ethers, upgrades, network } from "hardhat";
|
|
4
|
+
import { networkNames } from "@openzeppelin/upgrades-core";
|
|
5
|
+
import GReputationABI from "../artifacts/contracts/governance/GReputation.sol/GReputation.json";
|
|
6
|
+
import BulkProofABI from "../artifacts/contracts/utils/BulkProof.sol/BulkProof.json";
|
|
7
|
+
|
|
8
|
+
import MerkleTree, {
|
|
9
|
+
checkProof,
|
|
10
|
+
checkProofOrdered
|
|
11
|
+
} from "merkle-tree-solidity";
|
|
12
|
+
import { BigNumber } from "ethers";
|
|
13
|
+
|
|
14
|
+
console.log({
|
|
15
|
+
networkNames,
|
|
16
|
+
network: network.name,
|
|
17
|
+
upgrade: process.env.UPGRADE
|
|
18
|
+
});
|
|
19
|
+
const { name: networkName } = network;
|
|
20
|
+
networkNames[1] = networkName;
|
|
21
|
+
networkNames[122] = networkName;
|
|
22
|
+
networkNames[3] = networkName;
|
|
23
|
+
|
|
24
|
+
export const bulkProof = async () => {
|
|
25
|
+
console.log("signer", await ethers.getSigners());
|
|
26
|
+
const fuseProvider = new ethers.providers.JsonRpcProvider(
|
|
27
|
+
"https://rpc.fuse.io"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const fuseGDProvider = new ethers.providers.JsonRpcProvider(
|
|
31
|
+
"https://gooddollar-rpc.fuse.io"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const goodFuse = new ethers.Contract(
|
|
35
|
+
"0x3A9299BE789ac3730e4E4c49d6d2Ad1b8BC34DFf",
|
|
36
|
+
GReputationABI.abi,
|
|
37
|
+
fuseGDProvider
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const START_BLOCK_FUSE = 14149729;
|
|
41
|
+
|
|
42
|
+
const LAST_BLOCK = await goodFuse.provider.getBlockNumber();
|
|
43
|
+
|
|
44
|
+
const claimedLogs = await goodFuse.queryFilter(
|
|
45
|
+
goodFuse.filters.StateHashProof(),
|
|
46
|
+
START_BLOCK_FUSE,
|
|
47
|
+
LAST_BLOCK
|
|
48
|
+
);
|
|
49
|
+
console.log("found logs:", claimedLogs.length);
|
|
50
|
+
console.log(claimedLogs.filter(_ => !_.args?.user));
|
|
51
|
+
|
|
52
|
+
const addrs = claimedLogs.map(_ => _.args?.user);
|
|
53
|
+
|
|
54
|
+
const { treeData, merkleRoot } = JSON.parse(
|
|
55
|
+
fs.readFileSync("airdrop/airdrop.json").toString()
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
let entries = Object.entries(treeData);
|
|
59
|
+
|
|
60
|
+
let elements = entries.map((e: any) =>
|
|
61
|
+
Buffer.from(e[1].hash.slice(2), "hex")
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
console.log("creating merkletree...", elements.length);
|
|
65
|
+
const merkleTree = new MerkleTree(elements, true);
|
|
66
|
+
|
|
67
|
+
const calcMerkleRoot = merkleTree.getRoot().toString("hex");
|
|
68
|
+
console.log("merkleroots:", {
|
|
69
|
+
fromFile: merkleRoot,
|
|
70
|
+
calculated: calcMerkleRoot
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const proofs = addrs => {
|
|
74
|
+
return addrs.map(addr => {
|
|
75
|
+
if (!addr) {
|
|
76
|
+
console.log("missing", addr);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const addrData = treeData[addr] || treeData[addr.toLowerCase()];
|
|
80
|
+
const proofFor = Buffer.from(addrData.hash.slice(2), "hex");
|
|
81
|
+
|
|
82
|
+
const proof = merkleTree.getProof(proofFor);
|
|
83
|
+
const proofIndex =
|
|
84
|
+
entries.findIndex((_: any) => _[1].hash === addrData.hash) + 1;
|
|
85
|
+
|
|
86
|
+
// console.log(
|
|
87
|
+
// "checkProof:",
|
|
88
|
+
// checkProofOrdered(proof, merkleTree.getRoot(), proofFor, proofIndex)
|
|
89
|
+
// );
|
|
90
|
+
|
|
91
|
+
const hexProof = proof.map(_ => "0x" + _.toString("hex"));
|
|
92
|
+
// console.log({
|
|
93
|
+
// proofIndex,
|
|
94
|
+
// rep: BigNumber.from(addrData.rep).toString(),
|
|
95
|
+
// // hexProof,
|
|
96
|
+
// addr
|
|
97
|
+
// });
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
index: proofIndex,
|
|
101
|
+
balance: BigNumber.from(addrData.rep).toString(),
|
|
102
|
+
proof: hexProof,
|
|
103
|
+
account: addr
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const bulkProofContract = await ethers.getContractAt(
|
|
109
|
+
"BulkProof",
|
|
110
|
+
"0x8449bb7BDa431F76c21bcCDEce2794D8aD24D8a8"
|
|
111
|
+
);
|
|
112
|
+
for (let addrChunk of chunk(addrs, 50)) {
|
|
113
|
+
console.log("calling bulk proof");
|
|
114
|
+
const proofsChunk = proofs(addrChunk);
|
|
115
|
+
await (
|
|
116
|
+
await bulkProofContract.bulkProof(proofsChunk, { gasLimit: 10000000 })
|
|
117
|
+
).wait();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const bulkProofGDX = async () => {
|
|
122
|
+
const signers = await ethers.getSigners();
|
|
123
|
+
console.log("signer", await ethers.getSigners());
|
|
124
|
+
// const signer = signers[0].connect(new ethers.providers.InfuraProvider());
|
|
125
|
+
const reserve = await ethers.getContractAt(
|
|
126
|
+
"GoodReserveCDai",
|
|
127
|
+
"0xa150a825d425B36329D8294eeF8bD0fE68f8F6E0"
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const { treeData, merkleRoot } = JSON.parse(
|
|
131
|
+
fs.readFileSync("airdrop/gdxAirdropRecovery.json").toString()
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
let entries = Object.entries(treeData);
|
|
135
|
+
|
|
136
|
+
let elements = entries.map((e: any) =>
|
|
137
|
+
Buffer.from(e[1].hash.slice(2), "hex")
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
let addrs = Object.keys(treeData);
|
|
141
|
+
|
|
142
|
+
console.log("creating merkletree...", elements.length, addrs);
|
|
143
|
+
const merkleTree = new MerkleTree(elements, false);
|
|
144
|
+
|
|
145
|
+
const calcMerkleRoot = merkleTree.getRoot().toString("hex");
|
|
146
|
+
console.log("merkleroots:", {
|
|
147
|
+
fromFile: merkleRoot,
|
|
148
|
+
calculated: calcMerkleRoot
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const proofs = addrs => {
|
|
152
|
+
return addrs.map(addr => {
|
|
153
|
+
if (!addr) {
|
|
154
|
+
console.log("missing", addr);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const addrData = treeData[addr] || treeData[addr.toLowerCase()];
|
|
158
|
+
const proofFor = Buffer.from(addrData.hash.slice(2), "hex");
|
|
159
|
+
|
|
160
|
+
const proof = merkleTree.getProof(proofFor);
|
|
161
|
+
|
|
162
|
+
// console.log(
|
|
163
|
+
// "checkProof:",
|
|
164
|
+
// checkProofOrdered(proof, merkleTree.getRoot(), proofFor, proofIndex)
|
|
165
|
+
// );
|
|
166
|
+
|
|
167
|
+
const hexProof = proof.map(_ => "0x" + _.toString("hex"));
|
|
168
|
+
// console.log({
|
|
169
|
+
// proofIndex,
|
|
170
|
+
// rep: BigNumber.from(addrData.rep).toString(),
|
|
171
|
+
// // hexProof,
|
|
172
|
+
// addr
|
|
173
|
+
// });
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
balance: BigNumber.from(addrData.gdx).toString(),
|
|
177
|
+
proof: hexProof,
|
|
178
|
+
account: addr
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const bulkProofContract = await ethers.getContractAt(
|
|
184
|
+
"BulkProof",
|
|
185
|
+
"0xDB8563287ecF1484228951804eC6bf241f884099"
|
|
186
|
+
);
|
|
187
|
+
for (let addrChunk of chunk(addrs, 60)) {
|
|
188
|
+
const proofsChunk = proofs(addrChunk);
|
|
189
|
+
console.log("calling bulk proof");
|
|
190
|
+
const tx = await bulkProofContract.callStatic.bulkProof(proofsChunk, {
|
|
191
|
+
gasLimit: 5000000,
|
|
192
|
+
maxPriorityFeePerGas: 1000000000,
|
|
193
|
+
maxFeePerGas: 40000000000
|
|
194
|
+
});
|
|
195
|
+
console.log(tx);
|
|
196
|
+
await (
|
|
197
|
+
await bulkProofContract.bulkProof(proofsChunk, {
|
|
198
|
+
gasLimit: 5000000,
|
|
199
|
+
maxPriorityFeePerGas: 1000000000,
|
|
200
|
+
maxFeePerGas: 40000000000
|
|
201
|
+
})
|
|
202
|
+
).wait();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// bulkProof().catch(console.log);
|
|
207
|
+
bulkProofGDX().catch(console.log);
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { get, range, chunk, flatten, mergeWith, sortBy, uniq } from "lodash";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import MerkleTree, {
|
|
4
|
+
checkProof,
|
|
5
|
+
checkProofOrdered
|
|
6
|
+
} from "merkle-tree-solidity";
|
|
7
|
+
import coreContracts from "@gooddollar/goodcontracts/releases/deployment.json";
|
|
8
|
+
import stakingContracts from "@gooddollar/goodcontracts/stakingModel/releases/deployment.json";
|
|
9
|
+
import upgradablesContracts from "@gooddollar/goodcontracts/upgradables/releases/deployment.json";
|
|
10
|
+
import { ethers as Ethers } from "hardhat";
|
|
11
|
+
import { BigNumber } from "ethers";
|
|
12
|
+
type Balances = {
|
|
13
|
+
[key: string]: {
|
|
14
|
+
isNotContract: boolean;
|
|
15
|
+
balance: number;
|
|
16
|
+
claims: number;
|
|
17
|
+
stake: number;
|
|
18
|
+
gdRepShare: number;
|
|
19
|
+
claimRepShare: number;
|
|
20
|
+
stakeRepShare: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type Tree = {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
hash: string;
|
|
27
|
+
rep: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const DefaultBalance = {
|
|
31
|
+
balance: 0,
|
|
32
|
+
claims: 0,
|
|
33
|
+
gdRepShare: 0,
|
|
34
|
+
claimRepShare: 0,
|
|
35
|
+
stake: 0,
|
|
36
|
+
stakeRepShare: 0,
|
|
37
|
+
isNotContract: true
|
|
38
|
+
};
|
|
39
|
+
const otherContracts = [
|
|
40
|
+
"0x8d441C2Ff54C015A1BE22ad88e5D42EFBEC6C7EF", //fuseswap
|
|
41
|
+
"0x0bf36731724f0baceb0748a9e71cd4883b69c533", //fuseswap usdc
|
|
42
|
+
"0x17b09b22823f00bb9b8ee2d4632e332cadc29458", //old bridge
|
|
43
|
+
"0xd5d11ee582c8931f336fbcd135e98cee4db8ccb0", //new bridge
|
|
44
|
+
"0xa56A281cD8BA5C083Af121193B2AaCCaAAC9850a", //mainnet uniswap
|
|
45
|
+
"0x66c0f5449ba4ff4fba0b05716705a4176bbdb848", //defender automation
|
|
46
|
+
"0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11" //"uniswap DAI"
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const systemContracts = {};
|
|
50
|
+
const allContracts = flatten(
|
|
51
|
+
[coreContracts, stakingContracts, upgradablesContracts].map(_ =>
|
|
52
|
+
Object.values(_).map(_ => Object.values(_))
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
flatten(
|
|
56
|
+
[].concat(
|
|
57
|
+
...[otherContracts, allContracts]
|
|
58
|
+
.map(Object.values)
|
|
59
|
+
.map(arr => arr.map(x => (typeof x === "object" ? Object.values(x) : x)))
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
.filter(x => typeof x === "string" && x.startsWith("0x"))
|
|
63
|
+
.map(addr => (systemContracts[addr.toLowerCase()] = true));
|
|
64
|
+
|
|
65
|
+
const isSystemContract = addr => systemContracts[addr.toLowerCase()] === true;
|
|
66
|
+
|
|
67
|
+
const updateBalance = (balance, update) => {
|
|
68
|
+
return Object.assign({}, DefaultBalance, balance, update);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const quantile = (sorted, q) => {
|
|
72
|
+
const pos = (sorted.length - 1) * q;
|
|
73
|
+
const base = Math.floor(pos);
|
|
74
|
+
|
|
75
|
+
let sum = 0;
|
|
76
|
+
for (let i = 0; i < base; i++) sum += sorted[i];
|
|
77
|
+
|
|
78
|
+
return sum;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const airdrop = (
|
|
82
|
+
ethers: typeof Ethers,
|
|
83
|
+
ethplorer_key,
|
|
84
|
+
etherscan_key
|
|
85
|
+
) => {
|
|
86
|
+
const fusePoktProvider = new ethers.providers.JsonRpcProvider({
|
|
87
|
+
url: "https://fuse-mainnet.gateway.pokt.network/v1/lb/60ee374fc6318362996a1fb0",
|
|
88
|
+
user: "",
|
|
89
|
+
password: "d57939c260bdf0a6f22550e2350b4312" //end point will be removed, so its ok to keep clear text password
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const fuseProvider = new ethers.providers.JsonRpcProvider(
|
|
93
|
+
"https://rpc.fuse.io"
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const fuseGDProvider = new ethers.providers.JsonRpcProvider(
|
|
97
|
+
"https://gooddollar-rpc.fuse.io"
|
|
98
|
+
);
|
|
99
|
+
const fuseArchiveProvider = new ethers.providers.JsonRpcBatchProvider(
|
|
100
|
+
"https://explorer-node.fuse.io/"
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const poktArchiveProvider = new ethers.providers.JsonRpcProvider({
|
|
104
|
+
url: "https://eth-trace.gateway.pokt.network/v1/lb/6130bad2dc57c50036551041",
|
|
105
|
+
user: "",
|
|
106
|
+
password: "15439e4f4aeceb469b6b38e319f4f2a5" //end point will be removed, so its ok to keep clear text password
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
console.log({ systemContracts });
|
|
110
|
+
|
|
111
|
+
const LAST_BLOCK_ETH = 14296865;
|
|
112
|
+
const LAST_BLOCK_FUSE = 14296865;
|
|
113
|
+
const START_BLOCK_FUSE = 14149729;
|
|
114
|
+
const START_BLOCK_ETH = 13683492;
|
|
115
|
+
|
|
116
|
+
const collectAirdropData = async () => {
|
|
117
|
+
const goodMainnet = await ethers
|
|
118
|
+
.getContractAt(
|
|
119
|
+
"GReputation",
|
|
120
|
+
"0x3A9299BE789ac3730e4E4c49d6d2Ad1b8BC34DFf"
|
|
121
|
+
)
|
|
122
|
+
.then(_ => _.connect(new ethers.providers.InfuraProvider()));
|
|
123
|
+
|
|
124
|
+
const goodFuse = await ethers
|
|
125
|
+
.getContractAt(
|
|
126
|
+
"GReputation",
|
|
127
|
+
"0x3A9299BE789ac3730e4E4c49d6d2Ad1b8BC34DFf"
|
|
128
|
+
)
|
|
129
|
+
.then(_ => _.connect(fuseGDProvider));
|
|
130
|
+
|
|
131
|
+
console.log({
|
|
132
|
+
LAST_BLOCK_ETH,
|
|
133
|
+
LAST_BLOCK_FUSE
|
|
134
|
+
});
|
|
135
|
+
const calcGoodMints = async (runForFuse = true) => {
|
|
136
|
+
const step = 10000;
|
|
137
|
+
const START_BLOCK = runForFuse ? START_BLOCK_FUSE : START_BLOCK_ETH;
|
|
138
|
+
|
|
139
|
+
const good = runForFuse ? goodFuse : goodMainnet;
|
|
140
|
+
const LAST_BLOCK = await good.provider.getBlockNumber();
|
|
141
|
+
const blocks = range(START_BLOCK, LAST_BLOCK, step);
|
|
142
|
+
const allLogs = [];
|
|
143
|
+
for (let blockChunk of chunk(blocks, 10)) {
|
|
144
|
+
// Get the filter (the second null could be omitted)
|
|
145
|
+
const ps = blockChunk.map(async (bc: number) => {
|
|
146
|
+
const logs = await good
|
|
147
|
+
.queryFilter(
|
|
148
|
+
good.filters.Mint(),
|
|
149
|
+
bc,
|
|
150
|
+
Math.min(bc + step - 1, LAST_BLOCK)
|
|
151
|
+
)
|
|
152
|
+
.catch(e => {
|
|
153
|
+
console.log("block transfer logs failed retrying...", bc);
|
|
154
|
+
return good.queryFilter(
|
|
155
|
+
good.filters.Mint(),
|
|
156
|
+
bc,
|
|
157
|
+
Math.min(bc + step - 1, LAST_BLOCK)
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
const claimedLogs = await good
|
|
161
|
+
.queryFilter(
|
|
162
|
+
good.filters.StateHashProof(),
|
|
163
|
+
bc,
|
|
164
|
+
Math.min(bc + step - 1, LAST_BLOCK)
|
|
165
|
+
)
|
|
166
|
+
.catch(e => {
|
|
167
|
+
console.log("block transfer logs failed retrying...", bc);
|
|
168
|
+
return good.queryFilter(
|
|
169
|
+
good.filters.StateHashProof(),
|
|
170
|
+
bc,
|
|
171
|
+
Math.min(bc + step - 1, LAST_BLOCK)
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
console.log("found logs:", claimedLogs.length, logs.length, bc);
|
|
175
|
+
return logs.concat(claimedLogs);
|
|
176
|
+
});
|
|
177
|
+
let chunkLogs = flatten(await Promise.all(ps));
|
|
178
|
+
console.log("chunk logs: ", chunkLogs.length, { blockChunk });
|
|
179
|
+
allLogs.push(chunkLogs);
|
|
180
|
+
}
|
|
181
|
+
const logs = flatten(allLogs);
|
|
182
|
+
|
|
183
|
+
const balances: { [key: string]: BigNumber } = {};
|
|
184
|
+
const result = [];
|
|
185
|
+
if (runForFuse) {
|
|
186
|
+
logs.forEach(l => {
|
|
187
|
+
if (l.event === "Mint")
|
|
188
|
+
balances[l.args._to.toLowerCase()] = (
|
|
189
|
+
balances[l.args._to.toLowerCase()] || BigNumber.from("0")
|
|
190
|
+
).add(l.args._amount);
|
|
191
|
+
else
|
|
192
|
+
balances[l.args.user.toLowerCase()] = (
|
|
193
|
+
balances[l.args.user.toLowerCase()] || BigNumber.from("0")
|
|
194
|
+
).sub(l.args.repBalance);
|
|
195
|
+
});
|
|
196
|
+
Object.entries(balances).forEach(
|
|
197
|
+
([k, v]) => {
|
|
198
|
+
if (v.gt(0)) result.push([k, v.toString()]);
|
|
199
|
+
}
|
|
200
|
+
// console.log(k, v.toString())
|
|
201
|
+
);
|
|
202
|
+
console.log(
|
|
203
|
+
"total balances found:",
|
|
204
|
+
Object.entries(balances).length,
|
|
205
|
+
"to update balances:",
|
|
206
|
+
result.length
|
|
207
|
+
);
|
|
208
|
+
fs.writeFileSync("airdrop/repRecoverFuse.json", JSON.stringify(result));
|
|
209
|
+
} else {
|
|
210
|
+
const mints = {};
|
|
211
|
+
const claims = {};
|
|
212
|
+
const result = {};
|
|
213
|
+
console.log("logs found:", logs.length);
|
|
214
|
+
let totalMints = ethers.constants.Zero;
|
|
215
|
+
logs.forEach(l => {
|
|
216
|
+
if (l.event === "Mint") {
|
|
217
|
+
mints[l.args._to.toLowerCase()] = (
|
|
218
|
+
mints[l.args._to.toLowerCase()] || BigNumber.from("0")
|
|
219
|
+
).add(l.args._amount);
|
|
220
|
+
totalMints = totalMints.add(l.args._amount);
|
|
221
|
+
} else
|
|
222
|
+
claims[l.args.user.toLowerCase()] = l.args.repBalance.toString();
|
|
223
|
+
});
|
|
224
|
+
Object.entries(mints).forEach(
|
|
225
|
+
([k, v]) =>
|
|
226
|
+
(result[k] = { claim: "0", ...result[k], mint: v.toString() })
|
|
227
|
+
);
|
|
228
|
+
Object.entries(claims).forEach(
|
|
229
|
+
([k, v]) =>
|
|
230
|
+
(result[k] = { mint: "0", ...result[k], claim: v.toString() })
|
|
231
|
+
);
|
|
232
|
+
console.log({ result, totalMints: totalMints.toString() });
|
|
233
|
+
fs.writeFileSync(
|
|
234
|
+
"airdrop/repRecoverMainnet.json",
|
|
235
|
+
JSON.stringify(result)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
await Promise.all([calcGoodMints(), calcGoodMints(false)]);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const buildMerkleTree = () => {
|
|
244
|
+
const { treeData } = JSON.parse(
|
|
245
|
+
fs.readFileSync("airdrop/airdrop.first.json").toString()
|
|
246
|
+
);
|
|
247
|
+
const fuseMints = JSON.parse(
|
|
248
|
+
fs.readFileSync("airdrop/repRecoverFuse.json").toString()
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const mainnetMints = JSON.parse(
|
|
252
|
+
fs.readFileSync("airdrop/repRecoverMainnet.json").toString()
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
fuseMints.forEach(([addr, v]) => {
|
|
256
|
+
const rep = BigNumber.from(v);
|
|
257
|
+
const repInWei = BigNumber.from(
|
|
258
|
+
treeData[addr.toLowerCase()]?.rep || "0"
|
|
259
|
+
).add(rep);
|
|
260
|
+
const hash = ethers.utils.keccak256(
|
|
261
|
+
ethers.utils.defaultAbiCoder.encode(
|
|
262
|
+
["address", "uint256"],
|
|
263
|
+
[addr, repInWei.toString()]
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
treeData[addr.toLowerCase()] = {
|
|
267
|
+
...treeData[addr.toLowerCase()],
|
|
268
|
+
rep: repInWei,
|
|
269
|
+
hash
|
|
270
|
+
};
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const ethRestore = Object.entries(mainnetMints).map(
|
|
274
|
+
([k, v]: [string, any]) => {
|
|
275
|
+
return [
|
|
276
|
+
k.toLowerCase(),
|
|
277
|
+
BigNumber.from(treeData[k.toLowerCase()]?.rep || "0").toString(),
|
|
278
|
+
v.mint
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
fs.writeFileSync(
|
|
283
|
+
"airdrop/ethAirdropRecover.json",
|
|
284
|
+
JSON.stringify({
|
|
285
|
+
accounts: ethRestore.map(_ => _[0]),
|
|
286
|
+
stateValue: ethRestore.map(_ => _[1]),
|
|
287
|
+
mintValue: ethRestore.map(_ => _[2])
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
let toTree: Array<[string, BigNumber]> = Object.entries(treeData).map(
|
|
291
|
+
([k, v]) => {
|
|
292
|
+
return [k, BigNumber.from((v as any).rep)];
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
toTree = toTree.sort((a, b) => (a[1].gte(b[1]) ? 1 : -1));
|
|
296
|
+
// console.log({ toTree });
|
|
297
|
+
// const topContracts = toTree.filter(_ => _[2] === true);
|
|
298
|
+
const totalReputationAirdrop = toTree
|
|
299
|
+
.reduce((c, a) => c.add(a[1]), BigNumber.from(0))
|
|
300
|
+
.toString();
|
|
301
|
+
console.log({
|
|
302
|
+
totalReputationAirdrop,
|
|
303
|
+
numberOfAccounts: toTree.length
|
|
304
|
+
});
|
|
305
|
+
// const sorted = toTree.map(_ => _[1]);
|
|
306
|
+
console.log(toTree.slice(0, 100).map(_ => [_[0], _[1].toString()]));
|
|
307
|
+
// fs.writeFileSync(`${folder}/reptree.json`, JSON.stringify(toTree));
|
|
308
|
+
// console.log("Reputation Distribution");
|
|
309
|
+
// [0.001, 0.01, 0.1, 0.5].forEach(q =>
|
|
310
|
+
// console.log({
|
|
311
|
+
// precentile: q * 100 + "%",
|
|
312
|
+
// addresses: (sorted.length * q).toFixed(0),
|
|
313
|
+
// rep:
|
|
314
|
+
// quantile(sorted, q) /
|
|
315
|
+
// (CLAIMER_REP_ALLOCATION +
|
|
316
|
+
// HOLDER_REP_ALLOCATION +
|
|
317
|
+
// STAKER_REP_ALLOCATION)
|
|
318
|
+
// })
|
|
319
|
+
// );
|
|
320
|
+
const elements = Object.values(treeData).map((e: any) =>
|
|
321
|
+
Buffer.from(e.hash.slice(2), "hex")
|
|
322
|
+
);
|
|
323
|
+
console.log("creating merkletree...", elements.length);
|
|
324
|
+
//NOTICE: we use a non sorted merkletree to save generation time, this requires also a different proof verification algorithm which
|
|
325
|
+
//is not in the default openzeppelin library
|
|
326
|
+
const merkleTree = new MerkleTree(elements, true);
|
|
327
|
+
// get the merkle root
|
|
328
|
+
// returns 32 byte buffer
|
|
329
|
+
const merkleRoot = merkleTree.getRoot().toString("hex");
|
|
330
|
+
// generate merkle proof
|
|
331
|
+
// returns array of 32 byte buffers
|
|
332
|
+
const proof = merkleTree.getProof(elements[0]).map(_ => _.toString("hex"));
|
|
333
|
+
const validProof = checkProofOrdered(
|
|
334
|
+
proof.map(_ => Buffer.from(_, "hex")),
|
|
335
|
+
merkleTree.getRoot(),
|
|
336
|
+
elements[0],
|
|
337
|
+
1
|
|
338
|
+
);
|
|
339
|
+
const lastProof = merkleTree
|
|
340
|
+
.getProof(elements[elements.length - 1])
|
|
341
|
+
.map(_ => _.toString("hex"));
|
|
342
|
+
const lastValidProof = checkProofOrdered(
|
|
343
|
+
lastProof.map(_ => Buffer.from(_, "hex")),
|
|
344
|
+
merkleTree.getRoot(),
|
|
345
|
+
elements[elements.length - 1],
|
|
346
|
+
elements.length
|
|
347
|
+
);
|
|
348
|
+
console.log({
|
|
349
|
+
merkleRoot,
|
|
350
|
+
proof,
|
|
351
|
+
validProof,
|
|
352
|
+
lastProof,
|
|
353
|
+
lastValidProof,
|
|
354
|
+
proofFor: toTree[0],
|
|
355
|
+
lastProofFor: toTree[toTree.length - 1]
|
|
356
|
+
});
|
|
357
|
+
fs.writeFileSync(
|
|
358
|
+
`airdrop/airdrop.json`,
|
|
359
|
+
JSON.stringify({
|
|
360
|
+
treeData,
|
|
361
|
+
merkleRoot
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const getProof = addr => {
|
|
367
|
+
const { treeData, merkleRoot } = JSON.parse(
|
|
368
|
+
fs.readFileSync("airdrop/airdrop.json").toString()
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
let entries = Object.entries(treeData as Tree);
|
|
372
|
+
let elements = entries.map(e => Buffer.from(e[1].hash.slice(2), "hex"));
|
|
373
|
+
|
|
374
|
+
console.log("creating merkletree...", elements.length);
|
|
375
|
+
const merkleTree = new MerkleTree(elements, true);
|
|
376
|
+
|
|
377
|
+
const calcMerkleRoot = merkleTree.getRoot().toString("hex");
|
|
378
|
+
console.log("merkleroots:", {
|
|
379
|
+
fromFile: merkleRoot,
|
|
380
|
+
calculated: calcMerkleRoot
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const addrData = treeData[addr] || treeData[addr.toLowerCase()];
|
|
384
|
+
const proofFor = Buffer.from(addrData.hash.slice(2), "hex");
|
|
385
|
+
|
|
386
|
+
const proof = merkleTree.getProof(proofFor);
|
|
387
|
+
const proofIndex = entries.findIndex(_ => _[1].hash === addrData.hash) + 1;
|
|
388
|
+
|
|
389
|
+
console.log(
|
|
390
|
+
"checkProof:",
|
|
391
|
+
checkProofOrdered(proof, merkleTree.getRoot(), proofFor, proofIndex)
|
|
392
|
+
);
|
|
393
|
+
const hexProof = proof.map(_ => "0x" + _.toString("hex"));
|
|
394
|
+
console.log({ proofIndex, proof: hexProof, [addr]: addrData });
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
return { buildMerkleTree, collectAirdropData, getProof };
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const _timer = async (name, promise) => {
|
|
401
|
+
const start = Date.now();
|
|
402
|
+
const res = await promise;
|
|
403
|
+
const milis = Date.now() - start;
|
|
404
|
+
console.log(`done task ${name} in ${milis / 1000} seconds`);
|
|
405
|
+
return res;
|
|
406
|
+
};
|
|
@@ -156,10 +156,10 @@ describe("GReputation", () => {
|
|
|
156
156
|
expect(rootState[0]).to.be.equal("0x" + merkleRoot.toString("hex"));
|
|
157
157
|
});
|
|
158
158
|
it("rootState should not change totalsupply until proof", async () => {
|
|
159
|
-
expect(await grep.totalSupply()).to.equal(
|
|
159
|
+
expect(await grep.totalSupply()).to.equal(100);
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
it("should update core balances after proof", async () => {
|
|
162
|
+
it("should update core balances and not change totalsupply after proof of rootState", async () => {
|
|
163
163
|
await grep.proveBalanceOfAtBlockchain("rootState", rep1, 1, proof, 1);
|
|
164
164
|
|
|
165
165
|
//root states changes the core balance
|
|
@@ -168,7 +168,7 @@ describe("GReputation", () => {
|
|
|
168
168
|
|
|
169
169
|
const newVotes = await grep.getVotes(rep1);
|
|
170
170
|
expect(newVotes.toNumber()).to.be.equal(1);
|
|
171
|
-
expect(await grep.totalSupply()).to.equal(
|
|
171
|
+
expect(await grep.totalSupply()).to.equal(100); //total supply shouldnt change by proof
|
|
172
172
|
});
|
|
173
173
|
it("should not set rootState again", async () => {
|
|
174
174
|
await setDAOAddress("GDAO_CLAIMERS", repOwner);
|
|
@@ -610,6 +610,10 @@ describe("GReputation", () => {
|
|
|
610
610
|
});
|
|
611
611
|
|
|
612
612
|
describe("real example of airdrop", async () => {
|
|
613
|
+
let startSupply = ethers.constants.Zero;
|
|
614
|
+
before(async () => {
|
|
615
|
+
startSupply = await grep.totalSupply();
|
|
616
|
+
});
|
|
613
617
|
it("should set a new state hash", async () => {
|
|
614
618
|
let encodedCall = grep.interface.encodeFunctionData(
|
|
615
619
|
"setBlockchainStateHash",
|
|
@@ -621,6 +625,9 @@ describe("GReputation", () => {
|
|
|
621
625
|
);
|
|
622
626
|
|
|
623
627
|
expect(await avatarGenericCall(grep.address, encodedCall)).to.not.throw;
|
|
628
|
+
expect(await grep.totalSupply()).to.eq(
|
|
629
|
+
startSupply.add(ethers.utils.parseEther("96000000"))
|
|
630
|
+
);
|
|
624
631
|
});
|
|
625
632
|
|
|
626
633
|
it("should prove real proof", async () => {
|
|
@@ -658,6 +665,9 @@ describe("GReputation", () => {
|
|
|
658
665
|
expect(
|
|
659
666
|
await grep.getVotes("0xf79b804bae955ae4cd8e8b0331c4bc437104804f")
|
|
660
667
|
).to.be.eq(prevVotes.add(rep)); //add new blockchain rep
|
|
668
|
+
expect(await grep.totalSupply()).to.eq(
|
|
669
|
+
startSupply.add(ethers.utils.parseEther("96000000"))
|
|
670
|
+
);
|
|
661
671
|
});
|
|
662
672
|
|
|
663
673
|
it("should prove real proof of last index", async () => {
|
|
@@ -690,6 +700,9 @@ describe("GReputation", () => {
|
|
|
690
700
|
expect(
|
|
691
701
|
await grep.getVotes("0x68b064891efb77b87fe1e872205e795f75a72a6d")
|
|
692
702
|
).to.be.eq(prevVotes.add(rep)); //add new blockchain rep
|
|
703
|
+
expect(await grep.totalSupply()).to.eq(
|
|
704
|
+
startSupply.add(ethers.utils.parseEther("96000000"))
|
|
705
|
+
);
|
|
693
706
|
});
|
|
694
707
|
|
|
695
708
|
it("it should be able get votes at the specific block", async () => {
|