From 2861fcdd5524891642bc5bf7290d36a6733a21c4 Mon Sep 17 00:00:00 2001 From: KimlikDAO-bot Date: Wed, 1 May 2024 11:29:56 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=B8=20Re-introduce=20snapshot=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ethereum/KDAO.sol | 67 +++-- foundry.toml | 4 + lib/interfaces | 2 +- test/ethereum/KDAO.t.sol | 1 + test/zksync/KDAO.snapshot.t.sol | 470 ++++++++++++++++++++++++++++++++ test/zksync/KDAO.t.sol | 74 ++++- zksync/SSBalance.sol | 2 +- 7 files changed, 595 insertions(+), 25 deletions(-) create mode 100644 test/zksync/KDAO.snapshot.t.sol diff --git a/ethereum/KDAO.sol b/ethereum/KDAO.sol index ddcf051..422c3d5 100644 --- a/ethereum/KDAO.sol +++ b/ethereum/KDAO.sol @@ -74,11 +74,15 @@ contract KDAO is IERC20Permit { 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(this) && to != PROTOCOL_FUND); uint256 allowed = allowance[from][msg.sender]; - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; // Checked substraction - balanceOf[from] -= amount; // Checked substraction + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; // Checked subtraction + balanceOf[from] -= amount; // Checked subtraction unchecked { balanceOf[to] += amount; } @@ -134,20 +138,31 @@ contract KDAO is IERC20Permit { bytes32 public constant override DOMAIN_SEPARATOR = 0x6faf94999332059363533545c34fe71ffc0642c13cd1fa816c361b545eb33d9b; - bytes32 private constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + bytes32 private 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); @@ -164,11 +179,11 @@ contract KDAO is IERC20Permit { /////////////////////////////////////////////////////////////////////////// function redeem(uint256 amount) public { - // Redeemtion on non EVM chains require providing a proof of knowledge of the private key + // Redemption on non EVM chains require providing a proof of knowledge of the private key // of the redeemer address. Because of this, redemption is allowed from EOA only. require(msg.sender == tx.origin); uint48x2 total = totals; - balanceOf[msg.sender] -= amount; // Checked substraction + balanceOf[msg.sender] -= amount; // Checked subtraction ProtocolFund.redeem(RedeemInfoFrom(total.setLo(amount), msg.sender)); totals = total.dec(amount); } @@ -187,9 +202,13 @@ contract KDAO is IERC20Permit { // /////////////////////////////////////////////////////////////////////////// - event BridgeToZkSync(bytes32 indexed l2TxHash, address indexed from, address indexed to, uint256 amount); + event BridgeToZkSync( + bytes32 indexed l2TxHash, address indexed from, address indexed to, uint256 amount + ); event ClaimFailedZkSyncBridge(bytes32 indexed l2TxHash, address indexed addr, uint256 amount); - event AcceptBridgeFromZkSync(L2LogLocator indexed logLocator, address indexed addr, uint256 amount); + event AcceptBridgeFromZkSync( + L2LogLocator indexed logLocator, address indexed addr, uint256 amount + ); mapping(bytes32 => amountAddr) private bridgedAmountAddr; mapping(L2LogLocator => bool) private isLogProcessed; @@ -204,7 +223,7 @@ contract KDAO is IERC20Permit { addr = msg.sender; aaddr = aaddr.addAddr(msg.sender); } - balanceOf[msg.sender] -= amount; // Checked substraction + balanceOf[msg.sender] -= amount; // Checked subtraction l2TxHash = ZkSync.requestL2Transaction{value: msg.value}( KDAO_ZKSYNC, 0, @@ -216,13 +235,14 @@ contract KDAO is IERC20Permit { ); totals = totals.decLo(amount); bridgedAmountAddr[l2TxHash] = aaddr.setAddr(msg.sender); - emit BridgeToZkSync(l2TxHash, msg.sender, addr, amount); } - function claimFailedZkSyncBridge(bytes32 l2TxHash, L2LogLocator logLocator, bytes32[] calldata merkleProof) - public - { + function claimFailedZkSyncBridge( + bytes32 l2TxHash, + L2LogLocator logLocator, + bytes32[] calldata merkleProof + ) public { require( ZkSync.proveL1ToL2TransactionStatus( l2TxHash, @@ -245,7 +265,11 @@ contract KDAO is IERC20Permit { emit ClaimFailedZkSyncBridge(l2TxHash, addr, amount); } - function acceptBridgeFromZkSync(amountAddr aaddr, L2LogLocator logLocator, bytes32[] calldata merkleProof) public { + function acceptBridgeFromZkSync( + amountAddr aaddr, + L2LogLocator logLocator, + bytes32[] calldata merkleProof + ) public { L2Log memory l2Log = L2Log({ l2ShardId: 0, isService: true, @@ -255,7 +279,11 @@ contract KDAO is IERC20Permit { value: keccak256(abi.encode(aaddr)) }); require(!isLogProcessed[logLocator]); - require(ZkSync.proveL2LogInclusion(logLocator.batchNumber(), logLocator.messageIndex(), l2Log, merkleProof)); + require( + ZkSync.proveL2LogInclusion( + logLocator.batchNumber(), logLocator.messageIndex(), l2Log, merkleProof + ) + ); (uint256 amount, address addr) = aaddr.unpack(); uint48x2 total = totals.incLo(amount); @@ -265,7 +293,6 @@ contract KDAO is IERC20Permit { balanceOf[addr] += amount; } isLogProcessed[logLocator] = true; - emit AcceptBridgeFromZkSync(logLocator, addr, amount); } } diff --git a/foundry.toml b/foundry.toml index c21f390..9ecfdb2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,3 +17,7 @@ src = "ethereum" [profile.zksync] solc_version = "0.8.24" src = "zksync" +test = "test/zksync" +fallback_oz = true +is_system = false +mode = "3" diff --git a/lib/interfaces b/lib/interfaces index dc325e7..d27e48f 160000 --- a/lib/interfaces +++ b/lib/interfaces @@ -1 +1 @@ -Subproject commit dc325e76255f7fd171ae1142a85c9f04dd81b1ce +Subproject commit d27e48f93399e49d658b5b8cec3325e0a28f0122 diff --git a/test/ethereum/KDAO.t.sol b/test/ethereum/KDAO.t.sol index 674348c..1ab8a46 100644 --- a/test/ethereum/KDAO.t.sol +++ b/test/ethereum/KDAO.t.sol @@ -49,6 +49,7 @@ contract KDAOTest is Test { vm.prank(address(0x1337)); kdao.transfer(address(0x1338), 100e6); + assertEq(kdao.balanceOf(address(0x1337)), 0); assertEq(kdao.balanceOf(address(0x1338)), 200e6); vm.startPrank(address(0x1338)); diff --git a/test/zksync/KDAO.snapshot.t.sol b/test/zksync/KDAO.snapshot.t.sol new file mode 100644 index 0000000..e9fcc68 --- /dev/null +++ b/test/zksync/KDAO.snapshot.t.sol @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {Test, console} from "forge-std/Test.sol"; +import {IProtocolFund} from "interfaces/kimlikdao/IProtocolFund.sol"; +import { + KDAO_LOCKED, + KDAO_LOCKED_DEPLOYER, + KDAO_ZKSYNC, + KDAO_ZKSYNC_DEPLOYER, + VOTING +} from "interfaces/kimlikdao/addresses.sol"; +import {amountAddrFrom} from "interfaces/types/amountAddr.sol"; +import {computeCreateAddress as computeZkSyncCreateAddress} from "interfaces/zksync/IZkSync.sol"; +import {KDAO} from "zksync/KDAO.sol"; +import {KDAOLocked} from "zksync/KDAOLocked.sol"; + +contract KDAOSnapshotTest is Test { + KDAO private kdao; + KDAOLocked private kdaol; + + function mintAll(uint256 amount) public { + vm.startPrank(VOTING); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mint(amountAddrFrom(amount, vm.addr(i))); + } + vm.stopPrank(); + } + + function setUp() external { + vm.etch(KDAO_LOCKED, type(KDAOLocked).runtimeCode); + kdaol = KDAOLocked(KDAO_LOCKED); + + vm.etch(KDAO_ZKSYNC, type(KDAO).runtimeCode); + kdao = KDAO(KDAO_ZKSYNC); + + mintAll(1e12); + } + + function testAddressConsistency() external pure { + assertEq(computeZkSyncCreateAddress(KDAO_LOCKED_DEPLOYER, 0), KDAO_LOCKED); + assertEq(computeZkSyncCreateAddress(KDAO_ZKSYNC_DEPLOYER, 0), KDAO_ZKSYNC); + } + + function testSnapshot0() external { + vm.prank(vm.addr(1)); + kdao.transfer(vm.addr(2), 250_000e6); + 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() external { + 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() external { + 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() external { + 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() external { + 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() external { + 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() external { + vm.prank(VOTING); + kdao.snapshot0(); + vm.store(address(kdao), bytes32(uint256(5)), 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() external { + vm.prank(VOTING); + kdao.snapshot0(); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(5)))) >> 232, 1); + vm.store(address(kdao), bytes32(uint256(5)), bytes32(((uint256(1) << 21) - 1) << 212)); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(5)))) >> 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(5)))) >> 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() external { + vm.prank(VOTING); + kdao.snapshot1(); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(5)))) >> 212, 1); + vm.store(address(kdao), bytes32(uint256(5)), bytes32(((uint256(1) << 21) - 1) << 192)); + assertEq(uint256(vm.load(address(kdao), bytes32(uint256(5)))) >> 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(5)))) >> 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() external { + 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() external { + 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() external { + 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() external { + 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() external { + 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); + + vm.prank(vm.addr(3)); + kdao.transfer(vm.addr(1337), 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/zksync/KDAO.t.sol b/test/zksync/KDAO.t.sol index ae7ea88..bcd38cb 100644 --- a/test/zksync/KDAO.t.sol +++ b/test/zksync/KDAO.t.sol @@ -3,16 +3,38 @@ pragma solidity ^0.8.0; import {Test} from "forge-std/Test.sol"; -import {KDAO_ZKSYNC, KDAO_ZKSYNC_DEPLOYER} from "interfaces/kimlikdao/addresses.sol"; +import { + KDAO_LOCKED, + KDAO_LOCKED_DEPLOYER, + KDAO_ZKSYNC, + KDAO_ZKSYNC_DEPLOYER, + VOTING +} from "interfaces/kimlikdao/addresses.sol"; +import {amountAddrFrom} from "interfaces/types/amountAddr.sol"; import {uint48x2From} from "interfaces/types/uint48x2.sol"; import {KDAO} from "zksync/KDAO.sol"; +import {KDAOLocked} from "zksync/KDAOLocked.sol"; contract KDAOTest is Test { KDAO private kdao; + KDAOLocked private kdaol; + + function mintAll(uint256 amount) public { + vm.startPrank(VOTING); + for (uint256 i = 1; i <= 20; ++i) { + kdao.mint(amountAddrFrom(amount, vm.addr(i))); + } + vm.stopPrank(); + } function setUp() public { - vm.prank(KDAO_ZKSYNC_DEPLOYER); - kdao = new KDAO(); + vm.etch(KDAO_LOCKED, type(KDAOLocked).runtimeCode); + kdaol = KDAOLocked(KDAO_LOCKED); + + vm.etch(KDAO_ZKSYNC, type(KDAO).runtimeCode); + kdao = KDAO(KDAO_ZKSYNC); + + mintAll(1e12); } function testDomainSeparator() external view { @@ -31,4 +53,50 @@ contract KDAOTest is Test { ) ); } + + 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 testTransfer() 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 testTransferFrom() 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); + } } diff --git a/zksync/SSBalance.sol b/zksync/SSBalance.sol index 87b55ad..d0d92ce 100644 --- a/zksync/SSBalance.sol +++ b/zksync/SSBalance.sol @@ -80,7 +80,7 @@ function reset1(SSBalance self, SSBalance t) pure returns (SSBalance) { function reset2(SSBalance self, SSBalance t) pure returns (SSBalance) { return SSBalance.wrap( - (SSBalance.unwrap(self) & ~((BALANCE_MASK << 96) | TICK1)) | (SSBalance.unwrap(t) & TICK1) + (SSBalance.unwrap(self) & ~((BALANCE_MASK << 144) | TICK2)) | (SSBalance.unwrap(t) & TICK2) ); }