m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Chrzanowski <marcin.j.chrzanowski@gmail.com>2020-08-26 20:23:47 -0400
committerMarcin Chrzanowski <marcin.j.chrzanowski@gmail.com>2020-08-26 20:23:47 -0400
commit5fb9633985a9563eb75b9125c222dd978307d0d2 (patch)
tree76a15b02bc11441069e95fc10f96db20c9f7a718
parent2d73ca9e187835a6364abd8c0a623002efae4d45 (diff)
Publish truffle contracts article
-rw-r--r--src/blog/extra-contracts-in-truffle-tests.html110
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>