Aaron Bloomfield (aaron@virginia.edu)
@github | ↑ |
uint8
, uint16
, uint24
, … uint256
uint
is an alias for uint256
int8
, int16
, int24
, …, int256
int
is an alias for int256
type(int256).max
and type(uint160).min
for min/maxuint
(aka uint256
) and uint8
uint
that is negative will cause a reversion
1e12
equals \(10^{12}\)
3.4e8
equals \(3.4 \ast 10^8\)bool
, values are true
and false
fixed
and ufixed
address
and address payable
address
and: uint160
or bytes20
address a = address(0);
uint160 u = uint160(a);
bytes20 b = bytes20(u);
msg.sender
address a;
// set a = something...
address payable b = payable(a);
uint160 b = uint160(msg.sender);
bytes20 c = bytes20(msg.sender);
if ( a == address(0) ) { ... }
address initiator = msg.sender;
balance
: the balance, in wei, for that addressaddress payable x = payable(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) {...}
transfer()
to send wei (requires a receive()
function on the receiving contract)send()
: low-level and not safe version of transfer()
call()
, delegatecall()
, and staticcall()
: calling a contract with an unknown ABIcode()
and codehash()
to get the bytecode of the contractbytes1
, bytes2
, up to bytes32
.length
to get the length[]
uint
when performing the operationbytes
: dynamically sized array of bytesstring
: dynamically sized array of UTF-8 characters
string s = "CS" '4970'; // equals "CS4970"
uint8[6] memory p = [ 1, 2, 3, 4, 5, 6 ];
memory
keyword shortly…uint
enum ActionChoices { GoLeft, GoRight,
GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant default = ActionChoices.GoLeft;
function setGoStraight() public {
choice = ActionChoices.GoStraight;
}
mapping (address => bool) public override voted;
mapping (uint => Choice) internal _choices;
public
, internal
, and override
laterif ( voted[msg.sender] ) { ... }
voted[msg.sender] = true;
mapping (uint => mapping(string => address) )
public twodmap;
twodmap[3]["foo"]
1
as the key, the actual key is the Keccak256 hash of thatdelete map[1];
mapping (address => bool) public override voted;
// this next one was not in the original Poll.sol:
mapping (address => uint) public how_voted;
// or:
mapping (uint => Choice) internal _choices;
uint public override num_choices;
voters
tells if an address has voted
how_voted
will tell what they voted forstruct Choice {
uint id;
string name;
uint votes;
}
mapping (uint => Choice) internal _choices;
Choice
struct into different mappings, you might now have two copies of that struct!block.timestamp
seconds
, minutes
, hours
, days
, and weeks
as time units
uint a = 1;
uint b = 5;
uint c = 2;
// valid:
uint endTime = block.timestamp +
1 seconds + 5 minutes + 2 days;
// not valid:
uint endTime = block.timestamp +
a seconds + b minutes + c days;
// valid:
uint endTime = block.timestamp +
a * 1 seconds + b * 1 minutes + c * 1 days;
assert(1 wei == 1);
assert(1 gwei == 1e9);
assert(1 ether == 1e18);
storage
: for all state variables, it is stored on the blockchain
memory
: a local variable, it only exists while the subroutine is executing
calldata
: read-only, used for function parametersTo (lhs) | From (rhs) | Result |
---|---|---|
memory | memory | reference aliasing |
memory | any storage | full copy |
memory | calldata | full copy |
any storage | memory | full copy |
any storage | calldata | full copy |
local storage | any storage | reference aliasing |
state storage | any storage | full copy |
calldata | (any) | not allowed |
calldata
bytes
and string
memory
, storage
, or calldata
string memory
bytes storage
if ( keccak256(x) == keccak256(y) ) { ... }
keccak256()
uses a lot of gasstring memory s = string.concat(s1, s2);
string memory
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
contract C {
// The data location of x is storage; this is the
// only place where the data location can be omitted
uint[] x;
// The data location of memoryArray is memory.
function f(uint[] memory memoryArray) public {
x = memoryArray; // works, copies the whole array to storage
uint[] storage y = x; // works, assigns a pointer, y's location is storage
y[7]; // fine, returns the 8th element
y.pop(); // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following doesn't work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer,
// but there is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
}
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: CC BY-SA
// SPDX-License-Identifier: GPL
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.29;
pragma solidity >=0.4.0 <0.6.0;
pragma solidity 0.8.11;
pragma solidity ^0.8.29;
import "./filename.sol";
import * as symbolName from "./filename.sol";
symbolName.thing
import "./filename.sol" as symbolName;
import {symbol1 as alias, symbol2} from "./filename.sol";
contract
abstract
interface
library
pure
functions (we’ll see pure
shortly)internal
visibility
is
keywordimport
’edinterface Foo is Bar {
// ...
}
contract Student is Person, Learner {
// ...
}
abstract
virtual
: if a thing (function, field, etc.) in a contract can be overridden
virtual
override
: when a sub-class is replacing an inherited function
function foo() public override(IERC165,ERC721) {
override
varies by compiler and versioncontract
must have all the methods implementedinterface
must have no methods implementedabstract contract
can have some methods implemented and some not
abstract contract Foo is Bar {
// ...
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.29;
interface IERC165 {
// ...
}
interface IERC721 is IERC165 {
// ...
}
contract ERC721 is IERC721 {
// ...
}
library Address {
// ...
}
public
: like any other OO language, it can be called by anybody or anyone
private
: like any other OO language, it can be called only by code in that classinternal
: like protected
in other OO languages: it can be called by that class or it’s sub-classesexternal
: like public
, but can ONLY be called by code outside the contract; code in the contract can not call an external
function
external
public
external
…
external
or public
(public
is more visible than external
)public
has a getter function defined for it – see the next slide
uint public k
, then you can call the k()
getter function to access its valuepublic
private
fields!uint public num_assignments;
mapping (address => string) public aliases;
mapping (uint => mapping(string => address) )
public twodmap;
function num_assignments() public view returns (uint);
function aliases(address a) public view
returns (string memory);
function twodmap(uint id, string s) public view
returns (address);
view
: this function does not modify (write to) the state of the contract
const
in C++pure
: this function does not read from or write to the state of the contract
library
must be pure
view
and pure
are mutually exclusive<type> <visibility> <override?> <name> [= <value>];
uint public duration = 86400;
uint public override k;
address public override initiator;
mapping (address => uint) internal values;
override
qualifiersfunction <name> ([<parameter_list>]) <visibility>
<qualifiers> [returns (<list>)] {
function addChoice (string memory _name) public
{ /* ... */ }
function unnecessaryFunction() public view
returns (string memory) { /* ... */ }
function overridePureFunction() external pure
override returns (uint,bool) { /* ... */ }
revert()
will do thisrequire(a>0);
will revert if \(a\) is not greater than zerorequire(a>0,"a must be greater than zero");
payable
qualifier means the function can accept ether as part of the callmsg.value
constructor() {
// ...
}
function
keyword, no return typeconstructor(uint foo) {
// ...
}
selfdesctruct()
selfdestruct()
methodmodifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
contract Modifiers {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier validAddress(address _addr) {
require(_addr != address(0), "Not valid address");
_;
}
function changeOwner(address _newOwner) public onlyOwner
validAddress(_newOwner) {
owner = _newOwner;
}
}
event votedEvent (uint indexed _id);
event choiceAddedEvent (uint indexed _id);
indexed
keyword allows for searching the event log by that valueemit
:emit choiceAddedEvent(num_choices);
emit votedEvent(_id);
try <something> {
// what to do if it succeeds
} catch {
// what to do if it fails (reverts)
}
(bool success, ) = payable(a).call{value: v}("");
require(success, "Failed to transfer ETH");
v
, is in weia
Foo
deploy another contract Bar
Bar public b;
address public c;
b = new Bar();
c = address(new Bar());
From here
Consider the following Solidity smart contract:
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.29;
contract MyContract {
uint public i = 1 + 2 * 3 - 4;
}
The bytecode was generated via:
.code
PUSH 80 contract MyContract {\n uin...
PUSH 40 contract MyContract {\n uin...
MSTORE contract MyContract {\n uin...
PUSH 3 1 + 2 * 3 - 4
PUSH 0 uint public i = 1 + 2 * 3 - 4
SSTORE uint public i = 1 + 2 * 3 - 4
CALLVALUE contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
ISZERO contract MyContract {\n uin...
PUSH [tag] 1 contract MyContract {\n uin...
JUMPI contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
REVERT contract MyContract {\n uin...
tag 1 contract MyContract {\n uin...
JUMPDEST contract MyContract {\n uin...
POP contract MyContract {\n uin...
PUSH #[$] 0000000000000000000000000000000000000000000000000000000000000000 contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
PUSH [$] 0000000000000000000000000000000000000000000000000000000000000000 contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
CODECOPY contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
RETURN contract MyContract {\n uin...
.data
0:
.code
PUSH 80 contract MyContract {\n uin...
PUSH 40 contract MyContract {\n uin...
MSTORE contract MyContract {\n uin...
CALLVALUE contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
ISZERO contract MyContract {\n uin...
PUSH [tag] 1 contract MyContract {\n uin...
JUMPI contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
REVERT contract MyContract {\n uin...
tag 1 contract MyContract {\n uin...
JUMPDEST contract MyContract {\n uin...
POP contract MyContract {\n uin...
PUSH 4 contract MyContract {\n uin...
CALLDATASIZE contract MyContract {\n uin...
LT contract MyContract {\n uin...
PUSH [tag] 2 contract MyContract {\n uin...
JUMPI contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
CALLDATALOAD contract MyContract {\n uin...
PUSH E0 contract MyContract {\n uin...
SHR contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
PUSH E5AA3D58 contract MyContract {\n uin...
EQ contract MyContract {\n uin...
PUSH [tag] 3 contract MyContract {\n uin...
JUMPI contract MyContract {\n uin...
tag 2 contract MyContract {\n uin...
JUMPDEST contract MyContract {\n uin...
PUSH 0 contract MyContract {\n uin...
DUP1 contract MyContract {\n uin...
REVERT contract MyContract {\n uin...
tag 3 uint public i = 1 + 2 * 3 - 4
JUMPDEST uint public i = 1 + 2 * 3 - 4
PUSH [tag] 4 uint public i = 1 + 2 * 3 - 4
PUSH 0 uint public i = 1 + 2 * 3 - 4
SLOAD uint public i = 1 + 2 * 3 - 4
DUP2 uint public i = 1 + 2 * 3 - 4
JUMP uint public i = 1 + 2 * 3 - 4
tag 4 uint public i = 1 + 2 * 3 - 4
JUMPDEST uint public i = 1 + 2 * 3 - 4
PUSH 40 uint public i = 1 + 2 * 3 - 4
MLOAD uint public i = 1 + 2 * 3 - 4
SWAP1
DUP2
MSTORE
PUSH 20
ADD
PUSH 40 uint public i = 1 + 2 * 3 - 4
MLOAD uint public i = 1 + 2 * 3 - 4
DUP1 uint public i = 1 + 2 * 3 - 4
SWAP2 uint public i = 1 + 2 * 3 - 4
SUB uint public i = 1 + 2 * 3 - 4
SWAP1 uint public i = 1 + 2 * 3 - 4
RETURN uint public i = 1 + 2 * 3 - 4
.data
60806040526003600055348015601457600080fd5b50607d80
6100236000396000f3fe6080604052348015600f57600080fd
5b506004361060285760003560e01c8063e5aa3d5814602d57
5b600080fd5b603560005481565b6040519081526020016040
5180910390f3fea26469706673582212206e6ad71c40961eec
8c1886164d5d543228ad45839283845f4e3bdbb431243be364
736f6c63430008110033
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.29;
interface IPoll {
struct Choice {
uint id;
string name;
uint votes;
}
function purpose() external pure returns (string memory);
function voted(address a) external view returns (bool);
function choices(uint i) external view returns (Choice memory);
function num_choices() external view returns (uint);
function addChoice (string memory _name) external;
function vote (uint _id) external;
event votedEvent (uint indexed _id);
event choiceAddedEvent (uint indexed _id);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.29;
import "./IPoll.sol";
contract Poll is IPoll {
mapping (address => bool) public override voted;
mapping (uint => Choice) internal _choices;
function choices(uint i) public view override returns (Choice memory) {
return _choices[i];
}
uint public override num_choices;
string public override constant purpose = "Vote on your favorite color";
constructor() {
addChoice("red");
addChoice("orange");
addChoice("yellow");
addChoice("green");
addChoice("blue");
addChoice("purple");
}
function addChoice (string memory _name) public override {
_choices[num_choices] = Choice(num_choices, _name, 0);
emit choiceAddedEvent(num_choices);
num_choices++;
}
function vote (uint _id) public override {
require(!voted[msg.sender], "sender has already voted");
require(_id >= 0 && _id < num_choices, "invalid vote selection");
voted[msg.sender] = true;
_choices[_id].votes++;
emit votedEvent(_id);
}
function unnecessaryFunction() public view returns (string memory) {
return _choices[0].name;
}
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return interfaceId == type(IPoll).interfaceId || interfaceId == 0x01ffc9a7;
}
}
$
5 to cover Bob$
5 to cover Charlie$
10 to cover Alice$
5 to Charlie; all other debts cancel outexternal
visibilityentries()
function has a tuple return type, not an Entry memory
return typeoverride
supportsInterface()
methodgeth --config geth-config.toml \
--rpc.enabledeprecatedpersonal
geth attach /path/to/ethprivate/geth.ipc
Or:
geth attach ./geth.ipc
In Windows, it’s something like:
geth attach \\.\pipe\geth.ipc
var addr = "0xffffffffffffffffffffffffffffffffffffffff";
var abi = [...];
var interface = eth.contract(abi);
var contract = interface.at(addr);
view
or pure
functions) via the .call()
methodcontract.num_entries.call()
contract.entries.call(0)
contract.entries.call(0)[2]
view
or pure
) method:contract.thisMethodDoesNotExist.call()
personal.unlockAccount(eth.coinbase,"password",0)
eth.coinbase
? If not, then enter:miner.setEtherbase(eth.accounts[0])
.sendTransaction()
methodcontract.addAlias.sendTransaction("mst3k",
"Your Name", {from:eth.coinbase, gas:1000000})
contract.payToAlias.sendTransaction("mst3k",17,
{from:eth.coinbase, gas:1000000});
… how much do I owe???
Also, we have the source code available:
require()
with 2 parametersrequire (value > 0);
require (value > 0, "value must be > 0 in foo()");
require()
to assure the correct staterequire()
a lot to make sure that the variables are in the state you expect them to berequire (msg.value > 0,
"must transfer ether with this transaction");
require (from_address != address(0),
"must specify a non-null from address");
require (to_address != address(0),
"must specify a non-null to address");
// and so on...
public
!!!contract Test {
uint public debug1;
uint public debug2;
function whyYouNotWork(uint a, uint b, uint c)
public returns (uint) {
uint x = 10**a;
debug1 = x;
x = x / b + c;
debug2 = x;
return x*123;
}
}
contract Test {
address erc20 = 0x123456...;
address tokenUser = 0x123456...;
function setup() {
IERC20(erc20).callFunction(param1,2,3);
tokenUser.setERC20(erc20);
// ... and so on
}
}
require()
statements
Comments