こんにちは!
最近、Web3周りを研究開発しているTKと申します。
Web3を始めたばかりなので、至らない点が多いと思います。
アドバイス等をお待ちしております!
はじめに
DAppをネイティブアプリで作りたい!
DAppsの開発に関する文献が、Webアプリばかりでネイティブアプリで開発することに四苦八苦したので、記事として残します。
ReactNative + MetaMaskSDKでウォレットにコネクトし、デプロイ済みのコントラクトの関数を呼び出したいと思います。
要約
- MetaMaskSDKをインスタンス化する。
- eth_requestAccountsをリクエストして、MetaMaskとコネクトしアドレスを取得する。
- eth_callとeth_sendTransactionをリクエストして、デプロイ済みのスマートコントラクトの関数をコールする。
セットアップ
MetaMaskSDKを使う関係上Expoは使えないので、素のReactNativeで始めます。
ReactNativeの環境構築は今回言及しないので、適宜調べてください。
MetaMaskSDKを入れていきます。
MetaMaskSDKを入れるのに一手間必要ですが、ここを上から読んで実行するだけです。
$ yarn add --dev rn-nodeify
$ yarn add react-native-crypto react-native-randombytes crypto process
package.jsonのscripts内に下記を記載する。
"postinstall": "rn-nodeify --install 'crypto,process,stream,events' --hack"
yarnを実行すると、rn-nodeifyで、shim.jsができるので、エントリーポイントの一番上にshimファイルをインポートする。
$ yarn postinstall
import './shim'
MetaMaskアプリとの行き来でバックグラウンドに移行するので、react-native-background-timerも必要みたいです。
$ yarn add react-native-background-timer
$ cd ios && pod install && cd ..
MetaMaskSDKを入れる。
$ yarn add @metamask/sdk
$ cd ios && pod install && cd ..
意外と工程が多いですね。。。
MetaMaskがmetamask-react-native-sdkを開発中とのことなので、metamask-react-native-sdkに期待です!
ethers.jsを使うので、ついでにethers.jsも入れます。
$ yarn add ethers
実装
MetaMaskSDKを初期化します。
import MetaMaskSDK from '@metamask/sdk';
import { Linking } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
const MMSDK = new MetaMaskSDK({
openDeeplink: (link) => {
Linking.openURL(link);
},
timer: BackgroundTimer,
dappMetadata: {
name: '自分のアプリ名',
url: '自分のアプリのURL',
},
});
const ethereum = MMSDK.getProvider();
この後、MetaMaskSDKのReactNativeセッティングページや、よくあるWebアプリで作るチュートリアルでは、
const provider = new ethers.providers.Web3Provider(ethereum);
で、ethers.jsのproviderを初期化してブロックチェーンとやりとりします。
ただ、ethers.jsのproviderを使うと、自分は挙動が安定しなかったです。。。
↑原因がわかる方教えてください。
なので、MetaMaskのproviderを直接使います!
まず、MataMaskにコネクトして、アドレスを取得します。
const res = await ethereum.send("eth_requestAccounts", [])
const address = res?.result?.[0]
このコードを実行するとMetaMaskが起動し、接続するか?の確認がされます。
承認すると接続されます!
次に、トランザクションの発生しない関数を呼び出します。
const to = "デプロイしたスマートコントラクトのアドレス"
const inter = new ethers.utils.Interface(<ABI>)
// 今回は、balanceOfを呼び出す
// 呼び出す関数と引数を、エンコードしてdataに詰める
const data = inter.encodeFunctionData("balanceOf", [
ethereum.selectedAddress,
])
const transactionParameters = {
to,
from: ethereum.selectedAddress,
data,
}
// トランザクションの発生しない関数なので、eth_callをリクエストする
const res = await ethereum.request({
method: "eth_call",
params: [transactionParameters],
})
// レスポンスをデコードする
const resDecode = inter.decodeFunctionResult("balanceOf", rse)
// デコード後は、balanceOfがBigNumberの戻り値なので、BigNumberの配列になる
const balance = BigNumber.from(_balanceDecode[0]).toNumber()
これでデプロイしたスマートコントラクトのbalanceOfを呼び出して、結果を取得することができました!
次は、トランザクションを発行します。
const to = "デプロイしたスマートコントラクトのアドレス"
const inter = new ethers.utils.Interface(<ABI>)
// 今回は、mintを呼び出す
// 呼び出す関数と引数を、エンコードしてdataに詰める
const data = inter.encodeFunctionData("mint", [])
const transactionParameters = {
to,
from: ethereum.selectedAddress,
data,
}
// トランザクションの発生する関数なので、eth_sendTransactionをリクエストする
await ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
})
上記を実行すると、MetaMaskが支払い画面を表示しますので、承認するとトランザクションが発行されます。
お疲れ様でした!
まとめ
ここまで読んでいただきありがとうございました!
蓋を開けてみれば単純だったのですが、ここに至るまでにかなり躓きました。
DApp + ネイティブアプリの構成に詳しい人は、「もっとこうした方がいい」とか「こんなやり方ある」みたいなコメントください!
DAppはまだWebアプリが主流なような印象を受けます。
DApp + ネイティブアプリが、もっと一般的になってほしいと切実に思っています!