From b55908ba12e17417aa2ba738382a109bad344eee Mon Sep 17 00:00:00 2001 From: Joshua <62268199+minimalsm@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:01:21 +0000 Subject: [PATCH] i18n(ja): translation import part 08 of 13 (23 files) --- .../how-to-view-nft-in-metamask/index.md | 25 +- .../how-to-write-and-deploy-an-nft/index.md | 194 +++-- .../index.md | 51 +- .../tutorials/ipfs-decentralized-ui/index.md | 73 ++ .../index.md | 78 +- .../index.md | 4 +- .../index.md | 113 ++- .../logging-events-smart-contracts/index.md | 28 +- .../index.md | 115 +-- .../index.md | 80 +- .../developers/tutorials/nft-minter/index.md | 384 ++++----- .../index.md | 709 +++++++++-------- .../reverse-engineering-a-contract/index.md | 692 +++++++--------- .../tutorials/run-node-raspberry-pi/index.md | 101 ++- .../tutorials/scam-token-tricks/index.md | 470 +++++++++++ .../tutorials/secret-state/index.md | 741 ++++++++++++++++++ .../secure-development-workflow/index.md | 43 +- .../tutorials/send-token-etherjs/index.md | 4 +- .../tutorials/send-token-ethersjs/index.md | 68 +- .../index.md | 122 ++- .../tutorials/server-components/index.md | 295 +++++++ .../index.md | 40 +- .../developers/tutorials/short-abi/index.md | 292 +++---- 23 files changed, 3154 insertions(+), 1568 deletions(-) create mode 100644 public/content/translations/ja/developers/tutorials/ipfs-decentralized-ui/index.md create mode 100644 public/content/translations/ja/developers/tutorials/scam-token-tricks/index.md create mode 100644 public/content/translations/ja/developers/tutorials/secret-state/index.md create mode 100644 public/content/translations/ja/developers/tutorials/server-components/index.md diff --git a/public/content/translations/ja/developers/tutorials/how-to-view-nft-in-metamask/index.md b/public/content/translations/ja/developers/tutorials/how-to-view-nft-in-metamask/index.md index 21fdf826cb0..4bb6d8afa0e 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-view-nft-in-metamask/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-view-nft-in-metamask/index.md @@ -1,36 +1,33 @@ --- -title: ウォレットでNFTを表示する方法(NFTチュートリアルシリーズのパート3/3) -description: このチュートリアルでは、既存のMetaMask上にNFTを表示する方法について説明します。 +title: "ウォレットでNFTを表示する方法(NFTチュートリアルシリーズ パート3/3)" +description: "このチュートリアルでは、MetaMaskで既存のNFTを表示する方法について説明します。" author: "Sumi Mudgil" -tags: - - "ERC-721" - - "Alchemy" - - "Solidity" +tags: [ "ERC-721", "Alchemy", "Solidity" ] skill: beginner lang: ja published: 2021-04-22 --- -このチュートリアルは、NFTチュートリアルシリーズのパート3/3です。ここでは、新しくミントされたNFTを見ていきますが、 MetaMaskを使用して、メインネットや任意のテストネットなど、ERC-721トークンの一般的なチュートリアルを使用することができます。 イーサリアム上でNFTをミントする方法については、[パート1のNFTスマートコントラクトの作成&デプロイ方法](/developers/tutorials/how-to-write-and-deploy-an-nft)をご覧ください。 +このチュートリアルは、NFTチュートリアルシリーズのパート3/3で、新しくミントしたNFTを表示します。 ただし、この一般的なチュートリアルは、メインネットや任意のテストネットを含め、MetaMaskを使用するあらゆるERC-721トークンに利用できます。 Ethereumで独自のNFTをミントする方法を学びたい場合は、[パート1「NFTスマートコントラクトの作成とデプロイ方法」](/developers/tutorials/how-to-write-and-deploy-an-nft)をご覧ください。 -朗報です。 これからご説明する仮想ウォレットで新しくミントされたNFTを表示する方法は、NFTチュートリアルシリーズの中で最短かつ最も簡単なパートです。 この例では、前の2つのパートで使用していたMetaMaskを使用します。 +おめでとうございます! NFTチュートリアルシリーズの中で最も短く、最も簡単なパートにたどり着きました。それは、仮想ウォレットで新しくミントしたNFTを表示する方法です。 この例では、前の2つのパートで使用したMetaMaskを使用します。 -前提条件として、モバイルにMetaMaskをインストールしておく必要があり、NFTを割り当てたアカウントも必要となります。[iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202)または[Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US)から無料でアプリを入手できます。 +前提条件として、モバイル版MetaMaskをインストールし、NFTをミントしたアカウントを含めておく必要があります。アプリは[iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202)または[Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US)で無料で入手できます。 ## ステップ1: ネットワークをSepoliaに設定する {#set-network-to-sepolia} -アプリの上部にある「Wallet」ボタンを押すと、ネットワークを選択するよう指示されます。 Sepoliaネットワーク上でNFTをミントしたので、ネットワークとしてSepoliaを選択します。 +アプリの上部にある「ウォレット」ボタンを押すと、ネットワークを選択するよう促されます。 NFTはSepoliaネットワークでミントしたため、ネットワークとしてSepoliaを選択します。 ![MetaMaskモバイルでSepoliaをネットワークとして設定する方法](./goerliMetamask.gif) -## ステップ2: 収集品をMetaMaskに追加する {#add-nft-to-metamask} +## ステップ2: コレクティブルをMetaMaskに追加する {#add-nft-to-metamask} -Sepoliaネットワークに接続後、右側の「Collectibles」タブを選択し、NFTスマートコントラクトアドレスとNFTのERC-721トークンIDを追加します。そうすることで、チュートリアルのパートIIでデプロイしたNFTからのトランザクションハッシュに基づいてEtherscanで見つけることができます。 +Sepoliaネットワークに接続したら、右側にある「コレクティブル」タブを選択し、NFTのスマートコントラクトアドレスとERC-721トークンIDを追加します。これらは、チュートリアルのパートIIでデプロイしたNFTのトランザクションハッシュを基に、Etherscanで確認できます。 ![トランザクションハッシュとERC-721トークンIDを見つける方法](./findNFTEtherscan.png) -何度か再読込をしないと表示されないこともありますが、NFTはそこに存在しています 。 +NFTを表示するために数回更新が必要な場合がありますが、最終的には表示されます! ![NFTをMetaMaskにアップロードする方法](./findNFTMetamask.gif) -おめでとうございます。 NFTのミントに成功しました。NFTを表示することもできます。 あなたがNFTの世界で新たな旋風を巻き起こすのを楽しみにしています。 +おめでとうございます! NFTのミントに成功し、表示できるようになりました。 あなたがNFTの世界で新たな旋風を巻き起こすのを楽しみにしています。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-write-and-deploy-an-nft/index.md b/public/content/translations/ja/developers/tutorials/how-to-write-and-deploy-an-nft/index.md index e1bb40929f2..fc148b12993 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-write-and-deploy-an-nft/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-write-and-deploy-an-nft/index.md @@ -1,12 +1,8 @@ --- -title: NFTの作成&デプロイ方法(NFTチュートリアルシリーズの1/3) -description: このチュートリアルは、イーサリアムとInterPlanetary File System(IPFS)を使用して、非代替性トークン(ERC-721トークン)のスマートコントラクトを作成、デプロイする方法について、段階的に学ぶNFTシリーズのパート1です +title: "NFTの作成とデプロイ方法(NFTチュートリアルシリーズ 1/3)" +description: "このチュートリアルは、イーサリアムとInterPlanetary File System(IPFS)を使用して、非代替性トークン(ERC-721トークン)のスマートコントラクトを作成、デプロイする方法について、段階的に学ぶNFTシリーズのパート1です" author: "Sumi Mudgil" -tags: - - "ERC-721" - - "Alchemy" - - "Solidity" - - "スマートコントラクト" +tags: [ "ERC-721", "Alchemy", "Solidity", "スマート契約" ] skill: beginner lang: ja published: 2021-04-22 @@ -14,72 +10,79 @@ published: 2021-04-22 NFTによってブロックチェーンが世間の目に触れるようになった今、イーサリアムブロックチェーン上に自分のNFTコントラクト(ERC-721トークン)を公開することで、自身のモチベーションを高める絶好の機会となります。 -Alchemyは、Makersplace(直近では、Christie'sでレコードデジタルアートワークが$69Mで落札され、記録を更新)、 Dapper Labs(NBA Top Shot&Crypto Kittiesのクリエイター)、OpenSea(世界最大のNFTマーケットプレイス)、Zora、Super Rare、NFTFi、Foundation、Enjin、Origin Protocol、Immutableなど、NFTスペースで著名人の力になれることを非常に誇りに思っています。 +Alchemyは、Makersplace (最近、クリスティーズで6900万ドルの記録的なデジタルアート作品の販売を達成)、Dapper Labs (NBA Top Shot & Crypto Kittiesの制作者)、OpenSea (世界最大のNFTマーケットプレイス)、Zora、Super Rare、NFTfi、Foundation、Enjin、Origin Protocol、Immutableなど、NFT分野のビッグネームを支えていることを非常に誇りに思っています。 -このチュートリアルでは、[MetaMask](https://metamask.io/)、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org/)、[Pinata](https://pinata.cloud/)、[Alchemy](https://alchemy.com/signup/eth)を使用してSepoliaテストネットワーク上でERC-721スマートコントラクトの作成とデプロイのウォークスルーを行います(現時点でしっかりと理解できていなくても、心配はご無用です。後ほどご説明します) 。 +このチュートリアルでは、[MetaMask](https://metamask.io/)、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org/)、[Pinata](https://pinata.cloud/)、[Alchemy](https://alchemy.com/signup/eth)を使用して、Sepoliaテストネット上でERC-721スマートコントラクトを作成し、デプロイする手順を説明します (これらの意味がまだ分からなくても心配はいりません — これから説明します!)。 チュートリアルのパート2では、スマートコントラクトを使用してNFTをミントする方法について、パート3では、MetaMaskでNFTを表示する方法について説明します。 -ご質問があれば[Alchemy Discord](https://discord.gg/gWuC7zB)にお問い合わせいただくか、 [AlchemyのNFT API docs](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api)をご覧ください。 +もちろん、いつでも質問があれば、[Alchemy Discord](https://discord.gg/gWuC7zB)でお気軽にお問い合わせいただくか、[AlchemyのNFT APIドキュメント](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api)をご覧ください! ## ステップ1: イーサリアムネットワークに接続する {#connect-to-ethereum} -イーサリアムのブロックチェーンにリクエストを行う方法はたくさんありますが、ここでは分かりやすくするため、[Alchemy](https://alchemy.com/signup/eth)の無料アカウントを使用します。このアカウントはブロックチェーンの開発者プラットフォームとAPIで、独自のノードを実行せずにイーサリアムチェーンと通信できるものです。 +イーサリアムのブロックチェーンにリクエストを送信する方法はいくつかありますが、簡単にするために、[Alchemy](https://alchemy.com/signup/eth)の無料アカウントを使用します。Alchemyは、独自のノードを実行することなくイーサリアムチェーンと通信できる、ブロックチェーン開発者向けのプラットフォームおよびAPIです。 -このチュートリアルでは、スマートコントラクトのデプロイメントの仕組みを理解するために、Alchemyの開発者用ツールも活用します。 Alchemyアカウントをお持ちでない場合は、 [こちら](https://alchemy.com/signup/eth)から無料で登録できます。 +このチュートリアルでは、スマートコントラクトのデプロイメントの仕組みを理解するために、Alchemyの開発者用ツールも活用します。 まだAlchemyアカウントをお持ちでない場合は、[こちら](https://alchemy.com/signup/eth)から無料で登録できます。 -## ステップ2: アプリ(およびAPIキー)を作成する {#make-api-key} +## ステップ2: アプリ (およびAPIキー) を作成する {#make-api-key} -Alchemyのアカウントを作成すると、アプリを作成することでAPIキーを生成できます。 これにより、Sepoliaテストネットワークへのリクエストが可能になります。 テストネットワークの詳細については、[こちらのガイド](https://docs.alchemyapi.io/guides/choosing-a-network)をご覧ください。 +Alchemyのアカウントを作成した後、アプリを作成することでAPIキーを生成することができます。 これにより、Sepoliaテストネットワークへのリクエストが可能になります。 テストネットについてさらに詳しく知りたい場合は、[こちらのガイド](https://docs.alchemyapi.io/guides/choosing-a-network)をご覧ください。 1. ナビゲーションバーの「Apps」にマウスを合わせて、「Create App)」をクリックし、Alchemyダッシュボードの「Create App」ページに移動してください。 -![アプリを作成する](./create-your-app.png) +![アプリを作成](./create-your-app.png) 2. アプリに名前を付け(私たちは「My First NFT!」にしました)、簡単な説明を記述し、「Ethereum」チェーンを選択して、ネットワークに「Sepolia」を設定します。 マージ以降、他のテストネットは非推奨となっています。 ![アプリを設定して公開する](./alchemy-explorer-sepolia.png) -3. 「Create app」をクリックして完了です。 下記のテーブルにアプリが表示されます。 +3. 「Create app」をクリックします。 アプリが下の表に表示されます。 -## ステップ 3: イーサリアムアカウント(アドレス)を作成する {#create-eth-address} +## ステップ3: イーサリアムアカウント (アドレス) を作成する {#create-eth-address} -トランザクションの送受信には、イーサリアムアカウントが必要です。 このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 イーサリアムのトランザクションの仕組みの詳細については、イーサリアム・ファウンデーションの[こちらのページ](/developers/docs/transactions/)をご覧ください。 +トランザクションの送受信には、イーサリアムアカウントが必要です。 このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 イーサリアム上のトランザクションの仕組みについてさらに詳しく知りたい場合は、イーサリアム・ファウンデーションの[こちらのページ](/developers/docs/transactions/)をご覧ください。 -Metamaskのアカウントは[こちら](https://metamask.io/download)から無料でダウンロード、作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は(実際に支払いが発生しないように)右上の「Sepolia Test Network」に切り替えてください。 +MetaMaskアカウントは、[こちら](https://metamask.io/download)から無料でダウンロードして作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は(実際に支払いが発生しないように)右上の「Sepolia Test Network」に切り替えてください。 -![Sepoliaをネットワークとして設定する](./metamask-goerli.png) +![Sepoliaをネットワークとして設定](./metamask-goerli.png) -## ステップ4: フォーセットからイーサリアムを追加する {#step-4-add-ether-from-a-faucet} +## ステップ4: フォーセットからイーサを追加する {#step-4-add-ether-from-a-faucet} -テストネットワークにスマートコントラクトをデプロイするには、偽のETHが複数必要になります。 ETHを取得するには、Alchemyがホストする[Sepoliaフォーセット](https://sepoliafaucet.com/)へ行き、ログインしてアカウントアドレスを入力し、「Send Me ETH」をクリックしてください。 MetamaskアカウントにETHが表示されるはずです。 +テストネットワークにスマートコントラクトをデプロイするには、偽のETHが複数必要になります。 ETHを入手するには、Alchemyがホストする[Sepoliaフォーセット](https://sepoliafaucet.com/)にアクセスし、ログインしてアカウントアドレスを入力し、「Send Me ETH」をクリックします。 MetamaskアカウントにETHが表示されるはずです。 ## ステップ5: 残高を確認する {#check-balance} -残高を再度確認するには、 [Alchemy CHAINS APIS](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)を使用して [eth_getBalance](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)をリクエストしてみましょう。 リクエストすると、ウォレット内のETHの量が返却されます。 Metamaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 +残高があることを再確認するために、[Alchemyのcomposerツール](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用して[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)リクエストを実行してみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 - `{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}` + ``` + {"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} + ``` -> **注:** この結果の単位はweiであり、ETHではありません。 weiはETHの最小単位として使われています。 「wei」 から「ETH」への変換は次の通りです: 1 eth = 1018 wei 。 例えば、0xde0b6b3a7640000を10進数に変換すると1*1018 weiとなり、1ETHに相当します。 +> **注** この結果はETHではなく、wei単位です。 weiはETHの最小単位として使われています。 「wei」 から「ETH」への変換は次の通りです: 1 eth = 1018 wei 。 例えば、0xde0b6b3a7640000を10進数に変換すると1\*1018 weiとなり、1ETHに相当します。 -ご安心ください。 私たちの偽物のお金はすべてそこにあります。 +ふう! 私たちの偽物のお金はすべてそこにあります。 ## ステップ6: プロジェクトを初期化する {#initialize-project} まず、プロジェクトのフォルダを作成する必要があります。 コマンドラインに移動し、次のように入力します。 + ``` mkdir my-nft cd my-nft + ``` -プロジェクトフォルダに入ったら、 npm initを使用してプロジェクトを初期化します。 npmがインストールされていない場合は、[こちらの手順](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)に従ってください。([Node.js](https://nodejs.org/en/download/)も必要となりますので、こちらもダウンロードしてください。) +プロジェクトフォルダに入ったら、 npm initを使用してプロジェクトを初期化します。 まだnpmをインストールしていない場合は、[これらの手順](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)に従ってください ([Node.js](https://nodejs.org/en/download/)も必要なので、そちらもダウンロードしてください!)。 + ``` npm init + ``` インストール時の質問に対する回答方法は自由です。参考までに過去の回答方法は次のとおりです。 + ```json package name: (my-nft) version: (1.0.0) - description: My first NFT! + description: 初めてのNFT! entry point: (index.js) test command: git repository: @@ -91,7 +94,7 @@ Metamaskのアカウントは[こちら](https://metamask.io/download)から無 { "name": "my-nft", "version": "1.0.0", - "description": "My first NFT!", + "description": "初めてのNFT!", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -100,26 +103,32 @@ Metamaskのアカウントは[こちら](https://metamask.io/download)から無 "license": "ISC" } ``` + 「package.json」を承認してください。これで準備が完了しました。 ## ステップ7: [Hardhat](https://hardhat.org/getting-started/#overview)をインストールする {#install-hardhat} -Hardhatは、イーサリアムのソフトウェアをコンパイル、デプロイ、テスト、デバッグするための開発環境です。 開発者がライブチェーンにデプロイする前に、スマートコントラクトやDappsをローカルに構築する際に役立ちます。 +Hardhatは、イーサリアムのソフトウェアをコンパイル、デプロイ、テスト、デバッグするための開発環境です。 デベロッパーがライブチェーンにデプロイする前に、スマートコントラクトや分散型アプリケーション(Dapp)をローカルに構築する際に役立ちます。 「my-nft」プロジェクトの中で実行してください。 + ``` npm install --save-dev hardhat + ``` -[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、こちらのページをご覧ください。 +[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、このページをご覧ください。 ## ステップ8: Hardhatプロジェクトを作成する {#create-hardhat-project} -プロジェクトフォルダ内で実行してください。 +プロジェクトフォルダ内で以下を実行します。 + ``` npx hardhat + ``` -次に、ウェルカムメッセージと選択肢が表示されます。 「create an empty hardhat.config.js」を選択してください。 +ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「create an empty hardhat.config.js」を選択してください。 + ``` 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 @@ -128,11 +137,12 @@ Hardhatは、イーサリアムのソフトウェアをコンパイル、デプ 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 - 👷 Welcome to Hardhat v2.0.11 👷‍ - ? What do you want to do? … - Create a sample project - ❯ Create an empty hardhat.config.js - Quit + 👷 Hardhat v2.0.11へようこそ 👷‍ + ? 何を行いますか? … + サンプルプロジェクトを作成する + ❯ 空のhardhat.config.jsを作成する + 終了 + ``` 「hardhat.config.js」というファイルが生成され、ここでプロジェクトのセットアップの全てを指定します (ステップ13)。 @@ -140,8 +150,10 @@ Hardhatは、イーサリアムのソフトウェアをコンパイル、デプ プロジェクトを整理するために、2つの新しいフォルダを作成します。 コマンドラインでプロジェクトのルートディレクトリに移動し、次のように入力します。 + ``` mkdir contracts mkdir scripts + ``` - 「contracts/」は、NFT スマートコントラクトコードを保持する場所です。 @@ -149,16 +161,16 @@ Hardhatは、イーサリアムのソフトウェアをコンパイル、デプ ## ステップ10: コントラクトを作成する {#write-contract} -さて、環境が整ったところで、もっと面白いことをやりましょう。_スマートコントラクトのコードの作成です。_ +環境が整ったので、もっと面白いこと、つまり_スマートコントラクトのコード作成_に取り掛かりましょう! -お気に入りのエディタでmy-nftプロジェクトを開きます(通常は[VScode](https://code.visualstudio.com/)を使用しています)。 スマートコントラクトは、Solidityと呼ばれる言語で記述されています。MyNFT.solスマートコントラクトの作成にこの言語を使用します。 +お気に入りのエディタ (私たちは[VSCode](https://code.visualstudio.com/)が好きです) でmy-nftプロジェクトを開いてください。 スマートコントラクトは、Solidityと呼ばれる言語で記述されています。MyNFT.solスマートコントラクトの作成にこの言語を使用します。 -1. `contracts`フォルダに移動し、MyNFT.solという名前の新規ファイルを作成します。 +1. `contracts`フォルダに移動し、MyNFT.solという名前の新しいファイルを作成します -2. 以下は、 NFTスマートコントラクトコードです。これは[OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721)ライブラリのERC-721実装に基づいています。 以下の内容をコピーして、MyNFT.solファイルに貼り付けます。 +2. 以下は、[OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721)ライブラリのERC-721実装をベースにした、私たちのNFTスマートコントラクトのコードです。 以下の内容をコピーして、MyNFT.solファイルに貼り付けます。 ```solidity - //Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) + //[https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) をベースにしたコントラクト // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -188,29 +200,29 @@ Hardhatは、イーサリアムのソフトウェアをコンパイル、デプ } ``` -3. OpenZeppelinのコントラクトライブラリからクラスを継承しているので、コマンドラインで`npm install @openzeppelin/contracts`を実行して、ライブラリをフォルダにインストールします。 +3. OpenZeppelinのコントラクトライブラリからクラスを継承しているため、コマンドラインで`npm install @openzeppelin/contracts^4.0.0`を実行して、ライブラリをフォルダにインストールします。 -では、このコードの_役割_は一体何でしょうか。 一行ずつ分解してみましょう。 +では、このコードは一体何を_している_のでしょうか? 一行ずつ分解してみましょう。 -スマートコントラクトの先頭で、3つの[OpenZeppelin](https://openzeppelin.com/)スマートコントラクトのクラスをインポートしています。 +スマートコントラクトの冒頭で、3つの[OpenZeppelin](https://openzeppelin.com/)スマートコントラクトクラスをインポートします: -- @openzeppelin/contracts/token/ERC721/ERC721.solには、ERC-721標準の実装が含まれており、NFTスマートコントラクトはこれを継承しています。 (有効なNFTであるためには、スマートコントラクトはERC-721標準のすべてのメソッドを実装する必要があります。) 継承されたERC-721関数の詳細については、[こちら](https://eips.ethereum.org/EIPS/eip-721)のインターフェイス定義をご覧ください。 +- @openzeppelin/contracts/token/ERC721/ERC721.solには、ERC-721標準の実装が含まれており、NFTスマートコントラクトはこれを継承しています。 (有効なNFTであるためには、スマートコントラクトはERC-721標準のすべてのメソッドを実装する必要があります。) 継承されたERC-721関数の詳細については、[こちら](https://eips.ethereum.org/EIPS/eip-721)のインターフェース定義をご確認ください。 - @openzeppelin/contracts/utils/Counters.solは、1つずつ増減するカウンタを提供しており、 私たちのスマートコントラクトは、ミントされたNFTの合計数を追跡し、新しいNFTにユニークなIDを設定するためにカウンタを使用しています。 (スマートコントラクトを使用してミントされた各NFTには、ユニークなIDが割り当てられている必要があります。ここでは、ユニークIDは、存在するNFTの合計数によって決定されます。 例えば、スマートコントラクトでミントした最初のNFTには「1」のIDが付与され、2番目のNFTには「2」のIDが付与されます。) -- @openzeppelin/contracts/access/Ownable.solは[アクセスコントロール](https://docs.openzeppelin.com/contracts/3.x/access-control)をスマートコントラクトに設定するため、スマートコントラクトの所有者(あなた)だけがNFTをミントできます。 (注: アクセス制御の実装は完全に任意です。 スマートコントラクトを使って誰でもNFTをミントできるようにしたい場合は、10行目の「Ownable」、17行目の「onlyOwner」を削除します。) +- @openzeppelin/contracts/access/Ownable.solは、スマートコントラクトに[アクセス制御](https://docs.openzeppelin.com/contracts/3.x/access-control)を設定するので、スマートコントラクトの所有者 (あなた) のみがNFTをミントできます。 (注: アクセス制御の実装は完全に任意です。 スマートコントラクトを使って誰でもNFTをミントできるようにしたい場合は、10行目の「Ownable」、17行目の「onlyOwner」を削除します。) -インポートステートメントの後にカスタムNFTスマートコントラクトがありますが、非常に短いもので、カウンタ、コンストラクタ、単一の関数しか含まれていません。 これは、NFTの所有者を返す`ownerOf`とNFTの所有権を他のアカウントに転送する`transferFrom`など、NFTを作成するために必要な大部分のメソッドを実装しているOpenZeppelinコントラクトを継承したおかげです。 +インポートステートメントの後にカスタムNFTスマートコントラクトがありますが、非常に短いもので、カウンタ、コンストラクタ、単一の関数しか含まれていません。 これは、継承したOpenZeppelinコントラクトのおかげです。このコントラクトには、NFTの所有者を返す`ownerOf`や、NFTの所有権をあるアカウントから別のアカウントに移転する`transferFrom`など、NFTの作成に必要なメソッドのほとんどが実装されています。 ERC-721コンストラクタでは、「MyNFT」と「NFT」の2つの文字列を渡すことに気づくでしょう。 最初の変数はスマートコントラクトの名前で、2番目の変数はそのシンボルです。 これらの変数にはそれぞれに自由に名前を付けることができます。 -最後に、`mintNFT(address recipient, string memory tokenURI)`という関数で、NFTをミントすることできます。 この関数には2つの変数が必要です。 +最後に、NFTをミントできる`mintNFT(address recipient, string memory tokenURI)`関数があります! この関数には2つの変数が必要です。 -- `address recipient`は、新しくミントされたNFTを受け取るアドレスを指定します。 +- `address recipient`は、新しくミントされたNFTを受け取るアドレスを指定します -- `string memory tokenURI`は、NFTのメタデータを記述したJSONドキュメントに解決される必要がある文字列です。 NFTのメタデータは、名前、説明、画像、その他の属性など、設定可能なプロパティを持つことができます。 チュートリアルのパート2では、このメタデータの設定方法について説明します。 +- `string memory tokenURI`は、NFTのメタデータを記述するJSONドキュメントに解決されるべき文字列です。 NFTのメタデータは、名前、説明、画像、その他の属性など、設定可能なプロパティを持つことができます。 チュートリアルのパート2では、このメタデータの設定方法について説明します。 -`mintNFT`は継承されたERC-721ライブラリから複数のメソッドを呼び出し、最終的にミントされたばかりのNFTのIDを示す数値を返します。 +`mintNFT`は、継承されたERC-721ライブラリからいくつかのメソッドを呼び出し、最終的に新しくミントされたNFTのIDを表す数値を返します。 ## ステップ11: MetaMaskとAlchemyをプロジェクトに接続する {#connect-metamask-and-alchemy} @@ -218,24 +230,28 @@ ERC-721コンストラクタでは、「MyNFT」と「NFT」の2つの文字列 仮想ウォレットから送信されるすべてのトランザクションには、固有の秘密鍵を使用した署名が必要です。 この許可をプログラムに与えるために、秘密鍵(とAlchemyのAPIキー)を環境ファイルに安全に格納する作業を行います。 -トランザクションの送信の詳細については、web3を使用したトランザクションの送信に関する[こちらのチュートリアル](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)をご覧ください。 +トランザクションの送信について詳しく知るには、web3を使用したトランザクション送信に関する[このチュートリアル](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)をご覧ください。 まず、プロジェクトディレクトリにdotenvパッケージをインストールします。 + ``` npm install dotenv --save + ``` -次に、 `.env`ファイルをプロジェクトのルートディレクトリに作成し、そのファイルにMetamaskの秘密鍵とHTTP Alchemy APIのURLを追加します。 +次に、プロジェクトのルートディレクトリに`.env`ファイルを作成し、MetaMaskの秘密鍵とHTTP Alchemy API URLを追加します。 -- MetaMaskから秘密鍵をエクスポートするには、 [こちらの手順](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)に従ってください。 +- MetaMaskから秘密鍵をエクスポートするには、[これらの手順](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)に従ってください - HTTP Alchemy API URLを取得し、クリップボードにコピーするには、以下を参照してください。 -![Alchemy API URLをコピーする](./copy-alchemy-api-url.gif) +![Alchemy API URLをコピー](./copy-alchemy-api-url.gif) -`.env`ファイルは次のようになります。 +これで、`.env`は次のようになります: + ``` API_URL="https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY="your-metamask-private-key" + ``` これらの変数を実際にコードに接続するために、ステップ13で hardhat.config.jsファイル内のこれらの変数を参照します。 @@ -243,21 +259,23 @@ ERC-721コンストラクタでは、「MyNFT」と「NFT」の2つの文字列 ## ステップ12: Ethers.jsをインストールする {#install-ethers} -Ethers.jsは、よりユーザーフレンドリーなメソッドで[標準のJSON-RPCメソッド](/developers/docs/apis/json-rpc/)をラップすることにより、イーサリアムとの対話やリクエストを簡単にするライブラリです。 +Ethers.jsは、[標準のJSON-RPCメソッド](/developers/docs/apis/json-rpc/)をよりユーザーフレンドリーなメソッドでラップすることで、イーサリアムとの対話やリクエストを容易にするライブラリです。 -Hardhatは、追加のツールと拡張機能のための[プラグイン](https://hardhat.org/plugins/)の統合を非常に簡単にしてくれます。 コントラクトのデプロイメントに[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を利用します([Ethers.js](https://github.com/ethers-io/ethers.js/)には、複数の非常にクリーンなコントラクトのデプロイメント方法があります)。 +Hardhatでは、追加のツールや拡張機能のための[プラグイン](https://hardhat.org/plugins/)を非常に簡単に統合できます。 コントラクトのデプロイには[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を活用します([Ethers.js](https://github.com/ethers-io/ethers.js/)には非常にクリーンなコントラクトデプロイメントメソッドがあります)。 プロジェクトのホームディレクトリで以下を実行します。 + ``` npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0 + ``` -次のステップでは、 hardhat.config.js でもイーサリアムが必要になります。 +次のステップのhardhat.config.jsでもEthers(.js)が必要になります。 -## ステップ13: hardhat.config.jsをアップデートする {#update-hardhat-config} +## ステップ13: hardhat.config.jsを更新する {#update-hardhat-config} -これまでにいくつかの依存関係とプラグインを追加しました。プロジェクトがそれらすべてを知るように、 hardhat.config.js を更新する必要があります。 +ここまでで、いくつかの依存関係とプラグインを追加しました。次に、プロジェクトがそれらすべてを認識できるように、hardhat.config.jsをアップデートする必要があります。 -「hardhat.config.js」を以下のように更新してください: +hardhat.config.jsを以下のようにアップデートします。 ```js /** @@ -281,28 +299,30 @@ Hardhatは、追加のツールと拡張機能のための[プラグイン](http ## ステップ14: コントラクトをコンパイルする {#compile-contract} -ここまででしっかりと動作していることを確認するため、コントラクトをコンパイルしてみましょう。 コンパイルは、Hardhat の組み込まれた機能の1つです。 +ここまでの作業がうまくいっていることを確認するために、コントラクトをコンパイルしてみましょう。 コンパイルは、組み込みのHardhatタスクの1つです。 コマンドラインで以下を実行します。 + ``` npx hardhat compile + ``` -SPDX license identifier not provided in source file という警告が表示されるかもしれません。しかし、それについて心配する必要はありません — うまくいけば、他のすべてが良く見えるでしょう! 表示された場合は、いつでも[Alchemy discord](https://discord.gg/u72VCg3)でメッセージを送信できます。 +SPDXライセンス識別子がソースファイルで提供されていないという警告が表示される場合がありますが、心配する必要はありません。警告が表示されないのがベストですが、 うまくいかない場合は、いつでも[Alchemy Discord](https://discord.gg/u72VCg3)でメッセージを送ることができます。 -## ステップ15: デプロイスクリプトを書く {#write-deploy} +## ステップ15: デプロイスクリプトを作成する {#write-deploy} コントラクトの作成と設定ファイルの作成が完了したら、いよいよコントラクトのデプロイのためのスクリプトを作成します。 -`scripts/` フォルダに移動し、 `deploy.js` という名前の新しいファイルを作成し、以下の内容を追加します: +`scripts/`フォルダに移動して`deploy.js`という名前の新しいファイルを作成し、以下の内容を追加します: ```js async function main() { const MyNFT = await ethers.getContractFactory("MyNFT") - // Start deployment, returning a promise that resolves to a contract object + // デプロイを開始し、コントラクトオブジェクトに解決されるプロミスを返す const myNFT = await MyNFT.deploy() await myNFT.deployed() - console.log("Contract deployed to address:", myNFT.address) + console.log("コントラクトがデプロイされたアドレス:", myNFT.address) } main() @@ -313,40 +333,48 @@ main() }) ``` -Hardhatがコードの各行で行っている驚くべき内容については、Hardhatの[コントラクトチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で説明されています。以下では、その説明を採用しています。 +Hardhatは、[コントラクトのチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で、これらのコードの各行が何をするかを非常にうまく説明しています。ここではその説明を採用しました。 + ``` const MyNFT = await ethers.getContractFactory("MyNFT"); + ``` -ethers.js の中の ContractFactory は新しいスマートコントラクトを作成するための抽象化です。ここでの MyNFT は NFT コントラクトのインスタンスのためのファクトリです。 hardhat-ethers プラグインを使用する場合、 ContractFactory および Contract インスタンスはデフォルトで最初の署名者に接続されます。 +ethers.jsのContractFactoryは新しいスマートコントラクトをデプロイするための抽象化であり、ここでのMyNFTはNFTコントラクトのインスタンスのためのファクトリです。 hardhat-ethersプラグインを使用する場合、 ContractFactoryおよびContractインスタンスはデフォルトで最初の署名者に接続されます。 + ``` const myNFT = await MyNFT.deploy(); + ``` -ContractFactory の deploy() を呼び出すとデプロイメントが開始し、 Contract を解決するための Promise が返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 +ContractFactoryのdeploy()を呼び出すとデプロイメントが開始し、 Contractを解決するためのPromiseが返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 ## ステップ16: コントラクトをデプロイする {#deploy-contract} -ようやく、スマートコントラクトをデプロイする準備が整いました。 プロジェクトディレクトリのルートに戻り、コマンドラインで以下を実行します: +ようやく、スマートコントラクトをデプロイする準備が整いました。 プロジェクトディレクトリのルートに戻り、コマンドラインで以下を実行します。 + ``` npx hardhat --network sepolia run scripts/deploy.js + ``` 次のような画面が表示されるはずです。 + ``` Contract deployed to address: 0x4C5266cCc4b3F426965d2f51b6D910325a0E7650 + ``` -[Sepolia etherscan](https://sepolia.etherscan.io/)に移動し、コントラクトアドレスを検索すると、正常にデプロイされたことが確認できるはずです。 すぐに見られない場合は、しばらくお待ちください。 トランザクションは以下のようなものになります。 +[Sepolia etherscan](https://sepolia.etherscan.io/)にアクセスし、コントラクトアドレスを検索すると、正常にデプロイされたことを確認できるはずです。 すぐに表示されない場合は、しばらくお待ちください。 トランザクションは以下のようなものになります。 -![Etherscan でトランザクションアドレスを表示する](./etherscan-sepoila-contract-creation.png) +![Etherscanでトランザクションアドレスを表示](./etherscan-sepoila-contract-creation.png) -From アドレスは MetaMask アカウントアドレスと一致し、To アドレスは「Contract Creation」となります。 トランザクション内容をクリックすると、To フィールドにコントラクトアドレスが表示されます: +FromアドレスはあなたのMetaMaskアカウントのアドレスと一致し、Toアドレスには「Contract Creation」と表示されます。 このトランザクションをクリックすると、Toフィールドにコントラクトアドレスが表示されます。 -![Etherscanでコントラクトアドレスを表示する](./etherscan-sepolia-tx-details.png) +![Etherscanでコントラクトアドレスを表示](./etherscan-sepolia-tx-details.png) -Yassss! イーサリアム(テストネット)チェーンにNFTスマートコントラクトをデプロイできました。 +おめでとうございます。 イーサリアム(テストネット)チェーンにNFTスマートコントラクトをデプロイできました。 -内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemyapi.io/explorer)のExplorerタブに移動してみましょう。 Alchemy のアプリが複数ある場合は、必ずアプリでフィルタリングし、「MyNFT」を選択してください。 +内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemyapi.io/explorer)のExplorerタブに移動してみましょう。 Alchemyのアプリが複数ある場合は、必ずアプリでフィルタリングして、「MyNFT」を選択してください。 -![Alchemy のエクスプローラーダッシュボードで「内部」で行われた通話を表示する](./alchemy-explorer-goerli.png) +![AlchemyのExplorerダッシュボードで「内部」で行われた呼び出しを表示](./alchemy-explorer-goerli.png) -ここでは、 .deploy() 関数を呼び出した際に、Hardhat/Ethers が内部で作った JSON-RPC の呼び出しをいくつか見ることができます。 ここで呼び出している2つの重要なJSON-RPCは、実際にSepoliaチェーン上でコントラクトを書き込むリクエストの[eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction)と、(トランザクションを送信する際の典型的なパターンである) ハッシュを与えられたトランザクションに関する情報を読み取るリクエスト[eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash)です。 トランザクションの送信の詳細については、このチュートリアルの [Web3 を使用したトランザクションの送信](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) をご覧ください。 +ここでは、Hardhat/Ethersが.deploy()関数を呼び出した際に、内部で行ったJSON-RPCの呼び出しを見ることができます。 ここで特筆すべき重要な2つのリクエストは、実際にSepoliaチェーンにスマートコントラクトを書き込むためのリクエストである[eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction)と、ハッシュが与えられたトランザクションに関する情報を読み取るためのリクエスト (トランザクション送信時の典型的なパターン) である[eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash)です。 トランザクションの送信についてさらに詳しく知るには、[Web3を使用したトランザクション送信](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)に関するこのチュートリアルをご覧ください。 -以上がこのチュートリアルのパート1です。 [パート2](/developers/tutorials/how-to-mint-an-nft/) では、NFT を発行することで実際にスマートコントラクトとやりとりをします。そして、[パート3](/developers/tutorials/how-to-view-nft-in-metamask/) では、Etherreum ウォレット内の NFT を確認する方法を示します! +チュートリアルのパート1は以上です。 [パート2では、NFTをミントしてスマートコントラクトと実際にやり取りし](/developers/tutorials/how-to-mint-an-nft/)、[パート3ではイーサリアムウォレットでNFTを表示する方法](/developers/tutorials/how-to-view-nft-in-metamask/)をご紹介します! diff --git a/public/content/translations/ja/developers/tutorials/interact-with-other-contracts-from-solidity/index.md b/public/content/translations/ja/developers/tutorials/interact-with-other-contracts-from-solidity/index.md index de8baa117fa..3d813505e4d 100644 --- a/public/content/translations/ja/developers/tutorials/interact-with-other-contracts-from-solidity/index.md +++ b/public/content/translations/ja/developers/tutorials/interact-with-other-contracts-from-solidity/index.md @@ -1,13 +1,8 @@ --- -title: Solidityを使用した他のコントラクトの活用 -description: 既存のコントラクトからスマートコントラクトをデプロイし、それを活用する方法 +title: "Solidityから他のコントラクトとやり取りする" +description: "既存のコントラクトからスマートコントラクトをデプロイし、それとやり取りする方法" author: "jdourlens" -tags: - - "スマートコントラクト" - - "Solidity" - - "Remix" - - "デプロイ" - - "構成可能性" +tags: [ "スマート契約", "Solidity", "Remix", "デプロイ", "構成可能性" ] skill: advanced lang: ja published: 2020-04-05 @@ -16,9 +11,9 @@ sourceUrl: https://ethereumdev.io/interact-with-other-contracts-from-solidity/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -これまでのチュートリアルでは、[最初のスマートコントラクトをデプロイする方法](/developers/tutorials/deploying-your-first-smart-contract/)と、[修飾子によるアクセス制御](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/)や[Solidityでのエラー処理](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/)といったいくつかの機能を追加する方法について学びました。 このチュートリアルでは、既存のコントラクトからスマートコントラクトをデプロイし、それを活用する方法について説明します。 +これまでのチュートリアルでは、[最初のスマートコントラクトのデプロイ方法](/developers/tutorials/deploying-your-first-smart-contract/)や、[修飾子(modifier)を使ったアクセス制御](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/)、[Solidityでのエラー処理](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/)といった機能の追加など、多くのことを学びました。 このチュートリアルでは、既存のコントラクトからスマートコントラクトをデプロイし、それとやり取りする方法を学びます。 -ここでは、`CounterFactory`という名前のファクトリーを作成することで、誰でも自分の`Counter`スマートコントラクトを所有できるようにするコントラクトを作成します。 初めに、これが`Counter`スマートコントラクトの初期コードです。 +ここでは、`CounterFactory`という名前のファクトリーを作成することで、誰もが自分自身の`Counter`スマートコントラクトを持てるようにするコントラクトを作成します。 まず、これが最初の`Counter`スマートコントラクトのコードです。 ```solidity pragma solidity 0.5.17; @@ -31,12 +26,12 @@ contract Counter { modifier onlyOwner(address caller) { - require(caller == _owner, "You're not the owner of the contract"); + require(caller == _owner, "あなたはこのコントラクトの所有者ではありません"); _; } modifier onlyFactory() { - require(msg.sender == _factory, "You need to use the factory"); + require(msg.sender == _factory, "ファクトリーを使用する必要があります"); _; } @@ -56,19 +51,19 @@ contract Counter { } ``` -ファクトリーのアドレスとコントラクト所有者のアドレスを追跡するために、コントラクトコードを少し変更していることに注意してください。 他のコントラクトからコントラクトコードを呼び出した場合、msg.senderはコントラクトファクトリーのアドレスを参照します。 コントラクトを使用して他のコントラクトとやり取りすることはよくあることなので、これは**理解しておくべき非常に重要なポイント**です。 したがって、複雑なケースでは誰が送信者なのかに注意を払う必要があります。 +ファクトリーのアドレスとコントラクト所有者のアドレスを追跡するために、コントラクトコードを少し変更したことに注意してください。 他のコントラクトからコントラクトコードを呼び出すと、`msg.sender`は私たちのコントラクトファクトリーのアドレスを参照します。 コントラクトを使って他のコントラクトとやり取りするのは一般的な方法であるため、これは**理解しておくべき、本当に重要な点**です。 したがって、複雑なケースでは誰が送信者(`sender`)なのかに注意する必要があります。 -このために、`onlyFactory`という修飾子も追加しました。この修飾子は、元の呼び出し元をパラメータとして渡すファクトリーのみが、状態変更関数を呼び出せるようにします。 +このため、元の呼び出し元をパラメータとして渡すファクトリーによってのみ状態変更関数が呼び出されるように、`onlyFactory`修飾子も追加しました。 -他のすべてのカウンターを管理する新しい`CounterFactory`内で、所有者をカウンターコントラクトのアドレスに関連付けるマッピングを追加します。 +他のすべての`Counter`を管理する新しい`CounterFactory`の中に、所有者とそのカウンターコントラクトのアドレスを関連付けるマッピングを追加します。 ```solidity mapping(address => Counter) _counters; ``` -イーサリアムでは、マッピングはJavaScriptのオブジェクトに相当します。これは、型Aのキーを型Bの値にマッピングできます。ここでは、所有者のアドレスをそのカウンターのインスタンスにマッピングしています。 +イーサリアムでは、マッピングはJavaScriptのオブジェクトに相当し、型Aのキーを型Bの値にマッピングできます。このケースでは、所有者のアドレスをその`Counter`のインスタンスにマッピングします。 -ユーザーのために新しいカウンターをインスタンス化する場合は、次のようになります。 +誰かのために新しい`Counter`をインスタンス化するには、次のようになります。 ```solidity function createCounter() public { @@ -77,9 +72,9 @@ mapping(address => Counter) _counters; } ``` -まず、そのユーザーがすでにカウンターを所有しているかどうかを確認します。 もしカウンターを所有していなければ(カウンターの数が0ならば)、そのアドレスを`Counter`コンストラクタに渡して新しいカウンターをインスタンス化し、新しく作成したインスタンスをマッピングに割り当てます。 +まず、その人がすでにカウンターを所有しているかどうかをチェックします。 その人がカウンターを所有していない場合、その人のアドレスを`Counter`のコンストラクタに渡して新しいカウンターをインスタンス化し、新しく作成されたインスタンスをマッピングに割り当てます。 -特定のカウンターの数を取得する場合は、次のようになります。 +特定の`Counter`のカウント数を取得するには、次のようになります。 ```solidity function getCount(address account) public view returns (uint256) { @@ -92,9 +87,9 @@ function getMyCount() public view returns (uint256) { } ``` -最初の関数は、指定されたアドレスにCounterコントラクトが存在するかどうかをチェックし、存在する場合にインスタンスから`getCount`メソッドを呼び出します。 2番目の関数`getMyCount`は、msg.senderを直接`getCount`関数に渡す短い関数です。 +最初の関数は、指定されたアドレスに対して`Counter`コントラクトが存在するかどうかをチェックし、その後インスタンスから`getCount`メソッドを呼び出します。 2番目の関数`getMyCount`は、`msg.sender`を直接`getCount`関数に渡すための、ただのショートカットです。 -`increment`関数もかなり類似していますが、`Counter`コントラクトに元のトランザクション送信者を渡します。 +`increment`関数も非常によく似ていますが、元のトランザクションの送信者(`sender`)を`Counter`コントラクトに渡します。 ```solidity function increment() public { @@ -103,11 +98,11 @@ function increment() public { } ``` -この関数を何度も呼び出すと、カウンターがオーバーフローする可能性がありますので注意してください。 オーバーフローが発生しないように、できる限り[SafeMathライブラリ](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/)を使用してください。 +何度も呼び出されると、カウンターがオーバーフローを起こす可能性があることに注意してください。 この可能性から保護するために、[SafeMathライブラリ](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/)をできるだけ使用すべきです。 -このコントラクトをデプロイするためには、`CounterFactory`と`Counter`の両方のコードが必要です。 例えばRemixでデプロイする場合、CounterFactoryを選択する必要があります。 +このコントラクトをデプロイするには、`CounterFactory`と`Counter`の両方のコードを提供する必要があります。 例えばRemixでデプロイする場合、`CounterFactory`を選択する必要があります。 -これが完成したコードです。 +こちらが全コードです。 ```solidity pragma solidity 0.5.17; @@ -120,12 +115,12 @@ contract Counter { modifier onlyOwner(address caller) { - require(caller == _owner, "You're not the owner of the contract"); + require(caller == _owner, "あなたはこのコントラクトの所有者ではありません"); _; } modifier onlyFactory() { - require(msg.sender == _factory, "You need to use the factory"); + require(msg.sender == _factory, "ファクトリーを使用する必要があります"); _; } @@ -172,6 +167,6 @@ contract CounterFactory { コンパイル後、Remixのデプロイセクションで、デプロイするファクトリーを選択します。 -![Remixにデプロイするファクトリーの選択](./counterfactory-deploy.png) +![Remixでデプロイするファクトリーの選択](./counterfactory-deploy.png) -コントラクトファクトリーを使うと、値の変化を確認できます。 もし、別のアドレスからスマートコントラクトを呼び出したい場合は、Remixのアカウントの選択で別のアドレスに変更する必要があります。 +その後、コントラクトファクトリーを操作して、値が変化することを確認できます。 もし、異なるアドレスからスマートコントラクトを呼び出したい場合は、Remixのアカウント選択でアドレスを変更する必要があります。 diff --git a/public/content/translations/ja/developers/tutorials/ipfs-decentralized-ui/index.md b/public/content/translations/ja/developers/tutorials/ipfs-decentralized-ui/index.md new file mode 100644 index 00000000000..8779919248d --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/ipfs-decentralized-ui/index.md @@ -0,0 +1,73 @@ +--- +title: "IPFSを利用した分散型ユーザーインターフェース" +description: "このチュートリアルでは、dappのユーザーインターフェースを保存するためにIPFSを使用する方法を学びます。 アプリケーションのデータとビジネスロジックは分散化されていますが、検閲耐性のあるユーザーインターフェースがなければ、ユーザーはいずれにせよそれにアクセスできなくなる可能性があります。" +author: Ori Pomerantz +tags: [ "ipfs" ] +skill: beginner +lang: ja +published: 2024-06-29 +--- + +あなたは素晴らしい新しいdappを作成しました。 そのための[ユーザーインターフェース](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/)も作成しました。 しかし、あなたのユーザーインターフェースはクラウド上の1つのサーバーにすぎず、誰かがそれを停止させて検閲しようとすることを恐れています。 このチュートリアルでは、ユーザーインターフェースを\*\*[Interplanetary File System (IPFS)](https://ipfs.tech/developers/)\*\*上に置くことで検閲を回避する方法を学びます。そうすれば、関心のある誰もが将来のアクセスのためにサーバーにそれをピン留めできるようになります。 + +[Fleek](https://resources.fleek.xyz/docs/)などのサードパーティサービスを使用して、すべての作業を行うこともできます。 このチュートリアルは、たとえ作業量が増えても、自分たちが何をしているのかを十分に理解したい人向けです。 + +## ローカルでの開始 {#getting-started-locally} + +複数の[サードパーティIPFSプロバイダー](https://docs.ipfs.tech/how-to/work-with-pinning-services/#use-a-third-party-pinning-service)がありますが、テストのためにローカルでIPFSを実行することから始めるのが最善です。 + +1. [IPFSユーザーインターフェース](https://docs.ipfs.tech/install/ipfs-desktop/#install-instructions)をインストールします。 + +2. Webサイトのディレクトリを作成します。 [Vite](https://vite.dev/)を使用している場合は、次のコマンドを使用します。 + + ```sh + pnpm vite build + ``` + +3. IPFS Desktopで、**[インポート] > [フォルダー]** をクリックし、前の手順で作成したディレクトリを選択します。 + +4. アップロードしたばかりのフォルダを選択し、**[名前の変更]** をクリックします。 より意味のある名前を付けます。 + +5. もう一度選択し、**[リンクを共有]** をクリックします。 URLをクリップボードにコピーします。 `https://ipfs.io/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ` のようなリンクになります。 + +6. **[ステータス]** をクリックします。 **[詳細設定]** タブを展開してゲートウェイアドレスを表示します。 例えば、私のシステムではアドレスは `http://127.0.0.1:8080` です。 + +7. リンク手順のパスとゲートウェイアドレスを組み合わせて、あなたのアドレスを見つけます。 例えば、上記の例では、URLは `http://127.0.0.1:8080/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ` です。 そのURLをブラウザで開いて、あなたのサイトを表示します。 + +## アップロード {#uploading} + +これでIPFSを使ってローカルでファイルを提供できるようになりましたが、これはあまりエキサイティングなことではありません。 次のステップは、あなたがオフラインのときに世界中から利用できるようにすることです。 + +よく知られている[ピニングサービス](https://docs.ipfs.tech/concepts/persistence/#pinning-services)がいくつかあります。 そのうちの1つを選びます。 どのサービスを使用するにしても、アカウントを作成し、IPFSデスクトップの**コンテンツ識別子 (CID)** を提供する必要があります。 + +個人的には、[4EVERLAND](https://docs.4everland.org/storage/4ever-pin/guides) が最も使いやすいと思いました。 その手順は次のとおりです。 + +1. [ダッシュボード](https://dashboard.4everland.org/overview)にアクセスし、ウォレットでログインします。 + +2. 左のサイドバーで **[ストレージ] > [4EVER Pin]** をクリックします。 + +3. **[アップロード] > [Selected CID]** をクリックします。 コンテンツに名前を付け、IPFSデスクトップからCIDを提供します。 現在、CIDは`Qm`で始まり、`QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`のように[base-58でエンコード](https://medium.com/bootdotdev/base64-vs-base58-encoding-c25553ff4524)されたハッシュを表す44文字の英数字が続く文字列ですが、[これは変更される可能性](https://docs.ipfs.tech/concepts/content-addressing/#version-1-v1)があります。 + +4. 初期ステータスは**Queued**です。 **Pinned**に変わるまでリロードします。 + +5. CIDをクリックしてリンクを取得します。 私のアプリケーションは[こちら](https://bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im/)でご覧いただけます。 + +6. 1か月以上ピン留めするには、アカウントを有効化する必要があるかもしれません。 アカウントの有効化には約1ドルかかります。 閉じてしまった場合は、ログアウトしてから再度ログインすると、再び有効化を求められます。 + +## IPFSからの使用 {#using-from-ipfs} + +この時点で、あなたはIPFSコンテンツを提供する中央集権型のゲートウェイへのリンクを持っています。 要するに、あなたのユーザーインターフェースは少し安全になったかもしれませんが、まだ検閲耐性はありません。 真の検閲耐性を得るには、ユーザーはIPFSを[ブラウザから直接](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites)使用する必要があります。 + +それがインストールされ(そしてデスクトップIPFSが動作していれば)、任意のサイトで[/ipfs/``](https://any.site/ipfs/bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im)にアクセスすると、そのコンテンツが分散型の方法で提供されます。 + +## 欠点 {#drawbacks} + +IPFSファイルを確実に削除することはできないため、ユーザーインターフェースを変更している間は、中央集権型のままにするか、IPFS上で可変性を提供するシステムである[Interplanetary Name System (IPNS)](https://docs.ipfs.tech/concepts/ipns/#mutability-in-ipfs)を使用するのがおそらく最善です。 もちろん、可変なものは検閲される可能性があります。IPNSの場合は、それに対応する秘密鍵を持つ人物に圧力をかけることで検閲されます。 + +さらに、一部のパッケージはIPFSとの間に問題を抱えているため、あなたのWebサイトが非常に複雑な場合、これは良い解決策ではないかもしれません。 そしてもちろん、サーバーとの統合に依存するものは、クライアントサイドをIPFSに置くだけでは分散化できません。 + +## 結論 {#conclusion} + +イーサリアムがdappのデータベースとビジネスロジックの側面を分散化できるように、IPFSはユーザーインターフェースを分散化できるようにします。 これにより、あなたのdappに対するもう一つの攻撃ベクトルを遮断できます。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md b/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md index 416e46b50bc..ed7ec7e198a 100644 --- a/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md +++ b/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md @@ -1,17 +1,15 @@ --- -title: create-eth-appでDappのフロントエンド開発をはじめましょう -description: create-eth-appの使い方と機能の概要 +title: "create-eth-appでdappのフロントエンド開発を始めましょう" +description: "create-eth-appの使い方と機能の概要" author: "Markus Waas" tags: - - "create-eth-app" - - "フロントエンド" - - "JavaScript" - - "ethers.js" - - "The Graph" - - "Aave" - - "Compound" - - "Uniswap" - - "Sablier" + [ + "フロントエンド", + "JavaScript", + "ethers.js", + "the graph", + "DeFi" + ] skill: beginner lang: ja published: 2020-04-27 @@ -19,11 +17,11 @@ source: soliditydeveloper.com sourceUrl: https://soliditydeveloper.com/create-eth-app --- -[create-eth-app](https://github.com/PaulRBerg/create-eth-app)については、前回の記事([Solidity の全体像](https://soliditydeveloper.com/solidity-overview-2020))で紹介しました。 今回は、create-eth-app をどのように使うか、どのような機能が統合されているか、およびさらに拡張する方法について学びます。 create-eth-app は、[ Sablier ](http://sablier.com/)の創業者である Paul Razvan Berg が立ち上げたプロジェクトで、フロントエンド開発をすばやく開始できるだけでなく、さまざまなオプションの統合機能も活用できます。 +前回は[Solidityの全体像](https://soliditydeveloper.com/solidity-overview-2020)を確認し、すでに[create-eth-app](https://github.com/PaulRBerg/create-eth-app)についても言及しました。 今回は、その使い方、統合されている機能、そしてそれを拡張するための追加のアイデアについて解説します。 [Sablier](http://sablier.com/)の創業者であるPaul Razvan Bergによって始められたこのアプリは、フロントエンド開発をすぐに開始でき、いくつかのオプションの統合機能から選択することができます。 ## インストール {#installation} -インストールには、Yarn 0.25 以上が必要です(`npm install yarn --global`)。 とても簡単です! +インストールには、Yarn 0.25以上が必要です (`npm install yarn --global`)。 次のように実行するだけで簡単です: ```bash yarn create eth-app my-eth-app @@ -31,44 +29,44 @@ cd my-eth-app yarn react-app:start ``` -このアプリでは、 [create-react-app](https://github.com/facebook/create-react-app)を利用しています。 アプリを表示するには、ブラウザで `http://localhost:3000/` を開きます。 本番環境にデプロイする準備ができたら、yarn build を実行してソースコードをまとめたファイルを作成します。 このアプリを手軽にホスティングするには、 [Netlify](https://www.netlify.com/)を利用すると良いでしょう。 GitHub リポジトリを作成して Netlify に登録し、ビルドコマンドをセットアップすれば完了です! あなたのアプリはインターネットに公開され、誰でも利用できるようになります。 これらはすべて無料です。 +内部で[create-react-app](https://github.com/facebook/create-react-app)を使用しています。 アプリを表示するには、`http://localhost:3000/`を開きます。 本番環境にデプロイする準備ができたら、`yarn build`で最小化されたバンドルを作成します。 これを簡単にホストする方法の一つとして、[Netlify](https://www.netlify.com/)があります。 GitHubリポジトリを作成し、それをNetlifyに追加し、ビルドコマンドをセットアップすれば完了です! あなたのアプリはホストされ、誰でも利用できるようになります。 そして、そのすべてが無料です。 ## 機能 {#features} -### React と create-react-app {#react--create-react-app} +### Reactとcreate-react-app {#react--create-react-app} -まずは、このアプリの核心である React と、*create-react-app*で提供される追加の機能すべてについて説明します。 イーサリアムとの統合を希望しない場合は、このアプリだけを使うのも良い方法です。 [React](https://reactjs.org/)を使えば、インタラクティブな UI を簡単に作成できます。 React は、[Vue](https://vuejs.org/)ほど初心者向けではありませんが、広く使われており、より多くの機能が提供されているだけでなく、何千ものライブラリを利用して機能を追加できます。 *create-react-app*も UI 開発を手軽に開始するのに役立つアプリで、以下の機能が含まれています。 +まず、このアプリの心臓部であるReactと、_create-react-app_に付属するすべての追加機能です。 Ethereumとの統合を望まない場合は、これだけを使用するのも良い選択肢です。 [React](https://react.dev/)を使えば、インタラクティブなUIをとても簡単に構築できます。 [Vue](https://vuejs.org/)ほど初心者向けではないかもしれませんが、依然として広く使われており、より多くの機能を持ち、そして最も重要なことに、何千もの追加のライブラリから選択することができます。 _create-react-app_を使えば、非常に簡単に始めることができ、次のものが含まれています: -- React、JSX、ES6、TypeScript、および Flow の構文に対応 -- スプレッド構文などの ES6 に含まれない言語拡張 -- オートプレフィックス CSS により、-webkit- などの接頭辞が不必要 -- カバレッジレポート機能を搭載した、高速でインタラクティブな単体テストランナー -- よくある間違いをリアルタイムで警告する開発環境用サーバ -- 本番環境用に、JS、CSS、および画像ファイルをハッシュやソースマップとバンドルできるビルドスクリプト +- React、JSX、ES6、TypeScript、Flow構文のサポート。 +- オブジェクトスプレッド演算子のようなES6を超える言語拡張。 +- 自動で接頭辞が付加されるCSS。-webkit-やその他の接頭辞は不要です。 +- カバレッジレポート機能を搭載した、高速でインタラクティブな単体テストランナー。 +- よくある間違いについて警告するライブ開発サーバー。 +- ハッシュとソースマップを使用して、本番用にJS、CSS、画像をバンドルするビルドスクリプト。 -特に*create-eth-app* は、新しい[副作用フック](https://reactjs.org/docs/hooks-effect.html)を利用しています。 これは、強力かつとてもコンパクトな、いわゆる関数コンポーネントを書くための方法です。 副作用フックを*create-eth-app*で活用する方法については、以下の Apollo に関するセクションを参照してください。 +特に_create-eth-app_は、新しい[副作用フック](https://legacy.reactjs.org/docs/hooks-effect.html)を利用しています。 強力でありながら非常に小さい、いわゆる関数コンポーネントを記述するためのメソッドです。 副作用フックをcreate-eth-appで活用する方法については、以下のApolloに関するセクションを参照してください。 ### Yarn Workspaces {#yarn-workspaces} -[Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/)では、複数のパッケージをひとつのルートフォルダで管理することができ、`yarn install`を使って依存関係を一度にインストールすることができます。 このアプローチは、スマートコントラクトのアドレスや ABI 管理(スマートコントラクトをどこにデプロイしたか、スマートコントラクトとどのようなやり取りを行うか)などの小さなパッケージを追加したり、The Graph との統合において特に有益であり、どちらの機能も`create-eth-app`に含まれています。 +[Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/)では、複数のパッケージをひとつのルートフォルダで管理することができ、`yarn install`を使って依存関係を一度にインストールすることができます。 このアプローチは、スマートコントラクトのアドレスやABI管理(スマートコントラクトをどこにデプロイしたか、スマートコントラクトとどのようなやり取りを行うか)などの小さなパッケージを追加したり、The Graphとの統合において特に有益であり、どちらの機能もcreate-eth-appに含まれています。 ### ethers.js {#ethersjs} -大部分のユーザーは現在でも[Web3.js](https://docs.web3js.org/)を使用していますが、昨年は[ethers.js](https://docs.ethers.io/)を Web3.js の代替として利用するユーザーが増加したため、*create-eth-app*には ethers.js が統合されています。 このライブラリで作業してもよいですし、Web3 に切り替えてもよいです。あるいは、もうすくベータが外れる[ethers.js v5](https://docs.ethers.org/v5/)にアップグレードするのもよいでしょう。 +[Web3](https://docs.web3js.org/)がまだ主流で使われていますが、[ethers.js](https://docs.ethers.io/)は昨年、代替として多くの注目を集めており、_create-eth-app_に統合されているものです。 これを使用するか、Web3に変更するか、またはベータ版がもうすぐ終了する[ethers.js v5](https://docs.ethers.org/v5/)へのアップグレードを検討することができます。 ### The Graph {#the-graph} -[GraphQL](https://graphql.org/)は、[RESTful API](https://restfulapi.net/)とは違う方法でデータを扱います。 特に分散型ブロックチェーンのデータを扱う場合、RESTful API よりもいくつかの利点があります。 その理由に興味がある方は、[GraphQL Will Power the Decentralized Web](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a)をご覧ください。 +[GraphQL](https://graphql.org/)は、[Restful API](https://restfulapi.net/)と比較して、データを処理するための代替方法です。 特に分散型ブロックチェーンデータにとって、Restful APIよりもいくつかの利点があります。 この背後にある理由に興味がある場合は、[GraphQL Will Power the Decentralized Web](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a)をご覧ください。 -通常、スマートコントラクトからは直接データを取得することができます。 最後に行った取引の時間を取得したい場合は、 `MyContract.methods.latestTradeTime().call()`を呼び出すだけで、infura のようなイーサリアムノードからデータを取得し、あなたの Dapp で使うことができます。 しかし、何百もの異なる種類のデータが必要な場合は事情が異なります。 ノードからのデータ取得が何百回も発生し、その都度[RTT](https://wikipedia.org/wiki/Round-trip_delay_time)が必要となるため、Dapp の処理速度が低下し、非効率になってしまいます。 ひとつの回避策としては、一度に複数のデータを返すデータ取得用の関数をスマートコントラクト側で用意する方法があるでしょう。 しかし、これは常に最善の方法とは言えません。 +通常、スマートコントラクトから直接データを取得します。 最新の取引の時刻を読み取りたいですか? `MyContract.methods.latestTradeTime().call()`を呼び出すだけで、Ethereumノードからdappにデータを取得します。 しかし、何百もの異なるデータポイントが必要な場合はどうでしょうか? そうなると、ノードへのデータフェッチが何百回も発生し、そのたびに[RTT](https://wikipedia.org/wiki/Round-trip_delay_time)が必要になり、dappが遅く非効率になります。 一つの回避策として、一度に複数のデータを返すフェッチャーコール関数をコントラクト内に設けることが考えられます。 しかし、これは必ずしも理想的ではありません。 -また、過去のデータが必要な場合もあるでしょう。 最後の取引の時間だけではなく、今まで自分が行った全ての取引の時間が知りたいかもしれません。 このような場合は、*create-eth-app*にある subgraph パッケージを活用できます。[ドキュメンテーション](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph)を参照して、あなたのニーズに合うように調整してください。 人気が高いスマートコントラクトの場合、すでに subgraph が含まれているかもしれません。 [subgraph explorer](https://thegraph.com/explorer/)をチェックしてみてください。 +そして、履歴データにも興味があるかもしれません。 最後の取引時間だけでなく、これまでに自分が行ったすべての取引の時間も知りたくなるでしょう。 _create-eth-app_のサブグラフパッケージを使用し、[ドキュメント](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph)を読んで、それを自分のコントラクトに適合させてください。 人気のあるスマートコントラクトを探している場合、すでにサブグラフが存在するかもしれません。 [サブグラフエクスプローラー](https://thegraph.com/explorer/)をチェックしてください。 -subgraph があれば、Dapp にシンプルなクエリをひとつ追加するだけで、過去のデータも含めた全てのブロックチェーンのデータを 1 回のフェッチ処理で取得することができます。 +サブグラフがあれば、dappで簡単なクエリを1つ書くだけで、必要な履歴データを含むすべての重要なブロックチェーンデータを取得でき、必要なフェッチは1回だけです。 ### Apollo {#apollo} -[Apollo Boost](https://www.apollographql.com/docs/react/get-started/)との統合により、React で作成した Dapp に The Graph を簡単に搭載できるようになりました。 特に[React hooks と Apollo](https://www.apollographql.com/blog/apollo-client-now-with-react-hooks)を使えば、コンポーネントに GraphQL のクエリをひとつ追加するだけで、簡単にデータ取得が可能になります: +[Apollo Boost](https://www.apollographql.com/docs/react/get-started/)の統合のおかげで、React dappにグラフを簡単に統合できます。 特に[ReactフックとApollo](https://www.apollographql.com/blog/apollo-client-now-with-react-hooks)を使用する場合、データのフェッチはコンポーネントに単一のGraphQlクエリを記述するのと同じくらい簡単です: ```js const { loading, error, data } = useQuery(myGraphQlQuery) @@ -82,32 +80,32 @@ React.useEffect(() => { ## テンプレート {#templates} -さらに、Dapp 用にさまざまなテンプレートが用意されています。 今のところ、Aave、Compound、UniSwap、および Sablier 用のテンプレートがあります。 これらのテンプレートはすべて、すでに subgraph と統合されており、スマートコントラクトのアドレスに対して重要なサービスを追加します。 `yarn create eth-app my-eth-app --with-template aave`などの作成コマンドを、テンプレートに追加するだけでよいです。 +さらに、いくつかの異なるテンプレートから選択できます。 これまでのところ、Aave、Compound、UniSwap、またはsablierの統合を使用できます。 それらはすべて、あらかじめ作成されたサブグラフ統合とともに、重要なサービススマートコントラクトのアドレスを追加します。 `yarn create eth-app my-eth-app --with-template aave`のように、作成コマンドにテンプレートを追加するだけです。 ### Aave {#aave} -[Aave](https://aave.com/)は、分散型の通貨レンディングプラットフォームです。 預金者は、市場に流動性を提供することで金利収入を獲得でき、借主は担保を提供して借り入れることができます。 Aave のユニークな特徴のひとつは、1 回のトランザクション内で返済する限り無担保で通貨を借入できる[フラッシュローン](https://docs.aave.com/developers/guides/flash-loans)という仕組みです。 この機能は、裁定取引で余分のキャッシュが必要になる場合などに有効でしょう。 +[Aave](https://aave.com/)は、分散型の金融市場です。 預金者は市場に流動性を提供して受動的収入を得る一方、借手は担保を使って借り入れることができます。 Aaveのユニークな特徴の1つは、[フラッシュローン](https://aave.com/docs/developers/flash-loans)です。これは、1つのトランザクション内でローンを返済する限り、担保なしでお金を借りることを可能にします。 これは、例えば裁定取引で追加の現金を得るのに役立ちます。 -利子が付与される取引中のトークンは、*aToken*と呼ばれます。 +利子を得るために取引されるトークンは、_aTokens_と呼ばれます。 -Aave と*create-eth-app*を統合すると、[Aave 用の subgraph](https://docs.aave.com/developers/getting-started/using-graphql)が使えるようになります。 Aave は The Graph を採用しており、[Ropsten](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten)および[メインネット](https://thegraph.com/explorer/subgraph/aave/protocol)上では、すぐに導入できる subgraph を[raw](https://thegraph.com/explorer/subgraph/aave/protocol-raw)または[formatted](https://thegraph.com/explorer/subgraph/aave/protocol)形式で提供しています。 +_create-eth-app_とAaveを統合することを選択すると、[サブグラフ統合](https://docs.aave.com/developers/getting-started/using-graphql)が得られます。 AaveはThe Graphを使用しており、[Ropsten](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten)と[メインネット](https://thegraph.com/explorer/subgraph/aave/protocol)上で、[raw](https://thegraph.com/explorer/subgraph/aave/protocol-raw)または[フォーマット済み](https://thegraph.com/explorer/subgraph/aave/protocol)形式で、すぐに使えるいくつかのサブグラフをすでに提供しています。 -![Aaveフラッシュローンのミーム - 「あ〜、フラッシュローンを1トランザクション以上に延長できれば、最高なんだけどなあ」](./flashloan-meme.png) +![Aaveフラッシュローンのミーム – 「ああ、もしフラッシュローンを1トランザクション以上保持できたら、最高なんだけどな」](./flashloan-meme.png) ### Compound {#compound} -[Compound](https://compound.finance/)は、Aave に類似したサービスです。 create-eth-app ではすでに、[Compound v2 の subgraph](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094195)が利用可能です。 驚くべきことに、Compound では利子が付与されるトークンを*cToken*と呼んでいます。 +[Compound](https://compound.finance/)はAaveに似ています。 この統合には、新しい[Compound v2 Subgraph](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094195)がすでに含まれています。 ここでの利息獲得トークンは、驚くべきことに_cTokens_と呼ばれています。 ### Uniswap {#uniswap} -[Uniswap](https://uniswap.exchange/)は、分散型取引所 (DEX) です。 流動性を供給するユーザーは、取引の売主/買主の両方に対して、必要なトークンや ether を提供して手数料を獲得することができます。 Uniswap は広く利用されているため、多種多様なトークンに最大規模の流動性を提供する取引所のひとつです。 あなたの Dapp に Uniswap を組み込むことで、ETH を DAI とスワップする機能などが簡単に実現できます。 +[Uniswap](https://uniswap.exchange/)は分散型取引所(DEX)です。 流動性供給者は、取引の両側に必要なトークンやEtherを提供することで手数料を得ることができます。 広く利用されているため、非常に広範囲のトークンに対して最も高い流動性の1つを持っています。 例えば、ユーザーがETHをDAIにスワップできるように、dappに簡単に統合できます。 -残念ながら、この記事の執筆時点で利用可能なのは Uniswap v1 のみとなっており、[最新リリースの v2](https://uniswap.org/blog/uniswap-v2/)は利用できません。 +残念ながら、この記事の執筆時点では、統合はUniswap v1のみで、[リリースされたばかりのv2](https://uniswap.org/blog/uniswap-v2/)には対応していません。 ### Sablier {#sablier} -[ Sablier](https://sablier.com/)は、ストリーミング決済を可能にする分散型アプリです。 Sablier をセットアップすれば、その後の管理を必要とせずに、1 回の支払日の代わりに常にお金を受け取ることができるようになります。 create-eth-app では、[独自の subgraph](https://thegraph.com/explorer/subgraph/sablierhq/sablier)が利用できます。 +[Sablier](https://sablier.com/)は、ユーザーがお金をストリーミングで支払うことを可能にします。 一度の給料日ではなく、初期設定後は追加の管理なしに継続的にお金を受け取ることができます。 この統合には、[独自のサブグラフ](https://thegraph.com/explorer/subgraph/sablierhq/sablier)が含まれています。 ## 次のステップ {#whats-next} -*create-eth-app*について質問がある場合は、[Sablier コミュニティーのサーバー](https://discord.gg/bsS8T47)にアクセスして、 *create-eth-app*の開発者に問い合わせることができます。 次のステップとしては、[Material UI](https://material-ui.com/)のような UI フレームワークの導入、あなたが実際に必要とするデータを取得するための GraphQL クエリの作成、およびデプロイのセットアップなどが考えられます。 +_create-eth-app_について質問がある場合は、[Sablierコミュニティサーバー](https://discord.gg/bsS8T47)にアクセスしてください。そこで_create-eth-app_の作成者と連絡を取ることができます。 最初の次のステップとして、[Material UI](https://mui.com/material-ui/)のようなUIフレームワークを統合し、実際に必要なデータのためにGraphQLクエリを書き、デプロイをセットアップすることをお勧めします。 diff --git a/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md b/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md index 0f0ab41e76d..6cade4dfe88 100644 --- a/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md +++ b/public/content/translations/ja/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md @@ -1,6 +1,6 @@ --- -title: create-eth-appでDappのフロントエンド開発をはじめましょう -description: create-eth-appの使い方と機能の概要 +title: "create-eth-appでDappのフロントエンド開発をはじめましょう" +description: "create-eth-appの使い方と機能の概要" author: "Markus Waas" tags: - "create-eth-app" diff --git a/public/content/translations/ja/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md b/public/content/translations/ja/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md index d2d4dc2fd2a..bbb909695df 100644 --- a/public/content/translations/ja/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md +++ b/public/content/translations/ja/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md @@ -1,11 +1,8 @@ --- -title: SQLでイーサリアムの基礎的なトピックについて学ぶ -description: このチュートリアルは、SQL(Structured Query Language)を使用してブロックチェーン上のデータに対するクエリを実行することで、トランザクション、ブロック、ガスといったイーサリアムの基本的な概念についての理解を深めるものです。 +title: "SQLでイーサリアムの基礎的なトピックについて学ぶ" +description: "このチュートリアルは、SQL(Structured Query Language)を使用してオンチェーンデータにクエリを実行することで、トランザクション、ブロック、ガスといったイーサリアムの基本的な概念についての理解を深めるものです。" author: "Paul Apivat" -tags: - - "SQL" - - "クエリ" - - "トランザクション" +tags: [ "SQL", "クエリ", "トランザクション" ] skill: beginner lang: ja published: 2021-05-11 @@ -13,27 +10,27 @@ source: paulapivat.com sourceUrl: https://paulapivat.com/post/query_ethereum/ --- -イーサリアムに関するチュートリアルの多くはデベロッパ向けのものですが、データアナリストや、クライアント/ノードを実行することなくオンチェーンのデータを確認したい人々を対象とする学習リソースは多くありません。 +イーサリアムに関するチュートリアルの多くはデベロッパー向けのものですが、データアナリストや、クライアント/ノードを実行することなくオンチェーンのデータを確認したい人々を対象とする学習リソースは多くありません。 -このチュートリアルは、[Dune Analytics](https://dune.xyz/home)が提供するインターフェースを用いて、オンチェーンのデータに対してSQL(Structured Query Language)のクエリを実行することで、トランザクション、ブロック、ガスといったイーサリアムの基本的なコンセプトについての理解を深めるものです。 +このチュートリアルは、[Dune Analytics](https://dune.com/)が提供するインターフェースを用いて、オンチェーンのデータに対して構造化問い合わせ言語(SQL)のクエリを実行することで、トランザクション、ブロック、ガスといったイーサリアムの基本的なコンセプトについての理解を深めるものです。 オンチェーンのデータは、イーサリアムやイーサリアム・ネットワークに関する理解を深めるのに役立つだけでなく、コンピュータ処理能力の経済学といった現在のイーサリアムが直面している課題(例:ガス代の上昇)や、より重要性が高いスケーリング・ソリューションに関する議論について、基本的な事項を理解する土台となるものです。 ### トランザクション {#transactions} -イーサリアムの新規ユーザーはまず、ETH残高を持つエンティティであるユーザー管理アカウントを初期化する必要があります。 イーサリアムのアカウントには、ユーザー管理アカウントとスマートコントラクトの2種類があります([ethereum.org](/developers/docs/accounts/)を参照)してください)。 +イーサリアムのユーザーの体験は、ユーザーが管理するアカウント、またはETH残高を持つエンティティを初期化することから始まります。 アカウントには、ユーザー管理アカウントとスマートコントラクトの2種類があります([ethereum.org](/developers/docs/accounts/)を参照)。 -すべてのアカウントは、[Etherscan](https://etherscan.io/)のようなブロックエクスプローラーで表示できます。 ブロックエクスプローラーは、イーサリアム上のデータポータルです。 このポータルから、ブロックのデータ、トランザクション、マイナー、アカウント、および他のオンチェーンのアクティビティをリアルタイムで確認できます([こちら](/developers/docs/data-and-analytics/block-explorers/)をご覧ください)。 +どのアカウントも、[Etherscan](https://etherscan.io/)や[Blockscout](https://eth.blockscout.com/)のようなブロックエクスプローラーで表示できます。 ブロックエクスプローラーは、イーサリアム上のデータポータルです。 このポータルから、ブロックのデータ、トランザクション、マイナー、アカウント、および他のオンチェーンのアクティビティをリアルタイムで確認できます([こちら](/developers/docs/data-and-analytics/block-explorers/)をご覧ください)。 -しかし、外部のブロックエスプローラーが提供する情報と直接照合したい場合は、オンチェーンのデータに対するクエリを実行したいと思うかもしれません。 [Dune Analytics](https://duneanalytics.com/)は、SQLに関する一定の知識を前提として、あらゆるユーザーにこのクエリ機能を提供します。 +しかし、外部のブロックエクスプローラーが提供する情報と直接照合したい場合は、オンチェーンのデータに対するクエリを実行したいと思うかもしれません。 [Dune Analytics](https://dune.com/)は、SQLに関する一定の知識を持つ人なら誰でもこの機能を利用できるようにします。 -参考までに、イーサリアム・ファウンデーション (EF) のスマートコントラクトアカウントは[Etherscan](https://etherscan.io/address/0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae)で表示することができます。 +参考までに、イーサリアム財団(EF)のスマートコントラクトアカウントは[Blockscout](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe)で閲覧できます。 -EFのアカウントを含むすべてのアカウントは、トランザクションの送受信に使用できる公開アドレスを持つ点に留意してください。 +注意すべき点として、EFのアカウントを含むすべてのアカウントは、トランザクションの送受信に使用できる公開アドレスを持つということが挙げられます。 -Etherscanのアカウント残高は、通常のトランザクションと内部トランザクションで構成されています。 内部トランザクションは誤解を招きやすい名前ですが、チェーンの状態を変更する _実際の_トランザクションではありません。 内部トランザクションとは、コントラクト([ソース](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions))を実行することで開始される「値の移転」を意味します。 内部トランザクションは署名を持たないためブロックチェーンには **含まれず**、Dune Analyticsでクエリを実行することができません。 +Etherscanのアカウント残高は、通常のトランザクションと内部トランザクションで構成されています。 内部トランザクションは、その名前とは裏腹に、チェーンの状態を変更する_実際の_トランザクションではありません。 これらは、コントラクトの実行によって開始される値の転送です([ソース](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions))。 内部トランザクションは署名を持たないためブロックチェーンには**含まれず**、Dune Analyticsでクエリを実行することができません。 -従って、このチュートリアルでは通常のトランザクションのみを取り上げます。 通常のトランザクションに対しては、以下のようにクエリを実行します: +したがって、このチュートリアルでは通常のトランザクションのみを取り上げます。 通常のトランザクションに対しては、以下のようにクエリを実行します。 ```sql WITH temp_table AS ( @@ -61,33 +58,33 @@ SELECT FROM temp_table ``` -これにより、Etherscanのトランザクションページで提供されるのと同一の情報が返されます。 これら2つのソースを比較してみましょう: +これにより、Etherscanのトランザクションページで提供されるのと同一の情報が返されます。 比較のために、2つのソースを以下に示します。 #### Etherscan {#etherscan} ![](./etherscan_view.png) -[Etherscan上のEFのコントラクトのページ](https://etherscan.io/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) +[Blockscout上のEFのコントラクトページ。](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) #### Dune Analytics {#dune-analytics} ![](./dune_view.png) -ダッシュボードは、[こちら](https://duneanalytics.com/paulapivat/Learn-Ethereum)からアクセスしてください。 テーブルをクリックすると、クエリを確認できます(上記も参照してください)。 +ダッシュボードは[こちら](https://dune.com/paulapivat/Learn-Ethereum)にあります。 テーブルをクリックすると、クエリを確認できます(上記も参照してください)。 -### トランザクションの内容を見る {#breaking_down_transactions} +### トランザクションの内訳 {#breaking_down_transactions} -送信されたトランザクションには、([ソース](/developers/docs/transactions/))を含むいくつかの情報が含まれています。 +送信されたトランザクションには、以下のようないくつかの情報が含まれています([ソース](/developers/docs/transactions/)): -- **Recipient**:受信者のアドレス(「to」のクエリに該当したアドレス)。 -- **Signature**:トランザクションに署名するのは送信者の秘密鍵ですが、SQLでクエリできるのは送信者の公開アドレス(「from」)です。 -- **Value**:送信されたETHの量 (`ether`列を参照してください)。 -- **Data**:ハッシュ化した任意のデータ(`data`列を参照してください)。 -- **gasLimit**:トランザクションで消費できるガスユニットの上限。 ガスユニットは、計算ステップを示します。 -- **maxPriorityFeePerGas**:マイナーへのチップとして提供できるガス量の上限。 -- **maxFeePerGas**:トランザクションに対して支払い可能であるガス代の上限(baseFeePerGasとmaxPriorityFeePerGasを含む) 。 +- **受信者**: 受信アドレス(クエリでは「to」) +- **署名**: 送信者の秘密鍵がトランザクションに署名しますが、SQLでクエリできるのは送信者の公開アドレス(「from」)です。 +- **値**: これは転送されたETHの量です(`ether`の列を参照)。 +- **データ**: ハッシュ化された任意のデータです(`data`列を参照) +- **ガスリミット** – トランザクションで消費できるガスユニットの最大量。 ガスユニットは、計算ステップを示します +- **maxPriorityFeePerGas** - マイナーへのチップとして含めることができるガスの最大量 +- **maxFeePerGas** - トランザクションに支払う意思のあるガスの最大額(baseFeePerGasとmaxPriorityFeePerGasを含む) -イーサリアムファウンデーションのパブリックアドレスへのトランザクションにつき、これらの具体的な情報をクエリしたい場合は以下を実行します: +イーサリアム財団の公開アドレスへのトランザクションについて、これらの具体的な情報を次のようにクエリできます: ```sql SELECT @@ -106,15 +103,15 @@ ORDER BY block_time DESC ### ブロック {#blocks} -各トランザクションは、イーサリアム仮想マシン([EVM](/developers/docs/evm/))の状態を変更します([ソース](/developers/docs/transactions/))。 トランザクションは、ネットワークにブロードキャストされ、検証を経てブロックに追加されます。 トランザクションごとに、ブロック番号が割り振られます。 データを見るには、特定のブロック番号でクエリすることができます。ブロック番号: 12396854は、執筆時点である2021年11月5日のイーサリアム・ファウンデーション内のトランザクションで最も最新のブロックです。 +各トランザクションは、イーサリアム仮想マシン([EVM](/developers/docs/evm/))の状態を変更します([ソース](/developers/docs/transactions/))。 トランザクションは、ネットワークにブロードキャストされ、検証を経てブロックに追加されます。 各トランザクションには、ブロック番号が関連付けられています。 データを見るには、特定のブロック番号でクエリすることができます。ブロック番号: 12396854は、執筆時点(2021/11/5)でイーサリアム財団のトランザクションの中で最も新しいブロックです。 さらに、次の2つのブロックに対してクエリを実行すると、各ブロックが1つ前のブロックのハッシュ(親ハッシュ)を含んでいることが確認でき、ブロックチェーンがどのように形成されるかを理解できます。 -各ブロックには、親ブロックに対する参照情報が含まれています。 これは、`hash`列と`parent_hash`列の間に表示されます([ソース](/developers/docs/blocks/))。 +各ブロックには、親ブロックへの参照が含まれています。 これは、以下の`hash`列と`parent_hash`列の間に示されます([ソース](/developers/docs/blocks/)): ![parent_hash](./parent_hash.png) -Dune Analyticsでは、[クエリ](https://duneanalytics.com/queries/44856/88292)は以下のように表示されます: +Dune Analyticsでの[クエリ](https://dune.com/queries/44856/88292)はこちらです: ```sql SELECT @@ -128,18 +125,18 @@ WHERE "number" = 12396854 OR "number" = 12396855 OR "number" = 12396856 LIMIT 10 ``` -ブロックを調べるには、時間、ブロック番号、難易度、ハッシュ、親ハッシュ、およびノンスに対してクエリを実行します。 +時間、ブロック番号、難易度、ハッシュ、親ハッシュ、およびノンスをクエリすることでブロックを調べることができます。 -このクエリでは、_トランザクションのリスト_だけは調べることができません。このためトランザクションのリストについては、_state root_を使って後述する別のクエリを実行します。 フルノードまたはアーカイブノードは、すべてのトランザクションと状態遷移を保存しますので、クライアントはいつでもチェーンの状態をクエリすることができます。 これには大容量のストレージが必要になりますので、チェーンデータと状態データを分離することができます: +このクエリがカバーしていないのは、_トランザクションのリスト_と_ステート・ルート_のみで、これらには以下の別のクエリが必要です。 フルノードまたはアーカイブノードは、すべてのトランザクションと状態遷移を保存しますので、クライアントはいつでもチェーンの状態をクエリすることができます。 これには大容量のストレージが必要になりますので、チェーンデータと状態データを分離することができます: - チェーンデータ(ブロックおよびトランザクションのリスト) - 状態データ(各トランザクションによる状態遷移の結果) -状態ルートは後者(状態データ)であり、_暗黙的な_データである(オンチェーンで保存されない)のに対し、チェーンデータは明示的なデータであり、チェーン自体に保存されます([ソース](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored))。 +ステート・ルートは後者に分類され_暗黙的_なデータ(オンチェーンで保存されない)ですが、チェーンデータは明示的であり、チェーン自体に保存されます([ソース](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored))。 -このチュートリアルでは、Dune Analyticsを使ってSQLで_クエリ可能_であるオンチェーンのデータを取り上げます。 +このチュートリアルでは、Dune Analyticsを使ってSQLでクエリ_できる_オンチェーンのデータに焦点を当てます。 -すでに述べたように、各ブロックにはトランザクションのリストが含まれているので、特定のブロックに絞り込んでクエリを実行できます。 さっそく、最新ブロック「12396854」を試してみましょう。 +すでに述べたように、各ブロックにはトランザクションのリストが含まれているので、特定のブロックに絞り込んでクエリを実行できます。 最新ブロック「12396854」を試してみましょう: ```sql SELECT * FROM ethereum."transactions" @@ -147,13 +144,13 @@ WHERE block_number = 12396854 ORDER BY block_time DESC` ``` -Duneでは、このようなSQL出力が得られます: +DuneでのSQL出力はこちらです: ![](./list_of_txn.png) -ブロックチェーンにこの1つのブロックが追加されると、イーサリアム仮想マシン ([EVM](/developers/docs/evm/))の状態が変化します。 ブロックチェーンでは、一度に数十、時には数百ものトランザクションが検証されます。 このブロックの場合、222件のトランザクションが含まれていました。 +この1つのブロックがチェーンに追加されると、イーサリアム仮想マシン([EVM](/developers/docs/evm/))の状態が変化します。 一度に数十、時には数百ものトランザクションが検証されます。 このブロックの場合、222件のトランザクションが含まれていました。 -実際にトランザクションが成功した件数を調べるには、成功したトランザクションのみを絞り込むフィルターを追加します: +実際に成功した件数を調べるには、成功したトランザクションをカウントするフィルターを追加します。 ```sql WITH temp_table AS ( @@ -166,26 +163,26 @@ SELECT FROM temp_table ``` -ブロック12396854では、計222件のトランザクションのうち、204件が正常に検証されました: +ブロック12396854では、計222件のトランザクションのうち、204件が正常に検証されました: ![](./successful_txn.png) -トランザクションリクエストは、毎秒あたり数十回発生しますが、ブロックがコミットされるのはおよそ15秒に1回です([ソース](/developers/docs/blocks/))。 +トランザクションリクエストは毎秒数十回発生しますが、ブロックがコミットされるのはおよそ15秒に1回です([ソース](/developers/docs/blocks/))。 -約15秒ごとに1つのブロックが生成されることを確認するには、1日に含まれる合計の秒数(86,400秒)を15で割ることで、1日に生成される平均ブロック数(およそ5,760)が分かります。 +約15秒ごとに1つのブロックが生成されることを確認するには、1日に含まれる合計の秒数(86400秒)を15で割ることで、1日に生成される平均ブロック数(およそ5760)が分かります。 -2016年から現在までに、イーサリアムで生成された1日あたりのブロック数については、この表を参照してください: +イーサリアムで1日あたりに生成されたブロック数(2016年〜現在)のグラフはこちらです: ![](./daily_blocks.png) -この期間に毎日生成されたブロックの平均数は、約5,874 です。 +この期間に毎日生成されたブロックの平均数は約5,874です: ![](./avg_daily_blocks.png) -クエリは、次のように行います: +クエリは、次のように行います。 ```sql -# query to visualize number of blocks produced daily since 2016 +# 2016年以降に毎日生成されたブロック数を可視化するクエリ SELECT DATE_TRUNC('day', time) AS dt, @@ -194,7 +191,7 @@ FROM ethereum."blocks" GROUP BY dt OFFSET 1 -# average number of blocks produced per day +# 1日あたりの平均ブロック生成数 WITH temp_table AS ( SELECT @@ -209,13 +206,13 @@ SELECT FROM temp_table ``` -2016年から現在までに1日に生成されたブロック数の平均は、5,874をわずかに上回っています。 反対に、86,400秒を平均ブロック数である5,874で割ると14.7となるため、およそ15秒に1回の頻度でブロックが生成されたことが分かります。 +2016年から現在までに1日に生成されたブロック数の平均は、この数字をわずかに上回る5,874です。 あるいは、86,400秒を平均ブロック数5,874で割ると14.7秒となり、およそ15秒に1つのブロックが生成されていることになります。 ### ガス {#gas} -ブロックのサイズは、制限されています。 ブロックの最大サイズは、ネットワーク需要に応じて12,500,000ユニットから25,000,000ユニットの間で動的に変化します。 ブロックのサイズを制限する理由は、フルノードに対してディスク容量や処理速度の要件([ソース](/developers/docs/blocks/))が過剰な負担となるのを防ぐために、無駄に大きなサイズのブロックが発生することを防ぐためです。 +ブロックのサイズは制限されています。 ブロックの最大サイズは動的で、ネットワーク需要に応じて12,500,000から25,000,000ユニットの間で変動します。 ブロックサイズが任意に大きくなることで、フルノードのディスクスペースや処理速度に負荷がかかることを防ぐため、制限が必要となります([ソース](/developers/docs/blocks/))。 -ブロックに対するガス上限という概念を理解するには、トランザクションをバッチ処理するために利用できるブロックのスペースをどれだけ**供給**できるか、と考えるとよいでしょう。 ブロックのガス上限に対してもクエリを実行できるので、2016年から現在までのグラフは以下のようになります: +ブロックのガスリミットを理解する一つの方法として、トランザクションをバッチ処理するために利用できるブロックスペースの**供給**量と考えることができます。 ブロックのガスリミットは、クエリを実行して2016年から現在までを可視化できます: ![](./avg_gas_limit.png) @@ -228,7 +225,7 @@ GROUP BY dt OFFSET 1 ``` -さらに、イーサリアム・ブロックチェーン上で実行された処理(トランザクションの送信、スマートコントラクトの呼び出し、NFTのミント)のために実際に支払われた1日あたりのガスを調べることもできます。 これは、利用可能なイーサリアムのブロックスペースに対する**需要**を示します: +そして、イーサリアムチェーン上での計算(トランザクションの送信、スマートコントラクトの呼び出し、NFTのミントなど)の支払いに毎日使用される実際のガスがあります。 これは、利用可能なイーサリアムのブロックスペースに対する**需要**です: ![](./daily_gas_used.png) @@ -241,17 +238,17 @@ GROUP BY dt OFFSET 1 ``` -これら2つのグラフを比較することで、 **需要と供給**の関係を確認することができます: +また、これら2つのグラフを並べて比較することで、**需要と供給**がどのように一致するかを確認できます。 ![gas_demand_supply](./gas_demand_supply.png) -ここから、ブロックスペースが十分に供給されている場合、ガス価格はブロックスペースへの需要に応じて上下することが分かります。 +したがって、利用可能な供給量を前提として、ガス価格はイーサリアムのブロックスペースへの需要の関数として理解できます。 -最後に、イーサリアムチェーンにおける1日のガス価格の平均値を調べたい場合、クエリ時間が非常に長くなるため、イーサリアム・ファウンデーションがトランザクション1件あたりに支払ったガス代の平均値を調べるようにクエリを絞り込みます。 +最後に、イーサリアムチェーンの1日あたりの平均ガス価格をクエリすることもできますが、クエリ時間が非常に長くなるため、イーサリアム財団によってトランザクションごとに支払われた平均ガス量にクエリを絞り込みます。 ![](./ef_daily_gas.png) -2016年から現在までに、イーサリアム・ファウンデーションのアドレスに対して実行されたすべてのトランザクションにおいて支払われたガス価格を確認することができます。 クエリは、以下のように実行します: +長年にわたるイーサリアム財団のアドレスへのすべてのトランザクションで支払われたガス価格を見ることができます。 クエリは、次のとおりです。 ```sql SELECT @@ -265,8 +262,8 @@ ORDER BY block_time DESC ### まとめ {#summary} -このチュートリアルでは、クエリを実行し、オンチェーンのデータを確認することで、イーサリアムの基本的な概念やイーサリアム・ブロックチェーンの仕組みについて学びました。 +このチュートリアルでは、オンチェーンのデータをクエリして理解することで、イーサリアムの基本的な概念とイーサリアムブロックチェーンの仕組みを理解します。 -このチュートリアルで使用したすべてのコードをまとめたダッシュボードは、[こちら](https://duneanalytics.com/paulapivat/Learn-Ethereum)からアクセスしてください。 +このチュートリアルで使用されたすべてのコードを含むダッシュボードは[こちら](https://dune.com/paulapivat/Learn-Ethereum)にあります。 -データを通じてWeb3の知識をさらに深めたい方は、[私をTwitterでフォローしてください](https://twitter.com/paulapivat)。 +データを使ってWeb3をさらに探求したい方は、[Twitterで私を見つけてください](https://twitter.com/paulapivat)。 diff --git a/public/content/translations/ja/developers/tutorials/logging-events-smart-contracts/index.md b/public/content/translations/ja/developers/tutorials/logging-events-smart-contracts/index.md index adb43255f91..d0cdc8fd9ef 100644 --- a/public/content/translations/ja/developers/tutorials/logging-events-smart-contracts/index.md +++ b/public/content/translations/ja/developers/tutorials/logging-events-smart-contracts/index.md @@ -1,12 +1,8 @@ --- -title: イベントを使用して、スマートコントラクトのデータをログに記録する -description: スマートコントラクトにおけるイベントを紹介し、データのログを取るためにイベントを使用する方法を学ぶ +title: "イベントを使用して、スマートコントラクトのデータをログに記録する" +description: "スマートコントラクトにおけるイベントを紹介し、データのログを取るためにイベントを使用する方法を学ぶ" author: "jdourlens" -tags: - - "スマートコントラクト" - - "Remix" - - "Solidity" - - "イベント" +tags: [ "スマート契約", "Remix", "Solidity", "イベント" ] skill: intermediate lang: ja published: 2020-04-03 @@ -15,7 +11,7 @@ sourceUrl: https://ethereumdev.io/logging-data-with-events/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -Solidityでは、スマートコントラクトがトリガーすることで送信される信号を[イベント](/developers/docs/smart-contracts/anatomy/#events-and-logs)と呼びます。 Dappだけでなく、イーサリアムのJSON-RPC APIに接続されたすべてのプログラムは、これらのイベントをリッスンし、それに応じて動作します。 イベントをインデックス化すれば、後でイベント履歴を参照することができます。 +Solidityでは、[イベント](/developers/docs/smart-contracts/anatomy/#events-and-logs)はスマートコントラクトが発行できるディスパッチされたシグナルです。 Dapps、またはイーサリアムのJSON-RPC APIに接続されたあらゆるものは、これらのイベントをリッスンして、それに応じて動作することができます。 イベントにインデックスを付けることで、後でイベント履歴を検索できるようになります。 ## イベント {#events} @@ -25,9 +21,9 @@ Solidityでは、スマートコントラクトがトリガーすることで送 event Transfer(address indexed from, address indexed to, uint256 value); ``` -イベントの署名はコントラクトのコード内で宣言され、emitキーワードと共に発行されます。 例えば、送信イベントでは、この送信における送信元(_from_)、送信先(_to_)、および送信したトークン量(_value_)のログが記録されます。 +イベントの署名はコントラクトのコード内で宣言され、emitキーワードと共に発行されます。 例えば、転送イベントでは、この転送における送信元(_from_)、送信先(_to_)、および送信したトークン量(_value_)のログが記録されます。 -Counterのスマートコントラクトに戻り、値が変更されるたびにログを取ることにしたと仮定しましょう。 このコントラクトは、デプロイを目的とせず、別のコントラクトを拡張する土台の役割を担うため、抽象コントラクトと呼びます。 カウンターのスマートコントラクトでは、以下のようになります: +Counterスマートコントラクトに戻り、値が変更されるたびにログを記録することにしたと仮定しましょう。 このコントラクトは、デプロイを目的とせず、別のコントラクトを拡張する土台の役割を担うため、抽象コントラクトと呼びます。 カウンターの例では、このようになります: ```solidity pragma solidity 0.5.17; @@ -36,16 +32,16 @@ contract Counter { event ValueChanged(uint oldValue, uint256 newValue); - // Private variable of type unsigned int to keep the number of counts + // カウント数を保持するための符号なし整数のプライベート変数 uint256 private count = 0; - // Function that increments our counter + // カウンターをインクリメントする関数 function increment() public { count += 1; emit ValueChanged(count - 1, count); } - // Getter to get the count value + // カウント値を取得するためのゲッター function getCount() public view returns (uint256) { return count; } @@ -53,11 +49,11 @@ contract Counter { } ``` -注意: +注意: -- **5行目**:イベントを宣言し、イベントに含まれる古い値と新しい値を宣言します。 +- **5行目**: イベントと、それに含まれる古い値および新しい値を宣言します。 -- **13行目**:カウントの変数が1増えるごとに、イベントが発行されます。 +- **13行目**: count変数をインクリメントするときに、イベントを発行します。 このコントラクトをデプロイしてインクリメント関数を呼び出し、ログという名前の配列にある新しいトランザクションをクリックすると、Remixが自動的にこのイベントを表示します。 diff --git a/public/content/translations/ja/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md b/public/content/translations/ja/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md index cfb8d93b96c..44e7e98471d 100644 --- a/public/content/translations/ja/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md +++ b/public/content/translations/ja/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md @@ -1,9 +1,8 @@ --- -title: オフラインデータの完全性のためのマークルプルーフ -description: オフチェーンに大部分が保存されているデータに対し、オンチェーンでのデータの完全性の確保 +title: "オフラインデータの完全性のためのマークルプルーフ" +description: "オフチェーンに大部分が保存されているデータに対し、オンチェーンでのデータの完全性の確保" author: Ori Pomerantz -tags: - - "ストレージ" +tags: [ "ストレージ" ] skill: advanced lang: ja published: 2021-12-30 @@ -13,29 +12,31 @@ published: 2021-12-30 すべてのデータは、イーサリアムストレージに保存することが理想的です。このストレージは、数千ものコンピューターに保存され、非常に高い可用性(データは検閲されない)と完全性(データは不正に変更されない)を備えていますが、32バイトワードを保存するのに通常2万ガスがかかります。 執筆時点で、そのコストは$6.60に相当します。 1バイトごとに21セントかかるため、多くの用途には高すぎます。 -この問題を解決するために、イーサリアムのエコシステムでは[データを保存する多くの分散型の方法](/developers/docs/storage/)が開発されました。 通常、これらは可用性と価格のトレードオフを伴います。 しかしながら、一般に完全性は保証されます。 +この問題を解決するために、イーサリアムのエコシステムはデータを分散型の +方法で保存するための多くの代替手段を開発しました。 通常、これらは可用性と価格のトレードオフを伴います。 しかしながら、一般に完全性は保証されます。 -この記事では、ブロックチェーンにデータを保存することなくデータ完全性を確保する**方法**として[マークルプルーフ](https://computersciencewiki.org/index.php/Merkle_proof)を使用する方法を学びます。 +この記事では、[マークルプルーフ](https://computersciencewiki.org/index.php/Merkle_proof)を使用して、 +ブロックチェーンにデータを保存せずにデータの完全性を確保する**方法**を学びます。 ## 仕組み {#how-does-it-work} -理論上、チェーン上にデータのハッシュだけを保存し、トランザクション内で必要なすべてのデータを送信することができます。 しかし、これでもまだ高すぎます。 1バイトのデータのトランザクションのコストは約16ガスで、現時点では0.5セントです。つまり、1キロバイトあたり約 $5になります。 1メガバイトでは $5000になり、データをハッシュ化するコストを差し引いても、多くの用途にはまだ高すぎます。 +理論上は、データのハッシュのみをオンチェーンに保存し、それを必要とするトランザクションですべてのデータを送信できます。 しかし、これでもまだ高すぎます。 1バイトのデータのトランザクションのコストは約16ガスで、現時点では0.5セントです。つまり、1キロバイトあたり約 $5になります。 1メガバイトでは $5000になり、データをハッシュ化するコストを差し引いても、多くの用途にはまだ高すぎます。 これを解決するには、異なるデータのサブセットを繰り返しハッシュ化します。そうすることで、データを送信する必要が無い場合は、ハッシュを送信するだけで済むようになります。 これを行うには、次のようなマークルツリーを使用します。このツリーは、それぞれのノードがその下のノードのハッシュとなるデータ構造を持ちます。 ![マークルツリー](tree.png) -チェーン上に保存する必要があるのは、ルートハッシュのみとなります。 特定の値を証明するには、ルートのハッシュを得るために、その値と結合させる必要があるハッシュをすべて提供します。 例えば、`C`を証明するには、`D`、`H(A-B)`、`H(E-H)`を提供します。 +オンチェーンに保存する必要があるのはルートハッシュのみです。 特定の値を証明するには、ルートのハッシュを得るために、その値と結合させる必要があるハッシュをすべて提供します。 例えば、`C`を証明するには、`D`、`H(A-B)`、`H(E-H)`を提供します。 ![Cの値の証明](proof-c.png) ## 実装 {#implementation} -[サンプルコードはこちらで入手できます](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity)。 +[サンプルコードはこちらで提供されています](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity)。 -### オフチェーンコード {#off-chain-code} +### オフチェーンコード {#offchain-code} -この記事では、オフチェーン計算にJavaScriptを使用します。 ほとんどの分散型アプリケーションには、JavaScriptのオフチェーンコンポーネントがあります。 +この記事では、オフチェーンの計算にJavaScriptを使用します。 ほとんどの分散型アプリケーションには、JavaScriptのオフチェーンコンポーネントがあります。 #### マークルルートの作成 {#creating-the-merkle-root} @@ -48,58 +49,60 @@ const ethers = require("ethers") [ethersパッケージのハッシュ関数を使用します](https://docs.ethers.io/v5/api/utils/hashing/#utils-keccak256)。 ```javascript -// The raw data whose integrity we have to verify. The first two bytes a -// are a user identifier, and the last two bytes the amount of tokens the -// user owns at present. +// 完全性を検証する必要がある生データです。最初の2バイトは +// ユーザー識別子、最後の2バイトはユーザーが +// 現在所有しているトークンの量です。 const dataArray = [ 0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060, 0xface0070, 0xbad00080, 0x060d0091, ] ``` -各エントリを単一の256ビット整数にエンコードすると、JSONを使用した場合などよりも読みにくいコードになります。 しかし、これによりコントラクト内のデータを取得するための処理量が大幅に削減され、ガス代も大幅に削減されます。 [チェーン上でJSONを読み取ることができますが](https://github.com/chrisdotn/jsmnSol)、回避できるのであれば避けるべきです。 +各エントリを単一の256ビット整数にエンコードすると、JSONを使用した場合などよりも読みにくいコードになります。 しかし、これによりコントラクト内のデータを取得するための処理量が大幅に削減され、ガス代も大幅に削減されます。 [オンチェーンでJSONを読み込むことはできますが](https://github.com/chrisdotn/jsmnSol)、回避できるのであれば良いアイデアではありません。 ```javascript -// The array of hash values, as BigInts +// BigIntとしてのハッシュ値の配列 const hashArray = dataArray ``` ここでは、256ビット値のデータで始めるので、処理は必要ありません。 文字列型のようなより複雑なデータ構造を使用する場合は、まずデータをハッシュ化してハッシュ配列を取得する必要があります。 これは、ユーザーが他のユーザーの情報を知っていても知らなくても構わないことを前提にしているためでもあります。 さもなければ、ユーザー1がユーザー0の値がわからないように、ユーザー2がユーザー3の値がわからないように、というようなハッシュ化をしなければならなくなります。 ```javascript -// Convert between the string the hash function expects and the -// BigInt we use everywhere else. +// ハッシュ関数が期待する文字列と +// 他の場所で使用するBigIntを相互に変換します。 const hash = (x) => BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0))) ``` -ethersのハッシュ関数は、`0x60A7`などの16進数のJavaScript文字列を受け取ることを想定しており、同じ構造の別の文字列で応答します。 ただし、コードの他の部分では、`BigInt`を使う方が簡単なため、16進数文字列に変換してから、もう一度`BigInt`に戻します。 +ethersのハッシュ関数は、`0x60A7`などの16進数のJavaScript文字列を受け取ることを想定しており、同じ構造の別の文字列で応答します。 ただし、コードの残りの部分では`BigInt`を使用する方が簡単なので、16進数の文字列に変換して、また元に戻します。 ```javascript -// ペアの対称ハッシュのため、順序が逆でも構いません。 +// 順序が逆でも気にしなくていいように、ペアを対称的にハッシュ化します。 const pairHash = (a, b) => hash(hash(a) ^ hash(b)) ``` -この関数は対称(a [xor](https://en.wikipedia.org/wiki/Exclusive_or) bのハッシュ)です。 そのため、マークルプルーフを確認するときに、計算された値の前と後のどちらにプルーフの値を配置すべきかについて考慮する必要はありません。 マークルプルーフの確認はオンチェーンで行われるので、必要な処理が少ないほど良いとされます。 +この関数は対称的です(a [xor](https://en.wikipedia.org/wiki/Exclusive_or) b のハッシュ)。 そのため、マークルプルーフを確認するときに、計算された値の前と後のどちらにプルーフの値を配置すべきかについて考慮する必要はありません。 マークルプルーフの確認はオンチェーンで行われるので、必要な処理が少ないほど良いとされます。 -警告: 暗号技術は見た目以上に難解です。 この記事の最初のバージョンでは、ハッシュ関数`hash(a^b)`を使用していました。 これは、**好ましくない**手法でした。`a`と`b`の正しい値を知っていれば、`b' = a^b^a'`を使用して目的の`a'`の値を証明できるからです。 この関数では、`hash(a') ^ hash(b')`が既知の値(ルートに向かう経路上の隣のブランチ)と等しくなるように`b'`を計算する必要があり、これは非常に困難です。 +警告: 暗号技術は見た目以上に難解です。 +この記事の初版では、ハッシュ関数`hash(a^b)`を使用していました。 +これは**悪い**アイデアでした。なぜなら、`a`と`b`の正当な値を知っていれば、`b' = a^b^a'`を使って任意の`a'`の値を証明できてしまうからです。 +この関数では、`hash(a') ^ hash(b')`が既知の値(ルートに向かう途中の次のブランチ)と等しくなるように`b'`を計算する必要があり、これははるかに困難です。 ```javascript -// The value to denote that a certain branch is empty, doesn't -// have a value +// あるブランチが空で、値を +// 持たないことを示すための値です。 const empty = 0n ``` 値の数が2の整数乗ではない時は、空のブランチを処理する必要があります。 このプログラムでは、ゼロをプレースホルダーとして配置する方法を使っています。 -![ブランチが欠けているマークルツリー](merkle-empty-hash.png) +![ブランチが欠落しているマークルツリー](merkle-empty-hash.png) ```javascript -// Calculate one level up the tree of a hash array by taking the hash of -// each pair in sequence +// 各ペアを順番にハッシュ化することで、ハッシュ配列のツリーを1レベル上に計算します。 const oneLevelUp = (inputArray) => { var result = [] - var inp = [...inputArray] // To avoid over writing the input // Add an empty value if necessary (we need all the leaves to be // paired) + var inp = [...inputArray] // 入力の上書きを避けるため // 必要であれば空の値を追加します (すべてのリーフが // ペアになるようにする必要があります) if (inp.length % 2 === 1) inp.push(empty) @@ -110,13 +113,13 @@ const oneLevelUp = (inputArray) => { } // oneLevelUp ``` -この関数は、現在のレイヤーで値のペアをハッシュ化すると、マークルツリーを1レベル「登り」ます。 これは効率性を追求した実装ではないことに留意してください。入力のコピーを避け、ループ内で適切なタイミングで単に`hashEmpty`を加えることもできますが、このコードは読みやすさを重視して最適化されています。 +この関数は、現在のレイヤーで値のペアをハッシュ化すると、マークルツリーを1レベル「登り」ます。 これは最も効率的な実装ではないことに注意してください。入力のコピーを避け、ループ内で適切なときに`hashEmpty`を追加することもできましたが、このコードは読みやすさを重視して最適化されています。 ```javascript const getMerkleRoot = (inputArray) => { var result - result = [...inputArray] // Climb up the tree until there is only one value, that is the // root. // // If a layer has an odd number of entries the // code in oneLevelUp adds an empty value, so if we have, for example, // 10 leaves we'll have 5 branches in the second layer, 3 // branches in the third, 2 in the fourth and the root is the fifth + result = [...inputArray] // 値が1つだけになるまでツリーを登ります。それが // ルートです。 // // レイヤーのエントリ数が奇数の場合、 // oneLevelUpのコードが空の値を追加するため、例えば、 // 10個のリーフがあれば、第2レイヤーには5個のブランチ、 // 第3レイヤーには3個のブランチ、第4レイヤーには2個、そしてルートが5番目になります。 while (result.length > 1) result = oneLevelUp(result) @@ -131,30 +134,30 @@ const getMerkleRoot = (inputArray) => { マークルプルーフは、マークルルートを得るために、証明される値と一緒にハッシュ化する値です。 証明する値は、他のデータから入手することが多いため、コードの一部としてではなく、別に提供することをお勧めします。 ```javascript -// A merkle proof consists of the value of the list of entries to -// hash with. Because we use a symmetrical hash function, we don't -// need the item's location to verify the proof, only to create it +// マークルプルーフは、一緒にハッシュ化するエントリのリストの値で +// 構成されます。対称的なハッシュ関数を使用しているため、プルーフの検証に +// アイテムの位置は不要で、作成時にのみ必要です。 const getMerkleProof = (inputArray, n) => {     var result = [], currentLayer = [...inputArray], currentN = n -    // Until we reach the top +    // トップに到達するまで     while (currentLayer.length > 1) { -        // No odd length layers +        // レイヤーの長さが奇数にならないようにする         if (currentLayer.length % 2)             currentLayer.push(empty)         result.push(currentN % 2 -               // If currentN is odd, add with the value before it to the proof +               // currentNが奇数の場合、その前の値をプルーフに追加します             ? currentLayer[currentN-1] -               // If it is even, add the value after it +               // 偶数の場合、その後の値を追加します             : currentLayer[currentN+1]) ``` -ハッシュ化は、`(v[0],v[1])`、`(v[2],v[3])`のように行っていきます。 したがって、偶数の値の場合はその次の値、基数の値の場合は1つ前の値が必要です。 +`(v[0],v[1])`、`(v[2],v[3])`などをハッシュ化します。 したがって、偶数の値の場合はその次の値、基数の値の場合は1つ前の値が必要です。 ```javascript -        // Move to the next layer up +        // 上のレイヤーに移動します         currentN = Math.floor(currentN/2)         currentLayer = oneLevelUp(currentLayer)     }   // while currentLayer.length > 1 @@ -163,9 +166,9 @@ const getMerkleProof = (inputArray, n) => { }   // getMerkleProof ``` -### オンチェーンコード {#on-chain-code} +### オンチェーンコード {#onchain-code} -最後は、証明を確認するコードです。 オンチェーンコードは、[Solidity](https://docs.soliditylang.org/en/v0.8.11/)で書かれています。 ガス代が比較的高価なため、ここでは最適化がかなり重要になります。 +最後は、証明を確認するコードです。 オンチェーンコードは[Solidity](https://docs.soliditylang.org/en/v0.8.11/)で記述されています。 ガス代が比較的高価なため、ここでは最適化がかなり重要になります。 ```solidity //SPDX-License-Identifier: Public Domain @@ -174,7 +177,7 @@ pragma solidity ^0.8.0; import "hardhat/console.sol"; ``` -コードの作成には、[Hardhat開発環境](https://hardhat.org/)を使用しました。この環境では、開発している間も[Solidityからコンソール出力](https://hardhat.org/docs/cookbook/debug-logs)を得ることができます。 +これは[Hardhat開発環境](https://hardhat.org/)を使用して作成しました。これにより、開発中に[Solidityからのコンソール出力](https://hardhat.org/docs/cookbook/debug-logs)が可能になります。 ```solidity @@ -185,15 +188,15 @@ contract MerkleProof {       return merkleRoot;     } -    // Extremely insecure, in production code access to -    // this function MUST BE strictly limited, probably to an -    // owner +    // 非常に安全性が低い。本番コードでは、この関数への +    // アクセスを、おそらくオーナーに +    // 限定するなど、厳しく制限する必要があります。     function setRoot(uint _merkleRoot) external {       merkleRoot = _merkleRoot;     }   // setRoot ``` -マークルルート用のset関数とget関数が書かれています。 プロダクションシステムにおいて、誰でもマークルルートを更新できるようにすることは、_非常に好ましくない手法_です。 ここでは、サンプルコードをシンプルにするために、あえて行っています。 **データの完全性が重要なシステムでは、行わないでください**。 +マークルルート用のset関数とget関数が書かれています。 本番システムで誰でもマークルルートを更新できるようにすることは、_極めて悪いアイデア_です。 ここでは、サンプルコードをシンプルにするために、あえて行っています。 **データの完全性が実際に重要となるシステムでは、これを行わないでください**。 ```solidity     function hash(uint _a) internal pure returns(uint) { @@ -205,12 +208,12 @@ contract MerkleProof {     } ``` -この関数は、ペアのハッシュを生成します。 JavaScripコードの`hash`と`pairHash`をSolidityコードに変換したものです。 +この関数は、ペアのハッシュを生成します。 これは、`hash`と`pairHash`のJavaScriptコードをSolidityに変換したものです。 -**注:** これも読みやすさを重視して最適化されています。 [関数定義](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm)によると、データを[`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays)の値として保存することで、変換を回避できる場合があります。 +\*\*注:\*\*これも読みやすさを優先して最適化された例です。 [関数の定義](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm)に基づけば、データを[`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays)値として保存し、変換を回避できる可能性があります。 ```solidity -    // Verify a Merkle proof +    // マークルプルーフを検証します     function verifyProof(uint _value, uint[] calldata _proof)         public view returns (bool) {       uint temp = _value; @@ -226,16 +229,18 @@ contract MerkleProof { }  // MarkleProof ``` -数学的表記では、マークルプルーフの検証は次のようになります。 `H(proof_n, H(proof_n-1, H(proof_n-2, .. H(proof_1, H(proof_0, value))...)))`. これをこのコードで実装しています。 +数学的記法では、マークルプルーフの検証は次のようになります: `H(proof_n, H(proof_n-1, H(proof_n-2, ...` `H(proof_1, H(proof_0, value))...)))`。 これをこのコードで実装しています。 -## マークルプルーフとロールアップを混在させない {#merkle-proofs-and-rollups} +## マークルプルーフとロールアップの相性 {#merkle-proofs-and-rollups} -マークルプルーフは、[ロールアップ](/developers/docs/scaling/#rollups)では、うまく機能しません。 ロールアップでは、すべてのトランザクションデータはL1(レイヤー1)に書き込まれ、処理はL2(レイヤー2)で行われるためです。 マークルプルーフをトランザクションで送信するのにかかるコストは、1レイヤーあたり平均638ガスです(現在のコールデータ1バイトは、ゼロでなければ16ガス、ゼロであれば4ガスかかります)。 1024ワードのデータがある場合、マークルプルーフには10レイヤー、つまり合計で6380ガスが必要になります。 +マークルプルーフは[ロールアップ](/developers/docs/scaling/#rollups)とうまく機能しません。 ロールアップでは、すべてのトランザクションデータはL1(レイヤー1)に書き込まれ、処理はL2(レイヤー2)で行われるためです。 マークルプルーフをトランザクションで送信するのにかかるコストは、1レイヤーあたり平均638ガスです(現在のコールデータ1バイトは、ゼロでなければ16ガス、ゼロであれば4ガスかかります)。 1024ワードのデータがある場合、マークルプルーフには10レイヤー、つまり合計で6380ガスが必要になります。 -[Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m)の例を見ると、L1への書き込みには約100gweiのガス代がかかり、L2では0.001gweiのガス代がかかります(これは通常の価格であり、混雑とともに上昇する可能性があります) 。 したがって、L1の1回分のガス代で、L2では10万ガスを処理に使えることになります。 ストレージを上書きしないと仮定すると、L1の1回のガス代でL2のストレージに約5ワード書き込めるということになります。 単一のマークルプルーフの場合、1024ワードすべてをストレージに書き込むことができ(トランザクションで提供されるのではなく、最初からチェーン上で計算できると仮定した場合)、依然としてほとんどのガスが残ります。 +例えば[Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m)を見ると、L1への書き込みのガス代は約100 gwei、L2のガス代は0.001 gweiです(これは通常の価格で、混雑状況によって上昇する可能性があります)。 したがって、L1の1回分のガス代で、L2では10万ガスを処理に使えることになります。 ストレージを上書きしないと仮定すると、L1の1回のガス代でL2のストレージに約5ワード書き込めるということになります。 単一のマークルプルーフの場合、(トランザクションで提供されるのではなく、最初からオンチェーンで計算できると仮定して)1024ワードすべてをストレージに書き込んでも、まだガスのほとんどが残ります。 -## まとめ {#conclusion} +## 結論 {#conclusion} 実際に、マークルツリーを自身で実装することはないかもしれません。 よく知られている監査済みのライブラリを使用できるため、基本的には独自の暗号論的プリミティブを実装しないことをお勧めします。 しかし、マークルプルーフをよく理解し、使いどころを判断できるようになっていただければと思います。 -マークルプルーフは、_完全性_を保持しますが、_可用性_は保持しないことに注意してください。 データストレージにアクセスを拒否され、データストレージにアクセスするためのマークルツリーを構築することもできない場合でも、誰も自分の資産を奪うことはできないということを知っていると、ささやかな慰めとなります。 この特性により、マークルツリーは、IPFSなどの分散型ストレージで最もよく使用されています。 +マークルプルーフは_完全性_を維持しますが、_可用性_は維持しないことに注意してください。 データストレージにアクセスを拒否され、データストレージにアクセスするためのマークルツリーを構築することもできない場合でも、誰も自分の資産を奪うことはできないということを知っていると、ささやかな慰めとなります。 この特性により、マークルツリーは、IPFSなどの分散型ストレージで最もよく使用されています。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md b/public/content/translations/ja/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md index b279859cd9e..3e1a62a595b 100644 --- a/public/content/translations/ja/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md +++ b/public/content/translations/ja/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md @@ -1,41 +1,40 @@ --- -title: InfluxDBとGrafanaを使って、Gethを監視する -description: +title: "InfluxDBとGrafanaを使ったGethの監視" +description: "InfluxDBとGrafanaを使用してGethノードの監視を設定し、パフォーマンスを追跡して問題を特定します。" author: "Mario Havel" -tags: - - "クライアント" - - "ノード" +tags: [ "クライアント", "ノード" ] skill: intermediate lang: ja published: 2021-01-13 --- -このチュートリアルでは、Gethノードのモニタリングを設定する方法について説明します。これにより、モニタリングのパフォーマンスについて理解を深め、どのような問題が発生しうるかを理解することができます。 +このチュートリアルでは、Gethノードの監視を設定することで、パフォーマンスをよりよく理解し、潜在的な問題を特定する方法を説明します。 -## 事前に必要な環境 {#prerequisites} +## 前提条件 {#prerequisites} -- Gethのインスタンスを実行していること。 -- 大部分の作業ステップ/具体例はLinuxを用いていますので、基本的なターミナルの知識が必要でしょう。 -- Gethにおける一連のモニタリング指標については、以下の動画を参考にしてください:[イーサリアムのインフラをモニタリングする(Péter Szilágyi)](https://www.youtube.com/watch?v=cOBab8IJMYI)。 +- Gethのインスタンスがすでに実行されている必要があります。 +- 手順と例のほとんどはLinux環境向けであり、ターミナルの基本的な知識があると役立ちます。 +- Gethの一連のメトリクスの概要については、こちらの動画をご覧ください: [Péter Szilágyi氏によるイーサリアムインフラストラクチャの監視](https://www.youtube.com/watch?v=cOBab8IJMYI)。 -## モニタリング用のスタック {#monitoring-stack} +## 監視スタック {#monitoring-stack} -イーサリアムのクライアントは、時系列データベースの形式で読み取り可能な多くのデータを収集します。 このデータをデータ可視化ソフトウェアにフィードすることで、モニタリング作業を容易にすることができます。 利用できるデータ可視化ソフトウェアには、以下があります: +イーサリアムクライアントは、時系列データベースの形式で読み取り可能な多くのデータを収集します。 監視を容易にするため、このデータをデータ可視化ソフトウェアに入力することができます。 利用可能なオプションは複数あります: -- [Prometheus](https://prometheus.io/) (プル型) -- [InfluxDB](https://www.influxdata.com/get-influxdb/)(プッシュ型) +- [Prometheus](https://prometheus.io/) (プルモデル) +- [InfluxDB](https://www.influxdata.com/get-influxdb/) (プッシュモデル) - [Telegraf](https://www.influxdata.com/get-influxdb/) - [Grafana](https://www.grafana.com/) - [Datadog](https://www.datadoghq.com/) - [Chronograf](https://www.influxdata.com/time-series-platform/chronograf/) -さらに、[Geth Prometheus Exporter](https://github.com/hunterlong/gethexporter)がありますが、これはInfluxDBおよびGrafana上ですでに設定済みのオプションです。 +また、InfluxDBとGrafanaで事前設定されたオプションである[Geth Prometheus Exporter](https://github.com/hunterlong/gethexporter)もあります。 -このチュートリアルでは、InfluxDBにデータをプッシュするように Gethクライアントを設定し、さらに、Grafanaがこのデータをグラフ化するように設定します。 この設定を手動で行うことで、設定プロセスについての理解を深めることができ、設定を変更したり、異なる環境でデプロイする方法を学ぶことができます。 +このチュートリアルでは、GethクライアントがInfluxDBにデータをプッシュしてデータベースを作成し、Grafanaがそのデータをグラフで可視化するように設定します。 手動で行うことで、プロセスをよりよく理解し、変更を加え、さまざまな環境にデプロイできるようになります。 -## InfluxDBを設定する {#setting-up-influxdb} +## InfluxDBの設定 {#setting-up-influxdb} -まず、InfluxDBをダウンロードしてインストールします。 [Influxdataのリリースページ](https://portal.influxdata.com/downloads/)では、さまざまなダウンロードのオプションが提供されています。 あなたの環境に合わせて選択してください。 また、[リポジトリ](https://repos.influxdata.com/)からインストールすることもできます。 例えば、Debianベースのディストリビューションの場合、以下のように実行します: +まず、InfluxDBをダウンロードしてインストールします。 さまざまなダウンロードオプションは[Influxdataのリリースぺージ](https://portal.influxdata.com/downloads/)で確認できます。 あなたの環境に合わせて選択してください。 +[リポジトリ](https://repos.influxdata.com/)からインストールすることもできます。 例えば、Debianベースのディストリビューションの場合は、以下のようになります: ``` curl -tlsv1.3 --proto =https -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add @@ -48,26 +47,27 @@ sudo systemctl start influxdb sudo apt install influxdb-client ``` -InfluxDB を正常にインストールしたら、バックグラウンドで実行されていることを確認してください。 デフォルトでは、`localhost:8086`からアクセス可能です。 `influx`クライアントを使用する前に、管理者権限を持つ新規ユーザーを作成する必要があります。 管理者ユーザーは、データベースおよびユーザーを作成し、高レベルの管理を行うユーザーです。 +InfluxDB を正常にインストールしたら、バックグラウンドで実行されていることを確認してください。 デフォルトでは、`localhost:8086`でアクセスできます。 +`influx`クライアントを使用する前に、管理者権限を持つ新しいユーザーを作成する必要があります。 このユーザーは、高度な管理、データベースの作成、ユーザーの作成に使用します。 ``` curl -XPOST "http://localhost:8086/query" --data-urlencode "q=CREATE USER username WITH PASSWORD 'password' WITH ALL PRIVILEGES" ``` -管理者ユーザーを作成すると、Influxクライアントから、[InfluxDBシェル](https://docs.influxdata.com/influxdb/v1.8/tools/shell/)にアクセスできるようになります。 +これで、このユーザーで`influx`クライアントを使用して[InfluxDBシェル](https://docs.influxdata.com/influxdb/v1.8/tools/shell/)に入ることができます。 ``` influx -username 'username' -password 'password' ``` -このシェルから InfluxDBと直接やりとりを行うことで、データベースとユーザーを作成し、Gethのモニタリング指標を取得することができます。 +シェル内でInfluxDBと直接通信することで、gethメトリクス用のデータベースとユーザーを作成できます。 ``` create database geth create user geth with password choosepassword ``` -作成したデータベース/ユーザーを、以下で確認します: +作成したエントリを以下で確認します: ``` show databases @@ -80,28 +80,30 @@ InfluxDBシェルを終了します。 exit ``` -InfluxDBはバックグラウンドで実行しており、Gethから送信される数値を保存するように設定されています。 +InfluxDBは実行中で、Gethからのメトリクスを保存するように設定されています。 -## Geth側の設定 {#preparing-geth} +## Gethの準備 {#preparing-geth} -データベースを設定したら、Geth上でのデータ収集を有効化する必要があります。 geth-helpの`geth --help`の`METRICS AND STATS OPTIONS`を確認してください。 複数のオプションが提供されていますが、ここでは、Gethが InfluxDBにデータをプッシュするように設定する必要があります。 基本的な設定では、InfluxDBがリーチ可能なエンドポイントと、当該データベースに対する認証について設定します。 +データベースを設定したら、Gethでのメトリクス収集を有効にする必要があります。 `geth --help`の`METRICS AND STATS OPTIONS`に注意してください。 そこには複数のオプションがありますが、このケースではGethがInfluxDBにデータをプッシュするようにします。 +基本設定では、InfluxDBがアクセスできるエンドポイントと、データベースの認証を指定します。 ``` geth --metrics --metrics.influxdb --metrics.influxdb.endpoint "http://0.0.0.0:8086" --metrics.influxdb.username "geth" --metrics.influxdb.password "chosenpassword" ``` -このフラグは、クライアントを起動するコマンドに追加するか、設定ファイル上で保存することが可能です。 +これらのフラグは、クライアントを起動するコマンドに追加するか、設定ファイルに保存できます。 -データベースに含まれる数値をリストアップするなどの方法で、Gethが実際にデータをプッシュしているかどうかを確認できます。 InfluxDBのシェルで、以下を入力してください: +例えば、データベース内のメトリクスを一覧表示することで、Gethが正常にデータをプッシュしていることを確認できます。 InfluxDBのシェルで、以下を入力してください: ``` use geth show measurements ``` -## Grafanaを設定する {#setting-up-grafana} +## Grafanaの設定 {#setting-up-grafana} -次に、データをグラフィック表示するためにGrafanaをインストールします。 Grafanaのドキュメンテーションを参照して、お使いの環境におけるインストール作業を実行してください。 特別な理由がない限り、OSSバージョンをインストールしてください。 レポジトリを利用してDebianのディストリビューションをインストールするステップは、以下の通りです: +次に、データをグラフィック表示するためにGrafanaをインストールします。 Grafanaのドキュメントで、お使いの環境に合わせたインストールプロセスに従ってください。 特別な理由がない限り、OSSバージョンをインストールしてください。 +リポジトリを使用したDebianディストリビューションへのインストール手順の例: ``` curl -tlsv1.3 --proto =https -sL https://packages.grafana.com/gpg.key | sudo apt-key add - @@ -112,36 +114,38 @@ sudo systemctl enable grafana-server sudo systemctl start grafana-server ``` -Grafanaが実行されている場合、`localhost:3000`でアクセスできるはずです。 お好みのブラウザからこのパスにアクセスして、デフォルトの認証情報(ユーザー:`admin`、パスワード:`admin`)でログインします。 プロンプトが表示されたら、デフォルトのパスワードを変更して保存してください。 +Grafanaが実行されたら、`localhost:3000`でアクセスできるはずです。 +お好みのブラウザでこのパスにアクセスし、デフォルトの認証情報(ユーザー: `admin`、パスワード: `admin`)でログインしてください。 プロンプトが表示されたら、デフォルトのパスワードを変更して保存してください。 ![](./grafana1.png) -Grafanaのホームページに転送されます。 まず、ソースデータを設定します。 左のバーにあるConfigurationアイコンをクリックし、「Data sources」を選択します。 +Grafanaのホームページに転送されます。 まず、ソースデータを設定します。 左のバーにある設定アイコンをクリックし、「Data sources」を選択します。 ![](./grafana2.png) -データソースを作成していないので、「Add data source」をクリックしてデータソースを定義します。 +まだデータソースは作成されていません。「Add data source」をクリックして定義します。 ![](./grafana3.png) -今回の設定では、「InfluxDB」を選択して、次に進みます。 +この設定では、「InfluxDB」を選択して続行します。 ![](./grafana4.png) -同一のマシンでツールを実行している場合、データソースの設定は非常に簡単です。 データベースにアクセスするには、InfluxDBのアドレスと詳細を設定する必要があります。 以下の画像を参照してください。 +同じマシンでツールを実行している場合、データソースの設定は非常に簡単です。 データベースにアクセスするには、InfluxDBのアドレスと詳細を設定する必要があります。 以下の画像を参照してください。 ![](./grafana5.png) -設定が完了し、InfluxDBがアクセス可能になったら、「Save and test」をクリックして、確認のポップアップ画面が表示されるまで待ってください。 +すべてが完了し、InfluxDBにアクセスできるようになったら、「Save and test」をクリックし、確認のポップアップが表示されるのを待ちます。 ![](./grafana6.png) -Grafanaの設定が完了し、InfluxDBのデータを読み込めるようになりました。 次に、データを分析して表示するダッシュボードを作成します。 ダッシュボードの属性はJSONファイルでエンコードされますので、誰でも簡単に作成し、インポートすることが可能です。 左のバーで、「Create and Import」をクリックしてください。 +GrafanaがInfluxDBからデータを読み取るように設定されました。 次に、データを解釈して表示するダッシュボードを作成する必要があります。 ダッシュボードのプロパティはJSONファイルにエンコードされており、誰でも簡単に作成してインポートできます。 左のバーで、「Create and Import」をクリックしてください。 ![](./grafana7.png) -Gethのモニタリング用ダッシュボードの場合、[このダッシュボード](https://grafana.com/grafana/dashboards/13877/)のIDをコピーして、Grafanaの「Import page」にペーストしてください。 ダッシュボードを保存すると、以下のような状態になっているはずです: +Gethのモニタリングダッシュボードには、[このダッシュボード](https://grafana.com/grafana/dashboards/13877/)のIDをコピーして、Grafanaの「Import page」に貼り付けてください。 ダッシュボードを保存すると、次のようになります: ![](./grafana8.png) -ダッシュボードの表示は変更可能です。 各パネルは、編集、移動、削除、追加が可能です。 各自の好みに合わせて、ダッシュボードの設定を変更してください。 あなた次第です! ダッシュボードの詳細な仕組みについては、 [Grafanaのドキュメンテーション](https://grafana.com/docs/grafana/latest/dashboards/)を参照してください。 また、[アラート機能](https://grafana.com/docs/grafana/latest/alerting/)も参照するとよいでしょう。 これは、各指標において一定の値に達した場合、アラート通知を受け取るように設定するものです。 アラート通知は、様々な通信チャネルに対応しています。 +ダッシュボードは変更できます。 各パネルは、編集、移動、削除、追加が可能です。 設定は変更できます。 あなた次第です! ダッシュボードの仕組みについて詳しくは、[Grafanaのドキュメント](https://grafana.com/docs/grafana/latest/dashboards/)を参照してください。 +[アラート](https://grafana.com/docs/grafana/latest/alerting/)にも興味があるかもしれません。 これにより、メトリクスが特定の値に達したときにアラート通知を設定できます。 さまざまな通信チャネルがサポートされています。 diff --git a/public/content/translations/ja/developers/tutorials/nft-minter/index.md b/public/content/translations/ja/developers/tutorials/nft-minter/index.md index b54dc60b215..b3440b662ed 100644 --- a/public/content/translations/ja/developers/tutorials/nft-minter/index.md +++ b/public/content/translations/ja/developers/tutorials/nft-minter/index.md @@ -1,14 +1,16 @@ --- -title: 非代替性トークン(NFT)ミンターチュートリアル -description: このチュートリアルでは、非代替性トークン(NFT)ミンターを構築します。さらに、スマートコントラクトをMetaMaskやWeb3ツールを使用して、Reactフロントエンドへ接続することでフルスタック分散型アプリケーション(Dapp)を作成する方法を学びます。 +title: "非代替性トークン(NFT)ミンターチュートリアル" +description: "このチュートリアルでは、非代替性トークン(NFT)ミンターを構築します。さらに、スマートコントラクトをMetaMaskやWeb3ツールを使用して、Reactフロントエンドへ接続することでフルスタック分散型アプリケーション(Dapp)を作成する方法を学びます。" author: "smudgil" tags: - - "Solidity" - - "NFT" - - "alchemy" - - "スマートコントラクト" - - "フロントエンド" - - "Pinata" + [ + "Solidity", + "NFT", + "Alchemy", + "スマート契約", + "フロントエンド", + "Pinata" + ] skill: intermediate lang: ja published: 2021-10-06 @@ -22,63 +24,63 @@ Web2のバックグラウンドを持つデベロッパーの最大の課題の1 - フロントエンドからスマートコントラクトメソッドを呼び出す - MetaMaskを使用してトランザクションに署名する -このチュートリアルでは、[React](https://reactjs.org/)をフロントエンドフレームワークとして使用します。 このチュートリアルはWeb3開発に焦点を当てているので、Reactの基礎についての説明に多くの時間を費やせません。 代わりに、プロジェクトの機能性を高めることに注力します。 +このチュートリアルでは、[React](https://react.dev/)をフロントエンドフレームワークとして使用します。 このチュートリアルはWeb3開発に焦点を当てているので、Reactの基礎についての説明に多くの時間を費やせません。 代わりに、プロジェクトの機能性を高めることに注力します。 -前提条件として、Reactに関する初級レベルの知識を有している必要があります。つまり、コンポーネント、プロパティ(props)、useStateおよびuseEffect、基本関数の呼び出しなどの仕組みを理解している必要があります。 これらの中に初めて耳にする用語がある場合は、[Reactの入門チュートリアル](https://reactjs.org/tutorial/tutorial.html)をご覧ください。 より視覚的な学習を好む方には、Net Ninjaによる素晴らしい[フルモダンReactチュートリアル](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d)のビデオシリーズをお勧めします。 +前提条件として、Reactに関する初級レベルの知識を有している必要があります。つまり、コンポーネント、プロパティ(props)、useStateおよびuseEffect、基本関数の呼び出しなどの仕組みを理解している必要があります。 これらの用語をこれまで聞いたことがない場合は、こちらの[React入門チュートリアル](https://react.dev/learn/tutorial-tic-tac-toe)をご覧ください。 視覚的な学習を好む方には、Net Ninjaによるこの素晴らしい[フルモダンReactチュートリアル](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d)のビデオシリーズを強くお勧めします。 -まだAlchemyアカウントをお持ちでない場合、このチュートリアルを完了したり、ブロックチェーンで何かを構築したりするために必ず必要になりますので、 [こちらから](https://alchemy.com/)無料アカウントに登録してください。 +まだAlchemyアカウントをお持ちでない場合、このチュートリアルを完了したり、ブロックチェーンで何かを構築したりするために必ず必要になりますので、 [こちら](https://alchemy.com/)から無料アカウントにサインアップしてください。 それでは、さっそく始めましょう! -## 非代替性トークン(NFT)作成入門 {#making-nfts-101} +## NFT作成の基礎 {#making-nfts-101} コードを見始める前に、非代替性トークン(NFT)作成の仕組みを理解することが重要です。 それには、次の2つのステップがあります。 -### イーサリアムブロックチェーン上で非代替性トークン(NFT)スマートコントラクトを公開 {#publish-nft} +### Ethereumブロックチェーン上にNFTスマートコントラクトを公開する {#publish-nft} ERC-1155とERC-721の2つのスマートコントラクト規格の最大の違いは、ERC-1155はマルチトークン規格でありバッチ機能を備えているのに対し、ERC-721はシングルトークン規格であり一度に1つのトークンの送信しかサポートしていないことです。 -### ミント関数の呼び出し {#minting-function} +### ミント関数を呼び出す {#minting-function} -通常、このミント関数は、パラメータとして2つの変数を渡す必要があります。1つ目は、新しくミントされた非代替性トークン(NFT)を受け取るアドレスを指定する`recipient`です。2つ目は、非代替性トークン(NFT)のメタデータを記述するJSONドキュメントに解決される文字列である非代替性トークン(NFT)の`tokenURI`です。 +通常、このミント関数では、パラメータとして2つの変数を渡す必要があります。1つ目は、新しくミントされたNFTを受け取るアドレスを指定する`recipient`で、2つ目は、NFTのメタデータを記述するJSONドキュメントに解決される文字列であるNFTの`tokenURI`です。 -非代替性トークン(NFT)のメタデータは、非代替性トークン(NFT)に名前、説明、画像(または別のデジタル資産)、その他の属性などのプロパティを持たせ、非代替性トークン(NFT)を利用できるようにします。 非代替性トークン(NFT)のメタデータが含まれている[tokenURIの例](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2)をご覧ください。 +非代替性トークン(NFT)のメタデータは、非代替性トークン(NFT)に名前、説明、画像(または別のデジタル資産)、その他の属性などのプロパティを持たせ、非代替性トークン(NFT)を利用できるようにします。 NFTのメタデータを含む[tokenURIの例](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2)はこちらです。 このチュートリアルでは、React UIを使用して既存の非代替性トークン(NFT)のスマートコントラクトのミント関数を呼び出すパート2(後半)の方に焦点を当てています。 -このチュートリアルで呼び出すERC-721非代替性トークン(NFT)スマートコントラクトへのリンクは、[こちら](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)です。 この作成方法について知りたい場合は、[非代替性トークン(NFT)の作り方](https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft)という別のチュートリアルを確認することを強くお勧めします。 +このチュートリアルで呼び出すERC-721 NFTスマートコントラクトへの[リンクはこちら](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)です。 その作成方法を学びたい場合は、もう一つのチュートリアル["NFTの作成方法"](https://www.alchemy.com/docs/how-to-create-an-nft)をご覧になることを強くお勧めします。 非代替性トークン(NFT)作成の仕組みを理解したところで、スターターファイルをクローンしましょう。 -## スターターファイルのクローン {#clone-the-starter-files} +## スターターファイルをクローンする {#clone-the-starter-files} -最初に、[非代替性トークン(NFT)ミンターチュートリアル(nft-minter-tutorial)のGitHubリポジトリ](https://github.com/alchemyplatform/nft-minter-tutorial)にアクセスし、このプロジェクトのスターターファイルを取得します。 リポジトリをローカル環境にクローンします。 +まず、[nft-minter-tutorialのGitHubリポジトリ](https://github.com/alchemyplatform/nft-minter-tutorial)にアクセスして、このプロジェクトのスターターファイルを入手します。 このリポジトリをローカル環境にクローンします。 -クローンされた`nft-minter-tutorial`リポジトリを開くと、`minter-starter-files`と`nft-minter`という2つのフォルダが含まれています。 +このクローンされた`nft-minter-tutorial`リポジトリを開くと、`minter-starter-files`と`nft-minter`という2つのフォルダが含まれていることがわかります。 -- `minter-starter-files`には、このプロジェクトのスターターファイル(基本的にはReact UI)が含まれています。 このチュートリアルでは、イーサリアムウォレットと非代替性トークン(NFT)スマートコントラクトに接続することで、このUIを利用できるようにする方法を学ぶ際に、**こちらのディレクトリで作業します**。 -- `nft-minter`には、完成したチュートリアル全体が含まれており、**困ったときに****リファレンス**として利用できます。 +- `minter-starter-files`には、このプロジェクトのスターターファイル(本質的にはReactのUI)が含まれています。 このチュートリアルでは、EthereumウォレットとNFTスマートコントラクトに接続してこのUIを有効にする方法を学ぶため、**このディレクトリで作業します**。 +- `nft-minter`には完成したチュートリアル全体が含まれており、行き詰まった場合に**参照**として利用できます。 次に、コードエディタで`minter-starter-files`のコピーを開き、`src`フォルダに移動します。 -これから作成するすべてのコードは、`src`フォルダに保存されます。 後ほど`Minter.js`コンポーネントを編集し、追加のjavascriptファイルを書くことで、このプロジェクトにWeb3機能を追加します。 +私たちが書くコードはすべて`src`フォルダの下に置かれます。 `Minter.js`コンポーネントを編集し、追加のjavascriptファイルを記述して、プロジェクトにWeb3機能を与えます。 -## ステップ2: スターターファイルの確認 {#step-2-check-out-our-starter-files} +## ステップ2: スターターファイルを確認する {#step-2-check-out-our-starter-files} コーディングを始める前に、スターターファイルで既に提供されるものを確認することが重要です。 -### Reactプロジェクトの実行 {#get-your-react-project-running} +### Reactプロジェクトを実行する {#get-your-react-project-running} まずは、ブラウザでReactプロジェクトを実行しましょう。 Reactの素晴らしいところは、一度ブラウザでプロジェクトを実行すると、保存した変更がブラウザでも同時に更新されることです。 -プロジェクトを実行するには、次のようにターミナルで`minter-starter-files`フォルダのルートディレクトリに移動し、`npm install`を実行してプロジェクトの依存関係をインストールします。 +プロジェクトを実行するには、`minter-starter-files`フォルダのルートディレクトリに移動し、ターミナルで`npm install`を実行してプロジェクトの依存関係をインストールします: ```bash cd minter-starter-files npm install ``` -インストールが完了したら、ターミナルで`npm start`を実行します。 +インストールが完了したら、ターミナルで`npm start`を実行します: ```bash npm start @@ -86,18 +88,18 @@ npm start これにより、ブラウザでhttp://localhost:3000/が開き、プロジェクトのフロントエンドが表示されます。 フロントエンドは3つのフィールドで構成されており、それぞれ、非代替性トークン(NFT)資産へのリンク、非代替性トークン(NFT)の名前、非代替性トークン(NFT)の説明を入力する場所になっています。 -「Connect Wallet」や「Mint NFT」ボタンをクリックしても、動作しません。これらの機能は、これからプログラムする必要があります。 :\) +「Connect Wallet」や「Mint NFT」ボタンをクリックしても、動作しません。これらの機能は、これからプログラムする必要があります。 :​) ### Minter.jsコンポーネント {#minter-js} -**注:** `minter-starter-files`フォルダにいることを確認してください。`nft-minter`フォルダではないことを確認します。 +**注:** `nft-minter`フォルダではなく、`minter-starter-files`フォルダにいることを確認してください。 -エディタの`src`フォルダに戻り、`Minter.js`ファイルを開きましょう。 このファイルには、これから作業を進めていく主要なReactコンポーネントが含まれています。すべての内容を理解することが非常に重要です。 +エディタで`src`フォルダに戻り、`Minter.js`ファイルを開きましょう。 このファイルには、これから作業を進めていく主要なReactコンポーネントが含まれています。すべての内容を理解することが非常に重要です。 このファイルの上部には、特定のイベントの後に更新される状態変数(State Variable)があります。 ```javascript -//State variables +//状態変数 const [walletAddress, setWallet] = useState("") const [status, setStatus] = useState("") const [name, setName] = useState("") @@ -105,135 +107,135 @@ const [description, setDescription] = useState("") const [url, setURL] = useState("") ``` -Reactの状態変数や状態フック(State Hook)を聞いたことがない場合は、 [こちらの](https://reactjs.org/docs/hooks-state.html)ドキュメントをご覧ください。 +Reactの状態変数や状態フック(State Hook)を聞いたことがない場合は、 [こちらの](https://legacy.reactjs.org/docs/hooks-state.html)ドキュメントをご覧ください。 それぞれの変数は以下を示します。 - `walletAddress` - ユーザーのウォレットアドレスを格納する文字列 - `status` - UIの下部に表示するメッセージを含む文字列 -- `name` - 非代替性トークン(NFT)の名前を格納する文字列 -- `description` - 非代替性トークン(NFT)の説明を格納する文字列 -- `url` - 非代替性トークン(NFT)のデジタル資産へのリンクを含んだ文字列 +- `name` - NFTの名前を格納する文字列 +- `description` - NFTの説明を格納する文字列 +- `url` - NFTのデジタル資産へのリンクである文字列 -状態変数(State Variable)の後に、`useEffect`、`connectWalletPressed`、`onMintPressed`という3つの未実装の関数があります。 これらの関数は、すべて`async`になっています。これは、それぞれの関数で非同期API呼び出しを行うためです。 それぞれの関数の名前は、その機能を示しています。 +状態変数の後には、`useEffect`、`connectWalletPressed`、`onMintPressed`という3つの未実装の関数があります。 これらの関数はすべて`async`であることにお気づきでしょう。これは、これらの関数内で非同期API呼び出しを行うためです。 それぞれの関数の名前は、その機能を示しています。 ```javascript useEffect(async () => { - //TODO: implement + //TODO: 実装 }, []) const connectWalletPressed = async () => { - //TODO: implement + //TODO: 実装 } const onMintPressed = async () => { - //TODO: implement + //TODO: 実装 } ``` -- [`useEffect`](https://reactjs.org/docs/hooks-effect.html) - コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列`[]`のpropが渡される(3行目を参照)ため、コンポーネントの_最初_のレンダリングでのみ呼び出されます。 ここでは、ウォレットリスナーと別のウォレット関数を呼び出し、ウォレットが接続されているかどうかに応じたUIの更新をします。 -- `connectWalletPressed` - この関数は、ユーザーのMataMaskウォレットを分散型アプリケーション(Dapp)に接続するために呼び出されます。 -- `onMintPressed` - この関数は、ユーザーの非代替性トークン(NFT)をミントするために呼び出されます。 +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) - コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列`[]`のpropが渡されるため(3行目を参照)、コンポーネントの_最初の_レンダリングでのみ呼び出されます。 ここでは、ウォレットリスナーと別のウォレット関数を呼び出し、ウォレットが接続されているかどうかに応じたUIの更新をします。 +- `connectWalletPressed` - この関数は、ユーザーのMetaMaskウォレットをdappに接続するために呼び出されます。 +- `onMintPressed` - この関数は、ユーザーのNFTをミントするために呼び出されます。 -このファイルの終盤には、コンポーネントのUIがあります。 このコードを注意深く読んでいくと、状態変数の`url`、`name`、`description`に対応するテキストフィールドの入力が変更された場合、これらの変数を更新していることが分かります。 +このファイルの終盤には、コンポーネントのUIがあります。 このコードを注意深く見ると、対応するテキストフィールドの入力が変更されたときに、`url`、`name`、`description`の状態変数が更新されることがわかります。 -さらに、`walletButton`または`mintButton`というIDを持つボタンがクリックされると、それぞれ`connectWalletPressed`または`onMintPressed`が呼び出されることも分かります。 +また、`mintButton`と`walletButton`のIDを持つボタンがクリックされると、それぞれ`connectWalletPressed`と`onMintPressed`が呼び出されることもわかります。 ```javascript -//the UI of our component +//コンポーネントのUI return (


-

🧙‍♂️ Alchemy NFT Minter

+

🧙‍♂️ Alchemy NFTミンター

- Simply add your asset's link, name, and description, then press "Mint." + アセットのリンク、名前、説明を追加し、「ミント」を押すだけです。

-

🖼 Link to asset:

+

🖼 アセットへのリンク:

setURL(event.target.value)} /> -

🤔 Name:

+

🤔 名前:

setName(event.target.value)} /> -

✍️ Description:

+

✍️ 説明:

setDescription(event.target.value)} />

{status}

-
+ ) ``` 最後に、このミンター(Minter)コンポーネントがどこに加えられるかについて説明します。 -他のすべてのコンポーネントのコンテナとして機能する、Reactのメインコンポーネントである`App.js`ファイルを表示すると、ミンター(Minter)コンポーネントが7行目に挿入されていることが分かります。 +Reactのメインコンポーネントであり、他のすべてのコンポーネントのコンテナとして機能する`App.js`ファイルを見ると、7行目にMinterコンポーネントが挿入されていることがわかります。 -**このチュートリアルでは、`Minter.js`ファイルの編集と、`src`フォルダへのファイルの追加のみを行います。** +**このチュートリアルでは、`Minter.js`ファイルの編集と`src`フォルダへのファイルの追加のみを行います。** これから取り組む内容を理解したところで、イーサリアムウォレットを設定しましょう。 -## イーサリアムウォレットの設定 {#set-up-your-ethereum-wallet} +## Ethereumウォレットを設定する {#set-up-your-ethereum-wallet} ユーザーがスマートコントラクトとやり取りできるようにするには、自分のイーサリアムウォレットを分散型アプリケーション(Dapp)に接続する必要があります。 -### MetaMaskをダウンロード {#download-metamask} +### MetaMaskをダウンロードする {#download-metamask} -このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 イーサリアムのトランザクションの仕組みの詳細については、[こちらのページ](/developers/docs/transactions/)をご覧ください。 +このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 Ethereumでのトランザクションの仕組みについて詳しく知りたい場合は、[こちらのページ](/developers/docs/transactions/)をご覧ください。 -Metamaskのアカウントは[こちら](https://metamask.io/download)から無料でダウンロード、作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は、(実際に支払いが発生しないように)右上の「Ropsten Test Network」に切り替えてください。 +MetaMaskアカウントは、[こちら](https://metamask.io/download)から無料でダウンロードして作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は、(実際に支払いが発生しないように)右上の「Ropsten Test Network」に切り替えてください。 -### フォーセットからイーサ(ETH)を追加 {#add-ether-from-faucet} +### フォーセットからEtherを追加する {#add-ether-from-faucet} -非代替性トークン(NFT)をミントする(または、イーサリアムのブロックチェーンのトランザクションに署名する)には、偽のETHが必要です。 ETHを取得するには、[Ropstenフォーセット](https://faucet.ropsten.be/)にアクセスして、Ropstenアカウントアドレスを入力し、「Send Ropsten ETH」をクリックします。 MetamaskアカウントにETHが表示されるはずです。 +非代替性トークン(NFT)をミントする(または、イーサリアムのブロックチェーンのトランザクションに署名する)には、偽のETHが必要です。 Ethを取得するには、[Ropstenフォーセット](https://faucet.ropsten.be/)にアクセスしてRopstenのアカウントアドレスを入力し、「Send Ropsten Eth」をクリックします。 MetamaskアカウントにETHが表示されるはずです。 -### 残高の確認 {#check-your-balance} +### 残高を確認する {#check-your-balance} -残高を再確認するために、[Alchemyのコンポーザーツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用して[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)をリクエストしてみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 +残高を確認するために、[Alchemyのcomposerツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用して[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)リクエストを行いましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 ```text {"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} ``` -**注:** この結果の単位は、ETHではなくweiです。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 10¹⁸ weiになります。 つまり、0xde0b6b3a7640000を10進数に変換すると、1\*10¹⁸となり、1 ETHに相当します。 +**注:** この結果はethではなくwei単位です。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 10¹⁸ weiになります。 つまり、0xde0b6b3a7640000を10進数に変換すると、1\*10¹⁸となり、1 ETHに相当します。 -ふう! これで、偽のお金を手に入れました。 +ご安心ください。 これで、偽のお金を手に入れました。 -## MetaMaskをUIに接続 {#connect-metamask-to-your-UI} +## MetaMaskをUIに接続する {#connect-metamask-to-your-UI} MetaMaskウォレットが設定されたので、分散型アプリケーション(Dapp)を接続しましょう。 -[モデルビューコントローラ(MVC)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)パラダイムを実践したいので、別のファイルを作成し、分散型アプリケーション(Dapp)のロジック、データ、ルールを管理する関数を含めます。次に、それらの関数をフロントエンド(Minter.jsコンポーネント)に渡します。 +[MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)パラダイムに従うため、dappのロジック、データ、ルールを管理する関数を含む別のファイルを作成し、それらの関数をフロントエンド(Minter.jsコンポーネント)に渡します。 ### `connectWallet`関数 {#connect-wallet-function} -これを行うには、`src`ディレクトリに`utils`という新しいフォルダを作成して、そこに`interact.js`というファイルを追加します。このファイルには、ウォレットとスマートコントラクトがやり取りする関数がすべて含まれます。 +そのためには、`src`ディレクトリに`utils`という新しいフォルダを作成し、その中に`interact.js`というファイルを追加します。このファイルには、ウォレットとスマートコントラクトのすべての対話関数が含まれます。 -`interact.js`ファイルに`connectWallet`関数を記述し、この関数を`Minter.js`コンポーネントにインポートして呼び出します。 +`interact.js`ファイルに`connectWallet`関数を記述し、それを`Minter.js`コンポーネントでインポートして呼び出します。 -`interact.js`ファイルに以下を追加します。 +`interact.js`ファイルに以下を追加します ```javascript export const connectWallet = async () => { @@ -243,7 +245,7 @@ export const connectWallet = async () => { method: "eth_requestAccounts", }) const obj = { - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 上のテキストフィールドにメッセージを書いてください。", address: addressArray[0], } return obj @@ -261,8 +263,7 @@ export const connectWallet = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

@@ -274,26 +275,26 @@ export const connectWallet = async () => { このコードが何をしているのか見てみましょう。 -まず、ブラウザで`window.ethereum`が有効になっているかどうかを関数がチェックしています。 +まず、この関数はブラウザで`window.ethereum`が有効になっているかどうかをチェックします。 -`window.ethereum`は、MetaMaskおよび他のウォレットプロバイダーによって挿入されるグローバルAPIであり、ウェブサイトがユーザーのイーサリアムアカウントを要求できるようにするものです。 承認されると、ユーザーが接続しているブロックチェーンからデータを読み取ったり、メッセージやトランザクションへの署名をユーザーに提案したりできるようになります。 詳細については、[MetaMaskのドキュメント](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)を参照してください。 +`window.ethereum`は、MetaMaskやその他のウォレットプロバイダーによって挿入されるグローバルAPIで、WebサイトがユーザーのEthereumアカウントを要求できるようにするものです。 承認されると、ユーザーが接続しているブロックチェーンからデータを読み取ったり、メッセージやトランザクションへの署名をユーザーに提案したりできるようになります。 詳細については[MetaMaskのドキュメント](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)をご覧ください。 -`window.ethereum`が_存在しない_場合は、MeTaMaskがインストールされていないことを意味します。 その結果、空の文字列に設定された、返される`address`と、ユーザーがMetaMaskをインストールする必要があることを伝える`status`JSXオブジェクトが入ったJSONオブジェクトが返されます。 +`window.ethereum`が_存在しない_場合、それはMetaMaskがインストールされていないことを意味します。 これにより、返される`address`が空の文字列で、`status` JSXオブジェクトがユーザーにMetaMaskをインストールするよう促すJSONオブジェクトが返されます。 -**これから記述するほとんどの関数は、状態変数(State Variable)とUIの更新に使用できるJSONオブジェクトを返します。** +**私たちが書く関数のほとんどは、状態変数とUIを更新するために使用できるJSONオブジェクトを返します。** -`window.ethereum`が_存在_する場合、興味深いことが起こります。 +さて、`window.ethereum`が_存在する_場合、ここからが面白くなります。 -try/catchループを使用して、`[window.ethereum.request({ method: "eth_requestAccounts" });](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)`を呼び出すことで、MetaMaskへの接続を試みます。 この関数を呼び出すと、ブラウザでMetaMaskが開き、ユーザーはウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 +try/catchループを使用して、[`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)を呼び出してMetaMaskへの接続を試みます。 この関数を呼び出すと、ブラウザでMetaMaskが開き、ユーザーはウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 -- ユーザーが接続を選んだ場合、`method: "eth_requestAccounts"`は、分散型アプリケーション(Dapp)に接続されているすべてのユーザーのアカウントアドレスを含む配列を返します。 `connectWallet`関数は、配列内の_最初の_`address`と\(9 行目参照\)、ユーザーにスマートコントラクトにメッセージを書き込むように促す`status`メッセージが入ったJSONオブジェクトを返します。 -- ユーザーが接続を拒否した場合、JSONオブジェクトには、返される`address`に入る空の文字列と、ユーザーが接続を拒否したことを示す`status`メッセージが入ることになります。 +- ユーザーが接続を選択した場合、`method: "eth_requestAccounts"`は、dappに接続されているすべてのユーザーのアカウントアドレスを含む配列を返します。 まとめると、`connectWallet`関数は、この配列の_最初の_`address`(9行目参照)と、ユーザーにスマートコントラクトへのメッセージを書き込むよう促す`status`メッセージを含むJSONオブジェクトを返します。 +- ユーザーが接続を拒否した場合、JSONオブジェクトには返される`address`の空文字列と、ユーザーが接続を拒否したことを示す`status`メッセージが含まれます。 -### Minter.js UIコンポーネントにconnectWallet関数を追加 {#add-connect-wallet} +### connectWallet関数をMinter.js UIコンポーネントに追加する {#add-connect-wallet} -`connectWallet`関数を記述したので、 `Minter.js`コンポーネントに接続しましょう。 +この`connectWallet`関数を書いたので、`Minter.js`コンポーネントに接続しましょう。 -まず、`Minter.js`ファイルの上部に`import { connectWallet } from "./utils/interact.js";`を追加して、`Minter.js`ファイルに関数をインポートする必要があります。 `Minter.js`の最初の11行は、次のようになります。 +まず、`Minter.js`ファイルの先頭に`import { connectWallet } from "./utils/interact.js";`を追加して、この関数を`Minter.js`ファイルにインポートする必要があります。 `Minter.js`の最初の11行は次のようになります。 ```javascript import { useEffect, useState } from "react"; @@ -301,7 +302,7 @@ import { connectWallet } from "./utils/interact.js"; const Minter = (props) => { - //State variables + //状態変数 const [walletAddress, setWallet] = useState(""); const [status, setStatus] = useState(""); const [name, setName] = useState(""); @@ -309,7 +310,7 @@ const Minter = (props) => { const [url, setURL] = useState(""); ``` -次に、`connectWalletPressed`関数の中で、インポートされた`connectWallet`関数を、以下のように呼び出します。 +次に、`connectWalletPressed`関数の中で、インポートした`connectWallet`関数を次のように呼び出します。 ```javascript const connectWalletPressed = async () => { @@ -321,9 +322,9 @@ const connectWalletPressed = async () => { `interact.js`ファイルによって、機能の大部分が`Minter.js`コンポーネントからどのように抽象化されているかに注目してください。 これは、モデルビューコントローラ(M-V-C)パラダイムに準拠しているためです。 -`connectWalletPressed`では、単にインポートされた`connectWallet`関数のawait呼び出しを行っています。さらに、そのレスポンスを使用し、`status`と`walletAddress`変数を状態フックを介して更新しています。 +`connectWalletPressed`では、インポートした`connectWallet`関数をawaitで呼び出し、そのレスポンスを使って状態フックを介して`status`と`walletAddress`変数を更新します。 -それでは、 `Minter.js`と `interact.js`の両方のファイルを保存して、これまでのUIをテストしてみましょう。 +それでは、`Minter.js`と`interact.js`の両方のファイルを保存して、これまでのUIをテストしてみましょう。 localhost:3000でブラウザを開き、ページ右上にある「Connect Wallet」ボタンを押します。 @@ -331,9 +332,9 @@ MetaMaskがインストールされている場合は、ウォレットを分散 ウォレットボタンに、接続した自分のアドレスが表示されているはずです。 -次に、ページを更新してみてください。変ですね。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。 +次に、ページを再読み込みしてみてください... これは奇妙です。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。 -でも心配しないでください。 `getCurrentWalletConnected`という関数を実装することで、簡単にこれを修正できます。この関数は、アドレスが分散型アプリケーション(Dapp)にすでに接続されているかどうかを確認し、それに応じてUIを更新します。 +でも心配しないでください。 `getCurrentWalletConnected`という関数を実装することで、これを簡単に修正できます。この関数は、アドレスがすでにdappに接続されているかどうかを確認し、それに応じてUIを更新します。 ### getCurrentWalletConnected関数 {#get-current-wallet} @@ -349,12 +350,12 @@ export const getCurrentWalletConnected = async () => { if (addressArray.length > 0) { return { address: addressArray[0], - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 上のテキストフィールドにメッセージを書いてください。", } } else { return { address: "", - status: "🦊 Connect to MetaMask using the top right button.", + status: "🦊 右上のボタンを使ってMetaMaskに接続してください。", } } } catch (err) { @@ -371,8 +372,7 @@ export const getCurrentWalletConnected = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

@@ -382,19 +382,19 @@ export const getCurrentWalletConnected = async () => { } ``` -このコードは、_非常に_前述の`connectWallet`関数に似ています。 +このコードは、先ほど書いた`connectWallet`関数と_非常によく似ています_。 -主な違いとしては、ユーザーがウォレットに接続するためにMetaMaskを開く`eth_requestAccounts`メソッドを呼び出す代わりに、 ここでは`eth_accounts`メソッドを呼び出しています。これは、現在、分散型アプリケーション(Dapp)に接続されているMetaMaskのアドレスを含む配列を単に返すだけです。 +主な違いは、ユーザーがウォレットを接続するためにMetaMaskを開く`eth_requestAccounts`メソッドを呼び出す代わりに、ここでは`eth_accounts`メソッドを呼び出している点です。これは、現在dappに接続されているMetaMaskのアドレスを含む配列を単に返すだけです。 この関数を動作させるため、`Minter.js`コンポーネントの`useEffect`関数で呼び出しましょう。 -`connectWallet`で行ったのと同様に、この関数を`interact.js`ファイルから `Minter.js`ファイルへ次のようにインポートする必要があります。 +`connectWallet`で行ったのと同様に、この関数を`interact.js`ファイルから`Minter.js`ファイルへ次のようにインポートする必要があります。 ```javascript import { useEffect, useState } from "react" import { connectWallet, - getCurrentWalletConnected, //import here + getCurrentWalletConnected, //ここでインポート } from "./utils/interact.js" ``` @@ -408,11 +408,11 @@ useEffect(async () => { }, []) ``` -`walletAddress`状態変数と`status`状態変数を更新するのに、呼び出した`getCurrentWalletConnected`のレスポンスを使用していることに注目してください。 +`walletAddress`と`status`の状態変数を更新するのに、`getCurrentWalletConnected`の呼び出しのレスポンスを使用していることに注目してください。 このコードを追加したら、ブラウザウィンドウを更新してみてください。 リフレッシュ後も、ボタンには接続されていることが示されており、接続されたウォレットのアドレスのプレビューが表示されているはずです。 -### addWalletListenerの実装 {#implement-add-wallet-listener} +### addWalletListenerを実装する {#implement-add-wallet-listener} 分散型アプリケーション(Dapp)ウォレットの設定の最終ステップは、ウォレットリスナーを実装することです。これにより、ユーザーが接続を切断したり、アカウントを切り替えたりした場合など、ウォレットの状態が変更されたときにUIが更新されます。 @@ -424,10 +424,10 @@ function addWalletListener() { window.ethereum.on("accountsChanged", (accounts) => { if (accounts.length > 0) { setWallet(accounts[0]) - setStatus("👆🏽 Write a message in the text-field above.") + setStatus("👆🏽 上のテキストフィールドにメッセージを書いてください。") } else { setWallet("") - setStatus("🦊 Connect to MetaMask using the top right button.") + setStatus("🦊 右上のボタンを使ってMetaMaskに接続してください。") } }) } else { @@ -435,7 +435,7 @@ function addWalletListener() {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

) @@ -445,9 +445,9 @@ function addWalletListener() { ここで何が起きているか、簡単に見ていきましょう。 -- まず、ブラウザで`window.ethereum`が有効になっているか\(すなわち MetaMaskがインストールされているか\)を関数がチェックしています。 - - 有効になっていない場合、ユーザーにMetaMaskのインストールを求めるJSX文字列を`status`状態変数に設定します。 - - 有効になっている場合、MetaMaskウォレットの状態変更をリッスンしている3行目の`window.ethereum.on("accountsChanged")`リスナーを設定します。この状態変更には、ユーザーが追加のアカウントを分散型アプリケーション(Dapp)に接続した場合、アカウントを切り替えた場合、アカウントを切断した場合が含まれます。 少なくとも1つのアカウントが接続されていれば、`accounts`配列の最初のアカウントがリスナーから返されたときに、`walletAddress`状態変数が更新されます。 それ以外の場合は、`walletAddress`に空の文字列が設定されます。 +- まず、この関数は`window.ethereum`が有効になっているか(つまり、MetaMaskがインストールされているか)をチェックします。 + - 有効でない場合、`status`状態変数を、ユーザーにMetaMaskのインストールを促すJSX文字列に設定するだけです。 + - 有効になっている場合、3行目のリスナー`window.ethereum.on("accountsChanged")`を設定します。これはMetaMaskウォレットの状態変更をリッスンします。これには、ユーザーがdappに追加のアカウントを接続した場合、アカウントを切り替えた場合、アカウントを切断した場合が含まれます。 少なくとも1つのアカウントが接続されていれば、`walletAddress`状態変数は、リスナーから返された`accounts`配列の最初のアカウントとして更新されます。 それ以外の場合、`walletAddress`には空の文字列が設定されます。 最後に、`useEffect`関数で次のように呼び出す必要があります。 @@ -463,11 +463,11 @@ useEffect(async () => { これで完了です。 ウォレットのすべての機能をプログラミングしました。 ウォレットが設定されたので、非代替性トークン(NFT)をミントする方法を理解しましょう! -## 非代替性トークン(NFT)メタデータ入門 {#nft-metadata-101} +## NFTメタデータの基礎 {#nft-metadata-101} このチュートリアルの最初の方で説明した非代替性トークン(NFT)のメタデータを思い出してください。非代替性トークン(NFT)メタデータは、非代替性トークン(NFT)にデジタル資産、名前、説明、その他の属性などのプロパティーを持たせ、非代替性トークン(NFT)を利用できるようにします。 -JSONオブジェクトとしてメタデータを設定し、保存する必要があります。これで、スマートコントラクトの`mintNFT`関数呼び出すときに`tokenURI`パラメータとして渡すことができます。 +このメタデータをJSONオブジェクトとして設定して保存し、スマートコントラクトの`mintNFT`関数を呼び出すときに`tokenURI`パラメータとして渡せるようにする必要があります。 「Link to Asset」、「Name」、「Description」フィールドのテキストは、非代替性トークン(NFT)のメタデータで別々のプロパティになります。 メタデータをJSONオブジェクトとしてフォーマットしますが、このJSONオブジェクトの格納には、以下のような複数のオプションがあります。 @@ -475,25 +475,25 @@ JSONオブジェクトとしてメタデータを設定し、保存する必要 - AWSやFirebaseなどの中央集権型サーバーに保存できます。 しかし、これは分散化の信念に反するものです。 - 惑星間ファイルシステム(IPFS)という、分散型ファイルシステムでデータを保存、共有するための、分散型プロトコルおよびピアツーピア・ネットワークを使用できます。 このプロトコルは、分散化されており無料のため、最良のオプションです。 -惑星間ファイルシステム(IPFS)にメタデータを保存するには、[Pinata](https://pinata.cloud/)という便利な惑星間ファイルシステム(IPFS) APIとツールキットを使用します。 次のステップでは、この方法を具体的に説明します。 +メタデータをIPFSに保存するには、便利なIPFS APIおよびツールキットである[Pinata](https://pinata.cloud/)を使用します。 次のステップでは、この方法を具体的に説明します。 -## Pinataを使用してメタデータをIPFSに固定化 {#use-pinata-to-pin-your-metadata-to-IPFS} +## Pinataを使ってメタデータをIPFSにピン留めする {#use-pinata-to-pin-your-metadata-to-IPFS} -[Pinata](https://pinata.cloud/)アカウントをお持ちでない場合は、[こちら](https://app.pinata.cloud/auth/signup)から無料のアカウントにサインアップし、メールアドレスとアカウントの認証手順を完了してください。 +[Pinata](https://pinata.cloud/)アカウントをお持ちでない場合は、[こちら](https://app.pinata.cloud/auth/signup)から無料アカウントにサインアップし、メールアドレスとアカウントの認証手順を完了してください。 -### Pinata APIキーの作成 {#create-pinata-api-key} +### Pinata APIキーを作成する {#create-pinata-api-key} -[https://pinata.cloud/keys](https://pinata.cloud/keys)ページに移動して、上部にある「New Key」ボタンを選択し、Adminウィジェットを有効(Enabled)に設定してからキーに名前を付けます。 +[https://pinata.cloud/keys](https://pinata.cloud/keys)ページに移動して、上部にある「New Key」ボタンを選択し、Adminウィジェットを有効に設定してからキーに名前を付けます。 API情報を含むポップアップが表示されます。 この情報は、必ず安全な場所に保存してください。 キーの設定が完了したので、プロジェクトに追加して使用できるようにしましょう。 -### .envファイルの作成 {#create-a-env} +### .envファイルを作成する {#create-a-env} -環境ファイルにPinataキーとシークレットを安全に保存できます。 [dotenvパッケージ](https://www.npmjs.com/package/dotenv)をプロジェクトディレクトリにインストールしましょう。 +環境ファイルにPinataキーとシークレットを安全に保存できます。 プロジェクトディレクトリに[dotenvパッケージ](https://www.npmjs.com/package/dotenv)をインストールしましょう。 -ターミナルで\(ローカルホストを実行しているタブとは別の\)新しいタブを開き、`minter-starter-files`フォルダにいることを確認してください。次に、ターミナルで以下のコマンドを実行します。 +ターミナルで新しいタブを開き(ローカルホストを実行しているタブとは別のタブ)、`minter-starter-files`フォルダにいることを確認してから、ターミナルで次のコマンドを実行します。 ```text npm install dotenv --save @@ -505,7 +505,7 @@ npm install dotenv --save vim.env ``` -vim\(テキストエディタ\)で `.env`ファイルが開きます。 保存するには、キーボードで「esc」+「:」+「q」をこの順序で押します。 +これにより、vim(テキストエディタ)で`.env`ファイルが開きます。 保存するには、キーボードで「esc」+「:」+「q」をこの順序で押します。 次に、VSCodeで`.env`ファイルに移動し、次のようにしてPinata APIキーとAPIシークレットを追加します。 @@ -516,11 +516,11 @@ REACT_APP_PINATA_SECRET = ファイルを保存します。これで、JSONメタデータを惑星間ファイルシステム(IPFS)にアップロードする関数を書き始める準備が整いました。 -### pinJSONToIPFSの実装 {#pin-json-to-ipfs} +### pinJSONToIPFSを実装する {#pin-json-to-ipfs} -幸いにもPinataでは、[惑星間ファイルシステム(IPFS)へのJSONデータのアップロードに特化したAPI](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json)と、少しの変更を加えるだけで使用できるaxiosのサンプルを備えた便利なJavaScriptを使用できます。 +幸いにもPinataには、[JSONデータをIPFSにアップロードするための専用API](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json)と、少しの変更で使えるaxiosを使った便利なJavaScriptのサンプルがあります。 -`utils`フォルダーに`pinata.js`という別のファイルを作成し、.envファイルからPinataのシークレットとキーをインポートしましょう。 +`utils`フォルダーに`pinata.js`という別のファイルを作成し、.envファイルからPinataのシークレットとキーを次のようにインポートしましょう。 ```javascript require("dotenv").config() @@ -528,7 +528,7 @@ const key = process.env.REACT_APP_PINATA_KEY const secret = process.env.REACT_APP_PINATA_SECRET ``` -次に、`pinata.js`ファイルに以下の追加コードを貼り付けます。 コードの意味はこれから説明しますので、心配する必要はありません。 +次に、pinata.jsファイルに以下の追加コードを貼り付けます。 コードの意味はこれから説明しますので、心配する必要はありません。 ```javascript require("dotenv").config() @@ -539,7 +539,7 @@ const axios = require("axios") export const pinJSONToIPFS = async (JSONBody) => { const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS` - //making axios POST request to Pinata ⬇️ + //Pinataへのaxios POSTリクエストを作成 ⬇️ return axios .post(url, JSONBody, { headers: { @@ -568,30 +568,30 @@ export const pinJSONToIPFS = async (JSONBody) => { 最初に、ブラウザとnode.jsのためのPromiseベースのHTTPクライアントである[axios](https://www.npmjs.com/package/axios)をインポートしています。axiosは、Pinataへのリクエストで使用します。 -その下に、`pinJSONToIPFS`非同期関数があります。この関数は、`pinJSONToIPFS` APIへのPOSTリクエストを行うために、`JSONBody`を入力として取り、PinataのAPIキーとシークレットをヘッダーに入れます。 +その下に、`pinJSONToIPFS`非同期関数があります。この関数は、`JSONBody`を入力として取り、PinataのAPIキーとシークレットをヘッダーに入れて、`pinJSONToIPFS` APIへのPOSTリクエストを行います。 -- POSTリクエストが成功した場合、この関数は、trueに設定された`success`ブール値と、メタデータがピン留めされた`pinataUrl`が入ったJSONオブジェクトを返します。 ここで返された`pinataUrl`は、スマートコントラクトのmint関数の`tokenURI`の入力として使用されます。 -- POSTリクエストが失敗した場合、この関数は、falseに設定された`success`ブール値と、エラーを伝える`message`文字列が入ったJSONオブジェクトを返します。 +- このPOSTリクエストが成功した場合、この関数は、`success`ブール値がtrueで、メタデータがピン留めされた`pinataUrl`が入ったJSONオブジェクトを返します。 ここで返された`pinataUrl`は、スマートコントラクトのmint関数の`tokenURI`の入力として使用されます。 +- このPOSTリクエストが失敗した場合、この関数は、`success`ブール値がfalseで、エラーを伝える`message`文字列が入ったJSONオブジェクトを返します。 `connectWallet`関数の戻り値の型と同様に、JSONオブジェクトが返されるので、そのパラメータを状態変数とUIの更新に使用できます。 -## スマートコントラクトのロード {#load-your-smart-contract} +## スマートコントラクトを読み込む {#load-your-smart-contract} -これで、`pinJSONToIPFS`関数を介して非代替性トークン(NFT)メタデータを惑星間ファイルシステム(IPFS)にアップロードする手段を手に入れました。次は、`mintNFT`関数を呼び出せるように、スマートコントラクトのインスタンスをロードする手段が必要です。 +`pinJSONToIPFS`関数を介してNFTメタデータをIPFSにアップロードする手段を手に入れたので、次は`mintNFT`関数を呼び出せるように、スマートコントラクトのインスタンスを読み込む手段が必要です。 -前述したように、このチュートリアルでは、[こちらの既存の非代替性トークン(NFT)スマートコントラクト](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)を使用します。ただし、この作成方法を学びたい、もしくは自分で作成したい場合は、[「非代替性トークン(NFT)の作り方」](https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft)という別のチュートリアルを確認することを強くお勧めします。 +前述したように、このチュートリアルでは[こちらの既存のNFTスマートコントラクト](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)を使用します。しかし、その作成方法を学びたい、もしくは自分で作成したい場合は、もう一つのチュートリアル["NFTの作成方法"](https://www.alchemy.com/docs/how-to-create-an-nft)をご覧になることを強くお勧めします。 -### コントラクトアプリケーションバイナリインターフェース(ABI) {#contract-abi} +### コントラクトABI {#contract-abi} ファイルを詳しく調べてみると、`src`ディレクトリに`contract-abi.json`ファイルがあることが分かります。 アプリケーションバイナリインターフェース(ABI)は、コントラクトが呼び出す関数を指定し、関数が確実に意図しているフォーマットでデータを返すようにするために必要です。 さらに、イーサリアムブロックチェーンに接続してスマートコントラクトをロードするための、Alchemy APIキーとAlchemy Web3 APIも必要になります。 -### Alchemy APIキーの作成 {#create-alchemy-api} +### Alchemy APIキーを作成する {#create-alchemy-api} -Alchemyのアカウントをお持ちでない場合は、[こちら](https://alchemy.com/?a=eth-org-nft-minter)から無料で登録できます。 +まだAlchemyアカウントをお持ちでない場合は、[こちらから無料でサインアップしてください](https://alchemy.com/?a=eth-org-nft-minter)。 -Alchemyのアカウントを作成した後、アプリを作成することでAPIキーを生成することができます。 これにより、Ropstenテストネットワークへのリクエストが可能になります。 +Alchemyのアカウントを作成した後、アプリを作成することでAPIキーを生成することができます。 これにより、Ropsten テストネットワークへのリクエストが可能になります。 ナビゲーションバーの「Apps」にマウスを合わせて、「Create App」をクリックし、Alchemyダッシュボードの「Create App」ページに移動してください。 @@ -601,7 +601,7 @@ Alchemyのアカウントを作成した後、アプリを作成することでA HTTP Alchemy API URLを作成したので、クリップボードにコピーします。 -それを`.env`ファイルに追加してみましょう。 これで.envファイル全体は、次のようになります。 +…そして、それを`.env`ファイルに追加しましょう。 これで.envファイル全体は、次のようになります。 ```text REACT_APP_PINATA_KEY = @@ -609,11 +609,11 @@ REACT_APP_PINATA_SECRET = REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/ ``` -コントラクトアプリケーションバイナリインターフェース(ABI)とAlchemy APIキーが用意できたので、[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)を使用してスマートコントラクトをロードする準備ができました。 +コントラクトABIとAlchemy APIキーが用意できたので、[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)を使用してスマートコントラクトを読み込む準備ができました。 -### Alchemy Web3エンドポイントとコントラクトの設定 {#setup-alchemy-endpoint} +### Alchemy Web3エンドポイントとコントラクトを設定する {#setup-alchemy-endpoint} -まず、[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)がインストールされていない場合は、ターミナルで次のようにホームディレクトリである`nft-minter-tutorial`に移動してインストールする必要があります。 +まず、[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)がまだインストールされていない場合は、ターミナルでホームディレクトリ`nft-minter-tutorial`に移動してインストールする必要があります: ```text cd .. @@ -629,7 +629,7 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(alchemyKey) ``` -[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)は、[Web3.js](https://docs.web3js.org/)のラッパーであり、強化されたAPIメソッドや重要なメリットを提供し、Web3デベロッパーの負担を軽減します。 最小限の設定で使えるように設計されているので、アプリですぐに使用可能です。 +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)は[Web3.js](https://docs.web3js.org/)のラッパーであり、強化されたAPIメソッドやその他の重要なメリットを提供し、web3開発者としての作業を容易にします。 最小限の設定で使えるように設計されているので、アプリですぐに使用可能です。 次に、コントラクトアプリケーションバイナリインターフェース(ABI)とコントラクトアドレスをファイルに追加しましょう。 @@ -645,9 +645,9 @@ const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE" これで両方を追加できたので、mint関数のコーディングを始める準備ができました。 -## mintNFT関数の実装 {#implement-the-mintnft-function} +## mintNFT関数を実装する {#implement-the-mintnft-function} -`interact.js`ファイル内に、`mintNFT`関数を定義しましょう。この関数は、名前が示すとおりに非代替性トークン(NFT)をミントします。 +`interact.js`ファイル内に、`mintNFT`関数を定義しましょう。この関数は、その名の通りNFTをミントします。 多数の非同期呼び出しを\(メタデータをIPFSにピン留めするためにPinataに対して、スマートコントラクトをロードするためにAlchemy Web3に対して、トランザクションに署名するためにMetaMaskに対して\)行うため、この関数もまた非同期になります。 @@ -663,21 +663,21 @@ export const mintNFT = async (url, name, description) => {} ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //エラー処理 if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗ミントする前にすべてのフィールドに入力してください。", } } } ``` -基本的に、入力パラメーターのいずれかが空の文字列である場合、falseに設定された`success`ブール値と、UIのすべてのフィールドに入力する必要があることを伝える`status`文字列が入ったJSONオブジェクトを返します。 +基本的に、入力パラメータのいずれかが空の文字列である場合、`success`ブール値がfalseで、UIのすべてのフィールドに入力する必要があることを伝える`status`文字列が入ったJSONオブジェクトを返します。 -### IPFSにメタデータをアップロード {#upload-metadata-to-ipfs} +### メタデータをIPFSにアップロードする {#upload-metadata-to-ipfs} -メタデータが適切にフォーマットされていることを確認したら、次のステップは、それをJSONオブジェクトにラップし、作成した`pinJSONToIPFS`を介して惑星間ファイルシステム(IPFS)にアップロードすることです。 +メタデータが適切にフォーマットされていることを確認したら、次のステップは、それをJSONオブジェクトにラップし、作成した`pinJSONToIPFS`を介してIPFSにアップロードすることです。 そのためにはまず、`pinJSONToIPFS`関数を`interact.js`ファイルにインポートする必要があります。 `interact.js`の最上部に、次の行を追加してください。 @@ -685,32 +685,32 @@ export const mintNFT = async (url, name, description) => { import { pinJSONToIPFS } from "./pinata.js" ``` -`pinJSONToIPFS`が、JSON本体を取ることを思い出してください。 そのため、呼び出す前に`url`、`name`、`description`パラメータをJSONオブジェクトにフォーマットする必要があります。 +`pinJSONToIPFS`がJSON本体を入力として取ることを思い出してください。 そのため、呼び出す前に`url`、`name`、`description`パラメータをJSONオブジェクトにフォーマットする必要があります。 次のようにコードを更新して、`metadata`というJSONオブジェクトを作成し、この`metadata`パラメータを使用して`pinJSONToIPFS`を呼び出します。 ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //エラー処理 if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗ミントする前にすべてのフィールドに入力してください。", } } - //make metadata + //メタデータを作成 const metadata = new Object() metadata.name = name metadata.image = url metadata.description = description - //make pinata call + //pinata呼び出しを作成 const pinataResponse = await pinJSONToIPFS(metadata) if (!pinataResponse.success) { return { success: false, - status: "😢 Something went wrong while uploading your tokenURI.", + status: "😢 tokenURIのアップロード中に問題が発生しました。", } } const tokenURI = pinataResponse.pinataUrl @@ -719,7 +719,7 @@ export const mintNFT = async (url, name, description) => { `pinJSONToIPFS(metadata)`の呼び出しのレスポンスを、`pinataResponse`オブジェクトに格納していることに注目してください。 次に、このオブジェクトにエラーがないか解析します。 -エラーがある場合、falseに設定された`success`ブール値と、呼び出しが失敗したことを伝える`status`文字列が入ったJSONオブジェクトを返します。 それ以外の場合は、`pinataURL`を`pinataResponse`から抽出し、それを`tokenURI`変数として格納します。 +エラーがある場合、`success`ブール値がfalseで、呼び出しが失敗したことを伝える`status`文字列が入ったJSONオブジェクトを返します。 それ以外の場合は、`pinataURL`を`pinataResponse`から抽出し、それを`tokenURI`変数として格納します。 では、ファイルの先頭で初期化したAlchemy Web3 APIを使用して、スマートコントラクトをロードしてみましょう。 `mintNFT`関数の下部に次のコードの行を追加して、`window.contract`グローバル変数にコントラクトを設定します。 @@ -727,19 +727,19 @@ export const mintNFT = async (url, name, description) => { window.contract = await new web3.eth.Contract(contractABI, contractAddress) ``` -`mintNFT`関数に最後に追加するのは、イーサリアムのトランザクションです。 +`mintNFT`関数に最後に追加するのは、Ethereumのトランザクションです。 ```javascript -//set up your Ethereum transaction +//Ethereumトランザクションを設定 const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: window.ethereum.selectedAddress, // must match user's active address. + to: contractAddress, // コントラクト公開時以外は必須 + from: window.ethereum.selectedAddress, // ユーザーのアクティブなアドレスと一致する必要あり data: window.contract.methods .mintNFT(window.ethereum.selectedAddress, tokenURI) - .encodeABI(), //make call to NFT smart contract + .encodeABI(), //NFTスマートコントラクトを呼び出し } -//sign the transaction via MetaMask +//MetaMask経由でトランザクションに署名 try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -748,13 +748,13 @@ try { return { success: true, status: - "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + + "✅ Etherscanでトランザクションを確認: https://ropsten.etherscan.io/tx/" + txHash, } } catch (error) { return { success: false, - status: "😥 Something went wrong: " + error.message, + status: "😥 問題が発生しました: " + error.message, } } ``` @@ -762,54 +762,54 @@ try { イーサリアムトランザクションをすでによくご存知ならば、構造が今まで見てきたものとかなり似ていることに気付くでしょう。 - まず、トランザクションパラメータを設定します。 - - `to`に受取人のアドレス\(スマートコントラクト\)を設定します 。 - - `from`にトランザクションの署名者\(MetaMaskに接続されているユーザーのアドレス: `window.ethereum.selectedAddress`\)を指定します。 - - `data`には、スマートコントラクトの`mintNFT`メソッド呼び出しが含まれ、`tokenURI`とユーザーのウォレットのアドレス`window.ethereum.selectedAddress`を入力として受け取ります。 -- 次に、`window.ethereum.request`をawaitで呼び出して、MetaMaskにトランザクションの署名を依頼します。 このリクエストで、ethメソッド\(eth_sendTransaction\)を指定し、`transactionParameters`を渡していることに注目してください。 この時点で、ブラウザでMetaMaskが開かれ、ユーザーにトランザクションの署名または拒否を求めます。 - - トランザクションが成功した場合、この関数は、trueに設定された`success`ブール値と、Etherscanでトランザクションについての詳細を確認するようユーザーに求める`status`文字列が入ったJSONオブジェクトを返します。 - - トランザクションが失敗した場合、この関数は、falseに設定された`success`ブール値と、エラーメッセージを伝える`status`文字列が入ったJSONオブジェクトを返します。 + - `to`は受信者アドレス(スマートコントラクト)を指定します + - `from`はトランザクションの署名者を指定します(ユーザーのMetaMaskに接続されたアドレス: `window.ethereum.selectedAddress`) + - `data`には、スマートコントラクトの`mintNFT`メソッドへの呼び出しが含まれ、入力として`tokenURI`とユーザーのウォレットアドレス`window.ethereum.selectedAddress`を受け取ります +- 次に、`window.ethereum.request`をawaitで呼び出して、MetaMaskにトランザクションの署名を依頼します。 このリクエストで、ethメソッド(`eth_sendTransaction`)を指定し、`transactionParameters`を渡していることに注目してください。 この時点で、ブラウザでMetaMaskが開かれ、ユーザーにトランザクションの署名または拒否を求めます。 + - トランザクションが成功した場合、この関数は、ブール値`success`がtrueに設定され、`status`文字列がユーザーにトランザクションの詳細についてEtherscanを確認するよう促すJSONオブジェクトを返します。 + - トランザクションが失敗した場合、この関数は、`success`ブール値がfalseに設定され、`status`文字列がエラーメッセージを伝えるJSONオブジェクトを返します。 `mintNFT`関数全体は、次のようになります。 ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //エラー処理 if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗ミントする前にすべてのフィールドに入力してください。", } } - //make metadata + //メタデータを作成 const metadata = new Object() metadata.name = name metadata.image = url metadata.description = description - //pinata pin request + //pinataピン留めリクエスト const pinataResponse = await pinJSONToIPFS(metadata) if (!pinataResponse.success) { return { success: false, - status: "😢 Something went wrong while uploading your tokenURI.", + status: "😢 tokenURIのアップロード中に問題が発生しました。", } } const tokenURI = pinataResponse.pinataUrl - //load smart contract + //スマートコントラクトを読み込む window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract(); - //set up your Ethereum transaction + //Ethereumトランザクションを設定 const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: window.ethereum.selectedAddress, // must match user's active address. + to: contractAddress, // コントラクト公開時以外は必須 + from: window.ethereum.selectedAddress, // ユーザーのアクティブなアドレスと一致する必要あり data: window.contract.methods .mintNFT(window.ethereum.selectedAddress, tokenURI) - .encodeABI(), //make call to NFT smart contract + .encodeABI(), //NFTスマートコントラクトを呼び出す } - //sign transaction via MetaMask + //MetaMask経由でトランザクションに署名 try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -818,13 +818,13 @@ export const mintNFT = async (url, name, description) => { return { success: true, status: - "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + + "✅ Etherscanでトランザクションを確認: https://ropsten.etherscan.io/tx/" + txHash, } } catch (error) { return { success: false, - status: "😥 Something went wrong: " + error.message, + status: "😥 問題が発生しました: " + error.message, } } } @@ -832,7 +832,7 @@ export const mintNFT = async (url, name, description) => { 巨大な関数でしたね! あとは、`mintNFT`関数を`Minter.js`コンポーネントに接続するだけです。 -## mintNFTをMinter.jsフロントエンドに接続 {#connect-our-frontend} +## mintNFTをMinter.jsフロントエンドに接続する {#connect-our-frontend} `Minter.js`ファイルを開いて、上部の`import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js";`の行を次のように更新してください。 @@ -844,7 +844,7 @@ import { } from "./utils/interact.js" ``` -最後に、次のように`onMintPressed`関数を実装し、インポートした`mintNFT`関数をawaitで呼び出します。さらに、`status`状態変数を更新し、トランザクションが成功したか失敗したかを反映させるようにします。 +最後に、`onMintPressed`関数を実装してインポートした`mintNFT`関数をawaitで呼び出し、`status`状態変数を更新してトランザクションが成功したか失敗したかを反映させます。 ```javascript const onMintPressed = async () => { @@ -853,22 +853,22 @@ const onMintPressed = async () => { } ``` -## 稼働中のウェブサイトに非代替性トークン(NFT)をデプロイ {#deploy-your-NFT} +## NFTを稼働中のWebサイトにデプロイする {#deploy-your-NFT} -プロジェクトを稼働させてユーザーが使える準備ができましたでしょうか? 稼働しているウェブサイトへMinterをデプロイする[チュートリアル](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online)をご覧ください。 +プロジェクトを稼働させてユーザーが使える準備ができましたでしょうか? ミンターを稼働中のWebサイトにデプロイする方法については、[こちらのチュートリアル](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online)をご覧ください。 次は最後のステップです。 -## ブロックチェーンの世界を席巻しよう! {#take-the-blockchain-world-by-storm} +## ブロックチェーンの世界に旋風を巻き起こす {#take-the-blockchain-world-by-storm} これは冗談です。あなたは、このチュートリアルを最後までやりきりました! 要約すると、非代替性トークン(NFT)ミンターを構築することで次の方法を学ぶことが出来ました。 -- フロントエンドのプロジェクト経由でMetaMaskへアクセス -- フロントエンドからスマートコントラクトメソッドの呼び出し -- MetaMaskを使ったトランザクションの署名 +- フロントエンドのプロジェクト経由でMetaMaskに接続する +- フロントエンドからスマートコントラクトメソッドを呼び出す +- MetaMaskを使用してトランザクションに署名する -ウォレットに分散型アプリケーション(Dapp)を介してミントされた非代替性トークン(NFT)を表示する方法については、[ウォレットに非代替性トークン(NFT)を表示する方法](https://docs.alchemyapi.io/alchemy/tutorials/how-to-write-and-deploy-a-nft-smart-contract/how-to-view-your-nft-in-your-wallet)という簡単なチュートリアルをご覧ください。 +おそらく、dappを介してミントされたNFTをウォレットで披露したいと思うでしょうから、簡単なチュートリアル[ウォレットでNFTを表示する方法](https://www.alchemy.com/docs/how-to-view-your-nft-in-your-mobile-wallet)をぜひご覧ください。 -ご不明な点がありましたら、いつでも[Alchemy Discord](https://discord.gg/gWuC7zB)でお問い合わせください。 このチュートリアルのコンセプトが、今後のプロジェクトでどのように応用されるのか楽しみでなりません。 +そして、いつものように、何か質問があれば、[Alchemy Discord](https://discord.gg/gWuC7zB)でお手伝いします。 このチュートリアルのコンセプトが、今後のプロジェクトでどのように応用されるのか楽しみでなりません。 diff --git a/public/content/translations/ja/developers/tutorials/optimism-std-bridge-annotated-code/index.md b/public/content/translations/ja/developers/tutorials/optimism-std-bridge-annotated-code/index.md index d1b9c3ffae4..4388b37ec0b 100644 --- a/public/content/translations/ja/developers/tutorials/optimism-std-bridge-annotated-code/index.md +++ b/public/content/translations/ja/developers/tutorials/optimism-std-bridge-annotated-code/index.md @@ -1,84 +1,89 @@ --- -title: "Optimismの標準ブリッジコントラクトを紹介します" -description: Optimismの標準ブリッジは、どのように機能するか。 および、その理由。 +title: "Optimism標準ブリッジコントラクトのウォークスルー" +description: "Optimismの標準ブリッジはどのように機能するのでしょうか? なぜこのように機能するのでしょうか?" author: Ori Pomerantz -tags: - - "Solidity" - - "ブリッジ" - - "レイヤー2" +tags: [ "Solidity", "ブリッジ", "レイヤー2" ] skill: intermediate published: 2022-03-30 lang: ja --- -[Optimism](https://www.optimism.io/)は、[Optimisitc ロールアップ](/developers/docs/scaling/optimistic-rollups/)を行うメカニズムのひとつです。 Optimistic ロールアップでは、ネットワークに含まれるすべてノードではなく一部のノードのみを対象としてトランザクションが処理されるため、イーサリアム・メインネット(「レイヤー1」または「L1」とも呼ばれます)よりも手数料が低くなります。 一部のノードのみを対象として処理されるものの、すべてのデータはL1に書き込まれるため、あらゆる事項につき、メインネットにおける完全性および可用性についての保証に基づいて証明、再構築することが可能です。 +[Optimism](https://www.optimism.io/)は[オプティミスティック・ロールアップ](/developers/docs/scaling/optimistic-rollups/)です。 +オプティミスティック・ロールアップは、ネットワーク上のすべてのノードではなく一部のノードのみでトランザクションが処理されるため、イーサリアムメインネット(レイヤー1またはL1とも呼ばれる)よりもはるかに低い価格でトランザクションを処理できます。 +同時に、すべてのデータがL1に書き込まれるため、メインネットの完全性と可用性の保証の元で、すべてを証明、再構築することが可能です。 -Optimism(またはその他のL2)上でL1のアセットを使用するには、当該アセットを[ブリッジ](/bridges/#prerequisites)する必要があります。 アセットをブリッジする方法のひとつとして、アセット(最も一般的なのは、ETHや[ERC-20 トークン](/developers/docs/standards/tokens/erc-20/)です)をL1上でロックし、L2上で同等のアセットを受け取る方法があります。 最終的に、これらのアセットを所持するユーザーは、再度L1にブリッジする必要があるでしょう。 L1にアセットをブリッジすると、L2上のアセットはバーンされ、L1上のアセットがユーザーに戻されます。 +Optimism(またはその他のL2)でL1アセットを使用するには、アセットを[ブリッジ](/bridges/#prerequisites)する必要があります。 +これを実現する一つの方法は、ユーザーがL1でアセット(最も一般的なのはETHと[ERC-20トークン](/developers/docs/standards/tokens/erc-20/)です)をロックし、L2で使用する同等のアセットを受け取ることです。 +最終的に、それらのアセットを手にした人は、L1にブリッジして戻したいと思うかもしれません。 +このとき、L2のアセットはバーンされ、L1でユーザーに返還されます。 -以上が、[Optimismにおける標準ブリッジ](https://docs.optimism.io/app-developers/bridging/standard-bridge)の仕組みです。 この記事では、このブリッジ機能についてSolidity上で適切に作成したソースコードを確認しながら、その仕組みを学びます。 +これが、[Optimism標準ブリッジ](https://docs.optimism.io/app-developers/bridging/standard-bridge)の仕組みです。 +この記事では、そのブリッジのソースコードをレビューし、その仕組みを確認し、適切に記述されたSolidityコードの例として学習します。 ## 制御フロー {#control-flows} -ブリッジは、2つのメインフローで構成されます: +ブリッジには、2つの主要なフローがあります: -- L1からL2への入金 -- L2からL1への出金 +- デポジット (L1からL2へ) +- 引き出し (L2からL1へ) -### 入金フロー {#deposit-flow} +### デポジットフロー {#deposit-flow} -#### L1 {#deposit-flow-layer-1} +#### レイヤー1 {#deposit-flow-layer-1} -1. ERC-20を入金する場合、入金者はブリッジに対し、入金額を使用するためのアローワンスを与えます。 -2. 入金者は、L1ブリッジ(`depositERC20`、`depositERC20To`、 `depositETH`あるいは `depositETHTo`)を呼び出します。 -3. L1ブリッジが、ブリッジされたアセットを保持します。 - - ETHの場合:アセットは、呼び出しを通じて入金者に送信されます。 - - ERC-20トークンの場合:アセットは、入金者が提供するアローワンスを使用して、ブリッジ自体に送信されます。 -4. L1のブリッジが、クロスドメインのメッセージメカニズムを通じて、L2のブリッジ上で`finalizeDeposit`を呼び出します。 +1. ERC-20をデポジットする場合、デポジットする人は、デポジットされる金額を使用する権限をブリッジに与えます。 +2. デポジットする人はL1ブリッジを呼び出します(`depositERC20`、`depositERC20To`、`depositETH`、または`depositETHTo`) +3. L1ブリッジは、ブリッジされた資産の所有権を取得します。 + - ETH: アセットは呼び出しの一部として、デポジットする人によって転送されます。 + - ERC-20: アセットは、デポジットする人から提供された権限を使用して、ブリッジによってそれ自体に転送されます。 +4. L1ブリッジは、クロスドメインメッセージメカニズムを使用して、L2ブリッジの`finalizeDeposit`を呼び出します。 -#### L2 {#deposit-flow-layer-2} +#### レイヤー2 {#deposit-flow-layer-2} -5. L2のブリッジは、`finalizeDeposit`の呼び出しにつき、以下が適切であることを確認します: - - クロスドメインのメッセージ・コントラクトからの呼び出しであること。 - - ブリッジがL1上で作成されたものであること。 -6. L2のブリッジはさらに、L2上のERC-20トークンコントラクトにつき、以下が適切であることを確認します: - - L2のコントラクトにおいて、L1における対応するコントラクトが、L1上で送信されたトークンと同一であると報告していること。 - - L2のコントラクトが、([ERC-165を使用した](https://eips.ethereum.org/EIPS/eip-165))適切なインターフェイスをサポートすると報告していること。 -7. L2のコントラクトが適切であると確認できた場合は、適切なアドレスに対して希望する量のトークンをミントするために、そのコントラクトを呼び出してください。 そうでない場合は、出金プロセスを開始して、ユーザーがL1上のトークンを請求できるようにします。 +5. L2ブリッジは`finalizeDeposit`への呼び出しが正当なものであることを検証します: + - クロスドメインメッセージコントラクトからの呼び出しであること + - もともとL1のブリッジからの呼び出しであること +6. L2ブリッジは、L2上のERC-20トークンコントラクトが正しいものであるかを確認します: + - L2コントラクトは、そのL1の対応物がL1から来たトークンと同じものであることを報告します。 + - L2コントラクトは正しいインターフェースをサポートしていることを報告します([ERC-165を使用](https://eips.ethereum.org/EIPS/eip-165))。 +7. L2コントラクトが正しい場合、それを呼び出して適切な数のトークンを適切なアドレスにミントします。 そうでない場合、ユーザーがL1でトークンを要求できるように、引き出しプロセスを開始します。 -### 出金フロー {#withdrawal-flow} +### 引き出しフロー {#withdrawal-flow} #### レイヤー2 {#withdrawal-flow-layer-2} -1. 出金者は、L2のブリッジ(`withdraw`または`withdrawTo`)を呼び出します 。 -2. L2のブリッジは、`msg.sender`が所有する適切な数のトークンをバーンします。 -3. L2のブリッジは、クロスドメインのメッセージ・メカニズムを利用して、L1のブリッジ上で、 `finalizeETHWithdrawal`または`finalizeERC20Withdrawal`を呼び出します。 +1. 引き出す人はL2ブリッジを呼び出します(`withdraw`または`withdrawTo`) +2. L2ブリッジは、`msg.sender`に属する適切な数のトークンをバーンします。 +3. L2ブリッジは、クロスドメインメッセージメカニズムを使用して、L1ブリッジで`finalizeETHWithdrawal`または`finalizeERC20Withdrawal`を呼び出します。 -#### L1 {#withdrawal-flow-layer-1} +#### レイヤー1 {#withdrawal-flow-layer-1} -4. L1のブリッジは、`finalizeETHWithdraw`または`finalizeERC20Withdral`の呼び出しにつき、以下が適切であるかを確認します: - - クロスドメインのメッセージ・メカニズムを経由していること。 - - L2のブリッジで作成されていること。 -5. L1のブリッジは、適切な資産(ETHまたはERC-20)を適切なアドレスに送信します。 +4. L1ブリッジは、`finalizeETHWithdrawal`または`finalizeERC20Withdrawal`への呼び出しが正当であることを検証します: + - クロスドメインメッセージメカニズムからの呼び出しであること + - もともとL2のブリッジからの呼び出しであること +5. L1ブリッジは、適切な資産(ETHまたはERC-20)を適切なアドレスに転送します。 -## レイヤー1のコード {#layer-1-code} +## レイヤー1コード {#layer-1-code} -以下は、イーサリアム・メインネット(L1)で実行されるコードです。 +これは、L1であるイーサリアムメインネットで実行されるコードです。 ### IL1ERC20Bridge {#IL1ERC20Bridge} -このインターフェイスは、[こちら](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol)で定義されています。 このインターフェイスには、ERC-20トークンをブリッジするために必要な機能と定義が含まれます。 +[このインターフェースはここで定義されています](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol)。 +これには、ERC-20トークンのブリッジングに必要な関数と定義が含まれています。 ```solidity // SPDX-License-Identifier: MIT ``` -Optimismのコードの大部分は、[MITライセンス](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-)に基づいています。 +[OptimismのコードのほとんどはMITライセンスの下でリリースされています](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-)。 ```solidity pragma solidity >0.5.0 <0.9.0; ``` -本記事の執筆時点で、Solidityの最新バージョンは0.8.12です。 バージョン0.9.0においてこのコードが利用できるかは、同バージョンがリリースされるまで不明です。 +執筆時点で、Solidityの最新バージョンは0.8.12です。 +バージョン0.9.0がリリースされるまで、このコードに互換性があるかどうかはわかりません。 ```solidity /** @@ -86,20 +91,23 @@ pragma solidity >0.5.0 <0.9.0; */ interface IL1ERC20Bridge { /********** - * Events * + * イベント * **********/ event ERC20DepositInitiated( ``` -Optimismのブリッジ関連用語において、_入金_とは、L1からL2に送金することを意味し、_出金_とは、L2からL1へ送金することを意味します。 +Optimismのブリッジ用語では、「デポジット」はL1からL2への転送を意味し、「引き出し」はL2からL1への転送を意味します。 ```solidity address indexed _l1Token, address indexed _l2Token, ``` -ほとんどの場合、L1上のERC-20のアドレスは、L2上で対応するERC-20のアドレスとは異なります。 トークンアドレスのリストは、[こちら](https://static.optimism.io/optimism.tokenlist.json)を参照してください。 `chainId`が1のアドレスであれば、L1 (メインネット) 上のトークンであり、`chainId`が10のアドレスでれば、L2(Optimism)上のトークンです。 残りの2つの`chainId`の値は、Kovanテストネットワーク(42)とOptimistic Kovanテストネットワーク(69)のためのものです。 +ほとんどの場合、L1上のERC-20のアドレスは、L2上の同等のERC-20のアドレスとは異なります。 +[トークンアドレスのリストはこちらで確認できます](https://static.optimism.io/optimism.tokenlist.json)。 +`chainId`が1のアドレスはL1 (メインネット) 上にあり、`chainId`が10のアドレスはL2 (Optimism) 上にあります。 +他の2つの`chainId`の値は、Kovanテストネットワーク(42)とOptimistic Kovanテストネットワーク(69)のものです。 ```solidity address indexed _from, @@ -109,7 +117,7 @@ Optimismのブリッジ関連用語において、_入金_とは、L1からL2に ); ``` -転送にはメモを追加することができ、メモは転送を報告するイベントに追加されます。 +転送にメモを追加することが可能で、その場合、それらを報告するイベントに追加されます。 ```solidity event ERC20WithdrawalFinalized( @@ -122,33 +130,34 @@ Optimismのブリッジ関連用語において、_入金_とは、L1からL2に ); ``` -入金と出金の両方向につき、同じブリッジのコントラクトが処理します。 つまり、L1のブリッジでは入金を初期化し、出金を確定します。 +同じブリッジコントラクトが、両方向の転送を処理します。 +L1ブリッジの場合、これはデポジットの開始と引き出しの完了を意味します。 ```solidity /******************** - * Public Functions * + * 公開関数 * ********************/ /** - * @dev get the address of the corresponding L2 bridge contract. - * @return Address of the corresponding L2 bridge contract. + * @dev 対応するL2ブリッジコントラクトのアドレスを取得します。 + * @return 対応するL2ブリッジコントラクトのアドレス。 */ function l2TokenBridge() external returns (address); ``` -実際には、L2のブリッジは事前にデプロイされており、常に「`0x42000000000000000000000000000000000000000010`」のアドレスとなるため、この機能は必要ではありません。 この機能は、L2上のブリッジとの対称性を確保するためのものです。というのも、L1ブリッジのアドレスを確認することは無意味とは_言えない_ためです。 +この関数は、L2では事前にデプロイされたコントラクトであるため、実際には必要ありません。したがって、常にアドレス`0x4200000000000000000000000000000000000010`にあります。 +これはL2ブリッジとの対称性のためにあります。なぜなら、L1ブリッジのアドレスは簡単にはわからないからです。 ```solidity /** - * @dev deposit an amount of the ERC20 to the caller's balance on L2. - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _amount Amount of the ERC20 to deposit - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev L2の呼び出し元残高にERC20の金額をデポジットします。 + * @param _l1Token デポジットするL1 ERC20のアドレス + * @param _l2Token L1の各L2 ERC20のアドレス + * @param _amount デポジットするERC20の金額 + * @param _l2Gas L2でデポジットを完了するために必要なガスリミット。 + * @param _data L2に転送するオプションのデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function depositERC20( address _l1Token, @@ -159,19 +168,20 @@ Optimismのブリッジ関連用語において、_入金_とは、L1からL2に ) external; ``` -`_l2Gas`のパラメータは、このトランザクションが使用できるL2上のガス量です。 この値は、[一定の(高い)上限まで無料であるため](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2)、ミント時にERC-20がコントラクトが特に異常な動作を行わない限り、問題は発生しません。 以下の関数は、異なるブロックチェーンにおける同一アドレスにアセットをブリッジしたいという一般的なシナリオで用いることができます。 +`_l2Gas`パラメータは、トランザクションが使用できるL2ガスの量です。 +[一定の(高い)制限まで、これは無料です](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2)。そのため、ミント時にERC-20コントラクトが本当に奇妙なことをしない限り、問題にはならないはずです。 +この関数は、ユーザーが異なるブロックチェーン上の同じアドレスに資産をブリッジするという、一般的なシナリオに対応します。 ```solidity /** - * @dev deposit an amount of ERC20 to a recipient's balance on L2. - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _to L2 address to credit the withdrawal to. - * @param _amount Amount of the ERC20 to deposit. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev L2の受取人の残高にERC20の金額をデポジットします。 + * @param _l1Token デポジットするL1 ERC20のアドレス + * @param _l2Token L1の各L2 ERC20のアドレス + * @param _to 引き出しの入金先L2アドレス。 + * @param _amount デポジットするERC20の金額。 + * @param _l2Gas L2でデポジットを完了するために必要なガスリミット。 + * @param _data L2に転送するオプションのデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function depositERC20To( address _l1Token, @@ -183,26 +193,24 @@ Optimismのブリッジ関連用語において、_入金_とは、L1からL2に ) external; ``` -次のコードは`depositERC20`とほぼ同一の関数ですが、ERC-20トークンを異なるアドレスに送信することができます。 +この関数は`depositERC20`とほぼ同じですが、ERC-20を異なるアドレスに送信できます。 ```solidity /************************* - * Cross-chain Functions * + * クロスチェーン関数 * *************************/ /** - * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the - * L1 ERC20 token. - * This call will fail if the initialized withdrawal from L2 has not been finalized. + * @dev L2からL1への引き出しを完了し、受取人のL1 ERC20トークン残高に資金を入金します。 + * この呼び出しは、L2からの初期化された引き出しが完了していない場合、失敗します。 * - * @param _l1Token Address of L1 token to finalizeWithdrawal for. - * @param _l2Token Address of L2 token where withdrawal was initiated. - * @param _from L2 address initiating the transfer. - * @param _to L1 address to credit the withdrawal to. - * @param _amount Amount of the ERC20 to deposit. - * @param _data Data provided by the sender on L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @param _l1Token finalizeWithdrawalの対象となるL1トークンのアドレス。 + * @param _l2Token 引き出しが開始されたL2トークンのアドレス。 + * @param _from 転送を開始するL2アドレス。 + * @param _to 引き出しの入金先L1アドレス。 + * @param _amount デポジットするERC20の金額。 + * @param _data L2の送信者から提供されたデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function finalizeERC20Withdrawal( address _l1Token, @@ -215,16 +223,20 @@ Optimismのブリッジ関連用語において、_入金_とは、L1からL2に } ``` -Optimismにおける出金(および、他のL2からL1へのメッセージ送信)は、2つのステップで実行されます: +Optimismでの引き出し(およびL2からL1への他のメッセージ)は、2段階のプロセスです: -1. L2でトランザクションを開始する。 -2. L1で、トランザクションを確定/クレームする。 L1でのトランザクションは、L2でのトランザクションに対する[異議申し立て期間](https://community.optimism.io/docs/how-optimism-works/#fault-proofs) が終了した後で実行可能になります。 +1. L2での開始トランザクション。 +2. L1での完了または請求トランザクション。 + このトランザクションは、L2トランザクションの[フォールトチャレンジ期間](https://community.optimism.io/docs/how-optimism-works/#fault-proofs)が終了した後に実行される必要があります。 ### IL1StandardBridge {#il1standardbridge} -このインターフェイスは、[こちら](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol)で定義されています。 このブリッジには、ETHを対象とするイベントおよび関数の定義が含まれています。 これらの定義は、ERC-20トークンを対象とする`IL1ERC20Bridge`の定義とほぼ同一です。 +[このインターフェースはここで定義されています](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol)。 +このファイルには、ETHのイベントと関数の定義が含まれています。 +これらの定義は、上記の`IL1ERC20Bridge`で定義されたERC-20のものと非常によく似ています。 -一部のERC-20トークンは、標準ブリッジでは処理できず、カスタム処理が必要となるため、このブリッジのインターフェイスは2つのファイルで構成されています。 これにより、カスタム処理が必要なトークンを取り扱うブリッジについては`IL1ERC20Bridge`を実装すればよく、ETHのブリッジ機能を実装する必要はありません。 +ブリッジインターフェースは2つのファイルに分かれています。なぜなら、一部のERC-20トークンはカスタム処理が必要で、標準ブリッジでは処理できないからです。 +これにより、そのようなトークンを処理するカスタムブリッジは、`IL1ERC20Bridge`を実装でき、ETHもブリッジする必要がありません。 ```solidity // SPDX-License-Identifier: MIT @@ -237,7 +249,7 @@ import "./IL1ERC20Bridge.sol"; */ interface IL1StandardBridge is IL1ERC20Bridge { /********** - * Events * + * イベント * **********/ event ETHDepositInitiated( address indexed _from, @@ -247,32 +259,33 @@ interface IL1StandardBridge is IL1ERC20Bridge { ); ``` -このイベントは、ERC-20トークン用のイベント(`ERC20DepositInitiated`)とほぼ同一ですが、L1およびL2上のトークンアドレスが含まれていない点が異なります。 他のイベントおよび関数についても、この点が異なります。 +このイベントは、ERC-20バージョン(`ERC20DepositInitiated`)とほぼ同じですが、L1とL2のトークンアドレスがない点が異なります。 +他のイベントや関数についても同様です。 ```solidity event ETHWithdrawalFinalized( . - 。 - 。 + . + . ); /******************** - * Public Functions * + * 公開関数 * ********************/ /** - * @dev Deposit an amount of the ETH to the caller's balance on L2. - 。 - 。 - 。 + * @dev L2の呼び出し元残高にETHの金額をデポジットします。 + . + . + . */ function depositETH(uint32 _l2Gas, bytes calldata _data) external payable; /** - * @dev Deposit an amount of ETH to a recipient's balance on L2. - 。 - 。 - 。 + * @dev L2の受取人の残高にETHの金額をデポジットします。 + . + . + . */ function depositETHTo( address _to, @@ -281,16 +294,15 @@ interface IL1StandardBridge is IL1ERC20Bridge { ) external payable; /************************* - * Cross-chain Functions * + * クロスチェーン関数 * *************************/ /** - * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the - * L1 ETH token. Since only the xDomainMessenger can call this function, it will never be called - * before the withdrawal is finalized. - 。 - 。 - 。 + * @dev L2からL1への引き出しを完了し、受取人のL1 ETHトークン残高に資金を入金します。 + * この関数はxDomainMessengerのみが呼び出せるため、引き出しが完了する前に呼び出されることはありません。 + . + . + . */ function finalizeETHWithdrawal( address _from, @@ -303,7 +315,7 @@ interface IL1StandardBridge is IL1ERC20Bridge { ### CrossDomainEnabled {#crossdomainenabled} -[このコントラクト](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol)は、[L1](#the-l1-bridge-contract)および[L2](#the-l2-bridge-contract)の両方のブリッジにおいて継承され、相手のレイヤーに対してメッセージを送信します。 +[このコントラクト](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol)は、両方のブリッジ([L1](#the-l1-bridge-contract)と[L2](#the-l2-bridge-contract))によって継承され、他のレイヤーにメッセージを送信します。 ```solidity // SPDX-License-Identifier: MIT @@ -313,52 +325,54 @@ pragma solidity >0.5.0 <0.9.0; import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; ``` -[このインターフェース](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol)は、クロスドメインのメッセンジャーを用いて、相手のレイヤーに対するメッセージの送信方法をコントラクトに通知するものです。 このクロスドメインのメッセンジャーは完全に別個のシステムであるため、今後改めて記事を執筆したいと考えています。 +[このインターフェース](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol)は、クロスドメインメッセンジャーを使用して、他のレイヤーにメッセージを送信する方法をコントラクトに伝えます。 +このクロスドメインメッセンジャーはまったく別のシステムであり、それ自体で記事にする価値があるため、将来的に書きたいと思っています。 ```solidity /** * @title CrossDomainEnabled - * @dev Helper contract for contracts performing cross-domain communications + * @dev クロスドメイン通信を実行するコントラクトのヘルパーコントラクト * - * Compiler used: defined by inheriting contract + * 使用されるコンパイラ: 継承するコントラクトによって定義 */ contract CrossDomainEnabled { /************* - * Variables * + * 変数 * *************/ - // Messenger contract used to send and receive messages from the other domain. + // 他のドメインからメッセージを送受信するために使用されるメッセンジャーコントラクト address public messenger; /*************** - * Constructor * + * コンストラクタ * ***************/ /** - * @param _messenger Address of the CrossDomainMessenger on the current layer. + * @param _messenger 現在のレイヤー上のCrossDomainMessengerのアドレス */ constructor(address _messenger) { messenger = _messenger; } ``` -このコントラクトが知る必要がある唯一のパラメータは、当該レイヤーにおけるクロスドメイン・メッセンジャーのアドレスです。 このパラメータは、コンストラクタ上で設定された後は変更されません。 +コントラクトが知る必要がある唯一のパラメータは、このレイヤー上のクロスドメインメッセンジャーのアドレスです。 +このパラメータはコンストラクタで一度設定され、変更されることはありません。 ```solidity /********************** - * Function Modifiers * + * 関数修飾子 * **********************/ /** - * Enforces that the modified function is only callable by a specific cross-domain account. - * @param _sourceDomainAccount The only account on the originating domain which is - * authenticated to call this function. + * 変更された関数が特定のクロスドメインアカウントによってのみ呼び出し可能であることを強制します。 + * @param _sourceDomainAccount この関数を呼び出すことが認証されている、発信元ドメインの唯一のアカウント。 */ modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) { ``` -クロスドメインのメッセージング機能は、ブロックチェーン(イーサリアム・メインネットまたはOptimism)上で実行されているあらゆるコントラクトからアクセス可能です。 ただし、相手方のブリッジから送信された特定のメッセージ_のみ_を信頼するためには、双方のブリッジが必要になります。 +クロスドメインメッセージングは、実行されているブロックチェーン(イーサリアムメインネットまたはOptimism)上のどのコントラクトからもアクセスできます。 +しかし、各側のブリッジが、他の側のブリッジから来た場合にのみ特定のメッセージを信頼するようにする必要があります。 ```solidity require( @@ -367,7 +381,7 @@ contract CrossDomainEnabled { ); ``` -適切なクロスドメイン・メッセンジャー (以下の`messenger`を参照)から送信されたメッセージのみ、信頼できます。 +適切なクロスドメインメッセンジャー(以下で見るように`messenger`)からのメッセージのみが信頼できます。 ```solidity @@ -377,9 +391,10 @@ contract CrossDomainEnabled { ); ``` -クロスドメイン・メッセンジャーでは、[the `.xDomainMessageSender()` 関数](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128)を用いて、相手方のレイヤーにメッセージを送信するアドレスを提供します。 当該メッセージで開始されたトランザクションで呼び出す場合に限り、メッセージの送信元アドレスを表示することができます。 +クロスドメインメッセンジャーが、他のレイヤーでメッセージを送信したアドレスを提供する方法は、[`.xDomainMessageSender()`関数](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128)です。 +メッセージによって開始されたトランザクションで呼び出される限り、この情報を提供できます。 -このメッセージにつき、相手方のブリッジから送信されたものであることを確認する必要があります。 +受け取ったメッセージが他のブリッジから来たことを確認する必要があります。 ```solidity @@ -387,29 +402,28 @@ contract CrossDomainEnabled { } /********************** - * Internal Functions * + * 内部関数 * **********************/ /** - * Gets the messenger, usually from storage. This function is exposed in case a child contract - * needs to override. - * @return The address of the cross-domain messenger contract which should be used. + * 通常はストレージからメッセンジャーを取得します。この関数は、子コントラクトがオーバーライドする必要がある場合に公開されます。 + * @return 使用すべきクロスドメインメッセンジャーコントラクトのアドレス。 */ function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) { return ICrossDomainMessenger(messenger); } ``` -この機能は、クロスドメイン・メッセンジャーを返します。 `messenger`の変数ではなく関数を使うのは、この値を継承するコントラクトに対し、どのクロスドメイン・メッセンジャーを使用するかを特定するアルゴリズムを使用できるようにするためです。 +この関数は、クロスドメインメッセンジャーを返します。 +変数`messenger`ではなく関数を使用するのは、これから継承するコントラクトが、どのクロスドメインメッセンジャーを使用するかを指定するアルゴリズムを使用できるようにするためです。 ```solidity /** - * Sends a message to an account on another domain - * @param _crossDomainTarget The intended recipient on the destination domain - * @param _message The data to send to the target (usually calldata to a function with - * `onlyFromCrossDomainAccount()`) - * @param _gasLimit The gasLimit for the receipt of the message on the target domain. + * 他のドメインのアカウントにメッセージを送信します。 + * @param _crossDomainTarget 宛先ドメインの意図した受信者 + * @param _message ターゲットに送信するデータ(通常は`onlyFromCrossDomainAccount()`を持つ関数へのcalldata) + * @param _gasLimit ターゲットドメインでのメッセージのレシートのgasLimit。 */ function sendCrossDomainMessage( address _crossDomainTarget, @@ -417,17 +431,18 @@ contract CrossDomainEnabled { bytes memory _message ``` -最後に、次の関数は相手方のレイヤーにメッセージを送信するものです。 +最後に、他のレイヤーにメッセージを送信する関数です。 ```solidity ) internal { // slither-disable-next-line reentrancy-events, reentrancy-benign ``` -[Slither](https://github.com/crytic/slither)は、Optimismにおいて、すべてのコントラクトの脆弱性やその他の潜在的な問題箇所を特定するために実行される静的解析ツールです。 ここでは、以下の行により2つの脆弱性がトリガーされます。 +[Slither](https://github.com/crytic/slither)は、Optimismがすべてのコントラクトで実行し、脆弱性やその他の潜在的な問題を検出するための静的アナライザーです。 +この場合、次の行は2つの脆弱性を引き起こします: -1. [リエントランシーのイベント](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) -2. [無害のリエントランシー](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) +1. [再入可能性イベント](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) +2. [良性の再入可能性](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) ```solidity getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit); @@ -435,18 +450,19 @@ contract CrossDomainEnabled { } ``` -このケースでは、Slitherが当該情報を把握する方法を持たない場合でも、`getCrossDomainMessenger()`が信頼できるアドレスを返すと分かっているため、リエントランシーについて心配する必要はありません。 +この場合、`getCrossDomainMessenger()`が信頼できるアドレスを返すことがわかっているため、再入可能性について心配する必要はありません。たとえSlitherがそれを知る方法がなくてもです。 -### L1上のブリッジコントラクト {#the-l1-bridge-contract} +### L1ブリッジコントラクト {#the-l1-bridge-contract} -このコントラクトのソースコードは、[こちら](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol)で入手してください。 +[このコントラクトのソースコードはこちらです](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol)。 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; ``` -このインターフェイスは、他のコントラクトにも含まれる場合があるため、Solidityのさまざまなバージョンをサポートする必要があります。 しかしここでは、ブリッジ自体がコントラクトであるため、使用できるSolidityのバージョンを限定することが可能です。 +インターフェースは他のコントラクトの一部になる可能性があるため、幅広いSolidityバージョンをサポートする必要があります。 +しかし、ブリッジ自体は私たちのコントラクトであり、使用するSolidityバージョンについて厳密にすることができます。 ```solidity /* Interface Imports */ @@ -454,124 +470,139 @@ import { IL1StandardBridge } from "./IL1StandardBridge.sol"; import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol"; ``` -[IL1ERC20Bridge](#IL1ERC20Bridge)と[IL1StandardBridge](#IL1StandardBridge)については、すでに説明しました。 +[IL1ERC20Bridge](#IL1ERC20Bridge)と[IL1StandardBridge](#IL1StandardBridge)については、上記で説明しました。 ```solidity import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol"; ``` -[このインターフェイス](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol)では、L2上で標準ブリッジを制御するためのメッセージを作成します。 +[このインターフェース](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol)により、L2の標準ブリッジを制御するためのメッセージを作成できます。 ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; ``` -[このインターフェイス](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)は、ERC-20のコントラクトを制御するために使用します。 詳細については、[こちら](/developers/tutorials/erc20-annotated-code/#the-interface)をご覧ください。 +[このインターフェース](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)により、ERC-20コントラクトを制御できます。 +[詳細はこちらで読むことができます](/developers/tutorials/erc20-annotated-code/#the-interface)。 ```solidity /* Library Imports */ import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; ``` -[上記](#crossdomainenabled)で説明したように、このコントラクトはレイヤー間のメッセージングに使用します。 +[上で説明したように](#crossdomainenabled)、このコントラクトはレイヤー間メッセージングに使用されます。 ```solidity import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; ``` -[`Lib_PredeployAddresses`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/constants/Lib_PredeployAddresses.sol)には、常に同じアドレスを持つL2上のコントラクトのアドレスが含まれています。 これには、L2上の標準ブリッジが含まれます。 +`Lib_PredeployAddresses`には、常に同じアドレスを持つL2コントラクトのアドレスが含まれています。 これにはL2の標準ブリッジが含まれます。 ```solidity import { Address } from "@openzeppelin/contracts/utils/Address.sol"; ``` -[OpenZeppelinのアドレス・ユーティリティ](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol)です。 これは、コントラクト上のアドレスと外部所有アカウント(EOA)に含まれるアドレスを区別するために使われます。 +[OpenZeppelinのアドレスユーティリティ](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol)。 これは、コントラクトアドレスと外部所有アカウント(EOA)に属するアドレスを区別するために使用されます。 -これは、直接の呼び出しとコントラクトのコンストラクタで作成した呼び出しを区別できないため完全なソリューションとは言えませんが、少なくとも、よくあるユーザーエラーを特定、防止することは可能です。 +これは、直接の呼び出しとコントラクトのコンストラクタからの呼び出しを区別する方法がないため、完璧な解決策ではないことに注意してください。しかし、少なくともこれにより、一般的なユーザーエラーを特定し、防ぐことができます。 ```solidity import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; ``` -[ERC-20標準](https://eips.ethereum.org/EIPS/eip-20)では、コントラクトが実行失敗を報告する手段として以下の2つがあります: +[ERC-20標準](https://eips.ethereum.org/EIPS/eip-20)は、コントラクトが失敗を報告する2つの方法をサポートしています: 1. 元に戻す 2. `false`を返す -両方のケースに対応するとコードがより複雑になるため、代わりに[OpenZeppelinの`SafeERC20`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol)を使用します。これにより、[失敗した場合は常に元に戻されます](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96)。 +両方のケースを処理するとコードが複雑になるため、代わりにOpenZeppelinの[`SafeERC20`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol)を使用します。これにより、[すべての失敗が revert になる](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96)ことが保証されます。 ```solidity /** * @title L1StandardBridge - * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard - * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits - * and listening to it for newly finalized withdrawals. + * @dev L1 ETHおよびERC20ブリッジは、デポジットされたL1資金と、L2で使用されている標準トークンを保存するコントラクトです。 + * 対応するL2ブリッジと同期し、デポジットを通知し、新しく完了した引き出しをリッスンします。 * */ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { using SafeERC20 for IERC20; ``` -この行は、`IERC20`インターフェイスを使用する際に、常に`IERC20`ラッパーを用いることを指定するものです。 +この行は、`IERC20`インターフェースを使用するたびに`SafeERC20`ラッパーを使用するように指定する方法です。 ```solidity /******************************** - * External Contract References * + * 外部コントラクト参照 * ********************************/ address public l2TokenBridge; ``` -[L2StandardBridge](#the-l2-bridge-contract)のアドレスです。 +[L2StandardBridge](#the-l2-bridge-contract)のアドレス。 ```solidity - // Maps L1 token to L2 token to balance of the L1 token deposited + // L1トークンをL2トークンにマッピングし、デポジットされたL1トークンの残高にマッピングします。 mapping(address => mapping(address => uint256)) public deposits; ``` -[2次元スパース配列](https://en.wikipedia.org/wiki/Sparse_matrix)を定義するには、このようなダブル[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)を用います。 このデータ構造の値は、`deposit[L1 token addr][L2 token addr]`として識別されます。 初期値はゼロになります。 ゼロ以外の値が設定されたセルのみが、ストレージに書き込まれます。 +このような二重の[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)は、[2次元スパース配列](https://en.wikipedia.org/wiki/Sparse_matrix)を定義する方法です。 +このデータ構造の値は、`deposit[L1トークンアドレス][L2トークンアドレス]`として識別されます。 +デフォルト値はゼロです。 +異なる値に設定されたセルのみがストレージに書き込まれます。 ```solidity /*************** - * Constructor * + * コンストラクタ * ***************/ - // This contract lives behind a proxy, so the constructor parameters will go unused. + // このコントラクトはプロキシの背後にあるため、コンストラクタのパラメータは使用されません。 constructor() CrossDomainEnabled(address(0)) {} ``` -ストレージ内のすべての変数をコピーせずに、このコントラクトを更新するには、 [`プロキシ`](https://docs.openzeppelin.com/contracts/3.x/api/proxy)を使用します。プロキシは、[`delegatecall`](https://solidity-by-example.org/delegatecall/)を用いて、プロキシのコントラクトにおいてアドレスが保存された別個のコントラクトに対して呼び出しを転送するものです(コントラクトを更新する際に、プロキシに対してこのアドレスを変更するように指示することになります)。 「`delegatecall`」を使用すると、ストレージは、_呼び出し元の_コントラクトのストレージのままになるため、すべてのコントラクト状態変数の値は影響を受けません。 +ストレージ内のすべての変数をコピーすることなく、このコントラクトをアップグレードできるようにしたいです。 +そのためには、[`Proxy`](https://docs.openzeppelin.com/contracts/3.x/api/proxy)を使用します。これは、[`delegatecall`](https://solidity-by-example.org/delegatecall/)を使用して、プロキシコントラクトによってアドレスが保存されている別のコントラクトに呼び出しを転送するコントラクトです(アップグレード時に、プロキシにそのアドレスを変更するように指示します)。 +`delegatecall`を使用すると、ストレージは呼び出し元コントラクトのストレージのままになるため、すべてのコントラクトの状態変数の値は影響を受けません。 -このパターンを用いる効果のひとつとして、`delegatecall`の_呼び出し先_であるコントラクトのストレージが使用されないため、送信されたコンストラクタの値が意味を持たない点が挙げられます。 これこそ、`CrossDomainEnabled`のコンストラクタに対して無意味な値を入力できる理由です。 同時に、以下の初期化がコンストラクタとは別個に存在するである理由でもあります。 +このパターンの1つの効果は、`delegatecall`の呼び出し先であるコントラクトのストレージが使用されないため、それに渡されるコンストラクタの値は重要ではないということです。 +これが、`CrossDomainEnabled`コンストラクタに無意味な値を提供できる理由です。 +また、以下の初期化がコンストラクタから分離されている理由でもあります。 ```solidity /****************** - * Initialization * + * 初期化 * ******************/ /** - * @param _l1messenger L1 Messenger address being used for cross-chain communications. - * @param _l2TokenBridge L2 standard bridge address. + * @param _l1messenger クロスチェーン通信に使用されるL1メッセンジャーアドレス。 + * @param _l2TokenBridge L2標準ブリッジアドレス。 */ // slither-disable-next-line external-function ``` -この[Slitherのテスト](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external)は、コントラクトのコードにより呼び出される関数以外の関数であり、`public`ではなく、`external`と宣言される関数を特定するものです。 `external`関数のガス代は、コールデータに含まれるパラメータで指定できるため、安価に抑えることができます。 `public`と宣言された関数は、コントラクト内でアクセス可能である必要があります。 コントラクトはそれ自体のコールデータを変更できないため、パラメータはメモリに保存する必要があります。 このような関数を外部から呼び出す場合、コールデータをメモリにコピーする必要があるため、ガス代が発生します。 今回は関数を1回のみ呼び出すため、コスト上の非効率性は度外視できます。 +この[Slitherテスト](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external)は、コントラクトコードから呼び出されず、したがって`public`ではなく`external`として宣言できる関数を特定します。 +`external`関数のガス代は、calldataでパラメータを提供できるため、低くなる可能性があります。 +`public`と宣言された関数は、コントラクト内からアクセス可能である必要があります。 +コントラクトは自身のcalldataを変更できないため、パラメータはメモリに保存する必要があります。 +そのような関数が外部から呼び出される場合、calldataをメモリにコピーする必要があり、ガス代がかかります。 +この場合、関数は一度しか呼び出されないため、非効率性は問題になりません。 ```solidity function initialize(address _l1messenger, address _l2TokenBridge) public { require(messenger == address(0), "Contract has already been initialized."); ``` -`initialize`関数は、1回だけ呼び出す必要があります。 L1のクロスドメイン・メッセンジャーまたは L2のトークンブリッジのいずれかのアドレスが変更されると、新しいプロキシとそれを呼び出す新しいブリッジが作成されます。 これは、システム全体がアップグレードされた場合を除いて発生する可能性は低く、非常にまれです。 +`initialize`関数は、一度だけ呼び出す必要があります。 +L1クロスドメインメッセンジャーまたはL2トークンブリッジのアドレスが変更された場合、新しいプロキシとそれを呼び出す新しいブリッジを作成します。 +これは、システム全体がアップグレードされる場合を除き、起こる可能性は低く、非常にまれな出来事です。 -この関数には、呼び出し可能な_アカウント_を制限するメカニズムが含まれない点に注意してください。 つまり理論的には、ネットワークに対する攻撃者は、プロキシとブリッジの最初のバージョンがデプロイされるまで待機し、正当なユーザーが`initialize`関数にアクセスできる前に[フロントラン](https://solidity-by-example.org/hacks/front-running/)を実行することが可能です。 これを防ぐには、以下の2つの方法があります: +この関数には、誰が呼び出せるかを制限するメカニズムがないことに注意してください。 +つまり理論的には、攻撃者はプロキシとブリッジの最初のバージョンがデプロイされるのを待ち、正当なユーザーが`initialize`関数にアクセスする前に[フロントラン](https://solidity-by-example.org/hacks/front-running/)を実行することができます。 しかし、これを防ぐ方法は2つあります: -1. コントラクトがEOAにより直接デプロイされるのではなく、[別のコントラクトが作成したトランザクションにおいて](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595)デプロイされる場合、全体のプロセスがアトミックになり、他のトランザクションが実行される前に終了する場合があります。 -2. 正当な`initialize`呼び出しが失敗した場合、常に、新たに作成されたプロキシおよびブリッジを無視し、さらに新しいものを作成することが可能です。 +1. コントラクトがEOAによって直接デプロイされるのではなく、[別のコントラクトがそれらを作成するトランザクション](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595)でデプロイされる場合、プロセス全体がアトミックになり、他のトランザクションが実行される前に完了することができます。 +2. `initialize`への正当な呼び出しが失敗した場合、新しく作成されたプロキシとブリッジを無視して、新しいものを作成することは常に可能です。 ```solidity messenger = _l1messenger; @@ -584,34 +615,33 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { ```solidity /************** - * Depositing * + * デポジット * **************/ - /** @dev Modifier requiring sender to be EOA. This check could be bypassed by a malicious - * contract via initcode, but it takes care of the user error we want to avoid. + /** @dev 送信者がEOAであることを要求する修飾子。このチェックは、悪意のあるコントラクトによって + * initcode経由で回避される可能性がありますが、私たちが避けたいユーザーエラーに対応します。 */ modifier onlyEOA() { - // Used to stop deposits from contracts (avoid accidentally lost tokens) + // コントラクトからのデポジットを停止するために使用(誤って失われたトークンを避けるため) require(!Address.isContract(msg.sender), "Account not EOA"); _; } ``` -これは、OpenZeppelinの`Address`ユーティリティが必要となる理由です。 +これが、OpenZeppelinの`Address`ユーティリティが必要だった理由です。 ```solidity /** - * @dev This function can be called with no data - * to deposit an amount of ETH to the caller's balance on L2. - * Since the receive function doesn't take data, a conservative - * default amount is forwarded to L2. + * @dev この関数は、データを指定せずに呼び出すことができ、L2の呼び出し元残高にETHの金額をデポジットします。 + * receive関数はデータを取らないため、保守的なデフォルト金額がL2に転送されます。 */ receive() external payable onlyEOA { _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes("")); } ``` -この関数は、テスト用のものです。 通常の使用を目的とするものではないため、インターフェイスの定義には含まれない点に注意してください。 +この関数は、テスト目的で存在します。 +インターフェース定義には表示されないことに注意してください。通常の使用のためではありません。 ```solidity /** @@ -633,18 +663,16 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { } ``` -これら2つの関数は、実際のETH入金を処理する関数である`_initiateETHDeposit`に関連したラッパーです。 +これら2つの関数は、実際のETHデポジットを処理する関数である`_initiateETHDeposit`のラッパーです。 ```solidity /** - * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of - * the deposit. - * @param _from Account to pull the deposit from on L1. - * @param _to Account to give the deposit to on L2. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev ETHを保存し、L2 ETHゲートウェイにデポジットを通知することで、デポジットのロジックを実行します。 + * @param _from L1でデポジットを引き出すアカウント。 + * @param _to L2でデポジットを与えるアカウント。 + * @param _l2Gas L2でデポジットを完了するために必要なガスリミット。 + * @param _data L2に転送するオプションのデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function _initiateETHDeposit( address _from, @@ -652,11 +680,13 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { uint32 _l2Gas, bytes memory _data ) internal { - // Construct calldata for finalizeDeposit call + // finalizeDeposit呼び出しのcalldataを構築 bytes memory message = abi.encodeWithSelector( ``` -クロスドメインのメッセージは、メッセージをコールデータとして宛先のコントラクトを呼び出す仕組みとして機能します。 Solidityのコントラクトは常に、コールデータを[ABI仕様](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html)に従って解釈します。 Solidityの[`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions)は、このコールデータを作成するものです。 +クロスドメインメッセージの仕組みは、宛先コントラクトがメッセージをcalldataとして呼び出されることです。 +Solidityコントラクトは、常に[ABI仕様](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html)に従ってcalldataを解釈します。 +Solidity関数[`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions)は、そのcalldataを作成します。 ```solidity IL2ERC20Bridge.finalizeDeposit.selector, @@ -669,24 +699,24 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { ); ``` -ここでのメッセージは、[ `finalizeDeposit`関数](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148)を以下のパラメータで呼び出すことです。 +ここでのメッセージは、これらのパラメータで[`finalizeDeposit`関数](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148)を呼び出すことです: -| パラメータ | 値 | 説明 | -| ----------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | -| \_l1Token | address(0) | L1上のETH (ERC-20トークンではない) を表す特別な値 | -| \_l2Token | Lib_PredeployAddresses.OVM_ETH | Optimism上でETHを管理するL2のコントラクト`0xDeadDeAddeAddEAddeadDEaDDeaDDeAD0000` (このコントラクトは、Optimism内部でのみ使用されます) | -| \_from | \_from | L1上のETH送信元アドレス | -| \_to | \_to | L2上のETH受領用アドレス | -| amount | msg.value | 送信されたwei額(すでにブリッジに送信済みの額) | -| \_data | \_data | 入金に添付する追加の日付 | +| パラメータ | 値 | 意味 | +| ------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| \_l1Token | address(0) | L1上のETH(ERC-20トークンではない)を表す特別な値 | +| \_l2Token | Lib_PredeployAddresses.OVM_ETH | OptimismでETHを管理するL2コントラクト、`0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000`(このコントラクトはOptimism内部でのみ使用されます) | +| \_from | \_from | L1でETHを送信するアドレス | +| \_to | \_to | L2でETHを受信するアドレス | +| 金額 | msg.value | 送信されたweiの量(すでにブリッジに送信済み) | +| \_data | \_data | デポジットに添付する追加データ | ```solidity - // Send calldata into L2 + // calldataをL2に送信 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); ``` -クロスドメイン・メッセンジャーを通じて、メッセージを送信します。 +クロスドメインメッセンジャーを介してメッセージを送信します。 ```solidity // slither-disable-next-line reentrancy-events @@ -694,16 +724,16 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { } ``` -この送信をリッスンするすべての分散型アプリケーションに対し、通知イベントを発行します。 +この転送をリッスンしている分散型アプリケーションに通知するためにイベントを発行します。 ```solidity /** * @inheritdoc IL1ERC20Bridge */ function depositERC20( - . - 。 - 。 + . + . + . ) external virtual onlyEOA { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data); } @@ -712,30 +742,28 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { * @inheritdoc IL1ERC20Bridge */ function depositERC20To( - . - 。 - 。 + . + . + . ) external virtual { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data); } ``` -これら2つの関数は、実際のERC-20トークンの入金を処理する関数である`_initiateERC20Deposit`に関連したラッパーです。 +これら2つの関数は、実際のERC-20デポジットを処理する`_initiateERC20Deposit`関数のラッパーです。 ```solidity /** - * @dev Performs the logic for deposits by informing the L2 Deposited Token - * contract of the deposit and calling a handler to lock the L1 funds. (e.g., transferFrom) + * @dev L2デポジットトークンコントラクトにデポジットを通知し、ハンドラを呼び出してL1資金をロックするロジックを実行します。(例: transferFrom) * - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _from Account to pull the deposit from on L1 - * @param _to Account to give the deposit to on L2 - * @param _amount Amount of the ERC20 to deposit. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @param _l1Token デポジットするL1 ERC20のアドレス + * @param _l2Token L1の各L2 ERC20のアドレス + * @param _from L1でデポジットを引き出すアカウント + * @param _to L2でデポジットを与えるアカウント + * @param _amount デポジットするERC20の金額。 + * @param _l2Gas L2でデポジットを完了するために必要なガスリミット。 + * @param _data L2に転送するオプションのデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function _initiateERC20Deposit( address _l1Token, @@ -748,26 +776,28 @@ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { ) internal { ``` -この関数は、上記の`_initiateETHDeposit`に似ていますが、いくつかの重要な点が異なります。 最大の違いは、この関数では、トークンのアドレスおよび送信額をパラメータとして受け取るという点です。 ETHの場合、ブリッジへの呼び出しにはすでに、ブリッジアカウントへの資産の移転(`msg.value`)が含まれています。 +この関数は上記の`_initiateETHDeposit`に似ていますが、いくつかの重要な違いがあります。 +最初の違いは、この関数がトークンアドレスと転送量をパラメータとして受け取ることです。 +ETHの場合、ブリッジへの呼び出しには、すでにブリッジアカウントへの資産の移転(`msg.value`)が含まれています。 ```solidity - // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future - // withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if - // _from is an EOA or address(0). + // L1でデポジットが開始されると、L1ブリッジは将来の引き出しのために資金を自身に転送します。 + // safeTransferFromは、コントラクトにコードがあるかどうかもチェックするため、_fromがEOAまたはaddress(0)の場合、これは失敗します。 // slither-disable-next-line reentrancy-events, reentrancy-benign IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); ``` -ERC-20トークンについては、ETHの場合とは異なる送信プロセスが実行されます: +ERC-20トークンの転送は、ETHとは異なるプロセスに従います: -1. ユーザー(`_from`)は、ブリッジに対し、送信したいトークン量に見合ったアローワンスを与えます。 -2. 次に、トークンコントラクトのアドレスや金額等で、ブリッジを呼び出します。 -3. ブリッジは、入金プロセスの一環として、トークンをブリッジ自体に送信します。 +1. ユーザー(`_from`)は、適切なトークンを転送するための権限をブリッジに与えます。 +2. ユーザーは、トークンコントラクトのアドレス、金額などでブリッジを呼び出します。 +3. ブリッジは、デポジットプロセスの一環として、トークンを(自身に)転送します。 -最初のステップは、第2、3のステップとは別のトランザクションで実行しても構いません。 ただし、`_initiateERC20Deposit`を呼び出す 2 つの関数 (`depositERC20`と`depositERC20To`)は、`_from`をパラメータとして`msg.sender`を含むこの関数を呼び出すだけなので、フロントランニングの問題は発生しません。 +最初のステップは、最後の2つのステップとは別のトランザクションで行われる場合があります。 +ただし、`_initiateERC20Deposit`を呼び出す2つの関数(`depositERC20`と`depositERC20To`)は、`_from`パラメータとして`msg.sender`を使用してこの関数を呼び出すだけなので、フロントランニングは問題になりません。 ```solidity - // Construct calldata for _l2Token.finalizeDeposit(_to, _amount) + // _l2Token.finalizeDeposit(_to, _amount)のcalldataを構築 bytes memory message = abi.encodeWithSelector( IL2ERC20Bridge.finalizeDeposit.selector, _l1Token, @@ -778,7 +808,7 @@ ERC-20トークンについては、ETHの場合とは異なる送信プロセ _data ); - // Send calldata into L2 + // calldataをL2に送信 // slither-disable-next-line reentrancy-events, reentrancy-benign sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); @@ -786,7 +816,8 @@ ERC-20トークンについては、ETHの場合とは異なる送信プロセ deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; ``` -`deposits`のデータ構造に、トークンの入金額を追加します。 L2上には、L1上にある同一のERC-20トークンに対応した複数のアドレスが存在する場合があるため、入金の推移を追跡するには、L1上のERC-20トークンに関するブリッジ上の残高を参照するだけでは不十分です。 +デポジットされたトークンの量を`deposits`データ構造に追加します。 +L2には同じL1 ERC-20トークンに対応する複数のアドレスが存在する可能性があるため、ブリッジのL1 ERC-20トークン残高を使用してデポジットを追跡するだけでは不十分です。 ```solidity @@ -795,7 +826,7 @@ ERC-20トークンについては、ETHの場合とは異なる送信プロセ } /************************* - * Cross-chain Functions * + * クロスチェーン関数 * *************************/ /** @@ -808,20 +839,21 @@ ERC-20トークンについては、ETHの場合とは異なる送信プロセ bytes calldata _data ``` -L2のブリッジは、L2のクロスドメイン・メッセンジャーに対して、L1のクロスドメイン・メッセンジャーがこの関数を呼び出すことを指示するメッセージを送信します(もちろん、L1上で、[このメッセージを確定するトランザクション](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions)が送信された後においてです)。 +L2ブリッジはL2クロスドメインメッセンジャーにメッセージを送信し、これによりL1クロスドメインメッセンジャーがこの関数を呼び出します(もちろん、[メッセージを完了するトランザクション](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions)がL1で送信された後)。 ```solidity ) external onlyFromCrossDomainAccount(l2TokenBridge) { ``` -このメッセージにつき、L2のトークン・ブリッジで作成され、クロスドメイン・メッセンジャーを経由して送信された_正当な_メッセージであることを確認してください。 この関数は、ブリッジからETHを出金するために使用するため、承認された呼び出し元だけが呼び出したことを確認する必要があります。 +これが、クロスドメインメッセンジャーから来て、L2トークンブリッジから発信された正当なメッセージであることを確認してください。 +この関数はブリッジからETHを引き出すために使用されるため、承認された呼び出し元によってのみ呼び出されることを確認する必要があります。 ```solidity // slither-disable-next-line reentrancy-events (bool success, ) = _to.call{ value: _amount }(new bytes(0)); ``` -ETHを送信するには、`msg.value`でwei金額を指定して、受領者を呼び出します。 +ETHを転送する方法は、`msg.value`にweiの量を指定して受信者を呼び出すことです。 ```solidity require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); @@ -830,7 +862,7 @@ ETHを送信するには、`msg.value`でwei金額を指定して、受領者を emit ETHWithdrawalFinalized(_from, _to, _amount, _data); ``` -出金イベントを発行します。 +引き出しに関するイベントを発行します。 ```solidity } @@ -848,17 +880,16 @@ ETHを送信するには、`msg.value`でwei金額を指定して、受領者を ) external onlyFromCrossDomainAccount(l2TokenBridge) { ``` -この関数は、上記の `finalizeETHWithdrawal`関数に類似していますが、ERC-20トークンに関する事項が異なります。 +この関数は上記の`finalizeETHWithdrawal`に似ていますが、ERC-20トークンに必要な変更が加えられています。 ```solidity deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount; ``` -`deposits`のデータ構造を更新します。 +`deposits`データ構造を更新します。 ```solidity - - // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer + // L1で引き出しが完了すると、L1ブリッジは資金を引き出し人に転送します。 // slither-disable-next-line reentrancy-events IERC20(_l1Token).safeTransfer(_to, _amount); @@ -868,28 +899,33 @@ ETHを送信するには、`msg.value`でwei金額を指定して、受領者を /***************************** - * Temporary - Migrating ETH * + * 一時的 - ETHの移行 * *****************************/ /** - * @dev Adds ETH balance to the account. This is meant to allow for ETH - * to be migrated from an old gateway to a new gateway. - * NOTE: This is left for one upgrade only so we are able to receive the migrated ETH from the - * old contract + * @dev アカウントにETH残高を追加します。これは、古いゲートウェイから新しいゲートウェイにETHを移行できるようにすることを目的としています。 + * 注意: これは、古いコントラクトから移行されたETHを受け取ることができるように、1回のアップグレードのみに残されます。 */ function donateETH() external payable {} } ``` -このブリッジは、従来の実装から変更されています。 以前の実装から現在の実装に移行した際に、すべての資産を移転させる必要がありました。 ERC-20トークンについては、移転のみが可能でした。 一方、ETHをコントラクトに移転するには、そのコントラクトの承認が必要であり、これは`donateETH`で実行できます。 +ブリッジの以前の実装がありました。 +その実装からこの実装に移行したとき、すべての資産を移動する必要がありました。 +ERC-20トークンは移動するだけです。 +ただし、ETHをコントラクトに転送するには、そのコントラクトの承認が必要であり、それが`donateETH`が提供するものです。 ## L2上のERC-20トークン {#erc-20-tokens-on-l2} -ERC-20トークンを標準ブリッジに適合させるためには、標準ブリッジ_のみが_トークンをミントできるようにする必要があります。 これが必要になるのは、各ブリッジにおいて、Optimism上で流通するトークンの数がL1のブリッジコントラクト内でロックされたトークンの数と一致することを保証する必要があるためです。 L2上のトークンが多すぎる場合、L2上のアセットL1にブリッジして戻すことができないユーザーが発生します。 この場合、信頼できるブリッジが存在せず、事実上、[部分準備銀行制度](https://www.investopedia.com/terms/f/fractionalreservebanking.asp)を生み出すことになってしまいます。 一方、L1上でのトークンが多くなりすぎると、L2トークンをバーンしない限りトークンをリリースできなくなるため、ブリッジコントラクト内の一部のトークンは永遠にロックされた状態になってしまいます。 +ERC-20トークンが標準ブリッジに適合するためには、標準ブリッジ、そして標準ブリッジのみがトークンをミントできるようにする必要があります。 +これは、Optimismで流通しているトークンの数が、L1ブリッジコントラクト内にロックされているトークンの数と等しいことをブリッジが保証する必要があるためです。 +L2にトークンが多すぎると、一部のユーザーは資産をL1に戻すことができなくなります。 +信頼できるブリッジの代わりに、私たちは本質的に[部分準備銀行制度](https://www.investopedia.com/terms/f/fractionalreservebanking.asp)を再現することになります。 +L1にトークンが多すぎると、L2トークンをバーンしない限り解放する方法がないため、それらのトークンの一部はブリッジコントラクト内に永久にロックされたままになります。 ### IL2StandardERC20 {#il2standarderc20} -標準ブリッジを使用するL2上のすべてのERC-20トークンは、標準ブリッジが必要とする関数およびイベントが搭載された[このインターフェイス](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol)を提供する必要があります。 +標準ブリッジを使用するL2上のすべてのERC-20トークンは、[このインターフェース](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol)を提供する必要があります。これには、標準ブリッジが必要とする関数とイベントが含まれています。 ```solidity // SPDX-License-Identifier: MIT @@ -898,20 +934,24 @@ pragma solidity ^0.8.9; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; ``` -[ERC-20に対する標準インターフェイス](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)には、`mint`および`burn`関数が含まれません。 これらのメソッドは、[ERC-20標準](https://eips.ethereum.org/EIPS/eip-20)において必須でないため、トークンを作成、破壊するメカニズムは指定されていないのです。 +[標準のERC-20インターフェース](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)には、`mint`および`burn`関数は含まれていません。 +これらのメソッドは、[ERC-20標準](https://eips.ethereum.org/EIPS/eip-20)では要求されておらず、トークンを作成および破棄するメカニズムは指定されていません。 ```solidity import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; ``` -[ERC-165インターフェイス](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol)は、コントラクトが提供する機能を特定するために用います。 [この標準については、こちらで読むことができます](https://eips.ethereum.org/EIPS/eip-165)。 +[ERC-165インターフェース](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol)は、コントラクトが提供する関数を指定するために使用されます。 +[こちらで標準を読むことができます](https://eips.ethereum.org/EIPS/eip-165)。 ```solidity interface IL2StandardERC20 is IERC20, IERC165 { function l1Token() external returns (address); ``` -この関数は、このコントラクトに対してブリッジされた L1上のトークンアドレスを提供するものです。 同じ機能の逆方向の関数が存在しない点に注意してください。 L1上のトークンについては、L2のサポートが計画されていたか、あるいはいつ実装されたかを問わず、常にブリッジ可能である必要があります。 +この関数は、このコントラクトにブリッジされたL1トークンのアドレスを提供します。 +逆方向の同様の関数がないことに注意してください。 +L2サポートが実装時に計画されていたかどうかに関係なく、任意のL1トークンをブリッジできる必要があります。 ```solidity @@ -924,11 +964,13 @@ interface IL2StandardERC20 is IERC20, IERC165 { } ``` -トークンをミント (作成) およびバーン (破棄) するための関数とイベントです。 トークン数が適切である(L1上でロックされたトークン数と一致する)ことを保証するため、これらの関数を実行できるのはこのブリッジのみである必要があります。 +トークンをミント(作成)およびバーン(破棄)するための関数とイベント。 +トークンの数が正しいこと(L1にロックされているトークンの数と等しいこと)を保証するため、ブリッジはこれらの関数を実行できる唯一のエンティティである必要があります。 ### L2StandardERC20 {#L2StandardERC20} -[これは](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol)、`IL2StandardERC20`インターフェイスの実装です。 カスタムロジックが必要ない場合は、常にこの関数を用いてください。 +[これは`IL2StandardERC20`インターフェースの実装です](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol)。 +何らかのカスタムロジックが必要でない限り、これを使用する必要があります。 ```solidity // SPDX-License-Identifier: MIT @@ -937,7 +979,8 @@ pragma solidity ^0.8.9; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; ``` -[OpenZeppelinで作成したERC-20コントラクト](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)です。 Optimismでは、既存の機能がよく監査され、資産を保持する上で十分信頼できる場合は、新たな機能を追加すべきではないと考えています。 +[OpenZeppelin ERC-20コントラクト](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)。 +Optimismは、特に車輪が十分に監査され、資産を保持するのに十分信頼できる必要がある場合に、車輪を再発明することを信じていません。 ```solidity import "./IL2StandardERC20.sol"; @@ -947,15 +990,15 @@ contract L2StandardERC20 is IL2StandardERC20, ERC20 { address public l2Bridge; ``` -これらは、通常ERC-20では必要になりませんが、ここでは追加で必要となる2つの設定パラメータです。 +これらは、私たちが要求し、通常ERC-20が必要としない2つの追加の設定パラメータです。 ```solidity /** - * @param _l2Bridge Address of the L2 standard bridge. - * @param _l1Token Address of the corresponding L1 token. - * @param _name ERC20 name. - * @param _symbol ERC20 symbol. + * @param _l2Bridge L2標準ブリッジのアドレス。 + * @param _l1Token 対応するL1トークンのアドレス。 + * @param _name ERC20名。 + * @param _symbol ERC20シンボル。 */ constructor( address _l2Bridge, @@ -968,7 +1011,7 @@ contract L2StandardERC20 is IL2StandardERC20, ERC20 { } ``` -まず、`ERC20(_name, _symbol)`で継承したコントラクトのコンストラクタを呼び出し、新たに変数を設定します。 +まず、継承元のコントラクトのコンストラクタ(`ERC20(_name, _symbol)`)を呼び出し、次に独自の変数を設定します。 ```solidity @@ -988,11 +1031,12 @@ contract L2StandardERC20 is IL2StandardERC20, ERC20 { } ``` -[ERC-165](https://eips.ethereum.org/EIPS/eip-165)は、このように動作します。 各インターフェイスは、サポートする一連の関数を含んでおり、これらの機能の[exclusive](https://en.wikipedia.org/wiki/Exclusive_or)または[ABI関数セレクタ](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector)として特定されます。 +これが[ERC-165](https://eips.ethereum.org/EIPS/eip-165)の仕組みです。 +すべてのインターフェースは、サポートされている関数の数であり、それらの関数の[ABI関数セレクタ](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector)の[排他的論理和](https://en.wikipedia.org/wiki/Exclusive_or)として識別されます。 -L2のブリッジは、ERC-165を健全性チェックとして用いて、資産を送信するのに用いるERC-20コントラクトが `IL2StandardERC20`であるか確認します。 +L2ブリッジは、ERC-165をサニティチェックとして使用して、資産を送信するERC-20コントラクトが`IL2StandardERC20`であることを確認します。 -**注意:不正なコントラクトが`supportsInterface`に対して虚偽の値を返すことを防ぐメカニズムは含まれていないため、これはセキュリティ保護のメカニズム_ではなく_、健全性チェックのメカニズムです。 +**注:** 不正なコントラクトが`supportsInterface`に偽の回答を提供することを防ぐものはないため、これはサニティチェックメカニズムであり、セキュリティメカニズムではありません。 ```solidity // slither-disable-next-line external-function @@ -1011,13 +1055,15 @@ L2のブリッジは、ERC-165を健全性チェックとして用いて、資 } ``` -資産をミント/バーンできるのは、L2のブリッジのみです。 +資産をミントおよびバーンできるのは、L2ブリッジのみです。 -`_mint`および`_burn`は、実際には、[OpenZeppelinで作成したERC-20コントラクト](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn)で定義されています。 トークンをミント/バーンできる条件は、ERC-20を使用する方法と同じように多種多様であるため、このコントラクトは単純に外部に露出していないのです。 +`_mint`と`_burn`は、実際には[OpenZeppelin ERC-20コントラクト](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn)で定義されています。 +そのコントラクトは、トークンをミントおよびバーンする条件がERC-20の使用方法と同じくらい多様であるため、それらを外部に公開しないだけです。 -## L2ブリッジのコード {#l2-bridge-code} +## L2ブリッジコード {#l2-bridge-code} -これは、Optimismでブリッジを実行するコードです。 このコントラクトのソースコードは、[こちら](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol)から入手できます。 +これは、Optimismでブリッジを実行するコードです。 +[このコントラクトのソースはこちらです](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol)。 ```solidity // SPDX-License-Identifier: MIT @@ -1029,48 +1075,50 @@ import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol"; import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol"; ``` -[IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol)のインターフェースは、上述した[L1用のインターフェイス](#IL1ERC20Bridge)とほぼ同様ですが、 以下の2点が大きく異なります。 +[IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol)インターフェースは、上で見た[L1の同等のもの](#IL1ERC20Bridge)と非常に似ています。 +2つの大きな違いがあります: -1. L1では、あなたが入金を開始し、出金を確定します。 L2の場合、あなたは出金を開始し、入金を確定します。 -2. L1では、ETHとERC-20トークンを区別する必要があります。 L2では、Optimism上のETH残高は内部で、アドレス「[0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://optimistic.etherscan.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000)」のERC-20トークンとして処理されるため、両方で同じ関数を使います。 +1. L1では、デポジットを開始し、引き出しを完了します。 + ここでは、引き出しを開始し、デポジットを完了します。 +2. L1では、ETHとERC-20トークンを区別する必要があります。 + L2では、Optimismの内部ETH残高はアドレス[0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://explorer.optimism.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000)のERC-20トークンとして処理されるため、両方に同じ関数を使用できます。 ```solidity -/* Library Imports */ +/* ライブラリのインポート */ import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; -/* Contract Imports */ +/* コントラクトのインポート */ import { IL2StandardERC20 } from "../../standards/IL2StandardERC20.sol"; /** * @title L2StandardBridge - * @dev The L2 Standard bridge is a contract which works together with the L1 Standard bridge to - * enable ETH and ERC20 transitions between L1 and L2. - * This contract acts as a minter for new tokens when it hears about deposits into the L1 Standard - * bridge. - * This contract also acts as a burner of the tokens intended for withdrawal, informing the L1 - * bridge to release L1 funds. + * @dev L2標準ブリッジは、L1標準ブリッジと連携して、L1とL2間のETHおよびERC20の移行を可能にするコントラクトです。 + * このコントラクトは、L1標準ブリッジへのデポジットを聞くと、新しいトークンのミンターとして機能します。 + * このコントラクトは、引き出しを意図したトークンのバーナーとしても機能し、L1ブリッジにL1資金を解放するように通知します。 */ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { /******************************** - * External Contract References * + * 外部コントラクト参照 * ********************************/ address public l1TokenBridge; ``` -これは、L1ブリッジのアドレスを追跡する関数です。 L1用のブリッジの場合とは異なり、この変数は_必須_です。 L1ブリッジのアドレスは、事前に知ることはできません。 +L1ブリッジのアドレスを追跡します。 +L1の同等のものとは対照的に、ここではこの変数が必要であることに注意してください。 +L1ブリッジのアドレスは事前にわかりません。 ```solidity /*************** - * Constructor * + * コンストラクタ * ***************/ /** - * @param _l2CrossDomainMessenger Cross-domain messenger used by this contract. - * @param _l1TokenBridge Address of the L1 bridge deployed to the main chain. + * @param _l2CrossDomainMessenger このコントラクトで使用されるクロスドメインメッセンジャー。 + * @param _l1TokenBridge メインチェーンにデプロイされたL1ブリッジのアドレス。 */ constructor(address _l2CrossDomainMessenger, address _l1TokenBridge) CrossDomainEnabled(_l2CrossDomainMessenger) @@ -1079,7 +1127,7 @@ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { } /*************** - * Withdrawing * + * 引き出し * ***************/ /** @@ -1108,21 +1156,21 @@ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { } ``` -これら2つの関数は、出金を開始するものです。 L1上のトークンアドレスを指定する必要がない点に注意してください。 L1上で対応するトークンのアドレスは、L2トークンが伝達するからです。 +これら2つの関数は、引き出しを開始します。 +L1トークンアドレスを指定する必要はないことに注意してください。 +L2トークンは、L1の同等のアドレスを教えてくれることが期待されています。 ```solidity /** - * @dev Performs the logic for withdrawals by burning the token and informing - * the L1 token Gateway of the withdrawal. - * @param _l2Token Address of L2 token where withdrawal is initiated. - * @param _from Account to pull the withdrawal from on L2. - * @param _to Account to give the withdrawal to on L1. - * @param _amount Amount of the token to withdraw. - * @param _l1Gas Unused, but included for potential forward compatibility considerations. - * @param _data Optional data to forward to L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev トークンをバーンし、L1トークンゲートウェイに引き出しを通知することで、引き出しのロジックを実行します。 + * @param _l2Token 引き出しが開始されたL2トークンのアドレス。 + * @param _from L2で引き出しを引き出すアカウント。 + * @param _to L1で引き出しを与えるアカウント。 + * @param _amount 引き出すトークンの量。 + * @param _l1Gas 未使用ですが、将来の互換性の考慮事項のために含まれています。 + * @param _data L1に転送するオプションのデータ。このデータは、外部コントラクトの便宜のためにのみ提供されます。 + * 最大長を強制する以外、これらのコントラクトはその内容について何の保証も提供しません。 */ function _initiateWithdrawal( address _l2Token, @@ -1132,17 +1180,16 @@ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { uint32 _l1Gas, bytes calldata _data ) internal { - // When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L2 - // usage + // 引き出しが開始されると、その後のL2での使用を防ぐために、引き出し人の資金をバーンします。 // slither-disable-next-line reentrancy-events IL2StandardERC20(_l2Token).burn(msg.sender, _amount); ``` -`_from`パラメータに依存_するのではなく_、`msg.sender`を使用する点に注意してください。これにより、偽造がより困難になります(私の知る限り、不可能です)。 +`_from`パラメータに依存するのではなく、偽造するのがはるかに難しい(私の知る限り不可能) `msg.sender`に依存していることに注意してください。 ```solidity - // Construct calldata for l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) + // l1TokenBridge.finalizeERC20Withdrawal(_to, _amount)のcalldataを構築 // slither-disable-next-line reentrancy-events address l1Token = IL2StandardERC20(_l2Token).l1Token(); bytes memory message; @@ -1150,7 +1197,7 @@ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { ``` -L1では、ETHとERC-20トークンを区別する必要があります。 +L1では、ETHとERC-20を区別する必要があります。 ```solidity message = abi.encodeWithSelector( @@ -1172,7 +1219,7 @@ L1では、ETHとERC-20トークンを区別する必要があります。 ); } - // Send message up to L1 bridge + // メッセージをL1ブリッジに送信 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l1TokenBridge, _l1Gas, message); @@ -1181,7 +1228,7 @@ L1では、ETHとERC-20トークンを区別する必要があります。 } /************************************ - * Cross-chain Function: Depositing * + * クロスチェーン関数: デポジット * ************************************/ /** @@ -1196,69 +1243,66 @@ L1では、ETHとERC-20トークンを区別する必要があります。 bytes calldata _data ``` -この関数は、`L1StandardBridge`が呼び出します。 +この関数は`L1StandardBridge`によって呼び出されます。 ```solidity ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) { ``` -メッセージの発信元が適切であることを確認します。 この関数は、`_mint`を呼び出すものであり、L1上でブリッジが所有するトークンに含まれないトークンを提供するために使用できるため、重要です。 +メッセージのソースが正当であることを確認してください。 +この関数は`_mint`を呼び出し、ブリッジがL1で所有するトークンでカバーされていないトークンを与えるために使用できるため、これは重要です。 ```solidity - // Check the target token is compliant and - // verify the deposited token on L1 matches the L2 deposited token representation here + // ターゲットトークンが準拠していることを確認し、 + // L1でデポジットされたトークンがここのL2デポジットトークン表現と一致することを検証します。 if ( // slither-disable-next-line reentrancy-events ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) && _l1Token == IL2StandardERC20(_l2Token).l1Token() ``` -以下の健全性チェックを実行してください: +サニティチェック: -1. 正しいインターフェースがサポートされていること。 -2. L2上のERC-20コントラクトにおけるL1アドレスが、L1上のトークンソースと一致すること。 +1. 正しいインターフェースがサポートされていること +2. L2 ERC-20コントラクトのL1アドレスが、トークンのL1ソースと一致すること ```solidity ) { - // When a deposit is finalized, we credit the account on L2 with the same amount of - // tokens. + // デポジットが完了すると、L2のアカウントに同額のトークンを入金します。 // slither-disable-next-line reentrancy-events IL2StandardERC20(_l2Token).mint(_to, _amount); // slither-disable-next-line reentrancy-events emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); ``` -健全性チェックに合格したら、入金を確定します。 +サニティチェックに合格した場合、デポジットを完了します: 1. トークンをミントします。 2. 適切なイベントを発行します。 ```solidity } else { - // Either the L2 token which is being deposited-into disagrees about the correct address - // of its L1 token, or does not support the correct interface. - // This should only happen if there is a malicious L2 token, or if a user somehow - // specified the wrong L2 token address to deposit into. - // In either case, we stop the process here and construct a withdrawal - // message so that users can get their funds out in some cases. - // There is no way to prevent malicious token contracts altogether, but this does limit - // user error and mitigate some forms of malicious contract behavior. + // デポジット先のL2トークンが、そのL1トークンの正しいアドレスについて同意しないか、正しいインターフェースをサポートしていないかのいずれかです。 + // これは、悪意のあるL2トークンがある場合、またはユーザーが何らかの方法でデポジット先の間違ったL2トークンアドレスを指定した場合にのみ発生するはずです。 + // いずれの場合も、ここでプロセスを停止し、引き出しメッセージを構築して、ユーザーが場合によっては資金を取り出せるようにします。 + // 悪意のあるトークンコントラクトを完全に防ぐ方法はありませんが、これによりユーザーエラーが制限され、悪意のあるコントラクトの動作のいくつかの形態が軽減されます。 ``` -L2のトークンアドレスが間違っており、検知可能なエラーが発生した場合、入金をキャンセルし、トークンをL1に返却する必要があります。 これをL2から実行する唯一の方法は、不正申し立て期間が経過してからメッセージを送信することですが、それでも、トークンを永遠に失うよりは、ユーザーにとってははるかにベターな選択でしょう。 +ユーザーが間違ったL2トークンアドレスを使用して検出可能なエラーを犯した場合、デポジットをキャンセルしてL1でトークンを返したいです。 +これをL2から行う唯一の方法は、フォールトチャレンジ期間を待つ必要があるメッセージを送信することですが、それはユーザーがトークンを永久に失うよりもはるかに良いです。 ```solidity bytes memory message = abi.encodeWithSelector( IL1ERC20Bridge.finalizeERC20Withdrawal.selector, _l1Token, _l2Token, - _to, // switched the _to and _from here to bounce back the deposit to the sender + _to, // ここで_toと_fromを切り替えて、デポジットを送信者に跳ね返す _from, _amount, _data ); - // Send message up to L1 bridge + // メッセージをL1ブリッジに送信 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l1TokenBridge, 0, message); // slither-disable-next-line reentrancy-events @@ -1268,10 +1312,15 @@ L2のトークンアドレスが間違っており、検知可能なエラーが } ``` -## まとめ {#conclusion} +## 結論 {#conclusion} + +標準ブリッジは、資産転送のための最も柔軟なメカニズムです。 +しかし、非常に汎用的であるため、必ずしも最も使いやすいメカニズムではありません。 +特に、出金に関しては、ほとんどのユーザーが、チャレンジ期間を待つ必要がなく、出金をファイナライズするためにマークル証明を必要としない[サードパーティ製ブリッジ](https://optimism.io/apps#bridge)を使用することを好みます。 -標準ブリッジは、アセットを移転する上で最も柔軟なメカニズムです。 しかし、汎用性が高いため、必ずしも使いやすいメカニズムではない場合もあります。 特に出金については、大部分のユーザーは、異議申し立て期間が存在せず、出金を最終確認するためにMerkleプルーフを必要としない[サードパーティーのブリッジ](https://optimism.io/apps#bridge)を使いたいと考えるでしょう。 +これらのブリッジは通常、L1に資産を持ち、それを少額の手数料(多くの場合、標準ブリッジの引き出しのガス代よりも安い)ですぐに提供することで機能します。 +ブリッジ(またはそれを運営する人々)がL1の資産が不足すると予想する場合、L2から十分な資産を転送します。 これらは非常に大きな引き出しであるため、引き出しコストは多額にわたって償却され、はるかに小さい割合になります。 -サードパーティのブリッジは通常、少額の手数料(標準ブリッジの出金にかかるガス代よりも安価な場合が多いです)で、L1上でアセットを保持する機能を提供します。 ブリッジ(または、ブリッジを稼働するスタッフ)がL1上での資産不足を予想する場合、L2から必要な資産が移転されます。 これらは非常に大規模な出金になるため、出金コストは高額を対象として償却され、手数料が全体に占める割合が非常に低くなります。 +この記事が、レイヤー2の仕組みと、明確で安全なSolidityコードの書き方について、より理解を深めるのに役立ったことを願っています。 -この記事が、レイヤー2の仕組みと、明確で安全なSolidityコードの書き方について、より理解を深めることに役立つことを願っています。 +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/reverse-engineering-a-contract/index.md b/public/content/translations/ja/developers/tutorials/reverse-engineering-a-contract/index.md index 3b8dd029780..de70fe292f9 100644 --- a/public/content/translations/ja/developers/tutorials/reverse-engineering-a-contract/index.md +++ b/public/content/translations/ja/developers/tutorials/reverse-engineering-a-contract/index.md @@ -1,40 +1,38 @@ --- title: "コントラクトのリバースエンジニアリング" -description: ソースコードがない場合にコントラクトを理解する方法 +description: "ソースコードがない場合にコントラクトを理解する方法" author: Ori Pomerantz lang: ja -tags: - - "イーサリアム仮想マシン(EVM)" - - "オペコード" +tags: [ "evm", "オペコード" ] skill: advanced published: 2021-12-30 --- ## はじめに {#introduction} -_ブロックチェーン上に秘密はありません。_ブロックチェーン上で起こる全てのことは、一貫性があり、検証可能で、公開されています。 理想的には、[コントラクトはEtherscanで公開され、検証されたソースコードであるべきです](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code)。 しかし、[必ずしもそうとは限りません](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code)。 この記事では、ソースコード([`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f))を見ることなくコントラクトをリバースエンジニアリングする方法を学びます。 +_ブロックチェーン上に秘密はありません_。ブロックチェーン上で起こる全てのことは、一貫性があり、検証可能で、公開されています。 理想的には、[コントラクトのソースコードはEtherscanで公開・検証されるべきです](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code)。 しかし、[必ずしもそうとは限りません](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code)。 この記事では、ソースコードがないコントラクト[`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f)を調べることで、コントラクトをリバースエンジニアリングする方法を学びます。 -リバースコンパイラを使用しても、必ず[利用可能な結果](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f)が生成されるわけではありません。 この記事では、手動でリバースエンジニアリングを実行し、[オペコード](https://github.com/wolflo/evm-opcodes)からコントラクトを理解する方法を学びます。また、デコンパイラによる結果を理解する方法も学びます。 +逆コンパイラは存在しますが、必ずしも[利用可能な結果](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f)が得られるとは限りません。 この記事では、手動でリバースエンジニアリングを行い、[オペコード](https://github.com/wolflo/evm-opcodes)からコントラクトを理解する方法と、デコンパイラの結果を解釈する方法を学びます。 -この記事を理解するには、イーサリアム仮想マシン(EVM)の基礎を理解しており、イーサリアム仮想マシン(EVM)アセンブラにある程度精通している必要があります。 [イーサリアム仮想マシン(EVM)に関するトピックについてはこちら](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e)をご覧ください。 +この記事を理解するには、イーサリアム仮想マシン(EVM)の基礎を理解しており、EVMアセンブラにある程度精通している必要があります。 [これらのトピックについてはこちらをお読みください](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e)。 ## 実行可能コードの準備 {#prepare-the-executable-code} -Etherscanにアクセスするとコントラクトのオペコードを入手できます。「**Contract**」タブをクリックし、次に「**Switch to Opcodes View**」をクリックしてください。 これで一行ずつオペコートが表示されます。 +コントラクトのEtherscanページにアクセスし、**Contract**タブをクリックしてから**Switch to Opcodes View**をクリックすると、オペコードを取得できます。 1行に1つのオペコードが表示されるビューになります。 -![Etherscanでのオペコードの表示](opcode-view.png) +![Etherscanのオペコードビュー](opcode-view.png) -ジャンプ (JUMP) を理解するには、コード内のオペコードの場所を理解する必要があります。 そのための1つの方法は、Googleスプレッドシートを開き、オペコードをC列に貼り付けることです。[既に準備されているこのスプレッドシートをコピーすれば、次のステップをスキップできます](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing)。 +ただし、ジャンプを理解するには、コード内の各オペコードの場所を知る必要があります。 そのためには、Googleスプレッドシートを開き、オペコードをC列に貼り付けるという方法があります。[こちらの準備済みのスプレッドシートのコピーを作成すれば、次の手順をスキップできます](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing)。 -次のステップは、正しいコードの位置を取得することです。これで、ジャンプを理解できるようになります。 オペコードのサイズをB列に、場所(16進数)をA列に配置します。`B1`セルに以下の関数を入力し、それをB列の残りの部分にコードの最終行までコピーアンドペーストします。 これを行った後、B列を非表示にできます。 +次のステップでは、ジャンプを理解できるように、正しいコードのロケーションを取得します。 B列にオペコードサイズを、A列に(16進数の)ロケーションを入れます。セル`B1`にこの関数を入力し、コードの最後までB列の残りのセルにコピー&ペーストします。 これを行った後、B列を非表示にできます。 ``` =1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0) ``` -この関数は、まず1バイトをオペコード自体に追加し、次に`PUSH`を探します。 PUSHは特殊なオペコードであり、プッシュされる値用に追加のバイトを保持する必要があります。 オペコードが`PUSH`の場合、バイト数を抽出して加算します。 +まず、この関数はオペコード自体のために1バイトを追加し、次に`PUSH`を探します。 プッシュオペコードは特殊で、プッシュされる値用に追加のバイトを保持する必要があります。 オペコードが`PUSH`の場合、バイト数を抽出して加算します。 -`A1`に最初のオフセットである0を配置します。 次に`A2`に下記の関数を配置し、A列の残りの部分にコピーアンドペーストします。 +`A1`に最初のオフセットであるゼロを配置します。 次に、`A2`にこの関数を配置し、A列の残りの部分にコピーアンドペーストします。 ``` =dec2hex(hex2dec(A1)+B1) @@ -42,112 +40,112 @@ Etherscanにアクセスするとコントラクトのオペコードを入手 ジャンプ(`JUMP`と`JUMPI`)の前にプッシュされる値は16進数で渡されるため、この関数で16進数の値を得る必要があります。 -## エントリーポイント(0x00) {#the-entry-point-0x00} +## エントリーポイント (0x00) {#the-entry-point-0x00} コントラクトは、必ず最初のバイトから実行されます。 以下は、コードの冒頭部分です。 -| オフセット | オペコード | スタック(オペコードの後) | -| -----:| ------------ | ------------------- | -| 0 | PUSH1 0x80 | 0x80 | -| 2 | PUSH1 0x40 | 0x40, 0x80 | -| 4 | MSTORE | なし | -| 5 | PUSH1 0x04 | 0x04 | -| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | -| 8 | LT | CALLDATASIZE\<4 | -| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE\<4 | -| C | JUMPI | なし | +| オフセット | オペコード | スタック(オペコードの後) | +| ----: | ------------ | -------------------------------------------- | +| 0 | PUSH1 0x80 | 0x80 | +| 2 | PUSH1 0x40 | 0x40, 0x80 | +| 4 | MSTORE | なし | +| 5 | PUSH1 0x04 | 0x04 | +| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | +| 8 | LT | CALLDATASIZE<4 | +| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE<4 | +| C | JUMPI | なし | このコードは、次の2つのことをしています。 -1. メモリロケーションの0x40~0x5Fへ、32バイト値として0x80を書き込みます(0x5Fに0x80が格納され、0x40~0x5Eはすべてゼロになります)。 -2. コールデータサイズ(CALLDATASIZE)を読み取ります。 通常、イーサリアムコントラクトのコールデータは、[アプリケーションバイナリインターフェース(ABI)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html)に従います。アプリケーションバイナリインターフェース(ABI)では、最低でも4バイトが関数セレクタに必要です。 コールデータのサイズが4未満の場合、0x5Eへジャンプします。 +1. メモリロケーションの0x40~0x5Fへ、32バイト値として0x80を書き込みます(0x5Fに0x80が格納され、0x40~0x5Eはすべてゼロになります)。 +2. コールデータサイズを読み取ります。 通常、イーサリアムコントラクトのコールデータは[ABI (アプリケーションバイナリインターフェース)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html)に従います。これには、関数セレクタ用に最低4バイトが必要です。 コールデータのサイズが4未満の場合、0x5Eへジャンプします。 ![この部分のフローチャート](flowchart-entry.png) -### 0x5Eのハンドラ(非アプリケーションバイナリインターフェース(ABI)コールデータの処理) {#the-handler-at-0x5e-for-non-abi-call-data} +### 0x5Eのハンドラ (非ABIコールデータ用) {#the-handler-at-0x5e-for-non-abi-call-data} | オフセット | オペコード | -| -----:| ------------ | +| ----: | ------------ | | 5E | JUMPDEST | -| 5F | CALLDATASIZE | +| 5E | CALLDATASIZE | | 60 | PUSH2 0x007c | | 63 | JUMPI | -このスニペットは、`JUMPDEST`で始まります。 イーサリアム仮想マシン(EVM)プログラムは、`JUMPDEST`ではないオペコードにジャンプした場合に例外を投げます。 次に、CALLDATASIZEを確認し、それが「true」の場合(ゼロではない場合)、0x7Cにジャンプします。 これについては後述します。 +このスニペットは、JUMPDESTで始まります。 イーサリアム仮想マシン(EVM)プログラムは、`JUMPDEST`ではないオペコードにジャンプした場合に例外を投げます。 次に、CALLDATASIZEを確認し、それが「true」の場合(ゼロではない場合)、0x7Cにジャンプします。 これについては後述します。 -| オフセット | オペコード | スタック(オペコードの後) | -| -----:| ---------- | --------------------------------------------------------------------- | -| 64 | CALLVALUE | 呼び出しによって[Wei](/glossary/#wei)が提供されます。 Solidityでは、`msg.value`が呼び出されます。 | -| 65 | PUSH1 0x06 | 6 CALLVALUE | -| 67 | PUSH1 0x00 | 0 6 CALLVALUE | -| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | -| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | -| 6B | SLOAD | Storage[6] CALLVALUE 0 6 CALLVALUE | +| オフセット | オペコード | スタック(オペコードの後) | +| ----: | ---------- | -------------------------------------------------------------------------------------- | +| 64 | CALLVALUE | 呼び出しによって提供された[Wei](/glossary/#wei)。 Solidityでは`msg.value`と呼ばれます | +| 65 | PUSH1 0x06 | 6 CALLVALUE | +| 67 | PUSH1 0x00 | 0 6 CALLVALUE | +| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | +| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | +| 6B | SLOAD | Storage[6] CALLVALUE 0 6 CALLVALUE | -コールデータがない場合、Storage[6]の値を読み取ります。 このStorage[6] の値はまだわかりませんが、コールデータなしで受信したコントラクトのトランザクションを探すことはできます。 コールデータなしで(つまりメソッドなしで)ETHを送金するだけのトランザクションの場合、Etherscanに`Transfer`メソッドがあります。 実際、[コントラクトが受信した最初のトランザクション](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7)は、送金(transfer)です。 +コールデータがない場合、Storage[6]の値を読み取ります。 このStorage[6]の値はまだわかりませんが、コールデータなしで受信したコントラクトのトランザクションを探すことはできます。 コールデータなしで(つまりメソッドなしで)ETHを送金するだけのトランザクションの場合、Etherscanに`Transfer`メソッドがあります。 実際、[コントラクトが受け取った最初のトランザクション](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7)は送金です。 -このトランザクションを調べるには、「**Click to show more**」をクリックします。コールデータ(Input Dataと表示される)が実際に空(`0x`)であることが分かります。 また、値が1.559ETHであることに留意してください。これに関しては、後述します。 +このトランザクションを調べ、「**Click to see More**」をクリックすると、コールデータ(インプットデータ)が実際に空(`0x`)であることが分かります。 また、値が1.559ETHであることに留意してください。これに関しては、後述します。 -![コールデータが空](calldata-empty.png) +![コールデータが空である](calldata-empty.png) -次に「**State**」タブをクリックし、リバースエンジニアリングしているコントラクト(0x2510...)を展開します。 トランザクション中に `Storage[6]` が変更されたことが分かります。「Hex」から「**Number**」に変更すると、次のコントラクトの値に応じてweiに変換された値、1,559,000,000,000,000,000になります(分かりやすくするためにコンマを追加しています) 。 +次に、**State**タブをクリックし、リバースエンジニアリングしているコントラクト(0x2510...)を展開します。 トランザクション中に`Storage[6]`が変更されたことが分かります。「Hex」から**Number**に変更すると、次のコントラクトの値に応じてweiに変換された値、1,559,000,000,000,000,000になります(分かりやすくするためにコンマを追加しています)。 ![Storage[6]の変化](storage6.png) -[同時期の他の`Transfer`トランザクション](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange)によって引き起こされた状態変更を確認すると、`Storage[6]`がしばらくの間、コントラクトの値を追跡していたことが分かります。 これを今から`Value*`と呼びます。 アスタリスク (`*`) は、この変数が何をするかまだ_分からない_ことを思い起こさせます。しかし、コントラクトの値を追跡するだけのものではありません。`ADDRESS BALANCE`を使用してアカウント残高を取得できる場合には、非常に高価なストレージを使う必要がないからです。 最初のオペコードは、コントラクト自体のアドレスをプッシュ(PUSH)します。 2番目のオペコードは、スタックの上部にあるアドレスを読み込み、そのアドレスの残高で置き換えます。 +[同期間の他の`Transfer`トランザクション](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange)によるステートの変更を見ると、`Storage[6]`がしばらくの間コントラクトの値を追跡していたことがわかります。 これを今から`Value*`と呼びます。 アスタリスク(`*`)は、この変数がまだ何をするかわからないことを示しています。しかし、これは単にコントラクトの値を追跡するためだけのものではありません。なぜなら、`ADDRESS BALANCE`を使ってアカウントの残高を取得できるのに、非常に高価なストレージを使う必要はないからです。 最初のオペコードは、コントラクト自体のアドレスをプッシュします。 2番目のオペコードは、スタックの上部にあるアドレスを読み込み、そのアドレスの残高で置き換えます。 -| オフセット | オペコード | スタック | -| -----:| ------------ | --------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | ------------ | ------------------------------------------- | | 6C | PUSH2 0x0075 | 0x75 Value\* CALLVALUE 0 6 CALLVALUE | | 6F | SWAP2 | CALLVALUE Value\* 0x75 0 6 CALLVALUE | | 70 | SWAP1 | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 71 | PUSH2 0x01a7 | 0x01A7 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 74 | JUMP | | +| 74 | JUMP | | -ジャンプ先(JUMPDEST)でも、このコードのトレースを続けます。 +ジャンプ先でも、このコードのトレースを続けます。 -| オフセット | オペコード | スタック | -| -----:| ---------- | ------------------------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | ---------- | ----------------------------------------------------------- | | 1A7 | JUMPDEST | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1A8 | PUSH1 0x00 | 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1AA | DUP3 | CALLVALUE 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1AB | NOT | 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -`NOT`は、ビットごとのNOTであるため、コール値内の全てのビット値を反転します。 +`NOT`はビットごとの演算であるため、コール値内の全てのビット値を反転します。 -| オフセット | オペコード | スタック | -| -----:| ------------ | ------------------------------------------------------------------------------- | -| 1AC | DUP3 | Value\* 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AD | GT | Value\*>2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AE | ISZERO | Value\*\<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AF | PUSH2 0x01df | 0x01DF Value\*\<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1B2 | JUMPI | | +| オフセット | オペコード | スタック | +| ----: | ------------ | ---------------------------------------------------------------------------------------------------- | +| 1AC | DUP3 | Value\* 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AD | GT | Value\*>2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AE | ISZERO | Value\*<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AF | PUSH2 0x01df | 0x01DF Value\*<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1B2 | JUMPI | | -`Value*`の値が、2^256-CALLVALUE-1以下の場合にジャンプ(JUMP)します。 これは、オーバーフローを防ぐためのロジックに見えます。 実際に、オフセット0X01DEでいくつかの無意味な操作(例: 削除される寸前にメモリへの書き込み)をした後、オーバーフローが検出されると、標準の動作としてコントラクトが元に戻されます。 +`Value*`の値が、2^256-CALLVALUE-1以下の場合にジャンプします。 これは、オーバーフローを防ぐためのロジックに見えます。 実際に、オフセット0x01DEでいくつかの無意味な操作(例: 削除される寸前にメモリへの書き込み)をした後、オーバーフローが検出されると、標準の動作としてコントラクトが元に戻されます。 このようなオーバーフローが発生する可能性は、非常に低いことに留意してください。これは、コール値に`Value*`を加えたものが、2^256 wei(約10^59 ETH)と同等になる必要があるためです。 [ETHの総供給量は、この記事の執筆時点で2億未満です](https://etherscan.io/stat/supply)。 -| オフセット | オペコード | スタック | -| -----:| -------- | ------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | -------- | ----------------------------------------- | | 1DF | JUMPDEST | 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1E0 | POP | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1E1 | ADD | Value\*+CALLVALUE 0x75 0 6 CALLVALUE | | 1E2 | SWAP1 | 0x75 Value\*+CALLVALUE 0 6 CALLVALUE | -| 1E3 | JUMP | | +| 1E3 | JUMP | | ここに到達した場合、`Value* + CALLVALUE`を取得して、オフセット0x75へジャンプします。 -| オフセット | オペコード | スタック | -| -----:| -------- | --------------------------------- | +| オフセット | オペコード | スタック | +| ----: | -------- | ------------------------------- | | 75 | JUMPDEST | Value\*+CALLVALUE 0 6 CALLVALUE | | 76 | SWAP1 | 0 Value\*+CALLVALUE 6 CALLVALUE | | 77 | SWAP2 | 6 Value\*+CALLVALUE 0 CALLVALUE | -| 78 | SSTORE | 0 CALLVALUE | +| 78 | SSTORE | 0 CALLVALUE | ここに到達した場合(コールデータが空である必要があります)、`Value*`にコール値を加えます。 これは、`Transfer`トランザクションが行うことと一致しています。 | オフセット | オペコード | -| -----:| ----- | +| ----: | ----- | | 79 | POP | | 7A | POP | | 7B | STOP | @@ -156,7 +154,7 @@ Etherscanにアクセスするとコントラクトのオペコードを入手 まとめると、最初のコードのフローチャートは次のようになります。 -![エントリポイントフローチャート](flowchart-entry.png) +![エントリーポイントのフローチャート](flowchart-entry.png) ## 0x7Cのハンドラ {#the-handler-at-0x7c} @@ -164,71 +162,71 @@ Etherscanにアクセスするとコントラクトのオペコードを入手 ここへ到達するのは、次のような場合です。 -- (オフセット0x63から)1バイト、2バイトまたは3バイトのコールデータががある場合 +- (オフセット0x63から)1バイト、2バイトまたは3バイトのコールデータがある場合 - (オフセット0x42と0x5Dから)メソッドのシグネチャが不明な場合 -| オフセット | オペコード | スタック | -| -----:| ------------ | -------------------- | -| 7C | JUMPDEST | | -| 7D | PUSH1 0x00 | 0x00 | -| 7F | PUSH2 0x009d | 0x9D 0x00 | -| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | +| オフセット | オペコード | スタック | +| ----: | ------------ | ------------------------------------------------------------------------ | +| 7C | JUMPDEST | | +| 7D | PUSH1 0x00 | 0x00 | +| 7F | PUSH2 0x009d | 0x9D 0x00 | +| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | | 84 | SLOAD | Storage[3] 0x9D 0x00 | これは別のストレージセルです。このセルはどのトランザクションにも見つからなかったので、これが何を意味しているのか理解するのは困難です。 しかし、以下のコードがこれを明確にします。 -| オフセット | オペコード | スタック | -| -----:| ------------------------------------------------- | ------------------------------- | +| オフセット | オペコード | スタック | +| ----: | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | 85 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xff....ff Storage[3] 0x9D 0x00 | -| 9A | AND | Storage[3]-as-address 0x9D 0x00 | +| 9A | AND | Storage[3]-as-address 0x9D 0x00 | これらのオペコードは、Storage[3]から読み取った値を160ビットに切り捨てています。これは、イーサリアムアドレスの長さです。 -| オフセット | オペコード | スタック | -| -----:| ----- | ------------------------------- | +| オフセット | オペコード | スタック | +| ----: | ----- | ----------------------------------------------------------------------------------- | | 9B | SWAP1 | 0x9D Storage[3]-as-address 0x00 | | 9C | JUMP | Storage[3]-as-address 0x00 | -次のオペコードに進むため、このジャンプ(JUMP)は不要です。 このコードは、ガス効率が良くありません。 +次のオペコードに進むため、このジャンプは不要です。 このコードは、ガス効率が良くありません。 -| オフセット | オペコード | スタック | -| -----:| ---------- | ------------------------------- | -| 9D | JUMPDEST | Storage[3]-as-address 0x00 | -| 9E | SWAP1 | 0x00 Storage[3]-as-address | -| 9F | POP | Storage[3]-as-address | -| A0 | PUSH1 0x40 | 0x40 Storage[3]-as-address | +| オフセット | オペコード | スタック | +| ----: | ---------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| 9D | JUMPDEST | Storage[3]-as-address 0x00 | +| 9E | SWAP1 | 0x00 Storage[3]-as-address | +| 9F | POP | Storage[3]-as-address | +| A0 | PUSH1 0x40 | 0x40 Storage[3]-as-address | | A2 | MLOAD | Mem[0x40] Storage[3]-as-address | コードの最初で、Mem[0x40]を0x80に設定しました。 その後の0x40を確かめると変更していないことが分かります。つまり、これは0x80であると推測できます。 -| オフセット | オペコード | スタック | -| -----:| ------------ | ------------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | ------------ | ----------------------------------------------------------------------------------------------------- | | A3 | CALLDATASIZE | CALLDATASIZE 0x80 Storage[3]-as-address | | A4 | PUSH1 0x00 | 0x00 CALLDATASIZE 0x80 Storage[3]-as-address | | A6 | DUP3 | 0x80 0x00 CALLDATASIZE 0x80 Storage[3]-as-address | | A7 | CALLDATACOPY | 0x80 Storage[3]-as-address | -0x80から始まるすべてのコールデータ(CALLDATA)をメモリにコピーします。 +0x80から始まるすべてのコールデータをメモリにコピーします。 -| オフセット | オペコード | スタック | -| -----:| ------------- | -------------------------------------------------------------------------------- | -| A8 | PUSH1 0x00 | 0x00 0x80 Storage[3]-as-address | -| AA | DUP1 | 0x00 0x00 0x80 Storage[3]-as-address | -| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AD | DUP6 | Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AE | GAS | GAS Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AF | DELEGATE_CALL | | +| オフセット | オペコード | スタック | +| ----: | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| A8 | PUSH1 0x00 | 0x00 0x80 Storage[3]-as-address | +| AA | DUP1 | 0x00 0x00 0x80 Storage[3]-as-address | +| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | +| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | +| AD | DUP6 | Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | +| AE | GAS | GAS Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | +| AF | DELEGATE_CALL | | -これで、かなり明確になりました。 このコントラクトは、[プロキシ](https://blog.openzeppelin.com/proxy-patterns/)として機能しており、Storage[3] 内のアドレスを呼び出して、実際の作業をしています。 `DELEGATE_CALL`は、別のコントラクトを呼び出しますが、同じストレージ内に留まります。 つまり、委任されたコントラクト(現在のコントラクトがこのコントラクトのプロキシとなります)が、同じストレージスペースにアクセスします。 コール(呼び出し)のパラメータは次の通りです。 +これで、かなり明確になりました。 このコントラクトは[プロキシ](https://blog.openzeppelin.com/proxy-patterns/)として機能しており、Storage[3] 内のアドレスを呼び出して、実際の作業をしています。 `DELEGATE_CALL`は、別のコントラクトを呼び出しますが、同じストレージ内に留まります。 つまり、委任されたコントラクト(現在のコントラクトがこのコントラクトのプロキシとなります)が、同じストレージスペースにアクセスします。 コール(呼び出し)のパラメータは次の通りです。 -- _ガス_: 残りのガス -- _呼び出されたアドレス_: Storage[3]-as-address +- _ガス_: 残りの全ガス +- _呼び出し先アドレス_: Storage[3]-as-address - _コールデータ_: 元のコールデータを配置する、0x80から始まるCALLDATASIZEバイト - _リターンデータ_: なし(0x00 - 0x00)。リターンデータは他の方法で取得(以下を参照) -| オフセット | オペコード | スタック | -| -----:| -------------- | --------------------------------------------------------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | B0 | RETURNDATASIZE | RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | B1 | DUP1 | RETURNDATASIZE RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | B2 | PUSH1 0x00 | 0x00 RETURNDATASIZE RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | @@ -237,73 +235,73 @@ Etherscanにアクセスするとコントラクトのオペコードを入手 ここでは、すべてのリターンデータを0x80から始まるメモリバッファにコピーします。 -| オフセット | オペコード | スタック | -| -----:| ------------ | ---------------------------------------------------------------------------------------------------------------------------- | -| B6 | DUP2 | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | +| オフセット | オペコード | スタック | +| ----: | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B6 | DUP2 | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | B7 | DUP1 | (((call success/failure))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | B8 | ISZERO | (((did the call fail))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | B9 | PUSH2 0x00c0 | 0xC0 (((did the call fail))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BC | JUMPI | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BD | DUP2 | RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BE | DUP5 | 0x80 RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BF | RETURN | | +| BC | JUMPI | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | +| BD | DUP2 | RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | +| BE | DUP5 | 0x80 RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | +| BF | RETURN | | -リターンデータをバッファ(0x80~0x80+RETURNDATASIZE)にコピーする呼び出しの後、その呼び出しが成功した場合は、そのバッファを正確に返します(`RETURN`します)。 +リターンデータをバッファ(0x80~0x80+RETURNDATASIZE)にコピーする呼び出しの後、その呼び出しが成功した場合は、そのバッファを正確に返します。 -### DELEGATECALLの失敗 {#delegatecall-failed} +### DELEGATECALL失敗 {#delegatecall-failed} 0xC0に到達した場合は、現在のコントラクトが呼び出したコントラクトが、元に戻された(REVERTされた)ことを意味します。 現在のコントラクトはこのコントラクトの単なるプロキシであるため、現在のコントラクトも同じデータを返して、元に戻す(REVERTする)必要があります。 -| オフセット | オペコード | スタック | -| -----:| -------- | ------------------------------------------------------------------------------------------------------------------- | +| オフセット | オペコード | スタック | +| ----: | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | C0 | JUMPDEST | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | C1 | DUP2 | RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | | C2 | DUP5 | 0x80 RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| C3 | REVERT | | +| C3 | REVERT | | -そのため、前に`RETURN`で使用した同じバッファ(0x80~0x80+RETURNDATASIZE)を元に戻します(`REVERT`します)。 +そのため、前に`RETURN`で使用した同じバッファ(0x80~0x80+RETURNDATASIZE)を元に戻します(REVERTします)。 -![プロキシするための呼び出しのフローチャート](flowchart-proxy.png) +![プロキシへのコールに関するフローチャート](flowchart-proxy.png) -## アプリケーションバイナリインターフェース(ABI)呼び出し {#abi-calls} +## ABIコール {#abi-calls} -コールデータのサイズが4バイト以上である場合、有効なアプリケーションバイナリインターフェース(ABI)呼び出しである可能性があります。 +コールデータのサイズが4バイト以上である場合、有効なABIコールである可能性があります。 -| オフセット | オペコード | スタック | -| -----:| ------------ | ------------------------------------------------- | -| D | PUSH1 0x00 | 0x00 | -| F | CALLDATALOAD | (((First word (256 bits) of the call data))) | -| 10 | PUSH1 0xe0 | 0xE0 (((First word (256 bits) of the call data))) | -| 12 | SHR | (((first 32 bits (4 bytes) of the call data))) | +| オフセット | オペコード | スタック | +| ----: | ------------ | ------------------------------------------------------------------------------------------------------------- | +| D | PUSH1 0x00 | 0x00 | +| F | CALLDATALOAD | (((コールデータの最初のワード (256ビット)))) | +| 10 | PUSH1 0xe0 | 0xE0 (((コールデータの最初のワード (256ビット)))) | +| 12 | SHR | (((コールデータの最初の32ビット (4バイト)))) | -Etherscanでは、`1C`が未知のオペコードとなっていますが、これは[Etherscanでこの機能が作成された後に追加された](https://eips.ethereum.org/EIPS/eip-145)ため、まだ反映がされていないのが理由です。 [最新のオペコードテーブル](https://github.com/wolflo/evm-opcodes)では、これが右シフトであることが示されています +Etherscanによると`1C`は未知のオペコードですが、これは[Etherscanがこの機能を実装した後に追加された](https://eips.ethereum.org/EIPS/eip-145)もので、まだ更新されていないためです。 [最新のオペコード表](https://github.com/wolflo/evm-opcodes)を見ると、これが右シフトであることがわかります -| オフセット | オペコード | スタック | -| -----:| ---------------- | -------------------------------------------------------------------------------------------------------- | -| 13 | DUP1 | (((first 32 bits (4 bytes) of the call data))) (((first 32 bits (4 bytes) of the call data))) | -| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((first 32 bits (4 bytes) of the call data))) (((first 32 bits (4 bytes) of the call data))) | -| 19 | GT | 0x3CD8045E>first-32-bits-of-the-call-data (((first 32 bits (4 bytes) of the call data))) | -| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E>first-32-bits-of-the-call-data (((first 32 bits (4 bytes) of the call data))) | -| 1D | JUMPI | (((first 32 bits (4 bytes) of the call data))) | +| オフセット | オペコード | スタック | +| ----: | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 13 | DUP1 | (((コールデータの最初の32ビット (4バイト)))) (((コールデータの最初の32ビット (4バイト)))) | +| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((コールデータの最初の32ビット (4バイト)))) (((コールデータの最初の32ビット (4バイト)))) | +| 19 | GT | 0x3CD8045E>コールデータの最初の32ビット (((コールデータの最初の32ビット (4バイト)))) | +| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E>コールデータの最初の32ビット (((コールデータの最初の32ビット (4バイト)))) | +| 1D | JUMPI | (((コールデータの最初の32ビット (4バイト)))) | -このようにメソッドシグネチャのマッチングテストを2つに分割することで、平均的にテストの半分を節約できます。 その直後のコードと0x43のコードは、コールデータの最初の32ビットの複製(`DUP1`)、プッシュ(`PUSH4 (((method signature>`)、`EQ`を実行し等式を判定、メソッドシグネチャがマッチした場合は`JUMPI`、という同じパターンをたどります。 以下に、メソッドシグネチャ、そのアドレス、既知の場合は[対応するメソッド定義](https://www.4byte.directory/)を示します。 +このようにメソッドシグネチャのマッチングテストを2つに分割することで、平均的にテストの半分を節約できます。 その直後のコードと0x43のコードは、コールデータの最初の32ビットの複製(`DUP1`)、プッシュ(`PUSH4 (((method signature)`)、`EQ`を実行し等式を判定、メソッドシグネチャがマッチした場合は`JUMPI`、という同じパターンをたどります。 以下に、メソッドシグネチャ、そのアドレス、既知の場合は[対応するメソッド定義](https://www.4byte.directory/)を示します。 -| メソッド | メソッドシグネチャ | ジャンプ先のオフセット | -| -------------------------------------------------------------------------------------- | ---------- | ----------- | +| メソッド | メソッドシグネチャ | ジャンプ先のオフセット | +| --------------------------------------------------------------------------------------------------------- | ---------- | ----------- | | [splitter()](https://www.4byte.directory/signatures/?bytes4_signature=0x3cd8045e) | 0x3cd8045e | 0x0103 | -| ??? | 0x81e580d3 | 0x0138 | +| ??? | 0x81e580d3 | 0x0138 | | [currentWindow()](https://www.4byte.directory/signatures/?bytes4_signature=0xba0bafb4) | 0xba0bafb4 | 0x0158 | -| ??? | 0x1f135823 | 0x00C4 | +| ??? | 0x1f135823 | 0x00C4 | | [merkleRoot()](https://www.4byte.directory/signatures/?bytes4_signature=0x2eb4a7ab) | 0x2eb4a7ab | 0x00ED | 一致するものが見つからない場合、そのコードは、現在のコントラクトがプロキシとなっているコントラクトに一致するものがあることを期待して、[0x7Cのプロキシハンドラ](#the-handler-at-0x7c)へジャンプします。 -![アプリケーションバイナリインターフェース(ABI)呼び出しのフローチャート](flowchart-abi.png) +![ABIコールのフローチャート](flowchart-abi.png) ## splitter() {#splitter} | オフセット | オペコード | スタック | -| -----:| ------------ | ----------------------------- | +| ----: | ------------ | ----------------------------- | | 103 | JUMPDEST | | | 104 | CALLVALUE | CALLVALUE | | 105 | DUP1 | CALLVALUE CALLVALUE | @@ -314,38 +312,38 @@ Etherscanでは、`1C`が未知のオペコードとなっていますが、こ | 10D | DUP1 | 0x00 0x00 CALLVALUE | | 10E | REVERT | | -この関数が最初に行うことは、呼び出しがETHを送金していないことを確認することです。 この関数は、[`payable`](https://solidity-by-example.org/payable/)ではありません。 誰かがETHを送金してきた場合は、何かの間違いです。ETHを戻せなくなる前に元に戻す(`REVERT`する)必要があります。 - -| オフセット | オペコード | スタック | -| -----:| ------------------------------------------------- | --------------------------------------------------------------------------- | -| 10F | JUMPDEST | | -| 110 | POP | | -| 111 | PUSH1 0x03 | 0x03 | -| 113 | SLOAD | (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 114 | PUSH1 0x40 | 0x40 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 116 | MLOAD | 0x80 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 12C | SWAP1 | 0x80 0xFF...FF (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 12D | SWAP2 | (((Storage[3] a.k.a the contract for which we are a proxy))) 0xFF...FF 0x80 | -| 12E | AND | ProxyAddr 0x80 | -| 12F | DUP2 | 0x80 ProxyAddr 0x80 | -| 130 | MSTORE | 0x80 | - -0x80にはプロキシアドレスが含まれるようになりました。 +この関数が最初に行うことは、呼び出しがETHを送金していないことを確認することです。 この関数は[`payable`](https://solidity-by-example.org/payable/)ではありません。 誰かがETHを送金してきた場合は、何かの間違いです。ETHを戻せなくなる前に`REVERT`して回避する必要があります。 + +| オフセット | オペコード | スタック | +| ----: | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 10F | JUMPDEST | | +| 110 | POP | | +| 111 | PUSH1 0x03 | 0x03 | +| 113 | SLOAD | (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) | +| 114 | PUSH1 0x40 | 0x40 (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) | +| 116 | MLOAD | 0x80 (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) | +| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) | +| 12C | SWAP1 | 0x80 0xFF...FF (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) | +| 12D | SWAP2 | (((Storage[3]、別名、このコントラクトがプロキシとなっているコントラクト))) 0xFF...FF 0x80 | +| 12E | AND | ProxyAddr 0x80 | +| 12F | DUP2 | 0x80 ProxyAddr 0x80 | +| 130 | MSTORE | 0x80 | + +そして0x80にはプロキシアドレスが含まれるようになりました | オフセット | オペコード | スタック | -| -----:| ------------ | --------- | +| ----: | ------------ | --------- | | 131 | PUSH1 0x20 | 0x20 0x80 | | 133 | ADD | 0xA0 | | 134 | PUSH2 0x00e4 | 0xE4 0xA0 | | 137 | JUMP | 0xA0 | -### E4のコード {#the-e4-code} +### E4コード {#the-e4-code} -これらの行を見るのは初めてですが、他のメソッドと共有されています(以下を参照)。 スタックXの値を呼び出します。`splitter()`で、このXの値が0xA0であることを覚えておいてください。 +これらの行を見るのは初めてですが、他のメソッドと共有されています(以下を参照)。 スタックの値をXと呼びます。`splitter()`で、このXの値が0xA0であることを覚えておいてください。 | オフセット | オペコード | スタック | -| -----:| ---------- | ----------- | +| ----: | ---------- | ----------- | | E4 | JUMPDEST | X | | E5 | PUSH1 0x40 | 0x40 X | | E7 | MLOAD | 0x80 X | @@ -355,7 +353,7 @@ Etherscanでは、`1C`が未知のオペコードとなっていますが、こ | EB | SWAP1 | 0x80 X-0x80 | | EC | RETURN | | -したがって、このコードは、スタック(X)内のメモリのポインターを受け取ります。これにより、コントラクトが0x80~Xまでのバッファを返します(`RETURN`します)。 +したがって、このコードは、スタック(X)内のメモリのポインターを受け取ります。これにより、コントラクトが0x80~Xまでのバッファを`RETURN`します。 `splitter()`の場合、これは現在のコントラクトがプロキシとなっているアドレスを返します。 `RETURN`は、0x80~0x9Fのバッファを返します。これは、このデータを書き込んだ場所です(上記のオフセット0x130)。 @@ -363,22 +361,22 @@ Etherscanでは、`1C`が未知のオペコードとなっていますが、こ オフセット0x158~0x163のコードは、`splitter()`の0x103~0x10Eで見たものと(`JUMPI`の宛先以外は)同一であるため、`currentWindow()`も同様に`payable`ではないことが分かります。 -| オフセット | オペコード | スタック | -| -----:| ------------ | -------------------- | -| 164 | JUMPDEST | | -| 165 | POP | | -| 166 | PUSH2 0x00da | 0xDA | -| 169 | PUSH1 0x01 | 0x01 0xDA | +| オフセット | オペコード | スタック | +| ----: | ------------ | ------------------------------------------------------------------------ | +| 164 | JUMPDEST | | +| 165 | POP | | +| 166 | PUSH2 0x00da | 0xDA | +| 169 | PUSH1 0x01 | 0x01 0xDA | | 16B | SLOAD | Storage[1] 0xDA | | 16C | DUP2 | 0xDA Storage[1] 0xDA | | 16D | JUMP | Storage[1] 0xDA | -### DAのコード {#the-da-code} +### DAコード {#the-da-code} -このコードもまた他のメソッドと共有されます。 スタックYの値を呼び出します。`currentWindow()`で、このYの値がStorage[1]であることを覚えておいてください。 +このコードもまた他のメソッドと共有されます。 スタックの値をYと呼びます。`currentWindow()`で、このYの値がStorage[1]であることを覚えておいてください。 | オフセット | オペコード | スタック | -| -----:| ---------- | ---------------- | +| ----: | ---------- | ---------------- | | DA | JUMPDEST | Y 0xDA | | DB | PUSH1 0x40 | 0x40 Y 0xDA | | DD | MLOAD | 0x80 Y 0xDA | @@ -389,7 +387,7 @@ Etherscanでは、`1C`が未知のオペコードとなっていますが、こ 0x80~0x9FにYを書き込みます。 | オフセット | オペコード | スタック | -| -----:| ---------- | -------------- | +| ----: | ---------- | -------------- | | E1 | PUSH1 0x20 | 0x20 0x80 0xDA | | E3 | ADD | 0xA0 0xDA | @@ -399,184 +397,184 @@ Etherscanでは、`1C`が未知のオペコードとなっていますが、こ オフセット0xED~0xF8のコードは、`splitter()`の0x103~0x10Eで見たものと(`JUMPI`の宛先以外は)同一であるため、`merkleRoot()`も同様に`payable`ではないことが分かります。 -| オフセット | オペコード | スタック | -| -----:| ------------ | -------------------- | -| F9 | JUMPDEST | | -| FA | POP | | -| FB | PUSH2 0x00da | 0xDA | -| FE | PUSH1 0x00 | 0x00 0xDA | +| オフセット | オペコード | スタック | +| ----: | ------------ | ------------------------------------------------------------------------ | +| F9 | JUMPDEST | | +| FA | POP | | +| FB | PUSH2 0x00da | 0xDA | +| FE | PUSH1 0x00 | 0x00 0xDA | | 100 | SLOAD | Storage[0] 0xDA | | 101 | DUP2 | 0xDA Storage[0] 0xDA | | 102 | JUMP | Storage[0] 0xDA | -ジャンプ(JUMP)の後に何が起こるかは、[すでに分かっています](#the-da-code)。 つまり、`merkleRoot()`は、Storage[0]を返します。 +ジャンプの後に何が起こるかは、[すでに分かっています](#the-da-code)。 つまり、`merkleRoot()`は、Storage[0]を返します。 ## 0x81e580d3 {#0x81e580d3} -オフセット0x138~0x143のコードは、`splitter()`の0x103~0x10Eで見たものと(`JUMPI`の宛先以外は)同一であるため、この関数も同様に`payable`ではないことが分かりります。 - -| オフセット | オペコード | スタック | -| -----:| ------------ | ------------------------------------------------------------ | -| 144 | JUMPDEST | | -| 145 | POP | | -| 146 | PUSH2 0x00da | 0xDA | -| 149 | PUSH2 0x0153 | 0x0153 0xDA | -| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | -| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | -| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | -| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | -| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | -| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 197 | SLT | CALLDATASIZE-4\<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +オフセット0x138~0x143のコードは、`splitter()`の0x103~0x10Eで見たものと(`JUMPI`の宛先以外は)同一であるため、この関数も同様に`payable`ではないことが分かります。 + +| オフセット | オペコード | スタック | +| ----: | ------------ | ----------------------------------------------------------------------------- | +| 144 | JUMPDEST | | +| 145 | POP | | +| 146 | PUSH2 0x00da | 0xDA | +| 149 | PUSH2 0x0153 | 0x0153 0xDA | +| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | +| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | +| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | +| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | +| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 197 | SLT | CALLDATASIZE-4<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | この関数は、コールデータに少なくとも32バイト(1ワード)を必要とするようです。 | オフセット | オペコード | スタック | -| -----:| ------ | -------------------------------------------- | +| ----: | ------ | -------------------------------------------- | | 19D | DUP1 | 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | | 19E | DUP2 | 0x00 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | | 19F | REVERT | | コールデータを取得しなかった場合、トランザクションはリターンデータなしで元に戻されます。 -この関数が、必要なコールデータを_取得した_場合、何が起こるか見ていきましょう。 +この関数が、必要なコールデータを取得した場合、何が起こるか見ていきましょう。 -| オフセット | オペコード | スタック | -| -----:| ------------ | ---------------------------------------- | -| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | +| オフセット | オペコード | スタック | +| ----: | ------------ | ----------------------------------------------------------- | +| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | | 1A2 | CALLDATALOAD | calldataload(4) CALLDATASIZE 0x0153 0xDA | -`calldataload(4)`は、メソッドシグネチャの_後にある_コールデータの最初のワードです。 - -| オフセット | オペコード | スタック | -| -----:| ------------ | ---------------------------------------------------------------------------- | -| 1A3 | SWAP2 | 0x0153 CALLDATASIZE calldataload(4) 0xDA | -| 1A4 | SWAP1 | CALLDATASIZE 0x0153 calldataload(4) 0xDA | -| 1A5 | POP | 0x0153 calldataload(4) 0xDA | -| 1A6 | JUMP | calldataload(4) 0xDA | -| 153 | JUMPDEST | calldataload(4) 0xDA | -| 154 | PUSH2 0x016e | 0x016E calldataload(4) 0xDA | -| 157 | JUMP | calldataload(4) 0xDA | -| 16E | JUMPDEST | calldataload(4) 0xDA | -| 16F | PUSH1 0x04 | 0x04 calldataload(4) 0xDA | -| 171 | DUP2 | calldataload(4) 0x04 calldataload(4) 0xDA | -| 172 | DUP2 | 0x04 calldataload(4) 0x04 calldataload(4) 0xDA | -| 173 | SLOAD | Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | -| 174 | DUP2 | calldataload(4) Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | -| 175 | LT | calldataload(4)\)`、もう一つが`isClaimed()`であり、エアドロップコントラクトのように見えます。 ここからは、オペコードごとに調べる代わりに、[デコンパイラ](https://etherscan.io/bytecode-decompiler?a=0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761)を使用します。デコンパイラは、このコントラクトの3つの関数について利用可能な結果を生成します。 他の関数のリバースエンジニアリングは、読者の演習として残しておきます。 @@ -585,160 +583,80 @@ calldataload(4)がStorage[4]より小さい場合、次のコードになりま この関数についてデコンパイラが提供した内容は、次のとおりです。 ```python -def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable: - require calldata.size - 4 >=′ 64 - if _param1 and _param2 > -1 / _param1: - revert with 0, 17 - return (_param1 * _param2 / 100 * 10^6) +def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable:\n require calldata.size - 4 >=′ 64\n if _param1 and _param2 > -1 / _param1:\n revert with 0, 17\n return (_param1 * _param2 / 100 * 10^6) ``` 最初の`require`では、コールデータ内に2つのパラメータにとって十分なサイズ、つまり関数シグネチャの4バイトに加え少なくとも64バイトあるかどうかをテストしています。 そうでない場合、問題があるのは明らかです。 -`if`文は、`_param1`がゼロでないこと、さらに`_param1 * _param2`が負でないことを確認していると思われます。 これは、オーバー(アンダー)フローを防止している可能性があります。 +`if`文は、`_param1`がゼロでないこと、さらに`_param1 * _param2`が負でないことを確認していると思われます。 これは、ラップアラウンドを防ぐためでしょう。 最後に、この関数はスケーリング値を返します。 ### claim {#claim} -デコンパイラが作成するコードは複雑であり、すべてが重要なわけではありません。 役立つ情報を提供していると思われる行に焦点を当てるので、ある程度スキップします。 +デコンパイラが作成するコードは複雑であり、すべてが重要なわけではありません。 役立つ情報を提供していると思われる行に焦点を当てるので、一部をスキップします。 ```python -def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable: - ... - require _param2 == addr(_param2) - ... - if currentWindow <= _param1: - revert with 0, 'cannot claim for a future window' +def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable:\n ...\n require _param2 == addr(_param2)\n ...\n if currentWindow <= _param1:\n revert with 0, '将来のウィンドウに対して請求することはできません' ``` ここでは、重要事項が2つあります。 -- `_param2`は、`uint256`として宣言されているが、実際にはアドレスである -- `_param1`は、請求対象のウィンドウであり、`currentWindow`であるか、前のウィンドウである必要がある +- `_param2`は、`uint256`として宣言されていますが、実際にはアドレスです +- `_param1`は、請求対象のウィンドウであり、`currentWindow`であるか、それ以前のウィンドウである必要があります。 ```python - ... - if stor5[_claimWindow][addr(_claimFor)]: - revert with 0, 'Account already claimed the given window' + ...\n if stor5[_claimWindow][addr(_claimFor)]:\n revert with 0, 'アカウントはすでに指定されたウィンドウで請求済みです' ``` そのため、Storage[5]はウィンドウとアドレスの配列であり、アドレスがウィンドウの報酬を請求したかどうかが分かります。 ```python - ... - idx = 0 - s = 0 - while idx < _param4.length: - ... - if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]: - mem[mem[64] + 32] = mem[(32 * idx) + 296] - ... - s = sha3(mem[_62 + 32 len mem[_62]]) - continue - ... - s = sha3(mem[_66 + 32 len mem[_66]]) - continue - if unknown2eb4a7ab != s: - revert with 0, 'Invalid proof' + ...\n idx = 0\n s = 0\n while idx < _param4.length:\n ...\n if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]:\n mem[mem[64] + 32] = mem[(32 * idx) + 296]\n ...\n s = sha3(mem[_62 + 32 len mem[_62]])\n continue\n ...\n s = sha3(mem[_66 + 32 len mem[_66]])\n continue\n if unknown2eb4a7ab != s:\n revert with 0, '無効な証明です' ``` -`unknown2eb4a7ab`は、実際には`merkleRoot()`関数であることが分かっています。したがって、このコードは[マークルプルーフ](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5)を検証していると思われます。 これは、`_param4`がマークルプルーフであることを意味します。 +`unknown2eb4a7ab`は実際には`merkleRoot()`関数であることがわかっているので、このコードは[マークル証明](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5)を検証しているようです。 これは、`_param4`がマークル証明であることを意味します。 ```python - call addr(_param2) with: - value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei - gas 30000 wei + call addr(_param2) with:\n value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei\n gas 30000 wei ``` -これは、コントラクトが自身のETHを別のアドレス(コントラクトまたは外部所有アカウント)に送金する方法です。 送金する金額の値で呼び出します。 これはETHのエアドロップであると思われます。 +これは、コントラクトが自身のETHを別のアドレス(コントラクトまたは外部所有)に送金する方法です。 送金する金額の値で呼び出します。 これはETHのエアドロップであると思われます。 ```python - if not return_data.size: - if not ext_call.success: - require ext_code.size(stor2) - call stor2.deposit() with: - value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei + if not return_data.size:\n if not ext_call.success:\n require ext_code.size(stor2)\n call stor2.deposit() with:\n value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei ``` -下の2行は、Storage[2] も呼び出すコントラクトであることを示しています。 [コンストラクタのトランザクションを見ると](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange)、このコントラクトは[0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2)であり、[ソースコードがEtherscanにアップロードされている](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code)ラップドイーサ(WETH)コントラクトであることが分かります。 +下の2行は、Storage[2]も呼び出すコントラクトであることを示しています。 [コンストラクタのトランザクション](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange)を見ると、このコントラクトが[0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) (Wrapped Etherコントラクトで、[そのソースコードはEtherscanにアップロードされています](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code)) であることがわかります。 -このコントラクトは、`_param2`にETHを送金しようとしていると思われます。 送金できれば問題ありません。 送金できない場合は、[ラップドイーサ(WETH)](https://weth.io/)を送金しようとします。 `_param2`が外部所有アカウント(EOA)である場合、必ずETHを受け取ることができますが、コントラクトはETHの受け取りを拒否することができます。 しかし、ラップドイーサ(WETH)はERC-20であるため、コントラクトは受け取りを拒否できません。 +このコントラクトは、`_param2`にETHを送金しようとしていると思われます。 送金できれば問題ありません。 そうでなければ、[WETH](https://weth.tkn.eth.limo/)を送ろうと試みます。 `_param2`が外部所有アカウント (EOA)である場合、必ずETHを受け取ることができますが、コントラクトはETHの受け取りを拒否することができます。 しかし、WETHはERC-20であるため、コントラクトは受け取りを拒否できません。 ```python - ... - log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success) + ...\n log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success) ``` -関数の最後に、ログエントリが生成されていることがわかります。 [生成されたログエントリを確認し](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events)、`0xdbd5...`で始まるトピックでフィルタリングします。 [そのようなエントリを生成したトランザクションの1つをクリックすると](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274)、実際に請求のように見えることがわかります。アカウントは、リバースエンジニアリングしているコントラクトにメッセージを送信し、代わりにETHを取得しています。 +関数の最後に、ログエントリが生成されていることがわかります。 [生成されたログエントリ](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events)を確認し、`0xdbd5...`で始まるトピックでフィルタリングします。 [そのようなエントリを生成したトランザクションの1つをクリックすると](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274)、実際に請求のように見えることがわかります。アカウントは、リバースエンジニアリングしているコントラクトにメッセージを送信し、代わりにETHを取得しています。 ![請求トランザクション](claim-tx.png) ### 1e7df9d3 {#1e7df9d3} -この関数は、上記の[`claim`](#claim)と非常に似ています。 この関数はマークルプルーフも同様に確認し、ETHを最初に送金しようと試み、同じタイプのログエントリを生成します。 +この関数は、上記の[`claim`](#claim)と非常に類似しています。 この関数はマークル証明も同様に確認し、ETHを最初に送金しようと試み、同じタイプのログエントリを生成します。 ```python -def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable: - ... - idx = 0 - s = 0 - while idx < _param3.length: - if idx >= mem[96]: - revert with 0, 50 - _55 = mem[(32 * idx) + 128] - if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]: - ... - s = sha3(mem[_58 + 32 len mem[_58]]) - continue - mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) - ... - if unknown2eb4a7ab != s: - revert with 0, 'Invalid proof' - ... - call addr(_param1) with: - value s wei - gas 30000 wei - if not return_data.size: - if not ext_call.success: - require ext_code.size(stor2) - call stor2.deposit() with: - value s wei - gas gas_remaining wei - ... - log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) +def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable:\n ...\n idx = 0\n s = 0\n while idx < _param3.length:\n if idx >= mem[96]:\n revert with 0, 50\n _55 = mem[(32 * idx) + 128]\n if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]:\n ...\n s = sha3(mem[_58 + 32 len mem[_58]])\n continue\n mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]])\n ...\n if unknown2eb4a7ab != s:\n revert with 0, '無効な証明です'\n ...\n call addr(_param1) with:\n value s wei\n gas 30000 wei\n if not return_data.size:\n if not ext_call.success:\n require ext_code.size(stor2)\n call stor2.deposit() with:\n value s wei\n gas gas_remaining wei\n ...\n log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) ``` -主な違いは、最初のパラメータです。引き出しを行うためのウィンドウがありません。 代わりに、請求対象となるすべてのウィンドウについてのループがあります。 +主な違いは、最初のパラメータである引き出しを行うためのウィンドウがないことです。 代わりに、請求対象となるすべてのウィンドウについてのループがあります。 ```python - idx = 0 - s = 0 - while idx < currentWindow: - ... - if stor5[mem[0]]: - if idx == -1: - revert with 0, 17 - idx = idx + 1 - s = s - continue - ... - stor5[idx][addr(_param1)] = 1 - if idx >= unknown81e580d3.length: - revert with 0, 50 - mem[0] = 4 - if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]: - revert with 0, 17 - if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6): - revert with 0, 17 - if idx == -1: - revert with 0, 17 - idx = idx + 1 - s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6) - continue + idx = 0\n s = 0\n while idx < currentWindow:\n ...\n if stor5[mem[0]]:\n if idx == -1:\n revert with 0, 17\n idx = idx + 1\n s = s\n continue\n ...\n stor5[idx][addr(_param1)] = 1\n if idx >= unknown81e580d3.length:\n revert with 0, 50\n mem[0] = 4\n if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]:\n revert with 0, 17\n if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6):\n revert with 0, 17\n if idx == -1:\n revert with 0, 17\n idx = idx + 1\n s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6)\n continue ``` -そのため、すべてのウィンドウについて請求する`claim`を少し変えたもののように思われます。 +そのため、すべてのウィンドウについて請求する`claim`の変種のようです。 -## まとめ {#conclusion} +## 結論 {#conclusion} ここまでで、オペコードまたは(動作する場合は)デコンパイラを使用して、ソースコードが入手できないコントラクトを理解する方法を身に付けられたはずです。 この記事の長さが示しているように、コントラクトのリバースエンジニアリングは簡単ではありません。しかしながら、セキュリティが不可欠なシステムでは、コントラクトが想定した通りに動作しているかを検証できることは、非常に重要なスキルです。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/run-node-raspberry-pi/index.md b/public/content/translations/ja/developers/tutorials/run-node-raspberry-pi/index.md index e45b9dfffcb..d74af809dbb 100644 --- a/public/content/translations/ja/developers/tutorials/run-node-raspberry-pi/index.md +++ b/public/content/translations/ja/developers/tutorials/run-node-raspberry-pi/index.md @@ -1,12 +1,8 @@ --- -title: マイクロSDカードに書き込んでRaspberry Pi 4をノードにする方法 -description: Raspberry Pi 4に書き込み、イーサネットケーブル、SSDをそれぞれ接続してから電源を入れるとRaspberry Pi 4がフルイーサリアムノードとバリデータになります。 +title: "Raspberry Pi 4でイーサリアムノードを実行する" +description: "Raspberry Pi 4に書き込み、イーサネットケーブル、SSDをそれぞれ接続してから電源を入れるとRaspberry Pi 4がフルイーサリアムノードとバリデータになります。" author: "EthereumOnArm" -tags: - - "クライアント" - - "実行レイヤ" - - "コンセンサスレイヤー" - - "ノード" +tags: [ "クライアント", "実行レイヤー", "コンセンサスレイヤー", "ノード" ] lang: ja skill: intermediate published: 2022-06-10 @@ -20,18 +16,18 @@ Ethereum on Armを使用してRaspberry Piをイーサリアムノードにす - Raspberry 4 (モデル B 8 GB)、Odroid M1またはRock 5B (8 GB / 16 GB RAM)ボード - マイクロSDカード(最小構成: 16 GB、クラス10) -- 2 TB SSD最小USB 3.0ディスクまたはUSB-SATAケース付きSSD +- 最低2TBのUSB 3.0 SSD、またはUSB-SATAケース付きSSD。 - 電源 - イーサネットケーブル -- ポートフォワーディング機能(詳細はクライアントを参照してください) +- ポートフォワーディング(詳細はクライアントを参照してください) - ヒートシンクとファンが付属しているケース - USBキーボード、モニター、HDMIケーブル(マイクロHDMI) (オプション) -## Ethereum on Armを実行する理由 {#why-run-ethereum-on-arm} +## ARM上でイーサリアムを実行する理由 {#why-run-ethereum-on-arm} ARMボードは価格が非常に手頃で、柔軟性の高い、小型のコンピュータです。 このボードは、イーサリアムノードを実行するのに適しています。手ごろな価格で、ノードだけにリソースを使うように設定が可能であり、効率的で電力消費量が少なく、物理的にも小さいので家に置いても邪魔にならないからです。 また、ノードを立ち上げるのも簡単です。Raspberry PiのマイクロSDカードは、ビルド済みイメージを簡単に書き込めるので、ソフトウェアのダウンロードやビルドが不要です。 -## 動作方法 {#how-does-it-work} +## 仕組み {#how-does-it-work} ビルド済みイメージをRaspberry Piのメモリーカードに書き込みます。 このイメージには、イーサリアムノードを実行するために必要なすべてが含まれています。 この書き込まれたカードがあれば、ユーザーはRaspberry Piの電源を入れるだけで済みます。 ノードを実行するのに必要なすべてのプロセスは自動で開始します。 メモリーカードにLinuxベースのオペレーティングシステム(OS)が含まれており、その上でシステムレベルのプロセスが自動的に実行され、ARMボードがイーサリアムノードになります。 @@ -39,97 +35,97 @@ ARMボードは価格が非常に手頃で、柔軟性の高い、小型のコ **このイメージは、必要なステップのすべてを行います**。環境のセットアップ、SSDディスクのフォーマット、イーサリアムソフトウェアのインストールと実行だけでなくブロックチェーンの同期も開始します。 -## 実行クライアントとコンセンサスクライアントに関する注意事項 {#note-on-execution-and-consensus-clients} +## 実行クライアントとコンセンサスクライアントに関する注意 {#note-on-execution-and-consensus-clients} -Ethereum on Armのイメージには、ビルド済みの実行クライアントとコンセンサスクライアントがサービスとして組み込まれています。 イーサリアムノードでは、両方のクライアントが同期されて実行される必要があります。 ここでユーザーがすべきことは、イメージをダウンロードして書き込んだ後、サービスを開始するだけです。 イメージには、次の実行クライアントが入っています。 +Ethereum on Armのイメージには、ビルド済みの実行クライアントとコンセンサスクライアントがサービスとして組み込まれています。 イーサリアムノードでは、両方のクライアントが同期されて実行される必要があります。 ユーザーがすべきことは、イメージをダウンロードして書き込んだ後、サービスを開始するだけです。 イメージには、次の実行クライアントが入っています。 - Geth - Nethermind -- Besu +- ベス -コンセンサスクライアントは、次のものが入っています。 +そして、以下のコンセンサスクライアント: -- ライトハウス -- ニンバス -- プリズム -- テク +- Lighthouse +- Nimbus +- Prysm +- Teku -使いたい実行クライアントとコンセンサスクライアントを各1つずつ選びます。すべての実行クライアントは、すべてのコンセンサスクライアントと連携可能です。 クライアントを選択しなかった場合、デフォルトでGethとライトハウスになります。ボードに電源を入れると、これらのクライアントが自動的に実行されます。 Gethがピアを見つけて接続できるように、ルーターで30303ポートを開く必要があります。 +使いたい実行クライアントとコンセンサスクライアントを各1つずつ選びます。すべての実行クライアントは、すべてのコンセンサスクライアントと連携可能です。 クライアントを明示的に選択しなかった場合、ノードはデフォルトのGethとLighthouseにフォールバックし、ボードに電源を入れると自動的に実行されます。 Gethがピアを見つけて接続できるように、ルーターで30303ポートを開く必要があります。 ## イメージのダウンロード {#downloading-the-image} Raspberry Pi 4のイーサリアムイメージは、「プラグ・アンド・プレイ」イメージです。実行クライアントとコンセンサスクライアントのインストールとセットアップが自動的に行われ、それらが互いに通信し、イーサリアムネットワークに接続するように設定されます。 ユーザーがすべきことは、単純なコマンドを使用してプロセスを開始するだけです。 -[Ethereum on Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1)からRaspberry Piイメージをダウンロードし、次のようにSHA256ハッシュを確認してください。 +[Ethereum on Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1)からRaspberry Piのイメージをダウンロードし、SHA256ハッシュを検証します: ```sh -# From directory containing the downloaded image +# ダウンロードしたイメージがあるディレクトリから shasum -a 256 ethonarm_22.04.00.img.zip -# Hash should output: fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f +# ハッシュ出力は次のようになります: fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f ``` -Rock 5BとOdroid M1ボードのイメージは、Ethereum-on-Armの[ダウンロードページ](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html)から入手可能です。 +Rock 5BおよびOdroid M1ボード用のイメージは、Ethereum-on-Armの[ダウンロードページ](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html)で入手できます。 -## マイクロSDへの書き込み {#flashing-the-microsd} +## MicroSDへの書き込み {#flashing-the-microsd} Raspberry Piで使用するマイクロSDカードは、まずデスクトップパソコンかノートパソコンに挿入して書き込む必要があります。 以下のターミナルコマンドで、ダウンロードしたイメージをSDカードに書き込みます。 ```shell -# check the MicroSD card name +# MicroSDカード名を確認 sudo fdisk -l >> sdxxx ``` -SDカード名を正しく取得することは、非常に重要です。次に使うコマンドに`dd`があり、これはイメージを書き込む前にカードの内容を完全に削除するからです。 zipイメージがあるディレクトリに移動して続行します。 +次のコマンドには `dd` が含まれており、これはイメージを書き込む前にカードの既存のコンテンツを完全に消去するため、名前を正しく入力することが非常に重要です。 続行するには、zipイメージが含まれているディレクトリに移動します。 ```shell -# unzip and flash image +# イメージを解凍して書き込み unzip ethonarm_22.04.00.img.zip sudo dd bs=1M if=ethonarm_22.04.00.img of=/dev/ conv=fdatasync status=progress ``` これでカードに書きこまれたので、Raspberry Piに挿入できます。 -## ノードの開始 {#start-the-node} +## ノードの起動 {#start-the-node} -SDカードを挿入したRaspberry Piに、SSDとイーサネットケーブルを接続し、電源を入れてください。 OSが起動し、クライアントソフトウェアのインストールやビルドなど、Raspberry Piをイーサリアムノードにする事前設定処理が自動的に開始します。 この処理には10~15分を要します。 +SDカードを挿入したRaspberry Piに、イーサネットケーブルとSSDを接続し、電源を入れてください。 OSが起動し、クライアントソフトウェアのインストールやビルドなど、Raspberry Piをイーサリアムノードにする事前設定処理が自動的に開始します。 この処理には10~15分を要します。 -自動インストールおよび設定が完了したら、モニターとキーボードがボードに接続されているならば直接ターミナルを使うか、SSH接続でデバイスへログインしてください。 `ethereum`アカウントを使用してログインします。このアカウントは、ノードの開始に必要なパーミッションを持っています。 +すべてがインストールおよび設定されたら、ssh接続でデバイスにログインするか、モニターとキーボードがボードに接続されている場合はターミナルを直接使用します。 ノードの開始に必要な権限を持つ `ethereum` アカウントを使用してログインします。 ```shell -User: ethereum -Password: ethereum +ユーザー: ethereum +パスワード: ethereum ``` -デフォルトの実行クライアントであるGethが自動的に開始します。 次のターミナルコマンドを使用してログをチェックすることで、開始を確認できます。 +デフォルトの実行クライアントであるGethが自動的に開始します。 次のターミナルコマンドを使用してログをチェックすることで、これを確認できます。 ```sh sudo journalctl -u geth -f ``` -コンセンサスクライアントは、明示的に開始する必要があります。 開始するには、まずルーターの9000ポートを開き、ライトハウスがピアを見つけて接続できるようにします。 次にライトハウスサービスを有効にして開始します。 +コンセンサスクライアントは明示的に起動する必要があります。 これを行うには、まずルーターのポート9000を開き、Lighthouseがピアを見つけて接続できるようにします。 次にLighthouseサービスを有効にして開始します。 ```sh sudo systemctl enable lighthouse-beacon sudo systemctl start lighthouse-beacon ``` -ログでクライアントの状態を確認してください。 +ログを使ってクライアントを確認してください。 ```sh sudo journalctl -u lighthouse-beacon ``` -チェックポイント同期を使用するため、コンセンサスクライアントは数分で同期されることに注意してください。 実行クライアントは、同期により時間がかかります。数時間かかる場合もあります。さらに、コンセンサスクライアントの同期が完了するまで、同期を開始しません(これは、実行クライアントが、同期されたコンセンサスクライアントが提供する同期先のターゲットを必要とするためです)。 +コンセンサスクライアントはチェックポイント同期を使用するため、数分で同期されることに注意してください。 実行クライアントは、同期により時間がかかります。数時間かかる場合もあります。さらに、コンセンサスクライアントの同期が完了するまで、同期を開始しません(これは、実行クライアントが、同期されたコンセンサスクライアントが提供する同期先のターゲットを必要とするためです)。 -Gethとライトハウスのサービスが開始し同期されると、Raspberry Piがイーサリアムノードになります。 イーサリアムネットワークとのやり取りを行うには、8545ポートでGethクライアントに接続し、GethのJavascriptコンソールを使うことが最も一般的な方法です。 また、Curlなどのリクエストツールを使用して、JSONオブジェクトとしてフォーマットされたコマンドを送信することもできます。 詳細は、[Gethのドキュメント](https://geth.ethereum.org/)をご覧ください。 +GethとLighthouseのサービスが開始し同期されると、Raspberry Piがイーサリアムノードになります。 イーサリアムネットワークとのやり取りを行うには、8545ポートでGethクライアントに接続し、GethのJavascriptコンソールを使うことが最も一般的な方法です。 また、Curlなどのリクエストツールを使用して、JSONオブジェクトとしてフォーマットされたコマンドを送信することもできます。 詳細は[Gethドキュメント](https://geth.ethereum.org/)をご覧ください。 -Gethは、ブラウザで表示できるGrafanaダッシュボードに、メトリクスをレポートするように事前設定されています。 この機能を使用してノードの健全性を監視したい上級ユーザーは、`ipaddress:3000`にアクセスして`user: admin`と`passwd: ethereum`を入力してください。 +Gethは、ブラウザで表示できるGrafanaダッシュボードに、メトリクスをレポートするように事前設定されています。 上級ユーザーは、この機能を使用して`ipaddress:3000`にアクセスし、`user: admin`と`passwd: ethereum`を渡してノードの状態を監視することができます。 ## バリデータ {#validators} -バリデータは、コンセンサスクライアントにオプションで追加することもできます。 バリデータソフトウェアを使用すると、ノードが積極的にコンセンサスに参加し、暗号経済のセキュリティをネットワークに提供できるようになります。 この作業の報酬としてETHを受け取れます。 バリデータを実行するには、まず32 ETHを持っている必要があり、これをデポジットコントラクトに預け入れる必要があります。 **長期的なコミットメントとなるため、このETHはまだ引き出すことはできません。** 預け入れは、[ランチパッド](https://launchpad.ethereum.org/)のステップバイステップガイドに従って行うことができます。 この作業は、デスクトップパソコンまたはノートパソコンで行いますが、キーは生成しないでください。キーはRaspberry Piで直接生成します。 +バリデータは、コンセンサスクライアントにオプションで追加することもできます。 バリデータソフトウェアを使用すると、ノードが積極的にコンセンサスに参加し、暗号経済のセキュリティをネットワークに提供できるようになります。 この作業の報酬としてETHを受け取れます。 バリデータを実行するには、まず32 ETHを持っている必要があり、これをデポジットコントラクトに預け入れる必要があります。 預け入れは、[Launchpad](https://launchpad.ethereum.org/)のステップバイステップガイドに従って行うことができます。 この作業はデスクトップ/ラップトップで行いますが、キーは生成しないでください — これはRaspberry Piで直接行うことができます。 Raspberry Piのターミナルを開き、以下のコマンドを実行して、デポジットキーを生成してください。 @@ -139,34 +135,37 @@ sudo apt-get install staking-deposit-cli cd && deposit new-mnemonic --num_validators 1 ``` -ニーモニックフレーズを安全に保管してください。 上記コマンドで、ノードのキーストアに2つのファイルが生成されました。これらは、バリデータキーとデポジットデータファイルです。 デポジットデータは、ランチパッドにアップロードする必要があるため、Raspberry Piからデスクトップパソコンまたはノートパソコンにコピーする必要があります。 これは、ssh接続や他のコピー/ペーストの手法を用いて行えます。 +(または、[staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli)をダウンロードしてエアギャップマシンで実行し、`deposit new-mnemnonic`コマンドを実行します) -ランチパッドを実行しているコンピューターでデポジットデータファイルが利用可能になったら、これをランチパッド画面の「`+`」にドラッグ・アンド・ドロップすることができます。 画面の指示に従って、デポジットコントラクトにトランザクションを送信してください。 +ニーモニックフレーズを安全に保管してください。 上記コマンドで、ノードのキーストアに2つのファイル(バリデータキーとデポジットデータファイル)が生成されました。 デポジットデータは、Launchpadにアップロードする必要があるため、Raspberry Piからデスクトップまたはラップトップにコピーする必要があります。 これは、ssh接続や他のコピー/ペーストの手法を用いて行えます。 -Raspberry Piに戻ると、バリデータが開始可能になります。 これには、バリデータキーのインポート、報酬を受け取るためのアドレスの設定、事前設定されたバリデータプロセスの開始が必要になります。 以下は、ライトハウス向けの例です。その他のコンセンサス クライアント向けの手順については、[Ethereum on Armのドキュメント](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/)を参照してください。 +Launchpadを実行しているコンピューターでデポジットデータファイルが利用可能になったら、これをLaunchpad画面の「+」にドラッグ・アンド・ドロップすることができます。 画面の指示に従って、デポジットコントラクトにトランザクションを送信してください。 + +Raspberry Piに戻ると、バリデータが開始可能になります。 これには、バリデータキーのインポート、報酬を受け取るためのアドレスの設定、事前設定されたバリデータプロセスの開始が必要になります。 以下の例はLighthouseのものです。他のコンセンサスクライアント向けの手順は、[Ethereum on Armドキュメント](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/)で参照できます: ```shell -# import the validator keys +# バリデータキーのインポート lighthouse account validator import --directory=/home/ethereum/validator_keys -# set the reward address +# 報酬受取アドレスの設定 sudo sed -i 's/' /etc/ethereum/lighthouse-validator.conf -# start the validator +# バリデータの開始 sudo systemctl start lighthouse-validator ``` おめでとうございます。これでフルイーサリアムノードとバリデータが、Raspberry Piで実行されました。 -## 詳細情報 {#more-details} +## 詳細 {#more-details} -このページでは、Raspberry Piを使用してGethおよびライトハウスのノードとバリデータを設定する方法の概要について説明しました。 さらに詳細な手順は、[Ethereum on Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html)のウェブサイトでご覧いただけます。 +このページでは、Raspberry Piを使用してGeth-Lighthouseノードとバリデータを設定する方法の概要について説明しました。 より詳細な手順は、[Ethereum-on-Armウェブサイト](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html)で入手できます。 -## フィードバックご協力のお願い {#feedback-appreciated} +## フィードバックのお願い {#feedback-appreciated} -Raspberry Piは多くのユーザーが利用しており、イーサリアムネットワークの健全性に非常に良い影響を与えてきました。 このチュートリアルを深く掘り下げていき、テストネットで実行してみてください。また、Ethereum on ArmのGitHubページの確認、フィードバックの提供、問題提起、プルリクエストの作成による、テクノロジーの進歩とドキュメント化推進へのご協力をお願いします。 +Raspberry Piには膨大なユーザーベースがあり、イーサリアムネットワークの健全性に非常に良い影響を与える可能性があります。 +このチュートリアルを深く掘り下げていき、テストネットで実行してみてください。また、Ethereum on ArmのGitHubページの確認、フィードバックの提供、問題提起、プルリクエストの作成による、テクノロジーの進歩とドキュメント化推進へのご協力をお願いします。 -## 参考文献 {#references} +## 参考資料 {#references} 1. https://ubuntu.com/download/raspberry-pi 2. https://wikipedia.org/wiki/Port_forwarding diff --git a/public/content/translations/ja/developers/tutorials/scam-token-tricks/index.md b/public/content/translations/ja/developers/tutorials/scam-token-tricks/index.md new file mode 100644 index 00000000000..92dfedc20ec --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/scam-token-tricks/index.md @@ -0,0 +1,470 @@ +--- +title: "詐欺トークンで使われる手口と、その見分け方" +description: "このチュートリアルでは、詐欺トークンを分析し、詐欺師が使う手口、その実装方法、そしてそれを検出する方法を解説します。" +author: Ori Pomerantz +tags: + [ + "詐欺", + "Solidity", + "ERC-20", + "JavaScript", + "typescript" + ] +skill: intermediate +published: 2023-09-15 +lang: ja +--- + +このチュートリアルでは、[ある詐欺トークン](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code)を分析し、詐欺師が使う手口やその実装方法について見ていきます。 このチュートリアルを終える頃には、ERC-20トークンコントラクト、その機能、そしてなぜ懐疑的な見方が必要なのかについて、より包括的な見解を得られるでしょう。 次に、その詐欺トークンが発行するイベントを見て、それが正当なものでないことを自動的に特定する方法を見ていきます。 + +## 詐欺トークン - それは何であり、なぜ人々はそれを行い、どうすればそれを回避できるか {#scam-tokens} + +イーサリアムの最も一般的な用途の1つは、グループが取引可能なトークン、いわば独自の通貨を作ることです。 価値をもたらす正当なユースケースを提供するトークンがある一方、その価値をトークン発行元が独占するようなトークンも存在します。 + +この件については、[ethereum.org の別の場所で](/guides/how-to-id-scam-tokens/)、ユーザーの視点から詳しく読むことができます。 このチュートリアルでは、詐欺トークンを分析し、それがどのように行われ、どのように検出できるかを見ていくことに焦点を当てます。 + +### wARBが詐欺だとどうしてわかるのか? {#warb-scam} + +私たちが分析するトークンは[wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code)で、正当な[ARBトークン](https://etherscan.io/token/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1)と同等であるかのように装っています。 + +どちらが正当なトークンであるかを知る最も簡単な方法は、発行元の組織である[Arbitrum](https://arbitrum.foundation/)を見ることです。 正当なアドレスは、[彼らのドキュメント](https://docs.arbitrum.foundation/deployment-addresses#token)に明記されています。 + +### なぜソースコードは利用できるのですか? {#why-source} + +通常、他人を騙そうとする人々は秘密主義であると予想され、実際に多くの詐欺トークンはコードを公開していません(例えば、[これ](https://optimistic.etherscan.io/token/0x15992f382d8c46d667b10dc8456dc36651af1452#code)や[これ](https://optimistic.etherscan.io/token/0x026b623eb4aada7de37ef25256854f9235207178#code)など)。 + +しかし、正当なトークンは通常ソースコードを公開するため、詐欺トークンの作者も正当に見せかけるために、同じことをすることがあります。 [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code)は、ソースコードが公開されているトークンの一つであり、そのため理解しやすくなっています。 + +コントラクトのデプロイ者はソースコードを公開するかどうかを選択できますが、間違ったソースコードを公開することは_できません_。 ブロックエクスプローラーは提供されたソースコードを独自にコンパイルし、全く同じバイトコードが得られなければ、そのソースコードを拒否します。 [これについてはEtherscanのサイトで詳しく読むことができます](https://etherscan.io/verifyContract)。 + +## 正当なERC-20トークンとの比較 {#compare-legit-erc20} + +このトークンを正当なERC-20トークンと比較します。 正当なERC-20トークンが通常どのように書かれているかについて詳しくない場合は、[このチュートリアル](/developers/tutorials/erc20-annotated-code/)をご覧ください。 + +### 特権アドレスの定数 {#constants-for-privileged-addresses} + +コントラクトには、特権アドレスが必要な場合があります。 長期的な使用を目的として設計されたコントラクトでは、一部の特権アドレスがそれらのアドレスを変更することを許可します。例えば、新しいマルチシグコントラクトの使用を可能にするためです。 これにはいくつかの方法があります。 + +[`HOP`トークンコントラクト](https://etherscan.io/address/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc#code)は、[`Ownable`](https://docs.openzeppelin.com/contracts/2.x/access-control#ownership-and-ownable)パターンを使用しています。 特権アドレスは、`_owner`と呼ばれるフィールドのストレージに保持されます(3番目のファイル`Ownable.sol`を参照)。 + +```solidity +abstract contract Ownable is Context { + address private _owner; + . + . + . +} +``` + +[`ARB`トークンコントラクト](https://etherscan.io/address/0xad0c361ef902a7d9851ca7dcc85535da2d3c6fc7#code)には、直接的な特権アドレスはありません。 しかし、それは必要ありません。 それは、[アドレス`0xb50721bcf8d664c30412cfbc6cf7a15145234ad1`](https://etherscan.io/address/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1#code)にある[`proxy`](https://docs.openzeppelin.com/contracts/5.x/api/proxy)の背後にあります。 そのコントラクトには、アップグレードに使用できる特権アドレスがあります(4番目のファイル、`ERC1967Upgrade.sol`を参照)。 + +```solidity + /** + * @dev EIP1967の管理者スロットに新しいアドレスを格納します。 + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: 新しい管理者はゼロアドレスです"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } +``` + +対照的に、`wARB`コントラクトにはハードコーディングされた`contract_owner`があります。 + +```solidity +contract WrappedArbitrum is Context, IERC20 { + . + . + . + address deployer = 0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1; + address public contract_owner = 0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33; + . + . + . +} +``` + +このコントラクトオーナーは、異なる時点で異なるアカウントによって制御されうるコントラクトではなく、[外部所有アカウント](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs)です。 これは、価値を維持し続けるERC-20を管理するための長期的なソリューションとしてではなく、個人による短期的な使用のために設計されている可能性が高いことを意味します。 + +そして実際にEtherscanを見ると、詐欺師がこのコントラクトを使用したのは2023年5月19日のわずか12時間([最初のトランザクション](https://etherscan.io/tx/0xf49136198c3f925fcb401870a669d43cecb537bde36eb8b41df77f06d5f6fbc2)から[最後のトランザクション](https://etherscan.io/tx/0xdfd6e717157354e64bbd5d6adf16761e5a5b3f914b1948d3545d39633244d47b)まで)だけであったことがわかります。 + +### 偽の`_transfer`関数 {#the-fake-transfer-function} + +実際の送金は、[内部`_transfer`関数](/developers/tutorials/erc20-annotated-code/#the-_transfer-function-_transfer)を使用して行われるのが標準です。 + +`wARB`では、この関数はほとんど正当に見えます: + +```solidity + function _transfer(address sender, address recipient, uint256 amount) internal virtual{ + require(sender != address(0), "ERC20: ゼロアドレスからの送金"); + require(recipient != address(0), "ERC20: ゼロアドレスへの送金"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: 送金額が残高を超えています"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +疑わしい部分は次のとおりです: + +```solidity + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); +``` + +コントラクトオーナーがトークンを送金した場合、なぜ`Transfer`イベントでは`deployer`から送金されたと表示されるのでしょうか? + +しかし、もっと重要な問題があります。 誰がこの`_transfer`関数を呼び出すのでしょうか? これは外部からは呼び出せず、`internal`とマークされています。 そして、私たちが持っているコードには`_transfer`への呼び出しは含まれていません。 明らかに、これはおとりとしてここにあります。 + +```solidity + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _f_(_msgSender(), recipient, amount); + return true; + } + + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _f_(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: 送金額が許容量を超えています")); + return true; + } +``` + +トークンを転送するために呼び出される関数`transfer`と`transferFrom`を見ると、それらが全く異なる関数`_f_`を呼び出していることがわかります。 + +### 本当の`_f_`関数 {#the-real-f-function} + +```solidity + function _f_(address sender, address recipient, uint256 amount) internal _mod_(sender,recipient,amount) virtual { + require(sender != address(0), "ERC20: ゼロアドレスからの送金"); + require(recipient != address(0), "ERC20: ゼロアドレスへの送金"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: 送金額が残高を超えています"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +この関数には2つの潜在的な危険信号があります。 + +- [関数修飾子](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `_mod_`の使用。 しかし、ソースコードを調べてみると、`_mod_`は実際には無害であることがわかります。 + + ```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } + ``` + +- `_transfer`で見たのと同じ問題で、`contract_owner`がトークンを送金すると、それらが`deployer`から来たように見えることです。 + +### 偽のイベント関数 `dropNewTokens` {#the-fake-events-function-dropNewTokens} + +ここで、実際の詐欺のように見えるものにたどり着きます。 読みやすくするために少し関数を編集しましたが、機能的には同等です。 + +```solidity +function dropNewTokens(address uPool, + address[] memory eReceiver, + uint256[] memory eAmounts) public auth() +``` + +この関数には`auth()`修飾子があり、これはコントラクトオーナーによってのみ呼び出されることを意味します。 + +```solidity +modifier auth() { + require(msg.sender == contract_owner, "対話は許可されていません"); + _; +} +``` + +この制限は完全に理にかなっています。なぜなら、私たちはランダムなアカウントがトークンを配布することを望まないからです。 しかし、関数の残りの部分は疑わしいです。 + +```solidity +{ + for (uint256 i = 0; i < eReceiver.length; i++) { + emit Transfer(uPool, eReceiver[i], eAmounts[i]); + } +} +``` + +プールアカウントから受信者の配列へ金額の配列を送金する関数は、完全に理にかなっています。 給与支払い、エアドロップなど、単一のソースから複数の宛先にトークンを配布したいユースケースはたくさんあります。 複数のトランザクションを発行したり、同じトランザクションの一部として別のコントラクトからERC-20を複数回呼び出したりする代わりに、単一のトランザクションで行う方が(ガス代が)安くなります。 + +しかし、`dropNewTokens`はそれをしません。 これは[`Transfer`イベント](https://eips.ethereum.org/EIPS/eip-20#transfer-1)を発行しますが、実際にはトークンを転送しません。 実際には起こらなかった送金をオフチェーンアプリケーションに伝えることで混乱させる正当な理由はありません。 + +### `Approve`関数を燃やす {#the-burning-approve-function} + +ERC-20コントラクトは、許容量のために[an `approve` function](/developers/tutorials/erc20-annotated-code/#approve)を持つことになっており、実際、私たちの詐欺トークンにはそのような関数があり、しかも正しいものです。 しかし、SolidityはC言語から派生しているため、大文字と小文字が区別されます。 「Approve」と「approve」は異なる文字列です。 + +また、その機能は`approve`とは関係ありません。 + +```solidity + function Approve( + address[] memory holders) +``` + +この関数は、トークン保有者のアドレスの配列で呼び出されます。 + +```solidity + public approver() { +``` + +`approver()` 修飾子は、`contract_owner` だけがこの関数を呼び出すことを許可するようにします(下記参照)。 + +```solidity + for (uint256 i = 0; i < holders.length; i++) { + uint256 amount = _balances[holders[i]]; + _beforeTokenTransfer(holders[i], 0x0000000000000000000000000000000000000001, amount); + _balances[holders[i]] = _balances[holders[i]].sub(amount, + "ERC20: burn amount exceeds balance"); + _balances[0x0000000000000000000000000000000000000001] = + _balances[0x0000000000000000000000000000000000000001].add(amount); + } + } + +``` + +ホルダーアドレスごとに、この関数はホルダーの残高全体を`0x00...01`アドレスに移動させ、事実上それをバーン(焼却)します(標準の実際の`burn`は総供給量も変更し、トークンを`0x00...00`に送金します)。 これは、`contract_owner`がどのユーザーの資産でも削除できることを意味します。 これは、ガバナンストークンに求める機能とは思えません。 + +### コード品質の問題 {#code-quality-issues} + +これらのコード品質の問題は、このコードが詐欺であると_証明する_ものではありませんが、疑わしいものに見せます。 Arbitrumのような組織化された企業は、通常このような質の悪いコードをリリースしません。 + +#### `mount`関数 {#the-mount-function} + +[標準](https://eips.ethereum.org/EIPS/eip-20)には明記されていませんが、一般的に新しいトークンを作成する関数は[`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn)と呼ばれます。 + +`wARB`のコンストラクタを見ると、ミント関数が何らかの理由で`mount`に改名されており、効率化のために全額を一度にではなく、初期供給の5分の1で5回呼び出されていることがわかります。 + +```solidity + constructor () public { + + _name = "Wrapped Arbitrum"; + _symbol = "wARB"; + _decimals = 18; + uint256 initialSupply = 1000000000000; + + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + } +``` + +`mount`関数自体も疑わしいです。 + +```solidity + function mount(address account, uint256 amount) public { + require(msg.sender == contract_owner, "ERC20: mint to the zero address"); +``` + +`require`を見ると、コントラクトオーナーだけがミントすることを許可されていることがわかります。 これは正当です。 しかし、エラーメッセージは_only owner is allowed to mint_(オーナーのみミント可能)などであるべきです。 代わりに、それは無関係な_ERC20: mint to the zero address_です。 ゼロアドレスへのミントを正しくテストするには `require(account != address(0), "<エラーメッセージ>")` としますが、コントラクトはこれをチェックする手間をかけていません。 + +```solidity + _totalSupply = _totalSupply.add(amount); + _balances[contract_owner] = _balances[contract_owner].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +さらに2つ、直接ミントに関連する疑わしい事実があります。 + +- `account`パラメータがあり、これはミントされた量を受け取るべきアカウントであると推測されます。 しかし、実際に増加する残高は`contract_owner`のものです。 + +- 増加した残高は`contract_owner`のものですが、発行されたイベントは`account`への送金を示しています。 + +### なぜ `auth` と `approver` の両方があるのか? なぜ何もしない`mod`があるのか? {#why-both-autho-and-approver-why-the-mod-that-does-nothing} + +このコントラクトには `_mod_`、`auth`、`approver` の3つの修飾子が含まれています。 + +```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } +``` + +`_mod_`は3つのパラメータを取り、それらで何もしません。 なぜそれがあるのでしょうか? + +```solidity + modifier auth() { + require(msg.sender == contract_owner, "対話は許可されていません"); + _; + } + + modifier approver() { + require(msg.sender == contract_owner, "対話は許可されていません"); + _; + } +``` + +`auth` と `approver` は、コントラクトが `contract_owner` によって呼び出されたことを確認するため、より理にかなっています。 ミントなどの特定の特権的なアクションは、そのアカウントに限定されることを期待します。 しかし、_全く同じこと_をする2つの別々の関数を持つことに何の意味があるのでしょうか? + +## 何を自動的に検出できるか? {#what-can-we-detect-automatically} + +Etherscanを見ることで、`wARB`が詐欺トークンであることがわかります。 しかし、それは中央集権的な解決策です。 理論的には、Etherscanは転覆されたりハッキングされたりする可能性があります。 トークンが正当なものかどうかを独自に判断できる方が良いです。 + +発行するイベントを見ることで、ERC-20トークンが疑わしい(詐欺か、非常に плохоく書かれているか)ことを特定するためのいくつかのトリックがあります。 + +## 疑わしい`Approval`イベント {#suspicious-approval-events} + +[`Approval`イベント](https://eips.ethereum.org/EIPS/eip-20#approval)は、直接のリクエストによってのみ発生すべきです(許容量の結果として発生する可能性のある[`Transfer`イベント](https://eips.ethereum.org/EIPS/eip-20#transfer-1)とは対照的です)。 この問題の詳細な説明と、なぜリクエストがコントラクトによって仲介されるのではなく直接である必要があるのかについては、[Solidityのドキュメント](https://docs.soliditylang.org/en/v0.8.20/security-considerations.html#tx-origin)を参照してください。 + +これは、[外部所有アカウント](/developers/docs/accounts/#types-of-account)からの支出を承認する`Approval`イベントは、そのアカウントから発生し、宛先がERC-20コントラクトであるトランザクションからでなければならないことを意味します。 外部所有アカウントからのその他の種類の承認は疑わしいです。 + +[viem](https://viem.sh/)と、型安全性を備えたJavaScriptの派生言語である[TypeScript](https://www.typescriptlang.org/docs/)を使用して、[この種のイベントを特定するプログラム](https://github.com/qbzzt/20230915-scam-token-detection)がここにあります。 実行方法: + +1. `.env.example` を `.env` にコピーします。 +2. `.env` を編集して、イーサリアムメインネットノードへのURLを提供します。 +3. `pnpm install` を実行して、必要なパッケージをインストールします。 +4. `pnpm susApproval` を実行して、疑わしい承認を検索します。 + +以下に一行ずつの説明を示します: + +```typescript +import { + Address, + TransactionReceipt, + createPublicClient, + http, + parseAbiItem, +} from "viem" +import { mainnet } from "viem/chains" +``` + +`viem`から型定義、関数、チェーン定義をインポートします。 + +```typescript +import { config } from "dotenv" +config() +``` + +`.env`を読み込んでURLを取得します。 + +```typescript +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.URL), +}) +``` + +Viemクライアントを作成します。 ブロックチェーンから読み取るだけなので、このクライアントには秘密鍵は必要ありません。 + +```typescript +const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82" +const fromBlock = 16859812n +const toBlock = 16873372n +``` + +疑わしいERC-20コントラクトのアドレスと、イベントを検索するブロックの範囲です。 ノードプロバイダーは通常、帯域幅が高価になる可能性があるため、イベントを読み取る能力を制限します。 幸いなことに、`wARB`は18時間使用されていなかったので、すべてのイベント(合計でわずか13件)を調べることができます。 + +```typescript +const approvalEvents = await client.getLogs({ + address: testedAddress, + fromBlock, + toBlock, + event: parseAbiItem( + "event Approval(address indexed _owner, address indexed _spender, uint256 _value)" + ), +}) +``` + +これはViemにイベント情報を要求する方法です。 フィールド名を含む正確なイベントシグネチャを提供すると、イベントが解析されます。 + +```typescript +const isContract = async (addr: Address): boolean => + await client.getBytecode({ address: addr }) +``` + +私たちのアルゴリズムは、外部所有アカウントにのみ適用されます。 `client.getBytecode`によってバイトコードが返された場合、それはこれがコントラクトであることを意味し、スキップすべきです。 + +これまでにTypeScriptを使用したことがない場合、関数定義は少し奇妙に見えるかもしれません。 最初の(そして唯一の)パラメータが`addr`と呼ばれるだけでなく、それが`Address`型であることも伝えます。 同様に、`: boolean`の部分は、関数の戻り値がブール値であることをTypeScriptに伝えます。 + +```typescript +const getEventTxn = async (ev: Event): TransactionReceipt => + await client.getTransactionReceipt({ hash: ev.transactionHash }) +``` + +この関数は、イベントからトランザクションレシートを取得します。 トランザクションの宛先が何であったかを確認するために、レシートが必要です。 + +```typescript +const suspiciousApprovalEvent = async (ev : Event) : (Event | null) => { +``` + +これは最も重要な関数で、イベントが疑わしいかどうかを実際に判断するものです。 戻り値の型 `(Event | null)` は、この関数が `Event` または `null` のいずれかを返すことができることを TypeScript に伝えます。 イベントが疑わしくない場合は`null`を返します。 + +```typescript +const owner = ev.args._owner +``` + +Viemはフィールド名を持っているので、イベントを解析してくれました。 `_owner`は、使用されるトークンの所有者です。 + +```typescript +// コントラクトによる承認は疑わしくない +if (await isContract(owner)) return null +``` + +所有者がコントラクトである場合、この承認は疑わしくないと仮定します。 コントラクトの承認が疑わしいかどうかを確認するには、トランザクションの完全な実行を追跡して、それが所有者コントラクトに到達したかどうか、そしてそのコントラクトがERC-20コントラクトを直接呼び出したかどうかを確認する必要があります。 それは、私たちが望むよりもはるかにリソースを消費します。 + +```typescript +const txn = await getEventTxn(ev) +``` + +承認が外部所有アカウントからのものである場合、それを引き起こしたトランザクションを取得します。 + +```typescript +// 承認は、トランザクションの`from`ではないEOA所有者からのものである場合、疑わしい +if (owner.toLowerCase() != txn.from.toLowerCase()) return ev +``` + +アドレスは16進数なので文字が含まれているため、単純に文字列の等価性をチェックすることはできません。 例えば、`txn.from`では、それらの文字はすべて小文字です。 `ev.args._owner`のような他のケースでは、アドレスは[エラー識別のために大文字と小文字が混在しています](https://eips.ethereum.org/EIPS/eip-55)。 + +しかし、トランザクションが所有者からのものではなく、その所有者が外部所有である場合、それは疑わしいトランザクションです。 + +```typescript +// トランザクションの宛先が調査中のERC-20コントラクトでない場合も +// 疑わしいです +if (txn.to.toLowerCase() != testedAddress) return ev +``` + +同様に、トランザクションの`to`アドレス、つまり最初に呼び出されたコントラクトが、調査対象のERC-20コントラクトでない場合も疑わしいです。 + +```typescript + // 疑わしい理由がない場合は、nullを返します。 + return null +} +``` + +どちらの条件も真でない場合、`Approval`イベントは疑わしくありません。 + +```typescript +const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev)) +const testResults = (await Promise.all(testPromises)).filter((x) => x != null) + +console.log(testResults) +``` + +[`async`関数](https://www.w3schools.com/js/js_async.asp)は`Promise`オブジェクトを返します。 一般的な構文 `await x()` を使用すると、その `Promise` が満たされるまで待ってから処理を続行します。 これはプログラムしやすく、追いやすいですが、非効率的でもあります。 特定のイベントの`Promise`が満たされるのを待っている間に、次のイベントの作業にすでに取りかかることができます。 + +ここでは[`map`](https://www.w3schools.com/jsref/jsref_map.asp)を使用して`Promise`オブジェクトの配列を作成します。 次に、[`Promise.all`](https://www.javascripttutorial.net/es6/javascript-promise-all/)を使用して、それらのすべてのプロミスが解決されるのを待ちます。 その後、それらの結果を[`filter`](https://www.w3schools.com/jsref/jsref_filter.asp)して、疑わしくないイベントを削除します。 + +### 疑わしい`Transfer`イベント {#suspicious-transfer-events} + +詐欺トークンを特定するもう一つの可能性のある方法は、疑わしい送金があるかどうかを確認することです。 例えば、それほど多くのトークンを持っていないアカウントからの送金などです。 [このテストの実装方法](https://github.com/qbzzt/20230915-scam-token-detection/blob/main/susTransfer.ts)を見ることができますが、`wARB`にはこの問題はありません。 + +## 結論 {#conclusion} + +詐欺が完全に正常なERC-20トークンコントラクトを使用し、それが何も実体を表していないだけの場合があるため、ERC-20詐欺の自動検出は[偽陰性](https://en.wikipedia.org/wiki/False_positives_and_false_negatives#False_negative_error)に悩まされます。 したがって、常に_信頼できる情報源からトークンアドレスを取得する_ように努めるべきです。 + +自動検出は、DeFiピースのような、多くのトークンがあり、それらを自動的に処理する必要がある特定のケースで役立ちます。 しかし、いつものように[caveat emptor](https://www.investopedia.com/terms/c/caveatemptor.asp)(買い手注意)、自分で調査し、ユーザーにも同様に行うよう奨励してください。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/secret-state/index.md b/public/content/translations/ja/developers/tutorials/secret-state/index.md new file mode 100644 index 00000000000..d878fac89c8 --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/secret-state/index.md @@ -0,0 +1,741 @@ +--- +title: "秘密のステートにゼロ知識を使用する" +description: "オンチェーンゲームは隠された情報を保持できないため、制限があります。 このチュートリアルを読むと、読者はゼロ知識証明とサーバーコンポーネントを組み合わせて、オフチェーンコンポーネントで秘密のステートを持つ検証可能なゲームを作成できるようになります。 これを行うための手法は、マインスイーパゲームを作成することで実証されます。" +author: Ori Pomerantz +tags: + [ + "サーバー", + "オフチェーン", + "中央集権型", + "ゼロ知識", + "zokrates", + "mud" + ] +skill: advanced +lang: ja +published: 2025-03-15 +--- + +ブロックチェーンには秘密はありません。 ブロックチェーンに投稿されたものはすべて、誰でも読むことができます。 ブロックチェーンは誰でも検証できることに基づいているため、これが必要です。 しかし、ゲームはしばしば秘密のステートに依存します。 例えば、[マインスイーパ](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\))というゲームは、ブロックチェーンエクスプローラーでマップを見ることができれば、まったく意味がありません。 + +最も簡単な解決策は、[サーバーコンポーネント](/developers/tutorials/server-components/)を使用して秘密のステートを保持することです。 しかし、私たちがブロックチェーンを使用する理由は、ゲームデベロッパーによる不正行為を防ぐためです。 サーバーコンポーネントの誠実性を確保する必要があります。 サーバーはステートのハッシュを提供し、[ゼロ知識証明](/zero-knowledge-proofs/#why-zero-knowledge-proofs-are-important)を使用して、動きの結果を計算するために使用されたステートが正しいものであることを証明できます。 + +この記事を読んだ後、あなたはこの種の秘密のステートを保持するサーバー、ステートを表示するためのクライアント、そして両者間の通信のためのオンチェーンコンポーネントを作成する方法を知るでしょう。 使用する主なツールは次のとおりです。 + +| ツール | 目的 | 検証済みバージョン | +| --------------------------------------------- | --------------------------- | --------------------------------------: | +| [Zokrates](https://zokrates.github.io/) | ゼロ知識証明とその検証 | 1.1.9 | +| [Typescript](https://www.typescriptlang.org/) | サーバーとクライアントの両方のためのプログラミング言語 | 5.4.2 | +| [Node](https://nodejs.org/en) | サーバーの実行 | 20.18.2 | +| [Viem](https://viem.sh/) | ブロックチェーンとの通信 | 2.9.20 | +| [MUD](https://mud.dev/) | オンチェーンデータ管理 | 2.0.12 | +| [React](https://react.dev/) | クライアントのユーザーインターフェース | 18.2.0 | +| [Vite](https://vitejs.dev/) | クライアントコードの提供 | 4.2.1 | + +## マインスイーパの例 {#minesweeper} + +[マインスイーパ](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\))は、地雷原のある秘密のマップを含むゲームです。 プレイヤーは特定の場所を掘ることを選択します。 その場所に地雷があれば、ゲームオーバーです。 そうでなければ、プレイヤーはその場所を囲む8つのマスにある地雷の数を取得します。 + +このアプリケーションは、[key-valueデータベース](https://aws.amazon.com/nosql/key-value/)を使用してデータをオンチェーンに保存し、そのデータをオフチェーンコンポーネントと自動的に同期させるフレームワークである[MUD](https://mud.dev/)を使用して書かれています。 同期に加えて、MUDはアクセス制御を容易にし、他のユーザーが私たちのアプリケーションをパーミッションレスで[拡張](https://mud.dev/guides/extending-a-world)できるようにします。 + +### マインスイーパの例の実行 {#running-minesweeper-example} + +マインスイーパの例を実行するには: + +1. [前提条件がインストールされている](https://mud.dev/quickstart#prerequisites)ことを確認してください:[Node](https://mud.dev/quickstart#prerequisites)、[Foundry](https://book.getfoundry.sh/getting-started/installation)、[`git`](https://git-scm.com/downloads)、[`pnpm`](https://git-scm.com/downloads)、そして[`mprocs`](https://github.com/pvolok/mprocs)。 + +2. リポジトリをクローンします。 + + ```sh copy + git clone https://github.com/qbzzt/20240901-secret-state.git + ``` + +3. パッケージをインストールします。 + + ```sh copy + cd 20240901-secret-state/ + pnpm install + npm install -g mprocs + ``` + + `pnpm install`の一部としてFoundryがインストールされた場合は、コマンドラインシェルを再起動する必要があります。 + +4. コントラクトをコンパイルする + + ```sh copy + cd packages/contracts + forge build + cd ../.. + ``` + +5. プログラム([anvil](https://book.getfoundry.sh/anvil/)ブロックチェーンを含む)を起動し、待ちます。 + + ```sh copy + mprocs + ``` + + 起動には時間がかかることに注意してください。 進捗を確認するには、まず下矢印を使用して _contracts_ タブまでスクロールし、MUDコントラクトがデプロイされていることを確認します。 「_Waiting for file changes…_」というメッセージが表示されたら、コントラクトはデプロイされ、さらなる進捗は _server_ タブで行われます。 そこで、「_Verifier address: 0x...._」というメッセージが表示されるまで待ちます。 + + このステップが成功すると、`mprocs`画面が表示され、左側に異なるプロセス、右側に現在選択されているプロセスのコンソール出力が表示されます。 + + ![mprocs画面](./mprocs.png) + + `mprocs`に問題がある場合は、4つのプロセスをそれぞれ独自のコマンドラインウィンドウで手動で実行できます: + + - **Anvil** + + ```sh + cd packages/contracts + anvil --base-fee 0 --block-time 2 + ``` + + - **コントラクト** + + ```sh + cd packages/contracts + pnpm mud dev-contracts --rpc http://127.0.0.1:8545 + ``` + + - **サーバー** + + ```sh + cd packages/server + pnpm start + ``` + + - **クライアント** + + ```sh + cd packages/client + pnpm run dev + ``` + +6. これで[クライアント](http://localhost:3000)にアクセスし、**New Game**をクリックしてプレイを開始できます。 + +### テーブル {#tables} + +オンチェーンには[いくつかのテーブル](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts)が必要です。 + +- `Configuration`: このテーブルはシングルトンであり、キーはなく、単一のレコードを持ちます。 ゲームの構成情報を保持するために使用されます: + - `height`:地雷原の高さ + - `width`:地雷原の幅 + - `numberOfBombs`:各地雷原の爆弾の数 + +- `VerifierAddress`: このテーブルもシングルトンです。 構成の一部である、検証者コントラクトのアドレス(`verifier`)を保持するために使用されます。 この情報を`Configuration`テーブルに入れることもできましたが、サーバーという別のコンポーネントによって設定されるため、別のテーブルに入れる方が簡単です。 + +- `PlayerGame`: キーはプレイヤーのアドレスです。 データは次のとおりです: + + - `gameId`:プレイヤーがプレイしているマップのハッシュである32バイトの値(ゲーム識別子)。 + - `win`:プレイヤーがゲームに勝ったかどうかを示すブール値。 + - `lose`:プレイヤーがゲームに負けたかどうかを示すブール値。 + - `digNumber`:ゲームでの成功した採掘の数。 + +- `GamePlayer`: このテーブルは、`gameId`からプレイヤーアドレスへの逆マッピングを保持します。 + +- `Map`: キーは3つの値のタプルです: + + - `gameId`:プレイヤーがプレイしているマップのハッシュである32バイトの値(ゲーム識別子)。 + - `x` 座標 + - `y` 座標 + + 値は単一の数値です。 爆弾が検出された場合は255です。 それ以外の場合は、その場所の周りの爆弾の数に1を加えたものです。 爆弾の数だけを使用することはできません。なぜなら、デフォルトではEVM内のすべてのストレージとMUD内のすべての行の値がゼロだからです。 「プレイヤーはまだここを掘っていない」と「プレイヤーはここを掘って、周りに爆弾がゼロであることを見つけた」とを区別する必要があります。 + +さらに、クライアントとサーバー間の通信はオンチェーンコンポーネントを介して行われます。 これもテーブルを使用して実装されています。 + +- `PendingGame`: 新しいゲームを開始するための未処理のリクエスト。 +- `PendingDig`: 特定のゲームの特定の場所で掘るための未処理のリクエスト。 これは[オフチェーンテーブル](https://mud.dev/store/tables#types-of-tables)であり、EVMストレージには書き込まれず、イベントを使用してオフチェーンでのみ読み取り可能です。 + +### 実行とデータフロー {#execution-data-flows} + +これらのフローは、クライアント、オンチェーンコンポーネント、サーバー間の実行を調整します。 + +#### 初期化 {#initialization-flow} + +`mprocs`を実行すると、次のステップが実行されます: + +1. [`mprocs`](https://github.com/pvolok/mprocs)は4つのコンポーネントを実行します: + + - [Anvil](https://book.getfoundry.sh/anvil/)、ローカルブロックチェーンを実行します + - [Contracts](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/contracts)、MUDのコントラクトをコンパイル(必要に応じて)し、デプロイします + - [Client](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/client)、UIとクライアントコードをWebブラウザに提供するために[Vite](https://vitejs.dev/)を実行します。 + - [Server](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/server)、サーバーアクションを実行します + +2. `contracts`パッケージはMUDコントラクトをデプロイし、その後[ `PostDeploy.s.sol` スクリプト](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol)を実行します。 このスクリプトは構成を設定します。 githubのコードは[10x5の地雷原に8つの地雷があること](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol#L23)を指定しています。 + +3. [サーバー](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts)は[MUDの設定](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L6)から始まります。 とりわけ、これによりデータ同期が有効になり、関連するテーブルのコピーがサーバーのメモリに存在することになります。 + +4. サーバーは、[`Configuration`テーブルが変更されたときに](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L23)実行される関数をサブスクライブします。 [この関数](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L24-L168)は、`PostDeploy.s.sol`が実行されてテーブルを変更した後に呼び出されます。 + +5. サーバーの初期化関数が構成を取得すると、[サーバーのゼロ知識部分](#using-zokrates-from-typescript)を初期化するために[`zkFunctions`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L34-L35)を呼び出します。 ゼロ知識関数は地雷原の幅と高さを定数として持つ必要があるため、構成を取得するまでこれは実行できません。 + +6. サーバーのゼロ知識部分が初期化された後、次のステップは[ゼロ知識検証コントラクトをブロックチェーンにデプロイ](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L42-L53)し、MUDで検証対象のアドレスを設定することです。 + +7. 最後に、プレイヤーが[新しいゲームの開始](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71)または[既存のゲームでの採掘](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73-L108)をリクエストしたときに表示されるように、アップデートをサブスクライブします。 + +#### 新しいゲーム {#new-game-flow} + +これはプレイヤーが新しいゲームをリクエストしたときに起こることです。 + +1. このプレイヤーに進行中のゲームがない場合、またはゲームIDがゼロのゲームがある場合、クライアントは[新しいゲームボタン](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175)を表示します。 ユーザーがこのボタンを押すと、[Reactは`newGame`関数を実行します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L96)。 + +2. [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L43-L46)は`System`コールです。 MUDでは、すべての呼び出しは`World`コントラクトを経由し、ほとんどの場合、`__`を呼び出します。 この場合、呼び出しは`app__newGame`であり、MUDはそれを[`GameSystem`の`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L16-L22)にルーティングします。 + +3. オンチェーン関数は、プレイヤーが進行中のゲームを持っていないことを確認し、持っていない場合は[`PendingGame`テーブルにリクエストを追加します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L21)。 + +4. サーバーは`PendingGame`の変更を検出し、[サブスクライブされた関数を実行します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71)。 この関数は[`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L110-L114)を呼び出し、それがさらに[`createGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L116-L144)を呼び出します。 + +5. `createGame`が最初に行うことは、[適切な数の地雷を持つランダムなマップを作成することです](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L120-L135)。 次に、Zokratesに必要な、空白の境界線を持つマップを作成するために[`makeMapBorders`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L147-L166)を呼び出します。 最後に、`createGame`は[`calculateMapHash`](#calculateMapHash)を呼び出して、ゲームIDとして使用されるマップのハッシュを取得します。 + +6. `newGame`関数は新しいゲームを`gamesInProgress`に追加します。 + +7. サーバーが最後に行うことは、オンチェーンにある[`app__newGameResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L38-L43)を呼び出すことです。 この関数は、アクセス制御を有効にするために、別の`System`、[`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol)にあります。 アクセス制御は、[MUD構成ファイル](https://mud.dev/config)、[`mud.config.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts#L67-L72)で定義されています。 + + アクセスリストは、単一のアドレスのみが`System`を呼び出すことを許可します。 これにより、サーバー関数へのアクセスが単一のアドレスに制限されるため、誰もサーバーになりすますことはできません。 + +8. オンチェーンコンポーネントは関連するテーブルを更新します: + + - `PlayerGame`でゲームを作成します。 + - `GamePlayer`で逆マッピングを設定します。 + - `PendingGame`からリクエストを削除します。 + +9. サーバーは`PendingGame`の変更を識別しますが、[`wantsGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L58-L60)がfalseであるため、何もしません。 + +10. クライアントでは、[`gameRecord`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L143-L148)はプレイヤーのアドレスの`PlayerGame`エントリに設定されます。 `PlayerGame`が変更されると、`gameRecord`も変更されます。 + +11. `gameRecord`に値があり、ゲームが勝利または敗北していない場合、クライアントは[マップを表示します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190)。 + +#### 採掘 {#dig-flow} + +1. プレイヤーは[マップセルのボタンをクリックし](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L188)、[ `dig` 関数](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L33-L36)を呼び出します。 この関数は[オンチェーンで `dig` を呼び出します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L24-L32)。 + +2. オンチェーンコンポーネントは[いくつかのサニティチェックを実行し](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L25-L30)、成功した場合、採掘リクエストを[`PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L31)に追加します。 + +3. サーバーは[`PendingDig`の変更を検出します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73)。 [それが有効な場合](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L75-L84)、結果とそれが有効であることの証明の両方を生成するために[ゼロ知識コードを呼び出します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L86-L95)(後述)。 + +4. [サーバー](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L97-L107)はオンチェーンで[`digResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L45-L64)を呼び出します。 + +5. `digResponse`は2つのことを行います。 まず、[ゼロ知識証明をチェックします](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L47-L61)。 次に、証明がチェックアウトされた場合、実際に結果を処理するために[`processDigResult`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L67-L86)を呼び出します。 + +6. `processDigResult`は、ゲームが[負けた](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L76-L78)か[勝った](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L83-L86)かを確認し、[オンチェーンマップである`Map`を更新します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L80)。 + +7. クライアントはアップデートを自動的に取得し、[プレイヤーに表示されるマップを更新し](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190)、該当する場合は勝ちか負けかをプレイヤーに伝えます。 + +## Zokratesの使用 {#using-zokrates} + +上記で説明したフローでは、ゼロ知識の部分をブラックボックスとして扱い、スキップしました。 では、それを開いて、そのコードがどのように書かれているかを見てみましょう。 + +### マップのハッシュ化 {#hashing-map} + +使用するZokratesハッシュ関数である[Poseidon](https://www.poseidon-hash.info)を実装するために、[このJavaScriptコード](https://github.com/ZK-Plus/ICBC24_Tutorial_Compute-Offchain-Verify-onchain/tree/solutions/exercise)を使用できます。 しかし、これは高速ですが、Zokratesハッシュ関数を使用して行うよりも複雑になります。 これはチュートリアルなので、コードはパフォーマンスではなく、シンプルさのために最適化されています。 したがって、2つの異なるZokratesプログラムが必要です。1つはマップのハッシュを計算するためだけのもので(`hash`)、もう1つは実際にマップ上の場所での採掘結果のゼロ知識証明を作成するためのものです(`dig`)。 + +### ハッシュ関数 {#hash-function} + +これはマップのハッシュを計算する関数です。 このコードを一行ずつ見ていきましょう。 + +``` +import "hashes/poseidon/poseidon.zok" as poseidon; +import "utils/pack/bool/pack128.zok" as pack128; +``` + +これら2行は、[Zokrates標準ライブラリ](https://zokrates.github.io/toolbox/stdlib.html)から2つの関数をインポートします。 [最初の関数](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/hashes/poseidon/poseidon.zok)は[Poseidonハッシュ](https://www.poseidon-hash.info/)です。 これは[`field`要素](https://zokrates.github.io/language/types.html#field)の配列を受け取り、`field`を返します。 + +Zokratesのフィールド要素は通常256ビット未満ですが、それほど短くはありません。 コードを簡略化するために、マップを最大512ビットに制限し、4つのフィールドの配列をハッシュ化し、各フィールドでは128ビットのみを使用します。 [`pack128`関数](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/utils/pack/bool/pack128.zok)は、この目的のために128ビットの配列を`field`に変更します。 + +``` + def hashMap(bool[${width+2}][${height+2}] map) -> field { +``` + +この行は関数定義を開始します。 `hashMap`は、`map`という単一のパラメータ、2次元の`bool`(ean)配列を取得します。 マップのサイズは、[後で説明する](#why-map-border)理由により、`width+2` x `height+2`です。 + +Zokratesプログラムはこのアプリケーションで[テンプレート文字列](https://www.w3schools.com/js/js_string_templates.asp)として保存されているため、`${width+2}`と`${height+2}`を使用できます。 `${`と`}`の間のコードはJavaScriptによって評価され、この方法でプログラムは異なるマップサイズに使用できます。 マップパラメータには、爆弾のない1つの場所幅の境界線が周囲にあり、これが幅と高さに2を加える必要がある理由です。 + +戻り値はハッシュを含む`field`です。 + +``` + bool[512] mut map1d = [false; 512]; +``` + +マップは2次元です。 しかし、`pack128`関数は2次元配列では機能しません。 そこで、まず`map1d`を使用してマップを512バイトの配列にフラット化します。 デフォルトではZokratesの変数は定数ですが、ループ内でこの配列に値を代入する必要があるため、[`mut`](https://zokrates.github.io/language/variables.html#mutability)として定義します。 + +Zokratesには`undefined`がないため、配列を初期化する必要があります。 `[false; 512]`という式は、[512個の`false`値の配列](https://zokrates.github.io/language/types.html#declaration-and-initialization)を意味します。 + +``` + u32 mut counter = 0; +``` + +また、`map1d`に既に埋め込まれたビットとそうでないビットを区別するためにカウンターも必要です。 + +``` + for u32 x in 0..${width+2} { +``` + +これはZokratesで[`for`ループ](https://zokrates.github.io/language/control_flow.html#for-loops)を宣言する方法です。 Zokratesの`for`ループは固定の境界を持つ必要があります。なぜなら、ループのように見えますが、コンパイラは実際にはそれを「展開」するからです。 `width`はTypeScriptコードがコンパイラを呼び出す前に設定されるため、式`${width+2}`はコンパイル時定数です。 + +``` + for u32 y in 0..${height+2} { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } +``` + +マップ内のすべての場所について、その値を`map1d`配列に入れ、カウンターをインクリメントします。 + +``` + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; +``` + +`pack128`は`map1d`から4つの`field`値の配列を作成します。 Zokratesでは`array[a..b]`は`a`で始まり`b-1`で終わる配列のスライスを意味します。 + +``` + return poseidon(hashMe); +} +``` + +`poseidon`を使用してこの配列をハッシュに変換します。 + +### ハッシュプログラム {#hash-program} + +サーバーはゲーム識別子を作成するために`hashMap`を直接呼び出す必要があります。 しかし、Zokratesはプログラムを開始するために`main`関数しか呼び出せないため、ハッシュ関数を呼び出す`main`を持つプログラムを作成します。 + +``` +${hashFragment} + +def main(bool[${width+2}][${height+2}] map) -> field { + return hashMap(map); +} +``` + +### digプログラム {#dig-program} + +これはアプリケーションのゼロ知識部分の中心であり、採掘結果を検証するために使用される証明を生成します。 + +``` +${hashFragment} + +// (x,y)の位置にある地雷の数 +def map2mineCount(bool[${width+2}][${height+2}] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; +} +``` + +#### なぜマップの境界線が必要なのか {#why-map-border} + +ゼロ知識証明は、`if`文に簡単な同等のものがない[算術回路](https://medium.com/web3studio/simple-explanations-of-arithmetic-circuits-and-zero-knowledge-proofs-806e59a79785)を使用します。 代わりに、[条件演算子](https://en.wikipedia.org/wiki/Ternary_conditional_operator)の同等のものを使用します。 `a`がゼロか1のいずれかである場合、`if a { b } else { c }`は`ab+(1-a)c`として計算できます。 + +このため、Zokratesの`if`文は常に両方の分岐を評価します。 たとえば、次のコードがあるとします。 + +``` +bool[5] arr = [false; 5]; +u32 index=10; +return if index>4 { 0 } else { arr[index] } +``` + +後でその値がゼロで乗算されるにもかかわらず、`arr[10]`を計算する必要があるため、エラーが発生します。 + +これが、マップの周囲に1つの場所幅の境界線が必要な理由です。 場所の周りの地雷の総数を計算する必要があり、それは、採掘している場所の上下、左右の場所を見る必要があることを意味します。 つまり、これらの場所はZokratesに提供されるマップ配列に存在する必要があります。 + +``` +def main(private bool[${width+2}][${height+2}] map, u32 x, u32 y) -> (field, u8) { +``` + +デフォルトでは、Zokratesの証明にはその入力が含まれています。 あるスポットの周りに5つの地雷があると知っていても、実際にどのスポットかがわからなければ意味がありません(また、自分のリクエストと照合するだけではダメです。なぜなら、証明者は異なる値を使用して、それについて教えない可能性があるからです)。 しかし、マップを秘密にしておく必要がありますが、Zokratesに提供する必要もあります。 解決策は、証明によって_明らかにされない_`private`パラメータを使用することです。 + +これにより、別の悪用の機会が開かれます。 証明者は正しい座標を使用するかもしれませんが、場所の周りや場所自体に任意の数の地雷を持つマップを作成する可能性があります。 この悪用を防ぐために、ゼロ知識証明にゲーム識別子であるマップのハッシュを含めます。 + +``` + return (hashMap(map), +``` + +ここでの戻り値は、採掘結果だけでなく、マップのハッシュ配列も含むタプルです。 + +``` + if map2mineCount(map, x, y) > 0 { 0xFF } else { +``` + +場所自体に爆弾がある場合、特別な値として255を使用します。 + +``` + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); +} +``` + +プレイヤーが地雷を踏んでいない場合、その場所の周りの地雷の数を合計して返します。 + +### TypeScriptからZokratesを使用する {#using-zokrates-from-typescript} + +Zokratesにはコマンドラインインターフェースがありますが、このプログラムでは[TypeScriptコード](https://zokrates.github.io/toolbox/zokrates_js.html)で使用します。 + +Zokratesの定義を含むライブラリは[`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts)と呼ばれます。 + +```typescript +import { initialize as zokratesInitialize } from "zokrates-js" +``` + +[Zokrates JavaScriptバインディング](https://zokrates.github.io/toolbox/zokrates_js.html)をインポートします。 Zokratesのすべての定義に解決されるプロミスを返すため、[`initialize`](https://zokrates.github.io/toolbox/zokrates_js.html#initialize)関数のみが必要です。 + +```typescript +export const zkFunctions = async (width: number, height: number) : Promise => { +``` + +Zokrates自体と同様に、1つの関数のみをエクスポートします。これも[非同期](https://www.w3schools.com/js/js_async.asp)です。 最終的に返されるとき、以下で見るようにいくつかの関数を提供します。 + +```typescript +const zokrates = await zokratesInitialize() +``` + +Zokratesを初期化し、ライブラリから必要なものをすべて取得します。 + +```typescript +const hashFragment = ` + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + . + . + . + } + ` + +const hashProgram = ` + ${hashFragment} + . + . + . + ` + +const digProgram = ` + ${hashFragment} + . + . + . + ` +``` + +次に、上記で見たハッシュ関数と2つのZokratesプログラムがあります。 + +```typescript +const digCompiled = zokrates.compile(digProgram) +const hashCompiled = zokrates.compile(hashProgram) +``` + +ここでこれらのプログラムをコンパイルします。 + +```typescript +// ゼロ知識検証用のキーを作成します。 +// 本番システムでは、セットアップセレモニーを使用することをお勧めします。 +// (https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony)。 +const keySetupResults = zokrates.setup(digCompiled.program, "") +const verifierKey = keySetupResults.vk +const proverKey = keySetupResults.pk +``` + +本番システムでは、より複雑な[セットアップセレモニー](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony)を使用するかもしれませんが、デモンストレーションにはこれで十分です。 ユーザーが証明者キーを知っていても問題ありません。それが真実でない限り、それを使って物事を証明することはできません。 エントロピー(2番目のパラメータ、`""`)を指定しているため、結果は常に同じになります。 + +**注:** Zokratesプログラムのコンパイルとキーの作成は遅いプロセスです。 毎回繰り返す必要はなく、マップサイズが変更されたときだけです。 本番システムでは、一度実行し、出力を保存します。 ここでそれをしていない唯一の理由は、単純さのためです。 + +#### `calculateMapHash` {#calculateMapHash} + +```typescript +const calculateMapHash = function (hashMe: boolean[][]): string { + return ( + "0x" + + BigInt(zokrates.computeWitness(hashCompiled, [hashMe]).output.slice(1, -1)) + .toString(16) + .padStart(64, "0") + ) +} +``` + +`computeWitness`関数(https://zokrates.github.io/toolbox/zokrates_js.html#computewitnessartifacts-args-options)は実際にZokratesプログラムを実行します。 これは2つのフィールドを持つ構造体を返します:JSON文字列としてのプログラムの出力である`output`、および結果のゼロ知識証明を作成するために必要な情報である`witness`です。 ここでは出力のみが必要です。 + +出力は`"31337"`の形式の文字列で、引用符で囲まれた10進数です。 しかし、`viem`に必要な出力は`0x60A7`の形式の16進数です。 そこで、`.slice(1,-1)`を使用して引用符を削除し、次に`BigInt`を使用して残りの文字列(10進数)を[`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)に変換します。 `.toString(16)`はこの`BigInt`を16進文字列に変換し、`"0x"+`は16進数のマーカーを追加します。 + +```typescript +// 採掘し、結果のゼロ知識証明を返します +// (サーバーサイドコード) +``` + +ゼロ知識証明には、公開入力(`x`と`y`)と結果(マップのハッシュと爆弾の数)が含まれます。 + +```typescript + const zkDig = function(map: boolean[][], x: number, y: number) : any { + if (x<0 || x>=width || y<0 || y>=height) + throw new Error("Trying to dig outside the map") +``` + +Zokratesでインデックスが範囲外かどうかを確認するのは問題なので、ここで行います。 + +```typescript +const runResults = zokrates.computeWitness(digCompiled, [map, `${x}`, `${y}`]) +``` + +digプログラムを実行します。 + +```typescript + const proof = zokrates.generateProof( + digCompiled.program, + runResults.witness, + proverKey) + + return proof + } +``` + +[`generateProof`](https://zokrates.github.io/toolbox/zokrates_js.html#generateproofprogram-witness-provingkey-entropy)を使用して証明を返し、それを返します。 + +```typescript +const solidityVerifier = ` + // マップサイズ: ${width} x ${height} + \n${zokrates.exportSolidityVerifier(verifierKey)} + ` +``` + +Solidity検証者、ブロックチェーンにデプロイして`digCompiled.program`によって生成された証明を検証するために使用できるスマートコントラクト。 + +```typescript + return { + zkDig, + calculateMapHash, + solidityVerifier, + } +} +``` + +最後に、他のコードが必要とする可能性のあるすべてを返します。 + +## セキュリティテスト {#security-tests} + +機能のバグはいずれ明らかになるため、セキュリティテストは重要です。 しかし、アプリケーションが安全でない場合、誰かが不正行為をして他人のリソースを手に入れてしまうまで、それが長期間隠されたままである可能性が高いです。 + +### パーミッション {#permissions} + +このゲームには特権を持つエンティティが1つ、サーバーがあります。 これは、[`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol)の関数を呼び出すことを許可された唯一のユーザーです。 [`cast`](https://book.getfoundry.sh/cast/)を使用して、許可された関数への呼び出しがサーバーアカウントとしてのみ許可されていることを確認できます。 + +[サーバーの秘密鍵は`setupNetwork.ts`にあります](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/mud/setupNetwork.ts#L52)。 + +1. `anvil`(ブロックチェーン)を実行するコンピュータで、これらの環境変数を設定します。 + + ```sh copy + WORLD_ADDRESS=0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b + UNAUTHORIZED_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + AUTHORIZED_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + ``` + +2. `cast`を使用して、検証者アドレスを未承認のアドレスとして設定しようとします。 + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $UNAUTHORIZED_KEY + ``` + + `cast`が失敗を報告するだけでなく、ブラウザのゲームで**MUD Dev Tools**を開き、**Tables**をクリックして、**app\_\_VerifierAddress**を選択することもできます。 アドレスがゼロでないことを確認してください。 + +3. 検証者アドレスをサーバーのアドレスとして設定します。 + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $AUTHORIZED_KEY + ``` + + **app\_\_VerifiedAddress**のアドレスはゼロになるはずです。 + +同じ`System`内のすべてのMUD関数は同じアクセス制御を通過するため、このテストで十分だと考えます。 そうでない場合は、[`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol)の他の関数を確認できます。 + +### ゼロ知識の悪用 {#zero-knowledge-abuses} + +Zokratesを検証するための数学は、このチュートリアル(そして私の能力)の範囲を超えています。 しかし、ゼロ知識コードに対してさまざまなチェックを実行して、正しく行われていない場合に失敗することを確認できます。 これらのテストはすべて、[`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts)を変更し、アプリケーション全体を再起動する必要があります。 サーバープロセスを再起動するだけでは不十分です。なぜなら、アプリケーションが不可能な状態になるからです(プレイヤーは進行中のゲームを持っていますが、そのゲームはサーバーにとって利用できなくなっています)。 + +#### 間違った答え {#wrong-answer} + +最も単純な可能性は、ゼロ知識証明で間違った答えを提供することです。 そのためには、`zkDig`の内部に入り、[91行目を変更します](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L91): + +```ts +proof.inputs[3] = "0x" + "1".padStart(64, "0") +``` + +これは、正しい答えに関係なく、常に1つの爆弾があると主張することを意味します。 このバージョンでプレイしてみてください。`pnpm dev`画面の**server**タブに次のエラーが表示されます: + +``` + cause: { + code: 3, + message: 'execution reverted: revert: Zero knowledge verification fail', + data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000 +000000000000000000000000000000000000000000000000205a65726f206b6e6f776c6564676520766572696669636174696f6 +e206661696c' + }, +``` + +したがって、この種の不正行為は失敗します。 + +#### 間違った証明 {#wrong-proof} + +正しい情報を提供するが、証明データが間違っている場合はどうなりますか? では、91行目を次のように置き換えます。 + +```ts +proof.proof = { + a: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + b: [ + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ], + c: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], +} +``` + +それでも失敗しますが、今回は検証者呼び出し中に発生するため、理由なしで失敗します。 + +### ユーザーはどのようにしてゼロトラストコードを検証できますか? {#user-verify-zero-trust} + +スマートコントラクトは比較的簡単に検証できます。 通常、デベロッパーはソースコードをブロックエクスプローラーに公開し、ブロックエクスプローラーはソースコードが[コントラクトデプロイメントトランザクション](/developers/docs/smart-contracts/deploying/)のコードにコンパイルされることを確認します。 MUD `System`sの場合、これは[少し複雑です](https://mud.dev/cli/verify)が、それほどではありません。 + +ゼロ知識ではこれはより困難です。 検証者はいくつかの定数を含み、それらに対していくつかの計算を実行します。 これは、何が証明されているかを教えてくれません。 + +```solidity + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = Pairing.G1Point(uint256(0x0f43f4fe7b5c2326fed4ac6ed2f4003ab9ab4ea6f667c2bdd77afb068617ee16), uint256(0x25a77832283f9726935219b5f4678842cda465631e72dbb24708a97ba5d0ce6f)); + vk.beta = Pairing.G2Point([uint256(0x2cebd0fbd21aca01910581537b21ae4fed46bc0e524c055059aa164ba0a6b62b), uint256(0x18fd4a7bc386cf03a95af7163d5359165acc4e7961cb46519e6d9ee4a1e2b7e9)], [uint256(0x11449dee0199ef6d8eebfe43b548e875c69e7ce37705ee9a00c81fe52f11a009), uint256(0x066d0c83b32800d3f335bb9e8ed5e2924cf00e77e6ec28178592eac9898e1a00)]); +``` + +解決策は、少なくともブロックエクスプローラーがユーザーインターフェースにZokrates検証を追加するまでは、アプリケーション開発者がZokratesプログラムを利用可能にし、少なくとも一部のユーザーが適切な検証キーを使用して自分でコンパイルすることです。 + +そのためには: + +1. [Zokratesをインストールします](https://zokrates.github.io/gettingstarted.html)。 + +2. Zokratesプログラムを含む`dig.zok`というファイルを作成します。 以下のコードは、元のマップサイズ10x5を維持していることを前提としています。 + + ```zokrates + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + + def hashMap(bool[12][7] map) -> field { + bool[512] mut map1d = [false; 512]; + u32 mut counter = 0; + + for u32 x in 0..12 { + for u32 y in 0..7 { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } + + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; + + return poseidon(hashMe); + } + + + // (x,y)の位置にある地雷の数 + def map2mineCount(bool[12][7] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; + } + + def main(private bool[12][7] map, u32 x, u32 y) -> (field, u8) { + return (hashMap(map) , + if map2mineCount(map, x, y) > 0 { 0xFF } else { + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); + } + ``` + +3. Zokratesコードをコンパイルし、検証キーを作成します。 検証キーは、元のサーバーで使用されたのと同じエントロピーで作成する必要があります。[この場合は空の文字列です](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L67)。 + + ```sh copy + zokrates compile --input dig.zok + zokrates setup -e "" + ``` + +4. 自分でSolidity検証者を作成し、ブロックチェーン上のものと機能的に同一であることを確認します(サーバーはコメントを追加しますが、それは重要ではありません)。 + + ```sh copy + zokrates export-verifier + diff verifier.sol ~/20240901-secret-state/packages/contracts/src/verifier.sol + ``` + +## 設計上の決定 {#design} + +十分に複雑なアプリケーションでは、トレードオフを必要とする競合する設計目標があります。 いくつかのトレードオフと、現在の解決策が他の選択肢よりも好ましい理由を見てみましょう。 + +### なぜゼロ知識なのか {#why-zero-knowledge} + +マインスイーパには、実際にはゼロ知識は必要ありません。 サーバーは常にマップを保持し、ゲームが終了したときにすべてを明らかにすることができます。 その後、ゲームの終わりに、スマートコントラクトはマップのハッシュを計算し、それが一致することを確認し、一致しない場合はサーバーにペナルティを課すか、ゲームを完全に無視することができます。 + +このより単純な解決策を使用しなかったのは、明確な終了ステートを持つ短いゲームでのみ機能するためです。 ゲームが潜在的に無限である場合([自律的な世界](https://0xparc.org/blog/autonomous-worlds)の場合など)、ステートを明らかに_せずに_証明する解決策が必要です。 + +チュートリアルとして、この記事は理解しやすい短いゲームを必要としていましたが、このテクニックはより長いゲームに最も役立ちます。 + +### なぜZokratesなのか? {#why-zokrates} + +[Zokrates](https://zokrates.github.io/)は利用可能な唯一のゼロ知識ライブラリではありませんが、通常の[命令型](https://en.wikipedia.org/wiki/Imperative_programming)プログラミング言語に類似しており、ブール変数をサポートしています。 + +あなたのアプリケーションでは、要件が異なるため、[Circum](https://docs.circom.io/getting-started/installation/)または[Cairo](https://www.cairo-lang.org/tutorials/getting-started-with-cairo/)を使用することを好むかもしれません。 + +### Zokratesをいつコンパイルするか {#when-compile-zokrates} + +このプログラムでは、[サーバーが起動するたびに](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L60-L61)Zokratesプログラムをコンパイルします。 これは明らかにリソースの無駄ですが、これはチュートリアルであり、単純さのために最適化されています。 + +本番レベルのアプリケーションを書いていたとしたら、この地雷原サイズのコンパイル済みZokratesプログラムのファイルがあるかどうかを確認し、もしあればそれを使用するでしょう。 オンチェーンで検証者コントラクトをデプロイする場合も同様です。 + +### 検証者キーと証明者キーの作成 {#key-creation} + +[キーの作成](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L63-L69)は、特定の地雷原サイズに対して一度以上行う必要のない純粋な計算です。 繰り返しますが、単純さのために一度だけ行われます。 + +さらに、[セットアップセレモニー](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony)を使用することもできます。 セットアップセレモニーの利点は、ゼロ知識証明で不正行為をするためには、各参加者からのエントロピーまたはいくつかの中間結果が必要であることです。 少なくとも1人のセレモニー参加者が正直で、その情報を削除すれば、ゼロ知識証明は特定の攻撃から安全です。 しかし、情報がどこからでも削除されたことを確認する_メカニズムはありません_。 ゼロ知識証明が非常に重要な場合は、セットアップセレモニーに参加することをお勧めします。 + +ここでは、数十人の参加者がいた[perpetual powers of tau](https://github.com/privacy-scaling-explorations/perpetualpowersoftau)に依存しています。 おそらく十分に安全で、はるかに単純です。 また、キー作成中にエントロピーを追加しないため、ユーザーが[ゼロ知識構成を検証](#user-verify-zero-trust)しやすくなります。 + +### どこで検証するか {#where-verification} + +ゼロ知識証明はオンチェーン(ガスがかかる)またはクライアント([`verify`](https://zokrates.github.io/toolbox/zokrates_js.html#verifyverificationkey-proof)を使用)で検証できます。 私が前者を選んだのは、これにより[検証者を一度検証](#user-verify-zero-trust)し、コントラクトアドレスが同じままである限り変更されないと信頼できるからです。 検証がクライアントで行われた場合、クライアントをダウンロードするたびに受け取るコードを検証する必要があります。 + +また、このゲームはシングルプレイヤーですが、多くのブロックチェーンゲームはマルチプレイヤーです。 オンチェーン検証は、ゼロ知識証明を一度だけ検証することを意味します。 クライアントでそれを行うには、各クライアントが独立して検証する必要があります。 + +### マップをTypeScriptまたはZokratesでフラット化するか? {#where-flatten} + +一般に、処理がTypeScriptまたはZokratesのいずれかで行える場合、はるかに高速で、ゼロ知識証明を必要としないTypeScriptで行う方が優れています。 これが、例えば、Zokratesにハッシュを提供して、それが正しいことを検証させない理由です。 ハッシュ化はZokrates内部で行う必要がありますが、返されたハッシュとオンチェーンのハッシュとの照合は外部で行うことができます。 + +しかし、TypeScriptでできたにもかかわらず、私たちはまだ[Zokratesでマップをフラット化しています](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L15-L20)。 その理由は、私の意見では、他の選択肢がもっと悪いからです。 + +- Zokratesコードに1次元のブール値配列を提供し、`x*(height+2) + +y`のような式を使用して2次元マップを取得します。 これにより[コード](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L44-L47)が多少複雑になるため、チュートリアルにはパフォーマンスの向上が価値がないと判断しました。 + +- Zokratesに1次元配列と2次元配列の両方を送信します。 しかし、この解決策では何も得られません。 Zokratesコードは、提供された1次元配列が本当に2次元配列の正しい表現であることを検証する必要があります。 したがって、パフォーマンスの向上はありません。 + +- Zokratesで2次元配列をフラット化します。 これが最も簡単な選択肢なので、私はそれを選びました。 + +### マップの保存場所 {#where-store-maps} + +このアプリケーションでは、[`gamesInProgress`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L20)は単にメモリ内の変数です。 これは、サーバーがダウンして再起動する必要がある場合、保存されていたすべての情報が失われることを意味します。 プレイヤーはゲームを続行できないだけでなく、オンチェーンコンポーネントがまだゲームが進行中であると考えているため、新しいゲームを開始することさえできません。 + +これは、この情報をデータベースに保存する本番システムにとっては明らかに悪い設計です。 ここで変数を使用した唯一の理由は、これがチュートリアルであり、単純さが主な考慮事項であるためです。 + +## 結論:どのような条件下でこれが適切なテクニックですか? {#conclusion} + +これで、オンチェーンに属さない秘密のステートを保存するサーバーでゲームを書く方法がわかりました。 しかし、どのような場合にそれを行うべきでしょうか? 主な考慮事項は2つあります。 + +- _長期間実行されるゲーム_:[上で述べたように](#why-zero-knowledge)、短いゲームでは、ゲームが終了したらステートを公開し、すべてを検証させることができます。 しかし、ゲームが長い時間または無期限にかかり、ステートを秘密にしておく必要がある場合、それは選択肢ではありません。 + +- _ある程度の中央集権化が許容される_:ゼロ知識証明は、エンティティが結果を偽造していないという整合性を検証できます。 彼らができないことは、エンティティがまだ利用可能であり、メッセージに応答することを保証することです。 可用性も分散化する必要がある状況では、ゼロ知識証明は十分な解決策ではなく、[マルチパーティ計算](https://en.wikipedia.org/wiki/Secure_multi-party_computation)が必要です。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). + +### 謝辞 {#acknowledgements} + +- Alvaro Alonsoがこの記事の草稿を読み、Zokratesに関する私の誤解のいくつかを明らかにしてくれました。 + +残りの誤りは私の責任です。 diff --git a/public/content/translations/ja/developers/tutorials/secure-development-workflow/index.md b/public/content/translations/ja/developers/tutorials/secure-development-workflow/index.md index a8c2f9e347f..a89f823d73f 100644 --- a/public/content/translations/ja/developers/tutorials/secure-development-workflow/index.md +++ b/public/content/translations/ja/developers/tutorials/secure-development-workflow/index.md @@ -1,45 +1,42 @@ --- -title: スマートコントラクトのセキュリティ・チェックリスト -description: セキュアなスマートコントラクトを作成するための推奨ワークフロー +title: "スマートコントラクトのセキュリティ・チェックリスト" +description: "セキュアなスマートコントラクトを作成するための推奨ワークフロー" author: "Trailofbits" -tags: - - "スマートコントラクト" - - "セキュリティ" - - "Solidity" +tags: [ "スマート契約", "セキュリティ", "Solidity" ] skill: intermediate lang: ja published: 2020-09-07 -source: セキュアなコントラクトの開発 +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/blob/master/development-guidelines/workflow.md --- -## スマートコントラクトの開発チェックリスト {#smart-contract-development-checklist} +## スマートコントラクト開発チェックリスト {#smart-contract-development-checklist} スマートコントラクトを作成する際には、以下に挙げる大まかなプロセスに従って行うことをお勧めします。 既知のセキュリティ関連の問題点について確認します: -- [Slither](https://github.com/crytic/slither)で、コントラクトをレビューする。 Slitherには、40種類以上のよくある脆弱性を対象とする検出機能が搭載されています。 新しいコードが追加されるたびにレビューを実行して、クリーンな報告になるようにします(特定の問題を無視する必要がある場合は、トリアージモードを使用します)。 -- [Crytic](https://crytic.io/)で、コントラクトをレビューする。 Cryticでは、Slitherでは検出できな50種類の問題点を確認できます。 さらに、GitHubのプルリクエストに含まれるセキュリティ関連の問題点を簡単に発見できるので、チーム内の問題把握に役立ちます。 +- [Slither](https://github.com/crytic/slither)でコントラクトをレビューする。 Slitherには、40種類以上のよくある脆弱性を対象とする検出機能が搭載されています。 新しいコードが追加されるたびにレビューを実行して、クリーンな報告になるようにします(特定の問題を無視する必要がある場合は、トリアージモードを使用します)。 +- [Crytic](https://crytic.io/)でコントラクトをレビューする。 Cryticでは、Slitherでは検出できな50種類の問題点を確認できます。 さらに、GitHubのプルリクエストに含まれるセキュリティ関連の問題点を簡単に発見できるので、チーム内の問題把握に役立ちます。 あなたのコントラクトに含まれる特別な機能について検討する: -- コントラクトがアップグレード可能かどうか: [`slither-check-upgradeability`](https://github.com/crytic/slither/wiki/Upgradeability-Checks)または[Crytic](https://blog.trailofbits.com/2020/06/12/upgradeable-contracts-made-safer-with-crytic/)を使って、アップグレード可能性に関するコードに欠陥がないか確認します。 当チームでは、アップグレードの失敗につながる17のケースを文書化しています。 -- コントラクトは、ERC準拠を謳っていますか? [`slither-check-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance)で確認してください。 このツールでは、6種類の一般的な仕様に準拠していない場合、ただちに指摘されます。 -- サードパーティのトークンと統合予定ですか? 外部コントラクトを利用する事前に、この[トークン統合チェックリスト](/developers/tutorials/token-integration-checklist/)でレビューしてください。 +- コントラクトがアップグレード可能かどうか: [`slither-check-upgradeability`](https://github.com/crytic/slither/wiki/Upgradeability-Checks)または[Crytic](https://blog.trailofbits.com/2020/06/12/upgradeable-contracts-made-safer-with-crytic/)で、アップグレード可能性コードの欠陥をレビューする。 当チームでは、アップグレードの失敗につながる17のケースを文書化しています。 +- コントラクトは、ERC準拠を謳っていますか? [`slither-check-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance)でそれらをチェックする。 このツールでは、6種類の一般的な仕様に準拠していない場合、ただちに指摘されます。 +- サードパーティのトークンと統合予定ですか? 外部コントラクトに依存する前に、[トークン統合チェックリスト](/developers/tutorials/token-integration-checklist/)をレビューする。 コードにおける重要なセキュリティ関連の機能を、視覚的にチェックします。 -- Slitherの[inheritance-graph ](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph)プリンターをレビューします。 不注意によるシャドーイングやC3 linearizationにまつわる問題を回避してください。 -- Slitherで、[機能サマリー](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary)のプリンター(表示)をレビューします。 機能の可視性およびアクセス管理のレポートが作成されます。 -- Slitherの[vars-and-auth](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization)プリンタをレビューします。 状態変数に対するアクセス管理のレポートが作成されます。 +- Slitherの[inheritance-graph](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph)プリンターをレビューする。 不注意によるシャドーイングやC3 linearizationにまつわる問題を回避してください。 +- Slitherの[function-summary](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary)プリンターをレビューする。 機能の可視性およびアクセス管理のレポートが作成されます。 +- Slitherの[vars-and-auth](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization)プリンターをレビューする。 状態変数に対するアクセス管理のレポートが作成されます。 セキュリティ関連の重要な属性を文書化し、自動化されたテスト生成機能を用いて評価します: -- [コードにおけるセキュリティ属性を文書化する](/developers/tutorials/guide-to-smart-contract-security-tools/)方法について学びます。 最初は大変ですが、最良の結果を得る上で最も重要な作業です。 また、このチュートリアルのより高度なテクニックを活用する上でも、必須の作業です。 -- [Echidna](https://github.com/crytic/echidna)および[Manticore](https://manticore.readthedocs.io/en/latest/verifier.html)で使用するために、Solidityでセキュリティ関連の属性を定義します。 状態マシン、アクセス管理、算術演算、外部とのやりとり、および標準の遵守に焦点を当ててください。 -- [SlitherのPython API](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/)で、セキュリティ関連のプロパティを定義します。 継承、変数の依存関係、アクセス管理、およびその他の構造上の問題に焦点を当ててください。 -- [Crytic](https://crytic.io)を使用して、コミットごとにプロパティテストを実行します。 Cryticでは、セキュリティ属性に関するテストを実行、評価できるため、チーム全員がGitHubで合格したかどうか簡単に確認できます。 テストが不合格だった場合、コミットをブロックできます。 +- [コードのセキュリティプロパティを文書化する](/developers/tutorials/guide-to-smart-contract-security-tools/)方法を学ぶ。 最初は大変ですが、最良の結果を得る上で最も重要な作業です。 また、このチュートリアルのより高度なテクニックを活用する上でも、必須の作業です。 +- [Echidna](https://github.com/crytic/echidna)および[Manticore](https://manticore.readthedocs.io/en/latest/verifier.html)で使用するために、Solidityでセキュリティプロパティを定義する。 状態マシン、アクセス管理、算術演算、外部とのやりとり、および標準の遵守に焦点を当ててください。 +- [SlitherのPython API](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/)でセキュリティプロパティを定義する。 継承、変数の依存関係、アクセス管理、およびその他の構造上の問題に焦点を当ててください。 +- [Crytic](https://crytic.io)で、コミットごとにプロパティテストを実行する。 Cryticでは、セキュリティ属性に関するテストを実行、評価できるため、チーム全員がGitHubで合格したかどうか簡単に確認できます。 テストが不合格だった場合、コミットをブロックできます。 最後に、自動化ツールでは容易に特定できない以下のような問題についても注意してください: @@ -48,8 +45,8 @@ sourceUrl: https://github.com/crytic/building-secure-contracts/blob/master/devel - 秘匿化された操作 - 外部DeFiコンポーネントとの危険なやりとり -## ヘルプを求めましょう {#ask-for-help} +## ヘルプを求める {#ask-for-help} -毎週火曜日の午後に、[イーサリアム・オフィス・アワー](https://calendly.com/dan-trailofbits/office-hours)が開かれています。 この1対1の1時間のセッションで、セキュリティに関する質問をしたり、ツールを使ってトラブルシューティングをしたり、現在のアプローチについて専門家からフィードバックを得ることができます。 私たちが、このガイドに基づいてサポートします。 +[イーサリアムオフィスアワー](https://calendly.com/dan-trailofbits/office-hours)は毎週火曜日の午後に開催されます。 この1対1の1時間のセッションで、セキュリティに関する質問をしたり、ツールを使ってトラブルシューティングをしたり、現在のアプローチについて専門家からフィードバックを得ることができます。 私たちが、このガイドに基づいてサポートします。 -ぜひ、私たちのスタックである[Empire Hacking](https://join.slack.com/t/empirehacking/shared_invite/zt-h97bbrj8-1jwuiU33nnzg67JcvIciUw)に参加してください。 質問があれば、いつでも#cryticチャンネルと#ethereumチャンネルにお問い合わせください。 +Slackに参加する:[Empire Hacking](https://join.slack.com/t/empirehacking/shared_invite/zt-h97bbrj8-1jwuiU33nnzg67JcvIciUw)。 質問があれば、いつでも#cryticチャンネルと#ethereumチャンネルにお問い合わせください。 diff --git a/public/content/translations/ja/developers/tutorials/send-token-etherjs/index.md b/public/content/translations/ja/developers/tutorials/send-token-etherjs/index.md index c160a581043..8047ed3dae4 100644 --- a/public/content/translations/ja/developers/tutorials/send-token-etherjs/index.md +++ b/public/content/translations/ja/developers/tutorials/send-token-etherjs/index.md @@ -1,6 +1,6 @@ --- -title: ethers.jsを使用したトークンの送信 -description: ethers.jsを使用してトークンを送信するための初心者向けのガイド +title: "ethers.jsを使用したトークンの送信" +description: "ethers.jsを使用してトークンを送信するための初心者向けのガイド" author: Kim YongJun tags: - "ETHERS.JS" diff --git a/public/content/translations/ja/developers/tutorials/send-token-ethersjs/index.md b/public/content/translations/ja/developers/tutorials/send-token-ethersjs/index.md index d55ad00cb25..66525f44a3d 100644 --- a/public/content/translations/ja/developers/tutorials/send-token-ethersjs/index.md +++ b/public/content/translations/ja/developers/tutorials/send-token-ethersjs/index.md @@ -1,11 +1,8 @@ --- -title: ethers.jsを使用したトークンの送信 -description: ethers.jsを使用してトークンを送信するための初心者向けのガイド +title: "ethers.jsを使用してトークンを送信する" +description: "ethers.jsを使用してトークンを送信するための初心者向けガイド。" author: Kim YongJun -tags: - - "ETHERS.JS" - - "ERC-20" - - "トークン" +tags: [ "ETHERS.JS", "ERC-20", "トークン" ] skill: beginner lang: ja published: 2021-04-06 @@ -13,15 +10,16 @@ published: 2021-04-06 ## ethers.js(5.0)を使用したトークンの送信 {#send-token} -### このチュートリアルでは、次の処理を行う方法について学びます。 {#you-learn-about} +### このチュートリアルで学ぶこと {#you-learn-about} -- ethers.js のインポート +- ethers.jsのインポート - トークンの転送 - ネットワークの混雑状況に応じたガス代の設定 ### はじめに {#to-get-started} -まず、ethers.js というライブラリを javascript にインポートする必要があります。これには、ethers.js 5.0 も含まれます。 +始めるには、まずethers.jsライブラリをJavaScriptにインポートする必要があります。 +ethers.js(5.0)をインクルードします。 ### インストール {#install-ethersjs} @@ -29,16 +27,16 @@ published: 2021-04-06 /home/ricmoo> npm install --save ethers ``` -ブラウザで ES6 を使用するには次のようにします。 +ブラウザでES6を使用するには次のようにします。 ```html ``` -ブラウザで ES3(UMD)を使用するには次のようにします。 +ブラウザでES3(UMD)を使用するには次のようにします。 ```html ``` -バックエンドで使用するライブラリをインストールしたい場合や、ビルドが必要なフロントエンドのプロジェクトの場合は、 次のようにnpmを使用してインストールします。 +バックエンドやビルドを要するフロントエンドプロジェクトで使うためにライブラリをインストールする場合は、npmを使ってインストールできます: ```bash npm install web3 --save ``` -次に、Node.jsのスクリプトやBrowserifyのフロントエンド・プロジェクトにWeb3.jsをインポートするには、以下のJavaScriptコードを使用します: +次に、Node.jsのスクリプトやBrowserifyのフロントエンドプロジェクトにWeb3.jsをインポートするには、以下のJavaScriptの行を使用します: ```js const Web3 = require("web3") ``` -プロジェクトにライブラリを追加したので、初期化する必要があります。 プロジェクトは、ブロックチェーンと通信できなければなりません。 イーサリアムのほとんどのライブラリは、リモートプロシージャーコール(RPC)を使って[ノード](/developers/docs/nodes-and-clients/)と通信します。 Web3プロバイダを開始するには、プロバイダのURLをコンストラクタとして橋渡しするWeb3のインスタンスを生成します。 お使いのコンピュータで、ノードあるいは[Ganacheインスタンス](https://ethereumdev.io/testing-your-smart-contract-with-existing-protocols-ganache-fork/)を実行中の場合は、以下のようになります: +プロジェクトにライブラリを組み込んだので、次に初期化が必要です。 プロジェクトは、ブロックチェーンと通信できる必要があります。 ほとんどのイーサリアムライブラリは、RPCコールを介して[ノード](/developers/docs/nodes-and-clients/)と通信します。 Web3プロバイダーを初期化するには、プロバイダーのURLをコンストラクタに渡してWeb3インスタンスを生成します。 お使いのコンピュータでノードまたは[ganacheインスタンスを実行](https://ethereumdev.io/testing-your-smart-contract-with-existing-protocols-ganache-fork/)している場合は、次のようになります: ```js const web3 = new Web3("http://localhost:8545") ``` -ホストされているノードに直接アクセスしたい場合は、[ノード・アズ・ア・サービス](/developers/docs/nodes-and-clients/nodes-as-a-service)の一覧から見つけることができます。 +ホストされているノードに直接アクセスしたい場合は、[サービスとしてのノード](/developers/docs/nodes-and-clients/nodes-as-a-service)でオプションを見つけることができます。 ```js const web3 = new Web3("https://cloudflare-eth.com") ``` -Web3インスタンスが正しく設定されたかをテストするために、 `getBlockNumber`関数を使用して、最新のブロック番号を取得してみましょう。 この関数は、コールバックをパラメータとして受け取り、ブロック番号を整数として返します。 +Web3インスタンスが正しく設定されたかテストするために、`getBlockNumber`関数を使って最新のブロック番号を取得してみましょう。 この関数は、コールバックをパラメータとして受け取り、ブロック番号を整数として返します。 ```js var Web3 = require("web3") @@ -56,7 +54,7 @@ web3.eth.getBlockNumber(function (error, result) { }) ``` -このプログラムを実行すると、最新のブロック番号(ブロックチェーンの最上部) が表示されます。 また、`await/async`の関数呼び出しを使用することで、コードにおける入れ子状の呼び出しを回避することができます。 +このプログラムを実行すると、最新のブロック番号、つまりブロックチェーンの最上部が、シンプルに表示されます。 また、`await/async`関数呼び出しを使うと、コード内でのコールバックのネストを避けることができます: ```js async function getBlockNumber() { @@ -68,27 +66,27 @@ async function getBlockNumber() { getBlockNumber() ``` -Web3インスタンス上で利用可能なすべての関数は、 [web3.jsの公式ドキュメンテーション](https://docs.web3js.org/)をご覧ください。 +Web3インスタンスで利用可能なすべての関数は、[web3.jsの公式ドキュメント](https://docs.web3js.org/)で確認できます。 -ほとんどのWeb3ライブラリでは、結果を送り返すノードに対してバックグラウンドでJSON RPCを呼び出すため、非同期で処理を行います。 +ほとんどのWeb3ライブラリは非同期です。これは、バックグラウンドでライブラリがノードにJSON-RPCコールを行い、ノードが結果を返すためです。 -ブラウザで作業している場合、一部のウォレットは、Web3インスタンスを直接注入します。トランザクションを行うためにユーザーのイーサリアムアドレスとやり取りを行う予定がある場合は特に、可能な限り`await/async`関数呼び出しを使用するようにしてください。 +ブラウザで作業している場合、一部のウォレットはWeb3インスタンスを直接インジェクトします。特にユーザーのイーサリアムアドレスとやり取りしてトランザクションを行う予定がある場合は、可能な限りそのインスタンスを使用するようにしてください。 -以下のコードスニペットは、MetaMaskウォレットが利用可能か確認し、利用できる場合は有効化するものです。 その後、あなたはユーザーの残高を確認できるようになり、各ユーザーは、あなたが彼らにイーサリアムブロックチェーン上で実行させたいトランザクションを各自で検証できるようになります: +これは、MetaMaskウォレットが利用可能かどうかを検出し、利用可能であれば有効化を試みるスニペットです。 これにより、後でユーザーの残高を読み取ったり、イーサリアムブロックチェーン上で実行させたいトランザクションをユーザーが検証できるようになります: ```js if (window.ethereum != null) { state.web3 = new Web3(window.ethereum) try { - // Request account access if needed + // 必要に応じてアカウントへのアクセスを要求 await window.ethereum.enable() - // Accounts now exposed + // アカウントが公開されました } catch (error) { - // User denied account access... + // ユーザーがアカウントアクセスを拒否しました... } } ``` -他にも[Ethers.js](https://docs.ethers.io/) など、 web3.js のようにイーサリアム・ブロックチェーンとやりとりするライブラリがあります。 次のチュートリアルでは、[ブロックチェーンに新たに追加されたブロックを簡単にリッスンし、その内容を確認する方法](https://ethereumdev.io/listening-to-new-transactions-happening-on-the-blockchain/)を紹介します。 +[Ethers.js](https://docs.ethers.io/)のようなweb3.jsの代替ライブラリも存在し、同様に広く使われています。 次のチュートリアルでは、[ブロックチェーン上で新たに着信するブロックを簡単にリッスンし、その内容を確認する方法](https://ethereumdev.io/listening-to-new-transactions-happening-on-the-blockchain/)を見ていきます。 diff --git a/public/content/translations/ja/developers/tutorials/short-abi/index.md b/public/content/translations/ja/developers/tutorials/short-abi/index.md index fd9cb3f38ea..72540ab722b 100644 --- a/public/content/translations/ja/developers/tutorials/short-abi/index.md +++ b/public/content/translations/ja/developers/tutorials/short-abi/index.md @@ -1,85 +1,106 @@ --- title: "コールデータを最適化するための簡潔なABI" -description: オプティミスティック・ロールアップのためのスマートコントラクトの最適化 +description: "オプティミスティック・ロールアップのためのスマートコントラクトの最適化" author: Ori Pomerantz lang: ja -tags: - - "レイヤー2" +tags: [ "レイヤー2" ] skill: intermediate published: 2022-04-01 --- ## はじめに {#introduction} -この記事では、[オプティミスティック・ロールアップ](/developers/docs/scaling/optimistic-rollups)とは何か、オプティミスティック・ロールアップにおけるトランザクションコスト、および、様々なコスト構造に応じてイーサリアム・メインネット上の様々な事項をいかに最適化すべきかについて学びます。 さらに、この最適化の実装方法についても紹介します。 +この記事では、[オプティミスティック・ロールアップ](/developers/docs/scaling/optimistic-rollups)やそのトランザクションコスト、そしてその異なるコスト構造が、Ethereumメインネットとは異なるものの最適化をどのように要求するかについて学びます。 +また、この最適化を実装する方法についても学びます。 -### 開示情報 {#full-disclosure} +### 完全な情報開示 {#full-disclosure} -筆者は、[Optimism](https://www.optimism.io/)のフルタイム従業員であり、この記事に含まれる実例はすべてOptimismで実行されます。 ただし、紹介するテクニックは他のロールアップでも問題なく実行できます。 +筆者は[Optimism](https://www.optimism.io/)のフルタイム従業員であるため、この記事の例はOptimismで実行されます。 +ただし、ここで説明するテクニックは、他のロールアップでも同様に機能するはずです。 ### 用語 {#terminology} -ロールアップの議論において、「レイヤー1」は、イーサリアムネットワークの本番環境であるメインネットを指します。 「レイヤー2」(L2)という用語は、ロールアップまたはセキュリティのためにL1に依存しているが、そのほとんどをオフチェーンで処理する他のシステムに使用されます。 +ロールアップについて議論する際、「レイヤー1」(L1) という用語は、本番のEthereumネットワークであるメインネットを指すために使用されます。 +「レイヤー2」(L2) という用語は、ロールアップ、またはセキュリティをL1に依存しつつ、処理のほとんどをオフチェーンで行うその他のシステムに使用されます。 -## L2上のトランザクションコストをさらに引き下げる方法 {#how-can-we-further-reduce-the-cost-of-L2-transactions} +## L2トランザクションのコストをさらに削減するには {#how-can-we-further-reduce-the-cost-of-L2-transactions} -[オプティミスティック・ロールアップ](/developers/docs/scaling/optimistic-rollups)では、すべてのユーザーが過去のトランザクションを参照し、現在の状態が正しいことを検証できるように、過去のすべてのトランザクション記録を保存する必要があります。 イーサリアムメインネットにデータを書き込む最も安価な方法は、コールデータとして書き込む方法です。 [Optimism](https://help.optimism.io/hc/en-us/articles/4413163242779-What-is-a-rollup-)と[Arbitrum](https://developer.offchainlabs.com/docs/rollup_basics#intro-to-rollups)はいずれも、コールデータのソリューションを採用しています。 +[オプティミスティック・ロールアップ](/developers/docs/scaling/optimistic-rollups)は、誰もがそれらを参照して現在の状態が正しいことを検証できるように、すべての過去のトランザクションの記録を保存する必要があります。 +Ethereumメインネットにデータを取り込む最も安価な方法は、コールデータとして書き込むことです。 +このソリューションは、[Optimism](https://help.optimism.io/hc/en-us/articles/4413163242779-What-is-a-rollup-)と[Arbitrum](https://developer.offchainlabs.com/docs/rollup_basics#intro-to-rollups)の両方で採用されました。 ### L2トランザクションのコスト {#cost-of-l2-transactions} -L2トランザクションのコストは、以下の2つの要素で構成されます: +L2トランザクションのコストは、2つの要素で構成されています。 -1. L2上の処理コスト。通常、非常に安価です。 -2. L1上のストレージコスト。これは、メインネットのガス代と連動します。 +1. L2処理。通常は極めて安価です。 +2. L1ストレージ。メインネットのガス代に連動します。 -この記事の執筆時点のOptimismで、L2ガス代は、0.001[Gwei](/developers/docs/gas/#pre-london)です。 一方、L1のガス代は約40Gweiです。 リアルタイムの価格は[こちら](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m)で確認できます。 +これを書いている時点では、OptimismでのL2のガス代は0.001 [Gwei](/developers/docs/gas/#pre-london)です。 +一方、L1のガス代は、約40 gweiです。 +[現在の価格はこちらで確認できます](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m)。 -1バイトのコールデータのコストは、4ガス (0バイトの場合) または16ガス (それ以外) のいずれかです。 EVMで最も費用が高い操作のひとつは、ストレージへの書き込みです。 L2上で32バイトのワードを書き込む場合、最大コストは22100ガスです。 現在のレートでは、22.1 gweiになります。 したがって、1つのコールデータをゼロバイトに節約できれば、約200バイトをストレージに書き込むことができ、まだお釣りが来ます。 +コールデータの1バイトは、それがゼロの場合は4ガス、その他の値の場合は16ガスのコストがかかります。 +EVMで最も高価な操作の1つは、ストレージへの書き込みです。 +L2でストレージに32バイトのワードを書き込む最大コストは22100ガスです。 現在、これは22.1 gweiです。 +したがって、コールデータのゼロ値のバイトを1つでも節約できれば、ストレージに約200バイトを書き込んでも、まだ利益が出ます。 ### ABI {#the-abi} -大多数のトランザクションは、外部所有アカウントからコントラクトにアクセスします。 ほとんどのコントラクトはSolidityで書かれており、データフィールドは[アプリケーション・バイナリ・インターフェイス(ABI) ](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding)で解釈されます。 +大多数のトランザクションは、外部所有アカウントからコントラクトにアクセスします。 +ほとんどのコントラクトはSolidityで書かれており、[アプリケーションバイナリインターフェース(ABI)](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding)に従ってデータフィールドを解釈します。 -ただしABIは、1バイトのコールデータがほぼ4回の算術演算のコストと同じになるL1を念頭に置いて設計されていますが、L2では、1バイトのコールデータのコストで算術演算を1000回以上実行することができます。 例えば、[このERC-20の送信トランザクション](https://kovan-optimistic.etherscan.io/tx/0x7ce4c144ebfce157b4de99d8ad53a352ae91b57b3fa06d8a1c79439df6bfa998)を見てみましょう。 コールデータは、以下のように分割されます: +しかし、ABIはL1向けに設計されており、そこではコールデータの1バイトのコストが約4回の算術演算に相当しますが、L2では1バイトのコストが1000回以上の算術演算に相当します。 +コールデータは次のように分割されます。 | セクション | 長さ | バイト | 浪費バイト | 浪費ガス | 必須バイト | 必須ガス | -| ------- | --:| -----:| -----:| ----:| -----:| ----:| -| 関数セレクタ | 4 | 0~3 | 3 | 48 | 1 | 16 | -| ゼロ値 | 12 | 4~15 | 12 | 48 | 0 | 0 | -| 送信先アドレス | 20 | 16~35 | 0 | 0 | 20 | 320 | -| 金額 | 32 | 36~67 | 17 | 64 | 15 | 240 | +| ------- | -: | ----: | ----: | ---: | ----: | ---: | +| 関数セレクタ | 4 | 0-3 | 3 | 48 | 1 | 16 | +| ゼロ | 12 | 4-15 | 12 | 48 | 0 | 0 | +| 送信先アドレス | 20 | 16-35 | 0 | 0 | 20 | 320 | +| 金額 | 32 | 36-67 | 17 | 64 | 15 | 240 | | 合計 | 68 | | | 160 | | 576 | -説明: +説明: -- **関数セレクター**: このコントラクトに含まれる関数は256未満であるため、1バイトで区別できます。 これらのバイトは通常0バイトではないので、[16ガス](https://eips.ethereum.org/EIPS/eip-2028)がかかります。 -- **0バイト **:これらのバイトは常にゼロです。と言うのも、20バイトのアドレスを保持するためには32バイトのワードを必要としないからです。 0バイトのコストは、4ガスです([イエローペーパー](https://ethereum.github.io/yellowpaper/paper.pdf)の27ページにあるAppendix Gで、`G``txdatazero`の値について確認してください)。 -- **金額**:このコントラクトの`decimals`が18(通常値)であり、送信できるトークンの上限が1018だとすると、金額の上限は1036になります。 25615 > 1036のため、必要なバイト数は15になります。 +- **関数セレクタ**:このコントラクトには256未満の関数しかないため、1バイトで区別できます。 + これらのバイトは通常ゼロ以外であるため、[16ガスのコストがかかります](https://eips.ethereum.org/EIPS/eip-2028)。 +- **ゼロ**:20バイトのアドレスを保持するのに32バイトのワードは必要ないため、これらのバイトは常にゼロです。 + ゼロを保持するバイトのコストは4ガスです([イエローペーパー](https://ethereum.github.io/yellowpaper/paper.pdf)の付録G、 + 27ページの `G``txdatazero` の値参照)。 +- **金額**:このコントラクトで `decimals` が18 (通常値) であり、送金するトークンの最大量が1018であると仮定すると、最大量は1036になります。 + 25615 > 1036なので、15バイトで十分です。 -通常、L1上で160ガスを浪費するのは無視できる範囲です。 1件のトランザクションには最低でも[21,000ガス](https://yakkomajuri.medium.com/blockchain-definition-of-the-week-ethereum-gas-2f976af774ed)が必要であるため、追加の0.8%はほとんど問題になりません。 しかし、L2では問題になります。 L2におけるほぼすべてのコストは、L1への書き込みで発生します。 トランザクションのコールデータに加えて、トランザクションのヘッダー(送信先アドレス、署名など)で109バイトが必要になります。 従って、L2おける総コストは`109*16+576+160=2480`となり、浪費分が全体の6.5%に達するのです。 +L1上での160ガスの浪費は、通常は無視できます。 トランザクションには少なくとも[21,000ガス](https://yakkomajuri.medium.com/blockchain-definition-of-the-week-ethereum-gas-2f976af774ed)がかかるため、0.8%の追加は問題になりません。 +しかし、L2では事情が異なります。 トランザクションのコストのほぼ全体が、L1への書き込みによるものです。 +トランザクションのコールデータに加えて、109バイトのトランザクションヘッダー (送信先アドレス、署名など) があります。 +したがって、総コストは `109*16+576+160=2480` となり、その約6.5%を浪費していることになります。 -## 送信先を限定しない場合のコスト削減方法 {#reducing-costs-when-you-dont-control-the-destination} +## 送信先を制御できない場合のコスト削減 {#reducing-costs-when-you-dont-control-the-destination} -送信先のコントラクトを制御できない場合でも、[こちら](https://github.com/qbzzt/ethereum.org-20220330-shortABI)のようなソリューションを活用できます。 関連するファイルを確認しておきましょう。 +送信先コントラクトを制御できない場合でも、[こちら](https://github.com/qbzzt/ethereum.org-20220330-shortABI)のようなソリューションを利用できます。 +関連ファイルを見ていきましょう。 ### Token.sol {#token-sol} -[これ](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/Token.sol)は、送信先のコントラクトです。 標準的なERC-20コントラクトですが、機能が1つ追加されています。 `faucet`関数により、すべてのユーザーがトークンを取得できるようになっています。 本番環境のERC-20コントラクトでは使えませんが、テスト環境では有益でしょう。 +[こちらが送信先コントラクトです](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/Token.sol)。 +これは標準的なERC-20コントラクトですが、1つ機能が追加されています。 +この `faucet` 関数により、どのユーザーも使用するためのトークンを取得できます。 +これにより、本番のERC-20コントラクトは役に立たなくなりますが、ERC-20がテストを容易にするためだけに存在する場合、作業が楽になります。 ```solidity /** - * @dev Gives the caller 1000 tokens to play with + * @dev 呼び出し元に試用のための1000トークンを与えます */ function faucet() external { _mint(msg.sender, 1000); } // function faucet ``` -[こちら](https://kovan-optimistic.etherscan.io/address/0x950c753c0edbde44a74d3793db738a318e9c8ce8)で、このコントラクトのデプロイ実例を確認できます。 - ### CalldataInterpreter.sol {#calldatainterpreter-sol} -[これ](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/CalldataInterpreter.sol)は、より短いコールデータでトランザクションを呼び出すことが想定されているコントラクトです。 一行ずつ見ていきましょう。 +[これは、トランザクションがより短いコールデータで呼び出すことになっているコントラクトです](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/CalldataInterpreter.sol)。 +一行ずつ見ていきましょう。 ```solidity //SPDX-License-Identifier: Unlicense @@ -89,7 +110,7 @@ pragma solidity ^0.8.0; import { OrisUselessToken } from "./Token.sol"; ``` -呼び出し方法を知るには、トークン関数が必要です。 +それを呼び出す方法を知るには、トークン関数が必要です。 ```solidity contract CalldataInterpreter { @@ -100,10 +121,9 @@ contract CalldataInterpreter { 私たちがプロキシとなるトークンのアドレスです。 ```solidity - /** - * @dev Specify the token address - * @param tokenAddr_ ERC-20 contract address + * @dev トークンアドレスを指定します + * @param tokenAddr_ ERC-20コントラクトアドレス */ constructor( address tokenAddr_ @@ -131,7 +151,9 @@ contract CalldataInterpreter { "calldataVal trying to read beyond calldatasize"); ``` -32バイト(256ビット)を持つ1つのワードをメモリにロードして、必要なフィールドに含まれない部分のバイトを削除します。 このアルゴリズムは、32バイト以上の値に対しては機能せず、コールデータの末尾を越えたデータを読むこともできません。 L1では、ガスを節約するためにこれらのテストを省略すべきかもしれませんが、L2のガス代はとても安価なので、あらゆるサニティチェックを実行することができます。 +単一の32バイト (256ビット) ワードをメモリにロードし、目的のフィールドの一部でないバイトを削除します。 +このアルゴリズムは、32バイトより長い値には機能せず、もちろんコールデータの末尾を超えて読み取ることはできません。 +L1ではガスを節約するためにこれらのテストをスキップする必要があるかもしれませんが、L2ではガスが非常に安いため、考えられるあらゆるサニティチェックが可能です。 ```solidity assembly { @@ -139,16 +161,18 @@ contract CalldataInterpreter { } ``` -`fallback()`への呼び出しからデータをコピーしてもよいのですが(以下を参照) 、EVMのアセンブリ言語である[Yul](https://docs.soliditylang.org/en/v0.8.12/yul.html)を使用する方が楽でしょう。 +`fallback()`への呼び出しからデータをコピーすることもできましたが (下記参照)、EVMのアセンブリ言語である[Yul](https://docs.soliditylang.org/en/v0.8.12/yul.html)を使用する方が簡単です。 -ここでは、[CALLDATALOADのオペコード](https://www.evm.codes/#35)を使用して、`startByte`から `startByte+31`までのバイトをスタックへ読み込みます。 一般に、Yulのオペコードの構文は`(,...`となります。 +ここでは、[CALLDATALOADオペコード](https://www.evm.codes/#35)を使用して、`startByte`から`startByte+31`までのバイトをスタックに読み込みます。 +一般に、Yulでのオペコードの構文は`(,...)`です。 ```solidity _retVal = _retVal >> (256-length*8); ``` -このフィールドに含まれるのは最も重要な`length`のバイトだけなので、[右シフトクリック](https://en.wikipedia.org/wiki/Logical_shift)で他の値を削除します。 この方法は、値をフィールドの右側に移動するという追加の利点があるので、256xを掛けた値ではなく、値そのものになります。 +最上位の `length` バイトのみがフィールドの一部であるため、[右シフト](https://en.wikipedia.org/wiki/Logical_shift)して他の値を取り除きます。 +これには、値をフィールドの右側に移動させるという追加の利点があり、値自体が256somethingを掛けたものではなく、値そのものになります。 ```solidity @@ -159,7 +183,8 @@ contract CalldataInterpreter { fallback() external { ``` -Solidityコントラクトへの呼び出しがどの関数の署名とも一致しない場合、 [`fallback()`関数](https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function)を呼び出します(存在する場合)。 `CalldataInterpreter`の場合、他の`external`または`public`の関数がないため、すべての呼び出しがここに到達します。 +Solidityコントラクトへの呼び出しがどの関数シグネチャとも一致しない場合、[`fallback()`関数](https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function)を呼び出します (存在する場合)。 +`CalldataInterpreter`の場合、他の`external`や`public`関数がないため、_どんな_呼び出しもここに到達します。 ```solidity uint _func; @@ -167,23 +192,27 @@ Solidityコントラクトへの呼び出しがどの関数の署名とも一致 _func = calldataVal(0, 1); ``` -この関数を返すコールデータの最初の1バイトを読み取ります。 ここで関数が取得できないのには、2つの理由があります: +コールデータの最初のバイトを読み取ります。これにより関数がわかります。 +ここで関数が利用できない理由は2つあります。 -1. `pure`または`view`の関数の場合。これらの関数は状態を変更しないため、ガスが発生しません(オフチェーンで呼び出す場合)。 ですから、ガス代を節約する必要がありません。 -2. [`msg.sender`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#block-and-transaction-properties)に依存した関数。 `msg.sender`の値は、呼び出し元のアドレスではなく、`CalldataInterpreter`のアドレスになります。 +1. `pure`または`view`の関数は状態を変更せず、ガス代もかかりません (オフチェーンで呼び出された場合)。 + これらのガス代を削減しようとしても意味がありません。 +2. [`msg.sender`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#block-and-transaction-properties)に依存する関数。 + `msg.sender`の値は、呼び出し元ではなく`CalldataInterpreter`のアドレスになります。 -残念ながら、[ERC-20の仕様](https://eips.ethereum.org/EIPS/eip-20)を確認すると、残りの関数は`transfer`のみです。 つまり、呼び出し可能な関数は、`transfer` (`transferFrom`を呼び出す)と、`faucet` (呼び出し元のアドレスにトークンを送信する)になります。 +残念ながら、[ERC-20の仕様](https://eips.ethereum.org/EIPS/eip-20)を見ると、残っている関数は`transfer`のみです。 +これにより、残る関数は`transfer` (`transferFrom`を呼び出せるため)と`faucet` (`transferFrom`を呼び出せるため)の2つだけになります。 ```solidity - // Call the state changing methods of token using - // information from the calldata + // コールデータの情報を使用して + // トークンの状態変更メソッドを呼び出します // faucet if (_func == 1) { ``` -次は、パラメータを持たない`faucet()`を呼び出すコードです。 +パラメータのない`faucet()`の呼び出しです。 ```solidity token.faucet(); @@ -192,46 +221,49 @@ Solidityコントラクトへの呼び出しがどの関数の署名とも一致 } ``` -`token.faucet()`を呼び出すと、トークンを取得します。 しかし、プロキシのコントラクトにおいてトークンは**必要ありません**。 トークンが必要なのは、外部所有アカウント(EOA)あるいは呼び出し元のコントラクトです。 ですから、所有するトークンをすべて呼び出し元アドレスに送信します。 +`token.faucet()`を呼び出すと、トークンを取得します。 しかし、プロキシコントラクトとして、私たちはトークンを**必要**としません。 +私たちを呼び出したEOA (外部所有アカウント) やコントラクトは、それを必要とします。 +したがって、所有するすべてのトークンを、私たちを呼び出した誰にでも送金します。 ```solidity - // transfer (assume we have an allowance for it) + // transfer (そのためのアローワンスがあると仮定します) if (_func == 2) { ``` -トークンを送信する場合、送信先アドレスと金額という2つのパラメータが必要です。 +トークンを送金するには、送信先アドレスと金額の2つのパラメータが必要です。 ```solidity token.transferFrom( msg.sender, ``` -送信できるトークンは、呼び出し元が所有するトークンのみです。 +呼び出し元が所有するトークンの送金のみを許可します ```solidity address(uint160(calldataVal(1, 20))), ``` -送信先アドレスは、#1のバイトから始まります(#0のバイトは、関数が使用します)。 アドレスの長さは、20バイトです。 +送信先アドレスはバイト#1から始まります (バイト#0は関数です)。 +アドレスとして、その長さは20バイトです。 ```solidity calldataVal(21, 2) ``` -このコントラクトでは、送信可能なトークンの最大数が2バイト以内(65536未満)に収まると想定します。 +この特定のコントラクトでは、誰もが送金したいと思うトークンの最大数は2バイト (65536未満) に収まると想定します。 ```solidity ); } ``` -1件の送信につき、35バイトのコールデータが発生します。 +全体として、1回の送金には35バイトのコールデータが必要です。 | セクション | 長さ | バイト | -| ------- | --:| -----:| +| ------- | -: | ----: | | 関数セレクタ | 1 | 0 | -| 送信先アドレス | 32 | 1~32 | -| 金額 | 2 | 33~34 | +| 送信先アドレス | 32 | 1-32 | +| 金額 | 2 | 33-34 | ```solidity } // fallback @@ -241,7 +273,8 @@ Solidityコントラクトへの呼び出しがどの関数の署名とも一致 ### test.js {#test-js} -[このJavaScriptによる単体テスト](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/test/test.js)では、このメカニズムを使用する方法(および、メカニズムが適切に動作していることをを確認する方法)を示します。 ここでは、[Chai](https://www.chaijs.com/)および[Ethers](https://docs.ethers.io/v5/)についてよく理解しているという前提に基づき、特にコントラクトに関連する部分のみを説明します。 +[このJavaScriptユニットテスト](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/test/test.js)は、このメカニズムの使用方法 (およびそれが正しく機能することを確認する方法) を示しています。 +[chai](https://www.chaijs.com/)と[ethers](https://docs.ethers.io/v5/)を理解していることを前提とし、コントラクトに特に関連する部分のみを説明します。 ```js const { expect } = require("chai"); @@ -264,21 +297,24 @@ describe("CalldataInterpreter", function () { まず、両方のコントラクトをデプロイします。 ```javascript - // Get tokens to play with + // 試用のためのトークンを取得 const faucetTx = { ``` -ここではABIを使用しないため、トランザクションを作成するために通常用いる高度な関数(`token.faucet()`など)を使用できません。 その代わりに、トランザクションをマニュアルで作成し、送信する必要があります。 +通常使用する高レベルの関数 (例: `token.faucet()`) は、ABIに従っていないため、トランザクションの作成には使用できません。 +代わりに、自分でトランザクションを作成してから送信する必要があります。 ```javascript to: cdi.address, data: "0x01" ``` -トランザクションには、次の2つのパラメータが必要です: +トランザクションには、次の2つのパラメータが必要です。 -1. `to`:送信先のアドレスです。 これは、コールデータのインタープリタのアドレスです。 -2. `data`:送信するコールデータです。 フォーセットを呼び出す場合、データは1バイト(`0x01`)です。 +1. `to`、送信先アドレスです。 + これは、コールデータのインタープリタコントラクトです。 +2. `data`、送信するコールデータです。 + フォーセットを呼び出す場合、データは1バイトの`0x01`です。 ```javascript @@ -286,26 +322,27 @@ describe("CalldataInterpreter", function () { await (await signer.sendTransaction(faucetTx)).wait() ``` -すでに送信先(`faucetTx.to`)を指定しており、トランザクションに対して署名を得る必要があるため、[署名者の`sendTransaction`メソッド](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction)を呼び出します。 +送信先 (`faucetTx.to`) をすでに指定しており、トランザクションに署名が必要なため、[署名者の `sendTransaction` メソッド](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction)を呼び出します。 ```javascript -// Check the faucet provides the tokens correctly +// フォーセットがトークンを正しく提供することを確認 expect(await token.balanceOf(signer.address)).to.equal(1000) ``` -ここでは、残高を確認します。 `view`関数ではガスを節約する必要がないので、単純に実行します。 +ここで残高を確認します。 +`view`関数ではガスを節約する必要がないので、通常どおり実行します。 ```javascript -// Give the CDI an allowance (approvals cannot be proxied) +// CDIにアローワンスを与える (承認はプロキシできません) const approveTX = await token.approve(cdi.address, 10000) await approveTX.wait() expect(await token.allowance(signer.address, cdi.address)).to.equal(10000) ``` -コールデータのインタープリタが送信できるように、アローワンスを設定します。 +コールデータのインタープリタに送金できるようにアローワンスを与えます。 ```javascript -// Transfer tokens +// トークンを送金 const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" const transferTx = { to: cdi.address, @@ -313,53 +350,50 @@ const transferTx = { } ``` -送信トランザクションを作成します。 最初のバイトは「0x02」で、次に送信先アドレスを置き、最後に金額(10進法で256である0x0100)を置きます。 +送金トランザクションを作成します。 最初のバイトは「0x02」で、次に送信先アドレス、最後に金額 (0x0100、10進数で256) が続きます。 ```javascript await (await signer.sendTransaction(transferTx)).wait() - // Check that we have 256 tokens less + // 256トークン少なくなっていることを確認 expect (await token.balanceOf(signer.address)).to.equal(1000-256) - // And that our destination got them + // そして、送信先がそれらを受け取ったことを確認 expect (await token.balanceOf(destAddr)).to.equal(256) }) // it }) // describe ``` -### 例 {#example} - -これらのファイルにつき、自ら実行せず、どのように動作するのか確認したい場合は、以下のリンクにアクセスしてください: +## 送信先コントラクトを制御できる場合のコスト削減 {#reducing-the-cost-when-you-do-control-the-destination-contract} -1. アドレス[`0x950c753c0edbde44a74d3793db738a318e9c8ce8`](https://kovan-optimistic.etherscan.io/address/0x950c753c0edbde44a74d3793db738a318e9c8ce8)に対する[ `OrisUselessToken`](https://kovan-optimistic.etherscan.io/tx/1410744)のデプロイメント。 -2. アドレス[`0x16617fea670aefe3b9051096c0eb4aeb4b3a5f55`](https://kovan-optimistic.etherscan.io/address/0x16617fea670aefe3b9051096c0eb4aeb4b3a5f55)に対する[`CalldataInterpreter`](https://kovan-optimistic.etherscan.io/tx/1410745)のデプロイメント。 -3. [`faucet()`](https://kovan-optimistic.etherscan.io/tx/1410746)の呼び出し。 -4. [`OrisUselessToken.approve()`](https://kovan-optimistic.etherscan.io/tx/1410747)の呼び出し。 処理が`msg.sender`に依存しているため、この呼び出しは、直接トークンコントラクトで行う必要があります。 -5. [`transfer()`](https://kovan-optimistic.etherscan.io/tx/1410748)の呼び出し。 +送信先コントラクトを制御できる場合、コールデータのインタープリタを信頼するため、`msg.sender`チェックをバイパスする関数を作成できます。 +[`control-contract`ブランチで、これがどのように機能するかの例をこちらで確認できます](https://github.com/qbzzt/ethereum.org-20220330-shortABI/tree/control-contract)。 -## 送信先コントラクトを制限する場合にコストを削減する方法 {#reducing-the-cost-when-you-do-control-the-destination-contract} - -送信先コントラクトを制限できる場合、コールデータのインタープリタが信頼されるため、`msg.sender`チェックを省略する関数を作成することができます。 [`control-contract`のブランチから、動作例を確認できます](https://github.com/qbzzt/ethereum.org-20220330-shortABI/tree/control-contract)。 - -コントラクトが外部のトランザクションのみに応答する場合、1つのコントラクトのみで対応することができます。 しかし、この方法では[コンポーザビリティ](/developers/docs/smart-contracts/composability/)が失われます。 通常のERC-20の呼び出しに応答するコントラクトと、短いコールデータを持つトランザクションに応答するコントラクトを共に用意する方が優れた方法だと言えます。 +コントラクトが外部トランザクションにのみ応答する場合、1つのコントラクトだけで済みます。 +しかし、それでは[構成可能性](/developers/docs/smart-contracts/composability/)が損なわれます。 +通常のERC-20の呼び出しに応答するコントラクトと、短いコールデータを持つトランザクションに応答する別のコントラクトを持つ方がはるかに優れています。 ### Token.sol {#token-sol-2} -この例では、`Token.sol`を修正します。 これにより、このプロキシだけが呼び出せる一連の関数を設定することができます。 以下は、追加の関数です: +この例では、`Token.sol`を修正できます。 +これにより、プロキシだけが呼び出せる多数の関数を持つことができます。 +新しい部分は次のとおりです。 ```solidity - // The only address allowed to specify the CalldataInterpreter address + // CalldataInterpreterアドレスを指定できる唯一のアドレス address owner; - // The CalldataInterpreter address + // CalldataInterpreterアドレス address proxy = address(0); ``` -ERC-20コントラクトは、許可されたプロキシの身元を知る必要があります。 しかし、この時点では値が不明なため、コンストラクタで変数を設定できません。 プロキシは、コンストラクタにおいてトークンのアドレスを要求するため、まずこのコントラクトのインスタンスが実行されます。 +ERC-20コントラクトは、承認されたプロキシのIDを知る必要があります。 +しかし、まだ値がわからないため、コンストラクタでこの変数を設定することはできません。 +このコントラクトは、プロキシがコンストラクタでトークンのアドレスを期待するため、最初にインスタンス化されます。 ```solidity /** - * @dev Calls the ERC20 constructor. + * @dev ERC20コンストラクタを呼び出します。 */ constructor( ) ERC20("Oris useless token-2", "OUT-2") { @@ -367,12 +401,12 @@ ERC-20コントラクトは、許可されたプロキシの身元を知る必 } ``` -作成者(`オーナー`と呼ぶ)のアドレスは、プロキシを設定することが許可された唯一のアドレスであるため、ここに保存されます。 +作成者 (「owner」と呼ばれる) のアドレスは、プロキシを設定できる唯一のアドレスであるため、ここに保存されます。 ```solidity /** - * @dev set the address for the proxy (the CalldataInterpreter). - * Can only be called once by the owner + * @dev プロキシ (CalldataInterpreter) のアドレスを設定します。 + * オーナーが1回だけ呼び出し可能 */ function setProxy(address _proxy) external { require(msg.sender == owner, "Can only be called by owner"); @@ -382,32 +416,35 @@ ERC-20コントラクトは、許可されたプロキシの身元を知る必 } // function setProxy ``` -プロキシは特権アクセスを持つため、セキュリティチェックが省略されます。 このプロキシが信頼できることを確認するには、`オーナー`に対し、1回のみこの関数を呼び出すことを許可します。 `proxy`が (ゼロではない)実際の値を持つと同時に、この値は変更不可となるため、オーナーが悪意のユーザーになった場合やそのニーモニックが明らかになった場合でも、安全性が維持されます。 +プロキシはセキュリティチェックをバイパスできるため、特権アクセスを持ちます。 +プロキシを信頼できることを確認するために、`owner`だけがこの関数を1回だけ呼び出せるようにします。 +一度 `proxy` が実際の値 (ゼロではない) を持つと、その値は変更できないため、オーナーが悪意を持ったり、そのニーモニックが漏洩したりしても、安全です。 ```solidity /** - * @dev Some functions may only be called by the proxy. + * @dev 一部の関数はプロキシによってのみ呼び出し可能です。 */ modifier onlyProxy { ``` -これは、他の関数の動作を修正する[`modifier`関数](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm)です。 +これは[`modifier`関数](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm)であり、他の関数の動作を変更します。 ```solidity require(msg.sender == proxy); ``` -まず、呼び出し元がプロキシであり、その他のユーザーではないことを確認します。 プロキシ以外から呼び出された場合は、 `revert`します。 +まず、プロキシによって呼び出され、他の誰にも呼び出されていないことを確認します。 +そうでなければ、`revert`します。 ```solidity _; } ``` -プロキシからの呼び出しであれば、修正する関数を実行します。 +もしそうなら、修正する関数を実行します。 ```solidity - /* Functions that allow the proxy to actually proxy for accounts */ + /* プロキシが実際にアカウントのプロキシとして機能できるようにする関数 */ function transferProxy(address from, address to, uint256 amount) public virtual onlyProxy() returns (bool) @@ -436,17 +473,18 @@ ERC-20コントラクトは、許可されたプロキシの身元を知る必 } ``` -以下は、トークンを送信する/アローワンスを承認するエンティティから直接メッセージを受信する際に通常必要となる3つの操作です。 ここでは、以下の特徴を持つプロキシバージョンを使います: +これらは通常、トークンを送金したり、アローワンスを承認したりするエンティティから直接メッセージが送信される必要がある3つの操作です。 +ここでは、これらの操作のプロキシバージョンがあります。 -1. `onlyProxy()`で修正されており、他のユーザーが管理権限を持たない。 -2. 追加のパラメータとして、通常`msg.sender`であるアドレスを取得する。 +1. `onlyProxy()`によって変更されているため、他の誰もそれらを制御することはできません。 +2. 通常`msg.sender`であるアドレスを、追加パラメータとして取得します。 ### CalldataInterpreter.sol {#calldatainterpreter-sol-2} -コールデータのインタープリタは、送信先を限定しない場合とほぼ同一ですが、プロキシの関数では`msg.sender`パラメータを受け取るため、`transfer`のアローワンスが必要ない点が異なります。 +コールデータのインタープリタは、プロキシされた関数が`msg.sender`パラメータを受け取り、`transfer`にアローワンスが不要である点を除いて、上記のインタープリタとほぼ同じです。 ```solidity - // transfer (no need for allowance) + // transfer (アローワンスは不要) if (_func == 2) { token.transferProxy( msg.sender, @@ -477,7 +515,7 @@ ERC-20コントラクトは、許可されたプロキシの身元を知る必 ### Test.js {#test-js-2} -送信先を限定しない場合とは、いくつかの点が異なります。 +以前のテストコードとこのコードにはいくつかの変更点があります。 ```js const Cdi = await ethers.getContractFactory("CalldataInterpreter") @@ -486,21 +524,22 @@ await cdi.deployed() await token.setProxy(cdi.address) ``` -ERC-20コントラクトに対し、どのプロキシを信頼するかを伝える必要があります。 +ERC-20コントラクトに、どのプロキシを信頼するかを伝える必要があります。 ```js console.log("CalldataInterpreter addr:", cdi.address) -// Need two signers to verify allowances +// アローワンスを確認するには2つの署名者が必要 const signers = await ethers.getSigners() const signer = signers[0] const poorSigner = signers[1] ``` -`approve()`と`transferFrom()`を確認するには、第2の署名者が必要です。 第2の署名者は、トークンを受け取らないため(もちろん、ETHを所有する必要はあります)に`poorSigner`と呼びます。 +`approve()`と`transferFrom()`を確認するには、2人目の署名者が必要です。 +これは私たちのトークンを一切受け取らないため、`poorSigner`と呼びます (もちろん、ETHは持っている必要があります)。 ```js -// Transfer tokens +// トークンを送金 const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" const transferTx = { to: cdi.address, @@ -509,10 +548,10 @@ const transferTx = { await (await signer.sendTransaction(transferTx)).wait() ``` -ERC-20コントラクトは、プロキシ (`cdi`) を信頼するため、送信をリレーするためのアローワンスは必要ありません。 +ERC-20コントラクトはプロキシ (`cdi`) を信頼するため、送金を中継するためのアローワンスは必要ありません。 ```js -// approval and transferFrom +// 承認とtransferFrom const approveTx = { to: cdi.address, data: "0x03" + poorSigner.address.slice(2, 42) + "00FF", @@ -527,28 +566,19 @@ const transferFromTx = { } await (await poorSigner.sendTransaction(transferFromTx)).wait() -// Check the approve / transferFrom combo was done correctly +// approve / transferFrom の組み合わせが正しく行われたことを確認 expect(await token.balanceOf(destAddr2)).to.equal(255) - -No key -Text -XPath: /pre[38]/code ``` -新たに追加した2つの関数をテストします。 `transferFromTx`のアドレスには、アローワンスの提供元と受領者という2つパラメータが要求される点に注意してください。 - -### 実例 {#example-2} +2つの新しい関数をテストします。 +`transferFromTx`には、アローワンスの提供者と受領者の2つのアドレスパラメータが必要であることに注意してください。 -これらのファイルにつき、自ら実行せず、どのように動作するのか確認したい場合は、以下のリンクにアクセスしてください: +## 結論 {#conclusion} -1. [`0xb47c1f550d8af70b339970c673bbdb2594011696`](https://kovan-optimistic.etherscan.io/address/0xb47c1f550d8af70b339970c673bbdb2594011696)のアドレスに対する[`OrisUselessToken-2`のデプロイメント](https://kovan-optimistic.etherscan.io/tx/1475397)。 -2. [`0x0dccfd03e3aaba2f8c4ea4008487fd0380815892`](https://kovan-optimistic.etherscan.io/address/0x0dccfd03e3aaba2f8c4ea4008487fd0380815892)のアドレスに対する[ `CalldataInterpreter`のデプロイメント](https://kovan-optimistic.etherscan.io/tx/1475400)。 -3. [`setProxy()`の呼び出し](https://kovan-optimistic.etherscan.io/tx/1475402)。 -4. [`faucet()`の呼び出し](https://kovan-optimistic.etherscan.io/tx/1475409)。 -5. [`transferProxy()`の呼び出し](https://kovan-optimistic.etherscan.io/tx/1475416)。 -6. [`approveProxy()`の呼び出し](https://kovan-optimistic.etherscan.io/tx/1475419)。 -7. [`transferFromProxy()`の呼び出し](https://kovan-optimistic.etherscan.io/tx/1475421)。 この呼び出しは、他のアドレスとは異なるアドレスからのものであることに注意してください (`signer`の代わりに`poorSigner`) 。 +[Optimism](https://medium.com/ethereum-optimism/the-road-to-sub-dollar-transactions-part-2-compression-edition-6bb2890e3e92)と[Arbitrum](https://developer.offchainlabs.com/docs/special_features)はどちらも、L1に書き込まれるコールデータのサイズ、ひいてはトランザクションのコストを削減する方法を模索しています。 +しかし、汎用的なソリューションを探しているインフラプロバイダーとして、私たちの能力には限界があります。 +dapp開発者であるあなたは、アプリケーション固有の知識を持っているため、汎用的なソリューションよりもはるかに優れた方法でコールデータを最適化できます。 +この記事が、あなたのニーズに合った理想的なソリューションを見つけるのに役立つことを願っています。 -## まとめ {#conclusion} +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). -[Optimism](https://medium.com/ethereum-optimism/the-road-to-sub-dollar-transactions-part-2-compression-edition-6bb2890e3e92)と[Arbitrum](https://developer.offchainlabs.com/docs/special_features)はどちらも、L1に書き込まれるコールデータのサイズを削減し、トランザクションコストを抑える方法を提供することを目指しています。 インフラプロバイダーが汎用性が高いソリューションを追求する一方で、デベロッパの能力には限界があります。 Dappのデベロッパーは、開発するアプリケーションについて具体的な知識を持つため、汎用性のソリューションよりも効率的にコールデータの最適化を実現できるのです。 この記事が、皆さんのニーズに合わせた理想的なソリューションを見出す上で役立つことを願っています。