NFT」カテゴリーアーカイブ

Invalid number of parameters for “mint”. Got 1 expected 2

ジェネラティブNFTの画像生成、スマートコントラクト、Mintサイト作成をする上で、HashLipsが提供しているコードは最初の題材、テンプレートとしてよく出てくる。

HashLips
https://github.com/HashLips

エンジニアのむなかたさんが提供している、「誰でもできる!ジェネラティブNFT開発」を見ながら進めていたのですが、Mintサイトでmint実行時に表題のエラーが出てしまいました

誰でもできる!ジェネラティブNFT開発
https://crypto-code.jp/materials/create-generative-nft

エラー詳細

Error: Invalid number of parameters for "mint". Got 1 expected 2!
▼ 2 stack frames were expanded.
InvalidNumberOfParams
c:/Dev/darejene/hashlips_nft_minting_dapp/node_modules/web3-core-helpers/lib/errors.js:33
_createTxObject
c:/Dev/darejene/hashlips_nft_minting_dapp/node_modules/web3-eth-contract/lib/index.js:669
▲ 2 stack frames were expanded.
claimNFTs
c:/Dev/darejene/hashlips_nft_minting_dapp/src/App.js:132
  129 | console.log("Gas limit: ", totalGasLimit);
  130 | setFeedback(`Minting your ${CONFIG.NFT_NAME}...`);
  131 | setClaimingNft(true);
> 132 | blockchain.smartContract.methods
      | ^  133 |   .mint(mintAmount)
  134 |   .send({
  135 |     gasLimit: String(totalGasLimit),
View compiled
onClick
c:/Dev/darejene/hashlips_nft_minting_dapp/src/App.js:360
  357 |   disabled={claimingNft ? 1 : 0}
  358 |   onClick={(e) => {
  359 |     e.preventDefault();
> 360 |     claimNFTs();
      | ^  361 |     getData();
  362 |   }}
  363 | >

この手のチュートリアルでエラーの原因はチュートリアル上の工程をすっ飛ばしてしまったり、誤字脱字によるタイポなわけです。

ただ、問題なのは、言われるがままにコードを修正する形になるので、そのあたりがつけづらいという点ですね。
特に今回のように、ゼロからコードを書くのではなく、テンプレートから作る場合はそもそも書いていないコードが多いので余計に原因にたどり着きづらいことが多いです。

質問しても良いのですが、それでは理解の助けにならないので自力で解決することを考えます。

mint関数の定義

コード内容としては、Reactで書かれたフロントエンドからスマートコントラクトの呼び出し時にエラー。
エラーメッセージからは「mint」関数の引数が、コード中では “mintAmount”一つに対して2つの引数を期待していると言われています。

というわけで確認するべきはmint関数の定義です。

mint関数の呼び出しとしては

blockchain.smartContract.methods.mint(mintAmount)

という呼び出しになっています。

スマートコントラクト上のコードとしては

// public
  function mint(uint256 _mintAmount) public payable {
    uint256 supply = totalSupply();
    require(!paused);
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require(supply + _mintAmount <= maxSupply);

    if (msg.sender != owner()) {
      require(msg.value >= cost * _mintAmount);
    }

    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(msg.sender, supply + i);
    }

と、引数を一つで定義されているので、呼び出しの仕方が問題というよりも、Mintサイトから見たスマートコントラクト上の定義では引数は2つと判断されている、ということなのでは。

という訳で、そのあたりを参照していけば良いはずですが。。。
少なくとも blockchain や smartContract をimport してはいないので、探します。
バックエンド主体で動いてきた私にはなかなか興味深いです

追っかける

blockchain 変数は App.js 内で useSelector によって値が入っています。

function App() {
  const dispatch = useDispatch();
  const blockchain = useSelector((state) => state.blockchain);

useSelectorに関してはこちらを参照

Reac初心者でも読めば必ずわかるReactのRedux講座
https://reffect.co.jp/react/react-redux-for-beginner#useSelector_Hooks

なるほど、さっぱりわからん。

わからんが、スマートコントラクトの定義っぽいところを探すと、bockchainActions.jsに

          const SmartContractObj = new Web3EthContract(
            abi,
            CONFIG.CONTRACT_ADDRESS
          );

という記述がある。

引数として渡しているabiは誰ジェネでも説明されている

ABIとは「Application Binary Interface」の略で、Webアプリからコントラクトへアクセスするために必要な設定となり、HashLipsのWebアプリではabi.jsonというファイルで管理しています

https://crypto-code.jp/chapters/create-mint-dapp/update-setting#index_0-3-0

ん。これが原因っぽい気がする

abi.jsonを見ると、HashLipsデフォルトの記述が書かれていて、更新し忘れていることがわかった。やはりこれが原因だった

という訳で、ここを正しく更新することで問題なくMintできるところまで行けた

最後に

ABIに関しては、公式に仕様が書かれていた

Contract ABI Specification
https://solidity-jp.readthedocs.io/ja/latest/abi-spec.html

とりあえず、なんとなく進め方はわかったけど、実際のところ各工程で行っている作業に対して正しく理解できている場所は少ない気がする。

数こなしていけば自然と覚えるものもあれば、調べて納得して覚えるものもある。

これなんだっけ?となる部分を少しでも減らしていければと思う。

NFT Startup Conference by #NMO へ参加

最近の興味関心事の一つとして、Web3やNFTに関することがあります。
私自身の現在のスタンスとして、どっぷり使っているわけではありませんが、エンジニアとしての技術的側面や、投資対象。単純にコミュニティ活動と、複数の切り口で面白い可能性があるかな、と考えています。

今回の投稿では、11/19に行われた NFT Marketing Orchestra というコミュニティが主催したカンファレンスのご紹介です

NFT Startup Conference

カンファレンス自体はオンラインで開催され、事前登録者はZoomのウェビナー。当日参加はTwitterスペースで聞くことができ、Twitterのスペースはアーカイブも残されていますので、興味がある方は是非。

NFT Startup Conference by #NMO
https://twitter.com/i/spaces/1zqKVPgnLkVJB

プログラムとしては以下の内容です

13:00 ~ 14:00 エンジニア対談 syou ✕ むなかた ✕ けいすけ
14:00 ~ 15:00 対談① わふくジェネ SOLOさん
15:00 ~ 16:00 対談② しきぶちゃん BUSONさん
16:00 ~ 17:00 対談③ CryptoNinja ikehayaさん
17:00     終了

私はというと、事前登録をしておいたのでZoomで参加しましたが、ランニングしながら聞いていたのでほとんど画面は見ていません。が、多分音声のみだったのではないかと。
セミナーじゃなくて、基本的に対談でしたしね。

それぞれの対談で思うこと、考えさせられることはありましたが、せっかくなのでエンジニア対談で思うところを。

エンジニア対談で思うこと

私自身、Solidityのコードを対価を頂く形で書いたことはなく、あくまでチュートリアルレベルなので、このお三方のような界隈で活躍されている人と比較はできないので、あくまで個人的な感想となります。

NFT。特にジェネラティブNFT関係のエンジニアリングでいうと、今回の対談の中でも出てきていますが、大きく3つの工程があります。

  • 画像生成
  • スマートコントラクト
  • Mintサイトの制作

CryptoZombieをやっているときはそれほど意識しませんでしたが、HashLipsを使うと上記3つが用意されているのでこの点はすごい認識・イメージはしやすかったです。

ただ、基本的にどれもベースとなるものは存在していて、結局のところどういう「画像を生成したいのか?」と、「どういう条件で販売したいのか」が決まれば結構なんとかなりそうな印象です。

あー、でもどこに画像を置くのかとか選択肢がいくつかあって、それらの選択肢の中から何を選んでもらうのか?とか考え始めると大変なのかな。

と言うかもっと言ってしまうと、大抵はエンジニアリングが大変というよりもコミュニケーションが大変なんでしょうね。

いずれにしても、作るものとしてのサイズ感は、凝ったことをしなければ、比較的ボイラープレートがある以上はそこまで大きくならない印象です。

チーム開発と個人開発

昨今のサービスやアプリケーションはどうしてもサイズが大きくなってしまうのでチーム開発が基本だと考えています。

チーム開発では、昔ながらのウォーターフォールで進めていったり、アジャイル的にスクラムを組んでみたりと色々な試行錯誤が繰り返されてきています。

できるエンジニアが一人で組んでしまったほうが、品質的にも安定するというのはあるのでしょうが、作るものの仕様の整理だったり業務知識。また、純粋な作業ボリュームと市場に投入したいスピード感から考えるとチーム開発になる認識です。

現在のNFTのように、比較的個人やコミュニティ手動で動いている場合は、そのWeb3的な考え方からコントラクトは共有ではなく独自が思想的にも好まれます。
そして、多くのギミックを入れられるほど資金が潤沢ではないので、ちょうど個人エンジニアで受けられるサイズ感に収まるのではないかと。

これから

ただし、多くのエンジニアがこれらの技術を習得し、ジェネレラクティブNFTの構築を行うことができたとしても、それを専業でやっていくほどのNFT発行プロジェクト量やエンジニアの需要を賄えるだけの金銭的な余裕も厳しいのでは。

そう考えると、今の私のように趣味で触っているのではなく、これだけでご飯を食べていこうとする道は結構厳しそう。

あるとすれば、自分自身がプロジェクトを立ち上げることによって、エンジニアと言うよりはファウンダーとして成り立たせるのが一番可能性としては高いかもしれない、と感じた。

実際のところ、NFTと言う単独な使い道以外にもスマートコントラクトの活用幅はこれから増えていく可能性がある。
NFTはブロックチェーンやSolidityという枠組みの中のあくまで一つの活用事例として考えて、他の分野も含めた形での目線を持っていたほうがいいかな、と考えている。

もちろん、NFTの活用方法が現在主力のPFPから大きく変わることも考えられ、その際にはNFT関連だけで充分な数のエンジニアを賄える分量が出てくるかもしれない。

いずれにしても、どうなっていくか、楽しみな分野ですね。

まー、そんなこと考えているよりも、手を動かせよって意見には頭が上がらないところではあるけれど。

Goerli テストネットワークでテストする準備

以前、cryptozombies でNFTやSolidityに関してのチュートリアル的な事をしたものの、それ以来全く触っていなかったNFT周りを改めて触ってみています。

今回は、その中で開発したコントラクトをテストするテストネットワークの一つ、Goerliテストネットを使ってみた話です。

テストネットワーク

今年の9月に実施された、 Ethereum 上の The Merge。
これ以降、それまで使えていたKiln、Ropsten、Rinkebyは廃止され、現状でテストネットワークとしてはGoerliとSepoliaが用意されている形となります。

The Merge以前の学習コンテンツやチュートリアルでは、Rinkebyなどのテストネットワークを採用されているものが多いので、注意が必要となります。

といっても、基本的な扱いが変わるわけではないので簡単に説明します。

Alchemyの登録

テストネットワークでコントラクトを動かすために必要となるGoerliETHを手に入れるために、事前にAlchemyというサービスのアカウントを作成しておきます。

Alchemy
https://www.alchemy.com/

「Get started for free」から、適当に入力をしていくことでアカウントを作成可能です。

途中で、支払い情報などを入力する項目が出てきますが、特に入力せずとも問題ありません。

faucetでGoerliETHをリクエストする

GoerliETHのリクエストは下記サイトにて行います

GOERLI FAUCET
https://goerlifaucet.com/

右上にある「Alchemy Login」から、先程作成したAlchemyアカウントでログインを行うと

アドレスの入力とボタンが活性化されるので、ウォレットのアドレスを入力して「Send Me ETH」ボタンを押下します。

押した瞬間、私の場合はこんな趣味の悪いページが出てきました。。。

ほんと、なんというか変な罠を踏んでしまったかと思いました。。左上にバツボタンがあるので閉じれば問題ありません。

ウォレットで確認

無事にウォレットにGoerliETHが届いていました。

ETHの送金は、24時間に1度可能な形で、送金金額も特に変更はできませんでした。
それくらいのボリュームでテストをしなさいということなんでしょうか?
フリープランだからかな?

ガス代高すぎ?

ちなみに、試しにデプロイしてみようとしたら。。。

まさかの資金不足。

なんか随分とガス代が高い気がする?
そもそもテストネットのガス代って何で決まるんだろうか?
メインネットと同様に、テストネットワークの混雑度によって決まるのだとすると、GoerliとSepoliaでどちらが空いているかとか、考えたほうがいいのかな?

盗難対策のためにウォレットを分けた

1年ほど前から仮想通貨を触り始めています。

BitCoinやEthを始め、あれこれ勉強も兼ねて触っているのですが、少し前からNFTを触っているのですが、ウォレットからNFTが盗まれるという事例があちこち。

気持ち的に、自分が罠にかかるというのは無いんじゃないか?と思って入るものの、そう思っている人間が一番引っかかるってこともありそうだな、と思い、ウォレットを分けることにした。

Brave

Metamskのウォレットを分けるには基本的にはChromeで別プロファイルを作って、新規のMetamaskウォレットを作成する形。

私のChromeにはすでにメイン用、ゲーム用(Dai用)、テスト用みたいなプロファイルが作ってある。
どーせだったらということで、ブラウザから分けてみることにした。
というわけでBraveですよ

Brave
https://brave.com/ja/

Braveはセキュリティ。特に個人情報保護に対しての尖った機能を要しているブラウザ。ベースとしてはChroniumなのでChromeと基本的には変わらなくMetamaskも対応している。

そして、広告のブロッキング機能がデフォルトで働いているため、多くの広告が表示されないというなかなか面白い体験を提供してくれる

ChromeでYahooを表示すると右側に広告が入ってくるのだが、

Braveで表示すると

見事に広告を消してくれる。

更に面白いのが、Braveでも広告は出ることがあるのだが、それはBraveに対しての広告であり、Webページに対しての広告ではないという点。
そして、Braveを使うと、その広告料が仮想通貨としてユーザの利用度に応じて支払われる点だ。

日本ではbitFlyerと連携して仮想通貨BATを獲得することが出来ます。
どれくらい使えば、どれくらい手に入るのかな?は気になるところですが、純粋に広告がブロックされるだけでも結構いいかも。

一方で、Chromeと比べると使いづらい点もいくつか見受けられます。
特にパスワードマネージャに関してはちょっといまいちで、URLで覚えているせいなのか、パラメータがついているとうまく反応してくれません。
このあたりはもう少し試行錯誤が必要になりそう

購入用ウォレットと保管用ウォレット

さて、そんなこんなで保管用ウォレットをBraveに追加したわけですが、実際のところ運用は少し悩みどころがあります。

基本運用

購入用ウォレットにETHを入れておき、Opensea等での売買はこちらで実施。
NFTなどを購入した際には保管用ウォレットに転送します。

売却時には保管用ウォレットから購入用ウォレットにNFTを転送する形で基本的には購入用ウォレットしかサイトへの接続はしないという形ですね

ユーティリティ

トレードという観点で考えるのであれば上記運用で事足りるのですが、例えば「このNFTを持っていれば~がもらえる」とか、「Discordの特定チャネルに入るにはこのNFTが必要」のようなユーティリティNFT。

上記のような場合には、NFTが入っている保管用ウォレットをサイトに接続する必要が生じます。
こちらに関しては、基本的に権限に注意数形を取れば。。。多分、大丈夫

ウォレットのアドレスをもとにWLが配られる場合

厄介なのが、特定のNFTを持っていることを条件にWLが配られたりするパターンですね。
この場合、そのウォレットで購入する必要が生じます。
ただ、そのNFTは保管用ウォレットに入っているわけなので、WLは保管用ウォレットにひも付き、保管用ウォレットで売買をする形になってしまいます。

こればっかりは正直気をつけるという、情けない対策手段しか無いんですよね。。。

いずれにしても、注意を払っておくに越したことはないので、みなさんもお気をつけくださいませ

Hardhatを使ってNFTを触ってみる

先日会社の飲み会で、デザイナーの人とたまたま同席になったのでNFTに関して軽く聞いてみた。
結論からすると、デザイナーって言ってもWebデザインやアプリのデザインばかりでいわゆる”絵”を書いていない。”絵”を書けないからWebデザインやってる的な話をされていた。

そうは言っても、私よりセンスあるだろうしやってみればいいじゃないって感じでNFTに関して軽く話をしたんだけど、エンジニアの人たちは難しいことをやっているんだな、って感じの印象しか与えることができなかった気がする。

といっても、NFTに関しては大雑把な話しか私も把握していないわけで、直接的に話の筋とは関係ないが、独自コントラクトを書いてみて理解を深められればと思った。

Hardhat・環境作成

数年前にBlockchain関連を触ったときには、Truffle と Ganache を使って開発を行った記憶があるが、今は Hardhat という開発環境を使う形でも出来るようなので、せっかくだから試してみることにする

Hardhat
https://hardhat.org/

早速、Hardhatをnpm経由でインストールする

PS C:\dev\base-nft> npm init --yes
PS C:\dev\base-nft> npm install --save-dev hardhat
PS C:\dev\base-nft> npm install @openzeppelin/contracts web3
PS C:\dev\base-nft> npx hardhat
You are using a version of Node.js that is not supported by Hardhat, and it may work incorrectly, or not work at all.

Please, make sure you are using a supported version of Node.js.

To learn more about which versions of Node.js are supported go to https://hardhat.org/nodejs-versions
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.9.7

? What do you want to do? ...
> Create a basic sample project
  Create an advanced sample project
  Create an advanced sample project that uses TypeScript
  Create an empty hardhat.config.js
  Quit

上記のような形となり、Hardhatで何をするのか選択肢が出てくるので、「Create a basic sample project」を選択する。

ちなみにnpm install で hardhat をすると警告が出てくる

npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'hardhat@2.9.7',
npm WARN EBADENGINE   required: { node: '^12.0.0 || ^14.0.0 || ^16.0.0' },
npm WARN EBADENGINE   current: { node: 'v18.1.0', npm: '8.8.0' }
npm WARN EBADENGINE }

node.js バージョン互換が取れていないということのようで、このまま突き進むとHardhat起動できないので16系に切り替えておく必要があった

ここでHardhatが問題なく起動するかを確認するためにtest実行してみる

PS C:\dev\base-nft>npx hardhat test
Error HH12: Trying to use a non-local installation of Hardhat, which is not supported.
Please install Hardhat locally using npm or Yarn, and try again.

エラーが出てきた。。。
Hardhatの公式を見てみるも特に解決せず、Stackoverflowでも同じ問題が出ていた

Can’t properly install hardhat using Powershell! Persistent Error HH12: Trying to use a non-local installation of Hardhat, which is not supported

あれこれと試してみて、結果としてはPowershellではなくコマンドプロンプトで実行したらうまくいった。謎い。

コントラクトの構築

コントラクトに関しては基本的にopenzeppelinえ用意されているものを利用する

pragma solidity ^0.8.0;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";

contract BaseNft is ERC721PresetMinterPauserAutoId {
    constructor() ERC721PresetMinterPauserAutoId("BaseNft", "nft", "https://raw.githubusercontent.com/krote/BaseNft/main/metadata/")
    {}
}

ERC721PresetMinterPauserAutoIdに渡している第3引数にMetadataの格納先を指定する必要があって、今回はGithubを指定してみた。

ローカルネットワークへのデプロイ

まずは、Hardhatを使用してローカルネットワークのノードを立ち上げる

C:\Dev\base-nft>npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
<<略>>

起動すると、ノードのアドレスとテスト利用可能なアカウントを20個ほど作ってくれる。これが後々利用するPublicKeyとPrivateKeyとなる

下記の様にdeploy.jsを用意

const hh = require("hardhat");

async function main() {
    const BaseNft = await hh.ethers.getContractFactory("BaseNft");
    const nft = await BaseNft.deploy();

    await nft.deployed();

    console.log("Deployed at: ", nft.address);
}

main()
    .then( () => process.exit(0))
    .catch( (error) => {
        console.error(error);
        process.exit(1);
    });

nodeを起動したコマンドプロンプトとは別のコマンドプロンプトにてdeploy.jsを実行する

C:\Dev\base-nft>npx hardhat run --network localhost scripts/deploy.js
Compiled 23 Solidity files successfully
Deployed at:  0x5FbDB2315678afecb367f032d93F642f64180aa3

無事にDeploy成功して、スマートコントラクトのアドレスを入手できた。
では続いてMintだ

Mint

Mint用にscripts/mint.jsを用意する

const Web3 = require("web3");

// CONTRACTをDeployした際のnodeアドレス
const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
// PUBLIC_KEY, PRIVATE_KEY は本来Metamaskなどを接続した際に取得可能なもの
// ここではHardhat起動時に作られたアカウントを固定で書く
const PUBLIC_KEY = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
// Deploy時に表示されたアドレスは127.0.0.1:8545だがlocalhostとする
const PROVIDER_URL = "http://localhost:8545";


async function mintNFT() {
    const web3 = new Web3(PROVIDER_URL);
    const contract = require("../artifacts/contracts/BaseNft.sol/BaseNft.json");
    const nftContract = new web3.eth.Contract(contract.abi, CONTRACT_ADDRESS);
    const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest");
    const tx = {
        from: PUBLIC_KEY,
        to: CONTRACT_ADDRESS,
        nonce: nonce,
        gas: 500000,
        data: nftContract.methods.mint(PUBLIC_KEY).encodeABI(),
    };

    const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
    signPromise
        .then((signedTx) => {
            const tx = signedTx.rawTransaction;
            if( tx !== undefined ){
                web3.eth.sendSignedTransaction(tx, function(err, hash) {
                    if( !err ) {
                        console.log("The hash of your transaction is:", hash);
                    } else {
                        console.log("Something went wrong when submittion your transaction:", err);
                    }
                });
            }
            console.log("row36");
        })
        .catch( (err) => {
            console.log("Promise failed:", err);
        });
}

mintNFT();

先に入手しているPublicKeyとPrivateKey。Deployしたアドレスをそれぞれ当てはめている。これを実行するとNFTを発行することが出来る

C:\Dev\base-nft>npx hardhat run --network localhost scripts/mint.js
The hash of your transaction is: 0xbdf81461a5416fe9c6903dba1d6780d576cf979b74ab87a43e1e7e0658d723fc      

これでMintされたことになるわけだけど、実際のところGithubの当該アドレスには特になにかおいているわけではないけど問題なくMintの処理自体は行われている。

そう考えると、指定したアドレスと言う文字列に対してBlockchain上のアドレスを割り当てているだけということで、色々と危うい仕組みなんだなぁということがよく分かる。

とりあえず、ここで使ったソースはGithubにおいた

https://github.com/krote/BaseNft