はじめに
先月devcon5に参加した中で、OpenZeppeliの発表のなかでGSNなどの発表があったので、戻ってきてから色々と試してみた覚書です。
ユーザーがDappsを使う際に、少量のGasを必要とすることが、DApssが普及するため障壁の1つであることは、すでに知られている通りです。これを解決するために、OpenZeppelinからGas Station Networkを用いて、これらを解決する一つの手段が提供されています。
(Introductionから引用)
https://forum.openzeppelin.com/t/advanced-gsn-gsnrecipientsignature-sol/1414
Using GSN allows developers to build (d)apps that don’t require end users to have ETH or even have their own wallet.
It does this by allowing for a GSN enabled smart contract to pay for its users transactions itself.
The question then of course is: how does a smart contract determine who is allowed to have their transactions subsidized?
If there is no criteria, it would be trivial for a user in bad faith to exploit a (d)app and obtain unlimited free transactions.
OpenZeppelin provides two sample strategy contracts which can be used to help (d)app developers decide who their recipient contacts are willing to pay for. ...
GSNを使うことで、開発者は"エンドユーザーがWalletにEthereumを必要としないDApps"を構築することができます。
これは、GSN対応のスマートコントラクトが、ユーザートランザクション自体に支払いを行うことを可能にすることで、実現しています。
「スマートコントラクトは、誰がGasの補助を受けることができるのかをどのように決めるのか?」という疑問は至極、当然です。
基準がない場合、悪意のあるユーザーがDAppsを悪用して無制限の無料トランザクションを取得するのは簡単なことです。
OpenZeppelinは、(d)アプリ開発者が受信者の連絡先の支払いを決定するのに役立つ2つのサンプル戦略契約を提供します。
(参考)
https://forum.openzeppelin.com/t/using-gas-station-network-starter-kit-on-a-public-testnet/1429
1–800-Ethereum: Gas Stations Network for Toll Free Transactions
https://medium.com/tabookey/1-800-ethereum-gas-stations-network-for-toll-free-transactions-4bbfc03a0a56
実行サンプル

EIP-1613
GSNの基本的なデザインはTabooKeyによって考案され、EIP-1613に提案されています。
使ってみる
1.Development環境で使ってみる
$ npm install -g ganache-cli
$ ganache-cli --deterministic
//プロジェクトのディレクトリを作成する
$ mkdir myproject
$ cd myproject
//GasStationNetworkのkitを展開する
$ npx @openzeppelin/cli@next unpack gsn
$ npx openzeppelin init
//clientアプリケーションを展開する
$ cd client
$ npm run start
//作成する
$ npx openzeppelin create
Nothing to compile, all contracts are up to date.
//質問:コントラクトの名前 (Counterを選択)
? Pick a contract to instantiate Counter
//質問:使用するネットワーク(development を選択)
? Pick a network development
All contracts are up to date
? Do you want to call a function on the instance after creating it? Yes
? Select which function * initialize(num: uint256)
2.Testnet環境で使ってみる
App.jsを書き換える
元は、127.0.0.1:8545に接続されていたものをinfuraに接続するコードを有効にして、127.0.0.1の方のcontextをコメントアウトする。下記のような感じでok。
// get GSN web3
const context = useWeb3Network(`wss://rinkeby.infura.io/ws/v3/${infuraToken}`, {
pollInterval: 15 * 1000,
gsn: {
signKey,
},
});
/*
const context = useWeb3Network('http://127.0.0.1:8545', {
gsn: {
dev: true,
signKey,
},
});
*/
MNEMONICSを生成して、.envに記述する
$ npx mnemonics
attack crane rabbit share proof truck glad next female win regular waste
.envに下記を設定する
$ touch .env
MNEMONIC="attack crane rabbit share proof truck glad next female win regular waste"
アカウントを取得する
$ npx openzeppelin accounts
? Pick a network (Use arrow keys)
❯ rinkeby
Accounts for rinkeby:
Default: 0x6303b56db2d46fb34EE996E8f3Db58793e7d97dE
All:
- 0: 0x6303b56db2d46fb34EE996E8f3Db58793e7d97dE
このアドレス(Accounts for rinkeby:)にいくらかEthereumを送っておく
Contractをdeployする
$ npx openzeppelin create
? Pick a contract to instantiate Counter
? Pick a network rinkeby
? Do you want to call a function on the instance after creating it? Yes
? Select which function * initialize()
(下記のように怒られたら、GASが足りてない)
✖ Creating instance for contract at 0x706Ffbc6470cBeB7be153750a119140D5E1f9970 and calling 'initialize' with no arguments
insufficient funds for gas * price + value
インスタンスが生成される
✓ Instance created at 0x0385451CfBd7DaDf9506d018E57d2fD4b9CA8604
0x0385451CfBd7DaDf9506d018E57d2fD4b9CA8604
下記リンクから、作成されたインスタンスのアドレスを入力する
Walletと接続するを選択する

デポジットを送金しておく

CLientを起動
cd client
npm run start
左側がClientで、0Ethereum。右側が作成したCounterContractで、先ほど送った0.5Ethがある状態。IncreaseCounterを押下すると...

CounterValueが1になって、IncreaseCounterのトランザクションが成功。

remixから実行
RemixやWalletを使うと、Gas=0のトランザクションを作ることはできないため、GSN経由にならずに普通にGasを支払って、コントラクトを実行するような流れになります。

1.環境設定
Metamask->Rinkeby
Rimix->Run->Environment:injectedWeb3 Account:Metamaskのaccount
2.Console
cd workspaces/
//実行したいCounter.solの位置を確認
less contracts/Counter.sol
//remixdを実行
remixd -s ./ --remix-ide https://remix.ethereum.org
3.Remixでlocalhostに接続
->Compile->localhost/contracts/Counter.sol
4.load
Remix->Run->Counter->AtAddress(上記でdeployしたアドレス)
5.試す
getCounter() -> 1
Reactから実行
おしまい
そういえば、OpenZeppelinって、自分の中では、なんとなくSmartContractの雛形みたいなイメージだったのですが、先日devcon5 OsakaのOpenZeppelinのセッションを聞いて、かなりSDK寄りになったというか、開発者が欲しいサービスがどんどんアドオンされているイメージを受けました。GSN以外にも、使いやすそうなものが展開されているようなので、リサーチしてみたいなと思っています。