IntroductionSending ETH Through a Function & RevertsOperationFields in a TransactionMore on v,r,spayablemsg.value & Other global keywordsrequirerevertChainlink & OraclesWhat is a blockchain oracle?What is the oracle problem?ChainlinkChainlink Price Feeds (Data Feeds)Chainlink VRFChainlink KeepersChainlink API CallsImporting Tokens into your MetamaskRequest and Receive Chainlink ModelInterfaces & Price FeedsChainlink Price Feeds (Data Feeds)Chainlink GitHubInterfaceImporting from GitHub & NPMChainlink NPM PackageFloating Point Math in SoliditytupleBasic Solidity: Arrays & StructsLibrariesLibrarySolidity-by-example LibrarySafeMath, Overflow Checking, and the "unchecked" keywordOpenzeppelin Safemathunchecked vs. checkedBasic Solidity: For LoopFor LoopSending ETH from a ContractTransfer, Send, Callthis keywordBasic Solidity: ConstructorConstructorBasic Solidity: ModifiersDouble equalsModifierTestnet DemoDisconnecting MetamaskAdvanced SolidityImmutable & ConstantCustom ErrorsReceive & Fallback Functions
Introduction
Create smart contract fill that request:
- Get funds from users
- Withdraw funds
- Set a minimum funding value in USD
Sending ETH Through a Function & Reverts
Operation
- create file
FundMe.sol
and program the code
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; contract FundMe { function fund() public payable { require(msg.value > 1e18, "Don't send enough!"); } }
- Complie FundMe.sol
- Deploy FundMe.sol
- Set value at least 1 eth, and call fund function
Fields in a Transaction
An Ethereum transaction refers to an action initiated by an externally-owned account.
A submitted transaction includes the following information:
recipient
– the receiving address (if an externally-owned account, the transaction will transfer value. If a contract account, the transaction will execute the contract code)
signature
– the identifier of the sender. This is generated when the sender's private key signs the transaction and confirms the sender has authorized this transaction
nonce
- a sequencially incrementing counter which indicate the transaction number from the account
value
– amount of ETH to transfer from sender to recipient (in WEI, a denomination of ETH)
data
– optional field to include arbitrary data
gasLimit
– the maximum amount of gas units that can be consumed by the transaction. Units of gas represent computational steps
maxPriorityFeePerGas
- the maximum amount of gas to be included as a tip to the validator
maxFeePerGas
- the maximum amount of gas willing to be paid for the transaction (inclusive ofbaseFeePerGas
andmaxPriorityFeePerGas
)
More on v,r,s
v
, r
, s
are the values for the transaction's signature. They can be used as in Get public key of any ethereum accountA little more information,
r
and s
are outputs of an ECDSA signature, and v
is the recovery id.payable
make a function payable with ethereum or any other need of blockchain currency.
msg.value & Other global keywords
msg.value
(uint
): number of wei sent with the messagerequire
require basically means to demand something before availing the service to the users.
revert
REVERT
will still undo all state changes, but it will be handled differently than an “invalid opcode” in two ways:- It will allow you to return a value.
- It will refund any remaining gas to the caller.
Chainlink & Oracles
What is a blockchain oracle?
Blockchain oracles are entities that connect blockchains to external systems, thereby enabling smart contracts to execute based upon inputs and outputs from the real world.
What is the oracle problem?
The oracle problem revolves around a very simple limitation—blockchains cannot pull in data from or push data out to any external system as built-in functionality.
Chainlink
Chainlink Price Feeds (Data Feeds)
Chainlink VRF
chainlink verifiable random function(vrf) is a way to get provably a random number into our smart contract.
Chainlink Keepers
decentralized event driven execution, if you have a decentralized application that needs to run at specific times, or after specific events or trigger, chainlink keepers are the solution to this.
chainlike keepers are chainlike nodes that listen to a registration contract for different events that you specify to fire.
Chainlink API Calls
function requestVolumeData() public returns (bytes32 requestId) { Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector); // Set the URL to perform the GET request on req.add('get', 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD'); // Set the path to find the desired data in the API response, where the response format is: // {"RAW": // {"ETH": // {"USD": // { // "VOLUME24HOUR": xxx.xxx, // } // } // } // } // request.add("path", "RAW.ETH.USD.VOLUME24HOUR"); // Chainlink nodes prior to 1.0.0 support this format req.add('path', 'RAW,ETH,USD,VOLUME24HOUR'); // Chainlink nodes 1.0.0 and later support this format // Multiply the result by 1000000000000000000 to remove decimals int256 timesAmount = 10**18; req.addInt('times', timesAmount); // Sends the request return sendChainlinkRequest(req, fee); }
Importing Tokens into your Metamask
Request and Receive Chainlink Model
Interfaces & Price Feeds
Chainlink Price Feeds (Data Feeds)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface internal priceFeed; /** * Network: Goerli * Aggregator: ETH/USD * Address: 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e */ constructor() { priceFeed = AggregatorV3Interface(0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e); } /** * Returns the latest price */ function getLatestPrice() public view returns (int) { ( /*uint80 roundID*/, int price, /*uint startedAt*/, /*uint timeStamp*/, /*uint80 answeredInRound*/ ) = priceFeed.latestRoundData(); return price; } }
Chainlink GitHub
Interface
- cannot have any functions implemented
- can inherit from other interfaces
- all declared functions must be external
- cannot declare a constructor
- cannot declare state variables
interface ICounter { function count() external view returns (uint); function increment() external; }
Importing from GitHub & NPM
Chainlink NPM Package
Installation
# via Yarn $ yarn add @chainlink/contracts # via npm $ npm install @chainlink/contracts --save
Directory Structure
@chainlink/contracts ├── src # Solidity contracts │ ├── v0.4 │ ├── v0.5 │ ├── v0.6 │ ├── v0.7 │ └── v0.8 └── abi # ABI json output ├── v0.4 ├── v0.5 ├── v0.6 ├── v0.7 └── v0.8
Usage
import '@chainlink/contracts/src/v0.8/KeeperCompatibleInterface.sol';
Floating Point Math in Solidity
tuple
- Gas Estimation Failed
- Someone should make an article explaining this error
Basic Solidity: Arrays & Structs
Libraries
Library
Solidity-by-example Library
SafeMath, Overflow Checking, and the "unchecked" keyword
Openzeppelin Safemath
unchecked vs. checked
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; contract C { function f(uint a, uint b) pure public returns (uint) { // This subtraction will wrap on underflow. unchecked { return a - b; } } function g(uint a, uint b) pure public returns (uint) { // This subtraction will revert on underflow. return a - b; } }
Basic Solidity: For Loop
For Loop
function withdraw() public { /* starting index, ending index, step amount */ for(uint256 funderIndex = 0; funderIndex < funders.length; funderIndex++) { address funder = funders[funderIndex]; addressToAmountFunded[funder] = 0; } // reset the array funders = new address[](0); }
Sending ETH from a Contract
Transfer, Send, Call
You can send Ether to other contracts by
- transfer (2300 gas, throws error)
- send (2300 gas, returns bool)
- call (forward all gas or set gas, returns bool)
// transfer payable(msg.sender).transfer(address(this).balance) // send bool sendSuccess = payable(msg.sender).send(address(this).balance); require(sendSuccess, "Send failed"); // call (bool callSuccess, bytes memory dataReturned) = payable(msg.sender).call{value: address(this).balance}("") require(callSuccess, "Call failed");
Using call is the recommended way to actually send and receive ethereum or your blockchain native token for now.
this keyword
Basic Solidity: Constructor
Constructor
address public owner; constructor() { owner = msg.sender; }
Basic Solidity: Modifiers
Double equals
require(msg.sender == owner, "Sender is not owner!");
Modifier
modifier onlyOwner { require(msg.sender == owner, "Sender is not owner!"); _; } function withdraw() public onlyOwner { ... }
Testnet Demo
Disconnecting Metamask
Advanced Solidity
Immutable & Constant
In the solidity there are two keywords that make it so that your variables can’t be changed.
address public i owner; // 21,508 gas - immutable // 23,644 gas - non immutable
uint256 public constant MINIMUM_USD = 50 * 1e18; // cost 21,415 gas - constant // cost 23,515 gas - non-constant // 21,415 * 141000000000 = $9. 058545 // 23,515 * 141000000000 = $9.946845
The reason that these two save gas is because instead of storing these variables inside of a storage slot, we actually store them directly into the bite code of the contract.
- Don't stress about gas optimizations! (yet)
- Naming Conventions
Custom Errors
error NotOwner(); modifier onlyOwner { // require(msg.sender == i_ owner, "Sender is not owner!"); if(msg.sender != i_ owner) { revert Notowner(); } _; }
This ends up saving us a lot of gas since we don’t have to store and emit this long string here.
We can revert any transaction or any function call in the middle of the function call without the conditional beforehand.
function test() { ... revert(); }
Receive & Fallback Functions
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; contract FallbackExample { uint256 public result; receive() external payable() { result = 1; } fallback() external payable() { result = 2; } }
call contract without a valid function, contract refer to fallback function, the value of result updated to 2.
call contract without specifying what you want to do, the value of result updates back to 1.
- Receive
A contract can have at most one receive function, declared using
receive( ) external payable ( .... ) (without the funct. ion keyword). This function cannot have arguments, cannot return anything and must have external visibility and payable state mutability. It can be virtual, can override and can have modifiers.
The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via f . send() or ,transfer() ). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception.