From 6550a102d87eb769f4293937192f071ce0c8067e Mon Sep 17 00:00:00 2001 From: KimlikDAO-bot Date: Fri, 29 Mar 2024 11:21:08 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=82=20Rename=20TCKO=20->=20KDAO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 78 +- contracts/{TCKO.sol => KDAO.sol} | 312 +++--- contracts/{KilitliTCKO.sol => LockedKDAO.sol} | 80 +- foundry.toml | 8 + lib/forge-std | 2 +- lib/interfaces | 2 +- logo/Makefile | 18 - logo/TCKO-k_32.png | Bin 1158 -> 0 bytes logo/TCKO_200.png | Bin 7650 -> 0 bytes logo/TCKO_32.png | Bin 1151 -> 0 bytes logo/source.svg | 13 - remappings.txt | 3 - scripts/mint.js | 13 +- scripts/package.json | 3 - test/KDAOGasTest.sol | 70 ++ test/KDAOMintTest.sol | 43 + test/KDAOSnapshotTest.sol | 516 ++++++++++ test/KDAOTest.sol | 885 +++++++++++++++++ test/RedeemIntegrationTest.sol | 139 +++ test/ReentrancyAttackContracts.sol | 51 - test/ReentrancyTest.sol | 88 -- test/TCKOGasTest.sol | 65 -- test/TCKOMintTest.sol | 79 -- test/TCKOSnapshotTest.sol | 554 ----------- test/TCKOTest.sol | 896 ------------------ 25 files changed, 1865 insertions(+), 2053 deletions(-) rename contracts/{TCKO.sol => KDAO.sol} (66%) rename contracts/{KilitliTCKO.sol => LockedKDAO.sol} (65%) delete mode 100644 logo/Makefile delete mode 100644 logo/TCKO-k_32.png delete mode 100644 logo/TCKO_200.png delete mode 100644 logo/TCKO_32.png delete mode 100644 logo/source.svg delete mode 100644 remappings.txt delete mode 100644 scripts/package.json create mode 100644 test/KDAOGasTest.sol create mode 100644 test/KDAOMintTest.sol create mode 100644 test/KDAOSnapshotTest.sol create mode 100644 test/KDAOTest.sol create mode 100644 test/RedeemIntegrationTest.sol delete mode 100644 test/ReentrancyAttackContracts.sol delete mode 100644 test/ReentrancyTest.sol delete mode 100644 test/TCKOGasTest.sol delete mode 100644 test/TCKOMintTest.sol delete mode 100644 test/TCKOSnapshotTest.sol delete mode 100644 test/TCKOTest.sol diff --git a/README.md b/README.md index fc174d9..a380371 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,74 @@ -## TCKO: KimlikDAO Token - +## KDAO: KimlikDAO Token ### Utility -1 TCKO represents a share of all assets of the KimlikDAO treasury located -at `kimlikdao.eth` and 1 voting right for all treasury investment decisions. -Further, KimlikDAO protocol nodes need to stake TCKOs to get promoted to a -signer node. - -Any TCKO holder can redeem their share of the DAO treasury assets by -transferring their TCKOs to `kimlikdao.eth` on Avalanche C-chain. Such a -transfer burns the transferred TCKOs and sends the redeemer their share of + +1 KDAO represents a share of all assets of the KimlikDAO protocol fund located +at `kimlikdao.eth` and 1 voting right for all protocol fund investment +decisions. Further, KimlikDAO protocol nodes need to stake KDAOs to get +promoted to a signer node. + +Any KDAO holder can redeem their share of the KimlikDAO protocol fund by +transferring their KDAOs to `kimlikdao.eth` on zkSync Era. Such a +transfer burns the transferred KDAOs and sends the redeemer their share of the treasury. The share of the redeemer is `sentAmount / totalSupply()` -fraction of all the ERC20 tokens and AVAX the treasury has. -Note however that the market value of TCKO is ought to be higher than the -redemption amount, as TCKO represents a share in KimlikDAO's future cash -flow as well. The redemption amount is merely a lower bound on TCKOs value +fraction of all the ERC20 tokens and ETH the treasury has. +Note however that the market value of KDAO is ought to be higher than the +redemption amount, as KDAO represents a share in KimlikDAO's future cash +flow as well. The redemption amount is merely a lower bound on KDAOs value and the `redeem` functionality should only be used as a last resort. - + Investment decisions are made through proposals to swap some treasury assets -to other assets on a DEX, which are voted on-chain by all TCKO holders. Once +to other assets on a DEX, which are voted on-chain by all KDAO holders. Once a voting has been completed, the decided upon trade is executed by the `kimlikdao.eth` contract using the nominated DEX. -Combined with a TCKT, TCKO gives a person voting rights for non-financial +Combined with a TCKT, KDAO gives a person voting rights for non-financial decisions of KimlikDAO also; however in such decisions the voting weight is -not necessarily proportional to one's TCKO holdings (guaranteed to be -sub-linear in one's TCKO holdings). Since TCKT is an ID token, it allows us +not necessarily proportional to one's KDAO holdings (guaranteed to be +sub-linear in one's KDAO holdings). Since TCKT is an ID token, it allows us to enforce the sub-linear voting weight. ### Supply Cap -There will be 100M TCKOs minted ever, distributed over 5 rounds of 20M TCKOs + +There will be 100M KDAOs minted ever, distributed over 5 rounds of 20M KDAOs each. Inside the contract, we keep track of another variable `distroStage`, which ranges between 0 and 7 inclusive, and can be mapped to the `distroRound` as follows. -> distroRound := distroStage / 2 + (distroStage == 0 ? 1 : 2) +> distroRound := distroStage / 2 + (distroStage == 0 ? 1 : 2) The `distroStage` has 8 values, corresponding to the beginning and the ending of the 5 distribution rounds; see `DistroStage` enum. The `distroStage` can only be incremented, and only by `dev.kimlikdao.eth` by calling the `incrementDistroStage()` method of this contract. -In distribution rounds 3 and 4, 20M TCKOs are minted to `kimlikdao.eth` +In distribution rounds 3 and 4, 20M KDAOs are minted to `kimlikdao.eth` automatically, to be sold / distributed to the public by `kimlikdao.eth`. In the rest of the rounds (1, 2, and 5), the minting is manually managed -by `dev.kimlikdao.eth`, however the total minted TCKOs is capped at -distroRound * 20M TCKOs at any moment during the lifetime of the contract. +by `dev.kimlikdao.eth`, however the total minted KDAOs is capped at +distroRound _ 20M KDAOs at any moment during the lifetime of the contract. Additionally, in round 2, the `presale2Contract` is also given minting -rights, again respecting the 20M * distroRound supply cap. +rights, again respecting the 20M _ distroRound supply cap. Since the `releaseRound` cannot be incremented beyond 5, this ensures that -there can be at most 100M TCKOs minted. +there can be at most 100M KDAOs minted. ### Lockup + Each mint to external parties results in some unlocked and some locked -TCKOs, and the ratio is fixed globally. Only the 40M TCKOs minted to +KDAOs, and the ratio is fixed globally. Only the 40M KDAOs minted to `kimlikdao.eth` across rounds 3 and 4 are fully unlocked. The unlocking schedule is as follows: -| Minted in round | Unlock time | +| Minted in round | Unlock time | |------------------|-----------------| -| Round 1 | End of round 3 | -| Round 2 | End of round 4 | -| Round 3 | Unlocked | -| Round 4 | Unlocked | -| Round 5 | Year 2028 | +| Round 1 | End of round 3 | +| Round 2 | End of round 4 | +| Round 3 | Unlocked | +| Round 4 | Unlocked | +| Round 5 | Year 2028 | ``` Define: @@ -79,13 +81,15 @@ Invariants: (I1) supplyCap() <= 20M * 1M * distroRound < 2^48 (I2) sum_a(balanceOf(a)) == totalSupply <= totalMinted (I3) totalMinted <= supplyCap() - (I4) balanceOf(KILITLI_TCKO) == KilitliTCKO.totalSupply() + (I4) balanceOf(KILITLI_KDAO) == KilitliKDAO.totalSupply() ``` + (F1) follows because DistroStage has 8 values and floor(7/2) + 2 = 5. -Combining (F1) and (I1) gives the 100M TCKO supply cap. +Combining (F1) and (I1) gives the 100M KDAO supply cap. ### Voting -TCKOs support three concurrent snapshots, allowing users to participate + +KDAOs support three concurrent snapshots, allowing users to participate in three polls / voting at the same time. The voting contract should call the `snapshot0()` method at the beginning of the voting. When a user votes, their voting weight is obtained by calling the @@ -95,5 +99,5 @@ their voting weight is obtained by calling the > `snapshot2BalanceOf(address)` methods. All operations are constant time, moreover use the same amount of -storage as just keeping the TCKO balances. This is achieved by packing the +storage as just keeping the KDAO balances. This is achieved by packing the snapshot values, ticks and the user balance all into the same EVM word. diff --git a/contracts/TCKO.sol b/contracts/KDAO.sol similarity index 66% rename from contracts/TCKO.sol rename to contracts/KDAO.sol index 7625628..1179640 100644 --- a/contracts/TCKO.sol +++ b/contracts/KDAO.sol @@ -1,49 +1,54 @@ // SPDX-License-Identifier: MIT -// 🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿 -pragma solidity 0.8.20; +pragma solidity ^0.8.0; -import "./KilitliTCKO.sol"; -import {OYLAMA} from "interfaces/Addresses.sol"; -import {DistroStage} from "interfaces/DistroStage.sol"; -import {IDAOKasasi, AMOUNT_OFFSET, SUPPLY_OFFSET} from "interfaces/IDAOKasasi.sol"; -import {IERC20Permit} from "interfaces/IERC20Permit.sol"; +import {LockedKDAO} from "./LockedKDAO.sol"; +import {KDAOL} from "interfaces/Addresses.sol"; +import {DEV_FUND, KDAOL, PROTOCOL_FUND, VOTING} from "interfaces/Addresses.sol"; +import {DistroStage, IDistroStage} from "interfaces/IDistroStage.sol"; +import {IERC20, IERC20Permit} from "interfaces/IERC20Permit.sol"; import {IERC20Snapshot3} from "interfaces/IERC20Snapshot3.sol"; +import { + IProtocolFund, + REDEEM_INFO_AMOUNT_OFFSET, + REDEEM_INFO_SUPPLY_OFFSET, + RedeemInfo +} from "interfaces/IProtocolFund.sol"; /** - * @title TCKO: KimlikDAO Token + * @title KDAO: KimlikDAO Token * * Utility * ======= - * 1 TCKO represents a share of all assets of the KimlikDAO treasury located + * 1 KDAO represents a share of all assets of the KimlikDAO treasury located * at `kimlikdao.eth` and 1 voting right for all treasury investment decisions. - * Further, KimlikDAO protocol nodes need to stake TCKOs to get promoted to a + * Further, KimlikDAO protocol nodes need to stake KDAOs to get promoted to a * signer node. * - * Any TCKO holder can redeem their share of the DAO treasury assets by - * transferring their TCKOs to `kimlikdao.eth` on Avalanche C-chain. Such a - * transfer burns the transferred TCKOs and sends the redeemer their share of + * Any KDAO holder can redeem their share of the DAO treasury assets by + * transferring their KDAOs to `kimlikdao.eth` on Avalanche C-chain. Such a + * transfer burns the transferred KDAOs and sends the redeemer their share of * the treasury. The share of the redeemer is `sentAmount / totalSupply()` * fraction of all the ERC20 tokens and AVAX the treasury has. - * Note however that the market value of TCKO is ought to be higher than the - * redemption amount, as TCKO represents a share in KimlikDAO's future cash - * flow as well. The redemption amount is merely a lower bound on TCKOs value + * Note however that the market value of KDAO is ought to be higher than the + * redemption amount, as KDAO represents a share in KimlikDAO's future cash + * flow as well. The redemption amount is merely a lower bound on KDAOs value * and the `redeem` functionality should only be used as a last resort. * * Investment decisions are made through proposals to swap some treasury assets - * to other assets on a DEX, which are voted on-chain by all TCKO holders. Once + * to other assets on a DEX, which are voted on-chain by all KDAO holders. Once * a voting has been completed, the decided upon trade is executed by the * `kimlikdao.eth` contract using the nominated DEX. * - * Combined with a TCKT, TCKO gives a person voting rights for non-financial + * Combined with a TCKT, KDAO gives a person voting rights for non-financial * decisions of KimlikDAO also; however in such decisions the voting weight is - * not necessarily proportional to one's TCKO holdings (guaranteed to be - * sub-linear in one's TCKO holdings). Since TCKT is an ID token, it allows us + * not necessarily proportional to one's KDAO holdings (guaranteed to be + * sub-linear in one's KDAO holdings). Since TCKT is an ID token, it allows us * to enforce the sub-linear voting weight. * * Supply Cap * ========== - * There will be 100M TCKOs minted ever, distributed over 5 rounds of 20M TCKOs + * There will be 100M KDAOs minted ever, distributed over 5 rounds of 20M KDAOs * each. * * Inside the contract, we keep track of another variable `distroStage`, which @@ -57,21 +62,21 @@ import {IERC20Snapshot3} from "interfaces/IERC20Snapshot3.sol"; * The `distroStage` can only be incremented, and only by `dev.kimlikdao.eth` * by calling the `incrementDistroStage()` method of this contract. * - * In distribution rounds 3 and 4, 20M TCKOs are minted to `kimlikdao.eth` + * In distribution rounds 3 and 4, 20M KDAOs are minted to `kimlikdao.eth` * automatically, to be sold / distributed to the public by `kimlikdao.eth`. * In the rest of the rounds (1, 2, and 5), the minting is manually managed - * by `dev.kimlikdao.eth`, however the total minted TCKOs is capped at - * distroRound * 20M TCKOs at any moment during the lifetime of the contract. + * by `dev.kimlikdao.eth`, however the total minted KDAOs is capped at + * distroRound * 20M KDAOs at any moment during the lifetime of the contract. * Additionally, in round 2, the `presale2Contract` is also given minting * rights, again respecting the 20M * distroRound supply cap. * * Since the `releaseRound` cannot be incremented beyond 5, this ensures that - * there can be at most 100M TCKOs minted. + * there can be at most 100M KDAOs minted. * * Lockup * ====== * Each mint to external parties results in some unlocked and some locked - * TCKOs, and the ratio is fixed globally. Only the 40M TCKOs minted to + * KDAOs, and the ratio is fixed globally. Only the 40M KDAOs minted to * `kimlikdao.eth` across rounds 3 and 4 are fully unlocked. * * The unlocking schedule is as follows: @@ -95,14 +100,14 @@ import {IERC20Snapshot3} from "interfaces/IERC20Snapshot3.sol"; * (I1) supplyCap() <= 20M * 1M * distroRound < 2^48. * (I2) sum_a(balanceOf(a)) == totalSupply <= totalMinted * (I3) totalMinted <= supplyCap() - * (I4) balanceOf(TCKOK) == KilitliTCKO.totalSupply() + * (I4) balanceOf(KDAOL) == KilitliKDAO.totalSupply() * * (F1) follows because DistroStage has 8 values and floor(7/2) + 2 = 5. - * Combining (F1) and (I1) gives the 100M TCKO supply cap. + * Combining (F1) and (I1) gives the 100M KDAO supply cap. * * Voting * ====== - * TCKOs support three concurrent snapshots, allowing users to participate + * KDAOs support three concurrent snapshots, allowing users to participate * in three polls / voting at the same time. The voting contract should call * the `snapshot0()` method at the beginning of the voting. When a user votes, * their voting weight is obtained by calling the @@ -112,27 +117,27 @@ import {IERC20Snapshot3} from "interfaces/IERC20Snapshot3.sol"; * `snapshot2BalanceOf(address)` * * methods. All operations are constant time, moreover use the same amount of - * storage as just keeping the TCKO balances. This is achieved by packing the + * storage as just keeping the KDAO balances. This is achieved by packing the * snapshot values, ticks and the user balance all into the same EVM word. */ -contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { +contract KDAO is IERC20Permit, IERC20Snapshot3, IDistroStage { mapping(address => mapping(address => uint256)) public override allowance; - /// @notice The total number of TCKOs in existence, locked or unlocked. + /// @notice The total number of KDAOs in existence, locked or unlocked. uint256 public override totalSupply; - /// @notice The total TCKOs minted so far, including ones that have been + /// @notice The total KDAOs minted so far, including ones that have been /// redeemed later (i.e., burned). uint256 public totalMinted; mapping(address => uint256) private balances; function name() external pure override returns (string memory) { - return "KimlikDAO Tokeni"; + return "KimlikDAO"; } function symbol() external pure override returns (string memory) { - return "TCKO"; + return "KDAO"; } function decimals() external pure override returns (uint8) { @@ -140,30 +145,30 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { } /** - * @notice The total number of TCKOs that will be minted ever. + * @notice The total number of KDAOs that will be minted ever. */ function maxSupply() external pure returns (uint256) { return 100_000_000e6; } /** - * @notice The total number of TCKOs in existence minus the locked ones. + * @notice The total number of KDAOs in existence minus the locked ones. */ function circulatingSupply() external view returns (uint256) { unchecked { // No overflow due to (I2) - return totalSupply - (balances[TCKOK] & BALANCE_MASK); + return totalSupply - (balances[KDAOL] & BALANCE_MASK); } } /** - * @notice The max number of TCKOs that can be minted at the current stage. + * @notice The max number of KDAOs that can be minted at the current stage. * * Ensures: * (E2) supplyCap() <= 20M * 1M * distroRound * * Recall that distroRound := distroStage / 2 + distroStage == 0 ? 1 : 2, - * so combined with distroRound <= 5, we get the 100M TCKO supply cap. + * so combined with distroRound <= 5, we get the 100M KDAO supply cap. */ function supplyCap() public view returns (uint256) { unchecked { @@ -172,53 +177,49 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { } } - function balanceOf( - address account - ) external view override returns (uint256) { + function balanceOf(address account) external view override returns (uint256) { return balances[account] & BALANCE_MASK; } /** - * @notice Transfer some TCKOs to a given address. + * @notice Transfer some KDAOs to a given address. * - * If the `to` address is `DAO_KASASI`, the transfer is understood as a - * redemption: the sent TCKOs are burned and the portion of `DAO_KASASI` - * corresponding to the sent TCKOs are given back to the `msg.sender`. + * If the `to` address is `PROTOCOL_FUND`, the transfer is understood as a + * redemption: the sent KDAOs are burned and the portion of `PROTOCOL_FUND` + * corresponding to the sent KDAOs are given back to the `msg.sender`. * * Sending to the 0 address is disallowed to prevent user error. Sending to - * this contract and the `KilitliTCKO` contract are disallowed to maintain + * this contract and the `KilitliKDAO` contract are disallowed to maintain * our invariants. * * @param to the address of the recipient. - * @param amount amount of TCKOs * 1e6. + * @param amount amount of KDAOs * 1e6. */ - function transfer( - address to, - uint256 amount - ) external override returns (bool) { + function transfer(address to, uint256 amount) external override returns (bool) { // Disallow sending to the 0 address, which is a common software / user // error. require(to != address(0)); - // Disallow sending TCKOs to this contract, as `rescueToken()` on - // TCKOs would result in a redemption to this contract, which is *bad*. + // Disallow sending KDAOs to this contract, as `rescueToken()` on + // KDAOs would result in a redemption to this contract, which is *bad*. require(to != address(this)); - // We disallow sending to `TCKOK` as we want to enforce (I4) + // We disallow sending to `KDAOL` as we want to enforce (I4) // at all times. - require(to != TCKOK); + require(to != KDAOL); unchecked { uint256 t = tick; uint256 fromBalance = balances[msg.sender]; require(amount <= fromBalance & BALANCE_MASK); // (*) balances[msg.sender] = preserve(fromBalance, t) - amount; - // If sent to `DAO_KASASI`, the tokens are burned and the portion + // If sent to `PROTOCOL_FUND`, the tokens are burned and the portion // of the treasury is sent back to the msg.sender (i.e., redeemed). // The redemption amount is `amount / totalSupply()` of all // treasury assets. - if (to == DAO_KASASI) { - IDAOKasasi(DAO_KASASI).redeem( - (amount << AMOUNT_OFFSET) | - (totalSupply << SUPPLY_OFFSET) | - uint160(msg.sender) + if (to == PROTOCOL_FUND) { + IProtocolFund(PROTOCOL_FUND).redeem( + RedeemInfo.wrap( + (amount << REDEEM_INFO_AMOUNT_OFFSET) | (totalSupply << REDEEM_INFO_SUPPLY_OFFSET) + | uint160(msg.sender) + ) ); totalSupply -= amount; // No overflow due to (I2) } else { @@ -230,28 +231,26 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { return true; } - function transferFrom( - address from, - address to, - uint256 amount - ) external override returns (bool) { + function transferFrom(address from, address to, uint256 amount) external override returns (bool) { require(to != address(0) && to != address(this)); - require(to != TCKOK); // For (I4) + require(to != KDAOL); // For (I4) uint256 senderAllowance = allowance[from][msg.sender]; - if (senderAllowance != type(uint256).max) - allowance[from][msg.sender] = senderAllowance - amount; // Checked sub + if (senderAllowance != type(uint256).max) { + allowance[from][msg.sender] = senderAllowance - amount; + } // Checked sub unchecked { uint256 t = tick; uint256 fromBalance = balances[from]; require(amount <= fromBalance & BALANCE_MASK); balances[from] = preserve(fromBalance, t) - amount; - if (to == DAO_KASASI) { - IDAOKasasi(DAO_KASASI).redeem( - (amount << AMOUNT_OFFSET) | - (totalSupply << SUPPLY_OFFSET) | - uint160(msg.sender) + if (to == PROTOCOL_FUND) { + IProtocolFund(PROTOCOL_FUND).redeem( + RedeemInfo.wrap( + (amount << REDEEM_INFO_AMOUNT_OFFSET) | (totalSupply << REDEEM_INFO_SUPPLY_OFFSET) + | uint160(msg.sender) + ) ); totalSupply -= amount; } else { @@ -262,19 +261,13 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { return true; } - function approve( - address spender, - uint256 amount - ) external override returns (bool) { + function approve(address spender, uint256 amount) external override returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } - function increaseAllowance( - address spender, - uint256 addedAmount - ) external returns (bool) { + function increaseAllowance(address spender, uint256 addedAmount) external returns (bool) { // Checked addition uint256 newAmount = allowance[msg.sender][spender] + addedAmount; allowance[msg.sender][spender] = newAmount; @@ -282,10 +275,7 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { return true; } - function decreaseAllowance( - address spender, - uint256 subtractedAmount - ) external returns (bool) { + function decreaseAllowance(address spender, uint256 subtractedAmount) external returns (bool) { // Checked subtraction uint256 newAmount = allowance[msg.sender][spender] - subtractedAmount; allowance[msg.sender][spender] = newAmount; @@ -304,46 +294,30 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { // keccak256( // "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" // ), - // keccak256(bytes("TCKO")), + // keccak256(bytes("KDAO")), // keccak256(bytes("1")), - // 43114, - // TCKO_ADDR + // 0x144, + // KDAO_ADDR // ) // ); bytes32 public constant override DOMAIN_SEPARATOR = 0xd3b6088e7d58a5742b419d3f22999e21c76cce42f96b85b3fdc54662eb2d445c; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - bytes32 public constant PERMIT_TYPEHASH = - 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) public override nonces; - function permit( - address owner, - address spender, - uint256 amount, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external { + function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external + { require(deadline >= block.timestamp); unchecked { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, - keccak256( - abi.encode( - PERMIT_TYPEHASH, - owner, - spender, - amount, - nonces[owner]++, - deadline - ) - ) + keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline)) ) ); address recovered = ecrecover(digest, v, r, s); @@ -367,12 +341,12 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { * Advances the distribution stage. * * If we've advanced to DAOSaleStart stage or DAOAMMStart stage, - * automatically mints 20M unlocked TCKOs to `DAO_KASASI`. + * automatically mints 20M unlocked KDAOs to `PROTOCOL_FUND`. * * @param newStage value to double check to prevent user error. */ function incrementDistroStage(DistroStage newStage) external { - require(msg.sender == DEV_KASASI); + require(msg.sender == DEV_FUND); // Ensure the user provided round number matches, to prevent user error. require(uint256(distroStage) + 1 == uint256(newStage)); // Make sure all minting has been done for the current stage @@ -384,23 +358,17 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { distroStage = newStage; - if ( - newStage == DistroStage.DAOSaleStart || - newStage == DistroStage.DAOAMMStart - ) { - // Mint 20M TCKOs to `DAO_KASASI` bypassing the standard locked + if (newStage == DistroStage.DAOSaleStart || newStage == DistroStage.DAOAMMStart) { + // Mint 20M KDAOs to `PROTOCOL_FUND` bypassing the standard locked // ratio. unchecked { uint256 amount = 20_000_000e6; totalMinted += amount; totalSupply += amount; - balances[DAO_KASASI] = - preserve(balances[DAO_KASASI], tick) + - amount; - emit Transfer(address(this), DAO_KASASI, amount); + balances[PROTOCOL_FUND] = preserve(balances[PROTOCOL_FUND], tick) + amount; + emit Transfer(address(this), PROTOCOL_FUND, amount); } } - IDAOKasasi(DAO_KASASI).distroStageUpdated(newStage); } /** @@ -410,19 +378,15 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { * A fixed locked / unlocked ratio is used across all mints to external * participants. * - * To mint TCKOs to `DAO_KASASI`, a separate code path is used, in which - * all TCKOs are unlocked. + * To mint KDAOs to `PROTOCOL_FUND`, a separate code path is used, in which + * all KDAOs are unlocked. * * @param amountAccount Account to be minted and the mint amount * packed in a single word. The amount is 48 bits * followed by a 160-bits address. */ function mintTo(uint256 amountAccount) public { - require( - msg.sender == DEV_KASASI || - (distroStage == DistroStage.Presale2 && - msg.sender == presale2Contract) - ); + require(msg.sender == DEV_FUND || (distroStage == DistroStage.Presale2 && msg.sender == presale2Contract)); mint(amountAccount); } @@ -439,9 +403,9 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { address account = address(uint160(amountAccount)); require(totalMinted + amount <= supplyCap()); // Checked addition (*) // We need this to satisfy (I4). - require(account != TCKOK); - // If minted to `DAO_KASASI` unlocking would lead to redemption. - require(account != DAO_KASASI); + require(account != KDAOL); + // If minted to `PROTOCOL_FUND` unlocking would lead to redemption. + require(account != PROTOCOL_FUND); unchecked { uint256 unlocked = (amount + 3) / 4; uint256 locked = amount - unlocked; @@ -451,10 +415,10 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { // No overflow due to (*) and (I1) balances[account] = preserve(balances[account], t) + unlocked; // No overflow due to (*) and (I1) - balances[TCKOK] = preserve(balances[TCKOK], t) + locked; + balances[KDAOL] = preserve(balances[KDAOL], t) + locked; emit Transfer(address(this), account, unlocked); - emit Transfer(address(this), TCKOK, locked); - KilitliTCKO(TCKOK).mint(account, locked, distroStage); + emit Transfer(address(this), KDAOL, locked); + LockedKDAO(KDAOL).mint(account, locked, distroStage); } } @@ -469,7 +433,6 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { mint(0x00ba43b7400052fbe88018537027b6fe4be2249fad2a7a2d2b4a); mint(0x00ba43b740009b5541ab008f30afa9b047a868ca5e11fa4e6752); mint(0x00ba43b740009c48199d8d3d8ee6ef4716b0cb7d99148788712e); - mint(0x00ba43b74000ccc07a7597494daf16568ff3e00c2a28edc92ccc); mint(0x005d21dba0003480d7de36a3d92ee0cc8685f0f3fea2ade86a9b); mint(0x005d21dba0003dd308d8a7035d414bd2ec934a83564f814675fa); mint(0x005d21dba000530a8eeb07d81ec4837f6e2c405357defd7cb1ba); @@ -486,22 +449,23 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { mint(0x00174876e80077c60E68158De0bC70260DFd1201be9445EfFc07); mint(0x00174876e8004F1DBED3c377646c89B4F8864E0b41806f2B79fd); mint(0x00174876e80086f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828); + mint(0x00174876e800c855dB548A6feB1f34AcAE6531c84261008ea55A); } } function setPresale2Contract(address addr) external { - require(msg.sender == DEV_KASASI); + require(msg.sender == DEV_FUND); presale2Contract = addr; } /** - * Move ERC20 tokens sent to this address by accident to `DAO_KASASI`. + * Move ERC20 tokens sent to this address by accident to `PROTOCOL_FUND`. */ function rescueToken(IERC20 token) external { - // We restrict this method to `OYLAMA` only, as we call a method of + // We restrict this method to `VOTING` only, as we call a method of // an unkown contract, which could potentially be a security risk. - require(msg.sender == OYLAMA); - token.transfer(DAO_KASASI, token.balanceOf(address(this))); + require(msg.sender == VOTING); + token.transfer(PROTOCOL_FUND, token.balanceOf(address(this))); } /////////////////////////////////////////////////////////////////////////// @@ -520,78 +484,57 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { // |-- 24 --|-- 20 --|-- 20 --|-- 48 --|-- 48 --|-- 48 --|-- 48 --| uint256 private tick; - function snapshot0BalanceOf( - address account - ) external view override returns (uint256) { + function snapshot0BalanceOf(address account) external view override returns (uint256) { uint256 balance = balances[account]; unchecked { - return - BALANCE_MASK & - (((balance ^ tick) & TICK0 == 0) ? (balance >> 48) : balance); + return BALANCE_MASK & (((balance ^ tick) & TICK0 == 0) ? (balance >> 48) : balance); } } - function consumeSnapshot0Balance( - address account - ) external override returns (uint256) { - require(msg.sender == OYLAMA); + function consumeSnapshot0Balance(address account) external override returns (uint256) { + require(msg.sender == VOTING); uint256 info = balances[account]; unchecked { uint256 t = tick; - uint256 balance = BALANCE_MASK & - (((info ^ t) & TICK0 == 0) ? (info >> 48) : info); + uint256 balance = BALANCE_MASK & (((info ^ t) & TICK0 == 0) ? (info >> 48) : info); info &= ~((BALANCE_MASK << 48) | TICK0); balances[account] = info | (t & TICK0); return balance; } } - function snapshot1BalanceOf( - address account - ) external view override returns (uint256) { + function snapshot1BalanceOf(address account) external view override returns (uint256) { uint256 info = balances[account]; unchecked { - return - BALANCE_MASK & - (((info ^ tick) & TICK1 == 0) ? (info >> 96) : info); + return BALANCE_MASK & (((info ^ tick) & TICK1 == 0) ? (info >> 96) : info); } } - function consumeSnapshot1Balance( - address account - ) external override returns (uint256) { - require(msg.sender == OYLAMA); + function consumeSnapshot1Balance(address account) external override returns (uint256) { + require(msg.sender == VOTING); uint256 info = balances[account]; unchecked { uint256 t = tick; - uint256 balance = BALANCE_MASK & - (((info ^ t) & TICK1 == 0) ? (info >> 96) : info); + uint256 balance = BALANCE_MASK & (((info ^ t) & TICK1 == 0) ? (info >> 96) : info); info &= ~((BALANCE_MASK << 96) | TICK1); balances[account] = info | (t & TICK1); return balance; } } - function snapshot2BalanceOf( - address account - ) external view override returns (uint256) { + function snapshot2BalanceOf(address account) external view override returns (uint256) { uint256 info = balances[account]; unchecked { - return - BALANCE_MASK & - (((info ^ tick) & TICK2 == 0) ? (info >> 144) : info); + return BALANCE_MASK & (((info ^ tick) & TICK2 == 0) ? (info >> 144) : info); } } - function consumeSnapshot2Balance( - address account - ) external override returns (uint256) { - require(msg.sender == OYLAMA); + function consumeSnapshot2Balance(address account) external override returns (uint256) { + require(msg.sender == VOTING); uint256 info = balances[account]; unchecked { uint256 t = tick; - uint256 balance = BALANCE_MASK & - (((info ^ t) & TICK2 == 0) ? (info >> 144) : info); + uint256 balance = BALANCE_MASK & (((info ^ t) & TICK2 == 0) ? (info >> 144) : info); info &= ~((BALANCE_MASK << 144) | TICK2); balances[account] = info | (t & TICK2); return balance; @@ -599,14 +542,14 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { } function snapshot0() external override { - require(msg.sender == OYLAMA); + require(msg.sender == VOTING); unchecked { tick += uint256(1) << 232; } } function snapshot1() external override { - require(msg.sender == OYLAMA); + require(msg.sender == VOTING); unchecked { uint256 t = tick; tick = t & TICK1 == TICK1 ? t & ~TICK1 : t + (uint256(1) << 212); @@ -614,17 +557,14 @@ contract TCKO is IERC20Permit, IERC20Snapshot3, HasDistroStage { } function snapshot2() external override { - require(msg.sender == OYLAMA); + require(msg.sender == VOTING); unchecked { uint256 t = tick; tick = t & TICK2 == TICK2 ? t & ~TICK2 : t + (uint256(1) << 192); } } - function preserve( - uint256 balance, - uint256 t - ) internal pure returns (uint256) { + function preserve(uint256 balance, uint256 t) internal pure returns (uint256) { unchecked { // tick.tick0 doesn't match balance.tick0; we need to preserve the // current balance. diff --git a/contracts/KilitliTCKO.sol b/contracts/LockedKDAO.sol similarity index 65% rename from contracts/KilitliTCKO.sol rename to contracts/LockedKDAO.sol index 672ae77..cdd0d85 100644 --- a/contracts/KilitliTCKO.sol +++ b/contracts/LockedKDAO.sol @@ -1,30 +1,29 @@ // SPDX-License-Identifier: MIT -// 🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿🧿 -pragma solidity 0.8.20; +pragma solidity ^0.8.0; -import "interfaces/Addresses.sol"; -import "interfaces/DistroStage.sol"; -import "interfaces/IERC20.sol"; +import {KDAO_ADDR, KDAO_ADDR, PROTOCOL_FUND, VOTING} from "interfaces/Addresses.sol"; +import {DistroStage, IDistroStage} from "interfaces/IDistroStage.sol"; +import {IERC20} from "interfaces/IERC20.sol"; /** - * @title TCKO-k: KimlikDAO Locked Token + * @title KDAO-k: KimlikDAO Locked Token * - * A KilitliTCKO represents a locked TCKO, which cannot be redeemed or - * transferred, but turns into a TCKO automatically at the prescribed + * A LockedKDAO represents a locked KDAO, which cannot be redeemed or + * transferred, but turns into a KDAO automatically at the prescribed * `DistroStage`. * - * The unlocking is triggered by the `DEV_KASASI` using the `unlockAllEven()` + * The unlocking is triggered by the `PROTOCOL_FUND` using the `unlockAllEven()` * or `unlockAllOdd()` methods and the gas is paid by KimlikDAO; the user does * not need to take any action to unlock their tokens. * * Invariants: * (I1) sum_a(lo(balances[a])) + sum_a(hi(balances[a])) == totalSupply - * (I2) totalSupply == TCKO.balanceOf(address(this)) + * (I2) totalSupply == KDAO.balanceOf(address(this)) * (I3) lo(balance[a]) > 0 => accounts0.includes(a) * (I4) hi(balance[a]) > 0 => accounts1.includes(a) */ -contract KilitliTCKO is IERC20 { +contract LockedKDAO is IERC20 { uint256 public override totalSupply; mapping(address => uint256) private balances; @@ -34,11 +33,11 @@ contract KilitliTCKO is IERC20 { address[] private accounts1; function name() external pure override returns (string memory) { - return "Kilitli TCKO"; + return "Locked KDAO"; } function symbol() external pure override returns (string memory) { - return "TCKO-k"; + return "KDAO-l"; } function decimals() external pure override returns (uint8) { @@ -47,9 +46,7 @@ contract KilitliTCKO is IERC20 { uint256 private constant BALANCE0_MASK = type(uint256).max >> 128; - function balanceOf( - address account - ) external view override returns (uint256) { + function balanceOf(address account) external view override returns (uint256) { unchecked { uint256 balance = balances[account]; return (balance & BALANCE0_MASK) + (balance >> 128); @@ -61,18 +58,11 @@ contract KilitliTCKO is IERC20 { return false; } - function transferFrom( - address, - address, - uint256 - ) external pure override returns (bool) { + function transferFrom(address, address, uint256) external pure override returns (bool) { return false; } - function allowance( - address, - address - ) external pure override returns (uint256) { + function allowance(address, address) external pure override returns (uint256) { return 0; } @@ -81,7 +71,7 @@ contract KilitliTCKO is IERC20 { } function mint(address account, uint256 amount, DistroStage stage) external { - require(msg.sender == TCKO_ADDR); + require(msg.sender == KDAO_ADDR); unchecked { if (uint256(stage) & 1 == 0) { accounts0.push(account); @@ -97,13 +87,10 @@ contract KilitliTCKO is IERC20 { function unlock(address account) public returns (bool) { unchecked { - DistroStage stage = HasDistroStage(TCKO_ADDR).distroStage(); + DistroStage stage = IDistroStage(KDAO_ADDR).distroStage(); uint256 locked; uint256 balance = balances[account]; - if ( - stage >= DistroStage.DAOSaleEnd && - stage != DistroStage.FinalMint - ) { + if (stage >= DistroStage.DAOSaleEnd && stage != DistroStage.FinalMint) { locked += balance & BALANCE0_MASK; balance &= ~BALANCE0_MASK; } @@ -115,7 +102,7 @@ contract KilitliTCKO is IERC20 { balances[account] = balance; emit Transfer(account, address(this), locked); totalSupply -= locked; - IERC20(TCKO_ADDR).transfer(account, locked); + IERC20(KDAO_ADDR).transfer(account, locked); return true; } return false; @@ -123,11 +110,8 @@ contract KilitliTCKO is IERC20 { } function unlockAllEven() external { - DistroStage stage = HasDistroStage(TCKO_ADDR).distroStage(); - require( - stage >= DistroStage.DAOSaleEnd && stage != DistroStage.FinalMint, - "TCKO-k: Not matured" - ); + DistroStage stage = IDistroStage(KDAO_ADDR).distroStage(); + require(stage >= DistroStage.DAOSaleEnd && stage != DistroStage.FinalMint, "KDAO-l: Not matured"); unchecked { uint256 length = accounts0.length; uint256 totalUnlocked; @@ -139,7 +123,7 @@ contract KilitliTCKO is IERC20 { balances[account] = balance & ~BALANCE0_MASK; emit Transfer(account, address(this), locked); totalUnlocked += locked; - IERC20(TCKO_ADDR).transfer(account, locked); + IERC20(KDAO_ADDR).transfer(account, locked); } } totalSupply -= totalUnlocked; @@ -147,11 +131,7 @@ contract KilitliTCKO is IERC20 { } function unlockAllOdd() external { - require( - HasDistroStage(TCKO_ADDR).distroStage() >= - DistroStage.Presale2Unlock, - "TCKO-k: Not matured" - ); + require(IDistroStage(KDAO_ADDR).distroStage() >= DistroStage.Presale2Unlock, "KDAO-l: Not matured"); unchecked { uint256 length = accounts1.length; @@ -164,7 +144,7 @@ contract KilitliTCKO is IERC20 { balances[account] = balance & BALANCE0_MASK; emit Transfer(account, address(this), locked); totalUnlocked += locked; - IERC20(TCKO_ADDR).transfer(account, locked); + IERC20(KDAO_ADDR).transfer(account, locked); } } totalSupply -= totalUnlocked; @@ -172,14 +152,14 @@ contract KilitliTCKO is IERC20 { } /** - * Moves ERC20 tokens sent to this address by accident to `DAO_KASASI`. + * Moves ERC20 tokens sent to this address by accident to `PROTOCOL_FUND`. */ function rescueToken(IERC20 token) external { - // We restrict this method to `OYLAMA` only, as we call a method of + // We restrict this method to `VOTING` only, as we call a method of // an unkown contract, which could potentially be a security risk. - require(msg.sender == OYLAMA); - // Disable sending out TCKO to ensure the invariant TCKO.(I4). - require(address(token) != TCKO_ADDR); - token.transfer(DAO_KASASI, token.balanceOf(address(this))); + require(msg.sender == VOTING); + // Disable sending out KDAO to ensure the invariant KDAO.(I4). + require(address(token) != KDAO_ADDR); + token.transfer(PROTOCOL_FUND, token.balanceOf(address(this))); } } diff --git a/foundry.toml b/foundry.toml index cd05e41..60b27cf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,4 +1,12 @@ +[fmt] +sort_imports = true + [profile.default] src = 'contracts' +solc_version = "0.8.25" optimizer = true optimizer_runs = 100000 +remappings = [ + "forge-std/=lib/forge-std/src/", + "interfaces/=lib/interfaces/contracts/", +] diff --git a/lib/forge-std b/lib/forge-std index 66bf4e2..e4aef94 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 66bf4e2c92cf507531599845e8d5a08cc2e3b5bb +Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd diff --git a/lib/interfaces b/lib/interfaces index 81e08d1..6a78a0c 160000 --- a/lib/interfaces +++ b/lib/interfaces @@ -1 +1 @@ -Subproject commit 81e08d19d9bdbcadfe6256c88b69437772198929 +Subproject commit 6a78a0c83656e1b94e304aec1a27aad070bccb1c diff --git a/logo/Makefile b/logo/Makefile deleted file mode 100644 index 5993cec..0000000 --- a/logo/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -all: TCKO_32.png TCKO-k_32.png TCKO_200.png - -INKSCAPE = /Applications/Inkscape.app/Contents/MacOS/inkscape - -TCKO_32.png: source.svg Makefile - $(INKSCAPE) --export-type png --export-filename $@ -w 32 $< - -TCKO_200.png: source.svg Makefile - $(INKSCAPE) --export-type png --export-filename $@ -w 200 $< - -TCKO-k_32.png: source.svg Makefile - sed 's/"st0"/"st2"/' $< > tmp.svg - $(INKSCAPE) --export-type png --export-filename $@ -w 32 tmp.svg - rm -rf tmp.svg - -.PHONY: clean -clean: - rm *.png diff --git a/logo/TCKO-k_32.png b/logo/TCKO-k_32.png deleted file mode 100644 index 8b6395ee63273b03bb6880f2fad8d816c97d095e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1158 zcmV;11bO?3P)l>&@GqDD& z4-IcVnI@(P85<-4#7e*zC4i`agocC&lv3J4?esFe*fVpM56(0|FLP+=|8&mU>#Xlj z_S*aGEx1&$vC-!-=~bdq0lE?>0dnKRy}&IME{W=C&q(U=UGKd*;HIu5OW1qp@H6T- zyHMT$GF|!|QaGqkw`Y6x^=TQX4}~&@qf(2?2Y|;V_)*y?`)zxmCb(nAh-(A22bxNa za2Ta*n&3xu4o9}`ersDtQUm)AHm!CXnt{A01W)J=Le=g!tN)tRK<$C1QUhmZ0X}BJ z>vhU^T2^NwU=k0(hK6(zzMVxlK!I(i9IFq7GR7J3re%L7%F0=U$E9nAM$LzbH-Y$4 zi#b<$jBXbZRCT&8B3`dky3?{c4S+h%F1PSS^8&nE`4Y>E3#JPP$hK`$10cc1MxR&G zJ8-edJXcc4D=)0Y7(+|z9gdtiKNAy!o)PZ?<1y(~Zr}^&2G|mZU%TB-^M#8ufdgdO zY0=G|_}-p0d(+xgq!>eM*L{whIgj(m{HLd;5_25vXw5v?%sAIp@=5BOOy*AKwS zKQE7i1HAwGW-7~9xrWEJ!hkGJmQ_4|9su`x`?weG8;gqw8&)hQKj6nvMMQYEa3Rkx zDRxB{SmFWoB}<%>l?6a}VDO=MzCV}sWy@G!whVx>k|N4Vis+3Da^*&AQUeNeOwufv zbiH@+`kHvnu(!y6KPPr)yfC1@GmefBq1VA7sd)!!7S{eoO0F~eE{-ueLUr#^Uzq-iP0JE<-1?c@#7zt>F(>tNYZl-017u! zDypq=fi+VlMutatdSN~}nHkLW<#4sNZERX1!y{b!`xgJ&QM$stT)W*)%yC>9K;^g* z)zfZrn=f1<<~S73&*QZh*O8H$>JBm`-#KM$uO0&ilO=cbggJWlcO1uIY0)A!m9L&D ze=zFR6DDX`5rso;zG!J}Y0aU5~X#2JsV+H_hd!Pn5H|?sfC1-ts%;4DD zgF|10Lix6Begg_;2d;NDs_Q;^$7)YR9@>g`T2?2*7U0e-(A#3Fl@G#Ul8edSP$*Ar z(*$gIGHf~P^~Sb7cpOh?!k^K{mUTbJpZT&XpWzNq2< zWt!y@JO~^xNL`}aCzI>N91MlBJ)hhRc<0&MEJ?}INt{?E8?c^ Yf4lO577(#T@Bjb+07*qoM6N<$f{%?Cxc~qF diff --git a/logo/TCKO_200.png b/logo/TCKO_200.png deleted file mode 100644 index ef60349ed9e312a0374728cd564e804ef40d14b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7650 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 GhW-!ab)b#_ diff --git a/logo/TCKO_32.png b/logo/TCKO_32.png deleted file mode 100644 index 9039deae8561bc444ce52a02e85f1d6587e92a61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1151 zcmV-_1c3XAP)h)Au#eo#Asd?0H!Jpl|O{Em`KIyt#*-}&>CcE(ll3VwFG zi!DgK8+TL)KV;BtL^su>7;BEoUNunB%x5m2Yh&{U(#>kZj9 z&~>n`NE_x4B0RfF@B#_+DzRRv?C7`?*FaZI%`PQ$0NIZN9;4eJwUzDd{mUBYI#^ev z9Of@;0G~HuYE|a)ct+;}dhVgKx;jNUtnb$l4v^;%X}w%upSs9^k(&A`$j&u{Mkt*OZf3wN;5WO7{96rVVOG7QGLyBTOYnTUy?PELAKM?`Avz|(i^n1_$|^l;;= z(}}hd^8Xe`|ix=&9$kg#` z0Ffobfx$t3d;JaWUb{9QyLGOGtH<88H7rCs;k|f8_s7NnNO|h%MY*J`OfpJJm>d|u zoSr7TV&9T$WEtJnz|DA76MfMQEG>U4X$1ufa#lD(U}%UxKWk#>%o&2W{sL`tV!`Kl zZF^A}irp&}hW&n?D0_i*8#l6ka~}VBy)fH`!+t-b?H!m?Qv`>6jQ8{)k%%n=8r7yz za>-8djniKvGc#nCmQvF20Y-YdEy&8UesUbm%Ny24(s3YprP8q9&-BO$8}{xcEkB=> z++4@r%~0q~?ecm93iX|xFUGpM=zH&d0)vD2dwUasgYZq1&HPE|I*?@d`vkY+ zuyW{UD%#q78X$UfR;M0igaqIy)aMm#Z9a%O5t*T>Mu7z9)t0VID*5PvnHY7t-J2Yi z{0eMdBlw76IQEpcw*E60SkN(|r&Xx~Zm$9SZ_(=ISok6X0F~|S{V1MC=zfgwE2dU^ zsyv=Q9!@OTGb=A#7z=1#LiiX6CIAkDGzAo{*t9He#>m!c8<+#ZsNt{V9*j>kZf$_kSYXg7prA RM3Vpj002ovPDHLkV1g*F8Ych% diff --git a/logo/source.svg b/logo/source.svg deleted file mode 100644 index 2ef2278..0000000 --- a/logo/source.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/remappings.txt b/remappings.txt deleted file mode 100644 index 8ea7e8a..0000000 --- a/remappings.txt +++ /dev/null @@ -1,3 +0,0 @@ -ds-test/=lib/forge-std/lib/ds-test/src/ -forge-std/=lib/forge-std/src/ -interfaces/=lib/interfaces/contracts/ diff --git a/scripts/mint.js b/scripts/mint.js index 7cfe812..dd6b792 100644 --- a/scripts/mint.js +++ b/scripts/mint.js @@ -4,7 +4,6 @@ const OLD_TCKO_ADDR = "0xB97Bf95b4F3110285727b70da5a7465bFD2098Ca"; /** @const {!Object} */ const DAO_MEMBERS = { "0x57074c1956d7ef1cda0a8ca26e22c861e30cd733": 4_000_000, - "0xcf7fea15b049ab04ffd03c86f353729c8519d72e": 4_000_000, "0x523c8c26e20bbff5f100221c2c4f99e755681731": 1_600_000, "0xd2f98777949a73867f4e5bd3b5cdb90030056383": 1_600_000, "0x1273ed0a8527bc5c6c7f99977fee362ee398190f": 1_200_000, @@ -12,7 +11,6 @@ const DAO_MEMBERS = { "0x52fbe88018537027b6fe4be2249fad2a7a2d2b4a": 800_000, "0x9b5541ab008f30afa9b047a868ca5e11fa4e6752": 800_000, "0x9c48199d8d3d8ee6ef4716b0cb7d99148788712e": 800_000, - "0xccc00bc7e6983b1901825888a7bb3bda3b051b12": 800_000, "0x3480d7de36a3d92ee0cc8685f0f3fea2ade86a9b": 400_000, "0x3dd308d8a7035d414bd2ec934a83564f814675fa": 400_000, "0x530a8eeb07d81ec4837f6e2c405357defd7cb1ba": 400_000, @@ -31,13 +29,12 @@ const SEED_SIGNERS = { "0x77c60E68158De0bC70260DFd1201be9445EfFc07": 100_000, "0x4F1DBED3c377646c89B4F8864E0b41806f2B79fd": 100_000, "0x86f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828": 100_000, + "0xc855dB548A6feB1f34AcAE6531c84261008ea55A": 100_000, + "0xE3581636Df37f1eBfFbdFE22F8719F57c555d4f7": 100_000, }; /** @const {!Object} */ -const ADDRESS_CHANGES = { - "0xccc00bc7e6983b1901825888a7bb3bda3b051b12": - "0xcCc07a7597494DaF16568ff3E00C2a28edc92cCc".toLowerCase() -} +const ADDRESS_CHANGES = {} /** * @param {number|!bigint} @@ -78,8 +75,8 @@ const generateConstructor = () => { }).reduce((a, b) => a + b); } -assertEq(sumBalances(DAO_MEMBERS), 19_216_000); -assertEq(sumBalances(SEED_SIGNERS), 500_000); +assertEq(sumBalances(DAO_MEMBERS), 18_416_000); +assertEq(sumBalances(SEED_SIGNERS), 600_000); console.log(generateConstructor()); diff --git a/scripts/package.json b/scripts/package.json deleted file mode 100644 index 3dbc1ca..0000000 --- a/scripts/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/test/KDAOGasTest.sol b/test/KDAOGasTest.sol new file mode 100644 index 0000000..1015eb9 --- /dev/null +++ b/test/KDAOGasTest.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {KDAO, LockedKDAO} from "contracts/KDAO.sol"; +import {Test} from "forge-std/Test.sol"; +import { + DEV_FUND, KDAOL_DEPLOYER, KDAO_DEPLOYER, PROTOCOL_FUND, PROTOCOL_FUND_DEPLOYER +} from "interfaces/Addresses.sol"; +import {IProtocolFund} from "interfaces/IProtocolFund.sol"; +import {MockProtocolFundV1} from "interfaces/testing/MockProtocolFundV1.sol"; +import {MockERC20Permit} from "interfaces/testing/MockTokens.sol"; + +contract KDAOGasTest is Test { + KDAO private kdao; + LockedKDAO private kdaol; + IProtocolFund private protocolFund; + MockERC20Permit private testToken; + + function setUp() public { + vm.prank(KDAO_DEPLOYER); + kdao = new KDAO(false); + + vm.prank(KDAOL_DEPLOYER); + kdaol = new LockedKDAO(); + + vm.prank(PROTOCOL_FUND_DEPLOYER); + protocolFund = new MockProtocolFundV1(); + + mintAll(1e12); + vm.deal(PROTOCOL_FUND, 80e18); + } + + function mintAll(uint256 amount) public { + vm.startPrank(DEV_FUND); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mintTo((amount << 160) | uint160(vm.addr(i))); + } + vm.stopPrank(); + } + + function testRedeemCorrectness() external { + vm.prank(vm.addr(1)); + kdao.transfer(PROTOCOL_FUND, 250_000e6); + + assertEq(vm.addr(1).balance, 1e18); + } + + function testRedeemGas() external { + vm.prank(vm.addr(1)); + kdao.transfer(PROTOCOL_FUND, 250_000e6); + } + + function testRedeemAllCorrectness() external { + for (uint256 i = 1; i <= 20; ++i) { + vm.prank(vm.addr(i)); + kdao.transfer(PROTOCOL_FUND, 250_000e6); + + assertEq(vm.addr(i).balance, 1e18); + } + assertEq(PROTOCOL_FUND.balance, 60e18); + } + + function testRedeemAllGas() external { + for (uint256 i = 1; i <= 20; ++i) { + vm.prank(vm.addr(i)); + kdao.transfer(PROTOCOL_FUND, 250_000e6); + } + } +} diff --git a/test/KDAOMintTest.sol b/test/KDAOMintTest.sol new file mode 100644 index 0000000..c5c14f3 --- /dev/null +++ b/test/KDAOMintTest.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {KDAO, LockedKDAO} from "contracts/KDAO.sol"; +import {Test} from "forge-std/Test.sol"; +import {KDAOL_DEPLOYER, KDAO_DEPLOYER, PROTOCOL_FUND_DEPLOYER} from "interfaces/Addresses.sol"; +import {IProtocolFund} from "interfaces/IProtocolFund.sol"; +import {MockProtocolFundV1} from "interfaces/testing/MockProtocolFundV1.sol"; + +contract KDAOMintTest is Test { + KDAO private kdao; + LockedKDAO private kdaol; + IProtocolFund private protocolFund; + + function setUp() public { + vm.prank(KDAOL_DEPLOYER); + kdaol = new LockedKDAO(); + + vm.prank(KDAO_DEPLOYER); + kdao = new KDAO(true); + + vm.prank(PROTOCOL_FUND_DEPLOYER); + protocolFund = new MockProtocolFundV1(); + } + + function testBalances() external view { + // Check signer node balances. + assertEq(kdao.balanceOf(0x299A3490c8De309D855221468167aAD6C44c59E0), 25000e6); + assertEq(kdaol.balanceOf(0x299A3490c8De309D855221468167aAD6C44c59E0), 75000e6); + assertEq(kdao.balanceOf(0x384bF113dcdF3e7084C1AE2Bb97918c3Bf15A6d2), 25000e6); + assertEq(kdaol.balanceOf(0x384bF113dcdF3e7084C1AE2Bb97918c3Bf15A6d2), 75000e6); + assertEq(kdao.balanceOf(0x77c60E68158De0bC70260DFd1201be9445EfFc07), 25000e6); + assertEq(kdaol.balanceOf(0x77c60E68158De0bC70260DFd1201be9445EfFc07), 75000e6); + assertEq(kdao.balanceOf(0x4F1DBED3c377646c89B4F8864E0b41806f2B79fd), 25000e6); + assertEq(kdaol.balanceOf(0x4F1DBED3c377646c89B4F8864E0b41806f2B79fd), 75000e6); + assertEq(kdao.balanceOf(0x86f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828), 25000e6); + assertEq(kdaol.balanceOf(0x86f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828), 75000e6); + assertEq(kdao.balanceOf(0x57074c1956d7eF1cDa0A8ca26E22C861e30cd733), 1_000_000e6); + assertEq(kdaol.balanceOf(0x57074c1956d7eF1cDa0A8ca26E22C861e30cd733), 3_000_000e6); + assertEq(kdao.totalSupply(), 18_416_000e6 + 600_000e6); + } +} diff --git a/test/KDAOSnapshotTest.sol b/test/KDAOSnapshotTest.sol new file mode 100644 index 0000000..42d8242 --- /dev/null +++ b/test/KDAOSnapshotTest.sol @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {KDAO, LockedKDAO} from "contracts/KDAO.sol"; +import {Test} from "forge-std/Test.sol"; +import { + DEV_FUND, + KDAOL_DEPLOYER, + KDAO_DEPLOYER, + PROTOCOL_FUND, + PROTOCOL_FUND_DEPLOYER, + VOTING +} from "interfaces/Addresses.sol"; +import {IProtocolFund} from "interfaces/IProtocolFund.sol"; +import {MockProtocolFund} from "interfaces/testing/MockProtocolFund.sol"; + +contract KDAOSnapshotTest is Test { + KDAO private kdao; + LockedKDAO private kdaol; + IProtocolFund private protocolFund; + + function mintAll(uint256 amount) public { + vm.startPrank(DEV_FUND); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mintTo((amount << 160) | uint160(vm.addr(i))); + } + vm.stopPrank(); + } + + function setUp() public { + vm.prank(KDAO_DEPLOYER); + kdao = new KDAO(false); + + vm.prank(KDAOL_DEPLOYER); + kdaol = new LockedKDAO(); + + vm.prank(PROTOCOL_FUND_DEPLOYER); + protocolFund = IProtocolFund(address(new MockProtocolFund())); + + mintAll(1e12); + } + + function testAuthentication() public { + vm.expectRevert(); + kdao.snapshot0(); + vm.expectRevert(); + kdao.snapshot1(); + vm.expectRevert(); + kdao.snapshot1(); + + vm.startPrank(VOTING); + kdao.snapshot0(); + kdao.snapshot1(); + kdao.snapshot2(); + vm.stopPrank(); + + vm.expectRevert(); + kdao.consumeSnapshot0Balance(vm.addr(1)); + vm.expectRevert(); + kdao.consumeSnapshot1Balance(vm.addr(1)); + vm.expectRevert(); + kdao.consumeSnapshot2Balance(vm.addr(1)); + + vm.startPrank(VOTING); + kdao.consumeSnapshot0Balance(vm.addr(1)); + kdao.consumeSnapshot1Balance(vm.addr(1)); + kdao.consumeSnapshot2Balance(vm.addr(1)); + vm.stopPrank(); + } + + function testTransferFunction() public { + 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 testTransferFromFunction() public { + vm.prank(vm.addr(1)); + kdao.approve(vm.addr(3), 250_000e6); + + vm.prank(vm.addr(3)); + kdao.transferFrom(vm.addr(1), vm.addr(2), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 500_000e6); + } + + function testSnapshot0() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + vm.prank(VOTING); + kdao.snapshot0(); + + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertFalse(kdao.snapshot0BalanceOf(vm.addr(3)) == 0); + assertFalse(kdao.snapshot0BalanceOf(vm.addr(2)) == 750_000e6); + } + + function testSnapshot1() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + vm.prank(VOTING); + kdao.snapshot1(); + + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500_000e6); + } + + function testSnapshot2() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + vm.prank(VOTING); + kdao.snapshot2(); + + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.snapshot2BalanceOf(vm.addr(3)), 250_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500_000e6); + } + + function testAllSnapshotsRepeatedly() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 750_000e6); + + vm.prank(vm.addr(4)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.snapshot0BalanceOf(vm.addr(4)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(4)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 750_000e6); + + vm.prank(VOTING); + kdao.snapshot2(); + assertEq(kdao.snapshot2BalanceOf(vm.addr(4)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 1e12); + + assertEq(kdao.snapshot0BalanceOf(vm.addr(4)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(4)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 750_000e6); + } + + function testUsingSnapshotMultipleTimes() public { + uint256 balance = kdao.balanceOf(vm.addr(1)); + for (uint256 i = 1; i <= 10; ++i) { + vm.prank(VOTING); + kdao.snapshot0(); + + uint256 amount = i * 10e6; + for (uint256 x = 1; x <= 20; ++x) { + vm.prank(vm.addr(x)); + kdao.transfer(vm.addr(50), amount); + } + + for (uint256 y = 1; y <= 20; ++y) { + assertEq(kdao.snapshot0BalanceOf(vm.addr(y)), balance); + } + + balance = balance - amount; + } + } + + function testSnapshotValuesArePreserved() public { + uint256 balance = kdao.balanceOf(vm.addr(1)); + for (uint256 i = 1; i <= 20; ++i) { + vm.prank(VOTING); + kdao.snapshot1(); + + uint256 amount = i * 10e6; + for (uint256 j = 1; j <= 20; ++j) { + vm.prank(vm.addr(j)); + kdao.transfer(vm.addr((j % 20) + 1), amount); + } + + for (uint256 j = 1; j <= 20; ++j) { + assertEq(kdao.balanceOf(vm.addr(i)), balance); + assertEq(kdao.snapshot1BalanceOf(vm.addr(j)), balance); + } + + for (uint256 j = 1; j <= 20; ++j) { + vm.prank(vm.addr(j)); + kdao.transfer(vm.addr(50), amount); + } + + for (uint256 j = 1; j <= 20; ++j) { + assertEq(kdao.balanceOf(vm.addr(i)), balance - amount); + assertEq(kdao.snapshot1BalanceOf(vm.addr(j)), balance); + } + + for (uint256 j = 1; j <= 20; ++j) { + vm.prank(vm.addr(50)); + kdao.transfer(vm.addr(j), amount); + } + + for (uint256 j = 1; j <= 20; ++j) { + assertEq(kdao.balanceOf(vm.addr(i)), balance); + assertEq(kdao.snapshot1BalanceOf(vm.addr(j)), balance); + } + + for (uint256 j = 1; j <= 20; ++j) { + vm.prank(vm.addr(j)); + kdao.transfer(vm.addr(50), amount); + } + + balance = balance - amount; + } + } + + function testSnapshot0WrapsAround() public { + vm.prank(VOTING); + kdao.snapshot0(); + vm.store(address(kdao), bytes32(uint256(6)), bytes32(((uint256(1) << 24) - 1) << 232)); + + 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); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250_000e6); + + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(2)); + kdao.transfer(vm.addr(1), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250_000e6); + } + + function testSnapshot1WrapsAround() public { + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 232, 1); + vm.store(address(kdao), bytes32(uint256(6)), bytes32(((uint256(1) << 21) - 1) << 212)); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 232, 1); + + 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); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 250_000e6); + + vm.prank(VOTING); + kdao.snapshot1(); + + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 232, 1); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(2)); + kdao.transfer(vm.addr(1), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 250_000e6); + } + + function testSnapshot2WrapsAround() public { + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 212, 1); + vm.store(address(kdao), bytes32(uint256(6)), bytes32(((uint256(1) << 21) - 1) << 192)); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 212, 1); + + 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); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 250_000e6); + + vm.prank(VOTING); + kdao.snapshot2(); + + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(6)))) >> 212, 1); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(vm.addr(2)); + kdao.transfer(vm.addr(1), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 250_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500_000e6); + + vm.prank(VOTING); + kdao.snapshot2(); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 250_000e6); + } + + function testConsumeSnapshotNBalance3Snapshots() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1 + 1), 50_000e6); + + vm.startPrank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 200_000e6); + uint256 snapshot0Balance = kdao.snapshot0BalanceOf(vm.addr(1)); + uint256 snapshot0ConsumedBalance = kdao.consumeSnapshot0Balance(vm.addr(1)); + vm.stopPrank(); + assertEq(snapshot0Balance, 200_000e6); + assertEq(snapshot0Balance, snapshot0ConsumedBalance); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1 + 1)), 300_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + + vm.prank(vm.addr(1 + 1)); + kdao.transfer(vm.addr(1), 50_000e6); + + vm.startPrank(VOTING); + kdao.snapshot1(); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250_000e6); + uint256 snapshot1Balance = kdao.snapshot1BalanceOf(vm.addr(1)); + uint256 snapshot1ConsumedBalance = kdao.consumeSnapshot1Balance(vm.addr(1)); + vm.stopPrank(); + assertEq(snapshot1Balance, 250_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 0); + assertEq(snapshot1Balance, snapshot1ConsumedBalance); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1 + 1)), 250_000e6); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1 + 1), 25_000e6); + + vm.startPrank(VOTING); + kdao.snapshot2(); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 225_000e6); + uint256 snapshot2Balance = kdao.snapshot2BalanceOf(vm.addr(1)); + uint256 snapshot2ConsumedBalance = kdao.consumeSnapshot2Balance(vm.addr(1)); + vm.stopPrank(); + assertEq(snapshot2Balance, 225_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 0); + assertEq(snapshot2Balance, snapshot2ConsumedBalance); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1 + 1)), 275_000e6); + } + + function testConsumeSnapshot0Balance() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 100_000e6); + + vm.startPrank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), kdao.consumeSnapshot0Balance(vm.addr(1))); + vm.stopPrank(); + assertEq(kdao.balanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 350_000e6); + + vm.prank(VOTING); + assertEq(kdao.consumeSnapshot0Balance(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + } + + function testConsumeSnapshot1Balance() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 100_000e6); + + vm.startPrank(VOTING); + kdao.snapshot1(); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), kdao.consumeSnapshot1Balance(vm.addr(1))); + vm.stopPrank(); + assertEq(kdao.balanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 350_000e6); + + vm.prank(VOTING); + assertEq(kdao.consumeSnapshot1Balance(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + } + + function testConsumeSnapshot2Balance() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 100_000e6); + + vm.startPrank(VOTING); + kdao.snapshot2(); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), kdao.consumeSnapshot2Balance(vm.addr(1))); + vm.stopPrank(); + assertEq(kdao.balanceOf(vm.addr(1)), 150_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 350_000e6); + + vm.prank(VOTING); + assertEq(kdao.consumeSnapshot2Balance(vm.addr(2)), 350_000e6); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 350_000e6); + } + + function testConsumeSnapshotBalanceWithTransactions() public { + vm.prank(VOTING); + kdao.snapshot0(); + + for (uint256 i = 1; i <= 4; ++i) { + assertEq(kdao.balanceOf(vm.addr(i)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(i)), 250_000e6); + } + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(2)), 500_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250_000e6); + + uint256 totalSupply = kdao.totalSupply(); + vm.prank(vm.addr(3)); + kdao.transfer(PROTOCOL_FUND, 250_000e6); + + assertEq(kdao.totalSupply(), totalSupply - 250_000e6); + assertEq(kdao.balanceOf(vm.addr(3)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250_000e6); + + vm.prank(vm.addr(2)); + kdao.transfer(vm.addr(1), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(2)), 250_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250_000e6); + + vm.prank(vm.addr(4)); + kdao.transfer(vm.addr(3), 100_000e6); + + assertEq(kdao.balanceOf(vm.addr(3)), 100_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250_000e6); + + assertEq(kdao.balanceOf(vm.addr(4)), 150_000e6); + assertEq(kdao.snapshot0BalanceOf(vm.addr(4)), 250_000e6); + + vm.startPrank(VOTING); + for (uint256 i = 1; i <= 4; ++i) { + assertEq(kdao.snapshot0BalanceOf(vm.addr(i)), kdao.consumeSnapshot0Balance(vm.addr(i))); + + assertEq(kdao.snapshot0BalanceOf(vm.addr(i)), 0); + } + vm.stopPrank(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 250_000e6); + assertEq(kdao.balanceOf(vm.addr(3)), 100_000e6); + assertEq(kdao.balanceOf(vm.addr(4)), 150_000e6); + } + + function testBalanceIsPreservedAfterConsume() external { + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + vm.prank(VOTING); + kdao.snapshot2(); + vm.prank(VOTING); + kdao.consumeSnapshot2Balance(vm.addr(1)); + assertEq(kdao.balanceOf(vm.addr(1)), 250_000e6); + } +} diff --git a/test/KDAOTest.sol b/test/KDAOTest.sol new file mode 100644 index 0000000..77325e9 --- /dev/null +++ b/test/KDAOTest.sol @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {KDAO} from "contracts/KDAO.sol"; +import {LockedKDAO} from "contracts/LockedKDAO.sol"; +import {Test, stdError} from "forge-std/Test.sol"; +import { + DEV_FUND, + KDAOL, + KDAOL_DEPLOYER, + KDAO_ADDR, + KDAO_DEPLOYER, + PROTOCOL_FUND, + PROTOCOL_FUND_DEPLOYER, + VOTING +} from "interfaces/Addresses.sol"; +import {DistroStage} from "interfaces/IDistroStage.sol"; + +import {IERC20Permit} from "interfaces/IERC20Permit.sol"; +import {IProtocolFund} from "interfaces/IProtocolFund.sol"; +import {MockProtocolFundV1} from "interfaces/testing/MockProtocolFundV1.sol"; +import {MockERC20Permit} from "interfaces/testing/MockTokens.sol"; + +contract KDAOTest is Test { + KDAO private kdao; + LockedKDAO private kdaol; + IProtocolFund private protocolFund; + MockERC20Permit private testToken; + + function setUp() public { + vm.prank(KDAO_DEPLOYER); + kdao = new KDAO(false); + + vm.prank(KDAOL_DEPLOYER); + kdaol = new LockedKDAO(); + + vm.prank(PROTOCOL_FUND_DEPLOYER); + protocolFund = new MockProtocolFundV1(); + + mintAll(1e12); + } + + function mintAll(uint256 amount) public { + vm.startPrank(DEV_FUND); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mintTo((amount << 160) | uint160(vm.addr(i))); + } + vm.stopPrank(); + } + + function testDAOAuthentication() public { + vm.expectRevert(); + kdao.mintTo((uint256(1) << 160) | uint160(vm.addr(1))); + + vm.expectRevert(); + kdao.setPresale2Contract(vm.addr(1337)); + + vm.expectRevert(); + kdao.incrementDistroStage(DistroStage.Presale2); + } + + function testSnapshotAuthentication() public { + 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() public { + assertEq(kdao.totalSupply(), 20e12); + assertEq(kdaol.totalSupply(), 15e12); + + 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); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2); + mintAll(1e12); + + assertEq(kdao.totalSupply(), 40e12); + assertEq(kdaol.totalSupply(), 30e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleStart); + + assertEq(kdao.totalSupply(), 60e12); + assertEq(kdaol.totalSupply(), 30e12); + assertEq(kdao.balanceOf(PROTOCOL_FUND), 20e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleEnd); + + kdaol.unlock(vm.addr(1)); + kdaol.unlock(vm.addr(2)); + + assertEq(kdao.balanceOf(vm.addr(1)), 1e12); + assertEq(kdao.balanceOf(vm.addr(2)), 1_500e9); + + kdaol.unlockAllEven(); + + assertEq(kdaol.balanceOf(vm.addr(1)), 750e9); + assertEq(kdaol.balanceOf(vm.addr(2)), 750e9); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOAMMStart); + + assertEq(kdao.totalSupply(), 80e12); + assertEq(kdaol.totalSupply(), 15e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2Unlock); + + kdaol.unlockAllOdd(); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.FinalMint); + mintAll(1e12); + + assertEq(kdaol.unlock(vm.addr(1)), false); + + assertEq(kdaol.balanceOf(vm.addr(1)), 750e9); + + vm.warp(1925097600); + vm.prank(DEV_FUND); + 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()); + } + + function testShouldNotUnlockBeforeMaturity() public { + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllEven(); + + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllOdd(); + + kdaol.unlock(vm.addr(10)); + assertEq(kdao.balanceOf(vm.addr(10)), 250e9); + assertEq(kdaol.balanceOf(vm.addr(10)), 750e9); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2); + + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllEven(); + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllOdd(); + + mintAll(1e12); + + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllEven(); + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllOdd(); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleStart); + + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllEven(); + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllOdd(); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleEnd); + + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllOdd(); + kdaol.unlockAllEven(); + + assertEq(kdao.balanceOf(vm.addr(2)), 1250e9); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOAMMStart); + kdaol.unlock(vm.addr(1)); + + assertEq(kdao.balanceOf(vm.addr(1)), 1250e9); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2Unlock); + kdaol.unlock(vm.addr(1)); + assertEq(kdao.balanceOf(vm.addr(1)), 2e12); + + kdaol.unlockAllOdd(); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.FinalMint); + + assertEq(kdao.totalSupply(), 80e12); + assertEq(kdaol.totalSupply(), 0); + + mintAll(1e12); + vm.expectRevert("KDAO-l: Not matured"); + kdaol.unlockAllEven(); + + assertEq(kdao.totalSupply(), 100e12); + assertEq(kdaol.totalSupply(), 15e12); + + vm.warp(1835470800000); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.FinalUnlock); + kdaol.unlockAllEven(); + + assertEq(kdaol.totalSupply(), 0); + } + + function testPreserveTotalPlusBurnedEqualsMinted() public { + assertEq(kdao.totalSupply(), 20e12); + assertEq(kdaol.totalSupply(), 15e12); + + for (uint256 i = 9; i <= 20; ++i) { + vm.prank(vm.addr(i)); + kdao.transfer(address(protocolFund), 250e9); + } + + assertEq(kdao.totalSupply(), 17e12); + assertEq(kdaol.totalSupply(), 15e12); + assertEq(kdao.totalMinted(), 20e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2); + mintAll(1e12); + + assertEq(kdao.totalSupply(), 37e12); + + for (uint256 i = 9; i <= 20; ++i) { + vm.prank(vm.addr(i)); + kdao.transfer(address(protocolFund), 250e9); + } + + assertEq(kdao.totalSupply(), 34e12); + } + + function testPreservesIndividualBalances() public { + for (uint256 i = 1; i < 20; ++i) { + vm.prank(vm.addr(i)); + kdao.transfer(vm.addr(i + 1), 250e9); + } + + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(20)), 500e9); + } + + function testTransferGas() public { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250e9); + } + + function testPreventOverspending() public { + vm.prank(vm.addr(1)); + vm.expectRevert(); + kdao.transfer(vm.addr(2), 251e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(3), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + + vm.prank(vm.addr(4)); + kdao.transfer(vm.addr(5), 250e9); + vm.prank(vm.addr(5)); + vm.expectRevert(); + kdao.transfer(vm.addr(6), 750e9); + } + + function testAuthorizedPartiesCanSpendOnOwnersBehalf() public { + vm.prank(vm.addr(1)); + kdao.approve(vm.addr(2), 1e12); + + assertEq(kdao.allowance(vm.addr(1), vm.addr(2)), 1e12); + + vm.prank(vm.addr(2)); + kdao.transferFrom(vm.addr(1), vm.addr(3), 250e9); + + assertEq(kdao.allowance(vm.addr(1), vm.addr(2)), 750e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(3)), 500e9); + } + + function testUsersCanAdjustAllowance() public { + vm.startPrank(vm.addr(1)); + kdao.increaseAllowance(vm.addr(2), 3); + + vm.expectRevert(stdError.arithmeticError); + kdao.increaseAllowance(vm.addr(2), type(uint256).max); + + vm.expectRevert(stdError.arithmeticError); + kdao.decreaseAllowance(vm.addr(2), 4); + + kdao.decreaseAllowance(vm.addr(2), 2); + vm.stopPrank(); + + vm.startPrank(vm.addr(2)); + vm.expectRevert(stdError.arithmeticError); + kdao.transferFrom(vm.addr(1), vm.addr(2), 2); + + kdao.transferFrom(vm.addr(1), vm.addr(2), 1); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9 - 1); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9 + 1); + } + + function testSnapshot0Preserved() public { + vm.prank(VOTING); + kdao.snapshot0(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250e9); + + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 150e9); + + vm.startPrank(vm.addr(2)); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + vm.stopPrank(); + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(3)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 150e9); + + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(3)), 250e9); + } + + function testSnapshot0PreservedOnSelfTransfer() public { + vm.prank(VOTING); + kdao.snapshot0(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 250e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot0(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr(1)), 250e9); + } + + function testSnapshot0Fuzz(uint8 from, uint8 to, uint256 amount) public { + vm.assume(from % 20 != to % 20); + amount %= 250e9; + + vm.prank(VOTING); + kdao.snapshot0(); + + vm.prank(vm.addr((from % 20) + 1)); + kdao.transfer(vm.addr((to % 20) + 1), amount); + + assertEq(kdao.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); + assertEq(kdao.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); + assertEq(kdao.snapshot0BalanceOf(vm.addr((from % 20) + 1)), 250e9); + assertEq(kdao.snapshot0BalanceOf(vm.addr((to % 20) + 1)), 250e9); + } + + function testSnapshot1Preserved() public { + vm.prank(VOTING); + kdao.snapshot1(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250e9); + + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 150e9); + + vm.startPrank(vm.addr(2)); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(3)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 150e9); + vm.stopPrank(); + + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(3)), 250e9); + } + + function testSnapshot1PreservedOnSelfTransfer() public { + vm.prank(VOTING); + kdao.snapshot1(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 250e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot1(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr(1)), 250e9); + } + + function testSnapshot1Fuzz(uint8 from, uint8 to, uint256 amount) public { + vm.assume(from % 20 != to % 20); + + amount %= 250e9; + vm.prank(VOTING); + kdao.snapshot1(); + + vm.prank(vm.addr((from % 20) + 1)); + kdao.transfer(vm.addr((to % 20) + 1), amount); + + assertEq(kdao.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); + assertEq(kdao.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); + assertEq(kdao.snapshot1BalanceOf(vm.addr((from % 20) + 1)), 250e9); + assertEq(kdao.snapshot1BalanceOf(vm.addr((to % 20) + 1)), 250e9); + } + + function testSnapshot2Preserved() public { + vm.prank(VOTING); + kdao.snapshot2(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250e9); + + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(3)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot2(); + assertEq(kdao.balanceOf(vm.addr(1)), 100e9); + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + assertEq(kdao.balanceOf(vm.addr(3)), 150e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(3)), 150e9); + + vm.startPrank(vm.addr(2)); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + kdao.transfer(vm.addr(3), 50e9); + kdao.transfer(vm.addr(1), 50e9); + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.balanceOf(vm.addr(2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(3)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 100e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 500e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(3)), 150e9); + vm.stopPrank(); + + vm.prank(VOTING); + kdao.snapshot2(); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(2)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(3)), 250e9); + } + + function testSnapshot2PreservedOnSelfTransfer() public { + vm.prank(VOTING); + kdao.snapshot2(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 250e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + + vm.prank(VOTING); + kdao.snapshot2(); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(1), 100e9); + + assertEq(kdao.balanceOf(vm.addr(1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr(1)), 250e9); + } + + function testSnapshot2Fuzz(uint8 from, uint8 to, uint256 amount) public { + vm.assume(from % 20 != to % 20); + + amount %= 250e9; + vm.prank(VOTING); + kdao.snapshot2(); + + vm.prank(vm.addr((from % 20) + 1)); + kdao.transfer(vm.addr((to % 20) + 1), amount); + + assertEq(kdao.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); + assertEq(kdao.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); + assertEq(kdao.snapshot2BalanceOf(vm.addr((from % 20) + 1)), 250e9); + assertEq(kdao.snapshot2BalanceOf(vm.addr((to % 20) + 1)), 250e9); + } + + function authorizePayment(uint256 ownerPrivateKey, address spender, uint256 amount, uint256 deadline, uint256 nonce) + internal + view + returns (uint8, bytes32, bytes32) + { + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + kdao.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9, + vm.addr(ownerPrivateKey), + spender, + amount, + nonce, + deadline + ) + ) + ) + ); + return vm.sign(ownerPrivateKey, digest); + } + + function testPermit() public { + vm.prank(vm.addr(2)); + vm.expectRevert(stdError.arithmeticError); + kdao.transferFrom(vm.addr(1), vm.addr(2), 250e9); + + uint256 time = block.timestamp + 1000; + (uint8 v, bytes32 r, bytes32 s) = authorizePayment(1, vm.addr(2), 250e9, time, 0); + kdao.permit(vm.addr(1), vm.addr(2), 250e9, time, v, r, s); + vm.prank(vm.addr(2)); + kdao.transferFrom(vm.addr(1), vm.addr(2), 250e9); + assertEq(kdao.balanceOf(vm.addr(2)), 500e9); + } + + function testTokenMethods() external view { + assertEq(kdao.decimals(), kdaol.decimals()); + // Increase coverage so we can always aim at 100%. + assertEq(kdao.name(), "KimlikDAO"); + assertEq(kdaol.name(), "Locked KDAO"); + assertEq(kdao.maxSupply(), 100_000_000e6); + + assertEq(kdao.circulatingSupply(), 5_000_000e6); + assertEq(bytes32(bytes(kdaol.symbol()))[0], bytes32(bytes(kdao.symbol()))[0]); + assertEq(bytes32(bytes(kdaol.symbol()))[1], bytes32(bytes(kdao.symbol()))[1]); + assertEq(bytes32(bytes(kdaol.symbol()))[2], bytes32(bytes(kdao.symbol()))[2]); + assertEq(bytes32(bytes(kdaol.symbol()))[3], bytes32(bytes(kdao.symbol()))[3]); + } + + event Transfer(address indexed from, address indexed to, uint256 amount); + + function testTransfer() external { + vm.startPrank(vm.addr(1)); + + vm.expectRevert(); + kdao.transfer(address(0), 250_000e6); + + vm.expectRevert(); + kdao.transfer(address(kdao), 250_000e6); + + vm.expectRevert(); + kdao.transfer(address(kdaol), 250_000e6); + + vm.expectRevert(); + kdao.transfer(vm.addr(2), 251_000e6); + + vm.expectEmit(true, true, false, true, address(kdao)); + emit Transfer(vm.addr(1), vm.addr(2), 250_000e6); + kdao.transfer(vm.addr(2), 250_000e6); + + assertEq(kdao.totalSupply(), 20_000_000e6); + + vm.stopPrank(); + + vm.startPrank(vm.addr(2)); + + kdao.transfer(PROTOCOL_FUND, 500_000e6); + + assertEq(kdao.totalSupply() + 500_000e6, kdao.supplyCap()); + + vm.stopPrank(); + + vm.startPrank(DEV_FUND); + vm.expectRevert(); + kdao.incrementDistroStage(DistroStage.Presale1); + + kdao.incrementDistroStage(DistroStage.Presale2); + vm.stopPrank(); + mintAll(1e12); + + assertEq(kdao.totalSupply() + 500_000e6, kdao.supplyCap()); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleStart); + + assertEq(kdao.supplyCap(), 60_000_000e6); + assertEq(kdao.totalSupply() + 500_000e6, kdao.supplyCap()); + assertEq(kdao.circulatingSupply(), 20_000_000e6 + 40_000_000e6 / 4 - 500_000e6); + } + + function testTransferFrom() external { + vm.startPrank(vm.addr(1)); + kdao.approve(vm.addr(11), 200_000e6); + kdao.approve(address(kdao), 50_000e6); + kdao.approve(address(0), 50_000e6); + kdao.approve(address(kdaol), 50_000e6); + vm.stopPrank(); + + vm.startPrank(vm.addr(11)); + vm.expectRevert(); + kdao.transferFrom(vm.addr(1), vm.addr(2), 201_000e6); + + vm.expectRevert(); + kdao.transferFrom(vm.addr(1), address(0), 200_000e6); + + vm.expectRevert(); + kdao.transferFrom(vm.addr(1), address(kdao), 200_000e6); + + vm.expectRevert(); + kdao.transferFrom(vm.addr(1), address(kdaol), 200_000e6); + + kdao.transferFrom(vm.addr(1), vm.addr(2), 200_000e6); + + assertEq(kdao.balanceOf(vm.addr(1)), 50_000e6); + assertEq(kdao.balanceOf(vm.addr(2)), 450_000e6); + assertEq(kdaol.balanceOf(vm.addr(1)), 750_000e6); + assertEq(kdaol.balanceOf(vm.addr(2)), 750_000e6); + + vm.stopPrank(); + + vm.prank(vm.addr(3)); + kdao.approve(vm.addr(13), 150_000e6); + + vm.startPrank(vm.addr(13)); + kdao.transferFrom(vm.addr(3), PROTOCOL_FUND, 150_000e6); + + assertEq(kdao.balanceOf(vm.addr(3)), 100_000e6); + assertEq(kdao.totalSupply(), 20_000_000e6 - 150_000e6); + + vm.stopPrank(); + + vm.prank(vm.addr(4)); + kdao.approve(vm.addr(14), 251_000e6); + + vm.startPrank(vm.addr(14)); + vm.expectRevert(); + kdao.transferFrom(vm.addr(4), vm.addr(5), 251_000e6); + + vm.stopPrank(); + } + + function testPresale2Contract() external { + vm.expectRevert(); + kdao.setPresale2Contract(vm.addr(0x94E008A7E2)); + + vm.startPrank(DEV_FUND); + kdao.setPresale2Contract(vm.addr(0x94E008A7E2)); + kdao.incrementDistroStage(DistroStage.Presale2); + vm.stopPrank(); + + vm.startPrank(vm.addr(0x94E008A7E2)); + vm.expectRevert(); + kdao.mintTo(uint160(address(kdaol)) | (1 << 160)); + vm.expectRevert(); + kdao.mintTo(uint160(address(PROTOCOL_FUND)) | (1 << 160)); + + kdao.mintTo(uint160(vm.addr(1)) | (20_000_000e6 << 160)); + vm.expectRevert(); + kdao.mintTo(uint160(vm.addr(1)) | (1 << 160)); + vm.stopPrank(); + + assertEq(kdao.balanceOf(vm.addr(1)), 5_250_000e6); + } + + function testLockedKDAORescueToken() external { + vm.startPrank(vm.addr(20)); + testToken = new MockERC20Permit("TestToken", "TT", 6); + testToken.transfer(address(KDAOL), 4e7); + vm.stopPrank(); + + assertEq(testToken.balanceOf(address(kdaol)), 4e7); + assertEq(testToken.balanceOf(vm.addr(20)), 6e7); + + vm.startPrank(vm.addr(1)); + vm.expectRevert(); + kdaol.rescueToken(testToken); + vm.stopPrank(); + + vm.startPrank(VOTING); + vm.expectRevert(); + kdaol.rescueToken(IERC20Permit(KDAO_ADDR)); + vm.stopPrank(); + + vm.startPrank(VOTING); + kdaol.rescueToken(testToken); + vm.stopPrank(); + + assertEq(testToken.balanceOf(PROTOCOL_FUND), 4e7); + assertEq(testToken.balanceOf(address(kdaol)), 0); + } + + function testLockedKDAOSelfDestruct() external { + vm.startPrank(vm.addr(1)); + vm.expectRevert(); + // kdaol.selfDestruct(); + vm.stopPrank(); + + vm.startPrank(DEV_FUND); + vm.expectRevert(); + // kdaol.selfDestruct(); + vm.stopPrank(); + + 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); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2); + mintAll(1e12); + + assertEq(kdao.totalSupply(), 40e12); + assertEq(kdaol.totalSupply(), 30e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleStart); + + assertEq(kdao.totalSupply(), 60e12); + assertEq(kdaol.totalSupply(), 30e12); + assertEq(kdao.balanceOf(PROTOCOL_FUND), 20e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOSaleEnd); + + kdaol.unlock(vm.addr(1)); + kdaol.unlock(vm.addr(2)); + + assertEq(kdao.balanceOf(vm.addr(1)), 1e12); + assertEq(kdao.balanceOf(vm.addr(2)), 1_500e9); + + kdaol.unlockAllEven(); + + assertEq(kdaol.balanceOf(vm.addr(1)), 750e9); + assertEq(kdaol.balanceOf(vm.addr(2)), 750e9); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.DAOAMMStart); + + assertEq(kdao.totalSupply(), 80e12); + assertEq(kdaol.totalSupply(), 15e12); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.Presale2Unlock); + + kdaol.unlockAllOdd(); + + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.FinalMint); + mintAll(1e12); + + assertEq(kdaol.unlock(vm.addr(1)), false); + + assertEq(kdaol.balanceOf(vm.addr(1)), 750e9); + + vm.warp(1925097600); + vm.prank(DEV_FUND); + kdao.incrementDistroStage(DistroStage.FinalUnlock); + + kdaol.unlockAllEven(); + + vm.prank(DEV_FUND); + } +} diff --git a/test/RedeemIntegrationTest.sol b/test/RedeemIntegrationTest.sol new file mode 100644 index 0000000..af00bac --- /dev/null +++ b/test/RedeemIntegrationTest.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {KDAO, LockedKDAO} from "contracts/KDAO.sol"; +import {Test} from "forge-std/Test.sol"; +import { + DEV_FUND, + KDAOL, + KDAOL_DEPLOYER, + KDAO_ADDR, + KDAO_DEPLOYER, + PROTOCOL_FUND, + PROTOCOL_FUND_DEPLOYER +} from "interfaces/Addresses.sol"; +import {IERC20} from "interfaces/IERC20.sol"; +import {IProtocolFund} from "interfaces/IProtocolFund.sol"; +import {MockProtocolFundV1} from "interfaces/testing/MockProtocolFundV1.sol"; + +contract ReentrancyAttackAttempt1 { + IERC20 private kdao = IERC20(KDAO_ADDR); + + function attack() external { + kdao.transfer(address(PROTOCOL_FUND), (kdao.balanceOf(address(this)) * 3) / 4); + } + + receive() external payable { + uint256 amount = (kdao.balanceOf(address(this)) * 3) / 4; + if (kdao.balanceOf(address(this)) >= amount) { + kdao.transfer(address(PROTOCOL_FUND), amount); + } + } +} + +contract ReentrancyAttackAttempt2 { + IERC20 private kdao = IERC20(KDAO_ADDR); + + function attack() external { + kdao.transfer(address(PROTOCOL_FUND), (kdao.balanceOf(address(this)) / 2)); + } + + receive() external payable { + if (kdao.balanceOf(address(this)) >= 125e9) { + kdao.transfer(address(PROTOCOL_FUND), 125e9); + } + } +} + +contract InnocentContract is Test { + IERC20 private kdao = IERC20(KDAO_ADDR); + + function sendToProtocolFund() external { + kdao.transfer(address(PROTOCOL_FUND), kdao.balanceOf(address(this))); + } + + receive() external payable { + vm.deal(address(this), 2e18); + } +} + +contract RedeemIngegrationTest is Test { + KDAO private kdao; + LockedKDAO private kdaol; + ReentrancyAttackAttempt1 private reentrancyContract1; + ReentrancyAttackAttempt2 private reentrancyContract2; + InnocentContract private innocentContract; + IProtocolFund private protocolFund; + + function setUp() public { + vm.prank(KDAO_DEPLOYER); + kdao = new KDAO(false); + vm.prank(KDAOL_DEPLOYER); + kdaol = new LockedKDAO(); + assertEq(address(kdaol), KDAOL); + + vm.startPrank(vm.addr(1)); + reentrancyContract1 = new ReentrancyAttackAttempt1(); + reentrancyContract2 = new ReentrancyAttackAttempt2(); + innocentContract = new InnocentContract(); + vm.stopPrank(); + + vm.prank(PROTOCOL_FUND_DEPLOYER); + protocolFund = new MockProtocolFundV1(); + + vm.deal(PROTOCOL_FUND, 80e18); + + mintAll(1e12); + } + + function mintAll(uint256 amount) public { + vm.startPrank(DEV_FUND); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mintTo((amount << 160) | uint160(vm.addr(i))); + } + vm.stopPrank(); + } + + function testReentrancyAttack1() external { + vm.startPrank(vm.addr(1)); + kdao.transfer(address(reentrancyContract1), kdao.balanceOf(vm.addr(1))); + + assertEq(kdao.balanceOf(address(reentrancyContract1)), 250e9); + assertEq(kdao.balanceOf(vm.addr(1)), 0); + + vm.expectRevert(); + reentrancyContract1.attack(); + + vm.stopPrank(); + } + + function testReentrancyAttack2() external { + vm.prank(vm.addr(2)); + kdao.transfer(address(reentrancyContract2), 250e9); + assertEq(kdao.balanceOf(address(reentrancyContract2)), 250e9); + assertEq(kdao.balanceOf(vm.addr(2)), 0); + + vm.expectRevert(); + reentrancyContract2.attack(); + } + + function testInnocent() external { + vm.prank(vm.addr(3)); + kdao.transfer(PROTOCOL_FUND, 250e9); + + assertEq(vm.addr(3).balance, 1e18); + } + + function testInnocentWithContract() external { + vm.prank(vm.addr(4)); + kdao.transfer(address(innocentContract), 250e9); + + assertEq(kdao.balanceOf(address(innocentContract)), 250e9); + assertEq(kdao.balanceOf(vm.addr(4)), 0); + + innocentContract.sendToProtocolFund(); + + assertEq(address(innocentContract).balance, 2e18); + } +} diff --git a/test/ReentrancyAttackContracts.sol b/test/ReentrancyAttackContracts.sol deleted file mode 100644 index d1d3b24..0000000 --- a/test/ReentrancyAttackContracts.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "forge-std/Test.sol"; -import {DAO_KASASI, TCKO_ADDR} from "interfaces/Addresses.sol"; -import {IERC20} from "interfaces/IERC20.sol"; - -contract ReentrancyAttackAttempt1 { - IERC20 private tcko = IERC20(TCKO_ADDR); - - function attack() external { - tcko.transfer( - address(DAO_KASASI), - (tcko.balanceOf(address(this)) * 3) / 4 - ); - } - - receive() external payable { - uint256 amount = (tcko.balanceOf(address(this)) * 3) / 4; - if (tcko.balanceOf(address(this)) >= amount) { - tcko.transfer(address(DAO_KASASI), amount); - } - } -} - -contract ReentrancyAttackAttempt2 { - IERC20 private tcko = IERC20(TCKO_ADDR); - - function attack() external { - tcko.transfer(address(DAO_KASASI), (tcko.balanceOf(address(this)) / 2)); - } - - receive() external payable { - if (tcko.balanceOf(address(this)) >= 125e9) { - tcko.transfer(address(DAO_KASASI), 125e9); - } - } -} - -contract InnocentContract is Test { - IERC20 private tcko = IERC20(TCKO_ADDR); - - function sendToDAOkasasi() external { - tcko.transfer(address(DAO_KASASI), tcko.balanceOf(address(this))); - } - - receive() external payable { - vm.deal(address(this), 2e18); - } -} diff --git a/test/ReentrancyTest.sol b/test/ReentrancyTest.sol deleted file mode 100644 index 0bbbc69..0000000 --- a/test/ReentrancyTest.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "./ReentrancyAttackContracts.sol"; -import "contracts/TCKO.sol"; -import "forge-std/Test.sol"; -import {MockDAOKasasiV1} from "interfaces/testing/MockDAOKasasiV1.sol"; - -contract ReentrancyTest is Test { - TCKO private tcko; - KilitliTCKO private tckok; - ReentrancyAttackAttempt1 private reentrancyContract1; - ReentrancyAttackAttempt2 private reentrancyContract2; - InnocentContract private innocentContract; - IDAOKasasi private daoKasasi; - - function setUp() public { - vm.prank(TCKO_DEPLOYER); - tcko = new TCKO(false); - vm.prank(TCKOK_DEPLOYER); - tckok = new KilitliTCKO(); - assertEq(address(tckok), TCKOK); - - vm.startPrank(vm.addr(1)); - reentrancyContract1 = new ReentrancyAttackAttempt1(); - reentrancyContract2 = new ReentrancyAttackAttempt2(); - innocentContract = new InnocentContract(); - vm.stopPrank(); - - vm.prank(DAO_KASASI_DEPLOYER); - daoKasasi = new MockDAOKasasiV1(); - - vm.deal(DAO_KASASI, 80e18); - - mintAll(1e12); - } - - function mintAll(uint256 amount) public { - vm.startPrank(DEV_KASASI); - for (uint256 i = 1; i <= 20; ++i) - tcko.mintTo((amount << 160) | uint160(vm.addr(i))); - vm.stopPrank(); - } - - function testReentrancyAttack1() external { - vm.startPrank(vm.addr(1)); - tcko.transfer(address(reentrancyContract1), tcko.balanceOf(vm.addr(1))); - - assertEq(tcko.balanceOf(address(reentrancyContract1)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - - vm.expectRevert(); - reentrancyContract1.attack(); - - vm.stopPrank(); - } - - function testReentrancyAttack2() external { - console.log(vm.addr(2).balance); - vm.prank(vm.addr(2)); - tcko.transfer(address(reentrancyContract2), 250e9); - assertEq(tcko.balanceOf(address(reentrancyContract2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(2)), 0); - - vm.expectRevert(); - reentrancyContract2.attack(); - } - - function testInnocent() external { - vm.prank(vm.addr(3)); - tcko.transfer(DAO_KASASI, 250e9); - - assertEq(vm.addr(3).balance, 1e18); - } - - function testInnocentWithContract() external { - vm.prank(vm.addr(4)); - tcko.transfer(address(innocentContract), 250e9); - - assertEq(tcko.balanceOf(address(innocentContract)), 250e9); - assertEq(tcko.balanceOf(vm.addr(4)), 0); - - innocentContract.sendToDAOkasasi(); - - assertEq(address(innocentContract).balance, 2e18); - } -} diff --git a/test/TCKOGasTest.sol b/test/TCKOGasTest.sol deleted file mode 100644 index b72de93..0000000 --- a/test/TCKOGasTest.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "contracts/TCKO.sol"; -import "forge-std/Test.sol"; -import "interfaces/testing/MockDAOKasasiV1.sol"; -import {MockERC20Permit} from "interfaces/testing/MockTokens.sol"; - -contract TCKOGasTest is Test { - TCKO private tcko; - KilitliTCKO private tckok; - IDAOKasasi private daoKasasi; - MockERC20Permit private testToken; - - function setUp() public { - vm.prank(TCKO_DEPLOYER); - tcko = new TCKO(false); - - vm.prank(TCKOK_DEPLOYER); - tckok = new KilitliTCKO(); - - vm.prank(DAO_KASASI_DEPLOYER); - daoKasasi = new MockDAOKasasiV1(); - - mintAll(1e12); - vm.deal(DAO_KASASI, 80e18); - } - - function mintAll(uint256 amount) public { - vm.startPrank(DEV_KASASI); - for (uint256 i = 1; i <= 20; ++i) - tcko.mintTo((amount << 160) | uint160(vm.addr(i))); - vm.stopPrank(); - } - - function testRedeemCorrectness() external { - vm.prank(vm.addr(1)); - tcko.transfer(DAO_KASASI, 250_000e6); - - assertEq(vm.addr(1).balance, 1e18); - } - - function testRedeemGas() external { - vm.prank(vm.addr(1)); - tcko.transfer(DAO_KASASI, 250_000e6); - } - - function testRedeemAllCorrectness() external { - for (uint256 i = 1; i <= 20; ++i) { - vm.prank(vm.addr(i)); - tcko.transfer(DAO_KASASI, 250_000e6); - - assertEq(vm.addr(i).balance, 1e18); - } - assertEq(DAO_KASASI.balance, 60e18); - } - - function testRedeemAllGas() external { - for (uint256 i = 1; i <= 20; ++i) { - vm.prank(vm.addr(i)); - tcko.transfer(DAO_KASASI, 250_000e6); - } - } -} diff --git a/test/TCKOMintTest.sol b/test/TCKOMintTest.sol deleted file mode 100644 index 9863681..0000000 --- a/test/TCKOMintTest.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "forge-std/Test.sol"; -import "interfaces/Addresses.sol"; -import {IDAOKasasi} from "interfaces/IDAOKasasi.sol"; -import {MockDAOKasasiV1} from "interfaces/testing/MockDAOKasasiV1.sol"; -import {TCKO, KilitliTCKO} from "contracts/TCKO.sol"; - -contract TCKOMintTest is Test { - TCKO private tcko; - KilitliTCKO private tckok; - IDAOKasasi private daoKasasi; - - function setUp() public { - vm.prank(TCKOK_DEPLOYER); - tckok = new KilitliTCKO(); - - vm.prank(TCKO_DEPLOYER); - tcko = new TCKO(true); - - vm.prank(DAO_KASASI_DEPLOYER); - daoKasasi = new MockDAOKasasiV1(); - } - - function testBalances() external { - // Check signer node balances. - assertEq( - tcko.balanceOf(0x299A3490c8De309D855221468167aAD6C44c59E0), - 25000e6 - ); - assertEq( - tckok.balanceOf(0x299A3490c8De309D855221468167aAD6C44c59E0), - 75000e6 - ); - assertEq( - tcko.balanceOf(0x384bF113dcdF3e7084C1AE2Bb97918c3Bf15A6d2), - 25000e6 - ); - assertEq( - tckok.balanceOf(0x384bF113dcdF3e7084C1AE2Bb97918c3Bf15A6d2), - 75000e6 - ); - assertEq( - tcko.balanceOf(0x77c60E68158De0bC70260DFd1201be9445EfFc07), - 25000e6 - ); - assertEq( - tckok.balanceOf(0x77c60E68158De0bC70260DFd1201be9445EfFc07), - 75000e6 - ); - assertEq( - tcko.balanceOf(0x4F1DBED3c377646c89B4F8864E0b41806f2B79fd), - 25000e6 - ); - assertEq( - tckok.balanceOf(0x4F1DBED3c377646c89B4F8864E0b41806f2B79fd), - 75000e6 - ); - assertEq( - tcko.balanceOf(0x86f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828), - 25000e6 - ); - assertEq( - tckok.balanceOf(0x86f6B34A26705E6a22B8e2EC5ED0cC5aB3f6F828), - 75000e6 - ); - assertEq( - tcko.balanceOf(0x57074c1956d7eF1cDa0A8ca26E22C861e30cd733), - 1_000_000e6 - ); - assertEq( - tckok.balanceOf(0x57074c1956d7eF1cDa0A8ca26E22C861e30cd733), - 3_000_000e6 - ); - assertEq(tcko.totalSupply(), 19_216_000e6 + 500_000e6); - } -} diff --git a/test/TCKOSnapshotTest.sol b/test/TCKOSnapshotTest.sol deleted file mode 100644 index 3b7533c..0000000 --- a/test/TCKOSnapshotTest.sol +++ /dev/null @@ -1,554 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "contracts/TCKO.sol"; -import "forge-std/Test.sol"; -import "interfaces/testing/MockDAOKasasi.sol"; - -contract TCKOSnapshotTest is Test { - TCKO private tcko; - KilitliTCKO private tckok; - IDAOKasasi private daoKasasi; - - function mintAll(uint256 amount) public { - vm.startPrank(DEV_KASASI); - for (uint256 i = 1; i <= 20; ++i) - tcko.mintTo((amount << 160) | uint160(vm.addr(i))); - vm.stopPrank(); - } - - function setUp() public { - vm.prank(TCKO_DEPLOYER); - tcko = new TCKO(false); - - vm.prank(TCKOK_DEPLOYER); - tckok = new KilitliTCKO(); - - vm.prank(DAO_KASASI_DEPLOYER); - daoKasasi = IDAOKasasi(address(new MockDAOKasasi())); - - mintAll(1e12); - } - - function testAuthentication() public { - vm.expectRevert(); - tcko.snapshot0(); - vm.expectRevert(); - tcko.snapshot1(); - vm.expectRevert(); - tcko.snapshot1(); - - vm.startPrank(OYLAMA); - tcko.snapshot0(); - tcko.snapshot1(); - tcko.snapshot2(); - vm.stopPrank(); - - vm.expectRevert(); - tcko.consumeSnapshot0Balance(vm.addr(1)); - vm.expectRevert(); - tcko.consumeSnapshot1Balance(vm.addr(1)); - vm.expectRevert(); - tcko.consumeSnapshot2Balance(vm.addr(1)); - - vm.startPrank(OYLAMA); - tcko.consumeSnapshot0Balance(vm.addr(1)); - tcko.consumeSnapshot1Balance(vm.addr(1)); - tcko.consumeSnapshot2Balance(vm.addr(1)); - vm.stopPrank(); - } - - function testTransferFunction() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - } - - function testTransferFromFunction() public { - vm.prank(vm.addr(1)); - tcko.approve(vm.addr(3), 250_000e6); - - vm.prank(vm.addr(3)); - tcko.transferFrom(vm.addr(1), vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - } - - function testSnapshot0() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - vm.prank(OYLAMA); - tcko.snapshot0(); - - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertFalse(tcko.snapshot0BalanceOf(vm.addr(3)) == 0); - assertFalse(tcko.snapshot0BalanceOf(vm.addr(2)) == 750_000e6); - } - - function testSnapshot1() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - vm.prank(OYLAMA); - tcko.snapshot1(); - - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500_000e6); - } - - function testSnapshot2() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - vm.prank(OYLAMA); - tcko.snapshot2(); - - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.snapshot2BalanceOf(vm.addr(3)), 250_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500_000e6); - } - - function testAllSnapshotsRepeatedly() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 750_000e6); - - vm.prank(vm.addr(4)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.snapshot0BalanceOf(vm.addr(4)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(4)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 750_000e6); - - vm.prank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.snapshot2BalanceOf(vm.addr(4)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 1e12); - - assertEq(tcko.snapshot0BalanceOf(vm.addr(4)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(4)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 750_000e6); - } - - function testUsingSnapshotMultipleTimes() public { - uint256 balance = tcko.balanceOf(vm.addr(1)); - for (uint256 i = 1; i <= 10; ++i) { - vm.prank(OYLAMA); - tcko.snapshot0(); - - uint256 amount = i * 10e6; - for (uint256 x = 1; x <= 20; ++x) { - vm.prank(vm.addr(x)); - tcko.transfer(vm.addr(50), amount); - } - - for (uint256 y = 1; y <= 20; ++y) { - assertEq(tcko.snapshot0BalanceOf(vm.addr(y)), balance); - } - - balance = balance - amount; - } - } - - function testSnapshotValuesArePreserved() public { - uint256 balance = tcko.balanceOf(vm.addr(1)); - for (uint256 i = 1; i <= 20; ++i) { - vm.prank(OYLAMA); - tcko.snapshot1(); - - uint256 amount = i * 10e6; - for (uint256 j = 1; j <= 20; ++j) { - vm.prank(vm.addr(j)); - tcko.transfer(vm.addr((j % 20) + 1), amount); - } - - for (uint256 j = 1; j <= 20; ++j) { - assertEq(tcko.balanceOf(vm.addr(i)), balance); - assertEq(tcko.snapshot1BalanceOf(vm.addr(j)), balance); - } - - for (uint256 j = 1; j <= 20; ++j) { - vm.prank(vm.addr(j)); - tcko.transfer(vm.addr(50), amount); - } - - for (uint256 j = 1; j <= 20; ++j) { - assertEq(tcko.balanceOf(vm.addr(i)), balance - amount); - assertEq(tcko.snapshot1BalanceOf(vm.addr(j)), balance); - } - - for (uint256 j = 1; j <= 20; ++j) { - vm.prank(vm.addr(50)); - tcko.transfer(vm.addr(j), amount); - } - - for (uint256 j = 1; j <= 20; ++j) { - assertEq(tcko.balanceOf(vm.addr(i)), balance); - assertEq(tcko.snapshot1BalanceOf(vm.addr(j)), balance); - } - - for (uint256 j = 1; j <= 20; ++j) { - vm.prank(vm.addr(j)); - tcko.transfer(vm.addr(50), amount); - } - - balance = balance - amount; - } - } - - function testSnapshot0WrapsAround() public { - vm.prank(OYLAMA); - tcko.snapshot0(); - vm.store( - address(tcko), - bytes32(uint256(6)), - bytes32(((uint256(1) << 24) - 1) << 232) - ); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250_000e6); - - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(2)); - tcko.transfer(vm.addr(1), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250_000e6); - } - - function testSnapshot1WrapsAround() public { - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 232, - 1 - ); - vm.store( - address(tcko), - bytes32(uint256(6)), - bytes32(((uint256(1) << 21) - 1) << 212) - ); - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 232, - 1 - ); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 250_000e6); - - vm.prank(OYLAMA); - tcko.snapshot1(); - - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 232, - 1 - ); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(2)); - tcko.transfer(vm.addr(1), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 250_000e6); - } - - function testSnapshot2WrapsAround() public { - vm.prank(OYLAMA); - tcko.snapshot1(); - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 212, - 1 - ); - vm.store( - address(tcko), - bytes32(uint256(6)), - bytes32(((uint256(1) << 21) - 1) << 192) - ); - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 212, - 1 - ); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 250_000e6); - - vm.prank(OYLAMA); - tcko.snapshot2(); - - assertEq( - uint256(vm.load(address(tcko), bytes32(uint256(6)))) >> 212, - 1 - ); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(vm.addr(2)); - tcko.transfer(vm.addr(1), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 250_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500_000e6); - - vm.prank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 250_000e6); - } - - function testConsumeSnapshotNBalance3Snapshots() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1 + 1), 50_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 200_000e6); - uint256 snapshot0Balance = tcko.snapshot0BalanceOf(vm.addr(1)); - uint256 snapshot0ConsumedBalance = tcko.consumeSnapshot0Balance( - vm.addr(1) - ); - vm.stopPrank(); - assertEq(snapshot0Balance, 200_000e6); - assertEq(snapshot0Balance, snapshot0ConsumedBalance); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1 + 1)), 300_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - - vm.prank(vm.addr(1 + 1)); - tcko.transfer(vm.addr(1), 50_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250_000e6); - uint256 snapshot1Balance = tcko.snapshot1BalanceOf(vm.addr(1)); - uint256 snapshot1ConsumedBalance = tcko.consumeSnapshot1Balance( - vm.addr(1) - ); - vm.stopPrank(); - assertEq(snapshot1Balance, 250_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 0); - assertEq(snapshot1Balance, snapshot1ConsumedBalance); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1 + 1)), 250_000e6); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1 + 1), 25_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 225_000e6); - uint256 snapshot2Balance = tcko.snapshot2BalanceOf(vm.addr(1)); - uint256 snapshot2ConsumedBalance = tcko.consumeSnapshot2Balance( - vm.addr(1) - ); - vm.stopPrank(); - assertEq(snapshot2Balance, 225_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 0); - assertEq(snapshot2Balance, snapshot2ConsumedBalance); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1 + 1)), 275_000e6); - } - - function testConsumeSnapshot0Balance() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 100_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 150_000e6); - assertEq( - tcko.snapshot0BalanceOf(vm.addr(1)), - tcko.consumeSnapshot0Balance(vm.addr(1)) - ); - vm.stopPrank(); - assertEq(tcko.balanceOf(vm.addr(1)), 150_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 350_000e6); - - vm.prank(OYLAMA); - assertEq(tcko.consumeSnapshot0Balance(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - } - - function testConsumeSnapshot1Balance() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 100_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 150_000e6); - assertEq( - tcko.snapshot1BalanceOf(vm.addr(1)), - tcko.consumeSnapshot1Balance(vm.addr(1)) - ); - vm.stopPrank(); - assertEq(tcko.balanceOf(vm.addr(1)), 150_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 350_000e6); - - vm.prank(OYLAMA); - assertEq(tcko.consumeSnapshot1Balance(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - } - - function testConsumeSnapshot2Balance() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 100_000e6); - - vm.startPrank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 150_000e6); - assertEq( - tcko.snapshot2BalanceOf(vm.addr(1)), - tcko.consumeSnapshot2Balance(vm.addr(1)) - ); - vm.stopPrank(); - assertEq(tcko.balanceOf(vm.addr(1)), 150_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 350_000e6); - - vm.prank(OYLAMA); - assertEq(tcko.consumeSnapshot2Balance(vm.addr(2)), 350_000e6); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 350_000e6); - } - - function testConsumeSnapshotBalanceWithTransactions() public { - vm.prank(OYLAMA); - tcko.snapshot0(); - - for (uint256 i = 1; i <= 4; ++i) { - assertEq(tcko.balanceOf(vm.addr(i)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(i)), 250_000e6); - } - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250_000e6); - - uint256 totalSupply = tcko.totalSupply(); - vm.prank(vm.addr(3)); - tcko.transfer(DAO_KASASI, 250_000e6); - - assertEq(tcko.totalSupply(), totalSupply - 250_000e6); - assertEq(tcko.balanceOf(vm.addr(3)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250_000e6); - - vm.prank(vm.addr(2)); - tcko.transfer(vm.addr(1), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(2)), 250_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250_000e6); - - vm.prank(vm.addr(4)); - tcko.transfer(vm.addr(3), 100_000e6); - - assertEq(tcko.balanceOf(vm.addr(3)), 100_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(4)), 150_000e6); - assertEq(tcko.snapshot0BalanceOf(vm.addr(4)), 250_000e6); - - vm.startPrank(OYLAMA); - for (uint256 i = 1; i <= 4; ++i) { - assertEq( - tcko.snapshot0BalanceOf(vm.addr(i)), - tcko.consumeSnapshot0Balance(vm.addr(i)) - ); - - assertEq(tcko.snapshot0BalanceOf(vm.addr(i)), 0); - } - vm.stopPrank(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 250_000e6); - assertEq(tcko.balanceOf(vm.addr(3)), 100_000e6); - assertEq(tcko.balanceOf(vm.addr(4)), 150_000e6); - } - - function testBalanceIsPreservedAfterConsume() external { - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - vm.prank(OYLAMA); - tcko.snapshot2(); - vm.prank(OYLAMA); - tcko.consumeSnapshot2Balance(vm.addr(1)); - assertEq(tcko.balanceOf(vm.addr(1)), 250_000e6); - } -} diff --git a/test/TCKOTest.sol b/test/TCKOTest.sol deleted file mode 100644 index e79706f..0000000 --- a/test/TCKOTest.sol +++ /dev/null @@ -1,896 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import "contracts/TCKO.sol"; -import "forge-std/Test.sol"; -import "interfaces/testing/MockDAOKasasiV1.sol"; -import {MockERC20Permit} from "interfaces/testing/MockTokens.sol"; - -contract TCKOTest is Test { - TCKO private tcko; - KilitliTCKO private tckok; - IDAOKasasi private daoKasasi; - MockERC20Permit private testToken; - - function setUp() public { - vm.prank(TCKO_DEPLOYER); - tcko = new TCKO(false); - - vm.prank(TCKOK_DEPLOYER); - tckok = new KilitliTCKO(); - - vm.prank(DAO_KASASI_DEPLOYER); - daoKasasi = new MockDAOKasasiV1(); - - mintAll(1e12); - } - - function mintAll(uint256 amount) public { - vm.startPrank(DEV_KASASI); - for (uint256 i = 1; i <= 20; ++i) - tcko.mintTo((amount << 160) | uint160(vm.addr(i))); - vm.stopPrank(); - } - - function testDAOAuthentication() public { - vm.expectRevert(); - tcko.mintTo((uint256(1) << 160) | uint160(vm.addr(1))); - - vm.expectRevert(); - tcko.setPresale2Contract(vm.addr(1337)); - - vm.expectRevert(); - tcko.incrementDistroStage(DistroStage.Presale2); - } - - function testSnapshotAuthentication() public { - vm.expectRevert(); - tcko.snapshot0(); - - vm.expectRevert(); - tcko.snapshot1(); - - vm.expectRevert(); - tcko.snapshot2(); - - vm.startPrank(OYLAMA); - tcko.snapshot0(); - tcko.snapshot1(); - tcko.snapshot2(); - vm.stopPrank(); - } - - function testShouldCompleteAllRounds() public { - assertEq(tcko.totalSupply(), 20e12); - assertEq(tckok.totalSupply(), 15e12); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2); - mintAll(1e12); - - assertEq(tcko.totalSupply(), 40e12); - assertEq(tckok.totalSupply(), 30e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleStart); - - assertEq(tcko.totalSupply(), 60e12); - assertEq(tckok.totalSupply(), 30e12); - assertEq(tcko.balanceOf(DAO_KASASI), 20e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleEnd); - - tckok.unlock(vm.addr(1)); - tckok.unlock(vm.addr(2)); - - assertEq(tcko.balanceOf(vm.addr(1)), 1e12); - assertEq(tcko.balanceOf(vm.addr(2)), 1_500e9); - - tckok.unlockAllEven(); - - assertEq(tckok.balanceOf(vm.addr(1)), 750e9); - assertEq(tckok.balanceOf(vm.addr(2)), 750e9); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOAMMStart); - - assertEq(tcko.totalSupply(), 80e12); - assertEq(tckok.totalSupply(), 15e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2Unlock); - - tckok.unlockAllOdd(); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalMint); - mintAll(1e12); - - assertEq(tckok.unlock(vm.addr(1)), false); - - assertEq(tckok.balanceOf(vm.addr(1)), 750e9); - - vm.warp(1925097600); - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalUnlock); - tckok.unlock(vm.addr(1)); - - assertEq(tckok.balanceOf(vm.addr(1)), 0); - - tckok.unlockAllEven(); - - assertEq(tckok.balanceOf(vm.addr(12)), 0); - assertEq(tckok.balanceOf(vm.addr(14)), 0); - assertEq(tckok.balanceOf(vm.addr(17)), 0); - - assertEq(tcko.balanceOf(address(tckok)), tckok.totalSupply()); - } - - function testShouldNotUnlockBeforeMaturity() public { - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllEven(); - - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllOdd(); - - tckok.unlock(vm.addr(10)); - assertEq(tcko.balanceOf(vm.addr(10)), 250e9); - assertEq(tckok.balanceOf(vm.addr(10)), 750e9); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2); - - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllEven(); - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllOdd(); - - mintAll(1e12); - - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllEven(); - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllOdd(); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleStart); - - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllEven(); - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllOdd(); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleEnd); - - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllOdd(); - tckok.unlockAllEven(); - - assertEq(tcko.balanceOf(vm.addr(2)), 1250e9); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOAMMStart); - tckok.unlock(vm.addr(1)); - - assertEq(tcko.balanceOf(vm.addr(1)), 1250e9); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2Unlock); - tckok.unlock(vm.addr(1)); - assertEq(tcko.balanceOf(vm.addr(1)), 2e12); - - tckok.unlockAllOdd(); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalMint); - - assertEq(tcko.totalSupply(), 80e12); - assertEq(tckok.totalSupply(), 0); - - mintAll(1e12); - vm.expectRevert("TCKO-k: Not matured"); - tckok.unlockAllEven(); - - assertEq(tcko.totalSupply(), 100e12); - assertEq(tckok.totalSupply(), 15e12); - - vm.warp(1835470800000); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalUnlock); - tckok.unlockAllEven(); - - assertEq(tckok.totalSupply(), 0); - } - - function testPreserveTotalPlusBurnedEqualsMinted() public { - assertEq(tcko.totalSupply(), 20e12); - assertEq(tckok.totalSupply(), 15e12); - - for (uint256 i = 9; i <= 20; ++i) { - vm.prank(vm.addr(i)); - tcko.transfer(address(daoKasasi), 250e9); - } - - assertEq(tcko.totalSupply(), 17e12); - assertEq(tckok.totalSupply(), 15e12); - assertEq(tcko.totalMinted(), 20e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2); - mintAll(1e12); - - assertEq(tcko.totalSupply(), 37e12); - - for (uint256 i = 9; i <= 20; ++i) { - vm.prank(vm.addr(i)); - tcko.transfer(address(daoKasasi), 250e9); - } - - assertEq(tcko.totalSupply(), 34e12); - } - - function testPreservesIndividualBalances() public { - for (uint256 i = 1; i < 20; ++i) { - vm.prank(vm.addr(i)); - tcko.transfer(vm.addr(i + 1), 250e9); - } - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(20)), 500e9); - } - - function testTransferGas() public { - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250e9); - } - - function testPreventOverspending() public { - vm.prank(vm.addr(1)); - vm.expectRevert(); - tcko.transfer(vm.addr(2), 251e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(3), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - - vm.prank(vm.addr(4)); - tcko.transfer(vm.addr(5), 250e9); - vm.prank(vm.addr(5)); - vm.expectRevert(); - tcko.transfer(vm.addr(6), 750e9); - } - - function testAuthorizedPartiesCanSpendOnOwnersBehalf() public { - vm.prank(vm.addr(1)); - tcko.approve(vm.addr(2), 1e12); - - assertEq(tcko.allowance(vm.addr(1), vm.addr(2)), 1e12); - - vm.prank(vm.addr(2)); - tcko.transferFrom(vm.addr(1), vm.addr(3), 250e9); - - assertEq(tcko.allowance(vm.addr(1), vm.addr(2)), 750e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(3)), 500e9); - } - - function testUsersCanAdjustAllowance() public { - vm.startPrank(vm.addr(1)); - tcko.increaseAllowance(vm.addr(2), 3); - - vm.expectRevert(stdError.arithmeticError); - tcko.increaseAllowance(vm.addr(2), type(uint256).max); - - vm.expectRevert(stdError.arithmeticError); - tcko.decreaseAllowance(vm.addr(2), 4); - - tcko.decreaseAllowance(vm.addr(2), 2); - vm.stopPrank(); - - vm.startPrank(vm.addr(2)); - vm.expectRevert(stdError.arithmeticError); - tcko.transferFrom(vm.addr(1), vm.addr(2), 2); - - tcko.transferFrom(vm.addr(1), vm.addr(2), 1); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9 - 1); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9 + 1); - } - - function testSnapshot0Preserved() public { - vm.prank(OYLAMA); - tcko.snapshot0(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250e9); - - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 150e9); - - vm.startPrank(vm.addr(2)); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - vm.stopPrank(); - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(3)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 150e9); - - vm.prank(OYLAMA); - tcko.snapshot0(); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(3)), 250e9); - } - - function testSnapshot0PreservedOnSelfTransfer() public { - vm.prank(OYLAMA); - tcko.snapshot0(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 250e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot0(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr(1)), 250e9); - } - - function testSnapshot0Fuzz(uint8 from, uint8 to, uint256 amount) public { - vm.assume(from % 20 != to % 20); - amount %= 250e9; - - vm.prank(OYLAMA); - tcko.snapshot0(); - - vm.prank(vm.addr((from % 20) + 1)); - tcko.transfer(vm.addr((to % 20) + 1), amount); - - assertEq(tcko.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); - assertEq(tcko.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); - assertEq(tcko.snapshot0BalanceOf(vm.addr((from % 20) + 1)), 250e9); - assertEq(tcko.snapshot0BalanceOf(vm.addr((to % 20) + 1)), 250e9); - } - - function testSnapshot1Preserved() public { - vm.prank(OYLAMA); - tcko.snapshot1(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250e9); - - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 150e9); - - vm.startPrank(vm.addr(2)); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(3)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 150e9); - vm.stopPrank(); - - vm.prank(OYLAMA); - tcko.snapshot1(); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(3)), 250e9); - } - - function testSnapshot1PreservedOnSelfTransfer() public { - vm.prank(OYLAMA); - tcko.snapshot1(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 250e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot1(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr(1)), 250e9); - } - - function testSnapshot1Fuzz(uint8 from, uint8 to, uint256 amount) public { - vm.assume(from % 20 != to % 20); - - amount %= 250e9; - vm.prank(OYLAMA); - tcko.snapshot1(); - - vm.prank(vm.addr((from % 20) + 1)); - tcko.transfer(vm.addr((to % 20) + 1), amount); - - assertEq(tcko.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); - assertEq(tcko.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); - assertEq(tcko.snapshot1BalanceOf(vm.addr((from % 20) + 1)), 250e9); - assertEq(tcko.snapshot1BalanceOf(vm.addr((to % 20) + 1)), 250e9); - } - - function testSnapshot2Preserved() public { - vm.prank(OYLAMA); - tcko.snapshot2(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250e9); - - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(3)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(3)), 250e9); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.balanceOf(vm.addr(1)), 100e9); - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - assertEq(tcko.balanceOf(vm.addr(3)), 150e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(3)), 150e9); - - vm.startPrank(vm.addr(2)); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - tcko.transfer(vm.addr(3), 50e9); - tcko.transfer(vm.addr(1), 50e9); - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.balanceOf(vm.addr(2)), 250e9); - assertEq(tcko.balanceOf(vm.addr(3)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 100e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 500e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(3)), 150e9); - vm.stopPrank(); - - vm.prank(OYLAMA); - tcko.snapshot2(); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(2)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(3)), 250e9); - } - - function testSnapshot2PreservedOnSelfTransfer() public { - vm.prank(OYLAMA); - tcko.snapshot2(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 250e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - - vm.prank(OYLAMA); - tcko.snapshot2(); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(1), 100e9); - - assertEq(tcko.balanceOf(vm.addr(1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr(1)), 250e9); - } - - function testSnapshot2Fuzz(uint8 from, uint8 to, uint256 amount) public { - vm.assume(from % 20 != to % 20); - - amount %= 250e9; - vm.prank(OYLAMA); - tcko.snapshot2(); - - vm.prank(vm.addr((from % 20) + 1)); - tcko.transfer(vm.addr((to % 20) + 1), amount); - - assertEq(tcko.balanceOf(vm.addr((from % 20) + 1)), 250e9 - amount); - assertEq(tcko.balanceOf(vm.addr((to % 20) + 1)), 250e9 + amount); - assertEq(tcko.snapshot2BalanceOf(vm.addr((from % 20) + 1)), 250e9); - assertEq(tcko.snapshot2BalanceOf(vm.addr((to % 20) + 1)), 250e9); - } - - function authorizePayment( - uint256 ownerPrivateKey, - address spender, - uint256 amount, - uint256 deadline, - uint256 nonce - ) internal view returns (uint8, bytes32, bytes32) { - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - tcko.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9, - vm.addr(ownerPrivateKey), - spender, - amount, - nonce, - deadline - ) - ) - ) - ); - return vm.sign(ownerPrivateKey, digest); - } - - function testPermit() public { - vm.prank(vm.addr(2)); - vm.expectRevert(stdError.arithmeticError); - tcko.transferFrom(vm.addr(1), vm.addr(2), 250e9); - - uint256 time = block.timestamp + 1000; - (uint8 v, bytes32 r, bytes32 s) = authorizePayment( - 1, - vm.addr(2), - 250e9, - time, - 0 - ); - tcko.permit(vm.addr(1), vm.addr(2), 250e9, time, v, r, s); - vm.prank(vm.addr(2)); - tcko.transferFrom(vm.addr(1), vm.addr(2), 250e9); - assertEq(tcko.balanceOf(vm.addr(2)), 500e9); - } - - function testTokenMethods() external { - assertEq(tcko.decimals(), tckok.decimals()); - // Increase coverage so we can always aim at 100%. - assertEq(tcko.name(), "KimlikDAO Tokeni"); - assertEq(tckok.name(), "Kilitli TCKO"); - assertEq(tcko.maxSupply(), 100_000_000e6); - - assertEq(tcko.circulatingSupply(), 5_000_000e6); - assertEq( - bytes32(bytes(tckok.symbol()))[0], - bytes32(bytes(tcko.symbol()))[0] - ); - assertEq( - bytes32(bytes(tckok.symbol()))[1], - bytes32(bytes(tcko.symbol()))[1] - ); - assertEq( - bytes32(bytes(tckok.symbol()))[2], - bytes32(bytes(tcko.symbol()))[2] - ); - assertEq( - bytes32(bytes(tckok.symbol()))[3], - bytes32(bytes(tcko.symbol()))[3] - ); - } - - event Transfer(address indexed from, address indexed to, uint256 amount); - - function testTransfer() external { - vm.startPrank(vm.addr(1)); - - vm.expectRevert(); - tcko.transfer(address(0), 250_000e6); - - vm.expectRevert(); - tcko.transfer(address(tcko), 250_000e6); - - vm.expectRevert(); - tcko.transfer(address(tckok), 250_000e6); - - vm.expectRevert(); - tcko.transfer(vm.addr(2), 251_000e6); - - vm.expectEmit(true, true, false, true, address(tcko)); - emit Transfer(vm.addr(1), vm.addr(2), 250_000e6); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.totalSupply(), 20_000_000e6); - - vm.stopPrank(); - - vm.startPrank(vm.addr(2)); - - tcko.transfer(DAO_KASASI, 500_000e6); - - assertEq(tcko.totalSupply() + 500_000e6, tcko.supplyCap()); - - vm.stopPrank(); - - vm.startPrank(DEV_KASASI); - vm.expectRevert(); - tcko.incrementDistroStage(DistroStage.Presale1); - - tcko.incrementDistroStage(DistroStage.Presale2); - vm.stopPrank(); - mintAll(1e12); - - assertEq(tcko.totalSupply() + 500_000e6, tcko.supplyCap()); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleStart); - - assertEq(tcko.supplyCap(), 60_000_000e6); - assertEq(tcko.totalSupply() + 500_000e6, tcko.supplyCap()); - assertEq( - tcko.circulatingSupply(), - 20_000_000e6 + 40_000_000e6 / 4 - 500_000e6 - ); - } - - function testTransferFrom() external { - vm.startPrank(vm.addr(1)); - tcko.approve(vm.addr(11), 200_000e6); - tcko.approve(address(tcko), 50_000e6); - tcko.approve(address(0), 50_000e6); - tcko.approve(address(tckok), 50_000e6); - vm.stopPrank(); - - vm.startPrank(vm.addr(11)); - vm.expectRevert(); - tcko.transferFrom(vm.addr(1), vm.addr(2), 201_000e6); - - vm.expectRevert(); - tcko.transferFrom(vm.addr(1), address(0), 200_000e6); - - vm.expectRevert(); - tcko.transferFrom(vm.addr(1), address(tcko), 200_000e6); - - vm.expectRevert(); - tcko.transferFrom(vm.addr(1), address(tckok), 200_000e6); - - tcko.transferFrom(vm.addr(1), vm.addr(2), 200_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 50_000e6); - assertEq(tcko.balanceOf(vm.addr(2)), 450_000e6); - assertEq(tckok.balanceOf(vm.addr(1)), 750_000e6); - assertEq(tckok.balanceOf(vm.addr(2)), 750_000e6); - - vm.stopPrank(); - - vm.prank(vm.addr(3)); - tcko.approve(vm.addr(13), 150_000e6); - - vm.startPrank(vm.addr(13)); - tcko.transferFrom(vm.addr(3), DAO_KASASI, 150_000e6); - - assertEq(tcko.balanceOf(vm.addr(3)), 100_000e6); - assertEq(tcko.totalSupply(), 20_000_000e6 - 150_000e6); - - vm.stopPrank(); - - vm.prank(vm.addr(4)); - tcko.approve(vm.addr(14), 251_000e6); - - vm.startPrank(vm.addr(14)); - vm.expectRevert(); - tcko.transferFrom(vm.addr(4), vm.addr(5), 251_000e6); - - vm.stopPrank(); - } - - function testPresale2Contract() external { - vm.expectRevert(); - tcko.setPresale2Contract(vm.addr(0x94E008A7E2)); - - vm.startPrank(DEV_KASASI); - tcko.setPresale2Contract(vm.addr(0x94E008A7E2)); - tcko.incrementDistroStage(DistroStage.Presale2); - vm.stopPrank(); - - vm.startPrank(vm.addr(0x94E008A7E2)); - vm.expectRevert(); - tcko.mintTo(uint160(address(tckok)) | (1 << 160)); - vm.expectRevert(); - tcko.mintTo(uint160(address(DAO_KASASI)) | (1 << 160)); - - tcko.mintTo(uint160(vm.addr(1)) | (20_000_000e6 << 160)); - vm.expectRevert(); - tcko.mintTo(uint160(vm.addr(1)) | (1 << 160)); - vm.stopPrank(); - - assertEq(tcko.balanceOf(vm.addr(1)), 5_250_000e6); - } - - function testTCKOKrescueToken() external { - vm.startPrank(vm.addr(20)); - testToken = new MockERC20Permit("TestToken", "TT", 6); - testToken.transfer(address(tckok), 4e7); - vm.stopPrank(); - - assertEq(testToken.balanceOf(address(tckok)), 4e7); - assertEq(testToken.balanceOf(vm.addr(20)), 6e7); - - vm.startPrank(vm.addr(1)); - vm.expectRevert(); - tckok.rescueToken(testToken); - vm.stopPrank(); - - vm.startPrank(OYLAMA); - vm.expectRevert(); - tckok.rescueToken(IERC20Permit(TCKO_ADDR)); - vm.stopPrank(); - - vm.startPrank(OYLAMA); - tckok.rescueToken(testToken); - vm.stopPrank(); - - assertEq(testToken.balanceOf(DAO_KASASI), 4e7); - assertEq(testToken.balanceOf(address(tckok)), 0); - } - - function testTCKOKselfDestruct() external { - vm.startPrank(vm.addr(1)); - vm.expectRevert(); - // tckok.selfDestruct(); - vm.stopPrank(); - - vm.startPrank(DEV_KASASI); - vm.expectRevert(); - // tckok.selfDestruct(); - vm.stopPrank(); - - vm.prank(vm.addr(1)); - tcko.transfer(vm.addr(2), 250_000e6); - - assertEq(tcko.balanceOf(vm.addr(1)), 0); - assertEq(tcko.balanceOf(vm.addr(2)), 500_000e6); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2); - mintAll(1e12); - - assertEq(tcko.totalSupply(), 40e12); - assertEq(tckok.totalSupply(), 30e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleStart); - - assertEq(tcko.totalSupply(), 60e12); - assertEq(tckok.totalSupply(), 30e12); - assertEq(tcko.balanceOf(DAO_KASASI), 20e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOSaleEnd); - - tckok.unlock(vm.addr(1)); - tckok.unlock(vm.addr(2)); - - assertEq(tcko.balanceOf(vm.addr(1)), 1e12); - assertEq(tcko.balanceOf(vm.addr(2)), 1_500e9); - - tckok.unlockAllEven(); - - assertEq(tckok.balanceOf(vm.addr(1)), 750e9); - assertEq(tckok.balanceOf(vm.addr(2)), 750e9); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.DAOAMMStart); - - assertEq(tcko.totalSupply(), 80e12); - assertEq(tckok.totalSupply(), 15e12); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.Presale2Unlock); - - tckok.unlockAllOdd(); - - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalMint); - mintAll(1e12); - - assertEq(tckok.unlock(vm.addr(1)), false); - - assertEq(tckok.balanceOf(vm.addr(1)), 750e9); - - vm.warp(1925097600); - vm.prank(DEV_KASASI); - tcko.incrementDistroStage(DistroStage.FinalUnlock); - - tckok.unlockAllEven(); - - vm.prank(DEV_KASASI); - // tckok.selfDestruct(); - - // Testing selfdestruct() is not implemented in anvil? - console.log(tckok.name()); - } -}