From 653474af8f2e188e17ab3166b248160908846fd7 Mon Sep 17 00:00:00 2001 From: stadolf Date: Fri, 5 Jul 2024 16:40:26 +0200 Subject: [PATCH] full mainnet fork test case for new Tokenizer Signed-off-by: stadolf --- test/Forking/Tokenizer13UpgradeForkTest.t.sol | 181 ++++++++++++++---- 1 file changed, 144 insertions(+), 37 deletions(-) diff --git a/test/Forking/Tokenizer13UpgradeForkTest.t.sol b/test/Forking/Tokenizer13UpgradeForkTest.t.sol index 279afd3b..e257910a 100644 --- a/test/Forking/Tokenizer13UpgradeForkTest.t.sol +++ b/test/Forking/Tokenizer13UpgradeForkTest.t.sol @@ -4,19 +4,16 @@ pragma solidity ^0.8.18; import "forge-std/Test.sol"; import { console } from "forge-std/console.sol"; -//import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { IPNFT } from "../../src/IPNFT.sol"; import { MustOwnIpnft, AlreadyTokenized, Tokenizer } from "../../src/Tokenizer.sol"; import { Tokenizer12 } from "../../src/helpers/test-upgrades/Tokenizer12.sol"; +import { IPToken12, OnlyIssuerOrOwner } from "../../src/helpers/test-upgrades/IPToken12.sol"; import { IPToken, TokenCapped, Metadata } from "../../src/IPToken.sol"; import { IPermissioner, BlindPermissioner } from "../../src/Permissioner.sol"; -// an error thrown by IPToken before 1.3 -//error OnlyIssuerOrOwner(); - contract Tokenizer13UpgradeForkTest is Test { using SafeERC20Upgradeable for IPToken; @@ -32,71 +29,93 @@ contract Tokenizer13UpgradeForkTest is Test { address mainnetTokenizer = 0x58EB89C69CB389DBef0c130C6296ee271b82f436; address mainnetIPNFT = 0xcaD88677CA87a7815728C72D74B4ff4982d54Fc1; - // old IP Token implementation - address vitaFASTAddress = 0x6034e0d6999741f07cb6Fb1162cBAA46a1D33d36; - - // https://app.safe.global/home?safe=eth:0xf7990CD398daFB4fe5Fd6B9228B8e6f72b296555 - address vitaFASTMultisig = 0xf7990CD398daFB4fe5Fd6B9228B8e6f72b296555; + address vitaDaoTreasury = 0xF5307a74d1550739ef81c6488DC5C7a6a53e5Ac2; // paulhaas.eth address paulhaas = 0x45602BFBA960277bF917C1b2007D1f03d7bd29e4; - // ValleyDAO multisig - address valleyDaoMultisig = 0xD920E60b798A2F5a8332799d8a23075c9E77d5F8; - - // ValleyDAO IPNFT - uint256 valleyDaoIpnftId = 3; + IPNFT ipnft = IPNFT(mainnetIPNFT); + Tokenizer tokenizer13; + IPToken newIPTokenImplementation; address alice = makeAddr("alice"); function setUp() public { - mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), 18968463); + mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"), 20240430); vm.selectFork(mainnetFork); } - function testCanUpgradeToV13() public { - IPNFT ipnftMainnetInstance = IPNFT(mainnetIPNFT); - Tokenizer12 tokenizer12 = Tokenizer12(mainnetTokenizer); - + function upgradeToTokenizer13() public { vm.startPrank(mainnetDeployer); Tokenizer newTokenizerImplementation = new Tokenizer(); - IPToken newIPTokenImplementation = new IPToken(); + newIPTokenImplementation = new IPToken(); vm.stopPrank(); vm.startPrank(mainnetOwner); - bytes memory upgradeCallData = abi.encodeWithSelector(Tokenizer.reinit.selector, address(newIPTokenImplementation)); + Tokenizer12 tokenizer12 = Tokenizer12(mainnetTokenizer); //todo: make sure that the legacy IPTs are indexed now + bytes memory upgradeCallData = abi.encodeWithSelector(Tokenizer.reinit.selector, address(newIPTokenImplementation)); tokenizer12.upgradeToAndCall(address(newTokenizerImplementation), upgradeCallData); - Tokenizer upgradedTokenizer = Tokenizer(mainnetTokenizer); - - assertEq(address(upgradedTokenizer.ipTokenImplementation()), address(newIPTokenImplementation)); + tokenizer13 = Tokenizer(mainnetTokenizer); + } - deployCodeTo("Permissioner.sol:BlindPermissioner", "", address(upgradedTokenizer.permissioner())); - vm.stopPrank(); + function testCanUpgradeToV13() public { + upgradeToTokenizer13(); + assertEq(address(tokenizer13.ipTokenImplementation()), address(newIPTokenImplementation)); + assertEq(address(tokenizer13.permissioner()), 0xC837E02982992B701A1B5e4E21fA01cEB0a628fA); vm.startPrank(alice); vm.expectRevert("Initializable: contract is already initialized"); - newTokenizerImplementation.initialize(IPNFT(address(0)), BlindPermissioner(address(0))); + tokenizer13.initialize(IPNFT(address(0)), BlindPermissioner(address(0))); + vm.expectRevert("Initializable: contract is already initialized"); newIPTokenImplementation.initialize(2, "Foo", "Bar", alice, "abcde"); - vm.stopPrank(); vm.startPrank(mainnetOwner); vm.expectRevert("Initializable: contract is already initialized"); - upgradedTokenizer.initialize(IPNFT(address(0)), BlindPermissioner(address(0))); + tokenizer13.initialize(IPNFT(address(0)), BlindPermissioner(address(0))); + vm.expectRevert("Initializable: contract is already initialized"); - upgradedTokenizer.reinit(IPToken(address(0))); - vm.stopPrank(); + tokenizer13.reinit(newIPTokenImplementation); + } + + function testOldIPTsAreMigratedAndCantBeReminted() public { + upgradeToTokenizer13(); + + assertEq(address(tokenizer13.synthesized(2)), 0x6034e0d6999741f07cb6Fb1162cBAA46a1D33d36); + assertEq(address(tokenizer13.synthesized(28)), 0x7b66E84Be78772a3afAF5ba8c1993a1B5D05F9C2); + assertEq(address(tokenizer13.synthesized(37)), 0xBcE56276591128047313e64744b3EBE03998783f); + assertEq(address(tokenizer13.synthesized(31415269)), address(0)); + + deployCodeTo("Permissioner.sol:BlindPermissioner", "", address(tokenizer13.permissioner())); + + address vitaFASTMultisig = 0xf7990CD398daFB4fe5Fd6B9228B8e6f72b296555; - assertEq(ipnftMainnetInstance.ownerOf(valleyDaoIpnftId), valleyDaoMultisig); + vm.startPrank(vitaFASTMultisig); + vm.expectRevert(AlreadyTokenized.selector); + tokenizer13.tokenizeIpnft(2, 1_000_000 ether, "VITA-FAST", "bafkreig274nfj7srmtnb5wd5wlwm3ig2s63wovlz7i3noodjlfz2tm3n5q", bytes("")); + + vm.startPrank(alice); + vm.expectRevert(MustOwnIpnft.selector); + tokenizer13.tokenizeIpnft(2, 1_000_000 ether, "VITA-FAST", "bafkreig274nfj7srmtnb5wd5wlwm3ig2s63wovlz7i3noodjlfz2tm3n5q", bytes("")); + } + + function testTokenizeNewIPTs() public { + upgradeToTokenizer13(); + address valleyDaoMultisig = 0xD920E60b798A2F5a8332799d8a23075c9E77d5F8; + uint256 valleyDaoIpnftId = 3; //hasnt been tokenized yet + deployCodeTo("Permissioner.sol:BlindPermissioner", "", address(tokenizer13.permissioner())); + + assertEq(ipnft.ownerOf(valleyDaoIpnftId), valleyDaoMultisig); vm.startPrank(valleyDaoMultisig); - IPToken ipt = upgradedTokenizer.tokenizeIpnft(valleyDaoIpnftId, 1_000_000 ether, "IPT", agreementCid, ""); + IPToken ipt = tokenizer13.tokenizeIpnft(valleyDaoIpnftId, 1_000_000 ether, "VALLEY", agreementCid, ""); assertEq(ipt.balanceOf(valleyDaoMultisig), 1_000_000 ether); ipt.transfer(alice, 100_000 ether); - assertEq(ipt.balanceOf(valleyDaoMultisig), 900_000 ether); + assertEq(ipt.balanceOf(alice), 100_000 ether); + //controlling the IPT from its own interface ipt.issue(valleyDaoMultisig, 1_000_000 ether); assertEq(ipt.totalSupply(), 2_000_000 ether); assertEq(ipt.balanceOf(valleyDaoMultisig), 1_900_000 ether); @@ -107,6 +126,12 @@ contract Tokenizer13UpgradeForkTest is Test { ipt.issue(valleyDaoMultisig, 100); vm.stopPrank(); + } + + function testOldTokensAreStillControllable() public { + upgradeToTokenizer13(); + address vitaFASTAddress = 0x6034e0d6999741f07cb6Fb1162cBAA46a1D33d36; + address vitaFASTMultisig = 0xf7990CD398daFB4fe5Fd6B9228B8e6f72b296555; IPToken vitaFast = IPToken(vitaFASTAddress); @@ -120,12 +145,94 @@ contract Tokenizer13UpgradeForkTest is Test { vm.stopPrank(); vm.startPrank(vitaFASTMultisig); - assertEq(vitaFast.balanceOf(vitaFASTMultisig), 16940676213630533216614); + assertEq(vitaFast.totalSupply(), 1_029_555 ether); + assertEq(vitaFast.balanceOf(vitaFASTMultisig), 13390539642731621592709); + //the old IPTs allow their original owner to issue more tokens vitaFast.issue(vitaFASTMultisig, 100_000 ether); - assertEq(vitaFast.totalSupply(), 1129555 ether); - assertEq(vitaFast.balanceOf(vitaFASTMultisig), 116940676213630533216614); + assertEq(vitaFast.totalSupply(), 1_129_555 ether); + assertEq(vitaFast.balanceOf(vitaFASTMultisig), 113390539642731621592709); + vitaFast.cap(); vm.expectRevert(TokenCapped.selector); vitaFast.issue(vitaFASTMultisig, 100_000 ether); + + /// --- same for VitaRNA, better safe than sorry. + address vitaRNAAddress = 0x7b66E84Be78772a3afAF5ba8c1993a1B5D05F9C2; + address vitaRNAMultisig = 0x452f3b60129FdB3cdc78178848c63eC23f38C80d; + IPToken vitaRna = IPToken(vitaRNAAddress); + + assertEq(vitaRna.balanceOf(paulhaas), 514.411456805927582924 ether); + assertEq(vitaRna.balanceOf(alice), 0); + + vm.startPrank(paulhaas); + vitaRna.transfer(alice, 100 ether); + assertEq(vitaRna.balanceOf(paulhaas), 414.411456805927582924 ether); + assertEq(vitaRna.balanceOf(alice), 100 ether); + vm.stopPrank(); + + vm.startPrank(vitaRNAMultisig); + assertEq(vitaRna.totalSupply(), 5_000_000 ether); + assertEq(vitaRna.balanceOf(vitaRNAMultisig), 200_000 ether); + vitaRna.issue(vitaRNAMultisig, 100_000 ether); + assertEq(vitaRna.totalSupply(), 5_100_000 ether); + assertEq(vitaRna.balanceOf(vitaRNAMultisig), 300_000 ether); + + vitaRna.cap(); + vm.expectRevert(TokenCapped.selector); + vitaRna.issue(vitaRNAMultisig, 100_000 ether); + } + + // IPN-21: and the main reason why we're doing all the above + function testOldTokensCanBeIssuedByNewIPNFTHolder() public { + upgradeToTokenizer13(); + + deployCodeTo("Permissioner.sol:BlindPermissioner", "", address(tokenizer13.permissioner())); + + address bob = makeAddr("bob"); + address vitaFASTMultisig = 0xf7990CD398daFB4fe5Fd6B9228B8e6f72b296555; + //we're using vita fast's original abi here. It actually is call-compatible to IPToken, but this is the ultimate legacy test + IPToken12 vitaFAST12 = IPToken12(0x6034e0d6999741f07cb6Fb1162cBAA46a1D33d36); + IPToken vitaFAST13 = IPToken(0x6034e0d6999741f07cb6Fb1162cBAA46a1D33d36); + + vm.startPrank(vitaFASTMultisig); + ipnft.transferFrom(vitaFASTMultisig, alice, 2); + assertEq(ipnft.ownerOf(2), alice); + + vm.startPrank(alice); + // This is new: originally Alice *would* indeed have been able to do this: + vm.expectRevert(AlreadyTokenized.selector); + tokenizer13.tokenizeIpnft(2, 1_000_000 ether, "VITA-FAST", "imfeelingfunny", bytes("")); + + assertEq(vitaFAST12.balanceOf(alice), 0); + + //this *should* be possible but can't work due to the old implementation + vm.expectRevert(OnlyIssuerOrOwner.selector); + vitaFAST12.issue(alice, 1_000_000 ether); + //the selector of course doesnt exist on the new interface, but the implementation reverts with it: + vm.expectRevert(OnlyIssuerOrOwner.selector); + vitaFAST13.issue(alice, 1_000_000 ether); + + //to issue new tokens, alice uses the Tokenizer instead: + tokenizer13.issue(vitaFAST13, 1_000_000 ether, alice); + assertEq(vitaFAST12.balanceOf(alice), 1_000_000 ether); + + //due to the original implementation, the original owner can still issue tokens and we cannot do anything about it: + vm.startPrank(vitaFASTMultisig); + vitaFAST12.issue(bob, 1_000_000 ether); + assertEq(vitaFAST12.balanceOf(bob), 1_000_000 ether); + assertEq(vitaFAST13.balanceOf(bob), 1_000_000 ether); + + // but they cannot do that using the tokenizer: + vm.expectRevert(MustOwnIpnft.selector); + tokenizer13.issue(vitaFAST13, 1_000_000 ether, alice); + vm.expectRevert(MustOwnIpnft.selector); + tokenizer13.cap(vitaFAST13); + + //but they unfortunately also can cap the token: + vitaFAST12.cap(); + + vm.startPrank(alice); + vm.expectRevert(TokenCapped.selector); + tokenizer13.issue(vitaFAST13, 1_000_000 ether, alice); } }