code and tests
This commit is contained in:
parent
8ef038400c
commit
8793ef1daf
@ -1,4 +1,4 @@
|
|||||||
# Proposal 45
|
# Proposal 47
|
||||||
|
|
||||||
Create airdrop with Sablier streams and snapshot
|
Create airdrop with Sablier streams and snapshot
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import "./ReentrancyGuard.sol";
|
|||||||
import "./CarefulMath.sol";
|
import "./CarefulMath.sol";
|
||||||
|
|
||||||
import "./interfaces/ISablierAirdrop.sol";
|
import "./interfaces/ISablierAirdrop.sol";
|
||||||
|
import "./interfaces/IRecipientStorage.sol";
|
||||||
import "./libraries/Types.sol";
|
import "./libraries/Types.sol";
|
||||||
|
|
||||||
contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
||||||
@ -146,31 +147,40 @@ contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Public Effects & Interactions Functions ***/
|
/**
|
||||||
|
* @notice To avoid "stack to deep" error
|
||||||
struct CreateStreamLocalVars {
|
*/
|
||||||
|
struct CreateAirdropLocalVars {
|
||||||
MathError mathErr;
|
MathError mathErr;
|
||||||
uint256 duration;
|
uint256 airdropDuration;
|
||||||
uint256 ratePerSecond;
|
uint256 ratePerSecond;
|
||||||
|
uint256 firstStream;
|
||||||
|
Types.Recipient[] airdropRecipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** Public Effects & Interactions Functions ***/
|
||||||
function createAirdrop(
|
function createAirdrop(
|
||||||
Types.Recipient[] memory recipients,
|
|
||||||
uint256 startTime,
|
uint256 startTime,
|
||||||
uint256 stopTime
|
uint256 stopTime,
|
||||||
|
address recipientStorage
|
||||||
) public onlyGovernance returns (bool) {
|
) public onlyGovernance returns (bool) {
|
||||||
uint256 airdropDuration = stopTime - startTime;
|
CreateAirdropLocalVars memory vars;
|
||||||
CreateStreamLocalVars memory vars;
|
vars.airdropDuration = stopTime - startTime;
|
||||||
|
vars.airdropRecipients = IRecipientStorage(recipientStorage).getAirdropRecipients();
|
||||||
|
vars.firstStream = nextStreamId;
|
||||||
|
|
||||||
for (uint256 i = 0; i < recipients.length; i++) {
|
require(vars.airdropRecipients.length > 0, "no airdrop recipients");
|
||||||
uint256 normalizedDeposit = recipients[i].deposit - (recipients[i].deposit % airdropDuration);
|
|
||||||
address recipientAddr = recipients[i].addr;
|
for (uint256 i = 0; i < vars.airdropRecipients.length; i++) {
|
||||||
uint256 recipientInitialLockedBalance = recipients[i].initialLockedBalance;
|
uint256 normalizedDeposit = vars.airdropRecipients[i].deposit -
|
||||||
|
(vars.airdropRecipients[i].deposit % vars.airdropDuration);
|
||||||
|
address recipientAddr = vars.airdropRecipients[i].addr;
|
||||||
|
uint256 recipientInitialLockedBalance = vars.airdropRecipients[i].initialLockedBalance;
|
||||||
|
|
||||||
/* Without this, the rate per second would be zero. */
|
/* Without this, the rate per second would be zero. */
|
||||||
require(normalizedDeposit >= airdropDuration, "deposit smaller than time delta");
|
require(normalizedDeposit >= vars.airdropDuration, "deposit smaller than time delta");
|
||||||
|
|
||||||
(vars.mathErr, vars.ratePerSecond) = divUInt(normalizedDeposit, airdropDuration);
|
(vars.mathErr, vars.ratePerSecond) = divUInt(normalizedDeposit, vars.airdropDuration);
|
||||||
/* `divUInt` can only return MathError.DIVISION_BY_ZERO but we know `duration` is not zero. */
|
/* `divUInt` can only return MathError.DIVISION_BY_ZERO but we know `duration` is not zero. */
|
||||||
assert(vars.mathErr == MathError.NO_ERROR);
|
assert(vars.mathErr == MathError.NO_ERROR);
|
||||||
|
|
||||||
@ -201,6 +211,7 @@ contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit CreateAirdrop(startTime, stopTime, vars.airdropRecipients.length, vars.firstStream, nextStreamId - 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +260,7 @@ contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
|||||||
* @param streamId The id of the stream to cancel.
|
* @param streamId The id of the stream to cancel.
|
||||||
* @return bool true=success, otherwise false.
|
* @return bool true=success, otherwise false.
|
||||||
*/
|
*/
|
||||||
function cancelStream(uint256 streamId) external nonReentrant streamExists(streamId) onlyGovernance returns (bool) {
|
function cancelStream(uint256 streamId) external streamExists(streamId) onlyGovernance returns (bool) {
|
||||||
Types.Stream memory stream = streams[streamId];
|
Types.Stream memory stream = streams[streamId];
|
||||||
uint256 remainingBalance = stream.remainingBalance;
|
uint256 remainingBalance = stream.remainingBalance;
|
||||||
|
|
||||||
@ -262,11 +273,36 @@ contract SablierAirdrop is ISablierAirdrop, ReentrancyGuard, CarefulMath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Cancels airdrop and withdraw all unclaimed tokens back to governance.
|
* @notice Cancels all airdrop streams and withdraw all unclaimed tokens back to governance.
|
||||||
* @dev Throws if there is a token transfer failure.
|
* @dev Throws if there is a token transfer failure.
|
||||||
* @return bool true=success, otherwise false.
|
* @return bool true=success, otherwise false.
|
||||||
*/
|
*/
|
||||||
function cancelAirdrop() external onlyGovernance returns (bool) {
|
function cancelAirdrop(uint256 firstStreamId, uint256 lastStreamId) external onlyGovernance returns (bool) {
|
||||||
|
require(lastStreamId < nextStreamId, "last id exceeds stream count");
|
||||||
|
|
||||||
|
uint256 airdropRemainingBalance;
|
||||||
|
for (uint256 streamId = firstStreamId; streamId <= lastStreamId; streamId++) {
|
||||||
|
Types.Stream memory stream = streams[streamId];
|
||||||
|
uint256 remainingBalance = stream.remainingBalance;
|
||||||
|
|
||||||
|
if (remainingBalance > 0) {
|
||||||
|
airdropRemainingBalance += stream.remainingBalance;
|
||||||
|
|
||||||
|
delete streams[streamId];
|
||||||
|
emit CancelStream(streamId, stream.recipient, stream.remainingBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
torn.safeTransfer(tornadoGovernance, airdropRemainingBalance);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Withdraw all unclaimed tokens back to governance without canceling airdrop streams
|
||||||
|
* @dev Throws if there is a token transfer failure.
|
||||||
|
* @return bool true=success, otherwise false.
|
||||||
|
*/
|
||||||
|
function withdrawFunds() external onlyGovernance returns (bool) {
|
||||||
torn.safeTransfer(tornadoGovernance, torn.balanceOf(address(this)));
|
torn.safeTransfer(tornadoGovernance, torn.balanceOf(address(this)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
21
contracts/AirdropRecipients.sol
Normal file
21
contracts/AirdropRecipients.sol
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.9;
|
||||||
|
|
||||||
|
import "./libraries/Types.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice i create separate contract bcs i will use the same airdrop recipients in two proposals
|
||||||
|
*/
|
||||||
|
contract AirdropRecipients {
|
||||||
|
Types.Recipient[] public airdropRecipients;
|
||||||
|
|
||||||
|
constructor(Types.Recipient[] memory _recipients) {
|
||||||
|
for (uint256 i = 0; i < _recipients.length; i++) {
|
||||||
|
airdropRecipients.push(_recipients[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAirdropRecipients() external view returns (Types.Recipient[] memory) {
|
||||||
|
return airdropRecipients;
|
||||||
|
}
|
||||||
|
}
|
@ -8,29 +8,19 @@ import { ISablierAirdrop } from "./interfaces/ISablierAirdrop.sol"; // copied fr
|
|||||||
contract Proposal {
|
contract Proposal {
|
||||||
IERC20 public constant torn = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
IERC20 public constant torn = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||||
ISablierAirdrop public immutable airdrop;
|
ISablierAirdrop public immutable airdrop;
|
||||||
|
address public immutable airdropRecipientStorage;
|
||||||
uint256 public constant airdropDuration = 180 days;
|
uint256 public constant airdropDuration = 180 days;
|
||||||
Types.Recipient[] public recipients;
|
|
||||||
|
|
||||||
constructor(address _airdropContract) {
|
constructor(address _airdropContract, address _airdropRecipientsStorage) {
|
||||||
Types.Recipient[1] memory _recipients = [
|
|
||||||
Types.Recipient({
|
|
||||||
addr: 0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4,
|
|
||||||
deposit: 1000 ether,
|
|
||||||
initialLockedBalance: 1000 ether
|
|
||||||
})
|
|
||||||
];
|
|
||||||
airdrop = ISablierAirdrop(_airdropContract);
|
airdrop = ISablierAirdrop(_airdropContract);
|
||||||
for (uint256 i = 0; i < _recipients.length; i++) {
|
airdropRecipientStorage = _airdropRecipientsStorage;
|
||||||
recipients.push(_recipients[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeProposal() external {
|
function executeProposal() public {
|
||||||
torn.transfer(address(airdrop), 1000000 ether);
|
torn.transfer(address(airdrop), 1000000 ether);
|
||||||
|
|
||||||
require(recipients[0].addr != address(0), "zero");
|
|
||||||
uint256 startTime = block.timestamp;
|
uint256 startTime = block.timestamp;
|
||||||
uint256 endTime = startTime + airdropDuration;
|
uint256 endTime = startTime + airdropDuration;
|
||||||
airdrop.createAirdrop(recipients, startTime, endTime);
|
airdrop.createAirdrop(startTime, endTime, airdropRecipientStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
contracts/interfaces/IRecipientStorage.sol
Normal file
10
contracts/interfaces/IRecipientStorage.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity >=0.5.17;
|
||||||
|
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../libraries/Types.sol";
|
||||||
|
|
||||||
|
interface IRecipientStorage {
|
||||||
|
function getAirdropRecipients() external view returns (Types.Recipient[] memory);
|
||||||
|
}
|
@ -18,6 +18,17 @@ interface ISablierAirdrop {
|
|||||||
uint256 stopTime
|
uint256 stopTime
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Emits when full airdrop is successfully created.
|
||||||
|
*/
|
||||||
|
event CreateAirdrop(
|
||||||
|
uint256 startTime,
|
||||||
|
uint256 stopTime,
|
||||||
|
uint256 recipientsAmount,
|
||||||
|
uint256 firstStreamId,
|
||||||
|
uint256 lastStreamId
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Emits when the recipient of a stream withdraws a portion or all their pro rata share of the stream.
|
* @notice Emits when the recipient of a stream withdraws a portion or all their pro rata share of the stream.
|
||||||
*/
|
*/
|
||||||
@ -44,11 +55,7 @@ interface ISablierAirdrop {
|
|||||||
uint256 ratePerSecond
|
uint256 ratePerSecond
|
||||||
);
|
);
|
||||||
|
|
||||||
function createAirdrop(
|
function createAirdrop(uint256 startTime, uint256 stopTime, address recipientStorage) external returns (bool);
|
||||||
Types.Recipient[] calldata recipients,
|
|
||||||
uint256 startTime,
|
|
||||||
uint256 stopTime
|
|
||||||
) external returns (bool);
|
|
||||||
|
|
||||||
function withdrawFromStream(uint256 streamId, uint256 funds) external returns (bool);
|
function withdrawFromStream(uint256 streamId, uint256 funds) external returns (bool);
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
// SPDX-License-Identifier: UNLICENSED
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
pragma solidity >=0.5.17;
|
pragma solidity >=0.5.17;
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Sablier Types
|
|
||||||
* @author Sablier
|
|
||||||
*/
|
|
||||||
library Types {
|
library Types {
|
||||||
struct Stream {
|
struct Stream {
|
||||||
uint256 deposit;
|
uint256 deposit;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"address": "0xA29DbaA84a9949A0A38a2Ba07E0b75e6603934DB",
|
|
||||||
"balance": "222061659733700101062"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xe9B4aA5906C6e842e52aE7B7ab4dec3EE52362cB",
|
|
||||||
"balance": "187871680391712414210"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x642df98E9d71Ba533dF7288512cF63D34a907659",
|
"address": "0x642df98E9d71Ba533dF7288512cF63D34a907659",
|
||||||
"balance": "3274920890240498949168"
|
"balance": "3274920890240498949168"
|
||||||
@ -19,46 +11,18 @@
|
|||||||
"address": "0xBFb910652F850F85E3F85AA0C12aE8f4037095b0",
|
"address": "0xBFb910652F850F85E3F85AA0C12aE8f4037095b0",
|
||||||
"balance": "18064223509187007832993"
|
"balance": "18064223509187007832993"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xe2506955723C01dDd6e619dD0829e28F76328c41",
|
|
||||||
"balance": "369300000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x3Df488Bd07C2082E84B1CD63F343Cf3d538342bA",
|
|
||||||
"balance": "268606679563771005193"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x92F82EF0D08d8ADa7f542fC300adfd0b712b5963",
|
"address": "0x92F82EF0D08d8ADa7f542fC300adfd0b712b5963",
|
||||||
"balance": "3614275869036584865053"
|
"balance": "3697937593601682896111"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x65C6AA5521F9b149E918A9A44A44FD651dF0AFF3",
|
"address": "0x65C6AA5521F9b149E918A9A44A44FD651dF0AFF3",
|
||||||
"balance": "1337537896786103209588"
|
"balance": "1337537896786103209588"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x25713B024a8004727Ba79c43647a77c7447948dB",
|
|
||||||
"balance": "327316949018727268744"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xB157ba30e3467DdBC844f14F02b4ba741f1d549F",
|
|
||||||
"balance": "300000008217141924923"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x83c418D2eD6670785330B996b47a18492b61e218",
|
"address": "0x83c418D2eD6670785330B996b47a18492b61e218",
|
||||||
"balance": "24604927081108500781847"
|
"balance": "24604927081108500781847"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x39978cc40e2D1d7E127050bDFFFBB0dFcfaEbAd0",
|
|
||||||
"balance": "407032960152083087629"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x1d7979F557BA38892f25D3d54Fa6C1D75a4CfA27",
|
|
||||||
"balance": "176136388919542490495"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x9433ea2C250D84d3fD6398699D48728fa610569A",
|
|
||||||
"balance": "320126637964798831618"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xF99d1946Bf038a1205d430Ec91401E760e5F8F6F",
|
"address": "0xF99d1946Bf038a1205d430Ec91401E760e5F8F6F",
|
||||||
"balance": "37075508978146311050113"
|
"balance": "37075508978146311050113"
|
||||||
@ -73,100 +37,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xeE1D1ac27A80De54F0ba92b2E25EBb3418495db2",
|
"address": "0xeE1D1ac27A80De54F0ba92b2E25EBb3418495db2",
|
||||||
"balance": "2331546926022547020105"
|
"balance": "2616085853090372402156"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xefe8Aa053Ef0C9F1879911ef1C83Da08AD385164",
|
|
||||||
"balance": "306015687989988508846"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x238926025E84475e3182774df480021470f8F978",
|
|
||||||
"balance": "119748510011508001412"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x0c3a4a0ed70fDB29860760b5ca78B116Fe77DE00",
|
|
||||||
"balance": "140306615000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x54F2f104Fb6ABf1a6991150B27662Aade958A338",
|
|
||||||
"balance": "319349649026838373837"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x327346e623A4334f7c4034D5a73Ac636d33a31c0",
|
|
||||||
"balance": "304751021776330822150"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xeafa6FbaebB26F9d07B42f3Fd8906818CE75026F",
|
|
||||||
"balance": "922236496491056455335"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xe443cbad516eE9b00A2CbE7F884A4bb314776dE3",
|
"address": "0xe443cbad516eE9b00A2CbE7F884A4bb314776dE3",
|
||||||
"balance": "64680368781627387285673"
|
"balance": "67999143733426979646229"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x2D56db2f95Ae61c2396d30ABEa61fc91E506943F",
|
|
||||||
"balance": "135065031612858769227"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xc7557AfDB4C2EF7E2A5e68ed60B1f8AC41c87459",
|
|
||||||
"balance": "100170264404649634361"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x6eA8BfAa4697187D3BB1E27688931d73BcbbEC0b",
|
|
||||||
"balance": "439287784325687715732"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xE0a9Cf94C4c41a7709B55D9bA37D788A8DF19F9E",
|
|
||||||
"balance": "118418383918603784875"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x06F166E11d765aE936E7ec36777E1b4B6f0Cb83B",
|
|
||||||
"balance": "180305703889462319555"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x1023aE712D5F219e9166a9D286e183226DFAafa6",
|
|
||||||
"balance": "143712248142294445923"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x75F3fC9189fd3228a4178e544CdE26b75Da81Bf3",
|
|
||||||
"balance": "547090923569720095523"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x90e14E059B28Af64B52d4Da0442b22052f507eF1",
|
|
||||||
"balance": "906706256793263291165"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x344651A2445484bd2928eB46D2610DaaC1B42A66",
|
|
||||||
"balance": "244547007283286030643"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x2490419318b3F69e8dc209604056f40Eb350d0d7",
|
"address": "0x2490419318b3F69e8dc209604056f40Eb350d0d7",
|
||||||
"balance": "1641166788776420175594"
|
"balance": "1641166788776420175594"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4aAE2d5072385b02465263d5feb91bb1995D4f37",
|
|
||||||
"balance": "133266200000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x0df59978Cc975e27C5E4e19d355e025F0770F088",
|
"address": "0x0df59978Cc975e27C5E4e19d355e025F0770F088",
|
||||||
"balance": "1278073898007810414591"
|
"balance": "1278073898007810414591"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x85e6C3c83929eE45712EFF4F89c5E74cCddB5D41",
|
|
||||||
"balance": "220168850000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xC346A5dc3b3821AEeF3f3fFB70989631521E41cD",
|
|
||||||
"balance": "224707550488945833056"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x5d17B355538A9ea9b45A0018e11b36947fC16376",
|
"address": "0x5d17B355538A9ea9b45A0018e11b36947fC16376",
|
||||||
"balance": "3461949885239649131822"
|
"balance": "3461949885239649131822"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xAaF7168Bae99a4860D7729745B22C4b48d1f9Adb",
|
|
||||||
"balance": "100475944611586099877"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xB835b4e277094D4AF9Fbb66e9f5Db9e6E8dC65dB",
|
"address": "0xB835b4e277094D4AF9Fbb66e9f5Db9e6E8dC65dB",
|
||||||
"balance": "2499187111378392690208"
|
"balance": "2499187111378392690208"
|
||||||
@ -175,78 +63,18 @@
|
|||||||
"address": "0x42DbF634c256acd17beDDC1330488F1BEa7B8BDf",
|
"address": "0x42DbF634c256acd17beDDC1330488F1BEa7B8BDf",
|
||||||
"balance": "6877216358537763535178"
|
"balance": "6877216358537763535178"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xE7304bA0f157f2Ade94015934284b6704BC72911",
|
|
||||||
"balance": "112608649098976008132"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x3eE9378Fb0809a243FcF7C30865941675d766D71",
|
"address": "0x3eE9378Fb0809a243FcF7C30865941675d766D71",
|
||||||
"balance": "2774004155905058916423"
|
"balance": "2774004155905058916423"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x81348BB6CFA32662cE76aE5FcC8c54F056CE7042",
|
|
||||||
"balance": "100400761930000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x06981EfbE070996654482F4F20786e0CEf0f8740",
|
"address": "0x06981EfbE070996654482F4F20786e0CEf0f8740",
|
||||||
"balance": "32754177841356351554810"
|
"balance": "43121397719834144990842"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x92e74E50edCE4573d181C03cA889f72640cD3a94",
|
|
||||||
"balance": "407199993263279657437"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x3dD44f129563787449367046c9cBF5610f21467f",
|
|
||||||
"balance": "196204479061497808556"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x0A54cf15a8719DB69B5cC40d797a213ca9407B8E",
|
|
||||||
"balance": "370960788258305263912"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x95880f2E70203f759168665Feb6948E81Fe5dF77",
|
|
||||||
"balance": "294020437588528361099"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x0050faCd2C04930257438b213FE9E132a26E5a29",
|
|
||||||
"balance": "119786423000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x1d14080067dE02E048DdE58a936b4EBED7ee56e4",
|
|
||||||
"balance": "135408011012662929013"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xbb6d1648A8BeF51603Badd347Dc91eb7D04a43e5",
|
|
||||||
"balance": "961751770023408248012"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xD7b2879C8922cd704E41E8CC1f18f6994D6B7C36",
|
"address": "0xD7b2879C8922cd704E41E8CC1f18f6994D6B7C36",
|
||||||
"balance": "1119299000000000000000"
|
"balance": "1119299000000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x7a628E9bCC082844797C37c79084ed6BC99e6Abc",
|
|
||||||
"balance": "138985632834248119775"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xDaa5C5a5B6725666B1e37B14dc8e1FCB446c5952",
|
|
||||||
"balance": "119080000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x5dab475E601D55efa2290D27333913e025C7Bd89",
|
|
||||||
"balance": "146964212351551947890"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x4051e1fcD4Be1366a0CDcfA47ca717868A96575E",
|
|
||||||
"balance": "561299291075539499694"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xB3AAd889bf41Ec39571aFa78aa5D9A26a05DDaAb",
|
|
||||||
"balance": "208128000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x4303Ddc9943D862f2B205aF468a4A786c5137E76",
|
|
||||||
"balance": "121978258758390804343"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xa1eBC8bdE2F1F87fe24F384497b6bD9cE3B14345",
|
"address": "0xa1eBC8bdE2F1F87fe24F384497b6bD9cE3B14345",
|
||||||
"balance": "2984693312345136089833"
|
"balance": "2984693312345136089833"
|
||||||
@ -255,102 +83,26 @@
|
|||||||
"address": "0xaf095d9cedBc20c8Bb0A75ED9509c1f31DC4E7E6",
|
"address": "0xaf095d9cedBc20c8Bb0A75ED9509c1f31DC4E7E6",
|
||||||
"balance": "4411294324551597847209"
|
"balance": "4411294324551597847209"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4839460F05b2a8fC1b615e0523EbC46eFba75420",
|
|
||||||
"balance": "147677697371346056769"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x6b69c30354Cef94ee9d77DBC7BD268B4A6683825",
|
|
||||||
"balance": "215671961915475600719"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x1CE7DC66CC98c61A573105514d073D64a9a8608f",
|
|
||||||
"balance": "183302847714096393245"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x0d4E989c7620C8749c9417d2BF218896C767B606",
|
"address": "0x0d4E989c7620C8749c9417d2BF218896C767B606",
|
||||||
"balance": "11111000000000000000000"
|
"balance": "11111000000000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4963C9C61f48272969587c27aA4e6384798d48cA",
|
|
||||||
"balance": "646591448141274005553"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x6fb4becf05497b79F0fCF61CfA5075efAA137DDF",
|
|
||||||
"balance": "118347626348277285232"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x6f9BB7e454f5B3eb2310343f0E99269dC2BB8A1d",
|
|
||||||
"balance": "554530214763375646077"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xb4e39F6D3d386E98AF0C064E087aCBC9A8F08944",
|
|
||||||
"balance": "147140882538472853905"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x2462375371935c8122895e701380A06CE815B0FD",
|
|
||||||
"balance": "214282527968312078762"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x24f04EC62597C11752c47448228B63052ED3158a",
|
|
||||||
"balance": "321761894521216575819"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x8904FFC6Aa039dB5941De11309a42372F3F7D2FD",
|
|
||||||
"balance": "296350209248534143997"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xa729adDeFe1fa7BCe87053ed55D55EDdDD13De60",
|
|
||||||
"balance": "105257962571588444433"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x9265f86d58b348060E43216f968bda8A6Bb63572",
|
|
||||||
"balance": "462054994392400414840"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xcE1fFD4cfE424757D6e597293566195Fba6f4cD8",
|
|
||||||
"balance": "378325759765781498396"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x2E63Dbd91657dc5fa97213Ba6a1AEb5f194805F6",
|
"address": "0x2E63Dbd91657dc5fa97213Ba6a1AEb5f194805F6",
|
||||||
"balance": "1200089842937217922038"
|
"balance": "1200089842937217922038"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4273397532A8dF43a6607C4441e3EB6edF63acf3",
|
|
||||||
"balance": "150000000000000000037"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xC68c10C524eC53A284FB76a91b2e97F15Fe79634",
|
"address": "0xC68c10C524eC53A284FB76a91b2e97F15Fe79634",
|
||||||
"balance": "1295708210000000000000"
|
"balance": "1295708210000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x12168AEa666Be756E1aD0595fEd9C306442a4103",
|
|
||||||
"balance": "129858882018647549604"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xE70eF2B9A263855535727Ee014C6Ffaf01881Ae3",
|
|
||||||
"balance": "126263397985613051754"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x5Fc0A53A7e33Aa6E41A93b718DD2216707251fC2",
|
"address": "0x5Fc0A53A7e33Aa6E41A93b718DD2216707251fC2",
|
||||||
"balance": "1216512730000000000000"
|
"balance": "1216512730000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x18F516dD6D5F46b2875Fd822B994081274be2a8b",
|
|
||||||
"balance": "808879218854315691416"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x7803Ab56769dDa5432CDbd63749a6BEeb3180008",
|
"address": "0x7803Ab56769dDa5432CDbd63749a6BEeb3180008",
|
||||||
"balance": "1000020482341666374383"
|
"balance": "1000020482341666374383"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4F50d47D20380172746527bbeAa274940C38EFAC",
|
|
||||||
"balance": "489392817721401322749"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xFeAe121618049802Ccb40f094536a7B186642699",
|
|
||||||
"balance": "748171152585153967146"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x069Dd24FC8ABEC3708841a4726617c17BC74d5eC",
|
"address": "0x069Dd24FC8ABEC3708841a4726617c17BC74d5eC",
|
||||||
"balance": "4465692444586264801956"
|
"balance": "4465692444586264801956"
|
||||||
@ -363,129 +115,37 @@
|
|||||||
"address": "0x26D3b88d464A575784Be70Edc5F9290dC5e296DB",
|
"address": "0x26D3b88d464A575784Be70Edc5F9290dC5e296DB",
|
||||||
"balance": "4584368997732369384071"
|
"balance": "4584368997732369384071"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x7Abbdbd51813d85B448F887b946719Dd2B09D6F7",
|
|
||||||
"balance": "477257927573167840914"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xEb89EfE3Ed80288E310c5529e1E242b4ab56E196",
|
"address": "0xEb89EfE3Ed80288E310c5529e1E242b4ab56E196",
|
||||||
"balance": "1300090776209185468018"
|
"balance": "1300090776209185468018"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xAD142B79f2500Bf194C174f06C7dC901A2CdA74f",
|
|
||||||
"balance": "174034164798218427885"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x4fFDAeB9f4ec23254B6DFA41eEeF2C8d23f8c554",
|
|
||||||
"balance": "131502368878796458509"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xb03384cA294C06E8d04B08412E8467ff2363E5E3",
|
|
||||||
"balance": "906139663513082751610"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x6A3738c6299f45c31697aceA647D49EdCC9C28A4",
|
|
||||||
"balance": "426032828677716268597"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xc74d1af789F5A0223448Db69a1B56c84845e17d5",
|
|
||||||
"balance": "386136667062531373859"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xDf90418385aF4E9833603069DEaF7C71d76f83a2",
|
|
||||||
"balance": "683465828138249494426"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x16eb5Fbb2a5c7dAbC2c7688482378e89471d5724",
|
|
||||||
"balance": "251802411781229744678"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x356eE97D7D560af2E97E9f9EEe56b2e09D60e6F5",
|
"address": "0x356eE97D7D560af2E97E9f9EEe56b2e09D60e6F5",
|
||||||
"balance": "10836062927446519092234"
|
"balance": "10836062927446519092234"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x21c756E2c7B898BDa8a3b2A77e4C56D855Ab9414",
|
|
||||||
"balance": "261098218101404112455"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xFB96D60ce3ee83ed4BC71c6E0da10C537601b77C",
|
|
||||||
"balance": "446702746160587197664"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xeb0fCD59212E521918b514c64313eF5a3dd101f8",
|
|
||||||
"balance": "100240415124564555330"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x34DF0B83cf4F30683f8AB2Ac971a52A5f663FEb4",
|
|
||||||
"balance": "116124431887044456353"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xF2Ba32f180B243D3138DEd0E1F1BdbdcCBCE0702",
|
"address": "0xF2Ba32f180B243D3138DEd0E1F1BdbdcCBCE0702",
|
||||||
"balance": "1482848214495146566693"
|
"balance": "1482848214495146566693"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xbC063735a5b8bdd12611157E7E0F29D1570D892D",
|
|
||||||
"balance": "509451890000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xe7c724F87EcFf8E2f563962f96b6c291cBD729cF",
|
|
||||||
"balance": "250243001109044335565"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x1fe7d323946A7C3B644528baA42eFf13056cd2B4",
|
"address": "0x1fe7d323946A7C3B644528baA42eFf13056cd2B4",
|
||||||
"balance": "2012288953464165015019"
|
"balance": "2012288953464165015019"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x48c0477Bf79a59e505d26eB33c5b331502a511B2",
|
"address": "0x48c0477Bf79a59e505d26eB33c5b331502a511B2",
|
||||||
"balance": "11028610297596262814514"
|
"balance": "11808140176616152755130"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xE5A5BfC1772D4A2b44Add05aae63026A89486C1B",
|
"address": "0xE5A5BfC1772D4A2b44Add05aae63026A89486C1B",
|
||||||
"balance": "2897272017864013753943"
|
"balance": "2897272017864013753943"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x00377B2FC0044Dec6507855eDd6531aF1755cCe4",
|
|
||||||
"balance": "942500316330675833924"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xD9D10dc5609d77F2A15FAa68414835A6ed19269B",
|
|
||||||
"balance": "604677796610747756803"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xdF20a1124d6B44e19ad75675a622d9c9669E59B6",
|
|
||||||
"balance": "720900000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xF4d2D64D1f9190A9daB0960c80e5C73c04710184",
|
|
||||||
"balance": "630536604758334996358"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xf68de31d7fC80A70959966F9Aa78e86A7b418A10",
|
|
||||||
"balance": "100424753924039137153"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x47B9cCa44fe405D12ff56aaCd1Df6dbA4A79Ae28",
|
"address": "0x47B9cCa44fe405D12ff56aaCd1Df6dbA4A79Ae28",
|
||||||
"balance": "1198321376929923406445"
|
"balance": "1198321376929923406445"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x71917F124be5C0Cd97aDBE7AB472f4C82577B542",
|
|
||||||
"balance": "171985544540000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x2a2a1FB6382a528F1B23E76d3Ed6F4C17A9e5966",
|
|
||||||
"balance": "679861011890874134021"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x343F48B346b0cE11342ffdEDaDAC4135E1FD525a",
|
"address": "0x343F48B346b0cE11342ffdEDaDAC4135E1FD525a",
|
||||||
"balance": "1112013784183226328856"
|
"balance": "1244831656243158248642"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x958622B4631212DC751c5B1Ba45013c833Dcb86c",
|
|
||||||
"balance": "1000000000000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x0F46576F3b8b0ed6522e4b200Df53ac73251b911",
|
|
||||||
"balance": "440579268126896398907"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xaAaC34d30d6938787c653AAfB922bc20bFa9C512",
|
"address": "0xaAaC34d30d6938787c653AAfB922bc20bFa9C512",
|
||||||
@ -495,10 +155,6 @@
|
|||||||
"address": "0x758aB8aC42a8c44dF8C31129Db146c65c2669391",
|
"address": "0x758aB8aC42a8c44dF8C31129Db146c65c2669391",
|
||||||
"balance": "9999580000000000000000"
|
"balance": "9999580000000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x149da2CB603A8818Fb2d703D72D46390Cd09A6D9",
|
|
||||||
"balance": "351315917791207543375"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xC2e6B265cb965DED721566f0f9Eb5ab1A6162A21",
|
"address": "0xC2e6B265cb965DED721566f0f9Eb5ab1A6162A21",
|
||||||
"balance": "2004486788385385993147"
|
"balance": "2004486788385385993147"
|
||||||
@ -517,11 +173,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xddbc1841BE23b2ab55501Deb4d6bc39E3f8AA2d7",
|
"address": "0xddbc1841BE23b2ab55501Deb4d6bc39E3f8AA2d7",
|
||||||
"balance": "48363478557462694029018"
|
"balance": "49454261304684065909473"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x7b5edF38D955dd9deC103aF05c2D68B28e02Ad90",
|
"address": "0x7b5edF38D955dd9deC103aF05c2D68B28e02Ad90",
|
||||||
"balance": "29597872806644626228418"
|
"balance": "30721199782932567542900"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xE4143f6377AEcd7193b9731d1C28815b57C4f5Ab",
|
"address": "0xE4143f6377AEcd7193b9731d1C28815b57C4f5Ab",
|
||||||
@ -535,13 +191,9 @@
|
|||||||
"address": "0xa39fE8D3094e2D69Bd579Dc2bc2fc44Feb54Fc57",
|
"address": "0xa39fE8D3094e2D69Bd579Dc2bc2fc44Feb54Fc57",
|
||||||
"balance": "62364443294474230012244"
|
"balance": "62364443294474230012244"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x6f4CbC2E042ed0D1Df4bf14d00eEb52Ff1E0e5F8",
|
|
||||||
"balance": "632357477375385036674"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x92374BA66EE27f207F9efff0837FF6D707006304",
|
"address": "0x92374BA66EE27f207F9efff0837FF6D707006304",
|
||||||
"balance": "26511566449802577442283"
|
"balance": "27582360269943737743173"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58",
|
"address": "0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58",
|
||||||
@ -549,11 +201,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
|
"address": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
|
||||||
"balance": "6237585974021878690758"
|
"balance": "6435143032935074622460"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x065f2A0eF62878e8951af3c387E4ddC944f1B8F4",
|
|
||||||
"balance": "289834051536966486101"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x7C199A0f301913212cfAB3756c3ed8F5B8927713",
|
"address": "0x7C199A0f301913212cfAB3756c3ed8F5B8927713",
|
||||||
@ -585,7 +233,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xBEF6d320Ed305BC9a0977D312c0c1BF9f56749D4",
|
"address": "0xBEF6d320Ed305BC9a0977D312c0c1BF9f56749D4",
|
||||||
"balance": "16792018296955216982422"
|
"balance": "17647018296955216982422"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x6e1Ea6FCEE2221844950FB269780BB450f47b6Ee",
|
"address": "0x6e1Ea6FCEE2221844950FB269780BB450f47b6Ee",
|
||||||
@ -601,7 +249,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xfA9330bb565c2F502D371a96A9F43D9182D732c9",
|
"address": "0xfA9330bb565c2F502D371a96A9F43D9182D732c9",
|
||||||
"balance": "2932970328956661635966"
|
"balance": "3346805409290437403641"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x09AdFab2635dcb681FADA41CeB0bfa6f52EFfd97",
|
"address": "0x09AdFab2635dcb681FADA41CeB0bfa6f52EFfd97",
|
||||||
@ -613,7 +261,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4",
|
"address": "0xeb3E49Af2aB5D5D0f83A9289cF5a34d9e1f6C5b4",
|
||||||
"balance": "1011706736327691989355"
|
"balance": "4736654395057849473603"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xD97938E0EAa80d8801174e6cF3E7155C872aDdcD",
|
"address": "0xD97938E0EAa80d8801174e6cF3E7155C872aDdcD",
|
||||||
@ -629,7 +277,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xef5031F705dfBD187B7F6A86f4173cB067afD1A9",
|
"address": "0xef5031F705dfBD187B7F6A86f4173cB067afD1A9",
|
||||||
"balance": "42729083148460923251275"
|
"balance": "44126667450172916291870"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xFB7c1D49e006eaDdff2385c7eF8B0C5Cf49d038A",
|
"address": "0xFB7c1D49e006eaDdff2385c7eF8B0C5Cf49d038A",
|
||||||
@ -639,25 +287,17 @@
|
|||||||
"address": "0x0AD50B53b0c142432c5f6c5c1fA1e4bb03021374",
|
"address": "0x0AD50B53b0c142432c5f6c5c1fA1e4bb03021374",
|
||||||
"balance": "10000000000000000000000"
|
"balance": "10000000000000000000000"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xa99a29c335590685cCeaCeD2A0dBAf2C099CBeD9",
|
|
||||||
"balance": "170002896000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xa983D03A49C15dE8AF54b313B0C9D4d1e489D600",
|
"address": "0xa983D03A49C15dE8AF54b313B0C9D4d1e489D600",
|
||||||
"balance": "1249862822519364975935"
|
"balance": "1249862822519364975935"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x9b78e16456c8C0A1FE386894c6f1E7F7C0104ADC",
|
|
||||||
"balance": "1000000000000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xD98f8EB41e46C5D22e6eD7A2fE720af3D8DBFcd6",
|
"address": "0xD98f8EB41e46C5D22e6eD7A2fE720af3D8DBFcd6",
|
||||||
"balance": "74604454901819108700316"
|
"balance": "66904454901819108700316"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xBf1F678061DFca5Dee9D85796751D3bb2dc64DE6",
|
"address": "0xBf1F678061DFca5Dee9D85796751D3bb2dc64DE6",
|
||||||
"balance": "2475233242348013353909"
|
"balance": "2547945659038579414017"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xC0F12799B8D3FA8810DfE1616095170C72117F8F",
|
"address": "0xC0F12799B8D3FA8810DfE1616095170C72117F8F",
|
||||||
@ -665,7 +305,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x8eD607D5E14EEc65DbcFe015905E7c19d3Dbe600",
|
"address": "0x8eD607D5E14EEc65DbcFe015905E7c19d3Dbe600",
|
||||||
"balance": "39407794500000000000000"
|
"balance": "39896294500000000000000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0xE5bbc8C362EB53C6f7264C94d969B5a7962A11DF",
|
"address": "0xE5bbc8C362EB53C6f7264C94d969B5a7962A11DF",
|
||||||
@ -677,23 +317,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x13CC8E623098Bb7076a7ae179A2A309282428589",
|
"address": "0x13CC8E623098Bb7076a7ae179A2A309282428589",
|
||||||
"balance": "89840000000000000000000"
|
"balance": "90992000000000000000000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x5aAF2c73342eEdb0B295f0DB7641FAB6e2e0ede7",
|
"address": "0x5aAF2c73342eEdb0B295f0DB7641FAB6e2e0ede7",
|
||||||
"balance": "11177220714563111217644"
|
"balance": "12421230169425669515176"
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x88cafaD2072054b5a641150C8710062D11C296Ad",
|
|
||||||
"balance": "4960490823893811264302"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x43F3D8909819B681aDB64c50EbcaEb77689A101B",
|
"address": "0x43F3D8909819B681aDB64c50EbcaEb77689A101B",
|
||||||
"balance": "9063225233738240554677"
|
"balance": "9163639294452615765232"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x2d1270896be64F2A3835CBb0528313511840AE02",
|
"address": "0x2d1270896be64F2A3835CBb0528313511840AE02",
|
||||||
"balance": "2663650000000000000000"
|
"balance": "2787650000000000000000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x28d5fdd7cD29f9676cc82f30ee4ff47C4aF3c5aB",
|
"address": "0x28d5fdd7cD29f9676cc82f30ee4ff47C4aF3c5aB",
|
||||||
@ -727,14 +363,6 @@
|
|||||||
"address": "0x26532eC1bb6b115a8Ae697be63D47122De02fCa7",
|
"address": "0x26532eC1bb6b115a8Ae697be63D47122De02fCa7",
|
||||||
"balance": "22733003153052279993119"
|
"balance": "22733003153052279993119"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x5D9A3E0Cb733d566FA887b4b74776A8cF9250640",
|
|
||||||
"balance": "287024310000000000000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0x9D1B637E76c0192b11aac1c59666866a8044f83d",
|
|
||||||
"balance": "446950120000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0xa0Ea24593C1571fCcea113Fe7eC1e8dc81191025",
|
"address": "0xa0Ea24593C1571fCcea113Fe7eC1e8dc81191025",
|
||||||
"balance": "3700000000000000000000"
|
"balance": "3700000000000000000000"
|
||||||
@ -743,18 +371,10 @@
|
|||||||
"address": "0x1cE7260e0c5776A2834892e4452bdB099D6f0713",
|
"address": "0x1cE7260e0c5776A2834892e4452bdB099D6f0713",
|
||||||
"balance": "43659039111640240367762"
|
"balance": "43659039111640240367762"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0xefC17Ff5dB1FbbF77304aB3423faab05BAA21d8C",
|
|
||||||
"balance": "732170000000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x0c9fCE6d71774709Ea41CAD8c55e171D5477DC18",
|
"address": "0x0c9fCE6d71774709Ea41CAD8c55e171D5477DC18",
|
||||||
"balance": "10282263191686831069699"
|
"balance": "10282263191686831069699"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "0x4Bb93141ceAFb41856695cC4fC2c07f3a45A5265",
|
|
||||||
"balance": "755139560000000000000"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"address": "0x6Da70176136DaE6eFA25A3399bdA5aEC78215723",
|
"address": "0x6Da70176136DaE6eFA25A3399bdA5aEC78215723",
|
||||||
"balance": "17075770306076350632861"
|
"balance": "17075770306076350632861"
|
||||||
@ -769,6 +389,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "0x6db80dCeB07984e36D6CB97EDeBCC34013fDa1a1",
|
"address": "0x6db80dCeB07984e36D6CB97EDeBCC34013fDa1a1",
|
||||||
"balance": "4994201825300195752523"
|
"balance": "17906842555414252576213"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xf2608E4Ea9e2cf9415Cbbc93610E63a068C2357c",
|
||||||
|
"balance": "1664328504317590735119"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xd858d1d1bcD52A37391064474375Eb601e17bc42",
|
||||||
|
"balance": "23083830817942002710430"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0xFA0eF1B3013b3c1B3228a0a689eddB80c140c46F",
|
||||||
|
"balance": "1500000000000000000000"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -37,11 +37,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
hardhat: {
|
hardhat: {
|
||||||
forking: {
|
forking: {
|
||||||
url: "https://rpc.mevblocker.io/fast",
|
url: "https://rpc.mevblocker.io",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
blockNumber: 19104883,
|
blockNumber: 19195278,
|
||||||
accounts: [process.env.REAL_PK],
|
accounts: [process.env.REAL_PK],
|
||||||
},
|
},
|
||||||
|
timeout: 100000000,
|
||||||
chainId: 1,
|
chainId: 1,
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
|
17
package-lock.json
generated
17
package-lock.json
generated
@ -1,17 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "proposal-45",
|
"name": "proposal-47",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "proposal-45",
|
"name": "proposal-47",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
||||||
"@openzeppelin/upgrades-core": "^1.30.1",
|
"@openzeppelin/upgrades-core": "^1.30.1",
|
||||||
"base58-solidity": "^1.0.2",
|
"base58-solidity": "^1.0.2",
|
||||||
|
"bignumber.js": "^9.0.1",
|
||||||
"content-hash": "^2.5.2",
|
"content-hash": "^2.5.2",
|
||||||
"torn-token": "^1.0.8"
|
"torn-token": "^1.0.8"
|
||||||
},
|
},
|
||||||
@ -2504,9 +2505,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bignumber.js": {
|
"node_modules/bignumber.js": {
|
||||||
"version": "9.1.2",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
|
||||||
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
|
"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
@ -11906,9 +11907,9 @@
|
|||||||
"integrity": "sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg=="
|
"integrity": "sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg=="
|
||||||
},
|
},
|
||||||
"bignumber.js": {
|
"bignumber.js": {
|
||||||
"version": "9.1.2",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
|
||||||
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
|
"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
|
||||||
},
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
||||||
"@openzeppelin/upgrades-core": "^1.30.1",
|
"@openzeppelin/upgrades-core": "^1.30.1",
|
||||||
"base58-solidity": "^1.0.2",
|
"base58-solidity": "^1.0.2",
|
||||||
|
"bignumber.js": "^9.0.1",
|
||||||
"content-hash": "^2.5.2",
|
"content-hash": "^2.5.2",
|
||||||
"torn-token": "^1.0.8"
|
"torn-token": "^1.0.8"
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,43 @@ const hre = require("hardhat");
|
|||||||
const { ethers } = require("hardhat");
|
const { ethers } = require("hardhat");
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
const airdropRecipients = [...require("../data/airdropRecipients.json")];
|
||||||
|
const recipientsFactory = await ethers.getContractFactory("AirdropRecipients");
|
||||||
|
const airdropRecipientsContract = await recipientsFactory.deploy(airdropRecipients);
|
||||||
|
const recipientStorageAddr = await airdropRecipientsContract.getAddress();
|
||||||
|
|
||||||
|
let tx = airdropRecipientsContract.deploymentTransaction();
|
||||||
|
await tx.wait(16);
|
||||||
|
console.log("Aidrop recipients contract confirmed with 16 blocks, waiting for verification on Etherscan");
|
||||||
|
await hre.run("verify:verify", {
|
||||||
|
address: recipientStorageAddr,
|
||||||
|
contract: "contracts/AirdropRecipients.sol:AirdropRecipients",
|
||||||
|
constructorArguments: [airdropRecipients],
|
||||||
|
});
|
||||||
|
|
||||||
const airdropFactory = await ethers.getContractFactory("SablierAirdrop");
|
const airdropFactory = await ethers.getContractFactory("SablierAirdrop");
|
||||||
const airdrop = await airdropFactory.deploy();
|
const airdrop = await airdropFactory.deploy();
|
||||||
const airdropAddr = await airdrop.getAddress();
|
const airdropAddr = await airdrop.getAddress();
|
||||||
let tx = airdrop.deploymentTransaction();
|
|
||||||
|
tx = airdrop.deploymentTransaction();
|
||||||
await tx.wait(16);
|
await tx.wait(16);
|
||||||
console.log("Aidrop deployment confirmed with 16 blocks, waiting for verification on Etherscan");
|
console.log("Aidrop deployment confirmed with 16 blocks, waiting for verification on Etherscan");
|
||||||
await hre.run("verify:verify", {
|
await hre.run("verify:verify", {
|
||||||
address: governanceImplAddr,
|
address: airdropAddr,
|
||||||
contract: "contracts/Airdrop.sol:SablierAirdrop",
|
contract: "contracts/Airdrop.sol:SablierAirdrop",
|
||||||
constructorArguments: [stakingAddr, gasCompensationAddr, userVaultAddr],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const proposalFactory = await ethers.getContractFactory("Proposal");
|
const proposalFactory = await ethers.getContractFactory("Proposal");
|
||||||
const proposal = await proposalFactory.deploy(airdropAddr);
|
const proposal = await proposalFactory.deploy(airdropAddr, recipientStorageAddr);
|
||||||
const deployedProposalAddr = await proposal.getAddress();
|
const deployedProposalAddr = await proposal.getAddress();
|
||||||
|
|
||||||
|
tx = proposal.deploymentTransaction();
|
||||||
|
await tx.wait(16);
|
||||||
|
console.log("Proposal deployment confirmed with 16 blocks, waiting for verification on Etherscan");
|
||||||
await hre.run("verify:verify", {
|
await hre.run("verify:verify", {
|
||||||
address: "0xc4217881856cA15c320B32A87F9ab3Ed17a5A9d0",
|
address: deployedProposalAddr,
|
||||||
contract: "contracts/Proposal.sol:Proposal",
|
contract: "contracts/Proposal.sol:Proposal",
|
||||||
|
constructorArguments: [airdropAddr, recipientStorageAddr],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,4 +53,4 @@ async function main() {
|
|||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
});
|
});
|
||||||
|
@ -24,7 +24,7 @@ async function main() {
|
|||||||
allStakers.push(...resultsPart);
|
allStakers.push(...resultsPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = allStakers.filter((staker) => staker.balance > 100n * 10n ** 18n);
|
const result = allStakers.filter((staker) => staker.balance > 1000n * 10n ** 18n);
|
||||||
fs.writeFileSync("data/stakers.json", JSON.stringify(result, null, 4), { encoding: "utf-8" });
|
fs.writeFileSync("data/stakers.json", JSON.stringify(result, null, 4), { encoding: "utf-8" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
358
test/Governance.js
Normal file
358
test/Governance.js
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
const { ethers, network } = require("hardhat");
|
||||||
|
const { time } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
|
||||||
|
const { expect } = require("chai");
|
||||||
|
const {
|
||||||
|
deployAndExecuteProposal,
|
||||||
|
getProxyImplAddr,
|
||||||
|
governanceAddr,
|
||||||
|
resetStateBeforeProposal,
|
||||||
|
getManyEth,
|
||||||
|
resolveAddr,
|
||||||
|
getTorn,
|
||||||
|
getGovernance,
|
||||||
|
getPermitSignature,
|
||||||
|
tornAddr,
|
||||||
|
stakingAddr,
|
||||||
|
executeNewProposal,
|
||||||
|
signerLockInGov,
|
||||||
|
} = require("./utils");
|
||||||
|
|
||||||
|
// comment some checks if your proposal changes something
|
||||||
|
describe("All Governance checks", function () {
|
||||||
|
beforeEach(resetStateBeforeProposal);
|
||||||
|
|
||||||
|
it("Governance implementation should be a valid contract", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const governanceImplAddr = await getProxyImplAddr(governanceAddr);
|
||||||
|
const code = await ethers.provider.getCode(governanceImplAddr);
|
||||||
|
expect(code).to.not.be.equal("0x");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createValidProposal() {
|
||||||
|
// this proposal changes governance impl addr to old governance impl 0xBa178126C28F50Ee60322a82f5EbCd6b3711e101
|
||||||
|
const proposalBytecode =
|
||||||
|
"0x608060405234801561001057600080fd5b50610161806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063373058b814610030575b600080fd5b61003861003a565b005b735efda50f22d34f262c29268506c5fa42cb56a1ce73ffffffffffffffffffffffffffffffffffffffff16633659cfe673ba178126c28f50ee60322a82f5ebcd6b3711e1016040518263ffffffff1660e01b815260040161009b9190610110565b600060405180830381600087803b1580156100b557600080fd5b505af11580156100c9573d6000803e3d6000fd5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100fa826100cf565b9050919050565b61010a816100ef565b82525050565b60006020820190506101256000830184610101565b9291505056fea264697066735822122071ee22910809ebd1174acbe4a8ce386abb553bbc1cf37fd7c9abb166b18ca6f664736f6c63430008140033";
|
||||||
|
const signer = (await ethers.getSigners())[0];
|
||||||
|
const proposalAbi = ["function executeProposal()"];
|
||||||
|
const proposalFactory = new ethers.ContractFactory(proposalAbi, proposalBytecode, signer);
|
||||||
|
const proposalContract = await proposalFactory.deploy();
|
||||||
|
const proposalAddr = await proposalContract.getAddress();
|
||||||
|
|
||||||
|
return proposalAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
it("Governance contract should be updatable", async function () {
|
||||||
|
const someOldGovernanceImpl = "0xBa178126C28F50Ee60322a82f5EbCd6b3711e101";
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await getProxyImplAddr(governanceAddr)).to.be.not.equal(someOldGovernanceImpl);
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
|
||||||
|
await executeNewProposal(proposalAddr);
|
||||||
|
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const lastProposalId = await governance.proposalCount();
|
||||||
|
expect(await governance.state(lastProposalId)).to.be.equal(5); // executed
|
||||||
|
expect(await getProxyImplAddr(governanceAddr)).to.be.equal(someOldGovernanceImpl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Proposal count should change by one", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const lastProposal = await governance.proposalCount();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.proposalCount()).to.be.equal(lastProposal + 1n);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Stakers should be able to lock and unlock", async function () {
|
||||||
|
const signer = (await ethers.getSigners())[0];
|
||||||
|
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
|
||||||
|
const governance = await getGovernance(signer);
|
||||||
|
const torn = await getTorn(governanceSigner);
|
||||||
|
const thousandTorns = 1000n * 10n ** 18n;
|
||||||
|
const balanceBefore = await torn.balanceOf(signer.address);
|
||||||
|
const lockedBalanceBefore = await governance.lockedBalance(signer.address);
|
||||||
|
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
|
||||||
|
await torn.transfer(signer.address, thousandTorns);
|
||||||
|
const deadline = ethers.MaxUint256;
|
||||||
|
const { v, r, s } = await getPermitSignature(signer, torn, governanceAddr, thousandTorns, deadline);
|
||||||
|
await governance.lock(signer.address, thousandTorns, deadline, v, r, s);
|
||||||
|
const lockedBalanceAfter = await governance.lockedBalance(signer.address);
|
||||||
|
expect(lockedBalanceAfter - lockedBalanceBefore).to.be.equal(thousandTorns);
|
||||||
|
await governance.unlock(thousandTorns);
|
||||||
|
const balanceAfter = await torn.balanceOf(signer.address);
|
||||||
|
expect(balanceAfter - balanceBefore).to.be.equal(thousandTorns);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Staker locked balance should not change", async function () {
|
||||||
|
const me = await resolveAddr("butterfly-attractor.eth");
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const lockedBalanceBefore = await governance.lockedBalance(me);
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.lockedBalance(me)).to.be.equal(lockedBalanceBefore);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Proposals info should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const lastProposal = await governance.proposalCount();
|
||||||
|
const proposalsBefore = await Promise.all(
|
||||||
|
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.proposals(i)),
|
||||||
|
);
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalsAfter = await Promise.all(
|
||||||
|
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.proposals(i)),
|
||||||
|
);
|
||||||
|
expect(proposalsAfter).to.deep.equal(proposalsBefore);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Proposals state should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const lastProposal = await governance.proposalCount();
|
||||||
|
const proposalStatesBefore = await Promise.all(
|
||||||
|
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.state(i)),
|
||||||
|
);
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalStatesAfter = await Promise.all(
|
||||||
|
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.state(i)),
|
||||||
|
);
|
||||||
|
expect(proposalStatesAfter).to.deep.equal(proposalStatesBefore);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Quorum votes should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const quorum = await governance.QUORUM_VOTES();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.QUORUM_VOTES()).to.be.equal(quorum);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Closing period should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const closingPeriod = await governance.CLOSING_PERIOD();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.CLOSING_PERIOD()).to.be.equal(closingPeriod);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Execution delay should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const executionDelay = await governance.EXECUTION_DELAY();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.EXECUTION_DELAY()).to.be.equal(executionDelay);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Execution expiration should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const expirationPeriod = await governance.EXECUTION_EXPIRATION();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.EXECUTION_EXPIRATION()).to.be.equal(expirationPeriod);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Proposal initiation threshold should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const proposalThreshold = await governance.PROPOSAL_THRESHOLD();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.PROPOSAL_THRESHOLD()).to.be.equal(proposalThreshold);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Voting extend time should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const votingExtendTime = await governance.VOTE_EXTEND_TIME();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.VOTE_EXTEND_TIME()).to.be.equal(votingExtendTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Voting initiation delay should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const proposalVotingDelay = await governance.VOTING_DELAY();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.VOTING_DELAY()).to.be.equal(proposalVotingDelay);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Voting period should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const votingPeriod = await governance.VOTING_PERIOD();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.VOTING_PERIOD()).to.be.equal(votingPeriod);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("User vault address should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const vaultAddr = await governance.userVault();
|
||||||
|
const vaultCode = await ethers.provider.getCode(vaultAddr);
|
||||||
|
expect(vaultCode).to.be.not.equal("0x");
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
|
||||||
|
expect(await governance.userVault()).to.be.equal(vaultAddr);
|
||||||
|
expect(await ethers.provider.getCode(vaultAddr)).to.be.equal(vaultCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Torn address should not be reinitialized", async function () {
|
||||||
|
const governanceContract = await getGovernance();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governanceContract.torn()).to.be.equal(tornAddr);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Staking address should not change", async function () {
|
||||||
|
const governanceContract = await getGovernance();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governanceContract.Staking()).to.be.equal(stakingAddr);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Gas compensator address should not change", async function () {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const gasCompensatorAddr = await governance.gasCompensationVault();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
expect(await governance.gasCompensationVault()).to.be.equal(gasCompensatorAddr);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function delegate(tornAmount, signer) {
|
||||||
|
const governance = await getGovernance();
|
||||||
|
if (!tornAmount) tornAmount = await governance.QUORUM_VOTES();
|
||||||
|
if (!signer) signer = (await ethers.getSigners())[0];
|
||||||
|
const governanceContract = await getGovernance(signer);
|
||||||
|
const zer0 = "0x000000000000000000000000000000000000dEaD";
|
||||||
|
await signerLockInGov(tornAmount, signer);
|
||||||
|
await governanceContract.delegate(zer0);
|
||||||
|
|
||||||
|
return { signer, delegated: await ethers.getImpersonatedSigner(zer0), governance: governanceContract };
|
||||||
|
}
|
||||||
|
|
||||||
|
it("Delegators state should not change", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const { signer, governance, delegated } = await delegate();
|
||||||
|
expect(await governance.delegatedTo(signer.address)).to.be.equal(delegated.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delegation should work", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const { signer, governance, delegated } = await delegate();
|
||||||
|
expect(await governance.delegatedTo(signer.address)).to.be.equal(delegated.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delegator should be able to cast vote", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const gov = await getGovernance();
|
||||||
|
await gov.propose(proposalAddr, "");
|
||||||
|
const { governance, delegated, signer } = await delegate();
|
||||||
|
const proposalId = await governance.proposalCount();
|
||||||
|
await time.increase(60 * 60);
|
||||||
|
const governanceDelegated = governance.connect(delegated);
|
||||||
|
await governanceDelegated.castDelegatedVote([signer.address], proposalId, true);
|
||||||
|
await time.increase(60 * 60 * 24 * 7 + 60);
|
||||||
|
|
||||||
|
expect(await governance.state(proposalId)).to.be.equal(4); // awaiting execution
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delegator should be able to propose", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const { governance, delegated, signer } = await delegate();
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const governanceDelegated = governance.connect(delegated);
|
||||||
|
await governanceDelegated.proposeByDelegate(signer.address, proposalAddr, "");
|
||||||
|
const proposalId = await governance.proposalCount();
|
||||||
|
await time.increase(60 * 60);
|
||||||
|
await expect(governanceDelegated.castVote(proposalId, true)).to.be.revertedWith("Governance: balance is 0");
|
||||||
|
await governanceDelegated.castDelegatedVote([signer.address], proposalId, true);
|
||||||
|
await time.increase(60 * 60 * 24 * 7 + 60);
|
||||||
|
await governanceDelegated.execute(proposalId);
|
||||||
|
|
||||||
|
expect(await governance.state(proposalId)).to.be.equal(5); // executed
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Staking rewards should be distributed without problem", async function () {
|
||||||
|
const governanceContract = await getGovernance();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const stakingVault = await governanceContract.userVault();
|
||||||
|
const torn = await getTorn();
|
||||||
|
const manyTorns = 100n * 1000n * 10n ** 18n;
|
||||||
|
const signer = await signerLockInGov(manyTorns);
|
||||||
|
const signerLockedBalance = await governanceContract.lockedBalance(signer.address);
|
||||||
|
const sumStaking = await torn.balanceOf(stakingVault);
|
||||||
|
const toDistibute = 10000n * 18n ** 18n;
|
||||||
|
const stakingAddr = await governanceContract.Staking();
|
||||||
|
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
|
||||||
|
const stakingContract = await ethers.getContractAt(
|
||||||
|
require("./abi/staking.abi.json"),
|
||||||
|
stakingAddr,
|
||||||
|
governanceSigner,
|
||||||
|
);
|
||||||
|
const ratioConstant = await stakingContract.ratioConstant();
|
||||||
|
const expectedReward = (toDistibute * ((signerLockedBalance * ratioConstant) / sumStaking)) / ratioConstant;
|
||||||
|
const rewardsBeforeDistribute = await stakingContract.checkReward(signer.address);
|
||||||
|
await stakingContract.addBurnRewards(toDistibute);
|
||||||
|
const rewardsAfterDistibute = await stakingContract.checkReward(signer.address);
|
||||||
|
|
||||||
|
expect((rewardsAfterDistibute - rewardsBeforeDistribute) / 1000n).to.be.equal(expectedReward / 1000n);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Check if account voted in proposal should work correct", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const signer = await signerLockInGov("quorum");
|
||||||
|
const governance = await getGovernance(signer);
|
||||||
|
let proposalId = await governance.proposalCount();
|
||||||
|
await governance.propose(proposalAddr, "");
|
||||||
|
expect(await governance.hasAccountVoted(proposalId + 1n, signer.address)).to.be.equal(false);
|
||||||
|
proposalId = await governance.proposalCount();
|
||||||
|
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(false);
|
||||||
|
await time.increase(60 * 60);
|
||||||
|
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(false);
|
||||||
|
await governance.castVote(proposalId, true);
|
||||||
|
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Staker should be able to change vote", async function () {
|
||||||
|
const governanceContract = await getGovernance();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const halfQuorum = (await governanceContract.QUORUM_VOTES()) / 2n;
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const signer = await signerLockInGov(halfQuorum);
|
||||||
|
const lockedBalance = await governanceContract.lockedBalance(signer.address);
|
||||||
|
const governance = await getGovernance(signer);
|
||||||
|
await governance.propose(proposalAddr, "");
|
||||||
|
const proposalId = await governance.proposalCount();
|
||||||
|
await time.increase(60 * 60);
|
||||||
|
await governance.castVote(proposalId, true);
|
||||||
|
let proposalInfo = await governance.proposals(proposalId);
|
||||||
|
expect(proposalInfo.forVotes).to.be.equal(lockedBalance);
|
||||||
|
expect(proposalInfo.againstVotes).to.be.equal(0n);
|
||||||
|
|
||||||
|
await governance.castVote(proposalId, false);
|
||||||
|
proposalInfo = await governance.proposals(proposalId);
|
||||||
|
expect(proposalInfo.forVotes).to.be.equal(0n);
|
||||||
|
expect(proposalInfo.againstVotes).to.be.equal(lockedBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Delegator should not be able to vote twice in one proposal", async function () {
|
||||||
|
const governanceContract = await getGovernance();
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const halfQuorum = (await governanceContract.QUORUM_VOTES()) / 2n;
|
||||||
|
const { signer, governance, delegated } = await delegate(halfQuorum);
|
||||||
|
await governance.propose(proposalAddr, "");
|
||||||
|
const proposalId = await governance.proposalCount();
|
||||||
|
await time.increase(60 * 60);
|
||||||
|
await governance.castVote(proposalId, true);
|
||||||
|
const governanceDelegated = governance.connect(delegated);
|
||||||
|
await expect(governanceDelegated.castDelegatedVote([signer.address], proposalId, true)).to.be.revertedWith(
|
||||||
|
"Governance: voted already",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("If staker change vote in last hour voting should prolong", async function () {
|
||||||
|
await deployAndExecuteProposal();
|
||||||
|
const proposalAddr = await createValidProposal();
|
||||||
|
const signer = await signerLockInGov("quorum");
|
||||||
|
const governance = await getGovernance(signer);
|
||||||
|
await governance.propose(proposalAddr, "");
|
||||||
|
const proposalId = await governance.proposalCount();
|
||||||
|
await time.increase(60 * 10);
|
||||||
|
await governance.castVote(proposalId, true);
|
||||||
|
await time.increase(60 * 60 * 24 * 4 + 60 * 60 * 23 + 60 * 30);
|
||||||
|
let proposalInfo = await governance.proposals(proposalId);
|
||||||
|
expect(proposalInfo.extended).to.be.equal(false);
|
||||||
|
await governance.castVote(proposalId, false);
|
||||||
|
proposalInfo = await governance.proposals(proposalId);
|
||||||
|
expect(proposalInfo.extended).to.be.equal(true);
|
||||||
|
});
|
||||||
|
});
|
193
test/Proposal.js
193
test/Proposal.js
@ -2,7 +2,7 @@ const { ethers, network } = require("hardhat");
|
|||||||
const contentHash = require("content-hash");
|
const contentHash = require("content-hash");
|
||||||
const { expect, assert } = require("chai");
|
const { expect, assert } = require("chai");
|
||||||
const { time } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
|
const { time } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
|
||||||
const { deployAndExecuteProposal } = require("./utils");
|
const { deployAndExecuteProposal, getGovernance, getTorn } = require("./utils");
|
||||||
|
|
||||||
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
||||||
const sablierAddr = "0xCD18eAa163733Da39c232722cBC4E8940b1D8888";
|
const sablierAddr = "0xCD18eAa163733Da39c232722cBC4E8940b1D8888";
|
||||||
@ -56,14 +56,191 @@ describe("Proposal results check", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getStreamArgs() {
|
async function getEvents(contract, eventName, fromBlock = 0) {
|
||||||
const sablier = await getSablierContract();
|
const filter = contract.filters[eventName];
|
||||||
const filter = sablier.filters.CreateStream;
|
const events = await contract.queryFilter(filter, fromBlock); //config.networks.hardhat.forking.blockNumber - 1
|
||||||
const events = await sablier.queryFilter(filter, config.networks.hardhat.forking.blockNumber - 1);
|
return events;
|
||||||
return events[0].args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("Proposal executed", async function () {
|
function normalizeAirdropAmount(amount) {
|
||||||
await deployAndExecuteProposal();
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
return amount - (amount % BigInt(halfYear));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("Airdrop recipients storage contracts should deployed successfully", async function () {
|
||||||
|
const { airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
expect(await ethers.provider.getCode(await airdropRecipientsContract.getAddress())).to.not.be.equal("0x");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipients deposits sum should be almost equal 1m", async function () {
|
||||||
|
const { airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const airdropSum = await recipients.reduce((acc, r) => acc + r[1], 0n);
|
||||||
|
const decimals = 10n ** 18n;
|
||||||
|
const millionTorn = 1000n * 1000n * decimals;
|
||||||
|
|
||||||
|
expect(airdropSum).to.greaterThan(millionTorn - 1n * decimals);
|
||||||
|
expect(airdropSum).to.lessThan(millionTorn + 1n * decimals);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient deposit should calculated correctly", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const governance = await getGovernance();
|
||||||
|
const recipientLockedBalance = await governance.lockedBalance(someRecipient);
|
||||||
|
|
||||||
|
const decimals = 10n ** 18n;
|
||||||
|
const millionTorn = 1000n * 1000n * decimals;
|
||||||
|
const { airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const airdropLockedSum = await recipients.reduce((acc, r) => acc + r[2], 0n);
|
||||||
|
const selectedRecipient = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const expectedRecipientAirdrop =
|
||||||
|
(recipientLockedBalance * ((millionTorn * decimals) / airdropLockedSum)) / decimals;
|
||||||
|
|
||||||
|
expect(selectedRecipient[1]).to.be.equal(expectedRecipientAirdrop);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop should start", async function () {
|
||||||
|
const { airdropContract } = await deployAndExecuteProposal();
|
||||||
|
const filter = airdropContract.filters.CreateAirdrop;
|
||||||
|
const events = await airdropContract.queryFilter(filter);
|
||||||
|
const args = events[0].args;
|
||||||
|
expect(args[1] - args[0]).to.be.equal(180 * 24 * 60 * 60);
|
||||||
|
expect(args[2]).to.be.equal([...require("../data/airdropRecipients.json")].length);
|
||||||
|
expect(args[3]).to.be.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should be able to withdraw funds", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
const normalizedAmount = recipientAirdropAmount - (recipientAirdropAmount % BigInt(halfYear));
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(halfYear);
|
||||||
|
const torn = await getTorn();
|
||||||
|
const recipientBalance = await torn.balanceOf(someRecipient);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await connectedAirdrop.withdrawFromStream(recipientStreamId, normalizedAmount);
|
||||||
|
|
||||||
|
expect(await torn.balanceOf(someRecipient)).to.be.equal(recipientBalance + normalizedAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should not be able to withdraw all funds early", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
const normalizedAmount = normalizeAirdropAmount(recipientAirdropAmount);
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(halfYear / 2);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await expect(connectedAirdrop.withdrawFromStream(recipientStreamId, normalizedAmount)).to.be.revertedWith(
|
||||||
|
"amount exceeds the available balance",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should not be able to withdraw funds if his stake balance is lower than initial", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
const normalizedAmount = normalizeAirdropAmount(recipientAirdropAmount);
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(halfYear);
|
||||||
|
const governance = await getGovernance(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
governance.unlock(1000n * 10n ** 18n);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await expect(connectedAirdrop.withdrawFromStream(recipientStreamId, normalizedAmount)).to.be.revertedWith(
|
||||||
|
"not enough locked tokens in governance",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should be able to withdraw part of funds", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const quarter = 90 * 24 * 60 * 60;
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(quarter);
|
||||||
|
const torn = await getTorn();
|
||||||
|
const recipientBalance = await torn.balanceOf(someRecipient);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await connectedAirdrop.withdrawFromStream(recipientStreamId, recipientAirdropAmount / 4n);
|
||||||
|
expect(await torn.balanceOf(someRecipient)).to.be.equal(recipientBalance + recipientAirdropAmount / 4n);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should not be able to withdraw funds if stream is canceled", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
const normalizedAmount = recipientAirdropAmount - (recipientAirdropAmount % BigInt(halfYear));
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(halfYear);
|
||||||
|
await airdropContract.cancelStream(recipientStreamId);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await expect(connectedAirdrop.withdrawFromStream(recipientStreamId, normalizedAmount)).to.be.revertedWith(
|
||||||
|
"stream does not exist",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop recipient should not be able to withdraw funds if airdrop is canceled", async function () {
|
||||||
|
const someRecipient = await resolveAddr("butterfly-effect.eth");
|
||||||
|
const { airdropContract, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const selectedRecipientInfo = recipients.find((r) => r[0] === someRecipient);
|
||||||
|
const recipientAirdropAmount = selectedRecipientInfo[1];
|
||||||
|
const halfYear = 180 * 24 * 60 * 60;
|
||||||
|
const normalizedAmount = normalizeAirdropAmount(recipientAirdropAmount);
|
||||||
|
const events = await getEvents(airdropContract, "CreateStream");
|
||||||
|
const recipientStreamId = events.find((e) => e.args[1] === someRecipient).args[0];
|
||||||
|
|
||||||
|
await time.increase(halfYear);
|
||||||
|
await airdropContract.cancelAirdrop(1, recipients.length);
|
||||||
|
const connectedAirdrop = airdropContract.connect(await ethers.getImpersonatedSigner(someRecipient));
|
||||||
|
await expect(connectedAirdrop.withdrawFromStream(recipientStreamId, normalizedAmount)).to.be.revertedWith(
|
||||||
|
"stream does not exist",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Airdrop cancelation should return tokens to governance", async function () {
|
||||||
|
const torn = await getTorn();
|
||||||
|
const tornDecimals = 10n ** 18n;
|
||||||
|
const govBalanceBeforeProposal = await torn.balanceOf(governanceAddr);
|
||||||
|
const { airdropContract, airdropAddr, airdropRecipientsContract } = await deployAndExecuteProposal();
|
||||||
|
const recipients = await airdropRecipientsContract.getAirdropRecipients();
|
||||||
|
const airdropSum = await recipients.reduce((acc, r) => acc + normalizeAirdropAmount(r[1]), 0n);
|
||||||
|
const governanceBalance = await torn.balanceOf(governanceAddr);
|
||||||
|
const airdropContractBalance = await torn.balanceOf(airdropAddr);
|
||||||
|
await airdropContract.cancelAirdrop(1, recipients.length);
|
||||||
|
|
||||||
|
expect(await torn.balanceOf(governanceAddr)).to.be.equal(governanceBalance + airdropSum);
|
||||||
|
expect(await torn.balanceOf(airdropAddr)).to.be.equal(airdropContractBalance - airdropSum);
|
||||||
|
expect((await torn.balanceOf(airdropAddr)) < 1n * tornDecimals);
|
||||||
|
|
||||||
|
await airdropContract.withdrawFunds();
|
||||||
|
expect(await torn.balanceOf(airdropAddr)).to.be.equal(0);
|
||||||
|
expect(await torn.balanceOf(governanceAddr)).to.be.equal(govBalanceBeforeProposal);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,374 +1,395 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"payable": false,
|
"payable": false,
|
||||||
"stateMutability": "nonpayable",
|
"stateMutability": "nonpayable",
|
||||||
"type": "constructor"
|
"type": "constructor"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"anonymous": false,
|
"anonymous": false,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": true,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "streamId",
|
"name": "streamId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": true,
|
||||||
"internalType": "address",
|
"internalType": "address",
|
||||||
"name": "recipient",
|
"name": "recipient",
|
||||||
"type": "address"
|
"type": "address"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "remainingBalance",
|
"name": "remainingBalance",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"name": "CancelStream",
|
"name": "CancelStream",
|
||||||
"type": "event"
|
"type": "event"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"anonymous": false,
|
"anonymous": false,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "streamId",
|
"name": "startTime",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": false,
|
||||||
"internalType": "address",
|
"internalType": "uint256",
|
||||||
"name": "recipient",
|
"name": "stopTime",
|
||||||
"type": "address"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "deposit",
|
"name": "recipientsAmount",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "startTime",
|
"name": "firstStreamId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "stopTime",
|
"name": "lastStreamId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"name": "CreateStream",
|
"name": "CreateAirdrop",
|
||||||
"type": "event"
|
"type": "event"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"anonymous": false,
|
"anonymous": false,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": true,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "streamId",
|
"name": "streamId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": true,
|
"indexed": true,
|
||||||
"internalType": "address",
|
"internalType": "address",
|
||||||
"name": "recipient",
|
"name": "recipient",
|
||||||
"type": "address"
|
"type": "address"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"indexed": false,
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "amount",
|
"name": "deposit",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"name": "WithdrawFromStream",
|
"indexed": false,
|
||||||
"type": "event"
|
"internalType": "uint256",
|
||||||
},
|
"name": "initialLockedBalance",
|
||||||
{
|
"type": "uint256"
|
||||||
"constant": true,
|
},
|
||||||
"inputs": [
|
{
|
||||||
{
|
"indexed": false,
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "streamId",
|
"name": "startTime",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"internalType": "address",
|
"indexed": false,
|
||||||
"name": "who",
|
"internalType": "uint256",
|
||||||
"type": "address"
|
"name": "stopTime",
|
||||||
}
|
"type": "uint256"
|
||||||
],
|
}
|
||||||
"name": "balanceOf",
|
],
|
||||||
"outputs": [
|
"name": "CreateStream",
|
||||||
{
|
"type": "event"
|
||||||
"internalType": "uint256",
|
},
|
||||||
"name": "balance",
|
{
|
||||||
"type": "uint256"
|
"anonymous": false,
|
||||||
}
|
"inputs": [
|
||||||
],
|
{
|
||||||
"payable": false,
|
"indexed": true,
|
||||||
"stateMutability": "view",
|
"internalType": "uint256",
|
||||||
"type": "function"
|
"name": "streamId",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
},
|
||||||
"constant": false,
|
{
|
||||||
"inputs": [],
|
"indexed": true,
|
||||||
"name": "cancelAirdrop",
|
"internalType": "address",
|
||||||
"outputs": [
|
"name": "recipient",
|
||||||
{
|
"type": "address"
|
||||||
"internalType": "bool",
|
},
|
||||||
"name": "",
|
{
|
||||||
"type": "bool"
|
"indexed": false,
|
||||||
}
|
"internalType": "uint256",
|
||||||
],
|
"name": "amount",
|
||||||
"payable": false,
|
"type": "uint256"
|
||||||
"stateMutability": "nonpayable",
|
}
|
||||||
"type": "function"
|
],
|
||||||
},
|
"name": "WithdrawFromStream",
|
||||||
{
|
"type": "event"
|
||||||
"constant": false,
|
},
|
||||||
"inputs": [
|
{
|
||||||
{
|
"constant": true,
|
||||||
"internalType": "uint256",
|
"inputs": [
|
||||||
"name": "streamId",
|
{
|
||||||
"type": "uint256"
|
"internalType": "uint256",
|
||||||
}
|
"name": "streamId",
|
||||||
],
|
"type": "uint256"
|
||||||
"name": "cancelStream",
|
},
|
||||||
"outputs": [
|
{
|
||||||
{
|
"internalType": "address",
|
||||||
"internalType": "bool",
|
"name": "who",
|
||||||
"name": "",
|
"type": "address"
|
||||||
"type": "bool"
|
}
|
||||||
}
|
],
|
||||||
],
|
"name": "balanceOf",
|
||||||
"payable": false,
|
"outputs": [
|
||||||
"stateMutability": "nonpayable",
|
{
|
||||||
"type": "function"
|
"internalType": "uint256",
|
||||||
},
|
"name": "balance",
|
||||||
{
|
"type": "uint256"
|
||||||
"constant": false,
|
}
|
||||||
"inputs": [
|
],
|
||||||
{
|
"payable": false,
|
||||||
"components": [
|
"stateMutability": "view",
|
||||||
{
|
"type": "function"
|
||||||
"internalType": "address",
|
},
|
||||||
"name": "recipient",
|
{
|
||||||
"type": "address"
|
"constant": false,
|
||||||
},
|
"inputs": [
|
||||||
{
|
{
|
||||||
"internalType": "uint256",
|
"internalType": "uint256",
|
||||||
"name": "deposit",
|
"name": "firstStreamId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"internalType": "struct Types.Recipient[]",
|
"internalType": "uint256",
|
||||||
"name": "recipients",
|
"name": "lastStreamId",
|
||||||
"type": "tuple[]"
|
"type": "uint256"
|
||||||
},
|
}
|
||||||
{
|
],
|
||||||
"internalType": "uint256",
|
"name": "cancelAirdrop",
|
||||||
"name": "startTime",
|
"outputs": [
|
||||||
"type": "uint256"
|
{
|
||||||
},
|
"internalType": "bool",
|
||||||
{
|
"name": "",
|
||||||
"internalType": "uint256",
|
"type": "bool"
|
||||||
"name": "stopTime",
|
}
|
||||||
"type": "uint256"
|
],
|
||||||
}
|
"payable": false,
|
||||||
],
|
"stateMutability": "nonpayable",
|
||||||
"name": "createAirdrop",
|
"type": "function"
|
||||||
"outputs": [
|
},
|
||||||
{
|
{
|
||||||
"internalType": "bool",
|
"constant": false,
|
||||||
"name": "",
|
"inputs": [
|
||||||
"type": "bool"
|
{
|
||||||
}
|
"internalType": "uint256",
|
||||||
],
|
"name": "streamId",
|
||||||
"payable": false,
|
"type": "uint256"
|
||||||
"stateMutability": "nonpayable",
|
}
|
||||||
"type": "function"
|
],
|
||||||
},
|
"name": "cancelStream",
|
||||||
{
|
"outputs": [
|
||||||
"constant": false,
|
{
|
||||||
"inputs": [
|
"internalType": "bool",
|
||||||
{
|
"name": "",
|
||||||
"internalType": "address",
|
"type": "bool"
|
||||||
"name": "recipient",
|
}
|
||||||
"type": "address"
|
],
|
||||||
},
|
"payable": false,
|
||||||
{
|
"stateMutability": "nonpayable",
|
||||||
"internalType": "uint256",
|
"type": "function"
|
||||||
"name": "deposit",
|
},
|
||||||
"type": "uint256"
|
{
|
||||||
},
|
"constant": false,
|
||||||
{
|
"inputs": [
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "startTime",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "startTime",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "stopTime",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "stopTime",
|
||||||
}
|
"type": "uint256"
|
||||||
],
|
},
|
||||||
"name": "createStream",
|
{
|
||||||
"outputs": [
|
"internalType": "address",
|
||||||
{
|
"name": "recipientStorage",
|
||||||
"internalType": "uint256",
|
"type": "address"
|
||||||
"name": "",
|
}
|
||||||
"type": "uint256"
|
],
|
||||||
}
|
"name": "createAirdrop",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "nonpayable",
|
"internalType": "bool",
|
||||||
"type": "function"
|
"name": "",
|
||||||
},
|
"type": "bool"
|
||||||
{
|
}
|
||||||
"constant": true,
|
],
|
||||||
"inputs": [
|
"payable": false,
|
||||||
{
|
"stateMutability": "nonpayable",
|
||||||
"internalType": "uint256",
|
"type": "function"
|
||||||
"name": "streamId",
|
},
|
||||||
"type": "uint256"
|
{
|
||||||
}
|
"constant": true,
|
||||||
],
|
"inputs": [
|
||||||
"name": "deltaOf",
|
{
|
||||||
"outputs": [
|
"internalType": "uint256",
|
||||||
{
|
"name": "streamId",
|
||||||
"internalType": "uint256",
|
"type": "uint256"
|
||||||
"name": "delta",
|
}
|
||||||
"type": "uint256"
|
],
|
||||||
}
|
"name": "deltaOf",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "view",
|
"internalType": "uint256",
|
||||||
"type": "function"
|
"name": "delta",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
}
|
||||||
"constant": true,
|
],
|
||||||
"inputs": [
|
"payable": false,
|
||||||
{
|
"stateMutability": "view",
|
||||||
"internalType": "uint256",
|
"type": "function"
|
||||||
"name": "streamId",
|
},
|
||||||
"type": "uint256"
|
{
|
||||||
}
|
"constant": true,
|
||||||
],
|
"inputs": [
|
||||||
"name": "getStream",
|
{
|
||||||
"outputs": [
|
"internalType": "uint256",
|
||||||
{
|
"name": "streamId",
|
||||||
"internalType": "address",
|
"type": "uint256"
|
||||||
"name": "recipient",
|
}
|
||||||
"type": "address"
|
],
|
||||||
},
|
"name": "getStream",
|
||||||
{
|
"outputs": [
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "deposit",
|
"internalType": "address",
|
||||||
"type": "uint256"
|
"name": "recipient",
|
||||||
},
|
"type": "address"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "startTime",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "deposit",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "stopTime",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "startTime",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "remainingBalance",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "stopTime",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "ratePerSecond",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "remainingBalance",
|
||||||
}
|
"type": "uint256"
|
||||||
],
|
},
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "view",
|
"internalType": "uint256",
|
||||||
"type": "function"
|
"name": "ratePerSecond",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
}
|
||||||
"constant": true,
|
],
|
||||||
"inputs": [],
|
"payable": false,
|
||||||
"name": "nextStreamId",
|
"stateMutability": "view",
|
||||||
"outputs": [
|
"type": "function"
|
||||||
{
|
},
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "",
|
"constant": true,
|
||||||
"type": "uint256"
|
"inputs": [],
|
||||||
}
|
"name": "nextStreamId",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "view",
|
"internalType": "uint256",
|
||||||
"type": "function"
|
"name": "",
|
||||||
},
|
"type": "uint256"
|
||||||
{
|
}
|
||||||
"constant": true,
|
],
|
||||||
"inputs": [],
|
"payable": false,
|
||||||
"name": "torn",
|
"stateMutability": "view",
|
||||||
"outputs": [
|
"type": "function"
|
||||||
{
|
},
|
||||||
"internalType": "contract IERC20",
|
{
|
||||||
"name": "",
|
"constant": true,
|
||||||
"type": "address"
|
"inputs": [],
|
||||||
}
|
"name": "torn",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "view",
|
"internalType": "contract IERC20",
|
||||||
"type": "function"
|
"name": "",
|
||||||
},
|
"type": "address"
|
||||||
{
|
}
|
||||||
"constant": true,
|
],
|
||||||
"inputs": [],
|
"payable": false,
|
||||||
"name": "tornadoGovernance",
|
"stateMutability": "view",
|
||||||
"outputs": [
|
"type": "function"
|
||||||
{
|
},
|
||||||
"internalType": "address",
|
{
|
||||||
"name": "",
|
"constant": true,
|
||||||
"type": "address"
|
"inputs": [],
|
||||||
}
|
"name": "tornadoGovernance",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "view",
|
"internalType": "address",
|
||||||
"type": "function"
|
"name": "",
|
||||||
},
|
"type": "address"
|
||||||
{
|
}
|
||||||
"constant": false,
|
],
|
||||||
"inputs": [
|
"payable": false,
|
||||||
{
|
"stateMutability": "view",
|
||||||
"internalType": "uint256",
|
"type": "function"
|
||||||
"name": "streamId",
|
},
|
||||||
"type": "uint256"
|
{
|
||||||
},
|
"constant": false,
|
||||||
{
|
"inputs": [
|
||||||
"internalType": "uint256",
|
{
|
||||||
"name": "amount",
|
"internalType": "uint256",
|
||||||
"type": "uint256"
|
"name": "streamId",
|
||||||
}
|
"type": "uint256"
|
||||||
],
|
},
|
||||||
"name": "withdrawFromStream",
|
{
|
||||||
"outputs": [
|
"internalType": "uint256",
|
||||||
{
|
"name": "amount",
|
||||||
"internalType": "bool",
|
"type": "uint256"
|
||||||
"name": "",
|
}
|
||||||
"type": "bool"
|
],
|
||||||
}
|
"name": "withdrawFromStream",
|
||||||
],
|
"outputs": [
|
||||||
"payable": false,
|
{
|
||||||
"stateMutability": "nonpayable",
|
"internalType": "bool",
|
||||||
"type": "function"
|
"name": "",
|
||||||
}
|
"type": "bool"
|
||||||
]
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "withdrawFunds",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "",
|
||||||
|
"type": "bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
1
test/abi/staking.abi.json
Normal file
1
test/abi/staking.abi.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"inputs":[{"internalType":"address","name":"governanceAddress","type":"address"},{"internalType":"address","name":"tornAddress","type":"address"},{"internalType":"address","name":"_relayerRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardsClaimed","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"RewardsUpdated","type":"event"},{"inputs":[],"name":"Governance","outputs":[{"internalType":"contract ITornadoGovernance","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accumulatedRewardPerTorn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accumulatedRewardRateOnLastUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accumulatedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addBurnRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"domains","type":"bytes32[]"}],"name":"bulkResolve","outputs":[{"internalType":"address[]","name":"result","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"checkReward","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ratioConstant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"relayerRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"torn","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amountLockedBeforehand","type":"uint256"}],"name":"updateRewardsOnLockedBalanceChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTorn","outputs":[],"stateMutability":"nonpayable","type":"function"}]
|
@ -8,7 +8,7 @@ const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
|
|||||||
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
|
||||||
|
|
||||||
async function getManyEth(addr) {
|
async function getManyEth(addr) {
|
||||||
await network.provider.send("hardhat_setBalance", [addr, "0x111166630153555558483537"]);
|
await network.provider.send("hardhat_setBalance", [addr, "0x111166630153483537"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPermitSignature(signer, tokenContract, spender, value, deadline) {
|
async function getPermitSignature(signer, tokenContract, spender, value, deadline) {
|
||||||
@ -117,6 +117,7 @@ async function getAirdrop(airdropAddr, signer) {
|
|||||||
|
|
||||||
async function signerLockInGov(tornAmount, signer) {
|
async function signerLockInGov(tornAmount, signer) {
|
||||||
if (!signer) signer = (await ethers.getSigners())[0];
|
if (!signer) signer = (await ethers.getSigners())[0];
|
||||||
|
if (tornAmount == "quorum") tornAmount = await (await getGovernance()).QUORUM_VOTES();
|
||||||
|
|
||||||
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
|
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
|
||||||
const torn = await getTorn(governanceSigner);
|
const torn = await getTorn(governanceSigner);
|
||||||
@ -140,6 +141,7 @@ async function executeNewProposal(proposalAddr) {
|
|||||||
await time.increase(60 * 60);
|
await time.increase(60 * 60);
|
||||||
await governanceContract.castVote(proposalId, true);
|
await governanceContract.castVote(proposalId, true);
|
||||||
await time.increase(60 * 60 * 24 * 7 + 60);
|
await time.increase(60 * 60 * 24 * 7 + 60);
|
||||||
|
await getManyEth(stakerSigner.address);
|
||||||
await governanceContract.execute(proposalId);
|
await governanceContract.execute(proposalId);
|
||||||
await time.increase(60 * 60 * 24 * 4);
|
await time.increase(60 * 60 * 24 * 4);
|
||||||
await governanceContract.unlock(quorum);
|
await governanceContract.unlock(quorum);
|
||||||
@ -148,18 +150,22 @@ async function executeNewProposal(proposalAddr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deployAndExecuteProposal() {
|
async function deployAndExecuteProposal() {
|
||||||
|
const recipientsFactory = await ethers.getContractFactory("AirdropRecipients");
|
||||||
|
const airdropRecipientsContract = await recipientsFactory.deploy(require("../data/airdropRecipients.json"));
|
||||||
|
const recipientStorageAddr = await airdropRecipientsContract.getAddress();
|
||||||
|
|
||||||
const airdropFactory = await ethers.getContractFactory("SablierAirdrop");
|
const airdropFactory = await ethers.getContractFactory("SablierAirdrop");
|
||||||
const airdrop = await airdropFactory.deploy();
|
const airdrop = await airdropFactory.deploy();
|
||||||
const airdropAddr = await airdrop.getAddress();
|
const airdropAddr = await airdrop.getAddress();
|
||||||
|
|
||||||
const proposalFactory = await ethers.getContractFactory("Proposal");
|
const proposalFactory = await ethers.getContractFactory("Proposal");
|
||||||
const proposal = await proposalFactory.deploy(airdropAddr);
|
const proposal = await proposalFactory.deploy(airdropAddr, recipientStorageAddr);
|
||||||
const deployedProposalAddr = await proposal.getAddress();
|
const deployedProposalAddr = await proposal.getAddress();
|
||||||
|
|
||||||
await executeNewProposal(deployedProposalAddr);
|
await executeNewProposal(deployedProposalAddr);
|
||||||
|
|
||||||
const airdropContract = await getAirdrop(airdropAddr);
|
const airdropContract = await getAirdrop(airdropAddr, await ethers.getImpersonatedSigner(governanceAddr));
|
||||||
return { airdropAddr, airdropContract };
|
return { airdropAddr, airdropContract, airdropRecipientsContract };
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
Loading…
Reference in New Issue
Block a user