This commit is contained in:
writer 2024-10-15 12:26:21 +09:00
parent c2d0866191
commit db37d209ab
58 changed files with 4663 additions and 1043 deletions

1778
basic_knowledge/README.md Normal file

File diff suppressed because it is too large Load diff

200
basic_knowledge/bitwise.md Normal file
View file

@ -0,0 +1,200 @@
## bitwise operators
<br>
### example of bitwise operations
```
contract BitwiseOps {
// x = 1110 = 8 + 4 + 2 + 0 = 14
// y = 1011 = 8 + 0 + 2 + 1 = 11
// x & y = 1010 = 8 + 0 + 2 + 0 = 10
function and(uint x, uint y) external pure returns (uint) {
return x & y;
}
// x = 1100 = 8 + 4 + 0 + 0 = 12
// y = 1001 = 8 + 0 + 0 + 1 = 9
// x | y = 1101 = 8 + 4 + 0 + 1 = 13
function or(uint x, uint y) external pure returns (uint) {
return x | y;
}
// x = 1100 = 8 + 4 + 0 + 0 = 12
// y = 0101 = 0 + 4 + 0 + 1 = 5
// x ^ y = 1001 = 8 + 0 + 0 + 1 = 9
function xor(uint x, uint y) external pure returns (uint) {
return x ^ y;
}
// x = 00001100 = 0 + 0 + 0 + 0 + 8 + 4 + 0 + 0 = 12
// ~x = 11110011 = 128 + 64 + 32 + 16 + 0 + 0 + 2 + 1 = 243
function not(uint8 x) external pure returns (uint8) {
return ~x;
}
// 1 << 0 = 0001 --> 0001 = 1
// 1 << 1 = 0001 --> 0010 = 2
// 1 << 2 = 0001 --> 0100 = 4
// 1 << 3 = 0001 --> 1000 = 8
// 3 << 2 = 0011 --> 1100 = 12
function shiftLeft(uint x, uint bits) external pure returns (uint) {
return x << bits;
}
// 8 >> 0 = 1000 --> 1000 = 8
// 8 >> 1 = 1000 --> 0100 = 4
// 8 >> 2 = 1000 --> 0010 = 2
// 8 >> 3 = 1000 --> 0001 = 1
// 8 >> 4 = 1000 --> 0000 = 0
// 12 >> 1 = 1100 --> 0110 = 6
function shiftRight(uint x, uint bits) external pure returns (uint) {
return x >> bits;
}
// Get last n bits from x
function getLastNBits(uint x, uint n) external pure returns (uint) {
// Example, last 3 bits
// x = 1101 = 13
// mask = 0111 = 7
// x & mask = 0101 = 5
uint mask = (1 << n) - 1;
return x & mask;
}
// Get last n bits from x using mod operator
function getLastNBitsUsingMod(uint x, uint n) external pure returns (uint) {
// 1 << n = 2 ** n
return x % (1 << n);
}
// Get position of most significant bit
// x = 1100 = 10, most significant bit = 1000, so this function will return 3
function mostSignificantBit(uint x) external pure returns (uint) {
uint i = 0;
while ((x >>= 1) > 0) {
++i;
}
return i;
}
// Get first n bits from x
// len = length of bits in x = position of most significant bit of x, + 1
function getFirstNBits(uint x, uint n, uint len) external pure returns (uint) {
// Example
// x = 1110 = 14, n = 2, len = 4
// mask = 1100 = 12
// x & mask = 1100 = 12
uint mask = ((1 << n) - 1) << (len - n);
return x & mask;
}
}
```
<br>
----
### most significant bit
<br>
```
contract MostSignificantBitFunction {
// Find most significant bit using binary search
function mostSignificantBit(uint x) external pure returns (uint msb) {
// x >= 2 ** 128
if (x >= 0x100000000000000000000000000000000) {
x >>= 128;
msb += 128;
}
// x >= 2 ** 64
if (x >= 0x10000000000000000) {
x >>= 64;
msb += 64;
}
// x >= 2 ** 32
if (x >= 0x100000000) {
x >>= 32;
msb += 32;
}
// x >= 2 ** 16
if (x >= 0x10000) {
x >>= 16;
msb += 16;
}
// x >= 2 ** 8
if (x >= 0x100) {
x >>= 8;
msb += 8;
}
// x >= 2 ** 4
if (x >= 0x10) {
x >>= 4;
msb += 4;
}
// x >= 2 ** 2
if (x >= 0x4) {
x >>= 2;
msb += 2;
}
// x >= 2 ** 1
if (x >= 0x2) msb += 1;
}
}
```
<br>
----
### most significant bit in assembly
<br>
```
contract MostSignificantBitAssembly {
function mostSignificantBit(uint x) external pure returns (uint msb) {
assembly {
let f := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x := shr(f, x)
// or can be replaced with add
msb := or(msb, f)
}
assembly {
let f := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := shl(5, gt(x, 0xFFFFFFFF))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := shl(4, gt(x, 0xFFFF))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := shl(3, gt(x, 0xFF))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := shl(2, gt(x, 0xF))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := shl(1, gt(x, 0x3))
x := shr(f, x)
msb := or(msb, f)
}
assembly {
let f := gt(x, 0x1)
msb := or(msb, f)
}
}
}
```

View file

@ -0,0 +1,28 @@
pragma solidity ^0.8.10;
// Defines a contract named `GmWorld`.
// A contract is a collection of functions and data (its state).
// Once deployed, a contract resides at a specific address on the Ethereum blockchain.
contract GmWorld {
// Declares a state variable `happiness` of type `string`.
// State variables are variables whose values are permanently stored in contract storage.
// The keyword `public` makes variables accessible from outside a contract
// and creates a function that other contracts or clients can call to access the value.
string public happiness;
// Similar to many class-based object-oriented languages, a constructor is
// a special function that is only executed upon contract creation.
// Constructors are used to initialize the contract's data.
constructor(string memory warmGun) public {
// Accepts a string argument `warmGun` and sets the value
// into the contract's `happiness` storage variable).
happiness = warmGun;
}
// A public function that accepts a string argument
// and updates the `happiness` storage variable.
function update(string memory bobaTea) public {
happiness = bobaTea;
}
}

View file

@ -0,0 +1,58 @@
pragma solidity ^0.5.10;
contract Token {
// An `address` is comparable to an email address - it's used to identify an account on Ethereum.
// Addresses can represent a smart contract or an external (user) accounts.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
address public owner;
// A `mapping` is essentially a hash table data structure.
// This `mapping` assigns an unsigned integer (the token balance) to an address (the token holder).
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
mapping (address => uint) public balances;
// Events allow for logging of activity on the blockchain.
// Ethereum clients can listen for events in order to react to contract state changes.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events
event Transfer(address from, address to, uint amount);
// Initializes the contract's data, setting the `owner`
// to the address of the contract creator.
constructor() public {
// All smart contracts rely on external transactions to trigger its functions.
// `msg` is a global variable that includes relevant data on the given transaction,
// such as the address of the sender and the ETH value included in the transaction.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
owner = msg.sender;
}
// Creates an amount of new tokens and sends them to an address.
function mint(address receiver, uint amount) public {
// `require` is a control structure used to enforce certain conditions.
// If a `require` statement evaluates to `false`, an exception is triggered,
// which reverts all changes made to the state during the current call.
// Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
// Only the contract owner can call this function
require(msg.sender == owner, "You are not the owner.");
// Enforces a maximum amount of tokens
require(amount < 1e60, "Maximum issuance exceeded");
// Increases the balance of `receiver` by `amount`
balances[receiver] += amount;
}
// Sends an amount of existing tokens from any caller to an address.
function transfer(address receiver, uint amount) public {
// The sender must have enough tokens to send
require(amount <= balances[msg.sender], "Insufficient balance.");
// Adjusts token balances of the two addresses
balances[msg.sender] -= amount;
balances[receiver] += amount;
// Emits the event defined earlier
emit Transfer(msg.sender, receiver, amount);
}
}

View file

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// An example of a basic wallet.
// Anyone can send ETH and only the owner can withdraw.
contract EtherWallet {
address payable public owner;
constructor() {
owner = payable(msg.sender);
}
receive() external payable {}
function withdraw(uint _amount) external {
require(msg.sender == owner, "caller is not owner");
payable(msg.sender).transfer(_amount);
}
function getBalance() external view returns (uint) {
return address(this).balance;
}
}

View file

@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// You cannot iterate through a mapping.
// So here is an example of how to create an iterable mapping.
library IterableMapping {
// Iterable mapping from address to uint;
struct Map {
address[] keys;
mapping(address => uint) values;
mapping(address => uint) indexOf;
mapping(address => bool) inserted;
}
function get(Map storage map, address key) public view returns (uint) {
return map.values[key];
}
function getKeyAtIndex(Map storage map, uint index) public view returns (address) {
return map.keys[index];
}
function size(Map storage map) public view returns (uint) {
return map.keys.length;
}
function set(Map storage map, address key, uint val) public {
if (map.inserted[key]) {
map.values[key] = val;
} else {
map.inserted[key] = true;
map.values[key] = val;
map.indexOf[key] = map.keys.length;
map.keys.push(key);
}
}
function remove(Map storage map, address key) public {
if (!map.inserted[key]) {
return;
}
delete map.inserted[key];
delete map.values[key];
uint index = map.indexOf[key];
address lastKey = map.keys[map.keys.length - 1];
map.indexOf[lastKey] = index;
delete map.indexOf[key];
map.keys[index] = lastKey;
map.keys.pop();
}
}
contract TestIterableMap {
using IterableMapping for IterableMapping.Map;
IterableMapping.Map private map;
function testIterableMap() public {
map.set(address(0), 0);
map.set(address(1), 100);
map.set(address(2), 200); // insert
map.set(address(2), 200); // update
map.set(address(3), 300);
for (uint i = 0; i < map.size(); i++) {
address key = map.getKeyAtIndex(i);
assert(map.get(key) == i * 100);
}
map.remove(address(1));
// keys = [address(0), address(3), address(2)]
assert(map.size() == 3);
assert(map.getKeyAtIndex(0) == address(0));
assert(map.getKeyAtIndex(1) == address(3));
assert(map.getKeyAtIndex(2) == address(2));
}
}

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// ERC20 tokens provide functionalities to
// - transfer tokens
// - allow others to transfer tokens on behalf of the token holder
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

View file

@ -0,0 +1,181 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC165 {
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
interface IERC721 is IERC165 {
function balanceOf(address owner) external view returns (uint balance);
function ownerOf(uint tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint tokenId) external;
function transferFrom(address from, address to, uint tokenId) external;
function approve(address to, uint tokenId) external;
function getApproved(uint tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(
address owner,
address operator
) external view returns (bool);
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external returns (bytes4);
}
contract ERC721 is IERC721 {
event Transfer(address indexed from, address indexed to, uint indexed id);
event Approval(address indexed owner, address indexed spender, uint indexed id);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
// Mapping from token ID to owner address
mapping(uint => address) internal _ownerOf;
// Mapping owner address to token count
mapping(address => uint) internal _balanceOf;
// Mapping from token ID to approved address
mapping(uint => address) internal _approvals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) public isApprovedForAll;
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
function ownerOf(uint id) external view returns (address owner) {
owner = _ownerOf[id];
require(owner != address(0), "token doesn't exist");
}
function balanceOf(address owner) external view returns (uint) {
require(owner != address(0), "owner = zero address");
return _balanceOf[owner];
}
function setApprovalForAll(address operator, bool approved) external {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function approve(address spender, uint id) external {
address owner = _ownerOf[id];
require(
msg.sender == owner || isApprovedForAll[owner][msg.sender],
"not authorized"
);
_approvals[id] = spender;
emit Approval(owner, spender, id);
}
function getApproved(uint id) external view returns (address) {
require(_ownerOf[id] != address(0), "token doesn't exist");
return _approvals[id];
}
function _isApprovedOrOwner(
address owner,
address spender,
uint id
) internal view returns (bool) {
return (spender == owner ||
isApprovedForAll[owner][spender] ||
spender == _approvals[id]);
}
function transferFrom(address from, address to, uint id) public {
require(from == _ownerOf[id], "from != owner");
require(to != address(0), "transfer to zero address");
require(_isApprovedOrOwner(from, msg.sender, id), "not authorized");
_balanceOf[from]--;
_balanceOf[to]++;
_ownerOf[id] = to;
delete _approvals[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(address from, address to, uint id) external {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
IERC721Receiver(to).onERC721Received(msg.sender, from, id, "") ==
IERC721Receiver.onERC721Received.selector,
"unsafe recipient"
);
}
function safeTransferFrom(
address from,
address to,
uint id,
bytes calldata data
) external {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
IERC721Receiver(to).onERC721Received(msg.sender, from, id, data) ==
IERC721Receiver.onERC721Received.selector,
"unsafe recipient"
);
}
function _mint(address to, uint id) internal {
require(to != address(0), "mint to zero address");
require(_ownerOf[id] == address(0), "already minted");
_balanceOf[to]++;
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint id) internal {
address owner = _ownerOf[id];
require(owner != address(0), "not minted");
_balanceOf[owner] -= 1;
delete _ownerOf[id];
delete _approvals[id];
emit Transfer(owner, address(0), id);
}
}
contract MyNFT is ERC721 {
function mint(address to, uint id) external {
_mint(to, id);
}
function burn(uint id) external {
require(msg.sender == _ownerOf[id], "not owner");
_burn(id);
}
}

View file

@ -0,0 +1,272 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC1155 {
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
function balanceOf(address owner, uint256 id) external view returns (uint256);
function balanceOfBatch(
address[] calldata owners,
uint256[] calldata ids
) external view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(
address owner,
address operator
) external view returns (bool);
}
interface IERC1155TokenReceiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
contract ERC1155 is IERC1155 {
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 value
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
event URI(string value, uint256 indexed id);
// owner => id => balance
mapping(address => mapping(uint256 => uint256)) public balanceOf;
// owner => operator => approved
mapping(address => mapping(address => bool)) public isApprovedForAll;
function balanceOfBatch(
address[] calldata owners,
uint256[] calldata ids
) external view returns (uint256[] memory balances) {
require(owners.length == ids.length, "owners length != ids length");
balances = new uint[](owners.length);
unchecked {
for (uint256 i = 0; i < owners.length; i++) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
function setApprovalForAll(address operator, bool approved) external {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
) external {
require(
msg.sender == from || isApprovedForAll[from][msg.sender],
"not approved"
);
require(to != address(0), "to = 0 address");
balanceOf[from][id] -= value;
balanceOf[to][id] += value;
emit TransferSingle(msg.sender, from, to, id, value);
if (to.code.length > 0) {
require(
IERC1155TokenReceiver(to).onERC1155Received(
msg.sender,
from,
id,
value,
data
) == IERC1155TokenReceiver.onERC1155Received.selector,
"unsafe transfer"
);
}
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external {
require(
msg.sender == from || isApprovedForAll[from][msg.sender],
"not approved"
);
require(to != address(0), "to = 0 address");
require(ids.length == values.length, "ids length != values length");
for (uint256 i = 0; i < ids.length; i++) {
balanceOf[from][ids[i]] -= values[i];
balanceOf[to][ids[i]] += values[i];
}
emit TransferBatch(msg.sender, from, to, ids, values);
if (to.code.length > 0) {
require(
IERC1155TokenReceiver(to).onERC1155BatchReceived(
msg.sender,
from,
ids,
values,
data
) == IERC1155TokenReceiver.onERC1155BatchReceived.selector,
"unsafe transfer"
);
}
}
// ERC165
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
// ERC1155 Metadata URI
function uri(uint256 id) public view virtual returns (string memory) {}
// Internal functions
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
require(to != address(0), "to = 0 address");
balanceOf[to][id] += value;
emit TransferSingle(msg.sender, address(0), to, id, value);
if (to.code.length > 0) {
require(
IERC1155TokenReceiver(to).onERC1155Received(
msg.sender,
address(0),
id,
value,
data
) == IERC1155TokenReceiver.onERC1155Received.selector,
"unsafe transfer"
);
}
}
function _batchMint(
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) internal {
require(to != address(0), "to = 0 address");
require(ids.length == values.length, "ids length != values length");
for (uint256 i = 0; i < ids.length; i++) {
balanceOf[to][ids[i]] += values[i];
}
emit TransferBatch(msg.sender, address(0), to, ids, values);
if (to.code.length > 0) {
require(
IERC1155TokenReceiver(to).onERC1155BatchReceived(
msg.sender,
address(0),
ids,
values,
data
) == IERC1155TokenReceiver.onERC1155BatchReceived.selector,
"unsafe transfer"
);
}
}
function _burn(address from, uint256 id, uint256 value) internal {
require(from != address(0), "from = 0 address");
balanceOf[from][id] -= value;
emit TransferSingle(msg.sender, from, address(0), id, value);
}
function _batchBurn(
address from,
uint256[] calldata ids,
uint256[] calldata values
) internal {
require(from != address(0), "from = 0 address");
require(ids.length == values.length, "ids length != values length");
for (uint256 i = 0; i < ids.length; i++) {
balanceOf[from][ids[i]] -= values[i];
}
emit TransferBatch(msg.sender, from, address(0), ids, values);
}
}
contract MyMultiToken is ERC1155 {
function mint(uint256 id, uint256 value, bytes memory data) external {
_mint(msg.sender, id, value, data);
}
function batchMint(
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external {
_batchMint(msg.sender, ids, values, data);
}
function burn(uint256 id, uint256 value) external {
_burn(msg.sender, id, value);
}
function batchBurn(uint256[] calldata ids, uint256[] calldata values) external {
_batchBurn(msg.sender, ids, values);
}
}

View file

@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// simple example of contract written in bytecode
contract Factory {
event Log(address addr);
// Deploys a contract that always returns 42
function deploy() external {
bytes memory bytecode = hex"69602a60005260206000f3600052600a6016f3";
address addr;
assembly {
// create(value, offset, size)
addr := create(0, add(bytecode, 0x20), 0x13)
}
require(addr != address(0));
emit Log(addr);
}
}
interface IContract {
function getMeaningOfLife() external view returns (uint);
}
// https://www.evm.codes/playground
/*
Run time code - return 42
602a60005260206000f3
// Store 42 to memory
mstore(p, v) - store v at memory p to p + 32
PUSH1 0x2a
PUSH1 0
MSTORE
// Return 32 bytes from memory
return(p, s) - end execution and return data from memory p to p + s
PUSH1 0x20
PUSH1 0
RETURN
Creation code - return runtime code
69602a60005260206000f3600052600a6016f3
// Store run time code to memory
PUSH10 0X602a60005260206000f3
PUSH1 0
MSTORE
// Return 10 bytes from memory starting at offset 22
PUSH1 0x0a
PUSH1 0x16
RETURN
*/

View file

@ -0,0 +1,12 @@
## contracts boilerplates and examples to learn solidity
<br>
* **[gm_world.sol](1_gm_world.sol)**
* **[token.sol](2_token.sol)**
* **[wallet.sol](3_wallet.sol)**
* **[interable_mapping.sol](4_interable_mapping.sol)**
* **[erc20_interface.sol](5_erc20_interface.sol)**
* **[erc721_interface.sol](6_erc721_interface.sol)**
* **[erc1155.sol](7_erc1155.sol)**
* **[bytecode_contract.sol](8_bytecode_contract.sol)**

View file

@ -0,0 +1,26 @@
## ethereum token standards
<br>
### tl; dr
<br>
* EIP stands for **ethereum improvement proposals**.
* ERC stands for **ethereum request for comments** (technical documents written by ethereum developers for ethereum community).
* each such document contains a set of rules required to implement tokens for the ethereum ecosystem.
<br>
---
### in this dir
<br>
* **[ERC20](erc20.md)**
* **[ERC777](erc777.md)**
* **[ERC721](erc721.md)**
<br>

View file

@ -0,0 +1,67 @@
## ERC20
<br>
* defines a common interface for contracts implementing this token, such that any compatible token can be accessed and used in the same way.
* a transaction sending ether to an address changes the state of an address. a transaction transferring a token to an address only changes the state of the token contract, not the state of the recipient address.
* one of the main reasons for the success of EIP-20 tokens is in the interplay between `approve` and `transferFrom`, which allows for tokens to not
only be transferred between externally owned accounts (EOA).
- but to be used in other contracts under application specific conditions by abstracting away `msg.sender` as the mechanism for token access control.
* a limiting factor lies from the fact that the EIP-20 `approve` function is defined in terms of `msg.sender`.
- this means that users initial action involving EIP-20 tokens must be performed by an EOA.
- if the user needs to interact with a smart contract, then they need to make 2 transactions (`approve` and the smart contract internal call `transferFrom`), with gas costs.
<br>
---
### ERC20-compliant token contract
<br>
* `totalSupply`: Returns the total units of this token that currently exist. ERC20 tokens can have a fixed or a variable supply.
* `balanceOf`: Given an address, returns the token balance of that address.
* `transfer`: Given an address and amount, transfers that amount of tokens to that address, from the balance of the address that executed the transfer.
* `transferFrom`: Given a sender, recipient, and amount, transfers tokens from one account to another. Used in combination with approve.
* `approve`: given a recipient address and amount, authorizes that address to execute several transfers up to that amount, from the account that issued the approval.
* `allowance`: given an owner address and a spender address, returns the remaining amount that the spender is approved to withdraw from the owner.
* `Transfer`: event triggered upon a successful transfer (call to transfer or transferFrom) (even for zero-value transfers).
* `Approval`: event logged upon a successful call to approve.
<br>
---
### ERC20 optional functions
<br>
* in addition to the required functions listed in the previous section, the following optional functions are also defined by the standard:
- `name`: returns the human-readable name (e.g., "US Dollars") of the token.
- `symbol`: returns a human-readable symbol (e.g., "USD") for the token.
- `decimals`: returns the number of decimals used to divide token amounts. For example, if decimals is 2, then the token amount is divided by 100 to get its user representation.
<br>
---
### the ERC20 interface
<br>
```
contract ERC20 {
function totalSupply() constant returns (uint theTotalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns
(bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns
(uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
```

View file

@ -0,0 +1,42 @@
## ERC721
<br>
* to see the difference between ERC20 and ERC721, look at the internal data structure used in ERC721:
```
mapping (uint256 => address) private deedOwner;
```
<br>
* whereas ERC20 tracks the balances that belong to each owner, with the owner being the primary key of the mapping, ERC721 tracks each deed ID and who owns it, with the deed ID being the primary key of the mapping.
<br>
---
### the ERC721 contract interface specification
<br>
```
interface ERC721 /* is ERC165 */ {
event Transfer(address indexed _from, address indexed _to, uint256 _deedId);
event Approval(address indexed _owner, address indexed _approved,
uint256 _deedId);
event ApprovalForAll(address indexed _owner, address indexed _operator,
bool _approved);
function balanceOf(address _owner) external view returns (uint256 _balance);
function ownerOf(uint256 _deedId) external view returns (address _owner);
function transfer(address _to, uint256 _deedId) external payable;
function transferFrom(address _from, address _to, uint256 _deedId)
external payable;
function approve(address _approved, uint256 _deedId) external payable;
function setApprovalForAll(address _operator, boolean _approved) payable;
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
```

View file

@ -0,0 +1,58 @@
## ERC777
<br>
* an ERC20-compatible interface
* transfer tokens using a send function, similar to ether transfers
* compatible with ERC820 for token contract registration
* allow contracts and addresses to control which tokens they send through a tokensToSend function that is called prior to sending
* enable contracts and addresses to be notified of the tokens' receipt by calling a tokensReceived function in the recipient, and to reduce the probability of tokens being locked into contracts by requiring contracts to provide a tokensReceived function
* allow existing contracts to use proxy contracts for the `tokensToSend and `tokensReceived` functions
* operate in the same way whether sending to a contract or an EOA
* provide specific events for the minting and burning of tokens
* enable operators (trusted third parties, intended to be verified contracts) to move tokens on behalf of a token holder
* provide metadata on token transfer transactions in userData and operatorData fields
<br>
---
<br>
### ERC777 contract interface specification
```
interface ERC777Token {
function name() public constant returns (string);
function symbol() public constant returns (string);
function totalSupply() public constant returns (uint256);
function granularity() public constant returns (uint256);
function balanceOf(address owner) public constant returns (uint256);
function send(address to, uint256 amount, bytes userData) public;
function authorizeOperator(address operator) public;
function revokeOperator(address operator) public;
function isOperatorFor(address operator, address tokenHolder)
public constant returns (bool);
function operatorSend(address from, address to, uint256 amount,
bytes userData,bytes operatorData) public;
event Sent(address indexed operator, address indexed from,
address indexed to, uint256 amount, bytes userData,
bytes operatorData);
event Minted(address indexed operator, address indexed to,
uint256 amount, bytes operatorData);
event Burned(address indexed operator, address indexed from,
uint256 amount, bytes userData, bytes operatorData);
event AuthorizedOperator(address indexed operator,
address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
```

View file

@ -0,0 +1,8 @@
## workspaces
<br>
* **[i use foundry](foundry.md)**
* **[notes on using remix](remix.md)**
* **[a simple intro to hardhat with a `erc721` token](hardhat/)**
* **[old stuff: truffle](https://trufflesuite.com/)**

View file

@ -0,0 +1,31 @@
## foundry and tests in solidity
<br>
### tl; dr
<br>
* **[foundry](https://book.getfoundry.sh/forge/differential-ffi-testing)** is a set of tools for evm-based smart contract development and tests, written in rust.
<br>
---
### resources
<br>
* **[ethernaut solutions systematically on foundry](https://github.com/go-outside-labs/ethernaut-foundry-detailed-solutions-sol)**
* **[go-outside-labs experiments on foundry](https://github.com/go-outside-labs/blockchain-science-rs#experiments-with-foundry)**
* **[how to test smart contracts](https://betterprogramming.pub/how-to-test-ethereum-smart-contracts-35abc8fa199d)**
* **[how to mock solidity contracts](https://ethereum.org/en/developers/tutorials/how-to-mock-solidity-contracts-for-testing/)**
* **[in-depth guide to testing ethereum smart contracts](https://iamdefinitelyahuman.medium.com/an-in-depth-guide-to-testing-ethereum-smart-contracts-2e41b2770297)**

View file

@ -0,0 +1,5 @@
API_URL = "https://eth-rinkeby.alchemyapi.io/v2/<project>"
PUBLIC_KEY =
PRIVATE_KEY =
METADATA_URL = <pinata IPFS cid dor metadata>"
CONTRACT_ADDRESS =

View file

@ -0,0 +1,25 @@
## get started with hardhat + erc721
<br>
1. compile contract:
```
npx hardhat compile
```
<br>
2. deploy contract:
```
npx hardhat run scripts/deploy-contract.js --network rinkeby
```
<br>
3. mint NFT:
```
node scripts/mint-nft.js
```

View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.3 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MiaNFT is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() public ERC721("Mia's NFT, "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}

View file

@ -0,0 +1,19 @@
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require('dotenv').config();
require("@nomiclabs/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
solidity: "0.7.3",
defaultNetwork: "rinkeby",
networks: {
hardhat: {},
rinkeby: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`]
}
},
}

View file

@ -0,0 +1,23 @@
{
"name": "MiaNFT",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Mia Stein",
"license": "MIT",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@openzeppelin/contracts": "^3.1.0-solc-0.7",
"chai": "^4.3.4",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.6",
"hardhat": "^2.6.2"
},
"dependencies": {
"@alch/alchemy-web3": "^1.1.4",
"dotenv": "^10.0.0"
}
}

View file

@ -0,0 +1,14 @@
async function main() {
const nft = await ethers.getContractFactory("MiaNFT");
const nft_deploy = await nft.deploy();
console.log(" ⛓🧱✨ Contract address:", nft_deploy.address);
console.log(" ➡️ (Please add this string to .env)");
}
main()
.then(() => process.exit(0))
.catch(e => {
console.error(e);
process.exit(1);
});

View file

@ -0,0 +1,41 @@
require('dotenv').config();
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const API_URL = process.env.API_URL;
const METADATA_URL = process.env.METADATA_URL;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/MiaNFT.sol/MiaNFT.json");
const nftContract = new web3.eth.Contract(contract.abi, CONTRACT_ADDRESS);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest');
const transaction = {
'from': PUBLIC_KEY,
'to': CONTRACT_ADDRESS,
'nonce': nonce,
'gas': 500000,
'maxPriorityFeePerGas': 1999999987,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
const sign = web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
sign.then((signedTransaction) => {
web3.eth.sendSignedTransaction(signedTransaction.rawTransaction, function(e, hash) {
if (!e) {
console.log("💾 Transaction hash: ", hash);
} else {
console.log("ERROR:", e)
}
});
}).catch((e) => {
console.log("ERROR:", e);
});
}
mintNFT(METADATA_URL);

View file

@ -0,0 +1,68 @@
## remix IDE
<br>
* remix IDE is an open source web3 application and it's used for the entire journey of smart contract development.
<br>
<img width="414" alt="Screen Shot 2022-03-10 at 5 57 22 PM" src="https://user-images.githubusercontent.com/1130416/157715032-63dfbe5d-292d-48e3-8594-04902fb008f6.png">
<br>
* everything in Remix is a plugin. the plugin mamanger is the place to load functionalities and create your own plugins.
* by default, Remix stores files in workspaces, which are folders in the browser's local storage.
* you can publish all files from current workspace to a gist, using the gist API.
<br>
#### compiler (Solidity)
* you can compile (and deploy) contracts with versions of Solidity older than 0.4.12. however, the older compilers used a legacy AST.
* the "fork selection" dropdown list allows to compile code against a specific ehtereum hard fork.
<br>
#### optimization
* the optimizer tries to simplify complicated expressions, which reduces both code size and execution cost. It can reduce gas needed for contract deployment as well as for external calls made to the contract.
<br>
#### environment
* `JavaScript VM`: All transactions will be executed in a sandbox blockchain in the browser.
* `Injected Provider`: Metamaask is an example of a profiver that inject web3.
* `Web3 Provider`: Remix will connect to a remote node (you need to provide the URL to the selected provider: geth, parity or any ethereum client)
<br>
#### setup
* gas Limit: sets the amount of `ETH`, `WEI`, `GWEI` that is sent to ta contract or a payable function.
* deploy: sends a transaction that deplpys the selected contract.
* `atAdress`: used to access a contract whtat has already been deployed (does not cost gas).
* to interact with a contract using the ABI, create a new file in remix, with extension `.abi`.
* the Recorder is a tool used to save a bunch of transactions in a `json` file and rerun them later either in the same environment or in another.
* the Debugger shows the contract's state while stepping through a transaction.
* using generated sources will make it easier to audit your contracts.
* dtatic code analysis can be done by a plugin, so that you can examine the code for security vulnerabilities, bad development practices, etc.
* hardhat integration can be done with `hardhat.config.js` (Hardhat websocket listener should run at `65522`). hardhat provider is a plugin for Rrmix IDE.
<br>
#### generate artifacts
* when a compilation for a Solidity file succeeds, remix creates three `json` files for each compiled contract, that can be seen in the `File Explorers plugin`:
1. `artifacts/<contractName>.json`: contains links to libraries, the bytecode, gas estimation, the ABI.
2. `articfacts/<contractName_metadata>.json`: contains the metadata from the output of Solidity compilation.
3. `artifcats/build-info/<dynamic_hash>.json`: contains info about `solc` compiler version, compiler input and output.