For this tutorial, we are not going to focus on developing the UI, instead, we will be focussing entirely on the core functionality and integration. In the frontend, we will mainly focus on 2 things
- Fetch all the greeting messages from our smart contract and display them in the frontend.
- Create functions to send messages along with the greeting.
Let us start integration with our front end. To test our frontend, start the server using the following command.
Before starting the server, make sure that the current directory is code/wave-portal-starter-boilerplate/
connecting to ethereum with svelte
Now you should see our front end as below on the localhost:5000. If localhost:5000 doesn't work then try http://127.0.0.1:5000/.
You will notice that, by clicking on any of the greetings, nothing is happening. Also, we are not able to see any previous greetings. So, let us add logic to send greetings to our smart contract and fetch all the previous greetings.
Navigate to the App.svelte file under the code/wave-portal-starter-boilerplate/src folder. App.svelte gets rendered on the home page upon server start and thus contains all the functionality to fetch the waves.
Update your contract address on line 10, which logged into the CLI while deploying it.
connecting to ethereum with svelte
const CONTRACT_ADDRESS = 'YOUR_CONTACT_ADDRESS';
const CONTRACT_ADDRESS = 'YOUR_CONTACT_ADDRESS';
const CONTRACT_ADDRESS = 'YOUR_CONTACT_ADDRESS';
Now, paste the below code in the getAllWaves() function on line 12 in App.svelte. This function fetches all the greetings from the blockchain network to our Client.
connecting to ethereum with svelte
async function getAllWaves() {
if (!window.ethereum) {
return;
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
provider
);
const recievedWaves = await wavePortalContract.getAllWaves();
if (!recievedWaves) {
waveList = [];
return;
}
const normalizeWave = (wave) => ({
reaction: wave.reaction,
message: wave.message,
waver: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
});
waveList = recievedWaves
.map(normalizeWave)
.sort((a, b) => b.timestamp - a.timestamp);
console.log('waveList: ', waveList);
return;
}
async function getAllWaves() {
if (!window.ethereum) {
return;
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
provider
);
const recievedWaves = await wavePortalContract.getAllWaves();
if (!recievedWaves) {
waveList = [];
return;
}
const normalizeWave = (wave) => ({
reaction: wave.reaction,
message: wave.message,
waver: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
});
waveList = recievedWaves
.map(normalizeWave)
.sort((a, b) => b.timestamp - a.timestamp);
console.log('waveList: ', waveList);
return;
}
async function getAllWaves() {
if (!window.ethereum) {
return;
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
provider
);
const recievedWaves = await wavePortalContract.getAllWaves();
if (!recievedWaves) {
waveList = [];
return;
}
const normalizeWave = (wave) => ({
reaction: wave.reaction,
message: wave.message,
waver: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
});
waveList = recievedWaves
.map(normalizeWave)
.sort((a, b) => b.timestamp - a.timestamp);
console.log('waveList: ', waveList);
return;
}
Explanation of the code above:
Line 1: Declaring an asynchronous function getAllWaves() that will fetch all the waves from our smart contract.
Line 2: Checking if we are getting an ethereum object in our window, if not we will return null.
Line 6: Getting the provider to access the blockchain data.
Line 7: Creating a local instance of our contract by passing Contract address, Contract abi and provider as an argument.
Line 12: Fetching all the waves from our smart contract by calling getAllWaves() method.
Line 13: If we do not get any waves, then we will return an empty array.
Line 18: Declaring function normalizeWave(), that will destruct the wave.
Line 25: Destructing the recievedWaves(), sorting on the basis of timestamp and assigning these sorted waves to the waveList variable.
We have to import the ABI of our WavePortal contract which enables us to interact with our smart contract. Thus add the following import statements on line 3 in App.svelte.
connecting to ethereum with svelte
import { ethers } from 'ethers';
import WavePortal from './artifacts/contracts/WavePortal.sol/WavePortal.json'
import { ethers } from 'ethers';
import WavePortal from './artifacts/contracts/WavePortal.sol/WavePortal.json'
import { ethers } from 'ethers';
import WavePortal from './artifacts/contracts/WavePortal.sol/WavePortal.json'
Now, start the development server of svelte. You should see an error as shown below.
This is because we are trying to import a json file in our App.svelte and to import a json we need to add an extra plugin rollup-plugin by running the following command.
connecting to ethereum with svelte
yarn add @rollup/plugin-json
yarn add @rollup/plugin-json
yarn add @rollup/plugin-json
Navigate to the rollup.config.js file in code/wave-portal-starter-boilerplate/ directory. This file contains all your configurations for the rollup. Now, in your rollup.config.js file navigate to the plugins array as show below and add json(), on line 60.
connecting to ethereum with svelte
plugins: [
commonjs(),
json(),
...
]
plugins: [
commonjs(),
json(),
...
]
plugins: [
commonjs(),
json(),
...
]
In rollup.config.js file, in code/wave-portal-starter-boilerplate/ directory in order to use json(), we also need to import json() from our newly added plugin, thus add the following import statement in line 7 of rollup.config.js file.
connecting to ethereum with svelte
import json from "@rollup/plugin-json";
import json from "@rollup/plugin-json";
import json from "@rollup/plugin-json";
Now, restart the development server, you should see the frontend server started successfully. Currently, you won't see any greetings on our front end because we don't have one. So, let's add a function to send the greeting.
For that, navigate to code/wave-portal-starter-boilerplate/src/components/SendWave.svelte file. This file will contain logic for sending the wave. Complete the sendWaveReaction() function in line 7 by pasting the code from below. This function will send wave reaction.
connecting to ethereum with svelte
async function sendWaveReaction(reaction, message) {
loading = true;
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
signer
);
const transaction = await wavePortalContract.wave(reaction, message, {
gasLimit: 400000,
});
await transaction.wait();
message = '';
fetchWaves();
loading = false;
} catch (error) {
alert('Error while sending wave', error);
loading = false;
}
}
async function sendWaveReaction(reaction, message) {
loading = true;
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
signer
);
const transaction = await wavePortalContract.wave(reaction, message, {
gasLimit: 400000,
});
await transaction.wait();
message = '';
fetchWaves();
loading = false;
} catch (error) {
alert('Error while sending wave', error);
loading = false;
}
}
async function sendWaveReaction(reaction, message) {
loading = true;
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
signer
);
const transaction = await wavePortalContract.wave(reaction, message, {
gasLimit: 400000,
});
await transaction.wait();
message = '';
fetchWaves();
loading = false;
} catch (error) {
alert('Error while sending wave', error);
loading = false;
}
}
Explanation of the code above:
Line 1: Declaring an asynchronous function sendWaveReaction(), that will send our reaction to our smart contract on the blockchain network.
Line 2: Setting loading variable to true.
Line 4: Declaring a provider variable that contains the read-only abstraction to access the blockchain data.
Line 5: Storing the signer object from our provider in singer variable, that will allow us to sign the transaction.
Line 6: Creating the local instance of our smart contract.
Line 11: Calling the wave() function from our smart contract, with reaction and message as an arguments.
Line 14: Waiting for the transaction to get completed.
Line 15: Resetting the value of message variable after sending the wave.
Line 16: Fetching all the waves again.
Line 17: Setting the loading indicator to false.
Line 19: Showing an alert if something goes wrong.
We also need to add the following import statements in SendWave.svelte file in line 2.
connecting to ethereum with svelte
import { ethers } from 'ethers';
import WavePortal from '../artifacts/contracts/WavePortal.sol/WavePortal.json';
import { ethers } from 'ethers';
import WavePortal from '../artifacts/contracts/WavePortal.sol/WavePortal.json';
import { ethers } from 'ethers';
import WavePortal from '../artifacts/contracts/WavePortal.sol/WavePortal.json';
To interact with our smart contract from our front end, we need to connect our MetaMask wallet to our website. For that, in code/wave-portal-starter-boilerplate/src/components/Wallet.svelte file, complete the connectWallet() function in line 6 by pasting the below code. Wallet.svelte will contain all the logic required for connecting MetaMask wallet to our frontend.
connecting to ethereum with svelte
async function connectWallet() {
walletConnected = false;
const { ethereum } = window;
await ethereum
.request({ method: 'eth_requestAccounts' })
.then((accountList) => {
const [firstAccount] = accountList;
account = firstAccount;
walletConnected = true;
})
.catch((error) => {
walletConnected = false;
connectWalletError = error;
console.log('error connecting wallet');
});
}
async function connectWallet() {
walletConnected = false;
const { ethereum } = window;
await ethereum
.request({ method: 'eth_requestAccounts' })
.then((accountList) => {
const [firstAccount] = accountList;
account = firstAccount;
walletConnected = true;
})
.catch((error) => {
walletConnected = false;
connectWalletError = error;
console.log('error connecting wallet');
});
}
async function connectWallet() {
walletConnected = false;
const { ethereum } = window;
await ethereum
.request({ method: 'eth_requestAccounts' })
.then((accountList) => {
const [firstAccount] = accountList;
account = firstAccount;
walletConnected = true;
})
.catch((error) => {
walletConnected = false;
connectWalletError = error;
console.log('error connecting wallet');
});
}
Explanation of the above code:
Line 1: Declaring an asynchronous function connectWallet().
Line 2: Setting walletConnected variable to false.
Line 3: Getthing an ethereum object from our window.
Line 4: Calling ethereum.request({ method: 'eth_requestAccounts' }) that will give us the accounts of the connected wallet.
Line 7: Getting first account from an array of all the accounts
Line 8: Assining account variable the value of first account.
Line 9: Setting walletConnected variable to true.
Line 12: Setting walletConnected variable to false, if encountered an error while connecting wallet.
Now, restart the server if needed and you should see a MetaMask popup on clicking the Connect MetaMask button. After connecting we'll be able to successfully send greetings, waves as well as fetch all the greetings.
Now, we have our smart contract running successfully on a local node, so let's deploy it on live TestNet.
Deploying to Ropsten TestNet
We'll be deploying to the Ropsten test network in this tutorial. For that, update your MetaMask wallet to connect to the Ropsten network and send yourself some test ethers from
this test faucet.
We can get access to Ropsten by signing up with QuickNode.
We’ll sign up for a
free trial QuickNode and copy the HTTP PROVIDER URL that we’ll need later to connect to the blockchain.
Once you've created the node, you'll get a URL that looks like this
To deploy to the test network we need to update our hardhat config with a piece of additional network information. One thing is to set the private key of the wallet we'll be deploying from. You can export your private key from MetaMask by clicking menu > Account details > Export private key.
Now add networks property in hardhat.config.js in line 24 under code/wave-portal-starter-boilerplate/ directory as shown below.
connecting to ethereum with svelte
module.exports = {
defaultNetwork: "hardhat",
paths: {
artifacts: './src/artifacts',
},
networks: {
hardhat: {},
ropsten: {
url: "<YOUR_QUICKNODE_URL_HERE>"
accounts: [`0x${your-private-key}`]
}
},
solidity: "0.8.4",
};
module.exports = {
defaultNetwork: "hardhat",
paths: {
artifacts: './src/artifacts',
},
networks: {
hardhat: {},
ropsten: {
url: "<YOUR_QUICKNODE_URL_HERE>"
accounts: [`0x${your-private-key}`]
}
},
solidity: "0.8.4",
};
module.exports = {
defaultNetwork: "hardhat",
paths: {
artifacts: './src/artifacts',
},
networks: {
hardhat: {},
ropsten: {
url: "<YOUR_QUICKNODE_URL_HERE>"
accounts: [`0x${your-private-key}`]
}
},
solidity: "0.8.4",
};
To deploy our smart contract to the Ropsten network, run the following command in code/wave-portal-starter-boilerplate/ directory.
connecting to ethereum with svelte
npx hardhat run scripts/deploy.js --network ropsten
npx hardhat run scripts/deploy.js --network ropsten
npx hardhat run scripts/deploy.js --network ropsten
On successful deployment, you should see the following output in your terminal.
connecting to ethereum with svelte
WavePortal deployed to: 0x4f5F98f3696e1dDc107fd786d252D6Ff8D351B6b
WavePortal deployed to: 0x4f5F98f3696e1dDc107fd786d252D6Ff8D351B6b
WavePortal deployed to: 0x4f5F98f3696e1dDc107fd786d252D6Ff8D351B6b
Once your contract is deployed you should be able to start interacting with it. Now you should be able to view your live Contract on
Etherscan TestNet Explorer.