【Web3】AlchemyApiを利用して、go言語でスマートコントラクトをデプロイする
はじめに
Web3系の開発をしていく中で、alchemy-sdk-jsというライブラリで、AlchemyApiを利用して開発していました。ただ、gethがgo言語で実装されていることから、AlchemyApiを利用して同様にgo言語で実装したいと考えて、go-alchemy-sdkを開発中です。
AlchemyApiとgethの架け橋になることを目標としています。
今回はgo-alchemy-sdkを利用して、スマートコントラクトをデプロイするチュートリアルや、内部実装について少し説明します。
AlchemyApiとは
web3開発のプラットフォーマーとしてAPIを提供しています。少なくとも開発レベルであれば、無料で十分かと。
go-alchemy-sdkでスマートコントラクトをデプロイする
チュートリアルは以下のドキュメントにまとめています。
ここでは、内部に注目して説明します。
AlchemyApiの接続設定をする
setting := gas.AlchemySetting{
ApiKey: "<apiKey>",
Network: types.EthSepolia,
}
alchemy := gas.NewAlchemy(setting)
ApiKeyからネームスペースAPIを設定したり、gethのethclientを設定したり、する。
func NewAlchemy(setting AlchemySetting) Alchemy {
alchemyConfig := NewAlchemyConfig(setting)
alchemyProvider := NewAlchemyProvider(alchemyConfig)
ether := ether.NewEtherApi(
alchemyProvider,
alchemyConfig.toEtherApiConfig(),
)
alchemyProvider.SetEth(ether)
coreNamespace := namespace.NewCore(ether)
return Alchemy{
config: alchemyConfig,
Core: coreNamespace,
provider: alchemyProvider,
}
}
Walletに接続する
w, _ := wallet.New("<privateKey>")
fmt.Println(w.GetAddress())
walletはアカウントの秘密鍵と公開鍵, AlchemyApi接続のためのproviderを持っています。
type wallet struct {
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
provider types.IAlchemyProvider
}
walletには秘密鍵の文字列を用いて接続します。
gethのecdsa変換メソッドを呼び出して、秘密鍵と公開鍵を作成します。
ちなみに公開鍵はウォレットのアドレスになっています。
func New(privateKeyStr string) (Wallet, error) {
privateKey, err := crypto.HexToECDSA(privateKeyStr)
if err != nil {
return &wallet{}, err
}
publicKey := privateKey.Public()
publicKeyECDSA := publicKey.(*ecdsa.PublicKey)
return &wallet{
privateKey: privateKey,
publicKey: publicKeyECDSA,
}, nil
}
func (w *wallet) GetAddress() string {
address := crypto.PubkeyToAddress(*w.publicKey)
return common.HexToAddress(address.Hex()).String()
}
WalletをAlchemyApiに接続する
w.Connect(alchemy.GetProvider())
walletからスマートコントラクトをデプロイしたり、トランザクションをチェーンに流したり、チェーン情報を取得したり、などを可能にするために、AlchemyApiに接続します。
単純にAlchemyProviderに接続するだけです。
func (w *wallet) Connect(provider types.IAlchemyProvider) {
w.mu.Lock()
defer w.mu.Unlock()
w.provider = provider
}
スマートコントラクトをデプロイする
スマートコントラクトのコンパイルなどは、ドキュメントを参照してください。
address, _ := w.DeployContract(&abi.StorageMetaData)
fmt.Println(fmt.Sprintf("deployed at: %s", address))
実装は主に、gethのbind/v2を利用しています。
まずスマートコントラクトをデプロイするのには、walletがapiに接続できている必要があります。
デプロイのために、秘密鍵とChainIDを用いて、singerを作ります。
このsingerとmetaDataを用いて、スマートコントラクトをデプロイし、そのアドレスを返します。
func (w *wallet) DeployContract(metaData *bind.MetaData) (common.Address, error) {
if w.provider == nil {
return common.Address{}, constant.ErrWalletIsNotConnected
}
chainID, err := w.provider.Eth().ChainID()
if err != nil {
return common.Address{}, err
}
auth := bind.NewKeyedTransactor(w.privateKey, chainID)
address, err := w.provider.Eth().DeployContract(auth, metaData)
if err != nil {
return common.Address{}, err
}
return address, nil
}
スマートコントラクトのデプロイもほとんど、ethclientのラッパーになっており、最終的には、デプロイ完了まで待つために、WaitDeployedを呼び出します。
func (ether *Ether) DeployContract(
auth *bind.TransactOpts,
metaData *bind.MetaData,
) (common.Address, error) {
client, err := ether.GetEthClient()
if err != nil {
return common.Address{}, err
}
defer client.Close()
// set up params to deploy an instance of the metadata
deployParams := bind.DeploymentParams{
Contracts: []*bind.MetaData{metaData},
}
deployer := bind.DefaultDeployer(auth, client)
// create and submit the contract deployment
deployRes, err := bind.LinkAndDeploy(&deployParams, deployer)
if err != nil {
return common.Address{}, err
}
tx := deployRes.Txs[metaData.ID]
// wait for deployment on chain
address, err := bind.WaitDeployed(context.Background(), client, tx.Hash())
if err != nil {
return common.Address{}, err
}
return address, nil
}
発行されるApiとトランザクション
この時、送信されるAPIコールは以下のようになっています。

EthSepoliaだけでなく、EthHoodiとPolygonAmoyのネットワークでも動かしましたが、動きませんでした。
EthHoodiでは、上記のメソッドの中に、動かないメソッドが存在し、PolygonAmoyでは、gethの実装の中で、TransactionのValueやDataではなく、Inputキーを利用していましたが、これがPolygonAmoyにおいておそらく動かないようです。
このへんのネットワーク対応を進めていきたいと思っています。
おわり
ぜひ、goで素敵なweb3ライフを!!
Discussion