mcpField
D
deployer

deployer

github.com/uniswap開発2361.1k 回インストール2 日前に更新
リポジトリ

deployer について

ContinuousClearingAuctionFactory を使った CCA オークションコントラクトのデプロイ向け Claude Code スキルです。CREATE2 によるチェーン間で一貫したアドレスを扱い、設定検証、確認、コマンド準備、鍵の安全な取り扱いを案内します。

主な機能

  1. 01ファクトリー経由でオークションをデプロイ
  2. 02CREATE2 による決定的なアドレス生成
  3. 03設定と入力値の検証
  4. 04Foundry デプロイコマンドの準備
  5. 05デプロイ後の手順とエクスプローラー検証
  6. 06デプロイの安全対策と鍵管理の案内

活用シーン

  1. 01設定から CCA オークションをデプロイ
  2. 02デプロイ前にアドレス、数値、オークション設定を検証
  3. 03テストネットへのデプロイとコントラクト検証のコマンドを準備

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uniswap/uniswap-ai --skill deployer

インストール後、Claude Code で/deployerを入力して読み込まれたか確認します。

詳細

CCA のデプロイ

ContinuousClearingAuctionFactory を CREATE2 とともに使用して、チェーン間でアドレスを統一しながら Continuous Clearing Auction(CCA)スマートコントラクトをデプロイします。

ランタイムの互換性: このスキルでは、対話形式のプロンプトに AskUserQuestion を使用します。実行環境で AskUserQuestion を利用できない場合は、自然言語での会話を通じて同じパラメーターを収集してください。

Claude Code の使用手順

ユーザーがこのスキルを呼び出したら、適切な安全上の警告と検証を行いながら、CCA のデプロイ手順を案内してください。

デプロイ前の要件

デプロイに進む前に、必ず以下を行ってください。

  1. 教育目的の免責事項を表示し、ユーザーの了承を得る
  2. 提供された場合は設定ファイルを検証する
  3. 対象ネットワークのファクトリーアドレスを確認する
  4. デプロイパラメーターをユーザーに確認する
デプロイのワークフロー
  1. 教育目的の免責事項を表示(必須)
  2. 設定を読み込む、または入力を求める
  3. 設定を検証する
  4. デプロイ計画を表示する
  5. ユーザーの確認を得る
  6. デプロイコマンドを提示する
  7. デプロイ後の手順

⚠️ 教育目的での使用に関する免責事項

重要:デプロイに進む前に、以下を了承する必要があります。

このツールとすべてのデプロイ手順は、教育目的のみで提供されています。AI が生成したデプロイコマンドには、エラーやセキュリティ上の脆弱性が含まれる可能性があります。

必ず以下を行ってください。

  1. ✅ デプロイ前に、すべての設定を慎重に確認する
  2. ✅ すべてのパラメーター(アドレス、価格、スケジュール)が正しいことを確認する
  3. ✅ メインネットにデプロイする前に、まずテストネットでテストする
  4. ✅ 実資金を使用してデプロイする前に、コントラクトを監査する

デプロイ手順に進む前に、AskUserQuestion を使用して、ユーザーがこれらの警告を了承したことを確認してください。

入力検証ルール

ユーザーが指定した値を forge/cast コマンドまたはデプロイスクリプトに補間する前に、必ず以下を確認してください。

  • Ethereum アドレス:^0x[a-fA-F0-9]{40}$ に一致する必要があります。一致しない場合は拒否してください
  • チェーン ID:サポート対象チェーンのリスト(1, 130, 1301, 8453, 42161, 11155111)に含まれている必要があります
  • 数値(供給量、価格、ブロック数、チェーン ID):0 以上で、^[0-9]+\.?[0-9]*$ に一致する必要があります
  • RPC エンドポイント URL($RPC_URL):https:// スキームである必要があり、以下のルールに記載されたメタ文字や空白を含んではなりません。この値は設定ファイルから取得するものではなく、手順の実行中にユーザーが指定し、以下のすべての forge script、cast send、forge verify-contract コマンドの --rpc-url に渡されます。「設定ではなくユーザーから取得した値」であることは免除の理由になりません。最初に入力された時点で検証し、$RPC_URL に割り当ててください。その後はこの変数のみを参照し、後続のコマンドで生の URL を再読み込みしないでください。プロバイダーの API キーなど、クエリー文字列を必要とするエンドポイントは、環境に別途エクスポートして $RPC_URL で参照してください。コマンドテキストに直接貼り付けてはいけません。
  • シェルのメタ文字または空白(;、|、&、$、`、(、)、>、<、\、'、"、スペース、タブ、改行)を含む入力は拒否してください。$(...) とバッククォートは二重引用符の内側でもコマンドを実行し、スペースは値を分割して forge/cast の追加フラグにするため、どちらも補間に使用してはいけません。問題の文字を削除して続行するのではなく、処理を中止し、検証に失敗した値と文字を報告してください。
  • 検証せずにユーザーの生の入力をシェルコマンドに渡してはいけません。
  • 生成するコマンドでは、すべての補間を必ず二重引用符で囲んでください(引用符のない形式ではなく、--rpc-url "$RPC_URL" と記述してください)。引用符は第二の防御層であり、最初の防御層ではありません。コマンドに渡す前に値を検証し、そのうえで引用符で囲んでください。
⚠️ 権限の安全性

Claude Code の設定で Bash(forge:*) や Bash(cast:*) を自動承認しないでください。 ガスを消費するコマンドやトランザクションをブロードキャストするコマンドは、必ず実行ごとに承認を求めてください。.claude/hooks/ の PreToolUse フックは安全策としてプログラムによる検証を行いますが、コマンドごとのユーザー承認が主要な制御手段です。


🔐 秘密鍵のセキュリティ

重要:安全なデプロイには、秘密鍵を安全に取り扱うことが不可欠です。

⚠️ 絶対にしてはいけないこと
  • ❌ 絶対に秘密鍵を git リポジトリや設定ファイルに保存しないでください
  • ❌ 絶対に秘密鍵をコマンドラインに直接貼り付けないでください(シェルの履歴に表示されます)
  • ❌ 絶対に秘密鍵を共有したり、共有環境に保存したりしないでください
  • ❌ 絶対に信頼できないコンピューターでメインネットの秘密鍵を使用しないでください
  • ❌ 絶対に--private-key フラグを使用しないでください(PreToolUse フックによってブロックされます)
✅ 推奨される方法
オプション 1:ハードウェアウォレット(最も安全)

Ledger または Trezor のハードウェアウォレットを --ledger フラグと併用します。

forge script script/Example.s.sol:ExampleScript \
  --rpc-url "$RPC_URL" \
  --broadcast \
  --ledger
オプション 2:暗号化されたキーストア

cast wallet import を使って、暗号化されたキーストアを作成します。

# 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"
オプション 3:環境変数(テスト専用)

環境変数を使用する場合は、次の点を確認してください。

  • 安全な .env ファイルに設定する(git にコミットしない)
  • source .env または dotenv で読み込む
  • 信頼できる安全なコンピューターでのみ使用する
  • 開発にはテストネットの鍵を使用する

例:

# .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"
まずテストネットで

メインネットの前に、必ずテストネットでテストしてください。

  • Sepolia(テストネット):フォーセットから無料の ETH を入手できます
  • Base Sepolia:Base でのテストに使える無料の ETH
  • テストネットでワークフロー全体をデプロイして検証する
  • 十分にテストした後でのみ、メインネットにデプロイする

デプロイガイド

ファクトリーのデプロイ

CCA インスタンスは ContinuousClearingAuctionFactory コントラクトを介してデプロイされます。このコントラクトは CREATE2 を使用し、チェーン間で一貫したアドレスを実現します。

ファクトリーのアドレス
バージョンアドレスステータス
v1.1.00xCCccCcCAE7503Cac057829BF2811De42E16e0bD5推奨
オークションインスタンスのデプロイ
ステップ 0:CCA リポジトリをクローンする

CCA コントラクトがまだローカルにない場合は、リポジトリをクローンして依存関係をインストールします。

git clone https://github.com/Uniswap/continuous-clearing-auction.git
cd continuous-clearing-auction
forge install

これにより、以降の手順で使用するデプロイスクリプト、コントラクト ABI、テストヘルパーを利用できるようになります。

ステップ 1:設定を準備する

有効な設定ファイルがあることを確認します(configurator スキルを使って生成するか、手動で作成してください)。

設定ファイルの構造例:

{
  "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 }
    ]
  }
}
ステップ 2:設定を検証する

デプロイ前に、設定がすべての検証ルールに合格することを確認します(「検証ルール」セクションを参照)。

ステップ 3:ファクトリーを介してデプロイする

ファクトリーのインターフェースはシンプルです。

function initializeDistribution(
    address token,
    uint256 amount,
    bytes calldata configData,
    bytes32 salt
) external returns (IDistributionContract);

各項目の説明:

  • token:販売するトークンのアドレス
  • amount:オークションで販売するトークンの数量
  • configData:ABI エンコードされた AuctionParameters 構造体
  • salt:バニティアドレスのマイニングに使う任意の bytes32 値
ステップ 3.5:設定を configData にエンコードする

ファクトリーの initializeDistribution は、ABI エンコードされた AuctionParameters として configData を受け取ります。JSON 設定をエンコード済みのバイト列に変換します。

cast を使用する場合(Foundry CLI):

# 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"

Foundry スクリプトを使用する場合:

// script/DeployAuction.s.sol
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.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);
    }
}

重要: initializeDistribution を呼び出す前に、ファクトリーへのトークン転送を承認する必要があります。ファクトリーは、あなたのアドレスから新しく作成されたオークションコントラクトへ amount 分のトークンを転送します。

ステップ 4:Foundry スクリプトを使用する
# 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"
ステップ 5:デプロイ後

デプロイ後、トークンが転送されたことをオークションに通知するため、onTokensReceived() を必ず呼び出してください。

cast send "$AUCTION_ADDRESS" "onTokensReceived()" --rpc-url "$RPC_URL" --account deployer --sender "$DEPLOYER_ADDRESS"

これは、オークションが入札を受け付けるために必要な前提条件です。

代替方法:コンストラクターを介してデプロイする

コンストラクターを介して直接デプロイすることもできます。

constructor(
    address token,
    uint128 amount,
    AuctionParameters memory parameters
) {}

この方法では salt パラメーターは不要ですが、CREATE2 による決定論的なアドレス生成の利点は得られません。

ブロックエクスプローラーでの検証

検証用の標準 JSON 入力を生成します。

forge verify-contract "$AUCTION_ADDRESS" \
  src/ContinuousClearingAuction.sol:ContinuousClearingAuction \
  --rpc-url "$RPC_URL" \
  --show-standard-json-input > standard-json-input.json

このファイルをブロックエクスプローラーにアップロードして検証します。


検証ルール

デプロイ前に、次の項目を確認してください。

  1. ブロック制約:startBlock < endBlock <= claimBlock
  2. 有効なアドレス:すべてのアドレスが有効な Ethereum アドレス(0x + 40 桁の 16 進数)であること
  3. 負でない値:すべての数値が 0 以上であること
  4. 最低価格の整合性:最低価格がティックスペーシングの倍数であること
  5. ティックスペーシング:最低価格の 1 ベーシスポイント以上(1%、10% を推奨)
  6. 供給スケジュール:最後のブロックで相当量のトークン(約 30% 以上)が売却されること
  7. 総供給量の上限:最大 1e30 wei(18 桁の小数を持つ 1 兆トークン)
  8. FoT トークンは不可:送金手数料のあるトークンはサポートされません
  9. 最小小数桁数:小数桁数が 6 未満のトークンは使用しないでください

技術概要

Q96 固定小数点演算

オークションでは Q96 固定小数点演算を使用します。

library FixedPoint96 {
    uint8 internal constant RESOLUTION = 96;
    uint256 internal constant Q96 = 0x1000000000000000000000000; // 2^96
}
  • 価格:小数を含む価格比率を表す Q96 固定小数点数
  • 需要:Q96 でスケーリングされた通貨額
オークションのステップ(供給量の発行)

ステップはバイトにパックされ、各ステップは uint64 です。

  • 最初の 24 ビット:mps(MPS 単位のブロックごとの発行率)
  • 最後の 40 ビット:blockDelta(ブロック数)
function parse(bytes8 data) internal pure returns (uint24 mps, uint40 blockDelta) {
    mps = uint24(bytes3(data));
    blockDelta = uint40(uint64(data));
}

データは、読み取りコストを抑えるため、外部の SSTORE2 コントラクトにデプロイされます。

主なコントラクト関数
submitBid()

ユーザーは次の値を指定して入札します。

  • maxPrice:支払いを許容する最高価格(Q96)
  • amount:入札する通貨額
  • owner:トークン/返金を受け取るアドレス
  • prevTickPrice:ガス最適化のためのヒント
  • hookData:検証フック用の任意データ
checkpoint()

新しい入札があるたびに、オークションはブロックごとにチェックポイントされます。チェックポイントによってトークンの割り当てが決まります。

exitBid() / exitPartiallyFilledBid()

入札は、より高い入札に上回られた場合、またはオークション終了後(グラデュエーション後のみ)に退出できます。

isGraduated()

currencyRaised >= requiredCurrencyRaised の場合に true を返します。グラデュエーション前は入札から退出できません。

claimTokens()

ユーザーは claimBlock 以降に購入したトークンを請求できます(グラデュエーションしたオークションのみ)。

sweepCurrency() / sweepUnsoldTokens()

オークション終了後:

  • sweepCurrency():調達した通貨を引き出します(グラデュエーションした場合のみ)
  • sweepUnsoldTokens():売れ残ったトークンを引き出します

対応チェーン

CCA は、選定された EVM チェーン上の正規アドレスにデプロイされています。

チェーン IDネットワークブロック時間
1メインネット12s
130Unichain1s
1301Unichain Sepolia2s
8453Base2s
42161Arbitrum2s
11155111Sepolia12s

トラブルシューティング

よくある問題
問題解決策
"Invalid block sequence"startBlock < endBlock <= claimBlock であることを確認します
"Floor price not aligned"最低価格をティックスペーシングの倍数に丸めます
"Tick spacing too small"最低価格の 1% 以上に設定します
"Total supply too large"最大 1e30 wei(18 桁の小数を持つ 1 兆トークン)
"Gas inefficiency"ティックスペーシングを大きくします
"Invalid address"アドレスが 0x で始まる 42 文字であることを確認します
検証チェックリスト

デプロイ前に確認してください。

  • ブロックの順序が有効である(start < end <= claim)
  • 最低価格がティック幅の倍数である
  • ティック幅が最低価格の 1% 以上である
  • すべてのアドレスが有効な Ethereum アドレスである
  • 総供給量が 1e30 wei 以下である
  • 通貨の価値がトークンより高い
  • ブロック時間がネットワークに合っている(メインネットは 12s、L2 は 2s)
  • 受取人のアドレスが設定されている(プレースホルダーではない)
  • 通貨アドレスがネットワークに対して正しい
  • 最後の供給ステップでトークンの約 30% 以上が売却される
  • 送金時に手数料が発生するトークンを使用していない
  • トークンの小数桁数が 6 以上である
  • デプロイ後に onTokensReceived() が呼び出される

追加リソース

コメント