ステートレスなロールアップ INTMAX および INTMAX Wallet を調査・学習するためのリポジトリです。
node のバージョンは、18.18.0
以上であることが必要です!
-
リポジトリをクローンしてくる。
git clone https://github.com/mashharuki/IntmaxRepo
-
事前準備
-
Scroll Sepolia の faucet を取得すること
例えば以下のサイトで取得できます。
- Covalent faucet
- ETHGlobal faucet
- Scroll が紹介している faucet 用のサイト
Scroll Sepolia の faucet が取得できればどのサイトでも良いのでそこにアクセスして Faucet を取得すること!!
-
ScrollScan の API を取得すること
デプロイしたコントラクトを Verify するのに使うので以下サイトにアクセスして API キーを作成する。
ScrollScan API Key
-
OpenZepplin Defender にログインして ScrollSepolia 上で Relayer を作成し、API キーを取得すること。
OpenZeppelin Defender Relayer
-
上記で作成した Relayer のウォレットアドレスに少額の ETH (0.5 Sepolia ETH くらい)を送金する(Scroll Sepolia 上で送金してください!!)。
**OpenZeppelin Defender で作成した Relayer アドレス - ScrollScan**
各作成した Relayer のアドレスが表示されているはずなのでそのアドレスに入金すること
-
コントラクトのデプロイに使用するウォレットアドレスにも少額の ETH (0.5 Sepolia ETH くらい)を送金する(Scroll Sepolia 上で送金してください!!)。
-
環境変数の設定
環境変数は
backend
とfrontend
でそれぞれ設定する。-
backend 側の環境変数の設定
.env
ファイルをbackend
フォルダ配下に作成する。cp pkgs/backend/.env.example pkgs/backend/.env
そして以下の環境変数を設定する。
PRIVATE_KEY= SCROLLSCAN_API_KEY= DEFENDER_API_KEY= DEFENDER_SECRET_KEY=
PRIVATE_KEY
は Metamask からコピペしてくる。SCROLLSCAN_API_KEY
とDEFENDER_API_KEY
とDEFENDER_SECRET_KEY
は上記で取得してきたものを貼り付ける。 -
frontend 側の環境変数の設定
.env.local
ファイルをfrontend
フォルダ配下に作成する。cp pkgs/frontend/.env.local.example pkgs/frontend/.env.local
そして以下の環境変数を設定する。
NEXT_PUBLIC_APP_ICON="https://intmaxwallet-sdk-wallet.vercel.app/vite.svg" NEXT_PUBLIC_WALLET_URL="https://intmaxwallet-sdk-wallet.vercel.app/" NEXT_PUBLIC_RPC_URL="https://sepolia-rpc.scroll.io/" DEFENDER_API_KEY= DEFENDER_SECRET_KEY=
NEXT_PUBLIC_WALLET_URL の値は、https://preeminent-creponne-d7212b.netlify.app/でも良い。
DEFENDER_API_KEY
とDEFENDER_SECRET_KEY
は上記で取得してきたものを貼り付ける。
-
-
-
インストール
yarn
-
スマートコントラクトのコンパイル
yarn backend compile
-
スマートコントラクトのテスト
yarn backend test
-
スマートコントラクト デプロイ
yarn backend deploy --network scrollSepolia
デプロイされたコントラクトのアドレスは、
pkgs/backend/outputs/contracts-scrollSepolia.json
に記載されます。もしデプロイがうまくいかないという場合には以下のデプロイ済みコントラクトのアドレスを使ってみてください!
デプロイ済みコントラクト(ScrollSepolia)
-
フロントエンドの定数ファイルの値を更新する。
pkgs/frontend/src/utils/constants.ts
でコントラクトのアドレスを設定しているので、上記でデプロイしたコントラクトのアドレスに更新します。 ※ デプロイがうまく行かないようであれば、すでに貼り付けてある値をそのまま使用してください。export const FORWARDER_CONTRACT_ADDRESS = <デプロイしたアドレス>; export const HELLOWORLD_CONTRACT_ADDRESS = <デプロイしたアドレス>;
-
コントラクトを Verify する (オプション)
yarn backend verify --network scrollSepolia
-
ガスレスでサンプルコントラクトの機能を呼び出す (オプション)
yarn backend gaslessSetNewText --network scrollSepolia
/pkgs/backend/scripts/relay/gaslessSetScore.ts
ファイルの内容を実行します!!OZ Defender
の機能を使ってリレイヤーからトランザクション実行させてます!!/** * レイヤー用のSignerオブジェクトを作成するメソッド */ const getRelayer = async () => { const credentials: any = { apiKey: DEFENDER_API_KEY, apiSecret: DEFENDER_SECRET_KEY, }; const ozProvider = new DefenderRelayProvider(credentials); const ozSigner = new DefenderRelaySigner(credentials, ozProvider, { speed: "fast", }); return ozSigner; };
-
コントラクトに保存されている Text の値を取得する。
初期値は、
newText
になっているはずyarn backend getText --network scrollSepolia
-
フロントエンドをビルドする
yarn frontend build
-
フロントエンドを起動する
yarn frontend dev
http://localhost:3000にアクセスします!!
以下のような画面が立ち上がるので
Let's Login
ボタンを押す!初回時は Wallet がないので新規作成するかどうか聞かれる。
Create new wallet
を選択して新しくウォレットを作成する。ニーモニックコードが表示されるので忘れないようにメモかダウンロードを行う。
Close
ボタンを押して、サイトを接続する。ポップアップが表示されるので
Sign
ボタンを押す!うまく処理されれば INTMAX Wallet SDK のメソッドが呼び出されて Wallet が作成される!!
-
ガスレスでコントラクトに保存されている値を更新してみる。
うまく Wallet が作成できたらいよいよガスレストランザクションを実行!!
SendGaslessRequest
ボタンを押す!ポップアップが表示されるので
Sign
ボタンを押す!Success というポップアップが表示されたら OK!!
一応、コンソールの方にも API の処理のログが出力されているので以下のような内容が出力されている確認する。
トランザクションデータが出力されていれば OK!!
========================================= [RequestRaler: START] ============================================== request: { from: '0xbFc39B0230D743C8F7FAb716E622C1FD2894B148', to: '0xEbdef95c2f60D070bD5f10E9D69F55943169A108', value: 0n, gas: 360000n, nonce: 5n, deadline: 1717898498n, data: '0x2742d0f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000f68656c6c6f20494e544d41585821210000000000000000000000000000000000', signature: '0xd13fbf2b821d44c80dee699982183a47b34eb2b2fa29cb179478e41cc4f8dcce041bdd1e0106a1476d7843ea9f79418ee9e3db16273fa098bbfa305f84ba905e1c' } true tx hash: 0x95d24a93afeb154397b92f672fb500d6db52c80ee4ea9e4f13bcc67f66fe6c75 ========================================= [RequestRaler: END] ==============================================
こんな感じのログが出ていればガスレストランザクション発行できています!!
-
もう一回コントラクトに保存されている Text の値を取得する。
初期値は空文字だったが、
hello INTMAXX!!
という文言が取得できるはず!! ※ 更新されていない場合は時間をおいて再度実行してみてください。別タブを開いて以下を実行。
yarn backend getText --network scrollSepolia
INTMAX Wallet SDK に関する実装は全て pkgs/frontend/src/context/IntmaxProvider.tsx
にまとめてあります!!
このファイルには次の機能を実装しています。
- SDK 用のインスタンスを生成するメソッド
- connect するメソッド
- トランザクションを送信するメソッド
- ガスレスでトランザクションを送信するメソッド
順番に説明していきます
- の部分では
intmax-walletsdk/dapp
のethereumProvider
とintmaxDappClient
を使って実装しています!!
/**
* SDK用のインスタンスを生成するメソッド
* @param walletUrl
* @returns
*/
const createSdk = () => {
setLoading(true);
try {
const client = intmaxDappClient({
wallet: {
url: DEFAULT_WALLET_URL,
name: "DEMO Wallet",
window: { mode: "iframe" }, // modeは iframeかpopupを選択できる
},
metadata: DAPP_METADATA,
providers: {
eip155: ethereumProvider({
httpRpcUrls: {
534351: RPC_URL, // 今回はScroll Sepoliaに接続するように設定
},
}),
},
});
// SDK インスタンスをセット
setSdk(client);
return client;
} catch (err: any) {
console.error("err:", err);
} finally {
setLoading(false);
}
};
これで connect する準備ができました!!
- の部分については 1.で作成したインスタンスの機能を使って connect しています。
※ 今回は同時に
eth_sign
API も呼び出して署名も実施するようにしています!
const sdk = createSdk();
const ethereum = sdk!.provider(`eip155:${CHAIN_ID}`);
// ウォレット情報を取得する。
await ethereum.request({ method: "eth_requestAccounts", params: [] });
const accounts = (await ethereum.request({
method: "eth_accounts",
params: [],
})) as string[];
console.log("Account Info:", accounts);
setAccounts(accounts);
setAddress(accounts[0]);
// ログイン時に署名
const result = await ethereum.request({
method: "eth_sign",
params: [accounts[0], "Hello INTMAX WalletSDK Sample Dapp!!"],
});
console.log(result);
- についても同様に 1.で作成したインスタンスの機能を使ってトランザクションを送信することになります。
/**
* トランザクションを送信するメソッド
*/
const sendTx = async (to: string, value: string) => {
const ethereum = await sdk.provider(`eip155:${CHAIN_ID}`);
setLoading(true);
try {
// send Simple Transaction
const result = await ethereum.request({
method: "eth_sendTransaction",
params: [
{
from: address,
to: to,
value: parseEther(value),
},
],
});
console.log("tx info:", `https://sepolia.etherscan.io/tx/${result}`);
// .. 以下略
} catch (err: any) {
console.error("error:", err);
// .. 以下略
} finally {
// .. 以下略
}
};
4.についてもこれまでとほぼ同じ流れです。ここでは、メタトランザクションで使う署名データ生成のためにeth_signTypedData_v4
の API を呼び出しています。
残りの実装部分についてはメタトランザクションを実装する時のほぼ同じ流れです!!
/**
* ガスレスでコントラクトのメソッドを呼び出す
*/
const gasslessRequest = async () => {
console.log(
"================================= [gasless: START] ================================="
);
const ethereum = await sdk.provider(`eip155:${CHAIN_ID}`);
const provider = await new ethers.JsonRpcProvider(RPC_URL);
setLoading(true);
try {
// create forwarder contract instance
const forwarder: any = new Contract(
FORWARDER_CONTRACT_ADDRESS,
SampleForwarderJson.abi,
provider
) as any;
// create ScoreValut contract instance
const helloWorld: any = new Contract(
HELLOWORLD_CONTRACT_ADDRESS,
HelloWorldJson.abi,
provider
) as any;
// 呼び出すメソッドのエンコードデータを用意
// 今回は"hello INTMAXX!!"という文字列を引数にして HelloWorldコントラクトのsetNewTextメソッドを呼び出したいと思います!
const encodedData: any = helloWorld.interface.encodeFunctionData(
"setNewText",
["hello INTMAXX!!"]
);
// get domain
const domain = await forwarder.eip712Domain();
// get unit48
const uint48Time = getUint48();
console.log("encodedData:", encodedData);
console.log("domain:", domain);
console.log("uint48Time:", uint48Time);
// test sign messages
const typedData = {
domain: {
name: domain[1],
version: domain[2],
chainId: CHAIN_ID, // scroll sepolia
verifyingContract: domain[4].toString(),
},
types: {
ForwardRequest: ForwardRequest,
},
primaryType: "ForwardRequest",
message: {
from: address.toString(),
to: HELLOWORLD_CONTRACT_ADDRESS.toString(),
value: 0,
gas: 360000,
nonce: (await forwarder.nonces(address)).toString(),
deadline: uint48Time.toString(),
data: encodedData.toString(),
},
};
// create request data
// eth_signTypedData_v4 のAPIを使って署名データを作成
const sig = await ethereum.request({
method: "eth_signTypedData_v4",
params: [address, JSON.stringify(typedData)],
});
console.log("sig:", sig);
// call requestRelayer API
const gaslessResult = await fetch("/api/requestRelayer", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
from: address,
to: HELLOWORLD_CONTRACT_ADDRESS,
value: 0,
gas: 360000,
nonce: (await forwarder.nonces(address!)).toString(),
deadline: uint48Time.toString(),
data: encodedData,
signature: sig,
}),
});
console.log(await gaslessResult.json());
// .. 以下略
} catch (err: any) {
// .. 以下略
} finally {
// .. 以下略
}
};
以下参考にしたサイトや文献です!!
他の ZK ロールアップのことも学べそうな参考リンクも貼り付けています。
- Scaling Ethereum 2023
- GitHub - webmax.js Public
- Intmax Wallet
- IntMax の公式サイト
- GetStarted
- Scroll bridge
- CLI のガイドライン
- intmax rolluo cli
- hardhat-Plugin
- Sample-Auction-dapp
- PRTIMES - INTMAX Walletless Wallet
- INTMAX Wallet Home Page
- GitHub - intmax-walletsdk
- npm - INTMAX WalletSDK
- INTMAX WalletSDK サンプル実装
- INTMAX Wallet SDK - GitBook
- レイヤー 2「INTMAX」とは?真の金融インフラを開発する日置玲於奈氏の展望に迫る
- INTMAX、「Plasma Next」メイネット α をローンチ。Plasma の完成により拡張性向上
- 大衆向けイーサリアムのスケーリング: INTMAX が Plasma Next を発表
- INTMAX ホワイトペーパー
- Plasma Next: Plasma without Online Requirements
- Youtube - INTMAX のステートレスなロールアップが他の ZK ロールアップと何が違うのが概要だけ解説してくれている動画
- 【完全保存版】zkEVM とは何か
- 初心者向け: #zkEVM とは?
- いま話題の「zkEVM」とは何か?~農業への応用を考察~
- イーサリアム開発者ドキュメント - プラズマチェーンとは何か?
- レイヤー 2 技術の Plasma は、提案から 2 年を経て実用段階に近づく= BlockChainJam 2019
- ヴィタリック、スケーリングソリューション「Plasma」の評価を再検討すべきと主張
- GitHub - Plasma ホワイトペーパー日本語訳
- Medium - Recursive Zero-Knowledge Proofs
- Intmax Wallet SDK のサンプル実装例を取り上げたブログ記事
- INTMAX Wallet SDK Sampple - GitHub - Sports-Voting-Demo
- PLASMACON - 公式サイト
- INTMAX、ラテンアメリカの著名な Web3 ビルダーを発表。Plasma Free、革新的な EVM 互換プロトコルの開発を主導
- INTMAX DeveloperHub
- Plasma Free についての X の投稿
- Exit games for EVM validiums: the return of Plasma
- INTMAX Wallet ブラウザアプリ
- GitHub - IntMax privacy mining CLI
- GitHub - InternetMaximalism/intmax2-mining-cli
- intmax2-mining-cli - mainnet-quickstart.md