Personal Notes-Solidity-1.0.2
Lesson 3
In order to interact with a contract, you need 2 things
- Address
- ABI -> Application Binary Interface
How do I inherit?
contract inheritedContract is ParentContract {
}
how to inherit a function, we use override.
parent function => function functionName(datatype argument) public **virtual** {
}
child function => function functionName( dataType Argument) public **override** {
}
Lesson 4
Sending ETH Through a function and Reverts.
Remix Fund Me
The following contract will do the following
- Get funds from users.
- Withdraw funds (only who publishes the contract)
- Set a minimum funding value in USD
SideNote:
- Smart Contract can hold funds just like how wallets can.
- payable keyword allows the function to deal with currency
msg.value determines the value send to the payable function
to validate something we can add middleware in a contract for example `` require(msg.value >1e18, "Didn't send the required amount" );
- 1e18 => 1 10 *18 wei => 1 eth => money transaction is done in terms of wei.
The second parameter is the fallback msg incase the condition is not met.
reverts means that in case the middleware fails, any transaction done before the middleware will be reset.
// SPDX-License-Identifier: MIT
pragma soidity ^0.8.8;
contract FundMe {
function fund() public payable { //send money to the owner
}
function withdraw(){ //only who publishes the contract can withdraw the funds
}
}
In solidity, we cannot use fetch to call an API, as a blockchain is a deterministic system we cannot use API calls. If a given node calls an API and its data changes for different nodes, nodes will have different values, which defeats the purpose. for that reason, we use an oracle. An oracle is a distributed network that gets values from different points of interest, combines the value, and sends it to the blockchain. if the node fails to send the value, other nodes in the oracle will forward the respective values, since an oracle does not have a single point to send data, it is not centralized.
To use an oracle we need to use an interface. an interface basically has a list of function calls whose code is not visible, in other words, an interface is an abstraction of a list of functions, which you use to access the functionality of the oracle. Think of it as ABI's
Steps to use an oracle.
- Find the address of the oracles functionality you want to use.
- import directly from GitHub.
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol "
- AggregatorV3Interface( Address Of Contract ).functionality()
(, int256 price,,,) = AggregatorV3Interface( **Address Of Contract**).latestRoundData();
// ETH price in USD
The value of price should have 18 decimal places because all value is in ref, of Wei. 1Eth -> 1e18 -> 110**18. So to get it to 18 dec places we multiply the result by `` price1e18 ``
- So now the price we got is in 18decimal places (ethPrice), and the msg.value by default is in 18decimal (ethAmount) places.
Since the price we get is in int and msg.value is uint we need to do type casting. => uint256(price *1e10).
(ethPrice * ethAmount)/1e18
any and all monitary values should be in the syntax of 1e18
Arrays and Structs - 2
- msg.sender is the address of whoever calls that function in a contract
- msg.value is the amount of eth the sender sends.
address[] public funder; //array of addresses
mapping(address=>uint256) public addressToAmountFunded;
//holds the list of addresses that payed
funder.push(msg.sender)
//holds the list of addresses against the amount paid.
addressToAmountFunded[msg.sender]=msg.value
Sidenote: If a function works with money, add a keyword called payable.
Library
A library is somewhat like a contract, but does not have state variables, and does not deal with money. So basically you can add functions that do not deal with state variables and currency.
A library function should be internal.
It can be called as a method in another contract.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol ";
library PriceConverter {
function getPrice() **internal** view returns(uint256){
}
}
- we import PriceConverter to the contract we want to use it in.
- we assign PriceConverter to a datatype
using PriceConverter for uint256;
- we can use the libraries on any state variables having the data type mentioned above, in this case uint256
msg.value.getConversionRate()
safeMath, Overflow checking, and the "unchecked" keyword
Different ways to send money
- Transfer : payable(owner_Address).transfer(amount-to-transfer) this can be msg.value or the amount present in the contract. (address(this).balance). It automatically revets if the transaction fails.
- Send: send returns a boolean, based on if the transaction failed. `` bool sendSuccess=payable(msg.sender).send(address(this).balance); require(sendSuccess,"Send failed")
- Call: Read more about it. This is standard.
(bool callSuccess,)= payable(msg.sender).call{value: address(this).balance}(""); require(callSuccess,"Call failed")
Constructor
we use this to set the address of the user as the owner of the contract.
address owner public;
constructor(){
owner =msg.sender
}
Modifiers
A modifier is like a middleware, whose value is associated with a keyword. so you don't have to copy-paste repeated code multiple times.
- The underscore basically means where the following code is executed. since its after a condition, we execute the code after the condition.
modifier onlyOwner {
require(msg.sender == owner, "Sender is not owner");
_;
}
function withdraw() public **onlyOwner** {
//some code
}
Advanced Solidity Concepts
Keep in mind 2 keywords that affect gas consumption
constant : keywords assigned with constant keyword don't change values
immutable : Both immutable and constant are keywords that can be used on state variables to restrict modifications to their state. The difference is that constant variables can never be changed after compilation, while immutable variables can be set within the constructor.
Advanced Solidity Custom Errors
//space outside the contract
contract ContractName {
if(msg.sender != i_owner) {
revert NotOwner();
}
}
fallback and receive
- receive is a default function that gets triggered when token/ money is send to the contract.
- fallback is a default function that gets triggered when information/data is send to the contract
receive() external payable {
fund() //routes incase of accidental sending of money
}
fallback() external payable {
fund() //routes incase of accidental sending of money
}
Ether.js
6:47:01
Ganache & Networks
npm i solc
- Solc allows you to compile solidity contracts in our code
- Solc allows you to compile solidity contracts separately
How to compile our contract?: Run the following command=>npm solcjs --bin
- --bin => to get the binary
- --abi => to get the abi
- --include-path node_modules/ => to get the node modules
- --base-path . -o . SimpleStorage.sol=> the "." means the current folder, the "-o" means to output the binary in the "." current directory and "SimpleStorage.sol" is the current solidity file to compile
npm solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol
- 2 files will be outputted => called simpeStorage_sol_simpleStorage.abi and simpeStorage_sol_simpleStorage.bin
"scripts":{
"compile":"yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol"
}
Ganache & Networks
In order to execute and test the code in a blockchain, we need a fake blockchain.
- Hardhat
Ganache ~ a virtual machine. it's basically a fake blockchain that we can use to test/deploy/run code locally
install Ganache application. > quickstart Etherium
- rpc-url is an entry point to connect to a blockchain.