diff --git "a/docs/AVAX-Messenger/ja/section-1/lesson-5_\343\202\244\343\203\231\343\203\263\343\203\210\343\202\222\350\277\275\345\212\240\343\201\227\343\202\210\343\201\206.md" "b/docs/AVAX-Messenger/ja/section-1/lesson-5_\343\202\244\343\203\231\343\203\263\343\203\210\343\202\222\350\277\275\345\212\240\343\201\227\343\202\210\343\201\206.md" index 53d77f115..fedc5d4b9 100644 --- "a/docs/AVAX-Messenger/ja/section-1/lesson-5_\343\202\244\343\203\231\343\203\263\343\203\210\343\202\222\350\277\275\345\212\240\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/AVAX-Messenger/ja/section-1/lesson-5_\343\202\244\343\203\231\343\203\263\343\203\210\343\202\222\350\277\275\345\212\240\343\201\227\343\202\210\343\201\206.md" @@ -134,9 +134,16 @@ contract Messenger { + it('Should emit an event on post', async function () { + const { messenger, otherAccount } = await loadFixture(deployContract); + -+ await expect( -+ messenger.post('text', otherAccount.address, { value: 1 }) -+ ).to.emit(messenger, 'NewMessage'); ++ await expect(messenger.post('text', otherAccount.address, { value: 1 })) ++ .to.emit(messenger, 'NewMessage') ++ .withArgs( ++ owner.address, ++ otherAccount.address, ++ 1, ++ anyValue, ++ 'text', ++ true, ++ ); + }); it('Should send the correct amount of tokens', async function () { @@ -145,7 +152,7 @@ contract Messenger { it('Should set the right Message', async function () { // ... - }); + }); }); ``` @@ -160,10 +167,9 @@ contract Messenger { + }); + + const first_index = 0; -+ await expect(messenger.connect(otherAccount).accept(first_index)).to.emit( -+ messenger, -+ 'MessageConfirmed' -+ ); ++ await expect(messenger.connect(otherAccount).accept(first_index)) ++ .to.emit(messenger, 'MessageConfirmed') ++ .withArgs(otherAccount.address, first_index); + }); it('isPending must be changed', async function () { @@ -191,10 +197,9 @@ contract Messenger { + }); + + const first_index = 0; -+ await expect(messenger.connect(otherAccount).deny(first_index)).to.emit( -+ messenger, -+ 'MessageConfirmed' -+ ); ++ await expect(messenger.connect(otherAccount).deny(first_index)) ++ .to.emit(messenger, 'MessageConfirmed') ++ .withArgs(otherAccount.address, first_index); + }); it('isPending must be changed', async function () { @@ -214,11 +219,48 @@ contract Messenger { それぞれ正しくイベントが発生したかどうかについて確認をしています。 ```ts -expect(関数実行).to.emit(コントラクト, 'イベント名'); +expect(関数実行).to.emit(コントラクト, 'イベント名').withArgs(イベントの引数, ...); ``` + とすることで指定したイベントが発生したのかをテストをすることができます。 +`NewMessage`イベントの確認に使用している`anyValue`は、`@nomicfoundation/hardhat-chai-matchers/withArgs`に定義されている関数です。引数として受け取った値に関わらず常にtrueを返すため、引数の具体的な値をチェックせずにイベントを確認するために使用されます。ここでは、NewMessageイベントの引数のうち`block.timestamp`値に使用することで、この値をテストの対象外としています。 + +それでは、ファイルの先頭に以下のimport文を追加してください。 + +```ts +import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; +``` + +テストを実行する前に`Messenger`コントラクト内で定義していた`console.log`を削除しましょう。コンストラクタの動作はこれまでのテストですでに確認済みです。また、`post`関数で定義していたconsole.logは、イベントの発生をテストすることで確認できるため削除します。 + +まずはimport文を削除します。 + +```soidity +// === 下記を削除 === +import "hardhat/console.sol"; +``` + +次に、コンストラクタ内のconsole.logを削除します。 + +```solidity + // === 下記を削除 === + console.log("Here is my first smart contract!"); +``` + +最後にpost関数内のconsole.logを削除します。 + +```solidity + // === 下記を削除 === + console.log( + "%s posts text:[%s] token:[%d]", + msg.sender, + _text, + msg.value + ); +``` + それではテストを実行しましょう! ターミナル上で`AVAX-Messenger/`直下にいることを確認し、以下のコマンドを実行してください。 diff --git "a/docs/AVAX-Messenger/ja/section-3/lesson-1_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\253\347\256\241\347\220\206\350\200\205\343\202\222\350\250\255\343\201\221\343\202\210\343\201\206.md" "b/docs/AVAX-Messenger/ja/section-3/lesson-1_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\253\347\256\241\347\220\206\350\200\205\343\202\222\350\250\255\343\201\221\343\202\210\343\201\206.md" index 5abc416d6..c4bcd2368 100644 --- "a/docs/AVAX-Messenger/ja/section-3/lesson-1_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\253\347\256\241\347\220\206\350\200\205\343\202\222\350\250\255\343\201\221\343\202\210\343\201\206.md" +++ "b/docs/AVAX-Messenger/ja/section-3/lesson-1_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\253\347\256\241\347\220\206\350\200\205\343\202\222\350\250\255\343\201\221\343\202\210\343\201\206.md" @@ -54,8 +54,6 @@ contract Messenger { event MessageConfirmed(address receiver, uint256 index); + constructor(uint256 _numOfPendingLimits) payable { -+ console.log("Here is my first smart contract!"); -+ + numOfPendingLimits = _numOfPendingLimits; + } @@ -73,13 +71,6 @@ contract Messenger { + // 保留中のメッセージの数をインクリメントします。 + _numOfPendingAtAddress[_receiver] += 1; - console.log( - "%s posts text:[%s] token:[%d]", - msg.sender, - _text, - msg.value - ); - _messagesAtAddress[_receiver].push( Message( payable(msg.sender), @@ -121,8 +112,6 @@ contract Messenger { ```solidity constructor(uint256 _numOfPendingLimits) payable { - console.log("Here is my first smart contract!"); - numOfPendingLimits = _numOfPendingLimits; } ``` @@ -167,6 +156,7 @@ contract Messenger { - `Post`テスト内の最後にテストケースを追加 ```ts +import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; import { Overrides } from 'ethers'; @@ -334,8 +324,6 @@ import "hardhat/console.sol"; + event NumOfPendingLimitsChanged(uint256 limits); constructor(uint256 _numOfPendingLimits) payable { - console.log("Here is my first smart contract!"); - + ownable(); numOfPendingLimits = _numOfPendingLimits; diff --git "a/docs/ETH-NFT-Game/ja/section-4/lesson-2_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\202\222\343\202\242\343\203\203\343\203\227\343\202\260\343\203\254\343\203\274\343\203\211\343\201\227\343\202\210\343\201\206.md" "b/docs/ETH-NFT-Game/ja/section-4/lesson-2_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\202\222\343\202\242\343\203\203\343\203\227\343\202\260\343\203\254\343\203\274\343\203\211\343\201\227\343\202\210\343\201\206.md" index cd01b4c52..5322ef793 100644 --- "a/docs/ETH-NFT-Game/ja/section-4/lesson-2_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\202\222\343\202\242\343\203\203\343\203\227\343\202\260\343\203\254\343\203\274\343\203\211\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/ETH-NFT-Game/ja/section-4/lesson-2_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\202\222\343\202\242\343\203\203\343\203\227\343\202\260\343\203\254\343\203\274\343\203\211\343\201\227\343\202\210\343\201\206.md" @@ -274,42 +274,109 @@ const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); describe('MyEpicGame', () => { + // テストで使用するキャラクターの情報を定義 + const characters = [ + { + name: 'ZORO', + imageURI: 'QmXxR67ryeUw4xppPLbF2vJmfj1TCGgzANfiEZPzByM5CT', + hp: 100, + maxHp: 100, + attackDamage: 100, + }, + { + name: 'NAMI', + imageURI: 'QmPHX1R4QgvGQrZym5dpWzzopavyNX2WZaVGYzVQQ2QcQL', + hp: 50, + maxHp: 50, + attackDamage: 50, + }, + { + name: 'USOPP', + imageURI: 'QmUGjB7oQLBZdCDNJp9V9ZdjsBECjwcneRhE7bHcs9HwxG', + hp: 300, + maxHp: 300, + attackDamage: 25, + }, + ]; + // テストで使用するボスの情報を定義 + const bigBoss = { + name: 'CROCODILE', + imageURI: 'https://i.imgur.com/BehawOh.png', + hp: 100, + maxHp: 100, + attackDamage: 50, + }; + async function deployTextFixture() { const gameContractFactory = await hre.ethers.getContractFactory( 'MyEpicGame', ); - // Hardhat がローカルの Ethereum ネットワークを、コントラクトのためだけに作成します。 + // Hardhat がローカルの Ethereum ネットワークを、コントラクトのためだけに作成 const gameContract = await gameContractFactory.deploy( // キャラクターの名前 - ['ZORO', 'NAMI', 'USOPP'], + [characters[0].name, characters[1].name, characters[2].name], // キャラクターの画像を IPFS の CID に変更 + [characters[0].imageURI, characters[1].imageURI, characters[2].imageURI], + [characters[0].hp, characters[1].hp, characters[2].hp], [ - 'QmXxR67ryeUw4xppPLbF2vJmfj1TCGgzANfiEZPzByM5CT', - 'QmPHX1R4QgvGQrZym5dpWzzopavyNX2WZaVGYzVQQ2QcQL', - 'QmUGjB7oQLBZdCDNJp9V9ZdjsBECjwcneRhE7bHcs9HwxG', + characters[0].attackDamage, + characters[1].attackDamage, + characters[2].attackDamage, ], - [100, 50, 300], - [100, 50, 25], - 'CROCODILE', // Bossの名前 - 'https://i.imgur.com/BehawOh.png', // Bossの画像 - 100, // Bossのhp - 50, // Bossの攻撃力 + bigBoss.name, // Bossの名前 + bigBoss.imageURI, // Bossの画像 + bigBoss.hp, // Bossのhp + bigBoss.attackDamage, // Bossの攻撃力 ); await gameContract.deployed(); return { gameContract, + characters, + bigBoss, }; } + it('check if the characters are deployed correctly', async () => { + const { gameContract, characters } = await loadFixture(deployTextFixture); + + const savedCharacters = await gameContract.getAllDefaultCharacters(); + + // デプロイ時に指定したキャラクターの情報が保存されているかを確認 + expect(savedCharacters.length).to.equal(characters.length); + + for (let i = 0; i < savedCharacters.length; i++) { + expect(savedCharacters[i].name).to.equal(characters[i].name); + expect(savedCharacters[i].imageURI).to.equal(characters[i].imageURI); + expect(savedCharacters[i].hp.toNumber()).to.equal(characters[i].hp); + expect(savedCharacters[i].maxHp.toNumber()).to.equal(characters[i].maxHp); + expect(savedCharacters[i].attackDamage.toNumber()).to.equal( + characters[i].attackDamage, + ); + } + }); + + it('check if the big boss is deployed correctly', async () => { + const { gameContract, bigBoss } = await loadFixture(deployTextFixture); + + const savedBigBoss = await gameContract.getBigBoss(); + + // デプロイ時に指定したボスの情報が保存されているかを確認 + expect(savedBigBoss.name).to.equal(bigBoss.name); + expect(savedBigBoss.imageURI).to.equal(bigBoss.imageURI); + expect(savedBigBoss.hp.toNumber()).to.equal(bigBoss.hp); + expect(savedBigBoss.maxHp.toNumber()).to.equal(bigBoss.maxHp); + expect(savedBigBoss.attackDamage.toNumber()).to.equal(bigBoss.attackDamage); + }); + it('attack was successful', async () => { const { gameContract } = await loadFixture(deployTextFixture); - // 3体のNFTキャラクターの中から、3番目のキャラクターを Mint しています。 + // 3体のNFTキャラクターの中から、3番目のキャラクターを Mint する let txn = await gameContract.mintCharacterNFT(2); - // Minting が仮想マイナーにより、承認されるのを待ちます。 + // Minting が仮想マイナーにより、承認されるのを待つ await txn.wait(); // mintしたNFTにおける、攻撃前と後のhpを取得する @@ -334,10 +401,10 @@ describe('MyEpicGame', () => { it('check boss attack does not happen if boss hp is smaller than 0', async () => { const { gameContract } = await loadFixture(deployTextFixture); - // 3体のNFTキャラクターの中から、1番目のキャラクターを Mint しています。 + // 3体のNFTキャラクターの中から、1番目のキャラクターを Mint する let txn = await gameContract.mintCharacterNFT(0); - // Minting が仮想マイナーにより、承認されるのを待ちます。 + // Minting が仮想マイナーにより、承認されるのを待つ await txn.wait(); // 1回目の攻撃: attackBoss 関数を追加 @@ -346,7 +413,7 @@ describe('MyEpicGame', () => { // 2回目の攻撃: attackBoss 関数を追加 // ボスのhpがなくなった時に、エラーが発生することを確認 - txn = expect(gameContract.attackBoss()).to.be.revertedWith( + expect(gameContract.attackBoss()).to.be.revertedWith( 'Error: boss must have HP to attack characters.', ); }); @@ -355,10 +422,10 @@ describe('MyEpicGame', () => { it('check boss attack does not happen if character hp is smaller than 0', async () => { const { gameContract } = await loadFixture(deployTextFixture); - // 3体のNFTキャラクターの中から、2番目のキャラクターを Mint しています。 + // 3体のNFTキャラクターの中から、2番目のキャラクターを Mint する let txn = await gameContract.mintCharacterNFT(1); - // Minting が仮想マイナーにより、承認されるのを待ちます。 + // Minting が仮想マイナーにより、承認されるのを待つ await txn.wait(); // 1回目の攻撃: attackBoss 関数を追加 @@ -367,65 +434,110 @@ describe('MyEpicGame', () => { // 2回目の攻撃: attackBoss 関数を追加 // キャラクターのhpがなくなった時に、エラーが発生することを確認 - txn = expect(gameContract.attackBoss()).to.be.revertedWith( + expect(gameContract.attackBoss()).to.be.revertedWith( 'Error: character must have HP to attack boss.', ); }); }); ``` -では下のコマンドを実行することでコントラクトのテストをしていきましょう! +次に、MyEpicGameコントラクト内に定義していた`console.log`を削除しましょう。テストスクリプトを作成することにより、目視で結果を確認する必要がなくなります。 +import文を削除します。 + +```solidity +// === 下記を削除 === +import 'hardhat/console.sol'; ``` -yarn test + +constructor内の`bigBoss`をログ出力している箇所を削除します。 + +```solidity + // === 下記を削除 === + console.log( + 'Done initializing boss %s w/ HP %s, img %s', + bigBoss.name, + bigBoss.hp, + bigBoss.imageURI + ); ``` -下のような結果がでいれば成功です! +constructor内の`character`変数と`defaultCharacters`をログ出力している箇所を削除します。 +```solidity + // === 下記を削除 === + CharacterAttributes memory character = defaultCharacters[i]; + + // ハードハットのconsole.log()では、任意の順番で最大4つのパラメータを指定できます。 + // 使用できるパラメータの種類: uint, string, bool, address + console.log( + 'Done initializing %s w/ HP %s, img %s', + character.name, + character.hp, + character.imageURI + ); ``` -Compiled 14 Solidity files successfully +`mintCharacterNFT`関数内のログ出力を削除します。 - MyEpicGame -Done initializing boss CROCODILE w/ HP 100, img https://i.imgur.com/BehawOh.png -Done initializing ZORO w/ HP 100, img QmXxR67ryeUw4xppPLbF2vJmfj1TCGgzANfiEZPzByM5CT -Done initializing NAMI w/ HP 50, img QmPHX1R4QgvGQrZym5dpWzzopavyNX2WZaVGYzVQQ2QcQL -Done initializing USOPP w/ HP 300, img QmUGjB7oQLBZdCDNJp9V9ZdjsBECjwcneRhE7bHcs9HwxG -Minted NFT w/ tokenId 1 and characterIndex 2 +```solidity + // === 下記を削除 === + console.log( + 'Minted NFT w/ tokenId %s and characterIndex %s', + newItemId, + _characterIndex + ); +``` + +`attackBoss`関数内の4つのログ出力を削除します。 + +```solidity + // === 下記を削除 === + console.log( + '\nPlayer w/ character %s about to attack. Has %s HP and %s AD', + player.name, + player.hp, + player.attackDamage + ); + console.log( + 'Boss %s has %s HP and %s AD', + bigBoss.name, + bigBoss.hp, + bigBoss.attackDamage + ); -Player w/ character USOPP about to attack. Has 300 HP and 25 AD -Boss CROCODILE has 100 HP and 50 AD -Player attacked boss. New boss hp: 75 -Boss attacked player. New player hp: 250 + // ... - ✔ attack was successful (2072ms) -Minted NFT w/ tokenId 1 and characterIndex 0 + // === 下記を削除 === + // プレイヤーの攻撃をターミナルに出力する。 + console.log('Player attacked boss. New boss hp: %s', bigBoss.hp); + // ボスの攻撃をターミナルに出力する。 + console.log('Boss attacked player. New player hp: %s\n', player.hp); +``` -Player w/ character ZORO about to attack. Has 100 HP and 100 AD -Boss CROCODILE has 100 HP and 50 AD -Player attacked boss. New boss hp: 0 -Boss attacked player. New player hp: 50 - ✔ check boss attack does not happen if boss hp is smaller than 0 (62ms) +では下のコマンドを実行することでコントラクトのテストをしていきましょう! + +``` +yarn test +``` -Player w/ character ZORO about to attack. Has 50 HP and 100 AD -Boss CROCODILE has 0 HP and 50 AD -Minted NFT w/ tokenId 1 and characterIndex 1 +下のような結果がでいれば成功です! -Player w/ character NAMI about to attack. Has 50 HP and 50 AD -Boss CROCODILE has 100 HP and 50 AD -Player attacked boss. New boss hp: 50 -Boss attacked player. New player hp: 0 +``` +Compiled 1 Solidity file successfully - ✔ check boss attack does not happen if character hp is smaller than 0 (76ms) -Player w/ character NAMI about to attack. Has 0 HP and 50 AD -Boss CROCODILE has 50 HP and 50 AD + MyEpicGame + ✔ check if the characters are deployed correctly (1031ms) + ✔ check if the big boss is deployed correctly + ✔ attack was successful (41ms) + ✔ check boss attack does not happen if boss hp is smaller than 0 + ✔ check boss attack does not happen if character hp is smaller than 0 - 3 passing (2s) + 5 passing (1s) -✨ Done in 5.84s. ``` diff --git "a/docs/ETH-NFT-Maker/ja/section-2/lesson-3_ipfs\343\202\222\343\201\244\343\201\213\343\201\243\343\201\246mint\343\201\227\343\202\210\343\201\206\357\274\201.md" "b/docs/ETH-NFT-Maker/ja/section-2/lesson-3_ipfs\343\202\222\343\201\244\343\201\213\343\201\243\343\201\246mint\343\201\227\343\202\210\343\201\206\357\274\201.md" index 597bb1c74..b7e71f1f1 100644 --- "a/docs/ETH-NFT-Maker/ja/section-2/lesson-3_ipfs\343\202\222\343\201\244\343\201\213\343\201\243\343\201\246mint\343\201\227\343\202\210\343\201\206\357\274\201.md" +++ "b/docs/ETH-NFT-Maker/ja/section-2/lesson-3_ipfs\343\202\222\343\201\244\343\201\213\343\201\243\343\201\246mint\343\201\227\343\202\210\343\201\206\357\274\201.md" @@ -283,7 +283,35 @@ describe('Web3Mint', () => { }); ``` +次に、`Web3Mint`コントラクト内で定義していた`console.log`を削除しましょう。 + +import文を削除します。 + +```solidity +// === 下記を削除 === +import "hardhat/console.sol"; +``` + +constructor関数内の`console.log`を削除します。 + +```solidity + // === 下記を削除 === + console.log('This is my NFT contract.'); +``` + +`mintIpfsNFT`関数内の`console.log`を削除します。 + +```solidity + // === 下記を削除 === + console.log( + "An NFT w/ ID %s has been minted to %s", + newItemId, + msg.sender + ); +``` + では下のコマンドを実行してみましょう。 + ``` yarn test ``` @@ -291,16 +319,15 @@ yarn test 結果として下のような結果が出力されていればテスト成功です! ``` - Web3Mint -This is my NFT contract. -An NFT w/ ID 0 has been minted to 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 -An NFT w/ ID 1 has been minted to 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 - ✔ Should return the nft (2319ms) +Compiled 1 Solidity file successfully + + + Web3Mint + ✔ Should return the nft (904ms) - 1 passing (2s) + 1 passing (905ms) -✨ Done in 4.93s. ``` **brave**ブラウザでは、`ipfs://bafkreievxssucnete4vpthh3klylkv2ctll2sk2ib24jvgozyg62zdtm2y`のままブラウザに貼れば表示され、他のブラウザの場合は`https://ipfs.io/ipfs/自分のCID`のようにして、画像を確認してみましょう! diff --git "a/docs/ETH-NFT-Maker/ja/section-4/lesson-1_Web\343\202\242\343\203\227\343\203\252\343\202\222\343\202\242\343\203\203\343\203\227\343\203\207\343\203\274\343\203\210\343\201\227\343\202\210\343\201\206.md" "b/docs/ETH-NFT-Maker/ja/section-4/lesson-1_Web\343\202\242\343\203\227\343\203\252\343\202\222\343\202\242\343\203\203\343\203\227\343\203\207\343\203\274\343\203\210\343\201\227\343\202\210\343\201\206.md" index d87bb516b..5a7ffd209 100644 --- "a/docs/ETH-NFT-Maker/ja/section-4/lesson-1_Web\343\202\242\343\203\227\343\203\252\343\202\222\343\202\242\343\203\203\343\203\227\343\203\207\343\203\274\343\203\210\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/ETH-NFT-Maker/ja/section-4/lesson-1_Web\343\202\242\343\203\227\343\203\252\343\202\222\343\202\242\343\203\203\343\203\227\343\203\207\343\203\274\343\203\210\343\201\227\343\202\210\343\201\206.md" @@ -14,7 +14,6 @@ import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; //OpenZeppelinが提供するヘルパー機能をインポートします。 import "@openzeppelin/contracts/utils/Counters.sol"; import "./libraries/Base64.sol"; -import "hardhat/console.sol"; contract Web3Mint is ERC721 { struct NftAttributes { @@ -28,9 +27,7 @@ contract Web3Mint is ERC721 { NftAttributes[] Web3Nfts; - constructor() ERC721("NFT", "nft") { - console.log("This is my NFT contract."); - } + constructor() ERC721("NFT", "nft") {} // ユーザーが NFT を取得するために実行する関数です。 @@ -38,11 +35,6 @@ contract Web3Mint is ERC721 { uint256 newItemId = _tokenIds.current(); _safeMint(msg.sender, newItemId); Web3Nfts.push(NftAttributes({name: name, imageURL: imageURI})); - console.log( - "An NFT w/ ID %s has been minted to %s", - newItemId, - msg.sender - ); _tokenIds.increment(); } diff --git "a/docs/ICP-Encrypted-Notes/ja/section-1/lesson-2_\343\202\244\343\203\263\343\202\277\343\203\225\343\202\247\343\203\274\343\202\271\343\202\222\345\256\232\347\276\251\343\201\227\343\202\210\343\201\206.md" "b/docs/ICP-Encrypted-Notes/ja/section-1/lesson-2_\343\202\244\343\203\263\343\202\277\343\203\225\343\202\247\343\203\274\343\202\271\343\202\222\345\256\232\347\276\251\343\201\227\343\202\210\343\201\206.md" index a7ef6e899..b30a1b4b6 100644 --- "a/docs/ICP-Encrypted-Notes/ja/section-1/lesson-2_\343\202\244\343\203\263\343\202\277\343\203\225\343\202\247\343\203\274\343\202\271\343\202\222\345\256\232\347\276\251\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/ICP-Encrypted-Notes/ja/section-1/lesson-2_\343\202\244\343\203\263\343\202\277\343\203\225\343\202\247\343\203\274\343\202\271\343\202\222\345\256\232\347\276\251\343\201\227\343\202\210\343\201\206.md" @@ -4,7 +4,7 @@ #### Candidとは -Candidは、サービスのパブリック・インタフェースを記述することを主な目的としたインタフェース記述言語です。Candidの主な利点のひとつは、言語にとらわれず、Motoko[https://internetcomputer.org/docs/current/motoko/main/about-this-guide]、Rust、JavaScriptなどの異なるプログラミング言語で書かれたサービスとフロントエンド間の相互運用を可能にすることです。詳細は[こちら](https://internetcomputer.org/docs/current/developer-docs/backend/candid/candid-concepts)をご覧ください。 +Candidは、サービスのパブリック・インタフェースを記述することを主な目的としたインタフェース記述言語です。Candidの主な利点のひとつは、言語にとらわれず、[Motoko](https://internetcomputer.org/docs/current/motoko/main/about-this-guide)、Rust、JavaScriptなどの異なるプログラミング言語で書かれたサービスとフロントエンド間の相互運用を可能にすることです。詳細は[こちら](https://internetcomputer.org/docs/current/developer-docs/backend/candid/candid-concepts)をご覧ください。 Motokoでキャニスターを記述した場合、プログラムをコンパイルする際にコンパイラが自動的にCandidで記述されたファイル`.did`を生成してくれます。しかし、Rustでは2023年9月時点でそのような機能は組み込まれておらず、Candidインタフェースを自動生成するためには設定が必要です。 diff --git "a/docs/ICP-Static-Site/ja/section-4/lesson-1_\343\203\201\343\202\247\343\203\274\343\203\263\344\270\212\343\201\253\343\203\207\343\203\227\343\203\255\343\202\244\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/ICP-Static-Site/ja/section-4/lesson-1_\343\203\201\343\202\247\343\203\274\343\203\263\344\270\212\343\201\253\343\203\207\343\203\227\343\203\255\343\202\244\343\202\222\343\201\227\343\202\210\343\201\206.md" index e7d5d1192..71be7cc09 100644 --- "a/docs/ICP-Static-Site/ja/section-4/lesson-1_\343\203\201\343\202\247\343\203\274\343\203\263\344\270\212\343\201\253\343\203\207\343\203\227\343\203\255\343\202\244\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/ICP-Static-Site/ja/section-4/lesson-1_\343\203\201\343\202\247\343\203\274\343\203\263\344\270\212\343\201\253\343\203\207\343\203\227\343\203\255\343\202\244\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -66,7 +66,7 @@ Deployed canisters. それでは、デプロイしたポートフォリオサイトにアクセスしてみましょう。URLはキャニスター IDを用いたものになります。 -`https://.ic0.app/` +`https://.icp0.io/` キャニスター IDは、デプロイ時の出力や生成された`canister_ids.json`ファイル、または以下のコマンドで確認できます。 diff --git "a/docs/NEAR-MulPay/ja/section-0/lesson-2_\347\222\260\345\242\203\346\247\213\347\257\211\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-0/lesson-2_\347\222\260\345\242\203\346\247\213\347\257\211\343\202\222\343\201\227\343\202\210\343\201\206.md" index e024a20fb..3944d7775 100644 --- "a/docs/NEAR-MulPay/ja/section-0/lesson-2_\347\222\260\345\242\203\346\247\213\347\257\211\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-0/lesson-2_\347\222\260\345\242\203\346\247\213\347\257\211\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -31,7 +31,7 @@ metamaskのセットアップではアカウントを2つ(受信用・送金 Flutterの環境構築はそれぞれのPCによって予期しないエラーが出ることがよくある(筆者の経験)ので何か問題があれば気軽にdiscordで質問してみてください! -最後にコントラクトとやり取りする仲介役をしてくれる`Infura`でアカウントを作って、Aurora Testnet用のhttp keyを[こちら](https://infura.io/)で取得しましょう。 +次に、コントラクトとやり取りする仲介役をしてくれる`Infura`でアカウントを作って、Aurora Testnet用のhttp keyを[こちら](https://infura.io/)で取得しましょう。 手順は下の通りです。 1. アカウントを作成(作成済みの人は2から) @@ -45,44 +45,50 @@ Flutterの環境構築はそれぞれのPCによって予期しないエラー drop down buttonを押すとテストネット用のものがあります。これは後で使います! ![](/public/images/NEAR-MulPay/section-0/0_2_4.png) -では次に、`node` / `yarn`を取得する必要があります。お持ちでない場合は、[こちら](https://hardhat.org/tutorial/setting-up-the-environment.html)にアクセスしてください。 +最後に、web3ウォレットとdAppsを連携してくれる[WalletConnect](https://walletconnect.com/)のSDKを使用するために必要なProject IDを取得しましょう。[こちら](https://cloud.walletconnect.com/)にアクセスをしてください。 +手順は下の通りです。 + +1. アカウントを作成 +2. アカウントを作成したら右上の`+ New Project`ボタンを押す +3. 任意のプロジェクト名を入力して`Create`ボタンを押す +4. プロジェクトが作成されたら完了! + ![](/public/images/NEAR-MulPay/section-0/0_2_5.png) + +Project IDは、後ほどフロントエンドを構築する際に必要となります。 -`node v16`をインストールすることを推奨しています。 +ここからは実際にファイルを作成していきます。まずは`Node.js`を取得する必要があります。お持ちでない場合は、[こちら](https://hardhat.org/tutorial/setting-up-the-environment#installing-node.js)にアクセスしてください。このプロジェクトで推奨するバージョンはv20です。 + +インストールが完了したら、ターミナルで以下のコマンドを実行し、バージョンを確認してください。 + +``` +$ node -v +v20.5.0 +``` それでは本プロジェクトで使用するフォルダーを作成してきましょう。作業を始めるディレクトリに移動したら、次のコマンドを実行します。 ``` -mkdir NEAR-Mulpay -cd NEAR-Mulpay -yarn init --private -y +mkdir NEAR-MulPay +cd NEAR-MulPay +yarn init -w ``` -NEAR-Mulpayディレクトリ内に、package.jsonファイルが生成されます。 +⚠️ `command not found: yarn`が発生した場合は、以下のコマンドを実行後、再度`yarn init -w`を実行してください([参照](https://yarnpkg.com/getting-started/install))。 ``` -NEAR-Mulpay - └── package.json +corepack enable ``` -それでは、`package.json`ファイルを以下のように更新してください。 +NEAR-MulPayディレクトリ内の構成を確認してみましょう。 -```json -{ - "name": "NEAR-Mulpay", - "version": "1.0.0", - "description": "Token swap dapp", - "private": true, - "workspaces": { - "packages": [ - "packages/*" - ] - }, - "scripts": { - "contract": "yarn workspace contract", - "client": "yarn workspace client", - "test": "yarn workspace contract test" - } -} +``` +NEAR-MulPay/ +├── .git/ +├── .gitignore +├── README.md +├── package.json +├── packages/ +└── yarn.lock ``` `package.json`ファイルの内容を確認してみましょう。 @@ -94,20 +100,27 @@ NEAR-Mulpay **workspaces**の定義をしている部分は以下になります。 ```json -"workspaces": { - "packages": [ + "workspaces": [ "packages/*" - ] -}, + ], ``` -また、ワークスペース内の各パッケージにアクセスするためのコマンドを以下の部分で定義しています。 +それでは、`package.json`ファイルにワークスペース内の各パッケージにアクセスするためのコマンドを記述しましょう。下記のように`"scripts"`を追加してください。 ```json -"scripts": { - "contract": "yarn workspace contract", - "client": "yarn workspace client", - "test": "yarn workspace contract test" +{ + "name": "NEAR-MulPay", + "version": "1.0.0", + "description": "Token swap dapp", + "private": true, + "workspaces": [ + "packages/*" + ], + "scripts": { + "contract": "yarn workspace contract", + "client": "yarn workspace client", + "test": "yarn workspace contract test" + } } ``` @@ -117,39 +130,41 @@ NEAR-Mulpay yarn <パッケージ名> <実行したいコマンド> ``` -それでは、ワークスペースのパッケージを格納するディレクトリを作成しましょう。 - -以下のようなフォルダー構成となるように、`packages`ディレクトリとその中に`contract`ディレクトリを作成してください(`client`ディレクトリは、後ほどのレッスンでスターターコードをクローンする際に作成したいと思います)。 +次に、Nodeパッケージのインストール方法を定義しましょう。プロジェクトのルートに`.yarnrc.yml`ファイルを作成し、以下の内容を記述してください。 -```diff -NEAR-Mulpay - ├── package.json -+└── packages/ -+   └── contract/ +```yml +nodeLinker: node-modules ``` -`contract`ディレクトリには、スマートコントラクトを構築するためのファイルを作成していきます。 - -最後に、NEAR-Mulpayディレクトリ下に`.gitignore`ファイルを作成して以下の内容を書き込みます。 +最後に、`.gitignore`ファイルを下記の内容に更新しましょう。 ``` -**/yarn-error.log* +# yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions # dependencies **/node_modules -# miscpackages +# misc **/.DS_Store ``` 最終的に以下のようなフォルダー構成となっていることを確認してください。 ``` -NEAR-Mulpay - ├── .gitignore - ├── package.json - └── packages/ -   └── contract/ +NEAR-MulPay +├── .git/ +├── .gitignore +├── .yarnrc.yml +├── README.md +├── package.json +├── packages/ +└── yarn.lock ``` これでモノレポの雛形が完成しました! @@ -162,46 +177,24 @@ NEAR-Mulpay - 「サーバー」がブロックチェーンであることを除けば、Hardhatはローカルサーバーと同じです。 -それでは、先ほど作成した`packages/contract`ディレクトリ内にファイルを作成します。ターミナルに向かい、`packages/contract`ディレクトリ内で以下のコマンドを実行します。 +それでは、先ほど準備をしたワークスペースにスマートコントラクトを構築するためのパッケージを作成していきましょう。`NEAR-MulPay/packages`ディレクトリ中に`contract`ディレクトリを作成してください。 -``` -cd packages/contract -yarn init --private -y +```diff + NEAR-MulPay + └── packages/ ++   └── contract/ ``` -`package.json`の内容を以下のように書き換えてください。 +次に、`package.json`ファイルを作成します。ターミナルに向かい、`packages/contract`ディレクトリ内で以下のコマンドを実行します。 -```json -{ - "name": "contract", - "version": "1.0.0", - "private": true, - "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", - "@nomicfoundation/hardhat-network-helpers": "^1.0.8", - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "@nomiclabs/hardhat-ethers": "^2.2.2", - "@nomiclabs/hardhat-etherscan": "^3.1.7", - "@typechain/ethers-v5": "^10.2.0", - "@typechain/hardhat": "^6.1.5", - "chai": "^4.3.7", - "ethers": "^6.1.0", - "hardhat": "^2.13.0", - "hardhat-gas-reporter": "^1.0.9", - "solidity-coverage": "^0.8.2", - "typechain": "^8.1.1" - }, - "dependencies": { - "@openzeppelin/contracts": "^4.8.2", - "dotenv": "^16.0.3" - }, -} +``` +yarn init -p ``` -その後以下のコマンドを実行して必要なパッケージをインストールしてください。 +必要なパッケージをインストールしましょう。 ``` -yarn install +yarn add @openzeppelin/contracts@4.8.2 dotenv@16.3.1 && yarn add --dev @nomicfoundation/hardhat-chai-matchers@1.0.6 @nomicfoundation/hardhat-network-helpers@1.0.8 @nomicfoundation/hardhat-toolbox@2.0.2 @nomiclabs/hardhat-ethers@2.2.3 @nomiclabs/hardhat-etherscan@3.1.7 @typechain/ethers-v5@11.1.2 @typechain/hardhat@7.0.0 @types/chai@4.3.8 @types/mocha@10.0.2 chai@4.3.10 ethers@5.7.2 hardhat@2.18.1 hardhat-gas-reporter@1.0.9 solidity-coverage@0.8.5 ts-node@10.9.1 typechain@8.3.2 typescript@5.2.2 ``` > ✍️: `warning`について @@ -218,7 +211,7 @@ yarn install `packages/contract`ディレクトリにいることを確認し、次のコマンドを実行します。 ``` -npx hardhat +npx hardhat init ``` `hardhat`がターミナル上で立ち上がったら、それぞれの質問を以下のように答えていきます。 @@ -231,7 +224,7 @@ npx hardhat (例) ``` -$ npx hardhat +$ npx hardhat init 888 888 888 888 888 888 888 888 888 888 @@ -242,39 +235,29 @@ $ npx hardhat 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.13.0 👷‍ +👷 Welcome to Hardhat v2.18.1 👷‍ -✔ What do you want to do? · Create a JavaScript project -✔ Hardhat project root: · /NEAR-Mulpay/packages/contract +✔ What do you want to do? · Create a TypeScript project +✔ Hardhat project root: · /NEAR-MulPay/packages/contract ✔ Do you want to add a .gitignore? (Y/n) · y ✨ Project created ✨ See the README.md file for some example tasks you can run -Give Hardhat a star on Github if you're enjoying it! 💞✨ +Give Hardhat a star on Github if you're enjoying it! ⭐️✨ https://github.com/NomicFoundation/hardhat ``` -> ⚠️: 注意 #1 +> ⚠️: 注意 > > Windows で Git Bash を使用してハードハットをインストールしている場合、このステップ (HH1) でエラーが発生する可能性があります。問題が発生した場合は、WindowsCMD(コマンドプロンプト)を使用して HardHat のインストールを実行してみてください。 -> ⚠️: 注意 #2 -> -> `npx hardhat`が実行されなかった場合、以下をターミナルで実行してください。 -> -> ``` -> yarn add --dev @nomicfoundation/hardhat-toolbox -> ``` - この段階で、フォルダー構造は下記のようになっていることを確認してください。 ```diff -NEAR-Mulpay - ├── .gitignore - ├── package.json + NEAR-MulPay └── packages/   └── contract/ + ├── .gitignore @@ -283,77 +266,102 @@ NEAR-Mulpay + ├── hardhat.config.ts + ├── package.json + ├── scripts/ -+ └── test/ ++ ├── test/ ++ └── tsconfig.json ``` -それでは、`contract`ディレクトリ内に生成された`package.json`ファイルを以下を参考に更新をしましょう。 +package.jsonファイルに`"scripts"`を追加しましょう。 -```diff +```json { "name": "contract", "version": "1.0.0", -- "main": "index.js", -- "license": "MIT", "private": true, - "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", - "@nomicfoundation/hardhat-network-helpers": "^1.0.8", - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "@nomiclabs/hardhat-ethers": "^2.2.2", - "@nomiclabs/hardhat-etherscan": "^3.1.7", - "@typechain/ethers-v5": "^10.2.0", - "@typechain/hardhat": "^6.1.5", - "chai": "^4.3.7", - "ethers": "^6.1.0", - "hardhat": "^2.13.0", - "hardhat-gas-reporter": "^1.0.9", - "solidity-coverage": "^0.8.2", - "typechain": "^8.1.1" + "scripts": { + "deploy":"npx hardhat run scripts/deploy.ts --network testnet_aurora", + "test": "npx hardhat test" }, -+ "scripts": { -+ "test": "npx hardhat test", -+ "deploy":"npx hardhat run scripts/deploy.ts --network testnet_aurora", -+ } -} + "dependencies": { + ... ``` -不要な定義を削除し、hardhatの自動テストを実行するためのコマンドを追加しました。 +test/Lock.tsファイルを下の内容で上書きしてください。ethers v6ベースのToolboxで生成された初期コードを、ethers v5ベースのコードに置き換えます。このプロジェクトではethers v5を使用するためです。 -### ⭐️ 実行する +修正箇所は2箇所です。まず初めに、ファイルの先頭でインポートしている`@nomicfoundation/hardhat-toolbox/network-helpers`を`@nomicfoundation/hardhat-network-helpers`に変更します。 -すべてが機能していることを確認するには、以下を実行します。 +**(変更前)** -``` -npx hardhat compile +```typescript +import { + time, + loadFixture, +} from "@nomicfoundation/hardhat-toolbox/network-helpers"; ``` -次に、以下を実行します。 +**(変更後)** +```typescript +import { + time, + loadFixture, +} from "@nomicfoundation/hardhat-network-helpers"; ``` -npx hardhat test + +次に、47行目の`lock.target`を`lock.address`に変更します。 + +**(変更前)** + +```typescript + expect(await ethers.provider.getBalance(lock.target)).to.equal( + lockedAmount + ); ``` -次のように表示されます。 +**(変更後)** -![](/public/images/NEAR-Mulpay/section-1/1_2_2.png) +```typescript + expect(await ethers.provider.getBalance(lock.address)).to.equal( + lockedAmount + ); +``` -ターミナル上で`ls`と入力してみて、下記のフォルダーとファイルが表示されていたら成功です。 +### ⭐️ 実行する + +すべてが機能していることを確認するには、以下を実行します。 ``` -README.md cache hardhat.config.ts package.json test -artifacts contracts node_modules scripts +yarn test ``` -ここまできたら、フォルダーの中身を整理しましょう。 +次のように表示されます。 + +``` + Lock + Deployment + ✔ Should set the right unlockTime (1257ms) + ✔ Should set the right owner + ✔ Should receive and store the funds to lock + ✔ Should fail if the unlockTime is not in the future + Withdrawals + Validations + ✔ Should revert with the right error if called too soon + ✔ Should revert with the right error if called from another account + ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it + Events + ✔ Should emit an event on withdrawals + Transfers + ✔ Should transfer the funds to the owner -まず、`test`の下のファイル`Lock.js`を削除します。 -1. `test`フォルダーに移動: `cd test` + 9 passing (1s) -2. `Lock.js`を削除: `rm Lock.js` +``` -次に、上記の手順を参考にして`contracts`の下の`Lock.sol`を削除してください。実際のフォルダは削除しないように注意しましょう。 +ターミナル上で`ls`と入力してみて、下記のフォルダーとファイルが表示されていたら成功です。 +``` +README.md artifacts cache contracts hardhat.config.ts package.json scripts test tsconfig.json typechain-types +``` ### ☀️ Hardhat の機能について @@ -371,31 +379,33 @@ Hardhatは段階的に下記を実行しています。 次にフロントエンドのプロジェクトを作成していきます。 -`NEAR-Mulpay/packages`ディレクトリに移動して下のコマンドをターミナルで実行しましょう。 +`NEAR-MulPay/packages`ディレクトリに移動して下のコマンドをターミナルで実行しましょう。 ``` flutter create client ``` -次に下のコマンドを実行して`package.json`を作成してください。 +次に、作成された`client`ディレクトリに移動して下のコマンドを実行しましょう。 ``` -yarn init --private -y +yarn init -p ``` -その後作成したpackage.jsonファイルを下のように編集してください。 +その後作成されたpackage.jsonファイルに`"scripts"`を追加しましょう。 ```json { "name": "client", "version": "1.0.0", - "scripts":{ - "start": "flutter run" + "private": true, + "scripts": { + "flutter:install": "flutter pub get", + "flutter:run": "flutter run" } } ``` -プロジェクトが完成したら`client`ディレクトリに移動して、ディレクトリ構造が以下のようになっていることを確認してください。 +プロジェクトが完成したら`client`ディレクトリの構造が以下のようになっていることを確認してください。 末尾が`/`となっているものはディレクトリ、それ以外はファイルであることを示しています @@ -404,11 +414,11 @@ client ├── README.md ├── analysis_options.yaml ├── android/ +├── client.iml ├── ios/ ├── lib/ ├── linux/ ├── macos/ -├── payment_dapp.iml ├── package.json ├── pubspec.lock ├── pubspec.yaml @@ -449,6 +459,12 @@ lib/ tree -L 3 -F ``` +最後に、プロジェクトのルートにある`yarn.lock`ファイルを更新したいと思います。これは、clientワークスペースに新たなpackage.jsonが追加されたことを反映するためです。プロジェクトルートで以下のコマンドを実行しましょう。 + +``` +yarn install +``` + これで環境構築+ディレクトリ構造の作成は完了です。 ### 🙋‍♂️ 質問する diff --git "a/docs/NEAR-MulPay/ja/section-1/lesson-1_\343\202\271\343\203\236\343\203\274\343\203\210\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\345\256\237\350\243\205\343\201\256\344\270\213\346\272\226\345\202\231\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-1/lesson-1_\343\202\271\343\203\236\343\203\274\343\203\210\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\345\256\237\350\243\205\343\201\256\344\270\213\346\272\226\345\202\231\343\202\222\343\201\227\343\202\210\343\201\206.md" index 4ecca2ed2..215c22cfb 100644 --- "a/docs/NEAR-MulPay/ja/section-1/lesson-1_\343\202\271\343\203\236\343\203\274\343\203\210\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\345\256\237\350\243\205\343\201\256\344\270\213\346\272\226\345\202\231\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-1/lesson-1_\343\202\271\343\203\236\343\203\274\343\203\210\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\345\256\237\350\243\205\343\201\256\344\270\213\346\272\226\345\202\231\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -11,18 +11,14 @@ contract/ ├── cache/ ├── contracts/ ├── hardhat.config.ts -├── node_modules/ -├── package-lock.json ├── package.json ├── scripts/ -├── src/ ├── test/ ├── tsconfig.json -├── typechain-types/ -└── yarn.lock +└── typechain-types/ ``` -この中の`contracts`フォルダに`ERC20Tokens.sol`と`Swap.sol`というファイルを作成してください。 +この中の`contracts`フォルダに`ERC20Tokens.sol`と`Swap.sol`というファイルを作成してください。元々あった`Lock.sol`は削除してください。 また、`test`フォルダには`swap.test.ts`を作成してください。元々あった`Lock.ts`は削除してください。 @@ -51,11 +47,10 @@ AURORA_PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY" ```ts import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; -import "@typechain/hardhat"; -require("dotenv").config(); +import "dotenv/config"; const config: HardhatUserConfig = { - solidity: "0.8.17", + solidity: "0.8.19", networks: { testnet_aurora: { url: "https://testnet.aurora.dev", diff --git "a/docs/NEAR-MulPay/ja/section-1/lesson-2_\347\213\254\350\207\252\343\203\210\343\203\274\343\202\257\343\203\263\347\231\272\350\241\214\343\200\201swap\346\251\237\350\203\275\343\201\256\345\256\237\350\243\205\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-1/lesson-2_\347\213\254\350\207\252\343\203\210\343\203\274\343\202\257\343\203\263\347\231\272\350\241\214\343\200\201swap\346\251\237\350\203\275\343\201\256\345\256\237\350\243\205\343\202\222\343\201\227\343\202\210\343\201\206.md" index cb4161221..518734b3b 100644 --- "a/docs/NEAR-MulPay/ja/section-1/lesson-2_\347\213\254\350\207\252\343\203\210\343\203\274\343\202\257\343\203\263\347\231\272\350\241\214\343\200\201swap\346\251\237\350\203\275\343\201\256\345\256\237\350\243\205\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-1/lesson-2_\347\213\254\350\207\252\343\203\210\343\203\274\343\202\257\343\203\263\347\231\272\350\241\214\343\200\201swap\346\251\237\350\203\275\343\201\256\345\256\237\350\243\205\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -10,7 +10,7 @@ ```solidity // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -123,7 +123,7 @@ etherの最小単位`wei`は`10の-18乗`であり、発行数の単位はweiな ```solidity // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; // Import this file to use console.log import "hardhat/console.sol"; diff --git "a/docs/NEAR-MulPay/ja/section-1/lesson-3_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\256\343\203\206\343\202\271\343\203\210\343\200\201depoloy\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-1/lesson-3_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\256\343\203\206\343\202\271\343\203\210\343\200\201depoloy\343\202\222\343\201\227\343\202\210\343\201\206.md" index b7dc83df7..c6df39f56 100644 --- "a/docs/NEAR-MulPay/ja/section-1/lesson-3_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\256\343\203\206\343\202\271\343\203\210\343\200\201depoloy\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-1/lesson-3_\343\202\263\343\203\263\343\203\210\343\203\251\343\202\257\343\203\210\343\201\256\343\203\206\343\202\271\343\203\210\343\200\201depoloy\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -9,102 +9,105 @@ [`swap.test.ts`] ```ts -const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); -import { ethers } from "hardhat"; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; -describe("Swap Contract", function () { +describe('Swap Contract', function () { async function deployTokenFixture() { const [owner, addr1] = await ethers.getSigners(); - const daiToken = await ethers.getContractFactory("DaiToken"); - const ethToken = await ethers.getContractFactory("EthToken"); - const auroraToken = await ethers.getContractFactory("AuroraToken"); - const shibainuToken = await ethers.getContractFactory("ShibainuToken"); - const solanaToken = await ethers.getContractFactory("SolanaToken"); - const tetherToken = await ethers.getContractFactory("TetherToken"); - const uniswapToken = await ethers.getContractFactory("UniswapToken"); - const polygonToken = await ethers.getContractFactory("PolygonToken"); - const swapFactory = await ethers.getContractFactory("SwapContract"); + const swapFactory = await ethers.getContractFactory('SwapContract'); + const auroraToken = await ethers.getContractFactory('AuroraToken'); + const daiToken = await ethers.getContractFactory('DaiToken'); + const ethToken = await ethers.getContractFactory('EthToken'); + const polygonToken = await ethers.getContractFactory('PolygonToken'); + const shibainuToken = await ethers.getContractFactory('ShibainuToken'); + const solanaToken = await ethers.getContractFactory('SolanaToken'); + const tetherToken = await ethers.getContractFactory('TetherToken'); + const uniswapToken = await ethers.getContractFactory('UniswapToken'); const SwapContract = await swapFactory.deploy(); + const AoaToken = await auroraToken.deploy(SwapContract.address); const DaiToken = await daiToken.deploy(SwapContract.address); const EthToken = await ethToken.deploy(SwapContract.address); - const AoaToken = await auroraToken.deploy(SwapContract.address); + const MaticToken = await polygonToken.deploy(SwapContract.address); const ShibToken = await shibainuToken.deploy(SwapContract.address); const SolToken = await solanaToken.deploy(SwapContract.address); - const UsdtToken = await tetherToken.deploy(SwapContract.address); const UniToken = await uniswapToken.deploy(SwapContract.address); - const MaticToken = await polygonToken.deploy(SwapContract.address); + const UsdtToken = await tetherToken.deploy(SwapContract.address); return { owner, addr1, + SwapContract, + AoaToken, DaiToken, EthToken, - AoaToken, + MaticToken, ShibToken, SolToken, - UsdtToken, UniToken, - MaticToken, - SwapContract, + UsdtToken, }; } - describe("Deployment", function () { + describe('Deployment', function () { // check if the owner of DAI token is smart contract - it("ERC20 token is minted from smart contract", async function () { + it('ERC20 token is minted from smart contract', async function () { const { DaiToken, SwapContract } = await loadFixture(deployTokenFixture); + const balanceOfDai = await DaiToken.balanceOf(SwapContract.address); - console.log(balanceOfDai.toString()); + // convert expected value `1000000 Ether` to Wei units + const expectedValue = ethers.utils.parseUnits('1000000', 18); + + expect(balanceOfDai).to.equal(expectedValue); }); // get the value between DAI and ETH - it("Get value between DAI and ETH", async function () { + it('Get value between DAI and ETH', async function () { const { DaiToken, EthToken, SwapContract } = await loadFixture( - deployTokenFixture + deployTokenFixture, ); + const value = await SwapContract.calculateValue( EthToken.address, - DaiToken.address - ); - console.log( - `value of ETH/DAI is ${ - value / parseInt(ethers.utils.parseEther("1").toString()) - }` + DaiToken.address, ); + // convert expected value `0.1 Ether` to Wei units + const expectedValue = ethers.utils.parseUnits('0.1', 18); + + expect(value).to.equal(expectedValue); }); // check swap function works - it("swap function", async function () { + it('Swap function', async function () { const { owner, addr1, DaiToken, EthToken, UniToken, SwapContract } = await loadFixture(deployTokenFixture); await DaiToken.approve( SwapContract.address, - ethers.utils.parseEther("200") + ethers.utils.parseEther('200'), ); await SwapContract.distributeToken( DaiToken.address, - ethers.utils.parseEther("100"), - owner.address + ethers.utils.parseEther('100'), + owner.address, ); + const ethAmountBefore = await DaiToken.balanceOf(addr1.address); - console.log( - `Before transfer, address_1 has ${ethAmountBefore.toString()} ETH` - ); + expect(ethAmountBefore).to.equal(0); await SwapContract.swap( DaiToken.address, UniToken.address, EthToken.address, - ethers.utils.parseEther("1"), - addr1.address + ethers.utils.parseEther('1'), + addr1.address, ); - const ethAmountAfter = ethers.utils.formatEther( - await EthToken.balanceOf(addr1.address) - ); - console.log(`After transfer, address_1 has ${ethAmountAfter} ETH`); + const ethAmountAfter = await EthToken.balanceOf(addr1.address); + const expectedValue = ethers.utils.parseUnits('0.1', 18); + expect(ethAmountAfter).to.equal(expectedValue); }); }); }); @@ -112,53 +115,58 @@ describe("Swap Contract", function () { 最初に宣言している`deployTokenFixture`関数はそれぞれのコントラクトをデプロイを行うための関数で、それぞれのテストを行う前に走らせる必要があります。 -```js -async function deployTokenFixture() { +```ts + async function deployTokenFixture() { const [owner, addr1] = await ethers.getSigners(); - const daiToken = await ethers.getContractFactory("DaiToken"); - const ethToken = await ethers.getContractFactory("EthToken"); - const auroraToken = await ethers.getContractFactory("AuroraToken"); - const shibainuToken = await ethers.getContractFactory("ShibainuToken"); - const solanaToken = await ethers.getContractFactory("SolanaToken"); - const tetherToken = await ethers.getContractFactory("TetherToken"); - const uniswapToken = await ethers.getContractFactory("UniswapToken"); - const polygonToken = await ethers.getContractFactory("PolygonToken"); - const swapFactory = await ethers.getContractFactory("SwapContract"); + const swapFactory = await ethers.getContractFactory('SwapContract'); + const auroraToken = await ethers.getContractFactory('AuroraToken'); + const daiToken = await ethers.getContractFactory('DaiToken'); + const ethToken = await ethers.getContractFactory('EthToken'); + const polygonToken = await ethers.getContractFactory('PolygonToken'); + const shibainuToken = await ethers.getContractFactory('ShibainuToken'); + const solanaToken = await ethers.getContractFactory('SolanaToken'); + const tetherToken = await ethers.getContractFactory('TetherToken'); + const uniswapToken = await ethers.getContractFactory('UniswapToken'); const SwapContract = await swapFactory.deploy(); + const AoaToken = await auroraToken.deploy(SwapContract.address); const DaiToken = await daiToken.deploy(SwapContract.address); const EthToken = await ethToken.deploy(SwapContract.address); - const AoaToken = await auroraToken.deploy(SwapContract.address); + const MaticToken = await polygonToken.deploy(SwapContract.address); const ShibToken = await shibainuToken.deploy(SwapContract.address); const SolToken = await solanaToken.deploy(SwapContract.address); - const UsdtToken = await tetherToken.deploy(SwapContract.address); const UniToken = await uniswapToken.deploy(SwapContract.address); - const MaticToken = await polygonToken.deploy(SwapContract.address); + const UsdtToken = await tetherToken.deploy(SwapContract.address); return { owner, addr1, + SwapContract, + AoaToken, DaiToken, EthToken, - AoaToken, + MaticToken, ShibToken, SolToken, - UsdtToken, UniToken, - MaticToken, - SwapContract, + UsdtToken, }; } ``` 最初の部分ではERC20規格のトークンがきちんとdeployされているかをテストしています。 -```js -it("ERC20 token is minted from smart contract", async function () { +```ts + // check if the owner of DAI token is smart contract + it('ERC20 token is minted from smart contract', async function () { const { DaiToken, SwapContract } = await loadFixture(deployTokenFixture); + const balanceOfDai = await DaiToken.balanceOf(SwapContract.address); - console.log(balanceOfDai.toString()); + // convert expected value `1000000 Ether` to Wei units + const expectedValue = ethers.utils.parseUnits('1000000', 18); + + expect(balanceOfDai).to.equal(expectedValue); }); ``` @@ -166,22 +174,23 @@ it("ERC20 token is minted from smart contract", async function () { 次に`DAI/ETH`の価値を算出しています。SwapContractが保有しているそれぞれのトークン量によって変動します。 -`ERC20Tokens.sol`ではETHの発行量はDAIのそれより1/10の量なので0.1になるはずです。 +`ERC20Tokens.sol`ではETHの発行量はDAIのそれより1/10の量なので0.1 etherになるはずです。 -```js -it("Get value ETH/DAI", async function () { +```ts + // get the value between DAI and ETH + it('Get value between DAI and ETH', async function () { const { DaiToken, EthToken, SwapContract } = await loadFixture( - deployTokenFixture + deployTokenFixture, ); + const value = await SwapContract.calculateValue( EthToken.address, - DaiToken.address - ); - console.log( - `value of ETH/DAI is ${ - value / parseInt(ethers.utils.parseEther("1").toString()) - }` + DaiToken.address, ); + // convert expected value `0.1 Ether` to Wei units + const expectedValue = ethers.utils.parseUnits('0.1', 18); + + expect(value).to.equal(expectedValue); }); ``` @@ -189,38 +198,36 @@ it("Get value ETH/DAI", async function () { その後受領者の残高をswap前後で確認しています。 -```js -// check swap function works - it("swap function", async function () { +```ts + // check swap function works + it('Swap function', async function () { const { owner, addr1, DaiToken, EthToken, UniToken, SwapContract } = await loadFixture(deployTokenFixture); await DaiToken.approve( SwapContract.address, - ethers.utils.parseEther("200") + ethers.utils.parseEther('200'), ); await SwapContract.distributeToken( DaiToken.address, - ethers.utils.parseEther("100"), - owner.address + ethers.utils.parseEther('100'), + owner.address, ); + const ethAmountBefore = await DaiToken.balanceOf(addr1.address); - console.log( - `Before transfer, address_1 has ${ethAmountBefore.toString()} ETH` - ); + expect(ethAmountBefore).to.equal(0); await SwapContract.swap( DaiToken.address, UniToken.address, EthToken.address, - ethers.utils.parseEther("1"), - addr1.address + ethers.utils.parseEther('1'), + addr1.address, ); - const ethAmountAfter = ethers.utils.formatEther( - await EthToken.balanceOf(addr1.address) - ); - console.log(`After transfer, address_1 has ${ethAmountAfter} ETH`); + const ethAmountAfter = await EthToken.balanceOf(addr1.address); + const expectedValue = ethers.utils.parseUnits('0.1', 18); + expect(ethAmountAfter).to.equal(expectedValue); }); ``` @@ -233,15 +240,11 @@ yarn test これによって以下のような結果が返ってくるはずです。 ``` - Swap Contract + Swap Contract Deployment -1000000000000000000000000 - ✔ ERC20 token is minted from smart contract (1241ms) -value of ETH/DAI is 0.1 + ✔ ERC20 token is minted from smart contract (1083ms) ✔ Get value between DAI and ETH -Before transfer, address_1 has 0 ETH -After transfer, address_1 has 0.1 ETH - ✔ swap function (50ms) + ✔ Swap function 3 passing (1s) @@ -263,33 +266,37 @@ After transfer, address_1 has 0.1 ETH [`deploy.ts`] -```js -require("dotenv").config(); -const hre = require("hardhat"); - -const provider = hre.ethers.provider; -const deployerWallet = new hre.ethers.Wallet( - process.env.AURORA_PRIVATE_KEY, - provider -); +```ts +import 'dotenv/config'; +import hre from 'hardhat'; async function main() { - console.log("Deploying contracts with the account:", deployerWallet.address); + if (process.env.AURORA_PRIVATE_KEY === undefined) { + throw Error('AURORA_PRIVATE_KEY is not set'); + } + + const provider = hre.ethers.provider; + const deployerWallet = new hre.ethers.Wallet( + process.env.AURORA_PRIVATE_KEY, + provider + ); + + console.log('Deploying contracts with the account:', deployerWallet.address); console.log( - "Account balance:", + 'Account balance:', (await deployerWallet.getBalance()).toString() ); - const swapFactory = await hre.ethers.getContractFactory("SwapContract"); - const daiToken = await hre.ethers.getContractFactory("DaiToken"); - const ethToken = await hre.ethers.getContractFactory("EthToken"); - const aoaToken = await hre.ethers.getContractFactory("AuroraToken"); - const shibToken = await hre.ethers.getContractFactory("ShibainuToken"); - const solToken = await hre.ethers.getContractFactory("SolanaToken"); - const usdtToken = await hre.ethers.getContractFactory("TetherToken"); - const uniToken = await hre.ethers.getContractFactory("UniswapToken"); - const maticToken = await hre.ethers.getContractFactory("PolygonToken"); + const swapFactory = await hre.ethers.getContractFactory('SwapContract'); + const aoaToken = await hre.ethers.getContractFactory('AuroraToken'); + const daiToken = await hre.ethers.getContractFactory('DaiToken'); + const ethToken = await hre.ethers.getContractFactory('EthToken'); + const maticToken = await hre.ethers.getContractFactory('PolygonToken'); + const shibToken = await hre.ethers.getContractFactory('ShibainuToken'); + const solToken = await hre.ethers.getContractFactory('SolanaToken'); + const uniToken = await hre.ethers.getContractFactory('UniswapToken'); + const usdtToken = await hre.ethers.getContractFactory('TetherToken'); const SwapContract = await swapFactory.connect(deployerWallet).deploy(); await SwapContract.deployed(); @@ -297,32 +304,32 @@ async function main() { const [deployer] = await hre.ethers.getSigners(); console.log(`deployer address is ${deployer.address}`); + const AoaToken = await aoaToken.deploy(SwapContract.address); const DaiToken = await daiToken.deploy(SwapContract.address); const EthToken = await ethToken.deploy(SwapContract.address); - const AoaToken = await aoaToken.deploy(SwapContract.address); + const MaticToken = await maticToken.deploy(SwapContract.address); const ShibToken = await shibToken.deploy(SwapContract.address); const SolToken = await solToken.deploy(SwapContract.address); - const UsdtToken = await usdtToken.deploy(SwapContract.address); const UniToken = await uniToken.deploy(SwapContract.address); - const MaticToken = await maticToken.deploy(SwapContract.address); + const UsdtToken = await usdtToken.deploy(SwapContract.address); + await AoaToken.deployed(); await DaiToken.deployed(); await EthToken.deployed(); - await AoaToken.deployed(); + await MaticToken.deployed(); await ShibToken.deployed(); await SolToken.deployed(); - await UsdtToken.deployed(); await UniToken.deployed(); - await MaticToken.deployed(); + await UsdtToken.deployed(); - console.log("Swap Contract is deployed to:", SwapContract.address); - console.log("DaiToken is deployed to:", DaiToken.address); - console.log("EthToken is deployed to:", EthToken.address); - console.log("AoaToken is deployed to:", AoaToken.address); - console.log("ShibToken is deployed to:", ShibToken.address); - console.log("SolToken is deployed to:", SolToken.address); - console.log("UsdtToken is deployed to:", UsdtToken.address); - console.log("UniToken is deployed to:", UniToken.address); - console.log("MaticToken is deployed to:", MaticToken.address); + console.log('Swap Contract is deployed to:', SwapContract.address); + console.log('AoaToken is deployed to:', AoaToken.address); + console.log('DaiToken is deployed to:', DaiToken.address); + console.log('EthToken is deployed to:', EthToken.address); + console.log('MaticToken is deployed to:', MaticToken.address); + console.log('ShibToken is deployed to:', ShibToken.address); + console.log('SolToken is deployed to:', SolToken.address); + console.log('UniToken is deployed to:', UniToken.address); + console.log('UsdtToken is deployed to:', UsdtToken.address); } main().catch((error) => { @@ -355,14 +362,14 @@ Deploying contracts with the account: 0xa9eD1748Ffcda5442dCaEA242603E7e3FF09dD7F Account balance: 232046724600000000 deployer address is 0xa9eD1748Ffcda5442dCaEA242603E7e3FF09dD7F Swap Contract is deployed to: 0xC678d76a12Dd7f87FF1f952B6bEEd2c0fd308CF8 +AoaToken is deployed to: 0x10E9C13e9f73A35d4a0C8AA8328b84EF9747b7a8 DaiToken is deployed to: 0x48a6b4beAeB3a959Cd358e3365fc9d178eB0B2D9 EthToken is deployed to: 0x395A1065eA907Ab366807d68bbe21Df83169bA6c -AoaToken is deployed to: 0x10E9C13e9f73A35d4a0C8AA8328b84EF9747b7a8 +MaticToken is deployed to: 0x4A8c0C9f9f2444197cE78b672F0D98D1Fe47bdA6 ShibToken is deployed to: 0xa11e679EE578B32d12Dbe2882FcC387A86C8f887 SolToken is deployed to: 0x30E198301969fDeddDCb84cE2C284dF58d4AB944 -UsdtToken is deployed to: 0x44734B834364c37d35e6E4253779e9459c93B5F4 UniToken is deployed to: 0xC73F7cBD464aC7163D03dE669dedc3d1fA6Af5E4 -MaticToken is deployed to: 0x4A8c0C9f9f2444197cE78b672F0D98D1Fe47bdA6 +UsdtToken is deployed to: 0x44734B834364c37d35e6E4253779e9459c93B5F4 ``` これでコントラクトのdeployは成功したので、コントラクトの実装は完了です! diff --git "a/docs/NEAR-MulPay/ja/section-2/lesson-1_UI\343\201\256\345\237\272\347\244\216\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-2/lesson-1_UI\343\201\256\345\237\272\347\244\216\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" index c40a466f5..98faff037 100644 --- "a/docs/NEAR-MulPay/ja/section-2/lesson-1_UI\343\201\256\345\237\272\347\244\216\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-2/lesson-1_UI\343\201\256\345\237\272\347\244\216\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" @@ -9,20 +9,20 @@ 追加後は下のようなディレクトリ構造になります。 ``` +client/ ├── README.md ├── analysis_options.yaml ├── android/ ├── assets/ ├── build/ -├── firebase.json ├── ios/ ├── lib/ ├── linux/ ├── macos/ -├── payment_dapp.iml +├── package.json ├── pubspec.lock ├── pubspec.yaml -├── smartcontracts/assets +├── smartcontracts/ ├── test/ ├── web/ └── windows/ @@ -43,50 +43,53 @@ [aurora-aoa-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_3.png) -[ethereum-eth-logo.png] +[dai-dai-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_4.png) -[polygon-matic-logo.png] +[ethereum-eth-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_5.png) -[shib-logo.png] +[polygon-matic-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_6.png) -[solana-sol-logo.png] +[shib-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_7.png) -[tether-usdt-logo.png] +[solana-sol-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_8.png) -[uniswap-uni-logo.png] +[tether-usdt-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_9.png) -[unchain_logo.png] +[uniswap-uni-logo.png] ![](/public/images/NEAR-MulPay/section-2/2_1_10.png) +[unchain_logo.png] +![](/public/images/NEAR-MulPay/section-2/2_1_11.png) + [multiple-coins.jpg] -![](/public/images/NEAR-MulPay/section-2/2_1_11.jpg) +![](/public/images/NEAR-MulPay/section-2/2_1_12.jpg) [pop.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_12.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_13.svg) [pay.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_13.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_14.svg) [home.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_14.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_15.svg) [three-dots.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_15.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_16.svg) [triangle.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_16.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_17.svg) [wallet_screen_img.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_17.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_18.svg) [wallet.svg] -![](/public/images/NEAR-MulPay/section-2/2_1_18.svg) +![](/public/images/NEAR-MulPay/section-2/2_1_19.svg) 次に`smartcontracts`ディレクトリにsection-1で作成したコントラクトのabiファイルを追加します。 @@ -99,6 +102,7 @@ smarcontractsディレクトリの中身は下のようになります。 ``` smartcontracts/ ├── AuroraToken.json +├── DaiToken.json ├── EthToken.json ├── PolygonToken.json ├── ShibainuToken.json @@ -127,23 +131,23 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - provider: ^6.0.3 - fluid_bottom_nav_bar: ^1.3.0 - hexcolor: ^3.0.1 - google_fonts: ^4.0.0 - flutter_svg: ^0.22.0 - dropdown_button2: ^1.7.1 - fluttertoast: ^8.0.9 - qr_flutter: ^4.0.0 + cupertino_icons: 1.0.6 + dropdown_button2: 1.9.4 + fluid_bottom_nav_bar: 1.3.0 + flutter_dotenv: 5.1.0 + flutter_svg: 0.22.0 + fluttertoast: 8.2.2 + google_fonts: 4.0.4 + hexcolor: 3.0.1 + http: 0.13.6 + provider: 6.0.5 qr_code_scanner: 1.0.0 - http: ^0.13.5 - web3dart: 2.3.5 - web_socket_channel: ^2.2.0 - flutter_dotenv: ^5.0.2 - url_launcher: ^6.1.2 - web3_connect: ^0.0.3 - responsive_framework: ^1.0.0 + qr_flutter: 4.1.0 + responsive_framework: 1.1.1 + url_launcher: 6.2.1 + walletconnect_flutter_v2: 2.1.8 + web_socket_channel: 2.4.0 + web3dart: 2.6.1 ``` 次に使用する画像やファイルの宣言、アイコンを変更するための記述をしていきます。先ほどの記述の下に書いてあるものを以下のように書き換えましょう。 @@ -183,8 +187,8 @@ flutter: ``` スペースについては下のようになっていればOKです。 -![](/public/images/NEAR-MulPay/section-2/2_1_19.png) ![](/public/images/NEAR-MulPay/section-2/2_1_20.png) +![](/public/images/NEAR-MulPay/section-2/2_1_21.png) その後、`client`ディレクトリに移動して下のコマンドをターミナルで実行しましょう。 @@ -208,37 +212,41 @@ flutter pub run flutter_launcher_icons:main [`.env`] ``` +SWAP_CONTRACT_ADDRESS = "0xC678d76a12Dd7f87FF1f952B6bEEd2c0fd308CF8" AOA_CONTRACT_ADDRESS = "0x10E9C13e9f73A35d4a0C8AA8328b84EF9747b7a8" -SHIB_CONTRACT_ADDRESS = "0xa11e679EE578B32d12Dbe2882FcC387A86C8f887" +DAI_CONTRACT_ADDRESS = "0x48a6b4beAeB3a959Cd358e3365fc9d178eB0B2D9" ETH_CONTRACT_ADDRESS = "0x395A1065eA907Ab366807d68bbe21Df83169bA6c" +MATIC_CONTRACT_ADDRESS = "0x4A8c0C9f9f2444197cE78b672F0D98D1Fe47bdA6" +SHIB_CONTRACT_ADDRESS = "0xa11e679EE578B32d12Dbe2882FcC387A86C8f887" SOL_CONTRACT_ADDRESS = "0x30E198301969fDeddDCb84cE2C284dF58d4AB944" -USDT_CONTRACT_ADDRESS = "0x44734B834364c37d35e6E4253779e9459c93B5F4" UNI_CONTRACT_ADDRESS = "0xC73F7cBD464aC7163D03dE669dedc3d1fA6Af5E4" -MATIC_CONTRACT_ADDRESS = "0x4A8c0C9f9f2444197cE78b672F0D98D1Fe47bdA6" -DAI_CONTRACT_ADDRESS = "0x48a6b4beAeB3a959Cd358e3365fc9d178eB0B2D9" -SWAP_CONTRACT_ADDRESS = "0xC678d76a12Dd7f87FF1f952B6bEEd2c0fd308CF8" +USDT_CONTRACT_ADDRESS = "0x44734B834364c37d35e6E4253779e9459c93B5F4" SWAP_CONTRACT_NAME = "SwapContract" +AOA_CONTRACT_NAME = "AuroraToken" DAI_CONTRACT_NAME = "DaiToken" ETH_CONTRACT_NAME = "EthToken" -AOA_CONTRACT_NAME = "AuroraToken" +MATIC_CONTRACT_NAME = "PolygonToken" SHIB_CONTRACT_NAME = "ShibainuToken" SOL_CONTRACT_NAME = "SolanaToken" -USDT_CONTRACT_NAME = "TetherToken" UNI_CONTRACT_NAME = "UniswapToken" -MATIC_CONTRACT_NAME = "PolygonToken" +USDT_CONTRACT_NAME = "TetherToken" + +AURORA_TESTNET_INFURA_KEY = "Infura's aurora testnet http key" -INFURA_KEY_TEST = "Infura's aurora testnet http key" +WALLETCONNECT_PROJECT_ID = "WalletConnect's project id" ``` `XXX_CONTRACT_ADDRESS`にはsection-1のlesson-2で行ったdeployから返ってきたそれぞれのアドレスを入れます。 -`INFURA_KEY_TEST`には前半で作ったinfuraアカウントの、aurora testnet用のhttp keyを入れます。下のような形式になっているものです。 +`AURORA_TESTNET_INFURA_KEY`にはsection-0のlesson-2で行った環境構築時に作成したinfuraアカウントの、aurora testnet用のhttp keyを入れます。下のような形式になっているものです。 ``` INFURA_KEY_TEST = "https://aurora-testnet.infura.io/v3/4b5...." ``` +`WALLETCONNECT_PROJECT_ID`には同じくsection-0のlesson-2で作成したWalletConnectのproject idを入れます。 + ではいよいよ基礎となるmodelやウィジェットを作成していきたいところですが、その前にホーム画面などのそれぞれの画面を簡単に作っておきます。 最終的には画面遷移を想定して作るので、その遷移先がなければエラーが出てしまい動かないという事態が起きかねないので簡単な画面を作っておきます。 @@ -249,8 +257,6 @@ INFURA_KEY_TEST = "https://aurora-testnet.infura.io/v3/4b5...." ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); @@ -263,7 +269,7 @@ class _HomeState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Home Screen")), + appBar: AppBar(title: const Text("Home Screen")), ); } } @@ -274,8 +280,6 @@ class _HomeState extends State { ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; class QRCodeScan extends StatefulWidget { const QRCodeScan({Key? key}) : super(key: key); @@ -288,7 +292,7 @@ class _HomeState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("QRCodeScan Screen")), + appBar: AppBar(title: const Text("QRCodeScan Screen")), ); } } @@ -298,8 +302,6 @@ class _HomeState extends State { ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; class Send extends StatefulWidget { const Send({Key? key}) : super(key: key); @@ -312,7 +314,7 @@ class _HomeState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Send Screen")), + appBar: AppBar(title: const Text("Send Screen")), ); } } @@ -322,8 +324,6 @@ class _HomeState extends State { ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; class SignIn extends StatefulWidget { const SignIn({Key? key}) : super(key: key); @@ -336,7 +336,7 @@ class _HomeState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("SignIn Screen")), + appBar: AppBar(title: const Text("SignIn Screen")), ); } } @@ -346,8 +346,6 @@ class _HomeState extends State { ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; class Wallet extends StatefulWidget { const Wallet({Key? key}) : super(key: key); @@ -360,7 +358,7 @@ class _HomeState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Wallet Screen")), + appBar: AppBar(title: const Text("Wallet Screen")), ); } } @@ -375,16 +373,16 @@ class _HomeState extends State { ```dart import 'dart:convert'; import 'dart:core'; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:http/http.dart'; import 'package:url_launcher/url_launcher_string.dart'; -import 'package:web3_connect/web3_connect.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; +import 'package:web3dart/crypto.dart'; import 'package:web3dart/web3dart.dart'; -import 'package:web_socket_channel/io.dart'; -import 'dart:math'; -import 'package:http/http.dart' as http; class ContractModel extends ChangeNotifier { List tokenList = [ @@ -396,29 +394,35 @@ class ContractModel extends ChangeNotifier { imagePath: "assets/aurora-aoa-logo.png", ), Token( - address: dotenv.env["SHIB_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["SHIB_CONTRACT_NAME"]!, - name: "Shibainu", - symbol: "SHIB", - imagePath: "assets/shib-logo.png"), + address: dotenv.env["DAI_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["DAI_CONTRACT_NAME"]!, + name: "Dai", + symbol: "DAI", + imagePath: "assets/dai-dai-logo.png"), Token( address: dotenv.env["ETH_CONTRACT_ADDRESS"]!, contractName: dotenv.env["ETH_CONTRACT_NAME"]!, name: "Ethereum", symbol: "ETH", imagePath: "assets/ethereum-eth-logo.png"), + Token( + address: dotenv.env["MATIC_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["MATIC_CONTRACT_NAME"]!, + name: "Polygon", + symbol: "MATIC", + imagePath: "assets/polygon-matic-logo.png"), + Token( + address: dotenv.env["SHIB_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["SHIB_CONTRACT_NAME"]!, + name: "Shibainu", + symbol: "SHIB", + imagePath: "assets/shib-logo.png"), Token( address: dotenv.env["SOL_CONTRACT_ADDRESS"]!, contractName: dotenv.env["SOL_CONTRACT_NAME"]!, name: "Solana", symbol: "SOL", imagePath: "assets/solana-sol-logo.png"), - Token( - address: dotenv.env["USDT_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["USDT_CONTRACT_NAME"]!, - name: "Tether", - symbol: "USDT", - imagePath: "assets/tether-usdt-logo.png"), Token( address: dotenv.env["UNI_CONTRACT_ADDRESS"]!, contractName: dotenv.env["UNI_CONTRACT_NAME"]!, @@ -426,59 +430,39 @@ class ContractModel extends ChangeNotifier { symbol: "UNI", imagePath: "assets/uniswap-uni-logo.png"), Token( - address: dotenv.env["MATIC_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["MATIC_CONTRACT_NAME"]!, - name: "Polygon", - symbol: "MATIC", - imagePath: "assets/polygon-matic-logo.png"), + address: dotenv.env["USDT_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["USDT_CONTRACT_NAME"]!, + name: "Tether", + symbol: "USDT", + imagePath: "assets/tether-usdt-logo.png"), ]; final SWAP_CONTRACT_ADDRESS = dotenv.env["SWAP_CONTRACT_ADDRESS"]; final SWAP_CONTRACT_NAME = dotenv.env["SWAP_CONTRACT_NAME"]; + String? _deepLinkUrl; + String? _account; + Web3App? _wcClient; + SessionData? _sessionData; + late Web3Client auroraClient; int ethBalance = 0; - bool _isLoading = true; - final String _rpcUrl = "https://testnet.aurora.dev"; - final String _wsUrl = "wss://testnet.aurora.dev"; - final String _deepLink = - "wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=91303dedf64285cbbaf9120f6e9d160a5c8aa3deb67017a3874cd272323f48ae"; - - Web3Client? _client; - String? _abiCode; - - Credentials? _credentials; - EthereumAddress? _contractAddress; - EthereumAddress? _ownAddress; DeployedContract? _contract; - ContractFunction? _transfer; - ContractFunction? _balanceOf; - - Web3Connect? _connection; - var account; - ContractModel() { init(); } Future init() async { - final INFURA_KEY_TEST = dotenv.env["INFURA_KEY_TEST"]; - http.Client httpClient = http.Client(); - auroraClient = Web3Client(INFURA_KEY_TEST!, httpClient); - _client = Web3Client(_rpcUrl, Client(), socketConnector: () { - return IOWebSocketChannel.connect(_wsUrl).cast(); - }); + var httpClient = Client(); + + auroraClient = + Web3Client(dotenv.env["AURORA_TESTNET_INFURA_KEY"]!, httpClient); } - Future getAbi(String contractName) async { - String abiStringFile = - await rootBundle.loadString("smartcontracts/" + contractName + ".json"); - var jsonAbi = jsonDecode(abiStringFile); - _abiCode = jsonEncode(jsonAbi["abi"]); - _contractAddress = - EthereumAddress.fromHex(jsonAbi["networks"]["1313161555"]["address"]); + getAccount() { + return _account; } Future getContract( @@ -492,7 +476,7 @@ class ContractModel extends ChangeNotifier { return contract; } -// This is called for read-only function of contract + // This is called for read-only function of contract Future> query(String contractName, String contractAddress, String functionName, List args) async { DeployedContract contract = @@ -506,40 +490,63 @@ class ContractModel extends ChangeNotifier { return result; } - Future sendTransaction(String contractName, String contractAddress, - String functionName, List args) async { - if (_connection != null && _client != null) { - final contract = await getContract(contractName, contractAddress); - ContractFunction function = contract.function(functionName); - final transaction = Transaction.callContract( - contract: contract, - function: function, - from: EthereumAddress.fromHex(_connection!.account), - parameters: args, - ); - final tra = _client!.sendTransaction( - _connection!.credentials, transaction, - chainId: 1313161555); - if (!await launchUrlString(_deepLink)) { - throw "Could not launch $_deepLink"; - } - await tra; - notifyListeners(); - } else { - print("There is no connection to wallet or no client"); - } + Future generateTransaction( + String contractName, + String contractAddress, + String functionName, + List parameters) async { + _contract = await getContract(contractName, contractAddress); + + ContractFunction function = _contract!.function(functionName); + + // web3dartを使用して、トランザクションを作成します。 + final Transaction transaction = Transaction.callContract( + contract: _contract!, function: function, parameters: parameters); + + // walletconnect_flutter_v2を使用して、Ethereumトランザクションを作成します。 + final EthereumTransaction ethereumTransaction = EthereumTransaction( + from: _account!, + to: contractAddress, + value: '0x${transaction.value?.getInWei.toRadixString(16) ?? '0'}', + data: transaction.data != null ? bytesToHex(transaction.data!) : null, + ); + + return ethereumTransaction; } - Future setConnection(Web3Connect connection) async { - _connection = connection; - account = connection.account; + Future sendTransaction(EthereumTransaction transaction) async { + // Metamaskアプリケーションを起動します。 + await launchUrlString(_deepLinkUrl!, mode: LaunchMode.externalApplication); + + // 署名をリクエストします。 + final String signResponse = await _wcClient?.request( + topic: _sessionData?.topic ?? "", + chainId: 'eip155:1313161555', + request: SessionRequestParams( + method: 'eth_sendTransaction', + params: [transaction.toJson()], + ), + ); + + return signResponse; + } + + Future setConnection( + String deepLinkUrl, Web3App wcClient, SessionData sessionData) async { + _deepLinkUrl = deepLinkUrl; + _wcClient = wcClient; + _sessionData = sessionData; + // セッションを認証したアカウントを取得します。 + _account = NamespaceUtils.getAccount( + sessionData.namespaces.values.first.accounts.first); + notifyListeners(); } Future getBalance( String tokenContractName, String tokenAddress) async { List result = await query(tokenContractName, tokenAddress, - 'balanceOf', [EthereumAddress.fromHex(_connection!.account)]); + 'balanceOf', [EthereumAddress.fromHex(_account!)]); return result[0].toString(); } @@ -570,28 +577,64 @@ class ContractModel extends ChangeNotifier { Future sendToken(String sendTokenContractName, String sendTokenAddress, String receiveTokenAddress, String recipientAddress, int amount) async { - await sendTransaction( - sendTokenContractName, - sendTokenAddress, - "approve", - [ + try { + // トークンの送信を承認します。 + final EthereumTransaction ethereumTransactionOfApprove = + await generateTransaction( + sendTokenContractName, sendTokenAddress, "approve", [ EthereumAddress.fromHex(dotenv.env['SWAP_CONTRACT_ADDRESS']!), BigInt.from(amount), - ], - ); - await sendTransaction( - dotenv.env['SWAP_CONTRACT_NAME']!, - dotenv.env['SWAP_CONTRACT_ADDRESS']!, - "swap", - [ - // measureToken is got rid of - EthereumAddress.fromHex(sendTokenAddress), - EthereumAddress.fromHex(sendTokenAddress), - EthereumAddress.fromHex(receiveTokenAddress), - BigInt.from(amount), - EthereumAddress.fromHex(recipientAddress), - ], - ); + ]); + + final String signResponseOfApprove = + await sendTransaction(ethereumTransactionOfApprove); + debugPrint('=== signResponseOfApprove: $signResponseOfApprove'); + + final EthereumTransaction ethereumTransactionOfSwap = + await generateTransaction( + dotenv.env['SWAP_CONTRACT_NAME']!, + dotenv.env['SWAP_CONTRACT_ADDRESS']!, + "swap", + [ + // measureToken is got rid of + EthereumAddress.fromHex(sendTokenAddress), + EthereumAddress.fromHex(sendTokenAddress), + EthereumAddress.fromHex(receiveTokenAddress), + BigInt.from(amount), + EthereumAddress.fromHex(recipientAddress), + ], + ); + + final String signResponseOfSwap = + await sendTransaction(ethereumTransactionOfSwap); + debugPrint('=== signResponseOfSwap: $signResponseOfSwap'); + } catch (error) { + rethrow; + } finally { + notifyListeners(); + } + } + + Future distributeToken(String tokenAddress) async { + try { + final EthereumTransaction ethereumTransaction = await generateTransaction( + dotenv.env["SWAP_CONTRACT_NAME"]!, + dotenv.env["SWAP_CONTRACT_ADDRESS"]!, + "distributeToken", + [ + EthereumAddress.fromHex(tokenAddress), + BigInt.from(100), + EthereumAddress.fromHex(_account!), + ], + ); + + final String signResponse = await sendTransaction(ethereumTransaction); + debugPrint('=== signResponse: $signResponse'); + } catch (error) { + rethrow; + } finally { + notifyListeners(); + } } Future getTotalBalance() async { @@ -631,6 +674,28 @@ class Token { } } +// Ethereumトランザクションを作成するためのモデルクラス +class EthereumTransaction { + const EthereumTransaction({ + required this.from, + required this.to, + required this.value, + this.data, + }); + + final String from; + final String to; + final String value; + final String? data; + + Map toJson() => { + 'from': from, + 'to': to, + 'value': value, + 'data': data, + }; +} + ``` 最初の`import`の部分で必要なライブラリを宣言しています。 @@ -638,6 +703,8 @@ class Token { ```dart import 'dart:convert'; import 'dart:core'; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; @@ -646,7 +713,6 @@ import 'package:url_launcher/url_launcher_string.dart'; import 'package:web3_connect/web3_connect.dart'; import 'package:web3dart/web3dart.dart'; import 'package:web_socket_channel/io.dart'; -import 'dart:math'; import 'package:http/http.dart' as http; ``` @@ -665,29 +731,35 @@ class ContractModel extends ChangeNotifier { imagePath: "assets/aurora-aoa-logo.png", ), Token( - address: dotenv.env["SHIB_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["SHIB_CONTRACT_NAME"]!, - name: "Shibainu", - symbol: "SHIB", - imagePath: "assets/shib-logo.png"), + address: dotenv.env["DAI_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["DAI_CONTRACT_NAME"]!, + name: "Dai", + symbol: "DAI", + imagePath: "assets/dai-dai-logo.png"), Token( address: dotenv.env["ETH_CONTRACT_ADDRESS"]!, contractName: dotenv.env["ETH_CONTRACT_NAME"]!, name: "Ethereum", symbol: "ETH", imagePath: "assets/ethereum-eth-logo.png"), + Token( + address: dotenv.env["MATIC_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["MATIC_CONTRACT_NAME"]!, + name: "Polygon", + symbol: "MATIC", + imagePath: "assets/polygon-matic-logo.png"), + Token( + address: dotenv.env["SHIB_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["SHIB_CONTRACT_NAME"]!, + name: "Shibainu", + symbol: "SHIB", + imagePath: "assets/shib-logo.png"), Token( address: dotenv.env["SOL_CONTRACT_ADDRESS"]!, contractName: dotenv.env["SOL_CONTRACT_NAME"]!, name: "Solana", symbol: "SOL", imagePath: "assets/solana-sol-logo.png"), - Token( - address: dotenv.env["USDT_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["USDT_CONTRACT_NAME"]!, - name: "Tether", - symbol: "USDT", - imagePath: "assets/tether-usdt-logo.png"), Token( address: dotenv.env["UNI_CONTRACT_ADDRESS"]!, contractName: dotenv.env["UNI_CONTRACT_NAME"]!, @@ -695,38 +767,25 @@ class ContractModel extends ChangeNotifier { symbol: "UNI", imagePath: "assets/uniswap-uni-logo.png"), Token( - address: dotenv.env["MATIC_CONTRACT_ADDRESS"]!, - contractName: dotenv.env["MATIC_CONTRACT_NAME"]!, - name: "Polygon", - symbol: "MATIC", - imagePath: "assets/polygon-matic-logo.png"), + address: dotenv.env["USDT_CONTRACT_ADDRESS"]!, + contractName: dotenv.env["USDT_CONTRACT_NAME"]!, + name: "Tether", + symbol: "USDT", + imagePath: "assets/tether-usdt-logo.png"), ]; final SWAP_CONTRACT_ADDRESS = dotenv.env["SWAP_CONTRACT_ADDRESS"]; final SWAP_CONTRACT_NAME = dotenv.env["SWAP_CONTRACT_NAME"]; + String? _deepLinkUrl; + String? _account; + Web3App? _wcClient; + SessionData? _sessionData; + late Web3Client auroraClient; int ethBalance = 0; - bool _isLoading = true; - final String _rpcUrl = "https://testnet.aurora.dev"; - final String _wsUrl = "wss://testnet.aurora.dev"; - final String _deepLink = - "wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=91303dedf64285cbbaf9120f6e9d160a5c8aa3deb67017a3874cd272323f48ae"; - - Web3Client? _client; - String? _abiCode; - - Credentials? _credentials; - EthereumAddress? _contractAddress; - EthereumAddress? _ownAddress; DeployedContract? _contract; - - ContractFunction? _transfer; - ContractFunction? _balanceOf; - - Web3Connect? _connection; - var account; ``` 後半の部分ではコントラクトにあるメソッドを呼び出すためのメソッドが書かれています。 @@ -739,27 +798,16 @@ ContractModel() { } Future init() async { - final INFURA_KEY_TEST = dotenv.env["INFURA_KEY_TEST"]; - http.Client httpClient = http.Client(); - auroraClient = Web3Client(INFURA_KEY_TEST!, httpClient); - _client = Web3Client(_rpcUrl, Client(), socketConnector: () { - return IOWebSocketChannel.connect(_wsUrl).cast(); - }); + var httpClient = Client(); + + auroraClient = + Web3Client(dotenv.env["AURORA_TESTNET_INFURA_KEY"]!, httpClient); } ``` -そして`getAbi,getContract`メソッドはそれぞれのコントラクトの情報を取得して、メソッドを呼ぶためのものです。 +そして`getContract`メソッドはそれぞれのコントラクトの情報を取得して、メソッドを呼ぶためのものです。 ```dart -Future getAbi(String contractName) async { - String abiStringFile = - await rootBundle.loadString("smartcontracts/" + contractName + ".json"); - var jsonAbi = jsonDecode(abiStringFile); - _abiCode = jsonEncode(jsonAbi["abi"]); - _contractAddress = - EthereumAddress.fromHex(jsonAbi["networks"]["1313161555"]["address"]); -} - Future getContract( String contractName, String contractAddress) async { String abi = @@ -789,91 +837,119 @@ Future> query(String contractName, String contractAddress, } ``` -次の`sendTransaction`関数はブロックチェーン上の値を書き換えるような関数を呼び出すことができます。ただし、毎回walletの許可が必要となります。 +`generateTransaction`関数は、その次に定義されている`sendTransaction`関数で使用するトランザクションを作成します。`sendTransaction`関数はブロックチェーン上の値を書き換えるような関数を呼び出すことができます。ただし、毎回walletの許可が必要となります。 ```dart -Future sendTransaction(String contractName, String contractAddress, - String functionName, List args) async { - if (_connection != null && _client != null) { - final contract = await getContract(contractName, contractAddress); - ContractFunction function = contract.function(functionName); - final transaction = Transaction.callContract( - contract: contract, - function: function, - from: EthereumAddress.fromHex(_connection!.account), - parameters: args, - ); - final tra = _client!.sendTransaction( - _connection!.credentials, transaction, - chainId: 1313161555); - if (!await launchUrlString(_deepLink)) { - throw "Could not launch $_deepLink"; - } - await tra; - notifyListeners(); - } else { - print("There is no connection to wallet or no client"); - } - } +Future generateTransaction( + String contractName, + String contractAddress, + String functionName, + List parameters) async { + _contract = await getContract(contractName, contractAddress); + + ContractFunction function = _contract!.function(functionName); + + // web3dartを使用して、トランザクションを作成します。 + final Transaction transaction = Transaction.callContract( + contract: _contract!, function: function, parameters: parameters); + + // walletconnect_flutter_v2を使用して、Ethereumトランザクションを作成します。 + final EthereumTransaction ethereumTransaction = EthereumTransaction( + from: _account!, + to: contractAddress, + value: '0x${transaction.value?.getInWei.toRadixString(16) ?? '0'}', + data: transaction.data != null ? bytesToHex(transaction.data!) : null, + ); + + return ethereumTransaction; +} + +Future sendTransaction(EthereumTransaction transaction) async { + // Metamaskアプリケーションを起動します。 + await launchUrlString(_deepLinkUrl!, mode: LaunchMode.externalApplication); + + // 署名をリクエストします。 + final String signResponse = await _wcClient?.request( + topic: _sessionData?.topic ?? "", + chainId: 'eip155:1313161555', + request: SessionRequestParams( + method: 'eth_sendTransaction', + params: [transaction.toJson()], + ), + ); + + return signResponse; +} ``` `setConnection`関数はwalletと接続した情報をインスタンス化したこのmodelに保存するための関数です。 ```dart -Future setConnection(Web3Connect connection) async { - _connection = connection; - account = connection.account; - notifyListeners(); - } +Future setConnection( + String deepLinkUrl, Web3App wcClient, SessionData sessionData) async { + _deepLinkUrl = deepLinkUrl; + _wcClient = wcClient; + _sessionData = sessionData; + // セッションを認証したアカウントを取得します。 + _account = NamespaceUtils.getAccount( + sessionData.namespaces.values.first.accounts.first); + + notifyListeners(); +} ``` それ以降はコントラクトに存在する関数を呼び出すもので、指定のトークンの残高を参照したり、swapを行うなどsection-1で作成した関数を呼び出すことができます。 ```dart Future getBalance( - String tokenContractName, String tokenAddress) async { - List result = await query(tokenContractName, tokenAddress, - 'balanceOf', [EthereumAddress.fromHex(_connection!.account)]); - return result[0].toString(); + String tokenContractName, String tokenAddress) async { + List result = await query(tokenContractName, tokenAddress, + 'balanceOf', [EthereumAddress.fromHex(_account!)]); + return result[0].toString(); +} + +Future getEthBalance(String tokenContractName) async { + List result = await query( + SWAP_CONTRACT_NAME!, SWAP_CONTRACT_ADDRESS!, 'calculateValue', [ + EthereumAddress.fromHex(dotenv.env["ETH_CONTRACT_ADDRESS"]!), + EthereumAddress.fromHex(tokenContractName) + ]); + return result[0].toString(); +} + +Future getTokensInfo() async { + for (int i = 0; i < tokenList.length; i++) { + final balance = + await getBalance(tokenList[i].contractName, tokenList[i].address); + final ethValue = await getEthBalance(tokenList[i].address); + final ethBalance = + ((double.parse(ethValue) * double.parse(balance) / (pow(10, 18))) + .ceil()) + .toString(); + tokenList[i] + ..balance = balance + ..ethBalance = ethBalance; } + return true; +} - Future getEthBalance(String tokenContractName) async { - List result = await query( - SWAP_CONTRACT_NAME!, SWAP_CONTRACT_ADDRESS!, 'calculateValue', [ - EthereumAddress.fromHex(tokenContractName), - EthereumAddress.fromHex(dotenv.env["ETH_CONTRACT_ADDRESS"]!) +Future sendToken(String sendTokenContractName, String sendTokenAddress, + String receiveTokenAddress, String recipientAddress, int amount) async { + try { + // トークンの送信を承認します。 + final EthereumTransaction ethereumTransactionOfApprove = + await generateTransaction( + sendTokenContractName, sendTokenAddress, "approve", [ + EthereumAddress.fromHex(dotenv.env['SWAP_CONTRACT_ADDRESS']!), + BigInt.from(amount), ]); - return result[0].toString(); - } - Future getTokensInfo() async { - for (int i = 0; i < tokenList.length; i++) { - final balance = - await getBalance(tokenList[i].contractName, tokenList[i].address); - final ethValue = await getEthBalance(tokenList[i].address); - final ethBalance = - ((double.parse(ethValue) * double.parse(balance) / (pow(10, 18))) - .ceil()) - .toString(); - tokenList[i] - ..balance = balance - ..ethBalance = ethBalance; - } - return true; - } + final String signResponseOfApprove = + await sendTransaction(ethereumTransactionOfApprove); + debugPrint('=== signResponseOfApprove: $signResponseOfApprove'); - Future sendToken(String sendTokenContractName, String sendTokenAddress, - String receiveTokenAddress, String recipientAddress, int amount) async { - await sendTransaction( - sendTokenContractName, - sendTokenAddress, - "approve", - [ - EthereumAddress.fromHex(dotenv.env['SWAP_CONTRACT_ADDRESS']!), - BigInt.from(amount), - ], - ); - await sendTransaction( + final EthereumTransaction ethereumTransactionOfSwap = + await generateTransaction( dotenv.env['SWAP_CONTRACT_NAME']!, dotenv.env['SWAP_CONTRACT_ADDRESS']!, "swap", @@ -886,25 +962,56 @@ Future getBalance( EthereumAddress.fromHex(recipientAddress), ], ); + + final String signResponseOfSwap = + await sendTransaction(ethereumTransactionOfSwap); + debugPrint('=== signResponseOfSwap: $signResponseOfSwap'); + } catch (error) { + rethrow; + } finally { + notifyListeners(); } +} - Future getTotalBalance() async { - double total = 0; - for (int i = 0; i < tokenList.length; i++) { - var balance = - await getBalance(tokenList[i].contractName, tokenList[i].address); - var ethValue = await getEthBalance(tokenList[i].address); - var ethBalance = - ((double.parse(ethValue) * double.parse(balance) / (pow(10, 18))) - .ceil()) - .toString(); - total += double.parse(ethBalance); - } - return total; +Future distributeToken(String tokenAddress) async { + try { + final EthereumTransaction ethereumTransaction = await generateTransaction( + dotenv.env["SWAP_CONTRACT_NAME"]!, + dotenv.env["SWAP_CONTRACT_ADDRESS"]!, + "distributeToken", + [ + EthereumAddress.fromHex(tokenAddress), + BigInt.from(100), + EthereumAddress.fromHex(_account!), + ], + ); + + final String signResponse = await sendTransaction(ethereumTransaction); + debugPrint('=== signResponse: $signResponse'); + } catch (error) { + rethrow; + } finally { + notifyListeners(); } +} + +Future getTotalBalance() async { + double total = 0; + for (int i = 0; i < tokenList.length; i++) { + var balance = + await getBalance(tokenList[i].contractName, tokenList[i].address); + var ethValue = await getEthBalance(tokenList[i].address); + var ethBalance = + ((double.parse(ethValue) * double.parse(balance) / (pow(10, 18))) + .ceil()) + .toString(); + total += double.parse(ethBalance); + } + return total; +} ``` -一番下に記述している`Token`というクラスはトークンの情報を保存するためのclassになります。 +一番下にはclassを定義しています。`Token`というクラスはトークンの情報を保存するためのclassになります。`EthereumTransaction`というクラスは、walletconnect_flutter_v2を使用してEthereumトランザクションを作成するためのclassになります。 ```dart class Token { @@ -927,11 +1034,33 @@ class Token { this.ethBalance = "0"; } } + +// Ethereumトランザクションを作成するためのモデルクラス +class EthereumTransaction { + const EthereumTransaction({ + required this.from, + required this.to, + required this.value, + this.data, + }); + + final String from; + final String to; + final String value; + final String? data; + + Map toJson() => { + 'from': from, + 'to': to, + 'value': value, + 'data': data, + }; +}s ``` では次にhome画面に表示する、それぞれのコインの残高情報を示すリスト一つ一つの元となるUIを作成していきます。 -![](/public/images/NEAR-MulPay/section-2/2_1_19.png) +![](/public/images/NEAR-MulPay/section-2/2_1_22.png) `lib/view/widgets/coin.dart`に移動して下のコードを追加していきましょう。 @@ -1052,11 +1181,12 @@ SizedBox( ```dart import 'package:fluid_bottom_nav_bar/fluid_bottom_nav_bar.dart'; import 'package:flutter/material.dart'; -import 'package:client/view/screens/home.dart'; -import 'package:client/view/screens/send.dart'; -import 'package:client/view/screens/wallet.dart'; -import 'package:provider/provider.dart'; import 'package:hexcolor/hexcolor.dart'; +import 'package:provider/provider.dart'; + +import '/view/screens/home.dart'; +import '/view/screens/send.dart'; +import '/view/screens/wallet.dart'; class BottomNavigationBarProvider with ChangeNotifier { int _currentIndex = 0; @@ -1214,15 +1344,12 @@ class _BottomNavigationBarState extends State { 次にユーザーのウォレットアドレスをQRコード化して表示するためのウィジェットを実装していきましょう。 -`lib/view/widgets/qr_code.dart`に以下のコードを +`lib/view/widgets/qr_code.dart`に以下のコードを記述しましょう。 [`qr_code.dart`] ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:qr_flutter/qr_flutter.dart'; class QRCode extends StatefulWidget { const QRCode({Key? key, this.qrImage}) : super(key: key); diff --git "a/docs/NEAR-MulPay/ja/section-2/lesson-2_\343\202\265\343\202\244\343\203\263\343\202\244\343\203\263\343\200\201\343\203\233\343\203\274\343\203\240\347\224\273\351\235\242\343\201\256\344\275\234\346\210\220\343\202\222\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-2/lesson-2_\343\202\265\343\202\244\343\203\263\343\202\244\343\203\263\343\200\201\343\203\233\343\203\274\343\203\240\347\224\273\351\235\242\343\201\256\344\275\234\346\210\220\343\202\222\343\201\227\343\202\210\343\201\206.md" index 97fd12c09..2a1ae07dc 100644 --- "a/docs/NEAR-MulPay/ja/section-2/lesson-2_\343\202\265\343\202\244\343\203\263\343\202\244\343\203\263\343\200\201\343\203\233\343\203\274\343\203\240\347\224\273\351\235\242\343\201\256\344\275\234\346\210\220\343\202\222\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-2/lesson-2_\343\202\265\343\202\244\343\203\263\343\202\244\343\203\263\343\200\201\343\203\233\343\203\274\343\203\240\347\224\273\351\235\242\343\201\256\344\275\234\346\210\220\343\202\222\343\201\227\343\202\210\343\201\206.md" @@ -9,38 +9,84 @@ [`signin.dart`] ```dart +import 'dart:async'; + import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:hexcolor/hexcolor.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:http/http.dart'; -import 'package:mulpay_frontend/model/contract_model.dart'; -import 'package:mulpay_frontend/view/screens/home.dart'; -import 'package:mulpay_frontend/view/widgets/navbar.dart'; +import 'package:hexcolor/hexcolor.dart'; import 'package:provider/provider.dart'; -import 'package:web3_connect/web3_connect.dart'; -import 'package:web3dart/web3dart.dart'; -import 'package:web_socket_channel/io.dart'; import 'package:responsive_framework/responsive_framework.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart'; + +import '../../model/contract_model.dart'; +import '../widgets/navbar.dart'; class SignIn extends StatelessWidget { - SignIn({Key? key}) : super(key: key); - final connection = Web3Connect(); - final String _rpcUrl = "https://testnet.aurora.dev"; - final _client = - Web3Client("https://testnet.aurora.dev", Client(), socketConnector: () { - return IOWebSocketChannel.connect("wss://testnet.aurora.dev") - .cast(); - }); + const SignIn({Key? key}) : super(key: key); + + static Web3App? _walletConnect; + static String? _url; + static SessionData? _sessionData; + + String get deepLinkUrl => 'metamask://wc?uri=$_url'; + + Future _initWalletConnect() async { + _walletConnect = await Web3App.createInstance( + projectId: dotenv.env["WALLETCONNECT_PROJECT_ID"]!, + metadata: const PairingMetadata( + name: 'NEAR MulPay', + description: 'Mobile Payment dApp with Swap Feature', + url: 'https://walletconnect.com/', + icons: [ + 'https://walletconnect.com/walletconnect-logo.png', + ], + ), + ); + } + + Future connectWallet() async { + if (_walletConnect == null) { + await _initWalletConnect(); + } + + try { + // セッション(dAppとMetamask間の接続)を開始します。 + final ConnectResponse connectResponse = await _walletConnect!.connect( + requiredNamespaces: { + 'eip155': const RequiredNamespace( + chains: ['eip155:1313161555'], + methods: ['eth_signTransaction', 'eth_sendTransaction'], + events: ['chainChanged']), + }, + ); + final Uri? uri = connectResponse.uri; + if (uri == null) { + throw Exception('Invalid URI'); + } + final String encodedUri = Uri.encodeComponent('$uri'); + _url = encodedUri; + + // Metamaskを起動します。 + await launchUrlString(deepLinkUrl, mode: LaunchMode.externalApplication); + + // セッションが確立されるまで待機します。 + final Completer session = connectResponse.session; + _sessionData = await session.future; + } catch (e) { + rethrow; + } + } @override Widget build(BuildContext context) { final displayHeight = MediaQuery.of(context).size.height; final displayWidth = MediaQuery.of(context).size.width; - var provider = Provider.of(context); final isDeskTop = ResponsiveBreakpoints.of(context).largerThan(MOBILE); + var provider = Provider.of(context); + return Scaffold( body: SafeArea( child: SizedBox.expand( @@ -108,15 +154,14 @@ class SignIn extends StatelessWidget { width: isDeskTop ? displayWidth * 0.4 : displayWidth * 0.7, child: ElevatedButton( onPressed: () async { - connection.enterChainId(1313161555); - connection.enterRpcUrl(_rpcUrl); - await connection.connect(); - if (connection.account != "") { - await context - .read() - .setConnection(connection); + try { + await connectWallet(); + await context.read().setConnection( + deepLinkUrl, _walletConnect!, _sessionData!); provider.currentIndex = 0; Navigator.pushReplacementNamed(context, '/home'); + } catch (error) { + debugPrint('error $error'); } }, child: Text( @@ -138,8 +183,6 @@ class SignIn extends StatelessWidget { } ``` -エラーが出ていると思いますが、次の実装によって消えるので気にしないでください。 - では次に`main.dart`へ移動して下のように変更しましょう。 [`main.dart`] @@ -148,12 +191,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hexcolor/hexcolor.dart'; -import 'package:mulpay_frontend/model/contract_model.dart'; -import 'package:mulpay_frontend/view/screens/signin.dart'; -import 'package:mulpay_frontend/view/widgets/navbar.dart'; import 'package:provider/provider.dart'; import 'package:responsive_framework/responsive_framework.dart'; +import 'model/contract_model.dart'; +import 'view/screens/signin.dart'; +import 'view/widgets/navbar.dart'; + Future main() async { await dotenv.load(fileName: ".env"); runApp( @@ -359,7 +403,7 @@ android { これ以降も、UIを確認する際は同じ手順で行います。 ``` -yarn client start +yarn client flutter:run ``` エミュレータであれば下のような画面が表示されていれば成功です。 @@ -368,24 +412,34 @@ yarn client start デスクトップ版であれば下のような画面が表示されていれば成功です。 ![](/public/images/NEAR-MulPay/section-2/2_2_6.png) +**👀 Wallet接続時のトラブルシューティング** + +`Wallet Connect`ボタンを押した際、walletconnect_flutter_v2ライブラリやMetaMaskに関するエラーが発生していないにも関わらずMetaMaskの接続要求のポップアップが開かない場合は、以下の対処法を試してみてください。 + +1\. **Wallet Connectの再試行** + +MetaMaskがパスワード入力後に立ち上がるが、接続要求のポップアップが表示されない場合、Swapアプリケーションに戻りもう一度Wallet Connectボタンを押してください。 + +2\. **システムの再起動** + +上記のステップを試してもポップアップが表示されない場合、エミュレータの再起動、またはPCの再起動を試してください。 + 次にホーム画面を作成していきましょう。`lib/view/screens/home.dart`へ移動して以下のコードを追加していきましょう! [`home.dart`] ```dart import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hexcolor/hexcolor.dart'; -import 'package:mulpay_frontend/model/contract_model.dart'; -import 'package:mulpay_frontend/view/widgets/qr_code.dart'; -import 'package:mulpay_frontend/view/widgets/coin.dart'; import 'package:provider/provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:responsive_framework/responsive_framework.dart'; +import '/model/contract_model.dart'; +import '/view/widgets/coin.dart'; +import '/view/widgets/qr_code.dart'; + class Home extends StatelessWidget { const Home({Key? key}) : super(key: key); @@ -511,7 +565,7 @@ class Home extends StatelessWidget { SizedBox( width: displayWidth * 0.2, child: Text( - contractModel.account, + contractModel.getAccount(), style: TextStyle( color: Colors.grey, fontSize: isDeskTop ? 28 : 13, @@ -526,8 +580,8 @@ class Home extends StatelessWidget { await showDialog( context: context, builder: (_) => QRCode( - qrImage: QrImage( - data: contractModel.account, + qrImage: QrImageView( + data: contractModel.getAccount(), size: 200, )), ); diff --git "a/docs/NEAR-MulPay/ja/section-2/lesson-3_\351\200\201\351\207\221\347\224\273\351\235\242\343\201\250wallet\347\224\273\351\235\242\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" "b/docs/NEAR-MulPay/ja/section-2/lesson-3_\351\200\201\351\207\221\347\224\273\351\235\242\343\201\250wallet\347\224\273\351\235\242\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" index beac40ab6..df9af08e1 100644 --- "a/docs/NEAR-MulPay/ja/section-2/lesson-3_\351\200\201\351\207\221\347\224\273\351\235\242\343\201\250wallet\347\224\273\351\235\242\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/NEAR-MulPay/ja/section-2/lesson-3_\351\200\201\351\207\221\347\224\273\351\235\242\343\201\250wallet\347\224\273\351\235\242\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" @@ -10,8 +10,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; @@ -145,17 +143,15 @@ class _QRCodeScanState extends State { import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hexcolor/hexcolor.dart'; -import 'package:client/view/screens/qr_code_scan.dart'; import 'package:provider/provider.dart'; +import 'package:responsive_framework/responsive_framework.dart'; import '../../model/contract_model.dart'; +import '/view/screens/qr_code_scan.dart'; class Send extends StatefulWidget { const Send({Key? key}) : super(key: key); @@ -176,6 +172,7 @@ class _SendState extends State { final displayHeight = MediaQuery.of(context).size.height; final displayWidth = MediaQuery.of(context).size.width; var contractModel = Provider.of(context, listen: true); + final isDeskTop = ResponsiveBreakpoints.of(context).largerThan(MOBILE); return Scaffold( body: SafeArea( @@ -184,15 +181,19 @@ class _SendState extends State { child: Column( children: [ SizedBox( - height: displayHeight * 0.04, + height: + isDeskTop ? (displayHeight * 0.01) : (displayHeight * 0.04), ), SizedBox( - height: displayHeight * 0.04, + height: + isDeskTop ? (displayHeight * 0.06) : (displayHeight * 0.04), child: Row( children: [ Text( 'Send', - style: Theme.of(context).textTheme.headline1, + style: isDeskTop + ? const TextStyle(fontSize: 50) + : (Theme.of(context).textTheme.headlineSmall), ), ], ), @@ -217,7 +218,7 @@ class _SendState extends State { "①Input receiver's wallet address", style: GoogleFonts.roboto( fontWeight: FontWeight.bold, - fontSize: 17, + fontSize: isDeskTop ? 30 : 17, color: Colors.black, ), ), @@ -234,7 +235,7 @@ class _SendState extends State { decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(3), - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.grey, width: 1.0, ), @@ -278,7 +279,7 @@ class _SendState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - SizedBox(width: 18), + const SizedBox(width: 18), SizedBox( height: 22, width: 22, @@ -287,11 +288,11 @@ class _SendState extends State { color: Colors.grey, ), ), - const Text( + Text( ' scan QR code', style: TextStyle( color: Colors.grey, - fontSize: 12, + fontSize: isDeskTop ? 20 : 12, fontWeight: FontWeight.w600, ), ), @@ -310,13 +311,13 @@ class _SendState extends State { "②Select coin you want to transfer \n and input amount", style: GoogleFonts.roboto( fontWeight: FontWeight.bold, - fontSize: 17, + fontSize: isDeskTop ? 30 : 17, color: Colors.black, ), ), ], ), - SizedBox( + const SizedBox( height: 10, ), Row( @@ -338,18 +339,18 @@ class _SendState extends State { customButton: Container( child: Row( children: [ - SizedBox( + const SizedBox( width: 5, ), - Container( + SizedBox( width: displayWidth * 0.13, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( - height: 50, - width: 50, + height: isDeskTop ? 80 : 50, + width: isDeskTop ? 80 : 50, child: Image.asset( dropdownValueOfSecond .imagePath), @@ -357,7 +358,7 @@ class _SendState extends State { ], ), ), - SizedBox( + const SizedBox( width: 5, ), SizedBox( @@ -369,7 +370,7 @@ class _SendState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 10, ), Text( @@ -377,32 +378,34 @@ class _SendState extends State { style: TextStyle( fontWeight: FontWeight.bold, color: Colors.black, - fontSize: 14, + fontSize: + isDeskTop ? 28 : 14, ), ), - SizedBox( + const SizedBox( height: 5, ), Text( dropdownValueOfSecond.name, style: TextStyle( - fontWeight: - FontWeight.bold, - color: Colors.grey, - fontSize: 12), + fontWeight: FontWeight.bold, + color: Colors.grey, + fontSize: + isDeskTop ? 24 : 12, + ), ), - SizedBox( + const SizedBox( height: 20, ), ], ), ), - SizedBox( + const SizedBox( width: 10, ), SizedBox( - height: displayHeight * 0.12, - width: 20, + height: isDeskTop ? 40 : 20, + width: isDeskTop ? 40 : 20, child: SvgPicture.asset( "assets/triangle.svg", color: HexColor("#628A8A"), @@ -433,7 +436,7 @@ class _SendState extends State { child: Image.asset( value.imagePath), ), - SizedBox( + const SizedBox( width: 10, ), Text(value.symbol) @@ -460,14 +463,16 @@ class _SendState extends State { margin: EdgeInsets.symmetric( horizontal: 15, vertical: displayHeight * 0.026), - width: displayWidth * 0.12, + width: isDeskTop + ? (displayWidth * 0.24) + : (displayWidth * 0.12), color: Colors.white, child: TextFormField( decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(3), - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.grey, width: 1.0, ), @@ -488,7 +493,7 @@ class _SendState extends State { controller: amountController, ), ), - Container( + SizedBox( height: double.infinity, child: Column( mainAxisAlignment: @@ -497,7 +502,7 @@ class _SendState extends State { Text( dropdownValueOfSecond.symbol, style: TextStyle( - fontSize: 20, + fontSize: isDeskTop ? 32 : 20, fontWeight: FontWeight.bold, ), ), @@ -523,13 +528,13 @@ class _SendState extends State { "③Select coin recipient want", style: GoogleFonts.roboto( fontWeight: FontWeight.bold, - fontSize: 17, + fontSize: isDeskTop ? 30 : 17, color: Colors.black, ), ), ], ), - SizedBox( + const SizedBox( height: 20, ), Container( @@ -545,22 +550,21 @@ class _SendState extends State { child: DropdownButton2( buttonWidth: 20, buttonHeight: 20, - customButton: Container( - child: Row( + customButton: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( + const SizedBox( width: 5, ), - Container( + SizedBox( width: displayWidth * 0.13, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( - height: 50, - width: 50, + height: isDeskTop ? 80 : 50, + width: isDeskTop ? 80 : 50, child: Image.asset( dropdownValueOfThird .imagePath), @@ -568,7 +572,7 @@ class _SendState extends State { ], ), ), - SizedBox( + const SizedBox( width: 15, ), SizedBox( @@ -580,7 +584,7 @@ class _SendState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 10, ), Text( @@ -588,38 +592,39 @@ class _SendState extends State { style: TextStyle( fontWeight: FontWeight.bold, color: Colors.black, - fontSize: 14, + fontSize: isDeskTop ? 28 : 14, ), ), - SizedBox( + const SizedBox( height: 5, ), Text( dropdownValueOfThird.name, style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey, - fontSize: 12), + fontWeight: FontWeight.bold, + color: Colors.grey, + fontSize: isDeskTop ? 24 : 12, + ), ), - SizedBox( + const SizedBox( height: 20, ), ], ), ), - SizedBox( + const SizedBox( width: 70, ), SizedBox( - height: displayHeight * 0.12, - width: 20, + height: isDeskTop ? 40 : 20, + width: isDeskTop ? 40 : 20, child: SvgPicture.asset( "assets/triangle.svg", color: HexColor("#628A8A"), ), ), ], - )), + ), icon: Icon( Icons.arrow_drop_down, size: 30, @@ -641,7 +646,7 @@ class _SendState extends State { width: 30, child: Image.asset(value.imagePath), ), - SizedBox( + const SizedBox( width: 10, ), Text(value.symbol) @@ -658,22 +663,28 @@ class _SendState extends State { margin: EdgeInsets.only(bottom: displayHeight * 0.02), child: SizedBox( height: displayHeight * 0.1, - width: displayWidth * 0.7, + width: isDeskTop + ? (displayWidth * 0.9) + : (displayWidth * 0.7), child: ElevatedButton( onPressed: () async { - await contractModel.sendToken( - dropdownValueOfSecond.contractName, - dropdownValueOfSecond.address, - dropdownValueOfThird.address, - addressController.text, - int.parse(amountController.text), - ); - setState(() { - dropdownValueOfSecond = tokenList[2]; - }); - dropdownValueOfThird = tokenList[2]; - addressController.clear(); - amountController.clear(); + try { + await contractModel.sendToken( + dropdownValueOfSecond.contractName, + dropdownValueOfSecond.address, + dropdownValueOfThird.address, + addressController.text, + int.parse(amountController.text), + ); + setState(() { + dropdownValueOfSecond = tokenList[2]; + }); + dropdownValueOfThird = tokenList[2]; + addressController.clear(); + amountController.clear(); + } catch (error) { + debugPrint('sendToken: $error'); + } }, child: Text( 'Transfer', @@ -721,19 +732,15 @@ class _SendState extends State { ```dart import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/src/foundation/key.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_svg/svg.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hexcolor/hexcolor.dart'; -import 'package:client/model/contract_model.dart'; -import 'package:client/view/widgets/qr_code.dart'; -import 'package:client/view/widgets/navbar.dart'; import 'package:provider/provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import 'package:web3dart/credentials.dart'; +import 'package:responsive_framework/responsive_framework.dart'; + +import '/model/contract_model.dart'; +import '/view/widgets/qr_code.dart'; class Wallet extends StatefulWidget { const Wallet({Key? key}) : super(key: key); @@ -747,12 +754,13 @@ class _WalletState extends State { List tokenList = ContractModel().tokenList; TextEditingController addressController = TextEditingController(); final amountController = TextEditingController(); + @override Widget build(BuildContext context) { final displayHeight = MediaQuery.of(context).size.height; final displayWidth = MediaQuery.of(context).size.width; - var provider = Provider.of(context); var contractModel = Provider.of(context, listen: true); + final isDeskTop = ResponsiveBreakpoints.of(context).largerThan(MOBILE); return Scaffold( body: SafeArea( @@ -761,15 +769,19 @@ class _WalletState extends State { child: Column( children: [ SizedBox( - height: displayHeight * 0.04, + height: + isDeskTop ? (displayHeight * 0.01) : (displayHeight * 0.04), ), SizedBox( - height: displayHeight * 0.05, + height: + isDeskTop ? (displayHeight * 0.06) : (displayHeight * 0.04), child: Row( children: [ Text( 'Wallet', - style: Theme.of(context).textTheme.headline1, + style: isDeskTop + ? const TextStyle(fontSize: 50) + : (Theme.of(context).textTheme.headlineSmall), ), ], ), @@ -801,12 +813,14 @@ class _WalletState extends State { Text( "wallet address", style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), + fontWeight: FontWeight.bold, + fontSize: isDeskTop ? 27 : 17, + ), ), Container( - padding: EdgeInsets.symmetric(vertical: 1), + padding: const EdgeInsets.symmetric(vertical: 1), child: Text( - contractModel.account, + contractModel.getAccount(), overflow: TextOverflow.ellipsis, maxLines: 1, style: TextStyle( @@ -826,8 +840,8 @@ class _WalletState extends State { await showDialog( context: context, builder: (_) => QRCode( - qrImage: QrImage( - data: contractModel.account, + qrImage: QrImageView( + data: contractModel.getAccount(), size: 200, )), ); @@ -843,11 +857,11 @@ class _WalletState extends State { color: Colors.grey, ), ), - const Text( + Text( ' display QR code', style: TextStyle( color: Colors.grey, - fontSize: 12, + fontSize: isDeskTop ? 24 : 12, fontWeight: FontWeight.w600, ), ), @@ -874,13 +888,13 @@ class _WalletState extends State { "Select coin and push the right button", style: GoogleFonts.roboto( fontWeight: FontWeight.bold, - fontSize: 17, + fontSize: isDeskTop ? 30 : 17, color: Colors.black, ), ), ], ), - SizedBox( + const SizedBox( height: 10, ), Row( @@ -899,21 +913,20 @@ class _WalletState extends State { child: DropdownButton2( buttonWidth: 20, buttonHeight: 20, - customButton: Container( - child: Row( + customButton: Row( children: [ - SizedBox( + const SizedBox( width: 5, ), - Container( + SizedBox( width: displayWidth * 0.13, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( - height: 50, - width: 50, + height: isDeskTop ? 80 : 50, + width: isDeskTop ? 80 : 50, child: Image.asset( dropdownValue .imagePath), @@ -921,7 +934,7 @@ class _WalletState extends State { ], ), ), - SizedBox( + const SizedBox( width: 5, ), SizedBox( @@ -933,7 +946,7 @@ class _WalletState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 10, ), Text( @@ -942,39 +955,42 @@ class _WalletState extends State { fontWeight: FontWeight.bold, color: Colors.black, - fontSize: 14, + fontSize: + isDeskTop ? 28 : 14, ), ), - SizedBox( + const SizedBox( height: 5, ), Text( dropdownValue.name, style: TextStyle( - fontWeight: - FontWeight.bold, - color: Colors.grey, - fontSize: 12), + fontWeight: + FontWeight.bold, + color: Colors.grey, + fontSize: + isDeskTop ? 24 : 12, + ), ), - SizedBox( + const SizedBox( height: 20, ), ], ), ), - SizedBox( + const SizedBox( width: 10, ), SizedBox( - height: displayHeight * 0.12, - width: 20, + height: isDeskTop ? 40 : 20, + width: isDeskTop ? 40 : 20, child: SvgPicture.asset( "assets/triangle.svg", color: HexColor("#628A8A"), ), ), ], - )), + ), icon: Icon( Icons.arrow_drop_down, size: 30, @@ -998,7 +1014,7 @@ class _WalletState extends State { child: Image.asset( value.imagePath), ), - SizedBox( + const SizedBox( width: 10, ), Text(value.symbol) @@ -1012,33 +1028,24 @@ class _WalletState extends State { SizedBox( width: displayWidth * 0.05, ), - Container( - child: SizedBox( - height: displayHeight * 0.08, - width: displayWidth * 0.37, - child: ElevatedButton( - onPressed: () async { - await contractModel.sendTransaction( - dotenv.env["SWAP_CONTRACT_NAME"]!, - dotenv - .env["SWAP_CONTRACT_ADDRESS"]!, - "distributeToken", - [ - EthereumAddress.fromHex( - dropdownValue.address), - BigInt.from(100), - EthereumAddress.fromHex( - contractModel.account), - ], - ); - }, - child: Text( - 'Get 100 ${dropdownValue.symbol}!', - style: GoogleFonts.patuaOne( - fontWeight: FontWeight.w500, - fontSize: 18, - color: Colors.black, - ), + SizedBox( + height: displayHeight * 0.08, + width: displayWidth * 0.37, + child: ElevatedButton( + onPressed: () async { + try { + await contractModel.distributeToken( + dropdownValue.address); + } catch (error) { + debugPrint('distributeToken: $error'); + } + }, + child: Text( + 'Get 100 ${dropdownValue.symbol}!', + style: GoogleFonts.patuaOne( + fontWeight: FontWeight.w500, + fontSize: isDeskTop ? 34 : 18, + color: Colors.black, ), ), ), @@ -1047,22 +1054,22 @@ class _WalletState extends State { ), ], ), - Container( - child: SizedBox( - height: displayHeight * 0.1, - width: displayWidth * 0.7, - child: ElevatedButton( - onPressed: () { - Navigator.pushReplacementNamed( - context, '/signIn'); - }, - child: Text( - 'Disconnect', - style: GoogleFonts.patuaOne( - fontWeight: FontWeight.w500, - fontSize: 27, - color: Colors.black, - ), + SizedBox( + height: displayHeight * 0.1, + width: isDeskTop + ? (displayWidth * 0.9) + : (displayWidth * 0.7), + child: ElevatedButton( + onPressed: () { + Navigator.pushReplacementNamed( + context, '/signIn'); + }, + child: Text( + 'Disconnect', + style: GoogleFonts.patuaOne( + fontWeight: FontWeight.w500, + fontSize: 27, + color: Colors.black, ), ), ), @@ -1080,7 +1087,6 @@ class _WalletState extends State { ); } } - ``` これでwallet画面は完成したので早速エミュレータを起動して動かしてみましょう。 diff --git "a/docs/Polygon-ENS-Domain/ja/section-3/lesson-1_\350\263\207\351\207\221\343\201\256\345\274\225\343\201\215\345\207\272\343\201\227\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" "b/docs/Polygon-ENS-Domain/ja/section-3/lesson-1_\350\263\207\351\207\221\343\201\256\345\274\225\343\201\215\345\207\272\343\201\227\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" index 133e7fe3e..2f2c7c79d 100644 --- "a/docs/Polygon-ENS-Domain/ja/section-3/lesson-1_\350\263\207\351\207\221\343\201\256\345\274\225\343\201\215\345\207\272\343\201\227\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/Polygon-ENS-Domain/ja/section-3/lesson-1_\350\263\207\351\207\221\343\201\256\345\274\225\343\201\215\345\207\272\343\201\227\343\202\222\345\256\237\350\243\205\343\201\227\343\202\210\343\201\206.md" @@ -18,7 +18,7 @@ ```solidity modifier onlyOwner() { - require(isOwner()); + require(isOwner(), "You aren't the owner"); _; } diff --git "a/docs/Polygon-ENS-Domain/ja/section-3/lesson-2_\347\220\206\350\247\243\343\202\222\346\267\261\343\202\201\343\202\210\343\201\206.md" "b/docs/Polygon-ENS-Domain/ja/section-3/lesson-2_\347\220\206\350\247\243\343\202\222\346\267\261\343\202\201\343\202\210\343\201\206.md" index 976511114..43cc00a51 100644 --- "a/docs/Polygon-ENS-Domain/ja/section-3/lesson-2_\347\220\206\350\247\243\343\202\222\346\267\261\343\202\201\343\202\210\343\201\206.md" +++ "b/docs/Polygon-ENS-Domain/ja/section-3/lesson-2_\347\220\206\350\247\243\343\202\222\346\267\261\343\202\201\343\202\210\343\201\206.md" @@ -14,11 +14,9 @@ mapping(uint => string) public names; // コントラクトのどこかに付け加えてください。 function getAllNames() public view returns (string[] memory) { - console.log("Getting all names from contract"); string[] memory allNames = new string[](_tokenIds.current()); for (uint i = 0; i < _tokenIds.current(); i++) { allNames[i] = names[i]; - console.log("Name for token %d is %s", i, allNames[i]); } return allNames; @@ -169,18 +167,13 @@ describe('ENS-Domain', () => { deployTextFixture, ); - let txn; - const ownerBeforeBalance = await hre.ethers.provider.getBalance( owner.address, ); - // スーパーコーダーとしてコントラクトから資金を奪おうとします。 - try { - txn = await domainContract.connect(superCoder).withdraw(); - await txn.wait(); - } catch (error) { - console.log('robber could not withdraw token'); - } + + await expect( + domainContract.connect(superCoder).withdraw(), + ).to.be.revertedWith("You aren't the owner"); const ownerAfterBalance = await hre.ethers.provider.getBalance( owner.address, @@ -225,6 +218,41 @@ describe('ENS-Domain', () => { ``` +次に、`Domains`コントラクト内で定義していた`console.log`を削除しましょう。 + +import文を削除します。 + +```solidity +// === 下記を削除 === +import "hardhat/console.sol"; +``` + +constructor関数内の`console.log`を削除します。 + +```solidity + // === 下記を削除 === + console.log('%s name service deployed', _tld); +``` + +`register`関数内の`console.log`を削除します。 + +```solidity + // === 下記を削除 === + console.log( + 'Registering %s.%s on the contract with tokenID %d', + name, + tld, + newRecordId + ); +``` + +```solidity + // === 下記を削除 === + console.log('\n--------------------------------------------------------'); + console.log('Final tokenURI', finalTokenUri); + console.log('--------------------------------------------------------\n'); +``` + では下のコマンドを実行することでコントラクトのテストをしていきましょう! ``` @@ -234,16 +262,18 @@ yarn test 最後に下のような結果がでいれば成功です! ``` - ✔ Token amount contract has is correct! (4986ms) -robber could not withdraw token - ✔ someone not owenr cannot withdraw token (68ms) +Compiled 1 Solidity file successfully + + + ENS-Domain + ✔ Token amount contract has is correct! (1417ms) + ✔ someone not owenr cannot withdraw token ✔ contract owner can withdrawl token from conteract! - ✔ Domain value is depend on how long it is (38ms) + ✔ Domain value is depend on how long it is - 4 passing (5s) + 4 passing (1s) -✨ Done in 8.83s. ``` ### 🙋‍♂️ 質問する diff --git a/docs/Polygon-Whitelist-NFT/en/section-2/lesson-2_Mint Function.md b/docs/Polygon-Whitelist-NFT/en/section-2/lesson-2_Mint Function.md index 0d30ebe4a..33979e472 100644 --- a/docs/Polygon-Whitelist-NFT/en/section-2/lesson-2_Mint Function.md +++ b/docs/Polygon-Whitelist-NFT/en/section-2/lesson-2_Mint Function.md @@ -244,7 +244,7 @@ Let's focus on explaining the `mint` function: 5. `tokenIds += 1;` After all the aforementioned conditions are met, `tokenIds` will be incremented by 1. Remember, the default value of `tokenIds` is 0, so our `tokenIds` range becomes 1, 2, 3, 4. -6. `_safeMint(msg.sender, tokenIds);` This is the functionality implemented by `"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"`. You can explore the specific functionalities by opening that contract. For now, we only need to understand that this will result in minting an NFT to the caller of this function. +6. `_safeMint(msg.sender, tokenIds);` This is the functionality implemented by `"@openzeppelin/contracts/token/ERC721/ERC721.sol"`. You can explore the specific functionalities by opening that contract. For now, we only need to understand that this will result in minting an NFT to the caller of this function. ```solidity /** diff --git a/docs/Polygon-Whitelist-NFT/en/section-5/lesson-1_Monorepo configration.md b/docs/Polygon-Whitelist-NFT/en/section-5/lesson-1_Monorepo configration.md index 4e6b35c5a..c4766c0cf 100644 --- a/docs/Polygon-Whitelist-NFT/en/section-5/lesson-1_Monorepo configration.md +++ b/docs/Polygon-Whitelist-NFT/en/section-5/lesson-1_Monorepo configration.md @@ -140,8 +140,7 @@ Now let's update the files in the `packages/contract` folder. First, let's move the contracts folder in the root of the project into the `packages/contract` folder. The following command should be executed in the root of the project. ``` -rm -r ./packages/contract/contracts/ && mv ./cont -contracts/ ./packages/contract/ +rm -r ./packages/contract/contracts/ && mv ./contracts/ ./packages/contract/ ``` Next, update `hardhat.config.ts` as follows diff --git "a/docs/Polygon-Whitelist-NFT/ja/section-2/lesson-2_\343\203\237\343\203\263\343\203\210\346\251\237\350\203\275.md" "b/docs/Polygon-Whitelist-NFT/ja/section-2/lesson-2_\343\203\237\343\203\263\343\203\210\346\251\237\350\203\275.md" index 576604a8d..a7478122e 100644 --- "a/docs/Polygon-Whitelist-NFT/ja/section-2/lesson-2_\343\203\237\343\203\263\343\203\210\346\251\237\350\203\275.md" +++ "b/docs/Polygon-Whitelist-NFT/ja/section-2/lesson-2_\343\203\237\343\203\263\343\203\210\346\251\237\350\203\275.md" @@ -246,7 +246,7 @@ contract Shield is ERC721Enumerable, Ownable { 5. `tokenIds += 1;`:上記すべての条件が満たされた後で、`tokenIds`は1増加します。デフォルトの`tokenIds`値は0なので、`tokenIds`の範囲は1, 2, 3, 4となります。 -6. `_safeMint(msg.sender, tokenIds);`:この機能は`"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"`によって実装されています。そのコントラクトを参照することで具体的な機能を確認することができます。今のところ、この関数を呼び出した人にNFTがミントされるということだけ理解しておけば良いです。 +6. `_safeMint(msg.sender, tokenIds);`:この機能は`"@openzeppelin/contracts/token/ERC721/ERC721.sol"`によって実装されています。そのコントラクトを参照することで具体的な機能を確認することができます。今のところ、この関数を呼び出した人にNFTがミントされるということだけ理解しておけば良いです。 ```solidity /** diff --git "a/docs/Polygon-Whitelist-NFT/ja/section-5/lesson-1_\343\203\242\343\203\216\343\203\254\343\203\235\343\201\256\350\250\255\345\256\232.md" "b/docs/Polygon-Whitelist-NFT/ja/section-5/lesson-1_\343\203\242\343\203\216\343\203\254\343\203\235\343\201\256\350\250\255\345\256\232.md" index 3a2fe1ea6..b391f428d 100644 --- "a/docs/Polygon-Whitelist-NFT/ja/section-5/lesson-1_\343\203\242\343\203\216\343\203\254\343\203\235\343\201\256\350\250\255\345\256\232.md" +++ "b/docs/Polygon-Whitelist-NFT/ja/section-5/lesson-1_\343\203\242\343\203\216\343\203\254\343\203\235\343\201\256\350\250\255\345\256\232.md" @@ -140,8 +140,7 @@ npx hardhat まずは、プロジェクトのルートにあるcontractsフォルダを`packages/contract`フォルダ内に移動しましょう。下記のコマンドは、プロジェクトのルートで実行してください。 ``` -rm -r ./packages/contract/contracts/ && mv ./cont -contracts/ ./packages/contract/ +rm -r ./packages/contract/contracts/ && mv ./contracts/ ./packages/contract/ ``` 次に、`hardhat.config.ts`を下記のように更新しましょう。 diff --git "a/docs/XRPL-NFT-Maker/ja/section-3/lesson-2_web\343\202\242\343\203\227\343\203\252\343\202\261\343\203\274\343\202\267\343\203\247\343\203\263\343\201\213\343\202\211NFT\343\202\222mint\343\201\227\343\202\210\343\201\206.md" "b/docs/XRPL-NFT-Maker/ja/section-3/lesson-2_web\343\202\242\343\203\227\343\203\252\343\202\261\343\203\274\343\202\267\343\203\247\343\203\263\343\201\213\343\202\211NFT\343\202\222mint\343\201\227\343\202\210\343\201\206.md" index 09d841fa0..4e323d80a 100644 --- "a/docs/XRPL-NFT-Maker/ja/section-3/lesson-2_web\343\202\242\343\203\227\343\203\252\343\202\261\343\203\274\343\202\267\343\203\247\343\203\263\343\201\213\343\202\211NFT\343\202\222mint\343\201\227\343\202\210\343\201\206.md" +++ "b/docs/XRPL-NFT-Maker/ja/section-3/lesson-2_web\343\202\242\343\203\227\343\203\252\343\202\261\343\203\274\343\202\267\343\203\247\343\203\263\343\201\213\343\202\211NFT\343\202\222mint\343\201\227\343\202\210\343\201\206.md" @@ -384,7 +384,24 @@ mint関数の処理を1つずつ説明していきます。 > **✅ テストを実行してみよう** > -> ミント機能を実装したので、再度テスト結果を確認してみましょう。 +> ミント機能を実装する際に、新しいライブラリ「`xrpl-client`」と「`nft.storage`」をインポートしました。テストを実行するためにこれらのライブラリをモックする必要があります。`src/components/NftMinter`ディレクトリ内の`index.test.jsx`を更新しましょう。 +> +>9行目から17行目までのコードのコメントアウトを下記のように外してください。 +> +>```js +>// Section2 Lesson2 「📝 NFTのMint処理を実装しよう」が完了した後にコメントアウトを外してください。 +> jest.mock('nft.storage', () => { +> const { MockedNFTStorage } = require('../../__test__/__mock__/NFTStorage'); +> return { NFTStorage: MockedNFTStorage }; +> }); +> +> jest.mock('xrpl-client', () => { +> const { XrplClient } = require('../../__test__/__mock__/XrplClient'); +> return { XrplClient }; +> }); +>``` +> +> それでは改めてテストを実行してみましょう。 > > ![test3](/public/images/XRPL-NFT-Maker/section-3/3_2_7.png) > diff --git a/public/images/AVAX-Messenger/section-1/1_5_1.png b/public/images/AVAX-Messenger/section-1/1_5_1.png index bdda7c382..f9512b4cd 100644 Binary files a/public/images/AVAX-Messenger/section-1/1_5_1.png and b/public/images/AVAX-Messenger/section-1/1_5_1.png differ diff --git a/public/images/AVAX-Messenger/section-3/3_1_1.png b/public/images/AVAX-Messenger/section-3/3_1_1.png index e48a74e30..52d27b85a 100644 Binary files a/public/images/AVAX-Messenger/section-3/3_1_1.png and b/public/images/AVAX-Messenger/section-3/3_1_1.png differ diff --git a/public/images/AVAX-Messenger/section-3/3_1_2.png b/public/images/AVAX-Messenger/section-3/3_1_2.png index 89767a1a4..63daf01d6 100644 Binary files a/public/images/AVAX-Messenger/section-3/3_1_2.png and b/public/images/AVAX-Messenger/section-3/3_1_2.png differ diff --git a/public/images/NEAR-MulPay/section-0/0_2_5.png b/public/images/NEAR-MulPay/section-0/0_2_5.png new file mode 100644 index 000000000..e95849f24 Binary files /dev/null and b/public/images/NEAR-MulPay/section-0/0_2_5.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_10.png b/public/images/NEAR-MulPay/section-2/2_1_10.png index 8286abcad..01ffcf726 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_10.png and b/public/images/NEAR-MulPay/section-2/2_1_10.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_11.png b/public/images/NEAR-MulPay/section-2/2_1_11.png new file mode 100644 index 000000000..8286abcad Binary files /dev/null and b/public/images/NEAR-MulPay/section-2/2_1_11.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_11.jpg b/public/images/NEAR-MulPay/section-2/2_1_12.jpg similarity index 100% rename from public/images/NEAR-MulPay/section-2/2_1_11.jpg rename to public/images/NEAR-MulPay/section-2/2_1_12.jpg diff --git a/public/images/NEAR-MulPay/section-2/2_1_12.svg b/public/images/NEAR-MulPay/section-2/2_1_12.svg deleted file mode 100644 index 3a00536eb..000000000 --- a/public/images/NEAR-MulPay/section-2/2_1_12.svg +++ /dev/null @@ -1,5 +0,0 @@ - - pop-out-line - - - \ No newline at end of file diff --git a/public/images/NEAR-MulPay/section-2/2_1_13.svg b/public/images/NEAR-MulPay/section-2/2_1_13.svg index 2a397876f..3a00536eb 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_13.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_13.svg @@ -1,62 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + pop-out-line + + + \ No newline at end of file diff --git a/public/images/NEAR-MulPay/section-2/2_1_14.svg b/public/images/NEAR-MulPay/section-2/2_1_14.svg index 228f67678..2a397876f 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_14.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_14.svg @@ -1,17 +1,33 @@ - + + - - - - - - - - + viewBox="0 0 489.8 489.8" style="enable-background:new 0 0 489.8 489.8;" xml:space="preserve"> + + + diff --git a/public/images/NEAR-MulPay/section-2/2_1_15.svg b/public/images/NEAR-MulPay/section-2/2_1_15.svg index 86425d7e6..228f67678 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_15.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_15.svg @@ -1,19 +1,17 @@ - - + - - - - + viewBox="0 0 26.39 26.39" style="enable-background:new 0 0 26.39 26.39;" xml:space="preserve"> + + + + + + + + diff --git a/public/images/NEAR-MulPay/section-2/2_1_16.svg b/public/images/NEAR-MulPay/section-2/2_1_16.svg index 2491cdd54..86425d7e6 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_16.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_16.svg @@ -1,8 +1,20 @@ - + + - + width="990.9px" height="990.9px" viewBox="0 0 990.9 990.9" style="enable-background:new 0 0 990.9 990.9;" xml:space="preserve" + > + + + + + diff --git a/public/images/NEAR-MulPay/section-2/2_1_17.svg b/public/images/NEAR-MulPay/section-2/2_1_17.svg index 221df2a6d..2491cdd54 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_17.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_17.svg @@ -1 +1,36 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/NEAR-MulPay/section-2/2_1_18.svg b/public/images/NEAR-MulPay/section-2/2_1_18.svg index 2a725e9c8..221df2a6d 100644 --- a/public/images/NEAR-MulPay/section-2/2_1_18.svg +++ b/public/images/NEAR-MulPay/section-2/2_1_18.svg @@ -1,20 +1 @@ - - - - - - - - + \ No newline at end of file diff --git a/public/images/NEAR-MulPay/section-2/2_1_19.png b/public/images/NEAR-MulPay/section-2/2_1_19.png deleted file mode 100644 index 78c5d44e6..000000000 Binary files a/public/images/NEAR-MulPay/section-2/2_1_19.png and /dev/null differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_19.svg b/public/images/NEAR-MulPay/section-2/2_1_19.svg new file mode 100644 index 000000000..2a725e9c8 --- /dev/null +++ b/public/images/NEAR-MulPay/section-2/2_1_19.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/public/images/NEAR-MulPay/section-2/2_1_20.png b/public/images/NEAR-MulPay/section-2/2_1_20.png index 2765c50eb..efefac77e 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_20.png and b/public/images/NEAR-MulPay/section-2/2_1_20.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_21.png b/public/images/NEAR-MulPay/section-2/2_1_21.png new file mode 100644 index 000000000..544b6bd38 Binary files /dev/null and b/public/images/NEAR-MulPay/section-2/2_1_21.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_22.png b/public/images/NEAR-MulPay/section-2/2_1_22.png new file mode 100644 index 000000000..44de6c7fa Binary files /dev/null and b/public/images/NEAR-MulPay/section-2/2_1_22.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_4.png b/public/images/NEAR-MulPay/section-2/2_1_4.png index c8ac5ec42..698ffc48e 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_4.png and b/public/images/NEAR-MulPay/section-2/2_1_4.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_5.png b/public/images/NEAR-MulPay/section-2/2_1_5.png index 7f5493343..c8ac5ec42 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_5.png and b/public/images/NEAR-MulPay/section-2/2_1_5.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_6.png b/public/images/NEAR-MulPay/section-2/2_1_6.png index 3666e5238..7f5493343 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_6.png and b/public/images/NEAR-MulPay/section-2/2_1_6.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_7.png b/public/images/NEAR-MulPay/section-2/2_1_7.png index bef8cfd36..3666e5238 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_7.png and b/public/images/NEAR-MulPay/section-2/2_1_7.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_8.png b/public/images/NEAR-MulPay/section-2/2_1_8.png index 212b301b5..bef8cfd36 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_8.png and b/public/images/NEAR-MulPay/section-2/2_1_8.png differ diff --git a/public/images/NEAR-MulPay/section-2/2_1_9.png b/public/images/NEAR-MulPay/section-2/2_1_9.png index 01ffcf726..212b301b5 100644 Binary files a/public/images/NEAR-MulPay/section-2/2_1_9.png and b/public/images/NEAR-MulPay/section-2/2_1_9.png differ