Aaron Bloomfield (aaron@virginia.edu)
@github | ↑ |
uint8, uint16, uint24, … uint256
uint is an alias for uint256int8, int16, int24, …, int256
int is an alias for int256type(int256).max and type(uint160).min for min/maxuint (aka uint256) and uint8uint that is negative will cause a reversion
1e12 equals \(10^{12}\)
3.4e8 equals \(3.4 \ast 10^8\)bool, values are true and falsefixed and ufixedaddress and address payableaddress and: uint160 or bytes20address a = address(0);
uint160 u = uint160(a);
bytes20 b = bytes20(u);
msg.senderaddress 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…uintenum 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.timestampseconds, 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 parameters| To (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 |
calldatabytes and stringmemory, storage, or calldatastring memorybytes storageif ( 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.28;
pragma solidity >=0.4.0 <0.6.0;
pragma solidity 0.8.11;
pragma solidity ^0.8.28;
import "./filename.sol";
import * as symbolName from "./filename.sol";
symbolName.thingimport "./filename.sol" as symbolName;
import {symbol1 as alias, symbol2} from "./filename.sol";
contract
abstractinterface
library
pure functions (we’ll see pure shortly)internal visibility
is keywordimport’edinterface Foo is Bar {
// ...
}
contract Student is Person, Learner {
// ...
}
abstractvirtual: if a thing (function, field, etc.) in a contract can be overridden
virtualoverride: 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.28;
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
externalpublicexternal…
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 pureview 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 weiaFoo deploy another contract BarBar 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.28;
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.28;
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.28;
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 typeoverridesupportsInterface() methodinput field, and there is no inputDecode fieldfunctionCall and inputDecode fieldsstatus field that lists the reversion reasongeth --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