🐶

【初心者向け】ETHの残高を計算する際に気をつけること

2023/08/25に公開

この記事は株式会社Gincoのテックブログとして書いています。

本記事では、GoでEthereum(ETH)の残高を計算する際に初心者が間違えやすいint64のオーバーフロー問題について解説します。

整数範囲の指定とオーバーフローについて

整数を扱う際、型ごとに表現できる範囲があります。その範囲を超えた場合、予期せぬ振る舞いやエラーが発生することがあります。これをオーバーフローと言います。

Ethereumの最小単位「wei」が小さすぎる件

ETHの残高は「wei」という単位で表現されます。このweiは非常に小さな単位で、1ETHは10^18 weiに相当します。そのため、ETHの残高にはint64(最大値: 9223372036854775807(約9*10^19))で表現できないことがあります。

例えば、ユーザーが10ETH(=1^20wei)を保持している場合、この数値はint64の範囲を超えてしまうため、そのユーザーの残高を計算する際には注意が必要です。

実際にint64でETHを計算すると…

初心者が間違えやすいエラーの一つとして、int64を用いて直接ETHの残高を計算しようとするケースがあります。以下にそのコード例を示します。

func main() {
	var balance int64 = 1000000000000000000 // 1 ETH in wei
	var ethValue int64 = 9000000000000000000  // 9 ETH in wei

	balance += ethValue

	fmt.Println("Balance: ", balance)
}

出力結果

Balance: -8446744073709551616

このコードを実行すると、正の値が期待されるにもかかわらず、負の値が出力されます。

これは加算の結果がint64の範囲を超えて、オーバーフローが発生したためです。

math/bigパッケージを活用して落とし穴を回避

オーバーフローを防ぐためには、Goの標準ライブラリであるmath/big パッケージが利用できます。このパッケージは、任意の大きさの整数(big.Int)や浮動小数点数(big.Float)を扱うことが可能です。

以下に、上記のコードをmath/big パッケージを使用して書き直したコード例を示します。

func main() {
	balance := new(big.Int)
	balance.SetString("1000000000000000000", 10) // 1 ETH in wei

	ethValue := new(big.Int)
	ethValue.SetString("9000000000000000000", 10) // 9 ETH in wei

	balance.Add(balance, ethValue)

	fmt.Printf("Balance: %s", balance.String())
}

出力結果

Balance: 10000000000000000000

上記のコードでは、math/big パッケージの、big.Int型を用いて大きな数値を扱っているため、オーバーフローは発生しません。

これにより、int64のオーバーフローを防ぐことができます。

弊社の「Ginco Enterprise Wallet」でもこちらの方法を使用しており、ウォレットの残高はDBにStringで保存し、計算時にはbig.Intを利用しています。

まとめ

大きな数値を扱う際には、適切な型とその範囲を理解することが重要です。特に、int64などの型を使用する際には、その型が表現できる範囲を超えないように注意が必要です。

また、大きな数値を扱う場合や範囲を超える可能性がある場合には、math/big パッケージのような任意精度の数値を扱うパッケージの利用することでオーバーフローを避けることができます。

株式会社Gincoではブロックチェーンを学びたい方、ウォレットについて詳しくなりたい方を募集していますので下記リンクから是非ご応募ください。

株式会社Ginco の求人一覧

Discussion