mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-07-31 20:10:49 +02:00
feat: refactored for 0.8.19 of solidity (#5332)
This commit is contained in:
150
solidity.md
150
solidity.md
@@ -42,7 +42,7 @@ One of the easiest ways to build, deploy, and test solidity code is by using the
|
||||
|
||||
To get started, [download the Metamask Browser Extension](https://metamask.io/).
|
||||
|
||||
Once installed, we will be working with Remix. The below code will be pre-loaded, but before we head over there, let's look at a few tips to get started with remix. Load it all by [hitting this link](https://remix.ethereum.org/#version=soljson-v0.6.6+commit.6c089d02.js&optimize=false&evmVersion=null&gist=f490c0d51141dd0515244db40bbd0c17&runs=200).
|
||||
Once installed, we will be working with Remix. The below code will be pre-loaded, but before we head over there, let's look at a few tips to get started with remix. Load it all by [hitting this link](https://remix.ethereum.org/#version=soljson-v0.8.19+commit.7dd6d404.js&optimize=false&evmVersion=null&gist=2acb47a0286fe45d2464fa937f00fef3&runs=200).
|
||||
|
||||
1. Choose the Solidity compiler
|
||||
|
||||
@@ -68,29 +68,7 @@ You've deployed your first contract! Congrats!
|
||||
|
||||
You can test out and play with the functions defined. Check out the comments to learn about what each does.
|
||||
|
||||
|
||||
## Working on a testnet
|
||||
|
||||
Deploying and testing on a testnet is the most accurate way to test your smart contracts in solidity.
|
||||
To do this let's first get some testnet ETH from the Kovan testnet.
|
||||
|
||||
[Pop into this Gitter Channel](https://gitter.im/kovan-testnet/faucet) and drop your metamask address in.
|
||||
|
||||
In your metamask, you'll want to change to the `Kovan` testnet.
|
||||
|
||||

|
||||
|
||||
You'll be given some free test Ethereum. Ethereum is needed to deploy smart contracts when working with a testnet.
|
||||
|
||||
In the previous example, we didn't use a testnet, we deployed to a fake virtual environment.
|
||||
When working with a testnet, we can actually see and interact with our contracts in a persistent manner.
|
||||
|
||||
To deploy to a testnet, on the `#4 Deploy` step, change your `environment` to `injected web3`.
|
||||
This will use whatever network is currently selected in your metamask as the network to deploy to.
|
||||
|
||||

|
||||
|
||||
For now, please continue to use the `JavaScript VM` unless instructed otherwise. When you deploy to a testnet, metamask will pop up to ask you to "confirm" the transaction. Hit yes, and after a delay, you'll get the same contract interface at the bottom of your screen.
|
||||
For now, please continue to use the `Remix VM` unless instructed otherwise.
|
||||
|
||||
|
||||
```solidity
|
||||
@@ -99,9 +77,12 @@ For now, please continue to use the `JavaScript VM` unless instructed otherwise.
|
||||
|
||||
// simple_bank.sol (note .sol extension)
|
||||
/* **** START EXAMPLE **** */
|
||||
// A special comment is used at the top of the file to indicate
|
||||
// the license for the code, and the version of Solidity used
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Declare the source file compiler version
|
||||
pragma solidity ^0.6.6;
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
// Start with Natspec comment (the three slashes)
|
||||
// used for documentation - and as descriptive data for UI elements/actions
|
||||
@@ -115,7 +96,6 @@ contract SimpleBank { // CapWords
|
||||
// Declare state variables outside function, persist through life of contract
|
||||
|
||||
// dictionary that maps addresses to balances
|
||||
// always be careful about overflow attacks with numbers
|
||||
mapping (address => uint) private balances;
|
||||
|
||||
// "private" means that other contracts can't directly query balances
|
||||
@@ -128,7 +108,7 @@ contract SimpleBank { // CapWords
|
||||
event LogDepositMade(address accountAddress, uint amount);
|
||||
|
||||
// Constructor, can receive one or many variables here; only one allowed
|
||||
constructor() public {
|
||||
constructor() {
|
||||
// msg provides details about the message that's sent to the contract
|
||||
// msg.sender is contract caller (address of contract creator)
|
||||
owner = msg.sender;
|
||||
@@ -139,6 +119,7 @@ contract SimpleBank { // CapWords
|
||||
function deposit() public payable returns (uint) {
|
||||
// Use 'require' to test user inputs, 'assert' for internal invariants
|
||||
// Here we are making sure that there isn't an overflow issue
|
||||
// In modern versions of Solidity, this is automatically checked
|
||||
require((balances[msg.sender] + msg.value) >= balances[msg.sender]);
|
||||
|
||||
balances[msg.sender] += msg.value;
|
||||
@@ -165,7 +146,7 @@ contract SimpleBank { // CapWords
|
||||
balances[msg.sender] -= withdrawAmount;
|
||||
|
||||
// this automatically throws on a failure, which means the updated balance is reverted
|
||||
msg.sender.transfer(withdrawAmount);
|
||||
payable(msg.sender).transfer(withdrawAmount);
|
||||
|
||||
return balances[msg.sender];
|
||||
}
|
||||
@@ -206,12 +187,11 @@ uint8 b;
|
||||
int64 c;
|
||||
uint248 e;
|
||||
|
||||
// Be careful that you don't overflow, and protect against attacks that do
|
||||
// In older versions of solidity, doing addition could cause "overflow"
|
||||
// For example, for an addition, you'd do:
|
||||
uint256 c = a + b;
|
||||
assert(c >= a); // assert tests for internal invariants; require is used for user inputs
|
||||
// For more examples of common arithmetic issues, see Zeppelin's SafeMath library
|
||||
// https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol
|
||||
assert(c >= a);
|
||||
// But modern versions of Solidity automatically check for overflow/underflow of integer math
|
||||
|
||||
|
||||
// No random functions built in, you can get a pseduo-random number by hashing the current blockhash, or get a truly random number using something like Chainlink VRF.
|
||||
@@ -220,7 +200,7 @@ assert(c >= a); // assert tests for internal invariants; require is used for use
|
||||
// Type casting
|
||||
int x = int(b);
|
||||
|
||||
bool b = true; // or do 'var b = true;' for inferred typing
|
||||
bool b = true;
|
||||
|
||||
// Addresses - holds 20 byte/160 bit Ethereum addresses
|
||||
// No arithmetic allowed
|
||||
@@ -237,7 +217,7 @@ address public owner;
|
||||
owner.transfer(SOME_BALANCE); // fails and reverts on failure
|
||||
|
||||
// Can also do a lower level .send call, which returns a false if it failed
|
||||
if (owner.send) {} // REMEMBER: wrap send in 'if', as contract addresses have
|
||||
if (owner.send(amount)) {} // REMEMBER: wrap send in 'if', as contract addresses have
|
||||
// functions executed on send and these can fail
|
||||
// Also, make sure to deduct balances BEFORE attempting a send, as there is a risk of a recursive
|
||||
// call that can drain the contract
|
||||
@@ -247,7 +227,7 @@ owner.balance; // the balance of the owner (user or contract)
|
||||
|
||||
|
||||
// Bytes available from 1 to 32
|
||||
byte a; // byte is same as bytes1
|
||||
bytes1 a; // bytes1 is the explicit form
|
||||
bytes2 b;
|
||||
bytes32 c;
|
||||
|
||||
@@ -260,20 +240,6 @@ string n = "hello"; // stored in UTF8, note double quotes, not single
|
||||
// string utility functions to be added in future
|
||||
// prefer bytes32/bytes, as UTF8 uses more storage
|
||||
|
||||
// Type inference
|
||||
// var does inferred typing based on first assignment,
|
||||
// can't be used in functions parameters
|
||||
var a = true;
|
||||
// use carefully, inference may provide wrong type
|
||||
// e.g., an int8, when a counter needs to be int16
|
||||
|
||||
// var can be used to assign function to variable
|
||||
function a(uint x) returns (uint) {
|
||||
return x * 2;
|
||||
}
|
||||
var f = a;
|
||||
f(22); // call
|
||||
|
||||
// by default, all values are set to 0 on instantiation
|
||||
|
||||
// Delete can be called on most types
|
||||
@@ -289,10 +255,10 @@ delete x;
|
||||
// Arrays
|
||||
bytes32[5] nicknames; // static array
|
||||
bytes32[] names; // dynamic array
|
||||
uint newLength = names.push("John"); // adding returns new length of the array
|
||||
names.push("John"); // adding an element (no longer returns length)
|
||||
// Length
|
||||
names.length; // get length
|
||||
names.length = 1; // lengths can be set (for dynamic arrays in storage only)
|
||||
// Note: Direct length assignment has been removed in newer Solidity versions
|
||||
|
||||
// multidimensional array
|
||||
uint[][5] x; // arr with 5 dynamic array elements (opp order of most languages)
|
||||
@@ -304,7 +270,7 @@ balances["charles"] = 1;
|
||||
// 'public' allows following from another contract
|
||||
contractName.balances("charles"); // returns 1
|
||||
// 'public' created a getter (but not setter) like the following:
|
||||
function balances(string _account) returns (uint balance) {
|
||||
function balances(string memory _account) public view returns (uint balance) {
|
||||
return balances[_account];
|
||||
}
|
||||
|
||||
@@ -345,6 +311,7 @@ uint createdState = uint(State.Created); // 0
|
||||
// Data locations: Memory vs. storage vs. calldata - all complex types (arrays,
|
||||
// structs) have a data location
|
||||
// 'memory' does not persist, 'storage' does
|
||||
// 'calldata' also does not persist, and is exclusively "read-only" meaning it cannot be modified
|
||||
// Default is 'storage' for local and state variables; 'memory' for func params
|
||||
// stack holds small local variables
|
||||
|
||||
@@ -369,14 +336,13 @@ this.someFunction(); // calls func externally via call, not via internal jump
|
||||
msg.sender; // address of sender
|
||||
msg.value; // amount of ether provided to this contract in wei, the function should be marked "payable"
|
||||
msg.data; // bytes, complete call data
|
||||
msg.gas; // remaining gas
|
||||
|
||||
// ** tx - This transaction **
|
||||
tx.origin; // address of sender of the transaction
|
||||
tx.gasprice; // gas price of the transaction
|
||||
|
||||
// ** block - Information about current block **
|
||||
now; // current time (approximately), alias for block.timestamp (uses Unix time)
|
||||
block.timestamp; // current time (approximately) (uses Unix time)
|
||||
// Note that this can be manipulated by miners, so use carefully
|
||||
|
||||
block.number; // current block number
|
||||
@@ -403,7 +369,7 @@ function increment(uint x, uint y) returns (uint x, uint y) {
|
||||
y += 1;
|
||||
}
|
||||
// Call previous function
|
||||
uint (a,b) = increment(1,1);
|
||||
(uint a, uint b) = increment(1,1);
|
||||
|
||||
// 'view' (alias for 'constant')
|
||||
// indicates that function does not/cannot change persistent vars
|
||||
@@ -434,7 +400,7 @@ function increment(uint x) view returns (uint x) {
|
||||
|
||||
// Functions hoisted - and can assign a function to a variable
|
||||
function a() {
|
||||
var z = b;
|
||||
function() internal z = b;
|
||||
z();
|
||||
}
|
||||
|
||||
@@ -464,7 +430,7 @@ function depositEther() public payable {
|
||||
event LogSent(address indexed from, address indexed to, uint amount); // note capital first letter
|
||||
|
||||
// Call
|
||||
LogSent(from, to, amount);
|
||||
emit LogSent(from, to, amount);
|
||||
|
||||
/**
|
||||
|
||||
@@ -493,7 +459,7 @@ Coin.LogSent().watch({}, '', function(error, result) {
|
||||
|
||||
// '_' (underscore) often included as last line in body, and indicates
|
||||
// function being called should be placed there
|
||||
modifier onlyAfter(uint _time) { require (now >= _time); _; }
|
||||
modifier onlyAfter(uint _time) { require (block.timestamp >= _time); _; }
|
||||
modifier onlyOwner { require(msg.sender == owner); _; }
|
||||
// commonly used with state machines
|
||||
modifier onlyIfStateA (State currState) { require(currState == State.A); _; }
|
||||
@@ -565,7 +531,7 @@ contract Consumer {
|
||||
function callFeed() {
|
||||
// final parentheses call contract, can optionally add
|
||||
// custom ether value or gas
|
||||
feed.info.value(10).gas(800)();
|
||||
feed.info{value: 10, gas: 800}();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,14 +684,15 @@ contract SomeOracle {
|
||||
// see example below for State enum and inState modifier
|
||||
```
|
||||
|
||||
Work with the full example below using the [`JavaScript VM` in remix here.](https://remix.ethereum.org/#version=soljson-v0.6.6+commit.6c089d02.js&optimize=false&evmVersion=null&gist=3d12cd503dcedfcdd715ef61f786be0b&runs=200)
|
||||
Work with the full example below using the [`Remix VM` in remix here.](https://remix.ethereum.org/#version=soljson-v0.8.19+commit7dd6d404.js&optimize=false&evmVersion=null&gist=2acb47a0286fe45d2464fa937f00fef3&runs=200)
|
||||
|
||||
```solidity
|
||||
// *** EXAMPLE: A crowdfunding example (broadly similar to Kickstarter) ***
|
||||
// ** START EXAMPLE **
|
||||
|
||||
// CrowdFunder.sol
|
||||
pragma solidity ^0.6.6;
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
/// @title CrowdFunder
|
||||
/// @author nemild
|
||||
@@ -735,7 +702,7 @@ contract CrowdFunder {
|
||||
address payable public fundRecipient; // creator may be different than recipient, and must be payable
|
||||
uint public minimumToRaise; // required to tip, else everyone gets refund
|
||||
string campaignUrl;
|
||||
byte version = "1";
|
||||
uint256 version = 1;
|
||||
|
||||
// Data structures
|
||||
enum State {
|
||||
@@ -771,7 +738,7 @@ contract CrowdFunder {
|
||||
// Wait 24 weeks after final contract state before allowing contract destruction
|
||||
modifier atEndOfLifecycle() {
|
||||
require(((state == State.ExpiredRefund || state == State.Successful) &&
|
||||
completeAt + 24 weeks < now));
|
||||
completeAt + 24 weeks < block.timestamp));
|
||||
_;
|
||||
}
|
||||
|
||||
@@ -786,7 +753,7 @@ contract CrowdFunder {
|
||||
fundRecipient = _fundRecipient;
|
||||
campaignUrl = _campaignUrl;
|
||||
minimumToRaise = _minimumToRaise;
|
||||
raiseBy = now + (timeInHoursForFundraising * 1 hours);
|
||||
raiseBy = block.timestamp + (timeInHoursForFundraising * 1 hours);
|
||||
}
|
||||
|
||||
function contribute()
|
||||
@@ -798,7 +765,7 @@ contract CrowdFunder {
|
||||
contributions.push(
|
||||
Contribution({
|
||||
amount: msg.value,
|
||||
contributor: msg.sender
|
||||
contributor: payable(msg.sender)
|
||||
}) // use array, so can iterate
|
||||
);
|
||||
totalRaised += msg.value;
|
||||
@@ -817,10 +784,10 @@ contract CrowdFunder {
|
||||
payOut();
|
||||
|
||||
// could incentivize sender who initiated state change here
|
||||
} else if ( now > raiseBy ) {
|
||||
} else if ( block.timestamp > raiseBy ) {
|
||||
state = State.ExpiredRefund; // backers can now collect refunds by calling getRefund(id)
|
||||
}
|
||||
completeAt = now;
|
||||
completeAt = block.timestamp;
|
||||
}
|
||||
|
||||
function payOut()
|
||||
@@ -828,7 +795,7 @@ contract CrowdFunder {
|
||||
inState(State.Successful)
|
||||
{
|
||||
fundRecipient.transfer(address(this).balance);
|
||||
LogWinnerPaid(fundRecipient);
|
||||
emit LogWinnerPaid(fundRecipient);
|
||||
}
|
||||
|
||||
function getRefund(uint256 id)
|
||||
@@ -846,14 +813,21 @@ contract CrowdFunder {
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeContract()
|
||||
public
|
||||
isCreator()
|
||||
atEndOfLifecycle()
|
||||
{
|
||||
selfdestruct(msg.sender);
|
||||
// creator gets all money that hasn't be claimed
|
||||
}
|
||||
// Warning: "selfdestruct" has been deprecated.
|
||||
// Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code
|
||||
// and data associated with an account and only transfers its Ether to the beneficiary, unless executed
|
||||
// in the same transaction in which the contract was created (see EIP-6780).
|
||||
|
||||
// Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account.
|
||||
// Future changes to the EVM might further reduce the functionality of the opcode.
|
||||
// function removeContract()
|
||||
// public
|
||||
// isCreator()
|
||||
// atEndOfLifecycle()
|
||||
// {
|
||||
// selfdestruct(msg.sender);
|
||||
// // creator gets all money that hasn't be claimed
|
||||
// }
|
||||
}
|
||||
// ** END EXAMPLE **
|
||||
```
|
||||
@@ -882,7 +856,7 @@ uint x = 5;
|
||||
|
||||
// Cryptography
|
||||
// All strings passed are concatenated before hash action
|
||||
sha3("ab", "cd");
|
||||
keccak256("ab", "cd");
|
||||
ripemd160("abc");
|
||||
sha256("def");
|
||||
|
||||
@@ -895,11 +869,15 @@ sha256("def");
|
||||
|
||||
// 12. LOW LEVEL FUNCTIONS
|
||||
// call - low level, not often used, does not provide type safety
|
||||
successBoolean = someContractAddress.call('function_name', 'arg1', 'arg2');
|
||||
(bool success, bytes memory data) = someContractAddress.call(
|
||||
abi.encodeWithSignature("function_name(string,string)", "arg1", "arg2")
|
||||
);
|
||||
|
||||
// callcode - Code at target address executed in *context* of calling contract
|
||||
// delegatecall - Code at target address executed in *context* of calling contract
|
||||
// provides library functionality
|
||||
someContractAddress.callcode('function_name');
|
||||
(bool success, bytes memory data) = someContractAddress.delegatecall(
|
||||
abi.encodeWithSignature("function_name()")
|
||||
);
|
||||
|
||||
|
||||
// 13. STYLE NOTES
|
||||
@@ -932,22 +910,20 @@ someContractAddress.callcode('function_name');
|
||||
|
||||
## Additional resources
|
||||
- [Solidity Docs](https://solidity.readthedocs.org/en/latest/)
|
||||
- [Cyfrin Updraft (Learn solidity development)](https://updraft.cyfrin.io/)
|
||||
- [Chainlink Beginner Tutorials](https://docs.chain.link/docs/beginners-tutorial)
|
||||
- [Smart Contract Best Practices](https://github.com/ConsenSys/smart-contract-best-practices)
|
||||
- [Superblocks Lab - Browser based IDE for Solidity](https://lab.superblocks.com/)
|
||||
- [EthFiddle - The JsFiddle for Solidity](https://ethfiddle.com/)
|
||||
- [Browser-based Solidity Editor](https://remix.ethereum.org/)
|
||||
- [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity)
|
||||
- [Modular design strategies for Ethereum Contracts](https://docs.erisindustries.com/tutorials/solidity/)
|
||||
- [Chainlink Documentation](https://docs.chain.link/docs/getting-started)
|
||||
|
||||
## Smart Contract Development Frameworks
|
||||
- [Foundry](https://getfoundry.sh/)
|
||||
- [Hardhat](https://hardhat.org/)
|
||||
- [Brownie](https://github.com/eth-brownie/brownie)
|
||||
- [Truffle](https://www.trufflesuite.com/)
|
||||
|
||||
## Important libraries
|
||||
- [Zeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts): Libraries that provide common contract patterns (crowdfuding, safemath, etc)
|
||||
- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts): Libraries that provide common contract patterns (crowdfuding, safemath, etc)
|
||||
- [Chainlink](https://github.com/smartcontractkit/chainlink): Code that allows you to interact with external data
|
||||
|
||||
## Sample contracts
|
||||
@@ -956,8 +932,11 @@ someContractAddress.callcode('function_name');
|
||||
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
|
||||
- [ConsenSys Contracts](https://github.com/ConsenSys/dapp-store-contracts)
|
||||
- [State of Dapps](http://dapps.ethercasts.com/)
|
||||
- [Security Codebase Examples](https://github.com/Cyfrin/sc-exploits-minimized/)
|
||||
|
||||
## Security
|
||||
- [Smart Contract Security Curriculum](https://updraft.cyfrin.io/courses/security)
|
||||
- [Smart Contract Security Reports Database](https://solodit.xyz/)
|
||||
- [Thinking About Smart Contract Security](https://blog.ethereum.org/2016/06/19/thinking-smart-contract-security/)
|
||||
- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/)
|
||||
- [Hacking Distributed Blog](http://hackingdistributed.com/)
|
||||
@@ -972,7 +951,6 @@ someContractAddress.callcode('function_name');
|
||||
- Editor Snippets ([Ultisnips format](https://gist.github.com/nemild/98343ce6b16b747788bc))
|
||||
|
||||
## Future To Dos
|
||||
- New keywords: protected, inheritable
|
||||
- List of common design patterns (throttling, RNG, version upgrade)
|
||||
- Common security anti patterns
|
||||
|
||||
|
Reference in New Issue
Block a user