ethers.jsはEthereumプロジェクトのためのJavaScriptライブラリであり、v6とv5のバージョン間にはいくつかの重要な変更があります。ここでは、それらの主な変更点について解説します。説明およびサンプルコードは、公式ドキュメントから和訳・引用しています。詳細な情報はこちらを参照してください。
開発者が心に留めておく必要がある最大の変更です。
v5では、プロジェクトは大きなサブパッケージの集合のmonorepoとして管理されていました。v6では、すべての Import はルートパッケージで利用可能です。また、より細かい制御を行いたい人のために、pkg.exportsは特定のフォルダを直接利用可能にしています。
v5の Import
v5では、多くのもの(全てではない)がルートパッケージで利用可能でした。
import { ethers } from "ethers"
いくつかのパッケージは追加プロパティの後ろにグループ化されていますが、
import { providers } from "ethers" const { InfuraProvider } = providers
詳細なアクセス制御を行うには、サブパッケージからのインポートが必要でした。
import { InfuraProvider } from "@ethersproject/providers"
v6の Import
v6では、すべてがルートパッケージで利用可能です。
import { ethers } from "ethers" import { InfuraProvider } from "ethers"
pkg.exportsは、詳細なアクセス制御を提供します。
import { InfuraProvider } from "ethers/providers"
Provider
これも最大の変更です。Web3Provider(これまで link-web3 Provider をラップするために使用されていた)がBrowserProviderと呼ばれるようになりました。
EIP-1193 Providerのラッピング
v5
provider = new ethers.providers.Web3Provider(window.ethereum)
v6
provider = new ethers.BrowserProvider(window.ethereum)
トランザクションのブロードキャスト
また、トランザクションをネットワークにブロードキャストする方法が変更されました。
v5
provider.sendTransaction(signedTx)
v6
provider.broadcastTransaction(signedTx)
Contracts
v6では、コントラクトがES6 Proxyであり、実行時にメソッド名を解決できます。
Ambiguous Methods(曖昧なメソッド)
v5のコントラクト
v5では、曖昧なメソッドの場合、正規化されたシグネチャでメソッドを検索する必要がありました。また、重複した定義を実行時に検出する方法がなかったため、コンソールに警告が表示されていました。
abi = [ "function foo(address bar)"、 "function foo(uint160 bar)"、 ] contract = new Contract(address, abi, provider)
// 必要なメソッドにアクセスするために、 正規化された完全修飾シグネチャを指定する必要がある
contract["foo(address)"](addr)
// シグネチャが正規化されていないため、これらは失敗する
contract["foo(address )"](addr)
contract["foo(address addr)"](addr)
// こちらもメソッドが曖昧なので、失敗する
contract.foo(addr)
v6のコントラクト
v6ではシグネチャを正規化する必要はなく、Typed APIは目的のメソッドにアクセスするためのよりクリーンな方法を提供しています。
abi = [ function foo(address bar)", function foo(uint160 bar)", ] contract = new Contract(address, abi, provider)
// どれも成功する contract["foo(address)"](addr) contract["foo(address )"](addr) contract["foo(address addr)"](addr) // これはまだ失敗する // どのメソッドが意図されたものなのかを知る方法がないため contract.foo(addr) // しかし、型付きAPIなら成功する // Contractに型付け情報を提供することができるため contract.foo(Typed.address(addr))
その他のメソッド操作
v5では、コントラクトは一連のメソッド・バケットを含み、そのメソッド・バケットに、より一般的でない操作を実行するためのすべてのシグネチャと曖昧でない名前が付けられていました。v6では、各メソッドには、それ自身のあまり一般的でない操作が直接付加されています。
v5のその他の操作
// デフォルトのアクションは、メソッドに基づいて送信または呼び出しを選択する // type (pure, view, constant, non-payable or payable) contract.foo(addr) // これは、デフォルトのアクションを実行するが // Resultオブジェクトを返す contract.functions.foo(addr) // 強制的に呼び出す contract.callStatic.foo(addr) // ガスを見積もる contract.estimateGas.foo(addr) // トランザクションを生成する contract.populateTransaction.foo(addr)
v6のその他の操作
// 動作は同じ contract.foo(addr) // 呼び出しを実行し、結果オブジェクトを直接返す contract.foo.staticCallResult(addr) // 呼び出しを強制的に使用する (支払い可能、支払い不可能の場合でも) contract.foo.staticCall(addr) // トランザクションを強制的に送信する(pureとviewの場合でも同様) contract.foo.send(addr) // ガスを見積もる contract.foo.estimateGas(addr) // トランザクションを入力する contract.foo.populateTransaction(addr)
BigNumber
v6では、BigNumberクラスが、ES2020 BigIntに置き換わりました。ES2020 BigIntは、最近のJavaScript環境で提供されている組み込みです。BigNumber と同様、ES2020 BigInt は整数しか扱えないことに注意しましょう。
JavaScriptのES2020 BigIntを使い始めるためのオンラインドキュメントはたくさんあります
( 例:ES2020の新機能 – とほほのWWW入門 )。
また、FixedNumber クラスは、固定小数点演算を行うために存在します。
大きな数値の作成
v5でBigNumberを使う
value = BigNumber.from("1000")
v6でBigIntを使う(リテラル記法を使う)
// 接尾辞 n に注意 value = 1000n // 文字列に対するBigInt関数の使用 value = BigInt("1000")
大きな数値の簡単な計算
v5 で2つの値を加算する
sum = value1.add(value2)
v6 で BigInt を使用して2つの値を加算する
// 両方の値がBigIntでなければならない sum = value1 + value2
大きな数値の単純比較
v5 で等しいかどうかをチェックする
isEqual = value1.eq(value2)
v6 で BigInt を使用
isEqual = (value1 == value2)
Signature(シグネチャ)
Signatureは、すべてのパースとシリアライズを容易にするクラスとなりました。
シグネチャ操作
v5
splitSig = splitSignature(sigBytes) sigBytes = joinSignature(splitSig)
v6
splitSig = ethers.Signature.from(sigBytes) sigBytes = ethers.Signature.from(splitSig).serialized
Transaction(トランザクション)
v5に存在したトランザクションヘルパーは、すべて Transaction クラスに含まれます。さらに、処理するためにサポートされている、任意のトランザクション形式を扱うことができます。
トランザクションの解析
v5
tx = parseTransaction(txBytes) txBytes = serializeTransaction(tx) txBytes = serializeTransaction(tx, sig)
v6
tx = Transaction.from(txBytes)
v6( tx はオプションでシグネチャを含むことができる)
txBytes = Transaction.from(tx).serialized
Utilities(ユーティリティ)
Bytes32 string helpers
v5
bytes32 = ethers.utils.formatBytes32String(text) text = ethers.utils.parseBytes32String(bytes32)
v6
bytes32 = ethers.encodeBytes32String(text) text = ethers.decodeBytes32String(bytes32)
constants(定数)
v5
ethers.constants.AddressZero ethers.constants.HashZero
v6
ethers.ZeroAddress ethers.ZeroHash
データ操作
v5
slice = ethers.utils.hexDataSlice(value, start, end) padded = ethers.utils.hexZeroPad(value, length)
v5(数値を16進数に変換する)
hex = hexlify(35)
v6
slice = ethers.dataSlice(value, start, end) padded = ethers.zeroPadValue(value, length)
v6(数値を16進数に変換する)
hex = toBeHex(35)
defaultAbiCoder
v5では、AbiCoderのプロパティを使用していました。
coder = AbiCoder.defaultAbiCoder
v6 では、AbiCoder の静的関数であり、シングルトン・パターンを使用します。初めて呼び出されたときに AbiCoder が生成され、それ以降の呼び出しではその最初のインスタンスが返されます。
coder = AbiCoder.defaultAbiCoder()
コンテンツの取得
v5(body付属、特殊なものはなし)
data = await ethers.utils.fetchJson(url, json, processFunc)
v5で接続をオーバーライドする
req = { url, user: "username", password: "password" // これらなどのプロパティは FetchRequest と同等 }; data = await ethers.utils.fetchJson(req, json, processFunc)
v6
req = new ethers.FetchRequest(url) // bodyを設定(任意) req.body = json // 認証情報を設定(任意) req.setCredentials("username", "password") // processFunc を設定する(任意) req.processFunc = processFunc // リクエストを送信する resp = await req.send()
希望する形式によって、response bodyを取得します。
// Uint8Array data = resp.body // Utf8String(無効な場合に返す) data = resp.bodyText // Object(無効な場合に返す) data = resp.bodyJson
16進数変換
v5
hex = ethers.utils.hexValue(value) array = ethers.utils.arrayify(value)
v6
hex = ethers.toQuantity(value) array = ethers.getBytes(value)
solidity 非標準パック
v5
ethers.utils.solidityPack(types, values) ethers.utils.solidityKeccak256(types, values) ethers.utils.soliditySha256(types, values)
v6
ethers.solidityPacked(types, values) ethers.solidityPackedKeccak256(types, values) ethers.solidityPackedSha256(types, values)
プロパティ操作
v5
ethers.utils.defineReadOnly(obj, "name", value)
v6
ethers.defineProperties(obj, { name: value });
commify
v5
ethers.utils.commify("1234.5")
v6では、これらのローカル固有のユーティリティの一部を削除しています。しかしこの機能は簡単に再現でき、希望の出力形式に応じて調整できます。
function commify(value) { const match = value.match(/^(-?)([0-9]*)(\.?)([0-9]*)$/); if (!match || (!match[2] && !match[4])) { throw new Error(`bad formatted number: ${ JSON.stringify(value) }`); } const neg = match[1]; const whole = BigInt(match[2] || 0).toLocaleString("en-us"); const frac = match[4] ? match[4].match(/^(.*?)0*$/)[1]: "0"; return `${ neg }${ whole }.${ frac }`; } commify("1234.5");
削除されたクラスと関数
Loggerクラスは、いくつかのErrorユーティリティ関数に置き換えられました。
checkPropertiesと shallowCopy は削除され、.map と Object.assign が使われるようになりました。
参考にしたページ
https://docs.ethers.org/v6/migrating/