4

I want to deploy a contract to Ethereum using Metamask and via a custom made UI. I have a problem with async calls and Metamask. I have attempted the code below which fails. Has anyone any sample code to deploy a contract using Metamask?

var contractCompiled;
    web3.eth.compile.solidity(contractstring, function (err, res) {
      //do stuff
      if(!error) {
          contractCompiled=result;
          console.log(result)
        }
      else
          console.error(error);
      });

//var contractCompiled = web3.eth.compile.solidity(contractstring);
var code = contractCompiled.code;
var abi = contractCompiled.info.abiDefinition;
var contract = web3.eth.contract(abi);
contract.new(
   {
     from: web3.eth.accounts[0],
     data: code,
     gas: '4700000'
   }, function (e, contract){
      console.log(e, contract);
      if (typeof contract.address !== 'undefined') {
         console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
       }
});

The error is that it fails to run the compiler and also the running sequence is not correct because the compiler tries to run after the other statements.status("compiling");

EDIT


    document.getElementById("compile-output").value = "";
    var result = compiler.compile(getSourceCode(), optimize);
    var stringResult = JSON.stringify(result);
    document.getElementById("compile-output").value = stringResult;

    var bytecode = result.contracts.greeter.bytecode;
    var abi = result.contracts.greeter.interface;

    web3 = new Web3(web3.currentProvider);

    // Our future code here..
     web3.eth.defaultAccount = web3.eth.accounts[0];

    var contract = web3.eth.contract(abi);
    contract.new(
       {
         from: web3.eth.accounts[0],
         data: bytecode,
         gas: '4700000'
       }, function (e, contract){
          console.log(e, contract);
          if (typeof contract.address !== 'undefined') {
             console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
           }
    });

The above code works to the point of the contract being defined but it seems Metamask does not deploy it. I checked Metamask and it looks OK. I checked the console and that all looks fine too. Any ideas what this could be?


p {eth: n, abi: "[{"constant":true,"inputs":[],"name":"greet","outp…"string"}],"payable":false,"type":"constructor"}]", new: ƒ}
abi
:
"[{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]"
eth
:
n {_requestManager: s, getBalance: ƒ, getStorageAt: ƒ, getCode: ƒ, getBlock: ƒ, …}
new
:
ƒ ()
__proto__
:
Object

9
  • in web3.eth.compile.solidity's callback function parameter's are "err & res", while condition is being applied on "error & result"
    – kherwa
    Aug 29, 2018 at 10:51
  • Thanks for spotting that! I changed that now. I still get an error - now it says Internal JSON RPC error, code -32603. But thanks for spotting the error/err mistake. Aug 29, 2018 at 11:00
  • This is because, eth_compileSolidity github.com/ethereum/wiki/wiki/… has been deprecated, you have to install solc compiler either as npm package or binaries to compile your contract.
    – kherwa
    Aug 29, 2018 at 11:07
  • 1
    Had written a blog on compiling and deploying a contract blog.kherwa.com/2018/05/22/…, that might help.
    – kherwa
    Aug 29, 2018 at 11:13
  • I seemed to recall that actually. I did install solc anyway. But I am trying to compile the contract dynamically from a UI - ie a user enters a simple contract, and the UI then compiles it. The UI has additional functions so we cannot use remix for this. Aug 29, 2018 at 11:18

2 Answers 2

3

Following code is working fine:

    <script>
        window.onload = function() {

            if (typeof BrowserSolc == 'undefined') {
                console.log("You have to load browser-solc.js in the page.  We recommend using a <script> tag.");
                throw new Error();
            }

            const source = "pragma solidity ^0.4.0; contract Mortal {     /* Define variable owner of the type address */     address owner;  /* This function is executed at initialization and sets the owner of the contract */    function Mortal() { owner = msg.sender; }    /* Function to recover the funds on the contract */    function kill() { if (msg.sender == owner) selfdestruct(owner); }}contract Greeter is Mortal {    /* Define variable greeting of the type string */    string greeting;    /* This runs when the contract is executed */    function Greeter(string _greeting) public {        greeting = _greeting;    }    /* Main function */    function greet() constant returns (string) {        return greeting;    }}";          

            BrowserSolc.loadVersion("soljson-v0.4.24+commit.e67f0147.js", function(compiler) {

            optimize = 1;
            var result = compiler.compile(source, optimize);
            // Bytecode
            var bytecode = result.contracts[':Greeter'].bytecode;
            // ABI
            var abi = result.contracts[':Greeter'].interface;

            var MyContract = web3.eth.contract(JSON.parse(abi));

            var myContractInstance = MyContract.new({
                from:web3.eth.accounts[0],
                data: "0x"+bytecode, 
                gas: '4700000'}, function(err,contract){
                    if(err) {
                        console.log(err)
                        return
                    }
                    if(contract.address) {
                     console.log("MyContract deployed at address :" + contract.address)
                    } else {
                     console.log("MyContract is waiting to be mined at transaction hash:" + contract.transactionHash);
                    }
                })

            });
        }
    </script>

I think issue is here :

var bytecode = result.contracts.greeter.bytecode;
var abi = result.contracts.greeter.interface;
var contract = web3.eth.contract(abi);

it should be:

var bytecode = result.contracts[':Greeter'].bytecode;
var abi = result.contracts[':Greeter'].interface;
var contract = web3.eth.contract(JSON.parse(abi));

Contract is not mined instantly, so, deployed address might not show but transaction hash will be available, so in else condition log transaction hash:

if (typeof contract.address !== 'undefined') {
             console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
           } 
else {
console.log('Contract is waiting to be mined at transaction hash:' + contract.transactionHash);
}
7
  • That all works. That was brilliant work by you. Actually I changed only "0x" for the bytecode and the JSON.parse in my code following your example and that was enough to get it working. Aug 30, 2018 at 8:24
  • On the contract address part, is there anyway to pause this until the contract.address field is populated? I get a timeout basically and the flow stops. It deploys OK but the address is not known. If the txn hash is recorded, can the contract address be obtained from that in a follow up event? Aug 31, 2018 at 12:24
  • When contract is deployed the callback function will get fired two times, first when transaction hash is available and second time when contract address is available. So, you can encapsulate contract deploying code in a Promise and resolve that Promise when contract address is available.
    – kherwa
    Aug 31, 2018 at 13:40
  • Are you familiar with how to calc the contract address - I understand this can be done using Web3 and some key data. I saw references to RLP etc in various formats. Because deployment can be slow (minutes or even hours) then I would prefer to store the calc address and then confirm later it is actually correct. Aug 31, 2018 at 13:44
  • You can store transaction hash , and can get its transaction receipt with web3.eth.getTransactionReceipt which will only be available when transaction has been mined
    – kherwa
    Aug 31, 2018 at 14:11
2

seems @kherwa 's answer doesn't work on 2022, July.

this is my solution:

async function loadWeb3() {
  if (window.ethereum) {
    window.web3 = new Web3(window.ethereum);
    window.ethereum.enable();
  }
}


async function getCurrentAccount() {
  const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
  return accounts[0];
}

async function deploy(){
  await loadWeb3();

  let abi = '<%= raw @contract.contract_template.abi %>'
  let binary_code = "<%= raw @contract.contract_template.binary_code %>"

  var myContract = new web3.eth.Contract(JSON.parse(abi));
  const account = await getCurrentAccount();

  myContract
    .deploy({
      data: "0x"+ binary_code,
    })
    .send(
      {
        from: account,
      },
      function(error, transactionHash){
        if(error){
          console.error(error)
        }else{
          console.info(transactionHash)
        }
      }
    )

    .on('error', function(error){ console.error(error) })
    .on('transactionHash', function(transactionHash){
      console.info(transactionHash)
    })
    .on('receipt', function(receipt){
       console.log(receipt.contractAddress) // contains the new contract address
    })
    .on('confirmation', function(confirmationNumber, receipt){
      console.info("== confirmation", confirmationNumber, receipt) }
    )
    .then(function(newContractInstance){
      console.log(newContractInstance.options.address) // instance with the new contract address
    })
}

refer to: https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#deploy

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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