--- name: deployer description: Deploy CCA (Continuous Clearing Auction) smart contracts using the Factory pattern. Use when user says "deploy auction", "deploy cca", "factory deployment", or wants to deploy a configured auction. allowed-tools: Read, Glob, Grep, Bash(forge:*), Bash(cast:*), Bash(curl:*), AskUserQuestion model: opus license: MIT metadata: author: uniswap version: '1.0.0' --- # CCA Deployment Deploy Continuous Clearing Auction (CCA) smart contracts using the `ContinuousClearingAuctionFactory` with CREATE2 for consistent addresses across chains. > **Runtime Compatibility:** This skill uses `AskUserQuestion` for interactive prompts. If `AskUserQuestion` is not available in your runtime, collect the same parameters through natural language conversation instead. ## Instructions for Claude Code When the user invokes this skill, guide them through the CCA deployment process with appropriate safety warnings and validation. ### Pre-Deployment Requirements Before proceeding with deployment, you MUST: 1. **Show educational disclaimer** and get user acknowledgment 2. **Validate configuration file** if provided 3. **Verify factory address** for the target network 4. **Confirm deployment parameters** with user ### Deployment Workflow 1. **Show Educational Disclaimer** (REQUIRED) 2. **Load or Request Configuration** 3. **Validate Configuration** 4. **Display Deployment Plan** 5. **Get User Confirmation** 6. **Provide Deployment Commands** 7. **Post-Deployment Steps** --- ## ⚠️ Educational Use Disclaimer **IMPORTANT: Before proceeding with deployment, you must acknowledge:** This tool and all deployment instructions are provided **for educational purposes only**. AI-generated deployment commands may contain errors or security vulnerabilities. **You must:** 1. ✅ **Review all configurations carefully** before deploying 2. ✅ **Verify all parameters** (addresses, pricing, schedules) are correct 3. ✅ **Test on testnets first** before deploying to mainnet 4. ✅ **Audit your contracts** before deploying with real funds **Use AskUserQuestion to confirm the user acknowledges these warnings before proceeding with deployment steps.** ### Input Validation Rules Before interpolating ANY user-provided value into forge/cast commands or deployment scripts: - **Ethereum addresses**: MUST match `^0x[a-fA-F0-9]{40}$` — reject otherwise - **Chain IDs**: MUST be from the supported chains list (1, 130, 143, 1301, 8453, 42161, 11155111) - **Numeric values** (supply, prices, blocks, chain IDs): MUST be non-negative and match `^[0-9]+\.?[0-9]*$` - **REJECT** any input containing shell metacharacters: `;`, `|`, `&`, `$`, `` ` ``, `(`, `)`, `>`, `<`, `\`, `'`, `"`, newlines - **Never** pass raw user input directly to shell commands without validation ### ⚠️ Permission Safety **Do NOT auto-approve `Bash(forge:*)` or `Bash(cast:*)` in your Claude Code settings.** Always require per-invocation approval for commands that spend gas or broadcast transactions. The PreToolUse hooks in `.claude/hooks/` provide programmatic validation as a safety net, but user approval per command is the primary control. --- ## 🔐 Private Key Security **CRITICAL: Handling private keys safely is essential for secure deployments.** ### ⚠️ Never Do These - ❌ **Never** store private keys in git repositories or config files - ❌ **Never** paste private keys directly in command line (visible in shell history) - ❌ **Never** share private keys or store them in shared environments - ❌ **Never** use mainnet private keys on untrusted computers - ❌ **Never** use `--private-key` flag (blocked by PreToolUse hook) ### ✅ Recommended Practices #### Option 1: Hardware Wallets (Most Secure) Use Ledger or Trezor hardware wallets with the `--ledger` flag: ```bash forge script script/Example.s.sol:ExampleScript \ --rpc-url $RPC_URL \ --broadcast \ --ledger ``` #### Option 2: Encrypted Keystore Create an encrypted keystore with `cast wallet import`: ```bash # Import private key to encrypted keystore (one-time setup) cast wallet import deployer --interactive # Use keystore for deployment forge script script/Example.s.sol:ExampleScript \ --rpc-url $RPC_URL \ --broadcast \ --account deployer \ --sender $DEPLOYER_ADDRESS ``` #### Option 3: Environment Variables (For Testing Only) If using environment variables, ensure they are: - Set in a secure `.env` file (never committed to git) - Loaded via `source .env` or `dotenv` - Only used on trusted, secure computers - Use testnet keys for development **Example:** ```bash # .env file (add to .gitignore) PRIVATE_KEY=0x... RPC_URL=https://... # Load environment source .env # Deploy (use encrypted keystore instead of --private-key) cast wallet import deployer --interactive forge script ... --account deployer --sender $DEPLOYER_ADDRESS ``` ### Testnet First **Always test on testnets before mainnet:** - Sepolia (testnet): Get free ETH from faucets - Base Sepolia: Free ETH for testing on Base - Deploy and verify full workflow on testnet - Only deploy to mainnet after thorough testing --- ## Deployment Guide ### Factory Deployment CCA instances are deployed via the `ContinuousClearingAuctionFactory` contract, which uses CREATE2 for consistent addresses across chains. #### Factory Addresses | Version | Address | Status | | ------- | -------------------------------------------- | --------------- | | v1.1.0 | `0xCCccCcCAE7503Cac057829BF2811De42E16e0bD5` | **Recommended** | ### Deploying an Auction Instance #### Step 0: Clone the CCA Repository If you don't already have the CCA contracts locally, clone the repository and install dependencies: ```bash git clone https://github.com/Uniswap/continuous-clearing-auction.git cd continuous-clearing-auction forge install ``` This gives you access to the deployment scripts, contract ABIs, and test helpers referenced in later steps. #### Step 1: Prepare Configuration Ensure you have a valid configuration file (generated via the `configurator` skill or manually created). Example configuration file structure: ```json { "1": { "token": "0x...", "totalSupply": 1e29, "currency": "0x0000000000000000000000000000000000000000", "tokensRecipient": "0x...", "fundsRecipient": "0x...", "startBlock": 24321000, "endBlock": 24327001, "claimBlock": 24327001, "tickSpacing": 79228162514264337593543950, "validationHook": "0x0000000000000000000000000000000000000000", "floorPrice": 7922816251426433759354395000, "requiredCurrencyRaised": 0, "supplySchedule": [ { "mps": 1000, "blockDelta": 6000 }, { "mps": 4000000, "blockDelta": 1 } ] } } ``` #### Step 2: Validate Configuration Before deployment, verify the configuration passes all validation rules (see Validation Rules section). #### Step 3: Deploy via Factory The factory has a simple interface: ```solidity function initializeDistribution( address token, uint256 amount, bytes calldata configData, bytes32 salt ) external returns (IDistributionContract); ``` Where: - `token`: Address of the token to be sold - `amount`: Amount of tokens to sell in the auction - `configData`: ABI-encoded `AuctionParameters` struct - `salt`: Optional bytes32 value for vanity address mining #### Step 3.5: Encode Configuration to configData The factory's `initializeDistribution` expects `configData` as ABI-encoded `AuctionParameters`. Convert your JSON config to encoded bytes: **Using cast (Foundry CLI):** ```bash # Encode the AuctionParameters struct cast abi-encode "initializeDistribution(address,uint256,bytes,bytes32)" \ "$TOKEN_ADDRESS" \ "$TOTAL_SUPPLY" \ "$(cast abi-encode "(address,address,address,uint64,uint64,uint64,uint256,address,uint256,uint128,bytes)" \ "$CURRENCY" \ "$TOKENS_RECIPIENT" \ "$FUNDS_RECIPIENT" \ "$START_BLOCK" \ "$END_BLOCK" \ "$CLAIM_BLOCK" \ "$TICK_SPACING" \ "$VALIDATION_HOOK" \ "$FLOOR_PRICE" \ "$REQUIRED_CURRENCY_RAISED" \ "$ENCODED_SUPPLY_SCHEDULE")" \ "0x0000000000000000000000000000000000000000000000000000000000000000" ``` **Using a Foundry Script:** ```solidity // script/DeployAuction.s.sol pragma solidity ^0.8.24; import "forge-std/Script.sol"; interface ICCAFactory { function initializeDistribution( address token, uint256 amount, bytes calldata configData, bytes32 salt ) external returns (address); } contract DeployAuction is Script { function run() external { // Load config values address token = vm.envAddress("TOKEN"); uint256 amount = vm.envUint("TOTAL_SUPPLY"); // Encode AuctionParameters bytes memory configData = abi.encode( vm.envAddress("CURRENCY"), vm.envAddress("TOKENS_RECIPIENT"), vm.envAddress("FUNDS_RECIPIENT"), uint64(vm.envUint("START_BLOCK")), uint64(vm.envUint("END_BLOCK")), uint64(vm.envUint("CLAIM_BLOCK")), vm.envUint("TICK_SPACING"), vm.envAddress("VALIDATION_HOOK"), vm.envUint("FLOOR_PRICE"), uint128(vm.envUint("REQUIRED_CURRENCY_RAISED")), vm.envBytes("ENCODED_SUPPLY_SCHEDULE") ); vm.startBroadcast(); // Approve token transfer to factory IERC20(token).approve( 0xCCccCcCAE7503Cac057829BF2811De42E16e0bD5, amount ); // Deploy auction address auction = ICCAFactory( 0xCCccCcCAE7503Cac057829BF2811De42E16e0bD5 ).initializeDistribution( token, amount, configData, bytes32(0) // salt ); vm.stopBroadcast(); console.log("Auction deployed at:", auction); } } ``` **Important:** You must approve the token transfer to the factory before calling `initializeDistribution`. The factory will transfer `amount` tokens from your address to the newly created auction contract. #### Step 4: Using Foundry Script ```bash # Deploy factory (if needed on new network) forge script script/deploy/DeployContinuousAuctionFactory.s.sol:DeployContinuousAuctionFactoryScript \ --rpc-url $RPC_URL \ --broadcast \ --account deployer --sender $DEPLOYER_ADDRESS # Deploy auction instance forge script script/Example.s.sol:ExampleScript \ --rpc-url $RPC_URL \ --broadcast \ --account deployer --sender $DEPLOYER_ADDRESS ``` #### Step 5: Post-Deployment After deployment, you **must** call `onTokensReceived()` to notify the auction that tokens have been transferred: ```bash cast send $AUCTION_ADDRESS "onTokensReceived()" --rpc-url $RPC_URL --account deployer --sender $DEPLOYER_ADDRESS ``` This is a required prerequisite before the auction can accept bids. ### Alternative: Deploy via Constructor You can also deploy directly via the constructor: ```solidity constructor( address token, uint128 amount, AuctionParameters memory parameters ) {} ``` This approach doesn't require a salt parameter but won't benefit from CREATE2's deterministic addressing. ### Verification on Block Explorers Generate standard JSON input for verification: ```bash forge verify-contract $AUCTION_ADDRESS \ src/ContinuousClearingAuction.sol:ContinuousClearingAuction \ --rpc-url $RPC_URL \ --show-standard-json-input > standard-json-input.json ``` Upload this file to block explorers for verification. --- ## Validation Rules Before deployment, ensure: 1. **Block constraints**: `startBlock < endBlock <= claimBlock` 2. **Valid addresses**: All addresses are valid Ethereum addresses (0x + 40 hex chars) 3. **Non-negative values**: All numeric values >= 0 4. **Floor price alignment**: Floor price must be a multiple of tick spacing 5. **Tick spacing**: At least 1 basis point of floor price (1%, 10% recommended) 6. **Supply schedule**: Last block sells significant tokens (~30%+) 7. **Total supply bounds**: Max 1e30 wei (1 trillion 18-decimal tokens) 8. **No FoT tokens**: Fee-on-transfer tokens not supported 9. **Minimum decimals**: Do not use tokens with < 6 decimals --- ## Technical Overview ### Q96 Fixed-Point Math The auction uses Q96 fixed-point arithmetic: ```solidity library FixedPoint96 { uint8 internal constant RESOLUTION = 96; uint256 internal constant Q96 = 0x1000000000000000000000000; // 2^96 } ``` - **Price**: Q96 fixed-point number for fractional price ratios - **Demand**: Currency amounts scaled by Q96 ### Auction Steps (Supply Issuance) Steps are packed into bytes, where each step is a `uint64`: - First 24 bits: `mps` (per-block issuance rate in MPS) - Last 40 bits: `blockDelta` (number of blocks) ```solidity function parse(bytes8 data) internal pure returns (uint24 mps, uint40 blockDelta) { mps = uint24(bytes3(data)); blockDelta = uint40(uint64(data)); } ``` The data is deployed to an external SSTORE2 contract for cheaper reads. ### Key Contract Functions #### submitBid() Users submit bids with: - `maxPrice`: Maximum price willing to pay (Q96) - `amount`: Currency amount to bid - `owner`: Address to receive tokens/refunds - `prevTickPrice`: Hint for gas optimization - `hookData`: Optional data for validation hooks #### checkpoint() Auction is checkpointed once per block with a new bid. Checkpoints determine token allocations. #### exitBid() / exitPartiallyFilledBid() Bids can be exited when outbid or when auction ends (only after graduation). #### isGraduated() Returns true if `currencyRaised >= requiredCurrencyRaised`. No bids can exit before graduation. #### claimTokens() Users claim purchased tokens after `claimBlock` (only for graduated auctions). #### sweepCurrency() / sweepUnsoldTokens() After auction ends: - `sweepCurrency()`: Withdraw raised currency (graduated only) - `sweepUnsoldTokens()`: Withdraw unsold tokens --- ## Supported Chains CCA is deployed to canonical addresses across select EVM chains: | Chain ID | Network | Block Time | | -------- | ---------------- | ---------- | | 1 | Mainnet | 12s | | 130 | Unichain | 1s | | 1301 | Unichain Sepolia | 2s | | 8453 | Base | 2s | | 42161 | Arbitrum | 2s | | 11155111 | Sepolia | 12s | --- ## Troubleshooting ### Common Issues | Issue | Solution | | ------------------------- | --------------------------------------------------- | | "Invalid block sequence" | Ensure startBlock < endBlock <= claimBlock | | "Floor price not aligned" | Round floor price to multiple of tick spacing | | "Tick spacing too small" | Use at least 1% of floor price | | "Total supply too large" | Max 1e30 wei (1 trillion 18-decimal tokens) | | "Gas inefficiency" | Increase tick spacing | | "Invalid address" | Verify addresses are 42 characters starting with 0x | ### Validation Checklist Before deployment: - [ ] Block sequence is valid (start < end <= claim) - [ ] Floor price is multiple of tick spacing - [ ] Tick spacing >= 1% of floor price - [ ] All addresses are valid Ethereum addresses - [ ] Total supply <= 1e30 wei - [ ] Currency is more valuable than token - [ ] Block times match network (12s mainnet, 2s L2s) - [ ] Recipients addresses are set (not placeholders) - [ ] Currency address is correct for network - [ ] Last supply step sells ~30%+ of tokens - [ ] No fee-on-transfer tokens used - [ ] Token decimals >= 6 - [ ] `onTokensReceived()` called post-deployment --- ## Additional Resources - **CCA Repository**: - **Technical Documentation**: See `docs/TechnicalDocumentation.md` in repo - **Deployment Guide**: See `docs/DeploymentGuide.md` in repo - **Whitepaper**: See `docs/assets/whitepaper.pdf` in repo - **Audits**: See `docs/audits/README.md` in repo - **Uniswap Docs**: - **Bug Bounty**: