From 5fb9633985a9563eb75b9125c222dd978307d0d2 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Wed, 26 Aug 2020 20:23:47 -0400 Subject: Publish truffle contracts article --- src/blog/extra-contracts-in-truffle-tests.html | 110 +++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/blog/extra-contracts-in-truffle-tests.html (limited to 'src/blog') diff --git a/src/blog/extra-contracts-in-truffle-tests.html b/src/blog/extra-contracts-in-truffle-tests.html new file mode 100644 index 0000000..719255e --- /dev/null +++ b/src/blog/extra-contracts-in-truffle-tests.html @@ -0,0 +1,110 @@ +title: Extra contracts in Truffle tests +date: August 26, 2020 +--- +

+Sometimes you just need a Truffle contract object. If you're developing a +full-blown Truffle project, this is easy for your "core" contracts, the ones you +keep in your contracts/ directory. Just +artifacts.require and call it a day. But what if you want to +interact with a different contract? +

+ +

+I ran into this while working on meta-tooling around smart contracts, +specifically scripts for running upgrades of proxied contracts. To this end, I +wanted to write unit tests that would use minimal example contracts with +edge cases that might not easily appear "in nature". I want to keep these dummy +contracts in a test resources directory rather than cluttering up the main +contracts/. +

+ +

+Unfortunately, not all parts of the Truffle API are very well documented, but +here's how I got my sweet, sweet Truffle contract wrappers. +

+ +

Getting the contract object

+
+import truffleContract = require('truffle-contract')
+const Contract = truffleContract({
+    abi: artifact.abi,
+    unlinked_binary: artifact.bytecode,
+})
+
+ +where artifact is a standard solc build artifact, say parsed from a +JSON file that truffle compile outputs. The thing to note is that +the bytecode is under an unlinked_binary property. + +

Making it usable

+

+Before you go run off playing with your new Contract, a few last +housekeeping things to take care of. If you try, say, deploying this contract +with Contract.new(), you'll get a nasty error message saying + +

+Error: Contract error: Please call setProvider() first before calling new().
+
+

+ +

+Actually not that nasty since it tells you what you need to do to fix things! +Call Contract.setProvider() with a standard Web3 provider. If you +already have a Web3 instance lying around, + +

+Contract.setProvider(web3.currentProvider)
+
+ +should do. +

+ +

+Last things last, at this point you would start seeing the following: + +

+Error: Contract has no network id set, cannot lookup artifact data. Either set
+the network manually using Contract.setNetwork(), run Contract.detectNetwork(),
+or use new(), at() or deployed() as a thenable which will detect the network
+automatically.
+
+ +I resolved this by calling Contract.setNetwork('development'), +given that this was in a unit tests context and would always execute against the +'development' Truffle network. You might need to pass in a +different network name, or feel free to look into one of the other solutions +suggested by the above error message. +

+ +

+At this point you'll be able to call Contract.new() to get a handle +to a freshly deployed contract, or Contract.at(address) to interact +with an already deployed instance of the contract. +

+ +

TL;DR

+

+Here's the little helper function I wrote to construct these Truffle objects: + +

+import truffleContract = require('truffle-contract')
+
+const makeTruffleContract = (artifact) => {
+  const Contract = truffleContract({
+    abi: artifact.abi,
+    unlinked_binary: artifact.bytecode,
+  })
+  Contract.setProvider(web3.currentProvider)
+  Contract.setNetwork('development')
+
+  return Contract
+}
+
+

+ +

Final note on linking

+

+If you're using any linked libraries in your contract, first deploy the +libraries, then link them with +Contract.link('LibraryName', libraryAddress). +

-- cgit v1.2.3