- Published on
How to Connect an Ethereum dApp UI to an Existing Deployed Contract
- Authors
- Name
- Yair Mark
- @yairmark
Basic dApp Structure
When working with an Ethereum app like Cryptokitties for example, from a conceptual level there are two parts:
- The deployed blockchain code AKA smart contracts.
- The UI / server that interacts with those deployed contracts using some web3 library.
Problem - How Do We Link the UI/Server to Existing Contracts
Let us pretend we have the model where we use a UI and not a server to connect to the contracts - we use web3.js.
One issue that is not clear with Truffle is: how do you connect your UI to existing smart contracts without doing a new migration?
Say for example we have one mono repo with both the contract code and the UI as below:
<projectRoot>
-> contracts
-> build
-> contracts
-> Contract1.json
...
-> ContractN.json
-> contracts
-> Contract1.sol
-> ...
-> ContractN.sol
-> ui
-> package.json
-> public
-> src
-> index.js
-> App.js
...
-> contracts [symlink to '../../contracts/build/contracts']
In the drizzle tutorial they recommend either symlinking to the contracts build directory (e.g. ln -s ../../contracts/build/contracts contracts from inside the ui/src directory) if you are using the same project or to export the contracts in your truffle-config.js as described here.
The problem is once you have migrated your contracts how do you run the UI from anywhere and refer to the same deployed contracts?
Solutions
Option 1 - Save Migration Build Directory
After quite a bit of searching it turns out there is a solution:
- When you first migrate the contracts using
truffle migrate --environment targetEnvironmentNametruffle will compile the contracts (under<contractsRoot>/build/contracts) and deploy those. - In the compiled contract
JSONthere will be a networks section that includes something like the below:
"networks": {
"3": {
"events": {},
"links": {},
"address": "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d",
"transactionHash": "0x1cdc7bdb4fedc48379341ef56fcfbc27a8c65723d236463e39b576c862e6fd1e"
},
"5777": {
"events": {},
"links": {},
"address": "0xE94327D07Fc17907b4DB788E5aDf2ed424adDff6",
"transactionHash": "0xe84e8de06546c693a6a9aef255a56ecc5d93f2ee0c2c6fd74bd0d209bff591fb"
}
},
You need to save the build directory that was generated.
- Specifically note the
addressandtransactionHashfields for the network id you want.
- Specifically note the
In your UI you now need to symlink to
contractsinside the savedbuilddirectory.
In the above:
- The network id of
3is Ropsten. The closer the number is to1the closer it is to Ethereum mainnet so1should be mainnet. - The network id of
5777is Ganache. - You may have more or less
networkobjects in your array depending on what yourtruffle-config.jslooks like.- You should have the same number of
networksas you have in yourtruffle-config.js. - For the sake of your UI you only need the network that you intend the UI to connect to.
- You should have the same number of
Now when you run your UI and switch to Ropsten in MetaMask you will be pointing to the correct contract address you originally deployed.
Option 2 - Use Deployed address and transactionHash
If you do not have your build directory from your original migration and your migration process you can then do the following:
- Run
truffle compile.- This will give you a fresh new
builddirectory. - The
networkssection will not be exactly correct.
- This will give you a fresh new
- Go to the
networkssection of the contract/s you need and change theaddressandtransactionHashto use the ones from your deployed contracts.- Ensure that you use the correct contract address corresponding to the correct network number e.g.
2is Ropsten.
- Ensure that you use the correct contract address corresponding to the correct network number e.g.
- Save this
builddirectory with the modified files somewhere. - Symlink the
build/contractsfolder into yourui/srcfolder.
Make this Process Less Cumbersome for Dev vs Testnet/Mainnet
To help make this process a bit easier if you use a makefile or something similar in the root of your project you can easily write a script to change the symlink on the fly depending on whether you are deving locally or this is being built for Ropsten or Mainnet. For example with the project structure above I would have a makefile as follows:
symlink-to-ropsten: ## Update the UIs contract symlink to point to the newest Ropsten build
cd ui/src && (rm contracts || true) && ln -s ../../contracts/deploy/buildRopsten-11-Apr-2019/contracts contracts && cd -
symlink-to-local: ## Update the UIs contract symlink to point to the newest Local build
cd ui/src && (rm contracts || true) && ln -s ../../contracts/build/contracts contracts && cd -