Introduction
Solidity is a contract-oriented language used to develop on-chain programs ( smart contracts ) on the ethereum network and compiling the contract is especially stressful when you’re setting up your local node to use for deployment and compilation, so these processes are usually skipped in some tutorials out there to prevent them from being bulky. In this tutorial we will show you three ways you can compile and two ways you can deploy your contract.
How to compile
In this section we'll talk about compiling your contract. We will start with using Remix IDE then we’ll talk about using VS Code and the solidity extension.
Remix IDE
This is an online development environment used to create and deploy smart contracts in the browser. It doesn’t need anyone to register before using and all the contracts that are created are stored in the browser’s local storage so your progress would not be lost whenever you exit the site unless you cleared the site’s data in your browser.
To compile your contract using this IDE we start by navigating to the site: https://remix.ethereum.org/
When the site is loaded it should look something like this
But don’t worry if it doesn’t, it means they have modified the user-interface by the time you are reading this article. It should still have the same basic navigation so you’ll be fine reading the article.
Now, In the Remix IDE, click on the contracts folder and select any of the available contracts. If there are no available contracts there, create a new file in the folder named Contract.sol and copy the following code into it.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.11;
contract Contract {
function greet() public view returns (string memory) {
return "hello";
}
}
Then, we navigate to the SOLIDITY COMPILER tab and click on the** Compile <contract name>.sol** button.
And our code is compiled. If you want to extract the ABI or the BYTECODE of the contract we click on Compilation Details and click on the copy icon beside ABI or any other entry that we want to copy.
VisualStudio Code
Now we’ll talk about using VisualStudio Code to compile our contract. We get started by opening the folder that our contract is in, in VS Code. If we don’t have a contract file created yet, we will open any directory that we want our contract in and create a new file Contract.sol and fill it with the contract code below.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.11;
contract Contract {
function greet() public view returns (string memory) {
return "hello";
}
}
Then we go to the extensions tab and install the solidity extension.
Once installed, we click on the contract file to open it.
After that, we right click inside the file and a menu will appear with a list of options to do with your contract. In this menu, click on Solidity: Download compiler and set workspace local path.
This makes a list of versions appear above. Then we select the version that our contract needs, ( in our case it is 0.8.11, specified by the pragma solidity ^0.8.11;
line ).
When it is done downloading the compiler, you’ll see a newly created JavaScript file in your project directory. And if you don’t see it, you should try downloading it again, check your network or check if the extension had an error while it was installing.
Now, right click in the contract file again and this time select Solidity: Compile with configured local solc file. After compiling the contract, you should see a new bin folder like the following:
If you don’t see the bin folder, it means that VS Code did not detect the compiler and it will try downloading the compiler again, so you should wait till it is done. And then, click on the Solidity: Compile with configured local solc file again and this time it should be working.
How to deploy
There are three ways we can deploy a smart contract to the ethereum network and they are
- Remix IDE
- Web3
- EtherJS
Remix IDE
Deploying our contract using the Remix IDE is really straightforward and doesn’t need you to write any deployment script whatsoever. But to deploy our contract to the ethereum network we need to have metamask installed in the browser we are using it in. To deploy the contract, you navigate to the DEPLOY & RUN TRANSACTIONS tab and click on the Deploy button.
This deployment is only limited to our browser, it is used for testing before deploying our contract to the public ethereum network. It is really helpful because we can test the contract’s interface before deploying to the network. It is already well known that deploying a contract to the ethereum network can cost a lot of money depending on how big the contract is, so the fact that we can test out the contract’s interface before deploying it is really a huge benefit.
To deploy our contract to the public network all we do is change the environment option from JavaScript VM to Injected Web3.
This triggers a Metamask window prompting you to connect your wallet. Click on connect wallet to connect and then hit the Deploy button.
After deployment, you will see the contract interface appear under the Deployed contracts dropdown.
We use this interface to interact with our deployed contract. And we can also copy the address of the deployed contract by clicking the copy icon on the top of the interface.
Web3JS
To deploy our contract using this method we start by creating a project folder ( we can call it web3-deployment ) then we install the web3 package and the package for our wallet provider. With the following commands:
npm init -y
npm i web3 wallet-provider
In our terminal/command-prompt in our web3-deployment folder. Also, replace the wallet-provider with the wallet provider of your choice.
Next, we create an index.js file in the web3-deployment folder and begin by copying the following code into it
const provider = require(/* your provider */);
const Web3 = require('web3');
const web3 = new Web3(provider.provider());
Also replacing the /* your provider */
part with the name of your wallet provider.
Then, add the following snippet
let abi = JSON.parse(`[
{
"inputs": [],
"name": "greet",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]`);
The contents of the JSON.parse()
method above is the ABI of the contract we compiled earlier. So you should change it to the abi of the contract you are compiling if you are deploying a different contract.
After that, we add the below to our index.js file
let bytecode = JSON.parse(`{
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "608060405234801561001057600080fd5b5061017c806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063cfae321714610030575b600080fd5b61003861004e565b6040516100459190610124565b60405180910390f35b60606040518060400160405280600581526020017f68656c6c6f000000000000000000000000000000000000000000000000000000815250905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100c55780820151818401526020810190506100aa565b838111156100d4576000848401525b50505050565b6000601f19601f8301169050919050565b60006100f68261008b565b6101008185610096565b93506101108185602086016100a7565b610119816100da565b840191505092915050565b6000602082019050818103600083015261013e81846100eb565b90509291505056fea26469706673582212206a88e5fc2cbb31aca036100317594eef00ab7e3975c29b02a81d4ed4674ed35a64736f6c634300080c0033",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x17C DUP1 PUSH2 0x20 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x2B JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xCFAE3217 EQ PUSH2 0x30 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x38 PUSH2 0x4E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x45 SWAP2 SWAP1 PUSH2 0x124 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x5 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x68656C6C6F000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 MLOAD SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 MSTORE PUSH1 0x20 DUP3 ADD SWAP1 POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xC5 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0xAA JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0xD4 JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F NOT PUSH1 0x1F DUP4 ADD AND SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF6 DUP3 PUSH2 0x8B JUMP JUMPDEST PUSH2 0x100 DUP2 DUP6 PUSH2 0x96 JUMP JUMPDEST SWAP4 POP PUSH2 0x110 DUP2 DUP6 PUSH1 0x20 DUP7 ADD PUSH2 0xA7 JUMP JUMPDEST PUSH2 0x119 DUP2 PUSH2 0xDA JUMP JUMPDEST DUP5 ADD SWAP2 POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP DUP2 DUP2 SUB PUSH1 0x0 DUP4 ADD MSTORE PUSH2 0x13E DUP2 DUP5 PUSH2 0xEB JUMP JUMPDEST SWAP1 POP SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH11 0x88E5FC2CBB31ACA0361003 OR MSIZE 0x4E 0xEF STOP 0xAB PUSH31 0x3975C29B02A81D4ED4674ED35A64736F6C634300080C003300000000000000 ",
"sourceMap": "67:108:0:-:0;;;;;;;;;;;;;;;;;;;"
}`);
This is the compiled bytecode we got from our contract. And again, should be changed when you are deploying a different contract.
Now that we have the bytecode and ABI in our deployment script, we add this to our file.
web3.eth.getAccounts()
.then(accounts => {
return new web3.eth.Contract(abi)
.deploy({ data: bytecode.object }).send({ from: accounts[0], gas: '1000000' });
})
.then(contract => {
console.log('Contract deployed at ', contract.options.address);
})
.catch(error => {
console.error(error);
});
This is what deploys our contract to the ethereum network and how it works is that:
- The 1st line gets the accounts that are provided by our wallet provider,
- In the 2nd line we call a
.then()
method and pass an anonymous function that initializes our contract to it, - Then the 3rd - 4th line creates an instance of
Contract
created with its ABI and then it deploys the contract using the.deploy()
method. This method takes in an object with the data key containing the bytecode object and an optional arguments key that contains an array of the arguments that passed to the contract on deployment. The deploy method initializes a transaction; it is not started until the.send()
method signs the transaction. It specifies the owner of this transaction with the from key and the limit of ethers it can spend deploying the contract with the gas key and returns a promise. - On the 6th line we call another
.then()
method so we can display the contract’s address from the contract object passed from the previous.then()
into the console, and, - Finally, we catch any errors during the contract’s deployment using the
.catch()
method and display it in the console.
We can now test our code in the console:
> node index.js
Contract deployed at 0x844D……………………18
If you see something like this, then it means your contract was successfully deployed, If not then you’ll have to do some bit of debugging :)
EthersJS
Deployment using EthersJS is done in almost the same way it is as Web3JS.
- We start with a new folder,
- Then, we initialize the folder as a node project,
- Next, we install ethers and our wallet provider,
- After that, we create an index.js file,
- In the index.js file we import ethers and our wallet provider then include the ABI and bytecode. The index.js file should look like the following in the beginning:
const provider = require('ganache-cli');
const ethers = require('ethers');
const abi = JSON.parse(`[
{
"inputs": [],
"name": "greet",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]`);
const bytecode = JSON.parse(`{
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "608060405234801561001057600080fd5b5061017c806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063cfae321714610030575b600080fd5b61003861004e565b6040516100459190610124565b60405180910390f35b60606040518060400160405280600581526020017f68656c6c6f000000000000000000000000000000000000000000000000000000815250905090565b600081519050919050565b600082825260208201905092915050565b60005b838110156100c55780820151818401526020810190506100aa565b838111156100d4576000848401525b50505050565b6000601f19601f8301169050919050565b60006100f68261008b565b6101008185610096565b93506101108185602086016100a7565b610119816100da565b840191505092915050565b6000602082019050818103600083015261013e81846100eb565b90509291505056fea26469706673582212206a88e5fc2cbb31aca036100317594eef00ab7e3975c29b02a81d4ed4674ed35a64736f6c634300080c0033",
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x17C DUP1 PUSH2 0x20 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x2B JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0xCFAE3217 EQ PUSH2 0x30 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x38 PUSH2 0x4E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x45 SWAP2 SWAP1 PUSH2 0x124 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x5 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x68656C6C6F000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 MLOAD SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 MSTORE PUSH1 0x20 DUP3 ADD SWAP1 POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xC5 JUMPI DUP1 DUP3 ADD MLOAD DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0xAA JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0xD4 JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F NOT PUSH1 0x1F DUP4 ADD AND SWAP1 POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF6 DUP3 PUSH2 0x8B JUMP JUMPDEST PUSH2 0x100 DUP2 DUP6 PUSH2 0x96 JUMP JUMPDEST SWAP4 POP PUSH2 0x110 DUP2 DUP6 PUSH1 0x20 DUP7 ADD PUSH2 0xA7 JUMP JUMPDEST PUSH2 0x119 DUP2 PUSH2 0xDA JUMP JUMPDEST DUP5 ADD SWAP2 POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP DUP2 DUP2 SUB PUSH1 0x0 DUP4 ADD MSTORE PUSH2 0x13E DUP2 DUP5 PUSH2 0xEB JUMP JUMPDEST SWAP1 POP SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH11 0x88E5FC2CBB31ACA0361003 OR MSIZE 0x4E 0xEF STOP 0xAB PUSH31 0x3975C29B02A81D4ED4674ED35A64736F6C634300080C003300000000000000 ",
"sourceMap": "67:108:0:-:0;;;;;;;;;;;;;;;;;;;"
}`);
After starting off with the index.js file, we create our signer:
const signer = (new ethers.providers.Web3Provider( provider.provider() ) ).getSigner();
This is basically a class instance that represents the accounts and is used to sign transactions in ethers.js.
Then we create a factory:
const factory = new ethers.ContractFactory(abi, bytecode.object, signer);
The ContractFactory sends a transaction to initialize the contract using the ABI and bytecode and then sign the transaction using the signer. The contract isn’t deployed until we call the factory.deploy()
method
factory.deploy()
.then(contract => {
console.log("Contract address is ", contract.address);
return contract.deployed();
})
.then(() => {
console.log("Contract is deployed");
})
.catch((err) => {
console.err(err);
});
This returns a promise with the contract instance. The contract object is obtained before the contract is deployed ( that is when the transaction is initialized ). At the 3rd line of this snippet, we get access to the contract’s address before we use the contract. We have to wait for the contract to be deployed using the contract.deployed();
method. This returns a promise that gets resolved when the contract is deployed so we add another .then()
method. Finally, we did some error handling using the .catch()
method to help with debugging our code.
After running the index.js we should get something like:
Contract address is 0x134027014b698b8A2F6DCEb51B576cDb3f42E550
Contract is deployed
If you got something else, It means there is an error and with the help of our error handling, it should be easier to debug than starting from scratch.
Conclusion
Easy ways to compile and deploy solidity contracts aren’t always talked about in a lot of tutorials. So this article serves as something to read and get you familiar with some of the ways you can compile your contract. And it is my greatest wish that this article did that perfectly.
Happy coding ;)
Top comments (1)
This write up is simple, enriched and awesome. @ghoulkingr am considering blockchain development.. Nice one fam