Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Deployed Contracts have no Network when using async/await #713

Closed
1 task done
jleeh opened this issue Dec 12, 2017 · 29 comments
Closed
1 task done

Deployed Contracts have no Network when using async/await #713

jleeh opened this issue Dec 12, 2017 · 29 comments

Comments

@jleeh
Copy link
Contributor

jleeh commented Dec 12, 2017


Issue

When my contracts are deployed using the truffle migrate --reset command, none of them have a network in the contract JSON even though the contracts are deployed. This causes the unit tests to fail saying the contract hasn't been deployed to the network.

This only happens with the contracts I'm working on as it works fine with different solidity projects.

I've tested the contracts in question on Remix and they deploy fine.

Steps to Reproduce

  • truffle migrate --reset
  • truffle test

Expected Behavior

  • Contract has network assigned in JSON
  • Can test against the contract

Actual Results

  • Contract is deployed after a successful migration:
Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0xf893631620cee218dfdac5a5b118e950b39c411add85ccf5ce0891abc594b451
  Migrations: 0x01afadc80ac64129f3100633b556f1a40a15a9c0
Saving successful migration to network...
  ... 0x0af95e5b3d45c4b37b56287395f0815b4e315d5b3a5bdc0bdcdc5134e444aeb4
Saving artifacts...
Running migration: 2_deploy_linkpool.js
  Replacing Arithmetic...
  ... 0x5667313901deae25a423b2f5c9bc5554ef691491bf1d8d7cd8e71d40948f416d
  Arithmetic: 0x9a3be86baaf7b84f60860b7be146963f5bc8a21b
Saving successful migration to network...
  Deploying PoolStorageLocator...
  ... 0x6ed6122d0851c169df7d156341845ca884e232d455b91825a7e495196d54d6ae
Saving artifacts...
  ... 0x19ca0c89a2c079c8f7dd1c44db88e3520c4ac3781f907db58480e364a31d812f
  PoolStorageLocator: 0xaed391873b70b273393ffc8cce46cfa34aeedc03
  Deploying Node...
  ... 0xbc09e15f761c95071533db92a3900bcdfdac68762bfde30abfa23e89a6434748
  Node: 0xd17e00d08da375b61a10d36a25fb20fde1c8bc29
  Deploying LinkPool...
  ... 0xb6f889a1d8a8b9bd04fc8ecbdf58e58dac33dc014e940c266e15d6e2b181d050
  LinkPool: 0x02961e046dfa5ed124013dac3893cc88af45d371
  Deploying PoolStorage...
  ... 0x2906e140275cab2ffb22d3198070ab325b1796daef5a18e935719d7d8fb3fb95
  PoolStorage: 0xd8962788087a931eee12512267a5263ece988091
  ... 0xe302b366dae309610d063a7bb3748e951fb1d7159ca496b82a29e6d27f26ed4c
  Running step...
  • JSON of contract (Bulk of contract redacted):
  "compiler": {
    "name": "solc",
    "version": "0.4.18+commit.9cf6e910.Emscripten.clang"
  },
  "networks": {},
  "schemaVersion": "1.0.1",
  "updatedAt": "2017-12-12T21:49:28.535Z"
  • Result of unit test:
  1) Contract: LinkPool "before each" hook for "should add a node":
     Error: LinkPool has not been deployed to detected network (network/artifact mismatch)
      at /usr/lib/node_modules/truffle/build/cli.bundled.js:318327:17
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

Environment

  • Operating System: Windows 10 Pro Insider Preview 1709
  • Truffle version: Truffle v4.0.1 (core: 4.0.1), Solidity v0.4.18 (solc-js)
  • Ethereum client: Ganache CLI v6.0.3 (ganache-core: 2.0.2) (Same happens with testrpc, geth)
  • node version: 8.9.3
  • npm version: 5.6.0

ℹ️ I can provide access to the repo where the contracts are stored. I'm reluctant to publicly post the source of them until they're ready.

@olekon
Copy link

olekon commented Dec 22, 2017

I have the same issue.
I also noticed one interesting thing. If you deploy several contracts in one migration file synchronously, i.e

await deployer.deploy(contract1)
await deployer.deploy(contract2)
await deployer.deploy(contract3)

then it updates only contract1 json file.

However, if you rewrite it with Promise chain

deployer.deploy(contract1).then(function() {
  return deployer.deploy(contract2).then(function() {
    return deployer.deploy(contract3).then(function() {
        //do something else
      })
  })
})

then all 3 JSON files are updated with valid addresses

I am using
Truffle v4.0.4 (core: 4.0.4)
Solidity v0.4.18 (solc-js)
EthereumJS TestRPC v4.1.3 (ganache-core: 1.1.3)

@jleeh
Copy link
Contributor Author

jleeh commented Dec 22, 2017

Ahh, I recently migrated to use ES7 in my truffle scripts, so it sounds like that's the root cause?

EDIT: Thanks @olekon, that fixed it. Closing.

@jleeh jleeh closed this as completed Dec 22, 2017
@Oxyaction
Copy link

Had the same issue with code

await deployer.deploy(Registry);
const registry = await Registry.deployed()
await deployer.deploy(Store, registry.address);

when refactored to

deployer.deploy(Registry)
  .then(() => Registry.deployed())
  .then(registry => deployer.deploy(Store, registry.address))

works fine, thanks a lot @olekon

@terraflops1048576
Copy link

terraflops1048576 commented Jan 21, 2018

Is there a reason for this? Is it a bug or intended behavior? I'm experiencing this issue, except that I deploy my contracts across multiple migration scripts; the workaround effectively makes me put everything in one migration script. I'm running Truffle v4.0.5.

EDIT:
Even that doesn't work. I get the same issue; Truffle prints out the addresses of the relevant contracts, but still doesn't store them in the JSON. Running ContractName.deployed() from the console gives the network/artifact mismatch stuff.

@mjhm
Copy link

mjhm commented Jan 24, 2018

Me too. I would swear that this was working in Truffle v4.0.4.

@bulgakovk
Copy link

Had the same issue. Rewriting migrations with Promises fixed my problem.

@nikitaeverywhere
Copy link

nikitaeverywhere commented Mar 13, 2018

This issue needs to be reopened, as the behavior of smart contracts deployment is different when using ES7 async/await syntax, which is totally unexpected. The latest version of Truffle still has this issue.

@macalinao
Copy link

Still an issue!

@nikitaeverywhere
Copy link

nikitaeverywhere commented Apr 4, 2018

@cgewecke should we open a new issue for this case? I will consider it pretty critical but possibly not as simple to fix. Thank you!

the behavior of smart contracts deployment is different when using ES7 async/await syntax, which is totally unexpected. The latest version of Truffle still has this issue.

@cgewecke
Copy link
Contributor

cgewecke commented Apr 4, 2018

@ZitRos Agree - we're actively working on this for the next release. In my view the issue here is semantic confusion caused by changes in JS norms around async. (Migrations was written before await became standard).

I'd rather not re-open just because it's not a 'bug' per se - it's super counter-intuitive behavior relative to await and an interface design problem we'll need to address in the next iteration of the tool.

@nikitaeverywhere
Copy link

nikitaeverywhere commented Apr 4, 2018

@cgewecke, I appreciate your work, thank you for sharing notes on this.

I've checked the truffle code, the issue seems to be very trivial. Looks like we just need to await on the [possible] promise returned from fn, because finish() runs synchronously afterwards.

If you would like I can make a PR fixing this.

@cgewecke
Copy link
Contributor

cgewecke commented Apr 4, 2018

@ZitRos truffle is on Node 6.9.1 which doesn't support await. There may be a simple fix with conventional promises as well but unfortunately to address this in the current release we need to do it in a way that is backward compatible and doesn't assume people are on latest Node. The solution will need tests for the new behavior and will need to accommodate the existing use pattern.

[EDIT - I just realized - even testing this is going to be an issue because of 6.9.1. I think we'd have to target a higher node version in CI or start using babel - that's something that we've been discussing for the next release.]

You're more than welcome to work on a PR - thank you! There's a guide to setting up truffle for development here you might find useful.

@nikitaeverywhere
Copy link

nikitaeverywhere commented Apr 4, 2018

@cgewecke, of course NodeJS 6.9.1 doesn't supports await but promises. await is nothing more than promise.then(...) baked in a prettier way. So I mean here you can put something like:

let result = fn(deployer, options.network, accounts);
if (result && typeof result.then === "function") {
  result.then(() => finish()).catch(() => "...");
} else {
  finish();
}

...instead of calling finish() synchronously after the exported fn finishes. And this will eventually wait until the returned promise (async function) resolves. I'll take a look

@cgewecke
Copy link
Contributor

cgewecke commented Apr 4, 2018

@ZitRos Right, ok excellent.

@nikitaeverywhere
Copy link

nikitaeverywhere commented Apr 4, 2018

@cgewecke, another thing I wanted to catch here right away is that any exceptions thrown in fn doesn't prevent migration from its successful completion, which is also a strange behavior. Do you agree we need to fix this as well? (make the whole migration fail if any exceptions are thrown)

@cgewecke
Copy link
Contributor

cgewecke commented Apr 4, 2018

@ZitRos I'm not certain about this, apologies. I need to look at the code a bit. . . .

@klivin
Copy link

klivin commented Apr 26, 2018

I had removed all references to await in my migration file, converted to promises, but I was still getting the issue where only libraries were getting an address field in their networks...

What fixed it for me was removing all references to async in the file!

@emkman
Copy link
Contributor

emkman commented Apr 26, 2018

Just ran into this same issue, and agree this should be reopened until proper async support is provided either through @ZitRos suggestion or through integration of babel (though I much prefer native node 8 support to babel)

@cgewecke
Copy link
Contributor

Re-opening.

@cgewecke cgewecke reopened this Apr 26, 2018
@cgewecke cgewecke changed the title Deployed Contracts have no Network with a Successful Migration Deployed Contracts have no Network when using async/await Apr 26, 2018
@ahester57
Copy link

ahester57 commented May 4, 2018

This is occurring when running truffle test. I believe it is because of the testing "clean-room" environment.

const Contract1 = artifacts.require("./Contract1.sol");
const Token = artifacts.require('./Token.sol');

module.exports = function(deployer) {
    deployer.deploy(Token).then(() => {
        Token.deployed().then((instance) => {
           return instance;
        }).then((token) => {
	  deployer.deploy(Contract1, token.address);
	});
    })
};

The above works fine when deploying to a chain, but truffle test does not seem to want to wait
for the promises to be resolved.

I can run tests successfully by giving the test the deployed address.

  // Instead of 
  Contract1.deployed()
  // say
  Contract1.at("0x36a8054476cef7d35251fae816f87e09a49e7e43")
  .then((instance) => console.log);

The above will display the contract and tests will pass, but the clean-room environment is thus rendered unused.

@nikitaeverywhere
Copy link

nikitaeverywhere commented May 4, 2018

@ahester57, I guess it may happen because you missed return statement on line 6 of your example code. Does it? (return Token.deployed()....)

@ahester57
Copy link

@ZitRos, Now that is something which simply ruins a night. I had to add returns before Token.deployed() and the second deployer.deploy() for tests to work.

My question: Why? Why does the migration work fine but the test suite not pick these up?

@nikitaeverywhere
Copy link

@ahester57, I guess that internally, in Truffle, deployer.deploy(Token) returns the promise, and Truffle waits until this promise resolves. Once it resolves, Truffle continues its pipeline.

Thus, by returning undefined from then callback the promise resolves almost immediately and doesn't wait until the nested promise resolves.

I guess that the reason of why your code may work during the deployment to the real network is simply because of the delays during deployment (like saving successful migration to the network). Before terminating the process (which in tests happens immediately after promise resolves) transaction gets successfully published to the network.

spirinvladimir added a commit to spirinvladimir/WRIO-InternetOS that referenced this issue May 20, 2018
- added WRIOOS.sol smart contract
- setup webpack with truffle-contract loading JSON(abi)  contract
- check MetaMask existing
- add links to MetaMask download
- extend profile dropdown menu with download MetaMask

Current problem: empty field "networks" at WRIOOS.json (builded smart contract) [trufflesuite/truffle#713]
@roderik
Copy link
Contributor

roderik commented Jul 6, 2018

We were hit by some the same issue, where strangely enough all our contracts deploy correctly, all of them have a completed network section in the json file, except the Migrations.json. We make heavy use of async/await so I happened on this issue and tried to figure out what was wrong.

I started with a clean truffle init and running migrate worked like expected.

var Migrations = artifacts.require('./Migrations.sol');

module.exports = function(deployer) {
  deployer.deploy(Migrations);
};

Now for the async version, we like to use:

var Migrations = artifacts.require('Migrations');

async function performMigration(deployer, network, accounts) {
  await deployer.deploy(Migrations);
}

module.exports = function(deployer, network, accounts) {
  deployer
    .then(() => performMigration(deployer, network, accounts))
    .catch(error => {
      console.log(error);
      process.exit(1);
    });
};

This one does not work, nothing in the networks section.

... 🤦‍♂️ here goes an hour or two of trying things 🤦‍♂️ ...

What I noticed is that it has nothing to do with the async awaits, but with the artifacts.require.
In the original one, it requires ./Migrations.sol, and in our version just Migrations.

var Migrations = artifacts.require('Migrations');

module.exports = function(deployer) {
  deployer.deploy(Migrations);
};

This does not work, and this does work:

var Migrations = artifacts.require('./Migrations.sol');

async function performMigration(deployer, network, accounts) {
  await deployer.deploy(Migrations);
}

module.exports = function(deployer, network, accounts) {
  deployer
    .then(() => performMigration(deployer, network, accounts))
    .catch(error => {
      console.log(error);
      process.exit(1);
    });
};

So, now I only have to figure out why this does work for my other contracts, and not for the Migrations contract.

@cgewecke cgewecke mentioned this issue Jul 6, 2018
5 tasks
@cgewecke
Copy link
Contributor

cgewecke commented Jul 6, 2018

Hi @roderik, thanks for this report!

Will investigate - at a minimum it looks like Truffle is hard-coding the Migrations.sol path internally here. @gnidan actually just picked this out as potentially problematic in a code review of a Migrations re-write referenced in the link above, although I'm not sure I understand why the resolver isn't handling this correctly.

I will add a regression test for this and make sure it's fixed in the new version.

@pstuermlinger
Copy link

pstuermlinger commented Jul 19, 2018

Hello guys,
I'm not sure if my issue fits 100% into this topic. Let's see:

I deploy ContractA via truffle and as a result, the contracts artifact contains its address. However, I've got a 2nd contract which is not deployed by truffle but within the ContracA's constructor:

// ContractA
constructor() public {
    contractB = new ContractB();
}

I prefer this method over single truffle deployments because it's about 1,000,000 gas cheaper (with Ganache - idk why). And as a side effect A owns B which is what I want. However, as a result, the ContracB's artifact DOES NOT contain it's address.
The question is: how can it be passed to Truffle so that Truffle will write it into the artifact?

module.exports = function(deployer, network, accounts) {
  return deployer.deploy(ContractA).then(() => {
    return ContractA.deployed();
  }).then(async (contractAInstance) => {
    const contractB = await contractAInstance.contractB();
    // How to tell truffle about contractB?
  });
};

EDIT: solved

var ContractA = artifacts.require("ContractA");
var ContractB = artifacts.require("ContractB");

module.exports = function(deployer, network, accounts) {
  return deployer.deploy(ContractA).then(() => {
    return ContractA.deployed();
  }).then(async (contractAInstance) => {
    const contractB = await contractAInstance.contractB();
    // Update artifact manually
    ContractB.address = contractB;
    ContractB.transactionHash = ContractA.transactionHash;
  });
};

@levino
Copy link

levino commented Sep 5, 2018

So this works:

module.exports = function(deployer) {
  // You have to use the `then` of the first deployer instance. Do not try to wrap this in async / await
  deployer.then(async () => {
    console.log('Deploying token')
    const token = await deployer.deploy(Token)
    console.log('Deploying crowdsale')
    const crowdsale = await deployer.deploy(
      Crowdsale,
      RATE,
      TREASURY,
      CAP,
      token.address
    )
    console.log('Make crowdsale owner of token')
    return token.transferOwnership(crowdsale.address)
  })
}

So you have to start with one deployer.then call.

The problem is obviously also that the exported function is not expected to return a promise. You can see this because the following actually works:

var Migrations = artifacts.require('Migrations');

module.exports = function(deployer) {
  deployer.deploy(Migrations);
};

While I actually would expect only this to work:

var Migrations = artifacts.require('Migrations');

module.exports = function(deployer) {
  return deployer.deploy(Migrations);
};

So instead of attaching some weird own then property to the deployer object, truffle should expect a promise to be returned from these functions. I think this is a really ugly antipattern here.

@cgewecke
Copy link
Contributor

cgewecke commented Sep 5, 2018

@levino Agree, this has been the source of confusion for a while.

truffle@beta includes a fix for it. The migrations can now be written using async/await and behave more a little more intuitively:

const One = artifacts.require("One");
const Two = artifacts.require("Two");

module.exports = async function(deployer) {
  await deployer.deploy(One);

  const one = await One.deployed();
  const value = await one.value();

  await deployer.deploy(Two, value);
};

@gnidan
Copy link
Contributor

gnidan commented Jan 16, 2019

Looks like this is fixed and released with the rest of v5. Closing this for issue maintenance, but please feel free to let us know and we will re-open. Thank you!

@gnidan gnidan closed this as completed Jan 16, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests