-
-
Save alexxuyang/7c2b8de66cbd7d8b2522db29cdfb0621 to your computer and use it in GitHub Desktop.
How to decode smart contract transation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"maxNftSupply","type":"uint256"},{"internalType":"uint256","name":"saleStart","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BAYC_PROVENANCE","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_APES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVEAL_TIMESTAMP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"apePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencySetStartingIndexBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flipSaleState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxApePurchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfTokens","type":"uint256"}],"name":"mintApe","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveApes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"saleIsActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"provenanceHash","type":"string"}],"name":"setProvenanceHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"revealTimeStamp","type":"uint256"}],"name":"setRevealTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setStartingIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startingIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startingIndexBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]p |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"encoding/hex" | |
"encoding/json" | |
"fmt" | |
"io" | |
"log" | |
"os" | |
"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" | |
) | |
func MapToJson(param map[string]interface{}) string { | |
dataType, _ := json.Marshal(param) | |
dataString := string(dataType) | |
return dataString | |
} | |
const PROVIDER_URL = "https://ethereum.publicnode.com" | |
func GetLocalABI(path string) string { | |
// read abi json from local file | |
abiFile, err := os.Open(path) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer abiFile.Close() | |
result, err := io.ReadAll(abiFile) | |
if err != nil { | |
log.Fatal(err) | |
} | |
return string(result) | |
} | |
// refer | |
// https://github.com/ethereum/web3.py/blob/master/web3/contract.py#L435 | |
func DecodeTransactionInputData(contractABI *abi.ABI, data []byte) { | |
// The first 4 bytes of the txn represent the ID of the method in the ABI | |
methodSigData := data[:4] | |
method, err := contractABI.MethodById(methodSigData) | |
if err != nil { | |
log.Fatal(err) | |
} | |
// parse the inputs to this method | |
inputsSigData := data[4:] | |
inputsMap := make(map[string]interface{}) | |
if err := method.Inputs.UnpackIntoMap(inputsMap, inputsSigData); err != nil { | |
log.Fatal(err) | |
} | |
fmt.Printf("Method Name: %s\n", method.Name) | |
fmt.Printf("Method inputs: %v\n", MapToJson(inputsMap)) | |
} | |
func GetTransactionMessage(tx *types.Transaction) types.Message { | |
msg, err := tx.AsMessage(types.LatestSignerForChainID(tx.ChainId()), nil) | |
if err != nil { | |
log.Fatal(err) | |
} | |
return msg | |
} | |
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", GetTransactionMessage(tx).From().Hex()) // from field is not inside of transation | |
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())) | |
fmt.Print("\n") | |
} | |
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) | |
} | |
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:] { | |
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{}) | |
err = contractABI.UnpackIntoMap(outputDataMap, event.Name, vLog.Data) | |
if err != nil { | |
log.Fatal(err) | |
} | |
fmt.Printf("Event outputs: %v\n", outputDataMap) | |
} | |
} | |
} | |
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() { | |
// read parameter from the command line | |
txHashStr := os.Args[1] | |
// client wrappers for the Ethereum RPC API. | |
client, err := ethclient.Dial(PROVIDER_URL) | |
if err != nil { | |
log.Println(err) | |
continue | |
} | |
// TransactionByHash returns the transaction with the given hash. | |
txHash := common.HexToHash(txHashStr) | |
tx, isPending, err := client.TransactionByHash(context.Background(), txHash) | |
if err != nil { | |
log.Fatal(err) | |
} | |
fmt.Printf("tx isPending: %t\n", isPending) | |
// parse the transaction information | |
fmt.Println("\nStart to pase transation base info") | |
ParseTransactionBaseInfo(tx) | |
// get abi from local | |
contractABI, err := abi.JSON(strings.NewReader(GetLocalABI("boredape.json"))) | |
if err != nil { | |
log.Fatal(err) | |
} | |
fmt.Println("\nStart to decode transation input data") | |
DecodeTransactionInputData(&contractABI, tx.Data()) | |
fmt.Println("\nStart to decode transation log") | |
receipt := GetTransactionReceipt(client, txHash) | |
DecodeTransactionLogs(receipt, &contractABI) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment