cmd/waku/rlngenerate/web3.go
//go:build !gowaku_no_rln
// +build !gowaku_no_rln
package rlngenerate
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/waku-org/go-waku/logging"
"github.com/waku-org/go-waku/waku/v2/protocol/rln/web3"
"github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap"
)
func getMembershipFee(ctx context.Context, rlnContract web3.RLNContract) (*big.Int, error) {
return rlnContract.MEMBERSHIPDEPOSIT(&bind.CallOpts{Context: ctx})
}
func buildTransactor(ctx context.Context, membershipFee *big.Int, chainID *big.Int) (*bind.TransactOpts, error) {
auth, err := bind.NewKeyedTransactorWithChainID(options.ETHPrivateKey, chainID)
if err != nil {
return nil, err
}
auth.Value = membershipFee
auth.Context = ctx
auth.GasLimit = options.ETHGasLimit
var ok bool
if options.ETHNonce != "" {
nonce := &big.Int{}
auth.Nonce, ok = nonce.SetString(options.ETHNonce, 10)
if !ok {
return nil, errors.New("invalid nonce value")
}
}
if options.ETHGasFeeCap != "" {
gasFeeCap := &big.Int{}
auth.GasFeeCap, ok = gasFeeCap.SetString(options.ETHGasFeeCap, 10)
if !ok {
return nil, errors.New("invalid gas fee cap value")
}
}
if options.ETHGasTipCap != "" {
gasTipCap := &big.Int{}
auth.GasTipCap, ok = gasTipCap.SetString(options.ETHGasTipCap, 10)
if !ok {
return nil, errors.New("invalid gas tip cap value")
}
}
if options.ETHGasPrice != "" {
gasPrice := &big.Int{}
auth.GasPrice, ok = gasPrice.SetString(options.ETHGasPrice, 10)
if !ok {
return nil, errors.New("invalid gas price value")
}
}
return auth, nil
}
func register(ctx context.Context, web3Config *web3.Config, idComm rln.IDCommitment) (rln.MembershipIndex, error) {
// check if the contract exists by calling a static function
membershipFee, err := getMembershipFee(ctx, web3Config.RLNContract)
if err != nil {
return 0, err
}
auth, err := buildTransactor(ctx, membershipFee, web3Config.ChainID)
if err != nil {
return 0, err
}
log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:]))
// registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress
tx, err := web3Config.RegistryContract.Register(auth, web3Config.RLNContract.StorageIndex, rln.Bytes32ToBigInt(idComm))
if err != nil {
return 0, fmt.Errorf("transaction error: %w", err)
}
explorerURL := ""
switch web3Config.ChainID.Int64() {
case 1:
explorerURL = "https://etherscan.io"
case 5:
explorerURL = "https://goerli.etherscan.io"
case 11155111:
explorerURL = "https://sepolia.etherscan.io"
}
if explorerURL != "" {
logger.Info(fmt.Sprintf("transaction broadcasted, find details of your registration transaction in %s/tx/%s", explorerURL, tx.Hash()))
} else {
logger.Info("transaction broadcasted.", zap.String("transactionHash", tx.Hash().String()))
}
logger.Warn("waiting for transaction to be mined...")
txReceipt, err := bind.WaitMined(ctx, web3Config.ETHClient, tx)
if err != nil {
return 0, fmt.Errorf("transaction error: %w", err)
}
if txReceipt.Status != types.ReceiptStatusSuccessful {
return 0, errors.New("transaction reverted")
}
// the receipt topic holds the hash of signature of the raised events
evt, err := web3Config.RLNContract.ParseMemberRegistered(*txReceipt.Logs[0])
if err != nil {
return 0, err
}
var eventIDComm rln.IDCommitment = rln.BigIntToBytes32(evt.IdCommitment)
log.Debug("information extracted from tx log", zap.Uint64("blockNumber", evt.Raw.BlockNumber), logging.HexBytes("idCommitment", eventIDComm[:]), zap.Uint64("index", evt.Index.Uint64()))
if eventIDComm != idComm {
return 0, errors.New("invalid id commitment key")
}
return rln.MembershipIndex(uint(evt.Index.Int64())), nil
}