⛓️

【Web3】AlchemyApiを利用して、go言語でスマートコントラクトをデプロイする

に公開

はじめに

Web3系の開発をしていく中で、alchemy-sdk-jsというライブラリで、AlchemyApiを利用して開発していました。ただ、gethがgo言語で実装されていることから、AlchemyApiを利用して同様にgo言語で実装したいと考えて、go-alchemy-sdkを開発中です。

AlchemyApiとgethの架け橋になることを目標としています。
https://github.com/poteto-go/go-alchemy-sdk

今回はgo-alchemy-sdkを利用して、スマートコントラクトをデプロイするチュートリアルや、内部実装について少し説明します。

AlchemyApiとは

web3開発のプラットフォーマーとしてAPIを提供しています。少なくとも開発レベルであれば、無料で十分かと。
https://www.alchemy.com/

go-alchemy-sdkでスマートコントラクトをデプロイする

チュートリアルは以下のドキュメントにまとめています。
https://go-alchemy-sdk.poteto-mahiro.com/tutorial/deployContract

ここでは、内部に注目して説明します。

AlchemyApiの接続設定をする

setting := gas.AlchemySetting{
    ApiKey:  "<apiKey>",
    Network: types.EthSepolia,
}

alchemy := gas.NewAlchemy(setting)

ApiKeyからネームスペースAPIを設定したり、gethethclientを設定したり、する。

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))

実装は主に、gethbind/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コールは以下のようになっています。
api-logs

EthSepoliaだけでなく、EthHoodiPolygonAmoyのネットワークでも動かしましたが、動きませんでした。
EthHoodiでは、上記のメソッドの中に、動かないメソッドが存在し、PolygonAmoyでは、gethの実装の中で、TransactionのValueDataではなく、Inputキーを利用していましたが、これがPolygonAmoyにおいておそらく動かないようです。

このへんのネットワーク対応を進めていきたいと思っています。

おわり

ぜひ、goで素敵なweb3ライフを!!

Discussion