2

Look at this transaction:

https://etherscan.io/tx/0x37c469573ca24f538d91f39e9d0b8d49927a36d70496ef74521cbf5c44a056d4

In geth's console, if I enter:

eth.getTransaction('0x37c4...')

It returns the transaction with a to value of null. I'm pretty sure this is because this transaction is a contract deployment.

If I look at:

eth.getTransactionReceipt('0x37c4...')

with the same transaction id, the contract address field is non-empty.

My question: is to being null enough to distinguish a transaction as one that deployed a contract? And secondly, is looking at the receipt the only way to get the address of the contract?

3

Q is to being null enough to distinguish a transaction as one that deployed a contract?

Yes

Q is looking at the receipt the only way to get the address of the contract?

Yes, in the go-ethereum client. , although you can probably work out the contract address from the trace results of the debug.traceTransaction(...) API call as well.

EDIT: I searched through the debug.traceTransaction(...) results and could not find the created contract address.



From the go-ethereum source code eth/api.go, lines 481-504 that follows, you can see that the condition where the To address is nil is used to decide whether the transaction is a contract creation transaction:

func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd string) (common.Hash, error) {
    args = prepareSendTxArgs(args, s.gpo)

    s.txMu.Lock()
    defer s.txMu.Unlock()

    if args.Nonce == nil {
        args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From))
    }

    var tx *types.Transaction
    if args.To == nil {
        tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
    } else {
        tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
    }

    signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes())
    if err != nil {
        return common.Hash{}, err
    }

    return submitTransaction(s.txPool, tx, signature)
}

This is confirmed in 4.3 The Transaction in the Yellow Paper: enter image description here

where the symbol ∅ represents an empty set.

Searching the go-ethereum source code for contractAddress shows that getTransactionReceipt(...) is the method to return the contract address. From eth/api.go, lines 1083-1132:

func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) {
    receipt := core.GetReceipt(s.chainDb, txHash)
    if receipt == nil {
        glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex())
        return nil, nil
    }

    tx, _, err := getTransaction(s.chainDb, s.txPool, txHash)
    if err != nil {
        glog.V(logger.Debug).Infof("%v\n", err)
        return nil, nil
    }

    txBlock, blockIndex, index, err := getTransactionBlockData(s.chainDb, txHash)
    if err != nil {
        glog.V(logger.Debug).Infof("%v\n", err)
        return nil, nil
    }

    from, err := tx.FromFrontier()
    if err != nil {
        glog.V(logger.Debug).Infof("%v\n", err)
        return nil, nil
    }

    fields := map[string]interface{}{
        "root":              common.Bytes2Hex(receipt.PostState),
        "blockHash":         txBlock,
        "blockNumber":       rpc.NewHexNumber(blockIndex),
        "transactionHash":   txHash,
        "transactionIndex":  rpc.NewHexNumber(index),
        "from":              from,
        "to":                tx.To(),
        "gasUsed":           rpc.NewHexNumber(receipt.GasUsed),
        "cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed),
        "contractAddress":   nil,
        "logs":              receipt.Logs,
    }

    if receipt.Logs == nil {
        fields["logs"] = []vm.Logs{}
    }

    // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
    if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 {
        fields["contractAddress"] = receipt.ContractAddress
    }

    return fields, nil
}
  • 1
    I asked this question so you could answer it. I knew you would know. Thanks. – Thomas Jay Rush Nov 10 '16 at 3:07
  • 1
    Hi @Thomas, I have been waiting for quite a while for you to ask this exact question :-) – Bokky WeAreAllAssange PooBah Nov 10 '16 at 3:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.