Skip to content

Commit

Permalink
feat(nfts): taikoons upgradeable v2 (#17580)
Browse files Browse the repository at this point in the history
  • Loading branch information
bearni95 authored Jun 14, 2024
1 parent cc10b04 commit df855a5
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 164 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/nfts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ jobs:
- name: Install pnpm dependencies
uses: ./.github/actions/install-pnpm-dependencies

- name: Check formatting
- name: Format solidity && update contract layout table
working-directory: ./packages/nfts
run: forge fmt --check
run: pnpm layout && forge fmt

- name: Solidity compilation
working-directory: ./packages/nfts
Expand Down
56 changes: 56 additions & 0 deletions packages/nfts/contract_layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## MerkleWhitelist

| Name | Type | Slot | Offset | Bytes | Contract |
| --------- | -------------------------- | ---- | ------ | ----- | ------------------------------------------------------ |
| root | bytes32 | 0 | 0 | 32 | contracts/snaefell/MerkleWhitelist.sol:MerkleWhitelist |
| minted | mapping(bytes32 => bool) | 1 | 0 | 32 | contracts/snaefell/MerkleWhitelist.sol:MerkleWhitelist |
| blacklist | contract IMinimalBlacklist | 2 | 0 | 20 | contracts/snaefell/MerkleWhitelist.sol:MerkleWhitelist |
| \_\_gap | uint256[48] | 3 | 0 | 1536 | contracts/snaefell/MerkleWhitelist.sol:MerkleWhitelist |

## TaikoonToken

| Name | Type | Slot | Offset | Bytes | Contract |
| ----------------- | -------------------------- | ---- | ------ | ----- | ----------------------------------------------- |
| root | bytes32 | 0 | 0 | 32 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| minted | mapping(bytes32 => bool) | 1 | 0 | 32 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| blacklist | contract IMinimalBlacklist | 2 | 0 | 20 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| \_\_gap | uint256[47] | 3 | 0 | 1504 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| \_totalSupply | uint256 | 50 | 0 | 32 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| \_baseURIExtended | string | 51 | 0 | 32 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |
| \_\_gap | uint256[47] | 52 | 0 | 1504 | contracts/taikoon/TaikoonToken.sol:TaikoonToken |

## SnaefellToken

| Name | Type | Slot | Offset | Bytes | Contract |
| ----------------- | -------------------------- | ---- | ------ | ----- | -------------------------------------------------- |
| root | bytes32 | 0 | 0 | 32 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| minted | mapping(bytes32 => bool) | 1 | 0 | 32 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| blacklist | contract IMinimalBlacklist | 2 | 0 | 20 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| \_\_gap | uint256[48] | 3 | 0 | 1536 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| \_totalSupply | uint256 | 51 | 0 | 32 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| \_baseURIExtended | string | 52 | 0 | 32 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |
| \_\_gap | uint256[48] | 53 | 0 | 1536 | contracts/snaefell/SnaefellToken.sol:SnaefellToken |

## ECDSAWhitelist

| Name | Type | Slot | Offset | Bytes | Contract |
| ---------- | -------------------------- | ---- | ------ | ----- | --------------------------------------------------------------- |
| mintSigner | address | 0 | 0 | 20 | contracts/trailblazers-badges/ECDSAWhitelist.sol:ECDSAWhitelist |
| minted | mapping(bytes32 => bool) | 1 | 0 | 32 | contracts/trailblazers-badges/ECDSAWhitelist.sol:ECDSAWhitelist |
| blacklist | contract IMinimalBlacklist | 2 | 0 | 20 | contracts/trailblazers-badges/ECDSAWhitelist.sol:ECDSAWhitelist |
| \_\_gap | uint256[47] | 3 | 0 | 1504 | contracts/trailblazers-badges/ECDSAWhitelist.sol:ECDSAWhitelist |

## TrailblazersBadges

| Name | Type | Slot | Offset | Bytes | Contract |
| ----------------- | ----------------------------------------------- | ---- | ------ | ----- | ----------------------------------------------------------------------- |
| mintSigner | address | 0 | 0 | 20 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| minted | mapping(bytes32 => bool) | 1 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| blacklist | contract IMinimalBlacklist | 2 | 0 | 20 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| \_\_gap | uint256[47] | 3 | 0 | 1504 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| \_baseURIExtended | string | 50 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| badges | mapping(uint256 => uint256) | 51 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| movements | mapping(address => uint256) | 52 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| userBadges | mapping(address => mapping(uint256 => uint256)) | 53 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| movementBadges | mapping(bytes32 => uint256[2]) | 54 | 0 | 32 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
| \_\_gap | uint256[43] | 55 | 0 | 1376 | contracts/trailblazers-badges/TrailblazersBadges.sol:TrailblazersBadges |
15 changes: 15 additions & 0 deletions packages/nfts/contracts/taikoon/TaikoonToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,19 @@ contract TaikoonToken is ERC721EnumerableUpgradeable, MerkleWhitelist {
_mint(_to, tokenIds[i]);
}
}

/// @dev V2 code additions

/// @notice Update the base URI
/// @param _rootURI The new base URI
/// @dev Only the owner can update the base URI
function updateBaseURI(string memory _rootURI) public onlyOwner {
_baseURIExtended = _rootURI;
}

/// @notice Get the base URI
/// @return The base URI
function baseURI() public view returns (string memory) {
return _baseURIExtended;
}
}
25 changes: 25 additions & 0 deletions packages/nfts/deployments/gen-layouts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# Define the list of contracts to inspect
contracts=(
"MerkleWhitelist"
"TaikoonToken"
"SnaefellToken"
"ECDSAWhitelist"
"TrailblazersBadges"
)

# Empty the output file initially
output_file="contract_layout.md"
> $output_file

# Loop over each contract
for contract in "${contracts[@]}"; do
# Run forge inspect and append to the file
# Ensure correct concatenation of the command without commas
echo "forge inspect ${contract} storagelayout --pretty >> $output_file"

echo "## ${contract}" >> $output_file
forge inspect ${contract} storagelayout --pretty >> $output_file
echo "" >> $output_file
done
2 changes: 1 addition & 1 deletion packages/nfts/deployments/taikoon/localhost.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"MerkleRoot": "0x1c3b504b4d5640d26ad1aa3b57a9df9ec034f19239768e734b849c306d10b110",
"Owner": "0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be",
"TaikoonToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
"TaikoonToken": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
}
1 change: 1 addition & 0 deletions packages/nfts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ remappings = [
"murky/=node_modules/murky/src/",
"solidity-stringutils/=node_modules/solidity-stringutils/",
"@taiko/blacklist/=node_modules/@taiko/supplementary-contracts/contracts/blacklist/",
"openzeppelin-foundry-upgrades/=node_modules/openzeppelin-foundry-upgrades/src/",
]

# Do not change the block_gas_limit value, TaikoL2.t.sol depends on it.
Expand Down
8 changes: 5 additions & 3 deletions packages/nfts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"lint:sol": "forge fmt && pnpm solhint 'contracts/**/*.sol'",
"test": "pnpm clean && pnpm compile && forge test --match-path 'test/*.t.sol' -vvv",
"node": "anvil",
"layout": "./deployments/gen-layouts.sh",
"taikoon:merkle": "node script/taikoon/js/generate-merkle-tree.js",
"snaefell:merkle": "node script/snaefell/js/generate-merkle-tree.js",
"taikoon:deploy:localhost": "forge clean && pnpm compile && forge script script/taikoon/sol/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast",
Expand All @@ -22,7 +23,8 @@
"snaefell:deploy:mainnet": "forge clean && pnpm compile && forge script script/snaefell/sol/Deploy.s.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast --legacy --with-gas-price 13 ",
"taikoon:deploy:holesky": "forge clean && pnpm compile && forge script script/taikoon/sol/Deploy.s.sol --rpc-url https://1rpc.io/holesky --broadcast --gas-estimate-multiplier 200",
"tbzb:deploy:localhost": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast",
"tbzb:deploy:hekla": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/Deploy.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200"
"tbzb:deploy:hekla": "forge clean && pnpm compile && forge script script/trailblazers-badges/sol/Deploy.s.sol --rpc-url https://rpc.hekla.taiko.xyz --broadcast --gas-estimate-multiplier 200",
"taikoon:deploy:v2": "forge clean && pnpm compile && forge script script/taikoon/sol/UpgradeV2.sol --rpc-url https://rpc.mainnet.taiko.xyz --broadcast"
},
"devDependencies": {
"@types/node": "^20.11.30",
Expand All @@ -47,6 +49,7 @@
"@openzeppelin/contracts": "5.0.2",
"@openzeppelin/contracts-upgradeable": "5.0.2",
"@openzeppelin/merkle-tree": "^1.0.6",
"@taiko/supplementary-contracts": "workspace:*",
"convert-csv-to-json": "^2.46.0",
"dotenv": "^16.4.5",
"ds-test": "github:dapphub/ds-test#e282159d5170298eb2455a6c05280ab5a73a4ef0",
Expand All @@ -57,7 +60,6 @@
"p256-verifier": "github:taikoxyz/p256-verifier#v0.1.0",
"sharp": "^0.33.3",
"solady": "github:Vectorized/solady#v0.0.167",
"solidity-stringutils": "github:Arachnid/solidity-stringutils",
"@taiko/supplementary-contracts": "workspace:*"
"solidity-stringutils": "github:Arachnid/solidity-stringutils"
}
}
2 changes: 1 addition & 1 deletion packages/nfts/script/taikoon/js/4everland.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function populateNFTMetadata(name, description, CID) {
return {
name,
description,
image: CID,
image: `https://taikonfts.4everland.link/ipfs/${CID}`,
};
}

Expand Down
18 changes: 10 additions & 8 deletions packages/nfts/script/taikoon/sol/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ contract DeployScript is Script {
address public deployerAddress;

// Taiko Mainnet Values
address owner = 0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be;
bytes32 root = 0xa7e510d5aed347e65609cf6f0e0738cdd752ffdf5980749057c634489fd09fc3;
string baseURI = "bafybeierqzehlrqeqqeb6fwmil4dj3ij2p6exgoj4lysl53fsxwob6wbdy";
IMinimalBlacklist blacklist = IMinimalBlacklist(0xfA5EA6f9A13532cd64e805996a941F101CCaAc9a);
// address owner = 0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be;
// bytes32 root = 0xa7e510d5aed347e65609cf6f0e0738cdd752ffdf5980749057c634489fd09fc3;
// string baseURI =
// "https://taikonfts.4everland.link/ipfs/bafybeiegdqpwx3he5dvoxqklspdjekjepjcobfaakyficksratn73qbbyy";
// IMinimalBlacklist blacklist = IMinimalBlacklist(0xfA5EA6f9A13532cd64e805996a941F101CCaAc9a);

// Holesky Testnet Values
// address owner = 0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be;
Expand All @@ -29,10 +30,11 @@ contract DeployScript is Script {
// IMinimalBlacklist blacklist = IMinimalBlacklist(0xe61E9034b5633977eC98E302b33e321e8140F105);

// Hardhat Testnet Values
//address owner = 0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be;
//bytes32 root = 0x1c3b504b4d5640d26ad1aa3b57a9df9ec034f19239768e734b849c306d10b110;
//string baseURI = "bafybeierqzehlrqeqqeb6fwmil4dj3ij2p6exgoj4lysl53fsxwob6wbdy";
//IMinimalBlacklist blacklist = IMinimalBlacklist(0xe61E9034b5633977eC98E302b33e321e8140F105);
address owner = 0xf8ff2AF0DC1D5BA4811f22aCb02936A1529fd2Be;
bytes32 root = 0x1c3b504b4d5640d26ad1aa3b57a9df9ec034f19239768e734b849c306d10b110;
string baseURI =
"https://taikonfts.4everland.link/ipfs/bafybeiegdqpwx3he5dvoxqklspdjekjepjcobfaakyficksratn73qbbyy";
IMinimalBlacklist blacklist = IMinimalBlacklist(0xe61E9034b5633977eC98E302b33e321e8140F105);

function setUp() public {
utils = new UtilsScript();
Expand Down
46 changes: 46 additions & 0 deletions packages/nfts/script/taikoon/sol/UpgradeV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { UtilsScript } from "./Utils.s.sol";
import { Script, console } from "forge-std/src/Script.sol";
import { MerkleMintersScript } from "./MerkleMinters.s.sol";
import { Merkle } from "murky/Merkle.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { TaikoonToken } from "../../../contracts/taikoon/TaikoonToken.sol";
import { IMinimalBlacklist } from "@taiko/blacklist/IMinimalBlacklist.sol";

contract UpgradeV2 is Script {
UtilsScript public utils;
string public jsonLocation;
uint256 public deployerPrivateKey;
address public deployerAddress;

address tokenV1 = 0x4A045C5016B200F7E08a4caBB2cdA6E85bF53295;

string baseURI =
"https://taikonfts.4everland.link/ipfs/bafybeiegdqpwx3he5dvoxqklspdjekjepjcobfaakyficksratn73qbbyy";

TaikoonToken public token;

function setUp() public {
utils = new UtilsScript();
utils.setUp();

jsonLocation = utils.getContractJsonLocation();
deployerPrivateKey = utils.getPrivateKey();
deployerAddress = utils.getAddress();
}

function run() public {
token = TaikoonToken(tokenV1);
vm.startBroadcast(deployerPrivateKey);

token.upgradeToAndCall(
address(new TaikoonToken()), abi.encodeCall(TaikoonToken.updateBaseURI, (baseURI))
);

token = TaikoonToken(token);

console.log("Upgraded TaikoonToken to:", address(token));
}
}
61 changes: 45 additions & 16 deletions packages/nfts/test/taikoon/Upgradeable.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import "forge-std/src/StdJson.sol";
import { UtilsScript } from "../../script/taikoon/sol/Utils.s.sol";
import { MockBlacklist } from "../util/Blacklist.sol";

import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import { ITransparentUpgradeableProxy } from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract UpgradeableTest is Test {
Expand All @@ -16,6 +20,7 @@ contract UpgradeableTest is Test {
UtilsScript public utils;

TaikoonToken public token;
TaikoonToken public tokenV2;

address public owner = vm.addr(0x5);

Expand All @@ -34,26 +39,50 @@ contract UpgradeableTest is Test {
blacklist = new MockBlacklist();

// create whitelist merkle tree
vm.startPrank(owner);
vm.startBroadcast(owner);
bytes32 root = tree.getRoot(leaves);

// deploy token with empty root
address impl = address(new TaikoonToken());
address proxy = address(
new ERC1967Proxy(
impl,
abi.encodeCall(TaikoonToken.initialize, (address(0), "ipfs://", root, blacklist))
)
token = new TaikoonToken();
address impl = address(token);

ERC1967Proxy proxy = new ERC1967Proxy(
impl, abi.encodeCall(TaikoonToken.initialize, (owner, "ipfs://", root, blacklist))
);
token = TaikoonToken(address(proxy));

// mint tokens on the v1 deployment
token.mint(minters[0], 5);

// upgrade to v2

token.upgradeToAndCall(
address(new TaikoonToken()), abi.encodeCall(TaikoonToken.updateBaseURI, ("ipfs://v2//"))
);

tokenV2 = TaikoonToken(address(proxy));

vm.stopBroadcast();
}

function test_upgraded_v2() public view {
assertEq(tokenV2.name(), token.name());
assertEq(tokenV2.symbol(), token.symbol());
assertEq(tokenV2.totalSupply(), token.totalSupply());
assertEq(tokenV2.maxSupply(), token.maxSupply());
}

function test_tokenURI() public view {
assertEq(tokenV2.baseURI(), "ipfs://v2//");
string memory uri = tokenV2.tokenURI(0);
assertEq(uri, "ipfs://v2///0.json");
}

function test_updateBaseURI() public {
vm.startBroadcast(owner);
tokenV2.updateBaseURI("ipfs://test//");
vm.stopBroadcast();

token = TaikoonToken(proxy);
// use the token to calculate leaves
for (uint256 i = 0; i < minters.length; i++) {
leaves[i] = token.leaf(minters[i], FREE_MINTS);
}
// update the root
root = tree.getRoot(leaves);
token.updateRoot(root);
vm.stopPrank();
assertEq(tokenV2.baseURI(), "ipfs://test//");
}
}
Loading

0 comments on commit df855a5

Please sign in to comment.