Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

DApps開発

DApps開発 1枚まとめ

DAppsの構成

DApps(Decentralized Applications) は、スマートコントラクトをバックエンドとするアプリケーション。典型的な構成は次のとおり。

構成要素役割
スマートコントラクト資産とビジネスロジックを管理するバックエンド
フロントエンド普通のWebアプリ。コントラクトの読み取りとトランザクションの作成を行う
ウォレット秘密鍵を管理し、ユーザーの確認を経てトランザクションに署名する
RPCノードJSON-RPC(eth_call, eth_sendRawTransaction など)でブロックチェーンとやりとりする窓口
インデクサーイベントを収集・整理してクエリしやすくする。コントラクトから直接「全保有者の一覧」などを取るのは難しいため

秘密鍵をサーバーに置かず、署名はユーザーのウォレットで行うのが基本。

開発ツール

コントラクト開発フレームワーク

ツール言語特徴
FoundrySolidity(テストもSolidityで書く), Rust製高速。ファジングやフォークテストが充実。現在の主流
HardhatJavaScript / TypeScriptプラグインが豊富。フロントエンドとの統合がしやすい
Remixブラウザインストール不要。学習やちょっとした検証向け

Foundryは次のツールからなる。

コマンド役割
forgeビルド、テスト、デプロイ
castコマンドラインからコントラクトの呼び出しやトランザクションの送信、データのエンコード・デコード
anvilローカルで動くEthereumノード(テスト用の資金を持つアカウントがあらかじめ用意される)
chiselSolidityのREPL

ライブラリ

種類例
コントラクトのライブラリOpenZeppelin Contracts(ERC-20/721の実装、アクセス制御、プロキシなど), Solady
フロントエンドからのチェーン接続viem, ethers.js, web3.js
Reactでのウォレット接続wagmi, RainbowKit, ConnectKit
Pythonからの接続web3.py

ネットワーク

ネットワーク用途
ローカル(anvil, Hardhat Network)開発・テスト。即座にブロックが作られる
テストネット(Sepolia, Hoodi)本番に近い環境での検証。テスト用のETHは無料の配布サービス(faucet)で入手する
メインネットのフォーク本番のステートをローカルに複製して、既存のコントラクトとの連携をテストする
メインネット本番

Foundryでの開発の流れ

プロジェクトの作成とテスト

# インストール
curl -L https://foundry.paradigm.xyz | bash
foundryup

# プロジェクト作成
forge init counter
cd counter
forge build
forge test

作成されるディレクトリ構成:

counter/
├── foundry.toml      # 設定ファイル
├── src/Counter.sol   # コントラクト
├── test/Counter.t.sol  # テスト
├── script/Counter.s.sol  # デプロイスクリプト
└── lib/              # 依存ライブラリ(forge-std など)

テストもSolidityで書く。test で始まる関数がテストケースになり、引数を取るテスト関数はファジングテストとして多数のランダムな入力で実行される。

// test/Counter.t.sol
import {Test} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";

contract CounterTest is Test {
    Counter counter;

    function setUp() public {
        counter = new Counter();
    }

    function test_Increment() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }

    // ファジング:ランダムなxで何度も実行される
    function testFuzz_SetNumber(uint256 x) public {
        counter.setNumber(x);
        assertEq(counter.number(), x);
    }
}

vm.prank(次の呼び出しの msg.sender の偽装)、vm.warp(時刻の変更)、vm.deal(残高の設定)、vm.expectRevert(次の呼び出しが失敗することの検証)などの チートコード で、テスト用にチェーンの状態を自由に操作できる。権限チェックのテストなら、vm.prank で権限のないアドレスになりすまして vm.expectRevert を確認する、といった使い方をする。

デプロイ

# ローカルノードを起動
anvil

# 別のターミナルでデプロイ(anvilが表示するテスト用の秘密鍵を使う)
forge script script/Counter.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key <秘密鍵>

# デプロイしたコントラクトを呼び出す
cast call <コントラクトアドレス> "number()(uint256)" --rpc-url http://127.0.0.1:8545
cast send <コントラクトアドレス> "increment()" --rpc-url http://127.0.0.1:8545 --private-key <秘密鍵>

テストネットやメインネットにデプロイしたら、Etherscanなどのエクスプローラーでソースコードを verify しておく。ソースコードが公開・照合されていると、ユーザーがコントラクトの中身を確認でき、エクスプローラー上から関数を呼び出すこともできる。

フロントエンドからの呼び出し

viemを使ってコントラクトを読み書きする例(TypeScript):

import { createPublicClient, createWalletClient, custom, http, parseAbi } from "viem";
import { sepolia } from "viem/chains";

const abi = parseAbi([
  "function number() view returns (uint256)",
  "function increment()",
]);
const address = "0x...";

// 読み取り:署名不要・gas不要(RPCノードで eth_call を実行するだけ)
const publicClient = createPublicClient({ chain: sepolia, transport: http() });
const n = await publicClient.readContract({ address, abi, functionName: "number" });

// 書き込み:ブラウザのウォレット(MetaMaskなど)で署名してトランザクションを送る
const walletClient = createWalletClient({ chain: sepolia, transport: custom(window.ethereum) });
const [account] = await walletClient.requestAddresses();
const hash = await walletClient.writeContract({ address, abi, functionName: "increment", account });
await publicClient.waitForTransactionReceipt({ hash });

ポイント:

  • 読み取り(view 関数)はトランザクションではなく、RPCノード上でのシミュレーション実行なので無料で即座に結果が返る

  • 書き込みはトランザクションを送るので、ウォレットでの署名、gas代、ブロックへの取り込み待ちが必要。UIでは「送信中」「確定」などの状態を表示する

  • コントラクトのイベントを購読して、状態の変化をUIに反映する