1

i use: python 3.5, docker, ganache (image image: trufflesuite/ganache-cli:v6.9.1)

what do i do: (simplified example) I have two container: container with ganache and container with general logic. Ganache container started fine. Then in second container i launched two scripts which deploy contracts and then mint tokens to wallet. Deploy was done successful but on mint step i get ganache error. I tried various solutions, but the problem repeats over and over again. (it's all need for the local environment tests and i use case without truffele, ganache only)

my code:
docker-compose.yaml:

  ganache:
    image: trufflesuite/ganache-cli:v6.9.1
    command: >
      --seed test
      --account="my_wallet_1,1000000000000000000000"
      --account="my_wallet_2,1000000000000000000000"
      --account="my_wallet_3,1000000000000000000000000"
    ports:
      - "8545:8545"

  my_second_container:
    build: <build_mane>
    entrypoint: "<script_for_parse_secret_env_variables>.sh"
    command: "<script_for_deploy_contract_and_mint_tokens>.sh"
    restart: always
    volumes:
      - <volumes_for_logs_scripts_etc.>
    depends_on:
      - db
      - ganache
    environment: 
      <NAME_ENV_VARIABLE>: <VALUE>
      CONTRACTS_PATH: ./contracts
      WEB3_RPC_URL: http://ganache:8545

deploy_contract.py:

import os
import json

from time import sleep
from web3 import Account, HTTPProvider, Web3
from pprint import pprint


node_url = os.environ['WEB3_RPC_URL']
w3 = Web3(HTTPProvider(node_url))

ARTIFACTS_PATH = './contracts/'

if 'CONTRACTS_PATH' in os.environ:
    ARTIFACTS_PATH = os.environ['CONTRACTS_PATH']

if not os.path.isdir(ARTIFACTS_PATH):
    raise Exception('No contracts directory found')

TOKEN_NAME = 'MyToken'
CROWDSALE_NAME = 'Crowdsale'

# Wait for ganache up
tries = 100
while tries > 0:
    if w3.isConnected():
        break
    tries = tries - 1
    sleep(1)

if tries == 0:
    raise Exception('No test net')

w3.eth.defaultAccount = w3.eth.accounts[0]

backend_address = Account.privateKeyToAccount(os.environ['BACKEND_PRIVATE_KEY']).address
if w3.eth.defaultAccount != backend_address:
    print('default:', w3.eth.defaultAccount)
    raise Exception('Wrong default account')
else:
    CORP_WALLET = backend_address
    print('CORPORATION WALLET :  %s' % w3.eth.defaultAccount)


#  --- MYTOKEN token deploy  ---

with open(os.path.join(ARTIFACTS_PATH, TOKEN_NAME + '.json')) as file:
    token_interface = json.loads(file.read())

TokenMY = w3.eth.contract(abi=token_interface['abi'], bytecode=token_interface['bytecode'])
tx_hash = TokenMY.constructor().transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
token_address = tx_receipt.contractAddress
print('MYTOKEN contracts successfully deployed')
print('MYTOKEN token address :  %s' % token_address)


#  --- CROWDS deploy  ---

with open(os.path.join(ARTIFACTS_PATH, CROWDSALE_NAME + '.json')) as file:
    crowdsale_interface = json.loads(file.read())
Crowdsale = w3.eth.contract(abi=crowdsale_interface['abi'], bytecode=crowdsale_interface['bytecode'])
tx_hash = Crowdsale.constructor(token_address, backend_address).transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
crowdsale_address = tx_receipt.contractAddress

token = w3.eth.contract(address=token_address, abi=token_interface['abi'])
crowdsale = w3.eth.contract(address=crowdsale_address, abi=crowdsale_interface['abi'])

tx_hash = token.functions.transferOwnership(crowdsale_address).transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
tx_hash = crowdsale.functions.claimTokenOwnership().transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print('CROWD SALE contract successfully deployed')
print('CROWD SALE address :  %s' % crowdsale_address)


#  -- save addresses to json file --

addresses = {
    TOKEN_NAME: token_address,
    CROWDSALE_NAME: crowdsale_address
}

with open(os.path.join(ARTIFACTS_PATH, 'Addresses.json'), 'w') as file:
    file.write(json.dumps(addresses))

print('Contract addresses saved to Addresses.json file')

print('==========addresses in deploy=============')
pprint(addresses)
print('=======================')

mint_tokens.py:

import json
import os

from pprint import pprint
from web3 import HTTPProvider, Web3


NODE_URL = os.environ['WEB3_RPC_URL']
w3 = Web3(HTTPProvider(NODE_URL))
TOKEN_NAME = 'MyToken'
CORP_WALLET = os.environ['BACKEND_WALLET']
ARTIFACTS_PATH = os.environ['CONTRACTS_PATH']
MYTOKEN_TOKENS_TO_MINT = 3000 * 10 ** 8

if not os.path.isdir(ARTIFACTS_PATH):
    raise Exception('No contracts directory found')

with open(os.path.join(ARTIFACTS_PATH, TOKEN_NAME + '.json')) as file:
    token_interface = json.loads(file.read())

with open(os.path.join(ARTIFACTS_PATH, 'Addresses.json'), 'r') as addr_file:
    addresses = json.loads(addr_file.read())

print('==========addresses in mint=============')
pprint(addresses)
print('=======================')

mytoken_contract = w3.eth.contract(abi=token_interface['abi'], address=addresses[TOKEN_NAME])
tx_hash = mytoken_contract.functions.mint(CORP_WALLET, MYTOKEN_TOKENS_TO_MINT).transact()
result = w3.eth.waitForTransactionReceipt(tx_hash)

balance = mytoken_contract.functions.balanceOf(CORP_WALLET).call()
print('To wallet %s was been charged %s MYTOKEN tokens' % (CORP_WALLET, balance))

print('MYTOKEN mint was SUCCESS !')

print returns:

==========addresses in deploy=============
{'Crowdsale': '0xe64dd1036374B5CDB1D639205e291BAC5bA0BF59',
 'MyToken': '0x6efD7f1cC59436Ca14dE520FCC5D491238C6e6dD'}
=======================

==========addresses in mint=============
{'Crowdsale': '0xe64dd1036374B5CDB1D639205e291BAC5bA0BF59',
 'MyToken': '0x6efD7f1cC59436Ca14dE520FCC5D491238C6e6dD'}
=======================

dir(mytoken_contract.functions) returns:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_functions', 'abi', 'allowance', 'approve', 'balanceOf', 'burn', 'burnFrom', 'claimOwnership', 'decimals', 'decreaseAllowance', 'increaseAllowance', 'mint', 'name', 'owner', 'pendingOwner', 'symbol', 'totalSupply', 'transfer', 'transferFrom', 'transferOwnership']

MyToken.sol

pragma solidity 0.5.10;

import "./Claimable.sol";
import "../openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "../openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol";


contract MyToken is ERC20, ERC20Burnable, Claimable {
    string public name = "Public Name Token";
    string public symbol = "PNT";
    uint8 public decimals = 8;

    /**
     * @dev Function to mint tokens
     * @param to The address that will receive the minted tokens.
     * @param value The amount of tokens to mint.
     * @return A boolean that indicates if the operation was successful.
     */
    function mint(address to, uint256 value) public onlyOwner returns (bool) {
        require(value > 0);
        _mint(to, value);
        return true;
    }
}  

Claimable.sol

pragma solidity 0.5.10;

/**
 * @title Claimable
 * @dev Claimable contract, where the ownership needs to be claimed.
 * This allows the new owner to accept the transfer.
 */
contract Claimable {
    address public owner;
    address public pendingOwner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
    * @dev The Claimable constructor sets the original `owner` of the contract to the sender
    * account.
    */
    constructor() public {
        owner = msg.sender;
    }

    /**
    * @dev Throws if called by any account other than the owner.
    */
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    /**
    * @dev Modifier throws if called by any account other than the pendingOwner.
    */
    modifier onlyPendingOwner() {
        require(msg.sender == pendingOwner);
        _;
    }

    /**
    * @dev Allows the current owner to set the pendingOwner address.
    * @param newOwner The address to transfer ownership to.
    */
    function transferOwnership(address newOwner) public onlyOwner {
        pendingOwner = newOwner;
    }

    /**
    * @dev Allows the pendingOwner address to finalize the transfer.
    */
    function claimOwnership() public onlyPendingOwner {
        emit OwnershipTransferred(owner, pendingOwner);
        owner = pendingOwner;
        pendingOwner = address(0);
    }
}

error:

Traceback (most recent call last):
  File "./manage.py", line 31, in <module>
    main()
  File "./manage.py", line 27, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.6/site-packages/django/core/management/commands/shell.py", line 92, in handle
    exec(sys.stdin.read())
  File "<string>", line 49, in <module>
  File "/usr/local/lib/python3.6/site-packages/web3/contract.py", line 1151, in transact
    **self.kwargs
  File "/usr/local/lib/python3.6/site-packages/web3/contract.py", line 1454, in transact_with_contract_function
    txn_hash = web3.eth.sendTransaction(transact_transaction)
  File "/usr/local/lib/python3.6/site-packages/web3/eth.py", line 264, in sendTransaction
    get_buffered_gas_estimate(self.web3, transaction),
  File "/usr/local/lib/python3.6/site-packages/web3/utils/transactions.py", line 84, in get_buffered_gas_estimate
    gas_estimate = web3.eth.estimateGas(gas_estimate_transaction)
  File "/usr/local/lib/python3.6/site-packages/web3/eth.py", line 305, in estimateGas
    [transaction],
  File "/usr/local/lib/python3.6/site-packages/web3/manager.py", line 112, in request_blocking
    raise ValueError(response["error"])
ValueError: {'message': 'VM Exception while processing transaction: revert', 'code': -32000, 'data': {'stack': 'c: VM Exception while processing transaction: revert\n    at Function.c.fromResults (/app/ganache-core.docker.cli.js:2:257479)\n    at e.exports (/app/ganache-core.docker.cli.js:53:1406305)', 'name': 'c'}}
1
  • Problem in token contract i guess, becauce 'bytecode' for this token was created for owner and it's not test address that i use locally, so i need to create a new 'bytecode' data with my test address and then to deploy it in ganache. If it will be right way i explain in detail in the answer to the question. – Vadim May 17 at 14:34
0

Rewrote deploy_contract.py and mint_tokens.py scripts.

deploy_contract.py:

import json
import os
from time import sleep
from datetime import datetime as dt, timedelta as td

from web3 import Account, HTTPProvider, Web3

node_url = os.environ['WEB3_RPC_URL']

ARTIFACTS_PATH = './contracts/'

if 'CONTRACTS_PATH' in os.environ:
    ARTIFACTS_PATH = os.environ['CONTRACTS_PATH']

if not os.path.isdir(ARTIFACTS_PATH):
    raise Exception('No contracts directory found')

TOKEN_NAME = 'MyToken'
KEEPER_NAME = 'Keeper'
DAI_NAME = 'DAI'

w3 = Web3(HTTPProvider(node_url))

# Wait for ganache up
tries = 100
while tries > 0:
    if w3.isConnected():
        break
    tries = tries - 1
    sleep(1)

if tries == 0:
    raise Exception('No test net')

w3.eth.defaultAccount = w3.eth.accounts[0]

backend_address = Account.privateKeyToAccount(os.environ['BACKEND_PRIVATE_KEY']).address
w3.eth.defaultAccount = backend_address
print('Default Account address:  %s' % backend_address)


#  ----------- Deploy MY Token -------------------

print('Deploy MY Token')
with open(os.path.join(ARTIFACTS_PATH, TOKEN_NAME + '.json')) as file:
    token_interface = json.loads(file.read())

Token = w3.eth.contract(abi=token_interface['abi'], bytecode=token_interface['bytecode'])
tx_hash = Token.constructor().transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
token_address = tx_receipt.contractAddress
print('MY Token address: %s' % token_address)
print('Deploy MY Token was successful')


#  ----------- Deploy KEEPER -------------------

print('Deploy KEEPER')
with open(os.path.join(ARTIFACTS_PATH, KEEPER_NAME + '.json')) as file:
    keeper_interface = json.loads(file.read())

Keeper = w3.eth.contract(abi=keeper_interface['abi'], bytecode=keeper_interface['bytecode'])

dt1 = dt.now() + td(days=1)
dt2 = dt1 + td(days=365)

tx_hash = Keeper.constructor(token_address, int(dt1.timestamp()), int(dt2.timestamp())).transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
keeper_address = tx_receipt.contractAddress
print('KEEPER address: %s' % keeper_address)
print('Deploy KEEPER was successful')


#  ----------- Deploy DAI Token -------------------

print('Deploy DAI Token')
with open(os.path.join(ARTIFACTS_PATH, DAI_NAME + '.json')) as f:
    dai_interface = json.loads(f.read())

DAI = w3.eth.contract(abi=dai_interface['abi'], bytecode=dai_interface['bytecode'])
tx_hash = DAI.constructor().transact()
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
dai_address = tx_receipt.contractAddress
print('DAI Token address: %s' % dai_address)
print('Deploy DAI Token was successful')


#  ----------- Create file with contracts addresses -------------------

file_name = 'Addresses.json'
print('Create file %s with contracts addresses' % file_name)

addresses = {
    TOKEN_NAME: token_address,
    KEEPER_NAME: keeper_address,
    DAI_NAME: dai_address,
}

with open(os.path.join(ARTIFACTS_PATH, file_name), 'w') as file:
    file.write(json.dumps(addresses))

print('File %s with contracts addresses was created' % token_address)
print('Deploy was completed successful')

mint_tokens.py:

import json
import os

from time import sleep
from web3 import Account, HTTPProvider, Web3


ARTIFACTS_PATH = './contracts/'

if 'CONTRACTS_PATH' in os.environ:
    ARTIFACTS_PATH = os.environ['CONTRACTS_PATH']

if not os.path.isdir(ARTIFACTS_PATH):
    raise Exception('No contracts directory found')

TOKEN_NAME = 'MyToken'
KEEPER_NAME = 'Keeper'
DAI_NAME = 'DAI'
node_url = os.environ['WEB3_RPC_URL']
w3 = Web3(HTTPProvider(node_url))

# Wait for ganache up
tries = 100
while tries > 0:
    if w3.isConnected():
        break
    tries = tries - 1
    sleep(1)

if tries == 0:
    raise Exception('No test net')

if int(w3.version.network) < 5:
    print('network_id not ganache')
    print(w3.version.network)
    exit(1)

backend_address = Account.privateKeyToAccount(os.environ['BACKEND_PRIVATE_KEY']).address
w3.eth.defaultAccount = backend_address
print('Default Account address:  %s' % backend_address)

with open(os.path.join(ARTIFACTS_PATH, 'Addresses.json'), 'r') as addr_file:
    addresses = json.loads(addr_file.read())


#  ----------- Mint DAI Token -------------------

print('Mint DAI Token')
DAI_TOKENS_TO_MINT = 1000 * 10 ** 18

with open(os.path.join(ARTIFACTS_PATH, DAI_NAME + '.json')) as f:
    dai_interface = json.loads(f.read())

DAI = w3.eth.contract(abi=dai_interface['abi'], address=addresses[DAI_NAME])
tx_hash_dai = DAI.functions.mint(backend_address, DAI_TOKENS_TO_MINT).transact()
result = w3.eth.waitForTransactionReceipt(tx_hash_dai)
balance_dai = DAI.functions.balanceOf(backend_address).call() / 1e18
print('To wallet %s was been charged %s DAI tokens' % (backend_address, balance_dai))
print('Mint DAI was SUCCESS !')


#  ----------- Mint MY Token -------------------

print('Mint MY Token')
MY_TOKENS_TO_MINT = 7000 * 10 ** 8

with open(os.path.join(ARTIFACTS_PATH, TOKEN_NAME + '.json')) as f:
    swg_interface = json.loads(f.read())

token = w3.eth.contract(abi=swg_interface['abi'], address=addresses[TOKEN_NAME])
tx = token.functions.mint(backend_address, MY_TOKENS_TO_MINT).buildTransaction({
    'gasPrice': w3.eth.gasPrice,
    'nonce': w3.eth.getTransactionCount(backend_address, 'latest'),
    'from': backend_address
})

tx_signed = w3.eth.account.signTransaction(tx, os.environ['BACKEND_PRIVATE_KEY'])
tx_hash_my = w3.eth.sendRawTransaction(tx_signed.rawTransaction)
balance_my = token.functions.balanceOf(backend_address).call() / 1e8
print('To wallet %s was been charged %s MY tokens' % (backend_address, balance_MY))
print('Mint MY was SUCCESS !')


#  ----------- Mint MY Token to KEEPER -------------------

print('Mint MY Token to KEEPER')
keeper_addr = addresses[KEEPER_NAME]
MY_TOKENS_TO_KEEPER = 10000 * 10 ** 8

tx = token.functions.mint(addresses[KEEPER_NAME], MY_TOKENS_TO_MINT).buildTransaction({
    'gasPrice': w3.eth.gasPrice,
    'nonce': w3.eth.getTransactionCount(backend_address, 'latest'),
    'from': backend_address
})

tx_signed_keeper = w3.eth.account.signTransaction(tx, os.environ['BACKEND_PRIVATE_KEY'])
tx_hash_keeper = w3.eth.sendRawTransaction(tx_signed_keeper.rawTransaction)
balance_keeper = token.functions.balanceOf(keeper_addr).call() / 1e8
print('To Keeper Wallet %s was been charged %s MY tokens' % (keeper_addr, balance_keeper))
print('Mint to Keeper Wallet was SUCCESS !')
print('Mint for ALL Tokens was completed successful')

"KEEPER" it's additional contract for one of my cases. You can ignore him in code.
It's working now:

enter image description here

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.