130 lines
4.1 KiB
Go
130 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
)
|
|
|
|
const membershipABI = `[{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"mintMembership","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountPaid","type":"uint256"},{"indexed":false,"internalType":"address","name":"currency","type":"address"}],"name":"MembershipMinted","type":"event"}]`
|
|
|
|
func encodeMintMembershipCalldata(recipient string) (string, error) {
|
|
parsed, err := abi.JSON(strings.NewReader(membershipABI))
|
|
if err != nil {
|
|
return "", fmt.Errorf("parse membership abi: %w", err)
|
|
}
|
|
address := common.HexToAddress(recipient)
|
|
data, err := parsed.Pack("mintMembership", address)
|
|
if err != nil {
|
|
return "", fmt.Errorf("pack mint calldata: %w", err)
|
|
}
|
|
return "0x" + common.Bytes2Hex(data), nil
|
|
}
|
|
|
|
func verifyMintedOnChain(ctx context.Context, cfg Config, txHash string, expectedWallet string) error {
|
|
if strings.TrimSpace(cfg.ChainRPCURL) == "" {
|
|
return nil
|
|
}
|
|
|
|
client, err := ethclient.DialContext(ctx, cfg.ChainRPCURL)
|
|
if err != nil {
|
|
return fmt.Errorf("dial chain rpc: %w", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
hash := common.HexToHash(txHash)
|
|
receipt, err := client.TransactionReceipt(ctx, hash)
|
|
if err != nil {
|
|
return fmt.Errorf("read tx receipt: %w", err)
|
|
}
|
|
if receipt == nil {
|
|
return fmt.Errorf("tx receipt not found")
|
|
}
|
|
if receipt.Status != types.ReceiptStatusSuccessful {
|
|
return fmt.Errorf("tx failed status=%d", receipt.Status)
|
|
}
|
|
|
|
parsed, err := abi.JSON(strings.NewReader(membershipABI))
|
|
if err != nil {
|
|
return fmt.Errorf("parse membership abi: %w", err)
|
|
}
|
|
mintedEvent := parsed.Events["MembershipMinted"]
|
|
expectedWallet = strings.ToLower(common.HexToAddress(expectedWallet).Hex())
|
|
|
|
for _, lg := range receipt.Logs {
|
|
if strings.ToLower(lg.Address.Hex()) != strings.ToLower(common.HexToAddress(cfg.MembershipContract).Hex()) {
|
|
continue
|
|
}
|
|
if len(lg.Topics) == 0 || lg.Topics[0] != mintedEvent.ID {
|
|
continue
|
|
}
|
|
if len(lg.Topics) < 2 {
|
|
continue
|
|
}
|
|
wallet := common.HexToAddress(lg.Topics[1].Hex()).Hex()
|
|
if strings.ToLower(wallet) == expectedWallet {
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("membership mint event not found for wallet")
|
|
}
|
|
|
|
func verifyTransactionSenderOnChain(ctx context.Context, cfg Config, txHash string, expectedSender string) error {
|
|
if strings.TrimSpace(cfg.ChainRPCURL) == "" {
|
|
return nil
|
|
}
|
|
expectedSender = strings.TrimSpace(expectedSender)
|
|
if expectedSender == "" {
|
|
return fmt.Errorf("expected sender missing")
|
|
}
|
|
|
|
client, err := ethclient.DialContext(ctx, cfg.ChainRPCURL)
|
|
if err != nil {
|
|
return fmt.Errorf("dial chain rpc: %w", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
hash := common.HexToHash(txHash)
|
|
receipt, err := client.TransactionReceipt(ctx, hash)
|
|
if err != nil {
|
|
return fmt.Errorf("read tx receipt: %w", err)
|
|
}
|
|
if receipt == nil {
|
|
return fmt.Errorf("tx receipt not found")
|
|
}
|
|
if receipt.Status != types.ReceiptStatusSuccessful {
|
|
return fmt.Errorf("tx failed status=%d", receipt.Status)
|
|
}
|
|
|
|
tx, pending, err := client.TransactionByHash(ctx, hash)
|
|
if err != nil {
|
|
return fmt.Errorf("read tx body: %w", err)
|
|
}
|
|
if pending {
|
|
return fmt.Errorf("tx pending")
|
|
}
|
|
|
|
chainID := tx.ChainId()
|
|
if chainID == nil || chainID.Sign() <= 0 {
|
|
chainID = big.NewInt(cfg.ChainID)
|
|
}
|
|
signer := types.LatestSignerForChainID(chainID)
|
|
from, err := types.Sender(signer, tx)
|
|
if err != nil {
|
|
return fmt.Errorf("resolve tx sender: %w", err)
|
|
}
|
|
|
|
gotSender := strings.ToLower(from.Hex())
|
|
wantSender := strings.ToLower(common.HexToAddress(expectedSender).Hex())
|
|
if gotSender != wantSender {
|
|
return fmt.Errorf("tx sender mismatch got=%s want=%s", gotSender, wantSender)
|
|
}
|
|
return nil
|
|
}
|