Skip to content

Instantly share code, notes, and snippets.

@novice-stack
Forked from crazygit/parseTransaction.go
Created October 25, 2022 06:37
Show Gist options
  • Save novice-stack/3777ad46dbca56e16b36ce7a4b06d74e to your computer and use it in GitHub Desktop.
Save novice-stack/3777ad46dbca56e16b36ce7a4b06d74e to your computer and use it in GitHub Desktop.

Revisions

  1. @crazygit crazygit revised this gist Jun 2, 2022. 1 changed file with 3 additions and 5 deletions.
    8 changes: 3 additions & 5 deletions parseTransaction.go
    Original file line number Diff line number Diff line change
    @@ -104,11 +104,9 @@ func DecodeTransactionLogs(receipt *types.Receipt, contractABI *abi.ABI) {
    if len(vLog.Data) > 0 {
    fmt.Printf("Log Data in Hex: %s\n", hex.EncodeToString(vLog.Data))
    outputDataMap := make(map[string]interface{})
    if len(vLog.Data) != 0 {
    err = contractABI.UnpackIntoMap(outputDataMap, event.Name, vLog.Data)
    if err != nil {
    log.Fatal(err)
    }
    err = contractABI.UnpackIntoMap(outputDataMap, event.Name, vLog.Data)
    if err != nil {
    log.Fatal(err)
    }
    fmt.Printf("Event outputs: %v\n", outputDataMap)
    }
  2. @crazygit crazygit revised this gist Jun 1, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion parseTransaction.go
    Original file line number Diff line number Diff line change
    @@ -93,7 +93,7 @@ func DecodeTransactionLogs(receipt *types.Receipt, contractABI *abi.ABI) {
    if err != nil {
    log.Fatal(err)
    }
    log.Printf("Event Name: %s\n", event.Name)
    fmt.Printf("Event Name: %s\n", event.Name)
    // topic[1:] is other indexed params in event
    if len(vLog.Topics) > 1 {
    for i, param := range vLog.Topics[1:] {
  3. @crazygit crazygit revised this gist Jun 1, 2022. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions parseTransaction.go
    Original file line number Diff line number Diff line change
    @@ -38,10 +38,10 @@ func GetContractRawABI(address string, apiKey string) (*RawABIResponse, error) {
    return nil, err
    }
    if !resp.IsSuccess() {
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s", resp))
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s\n", resp))
    }
    if *rawABIResponse.Status != "1" {
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s", *rawABIResponse.Result))
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s\n", *rawABIResponse.Result))
    }

    return rawABIResponse, nil
    @@ -93,7 +93,7 @@ func DecodeTransactionLogs(receipt *types.Receipt, contractABI *abi.ABI) {
    if err != nil {
    log.Fatal(err)
    }
    log.Printf("Event Name %s\n", event.Name)
    log.Printf("Event Name: %s\n", event.Name)
    // topic[1:] is other indexed params in event
    if len(vLog.Topics) > 1 {
    for i, param := range vLog.Topics[1:] {
  4. @crazygit crazygit revised this gist Jun 1, 2022. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions parseTransaction.go
    Original file line number Diff line number Diff line change
    @@ -66,19 +66,19 @@ func DecodeTransactionInputData(contractABI *abi.ABI, data []byte) {
    fmt.Printf("Method inputs: %v\n", inputsMap)
    }

    func getTransactionFrom(tx *types.Transaction) common.Address {
    func GetTransactionMessage(tx *types.Transaction) types.Message {
    msg, err := tx.AsMessage(types.LatestSignerForChainID(tx.ChainId()), nil)
    if err != nil {
    log.Fatal(err)
    }
    return msg.From()
    return msg
    }

    func GetTransactionBaseInfo(tx *types.Transaction) {
    func ParseTransactionBaseInfo(tx *types.Transaction) {
    fmt.Printf("Hash: %s\n", tx.Hash().Hex())
    fmt.Printf("ChainId: %d\n", tx.ChainId())
    fmt.Printf("Value: %s\n", tx.Value().String())
    fmt.Printf("From: %s\n", getTransactionFrom(tx).Hex())
    fmt.Printf("From: %s\n", GetTransactionMessage(tx).From().Hex())
    fmt.Printf("To: %s\n", tx.To().Hex())
    fmt.Printf("Gas: %d\n", tx.Gas())
    fmt.Printf("Gas Price: %d\n", tx.GasPrice().Uint64())
    @@ -140,7 +140,7 @@ func main() {
    // get etherscanAPIKEY from https://docs.etherscan.io/getting-started/viewing-api-usage-statistics
    const etherscanAPIKEY = "M3SF4WTDC4NWQIIVNAZDFXBW1SW49QWDNZ"
    const providerUrl = "https://ropsten.infura.io/v3/28d5693e8bee4b58a61f0c627d62331e"

    client, err := ethclient.Dial(providerUrl)
    if err != nil {
    log.Fatal(err)
    @@ -152,7 +152,7 @@ func main() {
    log.Fatal(err)
    }
    fmt.Printf("tx isPending: %t\n", isPending)
    GetTransactionBaseInfo(tx)
    ParseTransactionBaseInfo(tx)
    contractABI := GetContractABI(tx.To().String(), etherscanAPIKEY)
    DecodeTransactionInputData(contractABI, tx.Data())
    receipt := GetTransactionReceipt(client, txHash)
  5. @crazygit crazygit revised this gist Jun 1, 2022. No changes.
  6. @crazygit crazygit created this gist Jun 1, 2022.
    160 changes: 160 additions & 0 deletions parseTransaction.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,160 @@
    package main

    import (
    "context"
    "encoding/hex"
    "fmt"
    "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"
    "github.com/go-resty/resty/v2"
    "log"
    "strings"
    )

    type (
    RawABIResponse struct {
    Status *string `json:"status"`
    Message *string `json:"message"`
    Result *string `json:"result"`
    }
    )

    func GetContractRawABI(address string, apiKey string) (*RawABIResponse, error) {
    client := resty.New()
    rawABIResponse := &RawABIResponse{}
    resp, err := client.R().
    SetQueryParams(map[string]string{
    "module": "contract",
    "action": "getabi",
    "address": address,
    "apikey": apiKey,
    }).
    SetResult(rawABIResponse).
    Get("https://api-ropsten.etherscan.io/api")

    if err != nil {
    return nil, err
    }
    if !resp.IsSuccess() {
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s", resp))
    }
    if *rawABIResponse.Status != "1" {
    return nil, fmt.Errorf(fmt.Sprintf("Get contract raw abi failed: %s", *rawABIResponse.Result))
    }

    return rawABIResponse, nil
    }

    // refer
    // https://github.com/ethereum/web3.py/blob/master/web3/contract.py#L435
    func DecodeTransactionInputData(contractABI *abi.ABI, data []byte) {
    methodSigData := data[:4]
    inputsSigData := data[4:]
    method, err := contractABI.MethodById(methodSigData)
    if err != nil {
    log.Fatal(err)
    }
    inputsMap := make(map[string]interface{})
    if err := method.Inputs.UnpackIntoMap(inputsMap, inputsSigData); err != nil {
    log.Fatal(err)
    } else {
    fmt.Println(inputsMap)
    }
    fmt.Printf("Method Name: %s\n", method.Name)
    fmt.Printf("Method inputs: %v\n", inputsMap)
    }

    func getTransactionFrom(tx *types.Transaction) common.Address {
    msg, err := tx.AsMessage(types.LatestSignerForChainID(tx.ChainId()), nil)
    if err != nil {
    log.Fatal(err)
    }
    return msg.From()
    }

    func GetTransactionBaseInfo(tx *types.Transaction) {
    fmt.Printf("Hash: %s\n", tx.Hash().Hex())
    fmt.Printf("ChainId: %d\n", tx.ChainId())
    fmt.Printf("Value: %s\n", tx.Value().String())
    fmt.Printf("From: %s\n", getTransactionFrom(tx).Hex())
    fmt.Printf("To: %s\n", tx.To().Hex())
    fmt.Printf("Gas: %d\n", tx.Gas())
    fmt.Printf("Gas Price: %d\n", tx.GasPrice().Uint64())
    fmt.Printf("Nonce: %d\n", tx.Nonce())
    fmt.Printf("Transaction Data in hex: %s\n", hex.EncodeToString(tx.Data()))
    }

    func DecodeTransactionLogs(receipt *types.Receipt, contractABI *abi.ABI) {
    for _, vLog := range receipt.Logs {
    // topic[0] is the event name
    event, err := contractABI.EventByID(vLog.Topics[0])
    if err != nil {
    log.Fatal(err)
    }
    log.Printf("Event Name %s\n", event.Name)
    // topic[1:] is other indexed params in event
    if len(vLog.Topics) > 1 {
    for i, param := range vLog.Topics[1:] {
    fmt.Printf("Indexed params %d in hex: %s\n", i, param)
    fmt.Printf("Indexed params %d decoded %s\n", i, common.HexToAddress(param.Hex()))
    }
    }
    if len(vLog.Data) > 0 {
    fmt.Printf("Log Data in Hex: %s\n", hex.EncodeToString(vLog.Data))
    outputDataMap := make(map[string]interface{})
    if len(vLog.Data) != 0 {
    err = contractABI.UnpackIntoMap(outputDataMap, event.Name, vLog.Data)
    if err != nil {
    log.Fatal(err)
    }
    }
    fmt.Printf("Event outputs: %v\n", outputDataMap)
    }
    }
    }

    func GetContractABI(contractAddress, etherscanAPIKey string) *abi.ABI {
    rawABIResponse, err := GetContractRawABI(contractAddress, etherscanAPIKey)
    if err != nil {
    log.Fatal(err)
    }

    contractABI, err := abi.JSON(strings.NewReader(*rawABIResponse.Result))
    if err != nil {
    log.Fatal(err)
    }
    return &contractABI
    }

    func GetTransactionReceipt(client *ethclient.Client, txHash common.Hash) *types.Receipt {
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
    log.Fatal(err)
    }
    return receipt
    }

    func main() {
    // get etherscanAPIKEY from https://docs.etherscan.io/getting-started/viewing-api-usage-statistics
    const etherscanAPIKEY = "M3SF4WTDC4NWQIIVNAZDFXBW1SW49QWDNZ"
    const providerUrl = "https://ropsten.infura.io/v3/28d5693e8bee4b58a61f0c627d62331e"

    client, err := ethclient.Dial(providerUrl)
    if err != nil {
    log.Fatal(err)
    }
    // https://ropsten.etherscan.io/tx/0x7e605f68ff30509eb2bf3238936ef65a01bfa25243488c007244aabe645d0ec9
    txHash := common.HexToHash("0x7e605f68ff30509eb2bf3238936ef65a01bfa25243488c007244aabe645d0ec9")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
    log.Fatal(err)
    }
    fmt.Printf("tx isPending: %t\n", isPending)
    GetTransactionBaseInfo(tx)
    contractABI := GetContractABI(tx.To().String(), etherscanAPIKEY)
    DecodeTransactionInputData(contractABI, tx.Data())
    receipt := GetTransactionReceipt(client, txHash)
    DecodeTransactionLogs(receipt, contractABI)
    }