diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blog/extra-contracts-in-truffle-tests.html | 110 |
1 files changed, 110 insertions, 0 deletions
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 +--- +<p> +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 <code>contracts/</code> directory. Just +<code>artifacts.require</code> and call it a day. But what if you want to +interact with a different contract? +</p> + +<p> +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 +<code>contracts/</code>. +</p> + +<p> +Unfortunately, not all parts of the Truffle API are very well documented, but +here's how I got my sweet, sweet Truffle contract wrappers. +</p> + +<h3>Getting the contract object</h3> +<pre> +import truffleContract = require('truffle-contract') +const Contract = truffleContract({ + abi: artifact.abi, + unlinked_binary: artifact.bytecode, +}) +</pre> + +where <code>artifact</code> is a standard solc build artifact, say parsed from a +JSON file that <code>truffle compile</code> outputs. The thing to note is that +the bytecode is under an <code>unlinked_binary</code> property. + +<h3>Making it usable</h3> +<p> +Before you go run off playing with your new <code>Contract</code>, a few last +housekeeping things to take care of. If you try, say, deploying this contract +with <code>Contract.new()</code>, you'll get a nasty error message saying + +<pre> +Error: Contract error: Please call setProvider() first before calling new(). +</pre> +</p> + +<p> +Actually not that nasty since it tells you what you need to do to fix things! +Call <code>Contract.setProvider()</code> with a standard Web3 provider. If you +already have a Web3 instance lying around, + +<pre> +Contract.setProvider(web3.currentProvider) +</pre> + +should do. +</p> + +<p> +Last things last, at this point you would start seeing the following: + +<pre> +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. +</pre> + +I resolved this by calling <code>Contract.setNetwork('development')</code>, +given that this was in a unit tests context and would always execute against the +<code>'development'</code> 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. +</p> + +<p> +At this point you'll be able to call <code>Contract.new()</code> to get a handle +to a freshly deployed contract, or <code>Contract.at(address)</code> to interact +with an already deployed instance of the contract. +</p> + +<h3>TL;DR</h3> +<p> +Here's the little helper function I wrote to construct these Truffle objects: + +<pre> +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 +} +</pre> +</p> + +<h3>Final note on linking</h3> +<p> +If you're using any linked libraries in your contract, first deploy the +libraries, then link them with +<code>Contract.link('LibraryName', libraryAddress)</code>. +</p> |