# ValidatorNFT and ValidatorSale Flow Diagrams ## Deployment Flow ```mermaid graph TD A[Deploy ValidatorNFT Contract] --> B[ValidatorNFT Deployed] B --> C[Deploy ValidatorSale Contract] C --> |Pass NFT Address and Treasury Address| D[ValidatorSale Deployed] D --> E[Grant MINTER_ROLE to ValidatorSale] E --> |validatorNFT.grantRole| F[System Ready for Sales] style A fill:#f9f,stroke:#333,stroke-width:4px style F fill:#9f9,stroke:#333,stroke-width:4px ``` ## Sale Process Flow ```mermaid sequenceDiagram participant Buyer participant USDC participant ValidatorSale participant ValidatorNFT participant Treasury Note over Buyer: Buyer wants to purchase NFT 0 - Ace of Clubs Buyer->>USDC: approve(ValidatorSale, 52000 USDC) USDC-->>Buyer: Approval granted Buyer->>ValidatorSale: buy(tokenId: 0) ValidatorSale->>USDC: transferFrom(Buyer, ValidatorSale, 52000 USDC) USDC-->>ValidatorSale: Transfer complete Note over ValidatorSale: Calculate 52 percent for treasury ValidatorSale->>USDC: transfer(Treasury, 27040 USDC) USDC-->>Treasury: 52 percent transferred Note over ValidatorSale: 48 percent - 24960 USDC - remains for bonding ValidatorSale->>ValidatorNFT: mintAndTransfer(Buyer, tokenId: 0) ValidatorNFT-->>ValidatorNFT: _safeMint(Buyer, 0) ValidatorNFT-->>ValidatorNFT: cardMinted 0 equals true ValidatorNFT-->>ValidatorNFT: cardDisabled 0 equals true ValidatorNFT-->>Buyer: NFT 0 transferred ValidatorSale-->>Buyer: emit Bought(Buyer, 0) ValidatorNFT-->>Buyer: emit ValidatorAdded(Buyer, 0, totalSupply) Note over Buyer: NFT is disabled by default Buyer->>ValidatorNFT: toggleEnable(0) ValidatorNFT-->>ValidatorNFT: cardDisabled 0 equals false ValidatorNFT-->>Buyer: emit CardEnabled(0) Note over Buyer: Now an active validator! ``` ## Contract Interactions Overview ```mermaid graph LR subgraph ValidatorNFT["ValidatorNFT Contract"] A[MINTER_ROLE] B[mintAndTransfer] C[toggleEnable] D[Card State Management] end subgraph ValidatorSale["ValidatorSale Contract"] E[buy] F[Treasury Distribution] G[Bonding Reserve] H[withdrawToTreasury] end subgraph External["External"] I[USDC Contract] J[Treasury Address] K[Buyers] end K -.->|Approve USDC| I K -.->|Call buy| E E -.->|Transfer USDC| I E -.->|Send 52 percent| J E -.->|Keep 48 percent| G E -.->|Mint NFT| B A -.->|Authorizes| B K -.->|Enable NFT| C H -.->|Emergency| J ``` ### Flow Description: 1. **Buyers → USDC Contract**: Approve USDC spending 2. **Buyers → buy()**: Call purchase function 3. **buy() → USDC Contract**: Transfer payment 4. **buy() → Treasury Address**: Send 52% 5. **buy() → Bonding Reserve**: Keep 48% 6. **buy() → mintAndTransfer()**: Mint NFT 7. **MINTER_ROLE → mintAndTransfer()**: Authorizes minting 8. **Buyers → toggleEnable()**: Enable validator NFT 9. **withdrawToTreasury() → Treasury Address**: Emergency withdrawal ## Key Features ### ValidatorNFT - **52 unique NFTs** representing a deck of cards - **Access Control**: Only addresses with MINTER_ROLE can mint using mintAndTransfer function - **Default Disabled**: NFTs are disabled when minted - **Owner Control**: Only token owners can enable/disable their NFTs ### ValidatorSale - **Fixed Price**: 52,000 USDC per NFT - **Treasury Split**: 52% immediately to treasury - **Bonding Reserve**: 48% held for validator bonding/slashing - **Mint on Purchase**: No pre-minting required - **Failsafe**: Owner can withdraw bonding funds if needed ## Deployment Steps 1. Deploy ValidatorNFT with name and symbol 2. Deploy ValidatorSale with ValidatorNFT address and treasury address 3. Grant MINTER_ROLE on ValidatorNFT to ValidatorSale contract 4. System is ready for purchases ## Purchase Steps ### Direct USDC Purchase 1. Buyer approves USDC spending to ValidatorSale 2. Buyer calls `buy(tokenId)` with desired card ID (0-51) 3. Contract transfers 52,000 USDC from buyer 4. Contract sends 27,040 USDC (52%) to treasury 5. Contract keeps 24,960 USDC (48%) for bonding 6. Contract mints and transfers NFT to buyer 7. Buyer calls `toggleEnable(tokenId)` to activate validator status ### Quote System for Other Tokens The `quote` function allows buyers to check prices in other tokens (ETH, WBTC, etc.) before purchasing: ```mermaid sequenceDiagram participant Buyer participant ValidatorSale participant UniswapQuoter participant UniswapRouter Note over Buyer: Buyer has ETH but needs to know how much ETH equals 52000 USDC Buyer->>ValidatorSale: quote(WETH, 3000) Note over ValidatorSale: Fee 3000 = 0.3% pool ValidatorSale->>UniswapQuoter: quoteExactOutputSingle Note over UniswapQuoter: Calculate: How much WETH needed to get 52000 USDC? UniswapQuoter-->>ValidatorSale: Returns amount of WETH needed ValidatorSale-->>Buyer: Returns WETH amount Note over Buyer: Now buyer knows they need X amount of WETH Buyer->>UniswapRouter: Swap WETH for USDC UniswapRouter-->>Buyer: Receive 52000 USDC Buyer->>ValidatorSale: buy(tokenId) Note over ValidatorSale: Standard purchase flow continues ``` #### Why the Quote Function? 1. **Price Discovery**: Buyers can check how much of any token (ETH, WBTC, DAI) they need to purchase an NFT 2. **Multi-token Support**: While the sale only accepts USDC, buyers can plan their token swaps 3. **Fee Tiers**: Supports different Uniswap pool fee tiers (0.05%, 0.3%, 1%) 4. **Exact Output**: Calculates the exact input needed to get 52,000 USDC #### Example Usage: ```solidity // Check how much WETH is needed uint256 wethNeeded = validatorSale.quote(WETH_ADDRESS, 3000); // Check how much WBTC is needed uint256 wbtcNeeded = validatorSale.quote(WBTC_ADDRESS, 500); // Buyer then swaps their token for USDC on Uniswap before purchasing ``` ### Quote System Flow Overview ```mermaid graph TD A[Buyer has ETH/WBTC/other tokens] --> B{Wants to buy NFT} B --> C[Call quote function] C --> D[Get amount needed in their token] D --> E[Decision: Proceed?] E -->|Yes| F[Swap tokens for USDC on DEX] E -->|No| G[Cancel purchase] F --> H[Now has 52000 USDC] H --> I[Call buy function] I --> J[NFT purchased] style A fill:#f9f,stroke:#333,stroke-width:2px style J fill:#9f9,stroke:#333,stroke-width:2px ``` ## Testing Setup ### Mainnet Forking for Quote Function Testing The quote function requires interaction with Uniswap contracts on mainnet. To test this functionality locally, you need to set up mainnet forking: #### 1. Get an RPC Provider Sign up for a free account at: - [Alchemy](https://www.alchemy.com/) - [Infura](https://infura.io/) #### 2. Configure Environment Create a `.env` file in the contracts directory: ```bash FORK_URL=https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY ``` #### 3. Update Hardhat Config Add forking configuration to `hardhat.config.ts`: ```typescript networks: { hardhat: { forking: { url: process.env.FORK_URL || "", blockNumber: 18500000 // Optional: Pin to specific block } } } ``` #### 4. Run Tests Copy and paste these commands to run specific test suites: ```bash # Run all tests npm run hh:test # ValidatorNFT Tests npm run hh:test -- contracts/test/ValidatorNFT.test.ts # ValidatorSale Integration Tests npm run hh:test -- contracts/test/ValidatorSale.test.ts # ValidatorNFT and Sale Integration Tests npm run hh:test -- contracts/test/ValidatorNFTSale.integration.test.ts # Quote Function Tests (Simple - No Forking Required) npm run hh:test -- contracts/test/ValidatorSale.quote.simple.test.ts # Quote Function Tests (Full - Requires Mainnet Forking) npm run hh:test -- contracts/test/ValidatorSale.quote.test.ts # Run Multiple Specific Tests npm run hh:test -- contracts/test/ValidatorNFT.test.ts contracts/test/ValidatorSale.test.ts # Run Tests with Gas Reporting REPORT_GAS=true npm run hh:test # Run Tests with Coverage npm run hh:coverage ``` #### Test File Descriptions | Test File | Description | Forking Required | |-----------|-------------|------------------| | `ValidatorNFT.test.ts` | Core NFT functionality, mintAndTransfer, toggling | No | | `ValidatorSale.test.ts` | Purchase flow with mocked USDC | No | | `ValidatorNFTSale.integration.test.ts` | Integration between NFT and Sale contracts | No | | `ValidatorSale.quote.simple.test.ts` | Basic quote validation tests | No | | `ValidatorSale.quote.test.ts` | Full quote tests with real Uniswap | **Yes** | ### Why Mainnet Forking? - The quote function calls Uniswap V3 Quoter at `0x61fFE014bA17989E743c5F6cB21bF9697530B21e` - This contract only exists on mainnet - Forking creates a local copy with all mainnet contracts and state - Enables realistic testing without spending real money