0

My smart contract is deployed on https://sepolia.etherscan.io/tx/0x1b1742a3ff2ac160d5f245ab91828fcc56a803cb3826b6cbdb9f3cb2f293ba66

Once deployed I get contract creation code storage out of gas error

I got the gas estimation formula

gasLimit = 21000 + 68 * dataByteLength

from How do you calculate gas limit for transaction with data in Ethereum?

Then here is my Go code where I use the formula:

package main

import (
    "context"
    "crypto/ecdsa"
    "deploy-contract-tool/api"
    "fmt"
    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/jimlawless/whereami"
    "math/big"
)

func deploy() (string, string, string, string, error) {
    privateKey, err := crypto.HexToECDSA(wallet.privateKey)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        return "", "", whereami.WhereAmI(), "", err
    }

    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
    nonce, err := connection.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    pendingBalance, err := connection.PendingBalanceAt(context.Background(), fromAddress)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    // Set the gas price and gas limit
    suggestedGasPrice, err := connection.SuggestGasPrice(context.Background())
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    auth, err := bind.NewKeyedTransactorWithChainID(privateKey, currentConfig.ChainID)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    auth.Nonce = big.NewInt(int64(nonce))

    auth.Value = big.NewInt(0)
    auth.GasPrice = suggestedGasPrice

    contractByteLength := 15429
    //gasLimit calculation below
    gasLimit := 21000 + (68 * contractByteLength)
    auth.GasLimit = uint64(gasLimit)
    fmt.Println("auth.GasLimit: ", auth.GasLimit)

    gasLimitBigInt := new(big.Int).SetUint64(auth.GasLimit)
    calculatedCost := new(big.Int).Mul(gasLimitBigInt, auth.GasPrice)
    authValue := new(big.Int).SetUint64(auth.Value.Uint64())
    calculatedCost = new(big.Int).Add(calculatedCost, authValue)

    fmt.Println("calculatedCost: ", calculatedCost)
    fmt.Println("pendingBalance: ", pendingBalance)
    fmt.Println("suggestedGasPrice: ", suggestedGasPrice)
    fmt.Println("gasLimit: ", gasLimit)
    fmt.Println("auth.GasLimit: ", auth.GasLimit)
    fmt.Println("auth.GasPrice: ", auth.GasPrice)
    fmt.Println("nonce: ", auth.Nonce)

    address, tx, instance, err := api.DeployPigfox(auth, connection)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }

    fmt.Println("----After deploy----")
    fmt.Println("Token deployed at:", address)
    fmt.Println("Token tx:", tx.Hash())
    fmt.Println("Token cost:", tx.Cost())
    fmt.Println("Token gas limit:", tx.Gas())
    fmt.Println("Token gas price:", tx.GasPrice())
    fmt.Println("Waiting to be mined...")

    receipt, err := bind.WaitMined(context.Background(), connection, tx)
    if err != nil {
        return "", "", whereami.WhereAmI(), "", err
    }
    fmt.Println("Mined...")

    if receipt.Status == types.ReceiptStatusSuccessful {
        fmt.Println("Token deployed successfully")
    } else {
        reason, err := getRevertReason(receipt) //nolint:govet
        return "", "", whereami.WhereAmI(), reason, err
    }

    fmt.Println(instance)

    return address.Hex(), tx.Hash().Hex(), "", "", err
}

I got the contract byte length from

$ ls -l Contract.sol 
-rw-rw-r-- 1 user user 15429 Sep 19 05:11 Contract.sol

It was suggested to me that the gas estimation formula might be old and deprecated but that's all I have found.

This is terminal output when executing the code.

auth.GasLimit:  1070172
calculatedCost:  107351519592456
pendingBalance:  2388409999174700070
suggestedGasPrice:  100312398
gasLimit:  1070172
auth.GasLimit:  1070172
auth.GasPrice:  100312398
nonce:  30
----After deploy----
Token deployed at: 0xf985E6d5F0De41A8F21Dc0D050Dc9C0B3A23B043
Token tx: 0x1b1742a3ff2ac160d5f245ab91828fcc56a803cb3826b6cbdb9f3cb2f293ba66
Token cost: 107351519592456
Token gas limit: 1070172
Token gas price: 100312398
Waiting to be mined...
Mined...

Any suggestions on how I can fix this gas calculation error?

3 Answers 3

2
+25

You will need to calculate the sum of the following values:

32000 gas - creating a contract 
21000 gas - procedure call 
Gas per execution of creation code (initialization code + constructor. 
    The creation code initiates non-zero variables in the storage (that's 22,100 for each storage variable set) 
    and executes the code in the constructor - this may require different amounts of gas) 
200 gaz * byte of expanded bytecode (without initialization code) 
4 gaz for each zero byte in transaction data and 16 gaz for each non-zero byte in transaction (entire initialization code + executable bytecode)
1

This is probably not a satisfactory answer, but it addresses some issues in your question.

  1. Generally it's impossible to predict the gas limit exactly. Especially without actually dry-running it. But even if you do that, the exact cost may depend on the actual state of the blockchain. Therefore the whole world uses a margin when providing gas.

  2. Your contract byte length of 15429 is wrong. That's not the length of your contract, it's the length of your source code. You have to compile your source code and take the byte length of that to get the byte length of your contract. However, the dataByteLength in the referenced formula is not the byte length of your contract. It's the byte length of the input that's in the transaction. That's what data means in the context of the Ethereum blockchain. As a side note, while deploying/constructing the contract, the input/data of that "deployment" transaction is the same as the bytecode of the contract, but only then.

  3. The formula you reference seems something like a formula for the gas cost of "starting up" the transaction. After that, it needs much more gas to actually execute the transaction with the contract. So the formula gives only a small part of the gas cost for entering (and executing) a transaction. The exact cost depends on exactly what path inside the contract is executed during the transaction, what assembly (opcode) statements are executed, and what their parameters are.

  4. You say your contract is deployed, but it's not. The constructor ran out of gas and therefore your contract is not deployed. When trying to send transactions to the contract, you'll probably always (on testnet) get the same error (that you mention) that the deployment failed. I suppose if this happens on the main net, you'd get an error that the contract doesn't exist in this situation. I think you can retry to deploy it (on the test net) while providing much more gas.

1
  • Thank you @AlbertHedriks very insightful answer.
    – pigfox
    Commented Oct 18, 2023 at 18:06
0

That is not exactly correct. A safe and easy method estimate gas limit for contract creation calls is to using Hardhat or Foundry, which will give you fairly accurate and gas limits. As you are deploying on a testnet, you may try deploying with a doubled gas limit to your estimate to see how much gas the transaction uses. Alternatively, since you are using Go, you may use the ethereum clients estimategas function with an RPC url. An example can be found at https://gist.github.com/miguelmota/117caf685b84cba8317f07e1ac6cd0da

1
  • I looked at the url you provided and my problem is that I don't know the &tokenAddress in estimatedGas, err := client.EstimateGas(context.Background(), ethereum.CallMsg{ To: &tokenAddress, Data: []byte{0}, }) It's like a chicken or the egg problem.
    – pigfox
    Commented Oct 10, 2023 at 23:21

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