Skip to content

Commit

Permalink
🧪 Adapt KDAO tests
Browse files Browse the repository at this point in the history
  • Loading branch information
KimlikDAO-bot committed May 1, 2024
1 parent 2861fcd commit cf01704
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 31 deletions.
8 changes: 1 addition & 7 deletions test/zksync/KDAO.snapshot.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ import {
VOTING
} from "interfaces/kimlikdao/addresses.sol";
import {amountAddrFrom} from "interfaces/types/amountAddr.sol";
import {computeCreateAddress as computeZkSyncCreateAddress} from "interfaces/zksync/IZkSync.sol";
import {KDAO} from "zksync/KDAO.sol";
import {KDAOLocked} from "zksync/KDAOLocked.sol";

contract KDAOSnapshotTest is Test {
KDAO private kdao;
KDAOLocked private kdaol;

function mintAll(uint256 amount) public {
function mintAll(uint256 amount) internal {
vm.startPrank(VOTING);
for (uint256 i = 1; i <= 20; ++i) {
kdao.mint(amountAddrFrom(amount, vm.addr(i)));
Expand All @@ -38,11 +37,6 @@ contract KDAOSnapshotTest is Test {
mintAll(1e12);
}

function testAddressConsistency() external pure {
assertEq(computeZkSyncCreateAddress(KDAO_LOCKED_DEPLOYER, 0), KDAO_LOCKED);
assertEq(computeZkSyncCreateAddress(KDAO_ZKSYNC_DEPLOYER, 0), KDAO_ZKSYNC);
}

function testSnapshot0() external {
vm.prank(vm.addr(1));
kdao.transfer(vm.addr(2), 250_000e6);
Expand Down
137 changes: 129 additions & 8 deletions test/zksync/KDAO.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,48 @@

pragma solidity ^0.8.0;

import {Test} from "forge-std/Test.sol";
import {Test, console} from "forge-std/Test.sol";
import {DistroStage} from "interfaces/kimlikdao/IDistroStage.sol";
import {
DEV_FUND,
KDAO_LOCKED,
KDAO_LOCKED_DEPLOYER,
KDAO_ZKSYNC,
KDAO_ZKSYNC_DEPLOYER,
PROTOCOL_FUND_ZKSYNC,
VOTING
} from "interfaces/kimlikdao/addresses.sol";
import {amountAddrFrom} from "interfaces/types/amountAddr.sol";
import {uint48x2From} from "interfaces/types/uint48x2.sol";
import {computeCreateAddress as computeZkSyncCreateAddress} from "interfaces/zksync/IZkSync.sol";
import {KDAO} from "zksync/KDAO.sol";
import {KDAOLocked} from "zksync/KDAOLocked.sol";

contract KDAOTest is Test {
KDAO private kdao;
KDAOLocked private kdaol;

function mintAll(uint256 amount) public {
vm.startPrank(VOTING);
function mintAll(uint256 amount, address minter) internal {
vm.startPrank(minter);
for (uint256 i = 1; i <= 20; ++i) {
kdao.mint(amountAddrFrom(amount, vm.addr(i)));
}
vm.stopPrank();
}

function setUp() public {
function setUp() external {
vm.etch(KDAO_LOCKED, type(KDAOLocked).runtimeCode);
kdaol = KDAOLocked(KDAO_LOCKED);

vm.etch(KDAO_ZKSYNC, type(KDAO).runtimeCode);
kdao = KDAO(KDAO_ZKSYNC);

mintAll(1e12);
mintAll(1e12, VOTING);
}

function testAddressConsistency() external pure {
assertEq(computeZkSyncCreateAddress(KDAO_LOCKED_DEPLOYER, 0), KDAO_LOCKED);
assertEq(computeZkSyncCreateAddress(KDAO_ZKSYNC_DEPLOYER, 0), KDAO_ZKSYNC);
}

function testDomainSeparator() external view {
Expand All @@ -54,7 +63,7 @@ contract KDAOTest is Test {
);
}

function testAuthentication() public {
function testAuthentication() external {
vm.expectRevert();
kdao.snapshot0();
vm.expectRevert();
Expand Down Expand Up @@ -82,14 +91,14 @@ contract KDAOTest is Test {
vm.stopPrank();
}

function testTransfer() public {
function testTransfer() external {
vm.prank(vm.addr(1));
kdao.transfer(vm.addr(2), 250_000e6);
assertEq(kdao.balanceOf(vm.addr(1)), 0);
assertEq(kdao.balanceOf(vm.addr(2)), 500_000e6);
}

function testTransferFrom() public {
function testTransferFrom() external {
vm.prank(vm.addr(1));
kdao.approve(vm.addr(3), 250_000e6);

Expand All @@ -99,4 +108,116 @@ contract KDAOTest is Test {
assertEq(kdao.balanceOf(vm.addr(1)), 0);
assertEq(kdao.balanceOf(vm.addr(2)), 500_000e6);
}

function testProtocolAuthentication() external {
vm.expectRevert();
kdao.mint(amountAddrFrom(1, vm.addr(1)));

vm.expectRevert();
kdao.incrementDistroStage(DistroStage.Presale2);
}

function testSnapshotAuthentication() external {
vm.expectRevert();
kdao.snapshot0();

vm.expectRevert();
kdao.snapshot1();

vm.expectRevert();
kdao.snapshot2();

vm.startPrank(VOTING);
kdao.snapshot0();
kdao.snapshot1();
kdao.snapshot2();
vm.stopPrank();
}

function testShouldCompleteAllRounds() external {
// 1M each, 250k unlocked
assertEq(kdao.totalSupply(), 20e12);
assertEq(kdaol.totalSupply(), 15e12);

vm.prank(vm.addr(1));
kdao.transfer(vm.addr(2), 250_000e6);

// vm.addr(1): 0, 750k
// vm.addr(2): 500k, 750k
assertEq(kdao.balanceOf(vm.addr(1)), 0);
assertEq(kdaol.balanceOf(vm.addr(1)), 750_000e6);
assertEq(kdao.balanceOf(vm.addr(2)), 500_000e6);
assertEq(kdaol.balanceOf(vm.addr(2)), 750_000e6);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.Presale2);
mintAll(1e12, VOTING);
// vm.addr(1) 250k, 1_500k
// vm.addr(2) 750k, 1_500k
assertEq(kdao.totalSupply(), 40e12);
assertEq(kdaol.totalSupply(), 30e12);

assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6);
assertEq(kdaol.balanceOf(vm.addr(1)), 1_500_000e6);
assertEq(kdao.balanceOf(vm.addr(2)), 750_000e6);
assertEq(kdaol.balanceOf(vm.addr(2)), 1_500_000e6);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.ProtocolSaleStart);

assertEq(kdao.totalSupply(), 60e12);
assertEq(kdaol.totalSupply(), 30e12);
assertEq(kdao.balanceOf(PROTOCOL_FUND_ZKSYNC), 20e12);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.ProtocolSaleEnd);

kdaol.unlock(vm.addr(1));
kdaol.unlock(vm.addr(2));

assertEq(kdao.balanceOf(vm.addr(1)), 1_000_000e6);
assertEq(kdao.balanceOf(vm.addr(2)), 1_500_000e6);

kdaol.unlockAllEven();

assertEq(kdaol.balanceOf(vm.addr(1)), 750_000e6);
assertEq(kdaol.balanceOf(vm.addr(2)), 750_000e6);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.ProtocolAMMStart);

assertEq(kdao.totalSupply(), 80e12);
assertEq(kdaol.totalSupply(), 15e12);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.Presale2Unlock);

kdaol.unlockAllOdd();

assertEq(kdao.totalSupply(), 80e12);
assertEq(kdaol.totalSupply(), 0e12);

vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.FinalMint);
mintAll(1e12, DEV_FUND);

assertEq(kdaol.unlock(vm.addr(1)), false);

assertEq(kdaol.balanceOf(vm.addr(1)), 750e9);

vm.warp(1925097600);
vm.prank(VOTING);
kdao.incrementDistroStage(DistroStage.FinalUnlock);
kdaol.unlock(vm.addr(1));

assertEq(kdaol.balanceOf(vm.addr(1)), 0);

kdaol.unlockAllEven();

assertEq(kdaol.balanceOf(vm.addr(12)), 0);
assertEq(kdaol.balanceOf(vm.addr(14)), 0);
assertEq(kdaol.balanceOf(vm.addr(17)), 0);

assertEq(kdao.balanceOf(address(kdaol)), kdaol.totalSupply());
}
}
55 changes: 40 additions & 15 deletions zksync/KDAOLocked.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {IERC20} from "interfaces/erc/IERC20.sol";
import {DistroStage, IDistroStage} from "interfaces/kimlikdao/IDistroStage.sol";
import {KDAO_ZKSYNC} from "interfaces/kimlikdao/addresses.sol";
import {KDAO_ETHEREUM, PROTOCOL_FUND, VOTING} from "interfaces/kimlikdao/addresses.sol";
import {uint128x2} from "interfaces/types/uint128x2.sol";
import {uint48x2} from "interfaces/types/uint48x2.sol";

/**
* @title KDAO-l: Locked KimlikDAO Token
Expand All @@ -25,14 +25,6 @@ import {uint128x2} from "interfaces/types/uint128x2.sol";
* (I4) hi(balance[a]) > 0 => accounts1.includes(a)
*/
contract KDAOLocked is IERC20 {
uint256 public override totalSupply;

mapping(address => uint128x2) private balances;
// Split Presale2 accounts out, so that even if we can't unlock them in
// one shot due to gas limit, we can still unlock others in one shot.
address[] private addrs0;
address[] private addrs1;

function name() external pure override returns (string memory) {
return "Locked KDAO";
}
Expand All @@ -45,10 +37,26 @@ contract KDAOLocked is IERC20 {
return 6;
}

///////////////////////////////////////////////////////////////////////////
//
// IERC20 balance fields and methods + additional supply methods
//
///////////////////////////////////////////////////////////////////////////

uint256 public override totalSupply;

mapping(address => uint48x2) private balances;

function balanceOf(address addr) external view override returns (uint256) {
return balances[addr].sum();
}

///////////////////////////////////////////////////////////////////////////
//
// IERC20 transfer methods
//
///////////////////////////////////////////////////////////////////////////

function transfer(address to, uint256) external override returns (bool) {
if (to == address(this)) return unlock(msg.sender);
return false;
Expand All @@ -58,6 +66,12 @@ contract KDAOLocked is IERC20 {
return false;
}

///////////////////////////////////////////////////////////////////////////
//
// IERC20 allowance fields and methods
//
///////////////////////////////////////////////////////////////////////////

function allowance(address, address) external pure override returns (uint256) {
return 0;
}
Expand All @@ -72,14 +86,19 @@ contract KDAOLocked is IERC20 {
//
///////////////////////////////////////////////////////////////////////////

// Split Presale2 accounts out, so that even if we can't unlock them in
// one shot due to gas limit, we can still unlock others in one shot.
address[] private addrs0;
address[] private addrs1;

function mint(address addr, uint256 amount, DistroStage stage) external {
require(msg.sender == KDAO_ZKSYNC);
if (uint256(stage) & 1 == 0) {
addrs0.push(addr);
balances[addr] = balances[addr].incLo(amount);
} else {
addrs1.push(addr);
balances[addr] == balances[addr].incHi(amount);
balances[addr] = balances[addr].incHi(amount);
}
unchecked {
totalSupply += amount;
Expand All @@ -90,7 +109,7 @@ contract KDAOLocked is IERC20 {
function unlock(address addr) public returns (bool) {
DistroStage stage = IDistroStage(KDAO_ZKSYNC).distroStage();
uint256 unlocked;
uint128x2 balance = balances[addr];
uint48x2 balance = balances[addr];
if (stage >= DistroStage.ProtocolSaleEnd && stage != DistroStage.FinalMint) {
unchecked {
unlocked += balance.lo();
Expand All @@ -117,13 +136,16 @@ contract KDAOLocked is IERC20 {

function unlockAllEven() external {
DistroStage stage = IDistroStage(KDAO_ZKSYNC).distroStage();
require(stage >= DistroStage.ProtocolSaleEnd && stage != DistroStage.FinalMint, "KDAO-l: Not matured");
require(
stage >= DistroStage.ProtocolSaleEnd && stage != DistroStage.FinalMint,
"KDAO-l: Not matured"
);
unchecked {
uint256 length = addrs0.length;
uint256 totalUnlocked;
for (uint256 i = 0; i < length; ++i) {
address addr = addrs0[i];
uint128x2 balance = balances[addr];
uint48x2 balance = balances[addr];
uint256 unlocked = balance.lo();
if (unlocked > 0) {
balances[addr] = balance.clearLo();
Expand All @@ -137,13 +159,16 @@ contract KDAOLocked is IERC20 {
}

function unlockAllOdd() external {
require(IDistroStage(KDAO_ZKSYNC).distroStage() >= DistroStage.Presale2Unlock, "KDAO-l: Not matured");
require(
IDistroStage(KDAO_ZKSYNC).distroStage() >= DistroStage.Presale2Unlock,
"KDAO-l: Not matured"
);
unchecked {
uint256 length = addrs1.length;
uint256 totalUnlocked;
for (uint256 i = 0; i < length; ++i) {
address addr = addrs1[i];
uint128x2 balance = balances[addr];
uint48x2 balance = balances[addr];
uint256 unlocked = balance.hi();
if (unlocked > 0) {
balances[addr] = balance.clearHi();
Expand Down

0 comments on commit cf01704

Please sign in to comment.