- references
- https://www.oreilly.com/library/view/hands-on-smart-contract/9781492045250/
- https://www.amazon.com/Solidity-Programming-Essentials-building-contracts/dp/1803231181
- https://www.amazon.com/Beginning-Ethereum-Smart-Contracts-Programming/dp/1484292707
- https://www.springerprofessional.de/en/ethereum-smart-contract-development-in-solidity/18334966
- https://www.manning.com/books/blockchain-in-action
- https://www.packtpub.com/product/mastering-blockchain-programming-with-solidity/9781839218262
- https://www.algoexpert.io/blockchain/index
- https://chat.openai.com
- https://www.linkedin.com/pulse/what-token-burning-how-does-work-azhar-siddiqui
- https://medium.com/cryptronics/ethereum-smart-contract-security-73b0ede73fa8
- https://medium.com/immunefi/the-ultimate-guide-to-reentrancy-19526f105ac
- https://medium.com/neptune-mutual/understanding-signature-replay-attack-cbb70a7f46d8
- https://slowmist.medium.com/intro-to-smart-contract-security-audit-signature-replay-b71c23910629
- https://ethereum.stackexchange.com/questions/26/what-is-a-replay-attack
- https://mirror.xyz/0xbuidlerdao.eth/lOE5VN-BHI0olGOXe27F0auviIuoSlnou_9t3XRJseY
- https://stackoverflow.com/questions/74164255/x19ethereum-signed-message-n32-prefix-meaning
- https://ethereum.stackexchange.com/questions/128552/why-would-you-use-a-signed-message-to-verify-the-sender
- https://medium.com/mycrypto/the-magic-of-digital-signatures-on-ethereum-98fe184dc9c7
- https://medium.com/@kaishinaw/signing-and-verifying-ethereum-messages-f5acd41ca1a8
- ethers-io/ethers.js#555
- https://medium.com/@codetractio/walkthrough-of-an-ethereum-improvement-proposal-eip-6fda3966d171
- https://medium.com/@moplabs/eip-155-with-mopai-75b322962d1b
- https://medium.com/coinmonks/eip712-a-full-stack-example-e12185b03d54
- https://medium.com/coinmonks/ethereum-signatures-for-hackers-and-auditors-101-4da766cd6344
- https://blog.hook.xyz/validate-eip-712/
- https://ethereum.stackexchange.com/questions/125128/what-is-domain-separator-in-eip712
- https://kristaps.me/blog/solidity-eip-712-sign-ethers-js/
- https://dev.to/fassko/what-are-meta-transactions-the-eip-712-standard-and-how-to-sign-a-message-with-metamask-4mil
- https://medium.com/metamask/eip712-is-coming-what-to-expect-and-how-to-use-it-bb92fd1a7a26
- https://0xsomeone.medium.com/b002-solidity-ec-signature-pitfalls-b24a0f91aef4
- https://nfting.medium.com/what-is-a-replay-attack-23e39ebbb11a
- https://medium.com/coinmonks/what-the-heck-is-replay-protection-aae910f2a3cb
- https://medium.com/@MyPaoG/explaining-the-dao-exploit-for-beginners-in-solidity-80ee84f0d470
- https://medium.com/coinmonks/understanding-and-preventing-short-address-attacks-in-solidity-smart-contracts-3d46a4af9a15
- https://www.reddit.com/r/ethereum/comments/6r9nhj/cant_understand_the_erc20_short_address_attack/
- https://medium.com/huzzle/ico-smart-contract-vulnerability-short-address-attack-31ac9177eb6b
- https://medium.com/coinmonks/what-is-an-nft-and-how-does-it-works-1f85a5734f84
- https://johankristensson.medium.com/explaining-nft-technology-simplifying-the-basics-89aa539644ee
- https://medium.com/coinmonks/what-are-nfts-non-fungible-tokens-explained-f3a3d8d18ed7
- https://webdevelopmentkl.medium.com/nfts-explained-everything-you-need-to-know-323b6fa740d5
- https://medium.com/analytics-vidhya/what-is-nft-characteristics-and-use-cases-explained-ad7dc77f350f
- https://medium.com/@BryanBulte/nft-explained-simply-f4daee993096
- https://medium.com/coinmonks/nft-smart-contract-development-543d712003ef
- https://medium.com/@dropspace/nft-smart-contract-understanding-the-backbone-of-digital-collectibles-a1f91538c3be
- https://thebojda.medium.com/code-a-minimalistic-nft-smart-contract-in-solidity-on-ethereum-a-how-to-guide-hacker-noon-5cb72f0891c
- https://medium.com/@kaishinaw/creating-truly-decentralised-nfts-a-comprehensive-guide-to-erc721-ipfs-b2ae60e312b6
- https://medium.com/@JaysNotebook/learning-openzeppelin-everything-you-need-to-know-5152a0ad8c4
- https://medium.com/@ethdapp/using-the-openzeppelin-escrow-library-6384f22caa99
- https://stermi.medium.com/lets-play-ethernaut-ctf-learning-solidity-security-while-playing-1678bd6db3c4
- https://www.blockchain-council.org/ethereum/beginners-guide-what-is-erc20/
- https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
- https://www.ledger.com/academy/what-are-erc-tokens-and-why-do-we-use-them
- https://medium.com/@ajaotosinserah/introduction-to-erc20-token-ad30b7422db9
- https://medium.com/coinmonks/what-are-erc-20-tokens-afcccbc53962
- https://medium.com/theblockchainhub/erc20-introduction-part-1-e8c2062aed0f
- https://medium.com/coinmonks/solidity-lesson-28-understanding-the-erc-20-token-928758f053e1
- https://medium.com/@eiki1212/what-is-erc-20-explanation-of-details-eacf9f288f8b
- https://medium.com/@ankesh27/erc20-erc721-erc1155-token-standards-explained-f4d240a9c084
- https://medium.com/geekculture/code-your-own-erc-20-token-1678d9b381da
- https://medium.com/coinmonks/introduction-to-token-standards-for-ethereum-part-3-extensions-foundations-for-the-erc20-721-22bb727f072c
- https://medium.com/@lauraguy/what-is-an-erc20-token-d7320369bf76
- https://medium.com/hackernoon/the-innards-of-an-erc20-token-587c29e9b8a1
- https://medium.com/@danielque/what-we-learned-from-auditing-the-top-20-erc20-token-contracts-7526ef3b6fb1
- https://medium.com/@jgm.orinoco/understanding-erc-20-token-contracts-a809a7310aa5
- https://vitto.cc/how-to-create-and-deploy-an-erc20-token-in-20-minutes/
- https://medium.com/coinmonks/my-first-erc20-token-7d5d16632818
- https://medium.com/@infuyIT/understanding-erc-standards-a-guide-to-different-types-of-blockchain-tokens-e7991e4ecd61
- https://www.blockchain-council.org/ethereum/erc20-vs-erc721/
- https://medium.com/quick-programming/what-is-erc165-and-why-you-should-use-it-d0641a2f29e5
- https://medium.com/@chiqing/ethereum-standard-erc165-explained-63b54ca0d273
- https://ethereum.stackexchange.com/questions/83561/why-use-the-erc165-standard
- https://eips.ethereum.org/EIPS/eip-165
- https://docs.openzeppelin.com/contracts/
- https://techjd.medium.com/what-is-supportsinterface-understanding-erc165-503b40b942a6
- https://ethereum.stackexchange.com/questions/44880/erc-165-query-on-erc-721-implementation
- https://www.quicknode.com/guides/ethereum-development/nfts/how-to-create-and-deploy-an-erc-1155-nft#:~:text=What%20is%20ERC1155%3F,were%20required%20to%20achieve%20this.
- https://blog.thirdweb.com/what-is-erc-1155-nft/
- https://docs.openzeppelin.com/contracts/5.x/erc1155
- https://decrypt.co/resources/what-is-erc-1155-ethereums-flexible-token-standard
- https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/
- https://www.linkedin.com/pulse/erc-1155-demystified-deep-dive-versatile-token-asset-mukul-tripathi/
- https://moralis.io/erc-1155-nfts-what-is-the-erc-1155-standard/
- https://limechain.tech/blog/erc-721-vs-erc-1155-ethereum-token-standards/
- https://www.leewayhertz.com/erc-20-vs-erc-721-vs-erc-1155/
- https://mightyblock.co/blog/news/how-to-build-and-use-erc-1155-tokens/
- https://medium.com/@konradmgnat/what-is-the-erc1155-token-and-when-to-use-it-fc53b5d001ca
- https://medium.com/codex/token-standards-erc-20-vs-erc-721-vs-erc-1155-2e4a09dc0f8a
- https://medium.com/envienta-open-source-everything/erc-1155-non-fungible-tokens-on-steroids-71aab96fa674
- https://pancy.medium.com/building-event-tickets-with-erc-1155-contract-1c427d89a77d
- https://soenkeba.medium.com/truly-decentralized-nfts-by-erc-1155-b9be28db2aae
- https://medium.com/@Consensys/an-introduction-to-ipfs-9bba4860abd0#:~:text=At%20its%20core%2C%20IPFS%20is,also%20a%20distributed%20file%20system.
- https://whatdoesthequantsay.com/2015/09/13/ipfs-introduction-by-example/
- https://medium.com/geekculture/what-is-ipfs-the-inter-planetary-file-system-explained-40744a7ae95a
- https://medium.com/aleph-im/ipfs-explained-in-2min-24e10afdb191
- https://medium.com/@sjarancio/ipfs-what-it-is-how-it-works-and-why-its-needed-49b75d8e857b
- https://medium.com/virtuslab/the-complete-beginners-guide-to-ipfs-9713a6f59193
- https://medium.com/@akshay_111meher/how-ipfs-works-545e1c890437
- https://ipfs.io/ipfs/QmRU1jJ1kNd9fTzjFwM4X9YtA2wfXN1W2eFK7mgTMJ8xgK
- https://medium.com/coinmonks/the-technology-behind-ipfs-and-what-can-ipfs-do-c7009fe42bab
- https://itsromiljain.medium.com/ipfs-the-permanent-distributed-web-7a0d3ede10af
- https://itsromiljain.medium.com/ipfs-the-permanent-distributed-web-continues-ffbe1919bb94
- https://medium.com/pinata/what-is-an-ipfs-pinning-service-f6ed4cd7e475
- https://medium.com/hackernoon/ipfs-and-merkle-forest-a6b7f15f3537
- https://medium.com/hackernoon/a-beginners-guide-to-ipfs-20673fedd3f
- https://medium.com/textileio/swapping-bits-and-distributing-hashes-on-the-decentralized-web-5da98a3507
- https://medium.com/textileio/whats-really-happening-when-you-add-a-file-to-ipfs-ae3b8b5e4b0f
- https://medium.com/textileio/how-ipfs-peer-nodes-identify-each-other-on-the-distributed-web-8b5b6476aa5e
- https://medium.com/@Nico_Vergauwen/create-your-own-ethereum-token-part-2-erc223-3076f764cf62
- https://ethereum.stackexchange.com/questions/120996/what-is-the-difference-between-safetransferfrom-and-transferfrom-functions-i
- https://ethereum.stackexchange.com/questions/73125/what-is-the-bytes-data-param-in-safetransferfrom
- it may be worthwile to take a look here: https://github.com/mtumilowicz/solidity-basics-workshop
- goals of this workshop
- know best practices when developing smart contract with Solidity
- understand some basic attacks & how to protect against them
- introduction to tokens
- erc20, erc721, erc1155
- enumerating some standard design patterns in Solidity
- understanding basic of IPFS
- introduction to OpenZeppelin
- workshop task
- implement CustomNftToken - no third party libraries
- no third party libraries
- mint - everyone, but cost 1 eth
- transfer - only owner of token can transfer it
- withdraw - only owner of contract can get money
- implement tests in Solidity
- rewrite it using Ownable from OpenZeppelin
- no third party libraries
- OpenZeppelin
- implement Erc721Token
- should define miner role and restrict minting only to miners
- should define admin role and restrict burning only to admins
- uri should be configurable per every token (ipfs case)
- implement Erc20Token
- implement Erc1155Token
- fungible: GOLD, SILVER, SWORD, SHIELD
- non-fungible: THOR_HAMMER
- implement Erc721Token
- implement CustomNftToken - no third party libraries
* don't use plain secret on-chain
* problem: front-running attacks
* all the transaction data is open and can be seen by others
* even data of pending transaction can be seen by others
* example: domain name registration
* user is registering a unique value
* attacker watch for the transactions on that contract
* send the high gas-price transaction to front run the user's transaction
* solution: commit-and-reveal scheme
* hash of the original secret is submitted to the blockchain
* steps
1. all parties submit their secret hash
1. all parties reveal their choice by submitting salt (that was used to generate the secret hash)
* don't use `tx.origin` for authorization
* problem: intercepting transaction
1. we have `Vault` contract, that has function `withdraw()` using `tx.origin` for authorization
1. attacker deploys AttackerContract
1. attacker ask the original owner of the Vault contract to send some ether to the AttackerContract
1. AttackerContract calls the Vault.withdraw() function
* solution: always use `msg.sender`
* avoid dependency on untrusted external calls
* problems
* if the target contract is killed via selfdestruct, the external call to the function will always fail
* reentrancy attack
* re-enter origin contract before the state changes are finalized
* unpredictable gas costs
* reentrancy attacks
* problem: re-enter origin contract before the state changes are finalized
* example (2016 dao hack)
```
function withdraw(uint amount) public {
require(balanceOf[msg.sender] >= amount);
msg.sender.call{value: amount}(""); // invokes fallback function in caller, which in turns invokes withdraw again
balanceOf[msg.sender] -= amount;
Withdrawal(msg.sender, amount);
}
```
* solution: checks-effects-interactions (CEI) pattern
* example
```
function withdraw(uint amount) public {
// Checks phase
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
// Effects phase
balanceOf[msg.sender] -= amount;
// Interactions phase
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// Emit event after successful interaction
emit Withdrawal(msg.sender, amount);
}
```
* replay attack
* problem
* cross-chain signature replay
* example
```
function deposit() public payable { // reply transaction from testnet on the mainnet
balances[msg.sender] += msg.value;
}
```
* usually happen during chain splits or hard forks
* Ethereum: signed transaction is valid for all Ethereum chains
* Bitcoin: addresses in testnet use a different prefix from addresses in mainnet
* keys are different
* solution: use chainId when generating a signature
* same-chain signature replay
* example
```
function deposit() public payable { // attacker rebroadcasts the same transaction with the same parameters
balances[msg.sender] += msg.value;
}
```
* solution: use nonce when generating a signature
* solution
* two approaches
* strong replay protection
* one of the forked chains will make it mandatory to change some information in the transaction for it to be valid over its network
* opt-in reply protection
* users must make manual changes to the transaction to ensure that they won’t be replayed
* example: Ethereum Classic (ETC) did not implement strong replay protection during the hard fork
* EIP155
* called "simple replay attack protection"
* defines the chainID field in Ethereum transactions to prevent replay attacks
* before EIP155
* there are 6 inputs to an Ethereum transaction
* nonce, gasPrice, gasLimit, to, value, data
* transaction is not chain specific
* same addresses in different networks => can lead to unintended transactions
* user should sign the data along with a unique nonce value each time
* example
```
mapping(address => uint256) public nonces;
function deposit(uint256 nonce) public payable {
require(nonce > nonces[msg.sender], "Invalid nonce");
nonces[msg.sender] = nonce;
balances[msg.sender] += msg.value;
}
```
* every transaction signature should also encapsulate a unique identifier for the specific network
* EIP191
* called "signature data standard"
* introduces a prefix to the data that is being signed
* example
```
"\x19Ethereum Signed Message:\n32"
```
* byte `0x19` standardized because an already existing implementation (in the Go Ethereum client software)
was using it before the standard was finalized
* last number `32` is the byte length of the message (excluding the prefix)
* reason for prefixing is so that a cleverly designed message cannot possibly be a valid transaction
* allowing signing raw messages, without a prefix, enables an app to steal all ether, tokens and assets
* purpose is entirely to invalidate any payload as a valid RLP encoded transaction
* MetaMask does not permit you to perform this operation
* it will always force prefixing a signed message even when the message is a hash
* when you create a transaction: unsigned transaction -> hash it -> sign it
* when you create a message: unsigned message -> prefix it -> hash it -> sign it
* example
```
let unsignedTransaction = "0xe980850218711a00825208948ba1f109551bd432803012645ac136ddd64dba72880de0b6b3a764000080";
```
decoded with https://flightwallet.github.io/decode-eth-tx/
```
{
"nonce": 0,
"gasPrice": 9000000000,
"gasLimit": 21000,
"to": "0x8ba1f109551bd432803012645ac136ddd64dba72",
"value": 1000000000000000000,
"data": ""
}
```
* if attacked gives you this hash and you sign it => it is now a valid signed transaction which will send 1 ether to attacker
* string that begins with "\x19Ethereum Signed Message:" is not a valid transaction
* it is safe to sign it
* eliminates the risk of replay attacks on other EVM platforms
* if there were no platform-specific prefixes, the resulting signature would be the same for all platforms
* use case
* smart contract needs to verify a signed message
* external systems need to interact with Ethereum transactions in a standardized way
* list of chain ids: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md?ref=blog.hook.xyz#list-of-chain-ids
* EIP712
* is a more advanced and secure method of signing a transaction
* provides a way to hash and sign typed structs rather than just strings
* uses the keccak256 hashing algorithm
* requires TypedData (JSON object input)
* example
```
typedData := apitypes.TypedData{
Types: types,
PrimaryType: "ERC721Order",
Domain: domain,
Message: message,
}
```
* includes the following properties
* types
* used to define the structs that will be used in the message and specify their types
* is a mapping of string to a Type array
* example
```
types := apitypes.Types{
"EIP712Domain": {
{Name: "name", Type: "string"},
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
},
"ERC721Order": {
{Name: "direction", Type: "uint8"},
...
},
"Fee": { // custom types
{Name: "recipient", Type: "address"},
{Name: "amount", Type: "uint256"},
{Name: "feeData", Type: "bytes"},
},
```
* domain
* information specific to the protocol contract that the dapp used when asking for a signature
* designed to include bits of DApp unique information
* name
* human-readable string that represents the name of the domai
* often used to identify the dApp or smart contract associated with the message
* version
* string representing the version of the domain
* can be useful for distinguishing between different versions of the same dApp or smart contract
* chainId
* wallet providers should prevent signing if it does not match the network it is currently connected to
* it is crucial that chainId is verified on-chain
* contracts have no way to find out which chain ID they are on
* developers must hardcode chainId into their contracts and take extra care to make sure that it corresponds to the network they deploy on
* verifyingContract
* address of the smart contract that will verify the signed message
* domain separator
* information from the domain also needs to be hashed and used as a domain separator
* purpose is to disambiguate between two dapps with identical structures
* in order to avoid generating the same signatures for both
* example
* two DApps come up with an identical structure like Transfer(address from,address to,uint256 amount)
* with domain separator the dApp developers are guaranteed that there can be no signature collision
* primaryType
* string that represents the outermost type of the message object
* message
* contains the order element names as strings mapped to their values
* example
```
{
amount: 100,
token: “0x….”,
id: 15,
bidder: {
userId: 323,
wallet: “0x….”
}
}
```
can be split into two data structures
```
Bid: {
amount: uint256,
bidder: Identity
}
Identity: {
userId: uint256,
wallet: address
}
```
* example
```
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.M
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
hashBytes := keccak256(rawData)
hash := common.BytesToHash(hashBytes)
```
* standard for secure off-chain signature verification on the Ethereum blockchain
* signature verification
* process of checking that the address of the signer is equal to the address that you derive from the signature
![Alt Text](img/verifying_signature.png)
* ecrecover
* is vulnerable
* it interprets v values of both 27 and 28 as equivalent
* it only checks if v is greater than or equal to 27
* signatures produced with both v = 27 and v = 28 will yield the same public key
* it does not halt execution when invalid signatures are supplied
* it simply returns the address 0x0
* zero address in most contracts has a special meaning (i.e. burn address)
* smart contract might incorrectly assume that the zero address is a valid signer
* use: OpenZeppelin’s ECDSA library
* used to derive the address of a sender based on the digital signature
* needs assembly
* example
* replicate this formatting/hash function
```
struct Identity {
uint256 userId;
address wallet;
}
struct Bid {
uint256 amount;
Identity bidder;
}
string private constant IDENTITY_TYPE = "Identity(uint256 userId,address wallet)";
string private constant BID_TYPE = "Bid(uint256 amount,Identity bidder)Identity(uint256 userId,address wallet)";
uint256 constant chainId = 1;
address constant verifyingContract = 0x1C56346CD2A2Bf3202F771f50d3D14a367B48070;
bytes32 constant salt = 0xf2d857f4a3edcb9b78b4d503bfe733db1e3f6cdc2b7971ee739626c97e86a558;
string private constant EIP712_DOMAIN = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)";
bytes32 private constant DOMAIN_SEPARATOR = keccak256(abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256("My amazing dApp"),
keccak256("2"),
chainId,
verifyingContract,
));
function hashIdentity(Identity identity) private pure returns (bytes32) {
return keccak256(abi.encode(
IDENTITY_TYPEHASH,
identity.userId,
identity.wallet
));
}
function hashBid(Bid memory bid) private pure returns (bytes32){
return keccak256(abi.encodePacked(
"\\x19\\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
BID_TYPEHASH,
bid.amount,
hashIdentity(bid.bidder)
))
));
function verify(address signer, Bid memory bid, sigR, sigS, sigV) public pure returns (bool) {
return signer == ecrecover(hashBid(bid), sigV, sigR, sigS);
}
```
* wallets like Metamask can display the message in a more user-friendly manner
* the signer can actually check the data before signing
* display in a human-readable way that users can understand and review before signing
* example
![Alt Text](img/eip721_metamask.png)
* before EIP712
* it was difficult for users to verify the data they were asked to sign
* wallet signing interfaces would display a hashed message string
* the signer would have to assume that hash matched the message they thought they were signing
* use case
* meta-transactions
* relayer pays the gas fees on behalf of the user
* relayer = service responsible for submitting transactions on behalf of users
* users sign a request (off-chain) for a specific action they want to perform on the blockchain
* includes information like the target contract, function to call, and any required parameters
* short address attack
* equivalent of minor SQL injection bug
* problem: leading zeros is taken from the amount, and given to the shortened address
* * means you've multiplied your amount by 1<<8 or 256
* after the exchange has checked your balance on their internal ledger
* reason: EVM pads all input data with 0s
* example
1. user B's address: 0xbbbbbb00
1. user A's balance: 512 tokens
1. A inputs B's address as 0xbbbbbb
1. site (incorrectly) interprets this as a valid address and constructs transaction
* selector: 0x01020304
* transaction: 0x01020304bbbbbbbb00000001
1. if we slice that back up into a 4-byte signature and 2 4-byte words
* ['0x01020304', '0xbbbbbb00', '0x000001??']
1. any index into the transaction data that hasn't been provided returns 0s
* trailing zeros do not change the actual address
* `0x1234...5670` and `0x1234...567000` represent the same address
1. final argument being interpreted by the EVM as 0x00000100 = 256
* 256
1. given that 256 is still less than 512 (your comment), the transaction succeeds
* most vulnerable to this were large, shared wallets
* example: exchange hot wallets
* solution: smart contract must validate the length of an address input
* manipulating contract balance
* problem: ether can be sent forcibly to a contract
* if contract has some decision logic using `address(this).balance` - attacked can influence it
* example
```
selfdestruct(addressOfAttackedContract)
```
* solution: there is no possible way to prevent forceful ether sending from happening
* Ethernaut game
* Web3/Solidity based war game created by OpenZeppelin
* each level is a smart contract that needs to be ‘hacked’
* solutions: https://stermi.medium.com/lets-play-ethernaut-ctf-learning-solidity-security-while-playing-1678bd6db3c4
* security
* pull-over-push (withdrawal pattern)
* example of the problem
```
for(uint i = 0; i < users.length; i++) { users[i].transfer(amount); };”
```
* if some address is a contract it may have continually failing fallback function
* leads to whole transaction failure each time
* solution: user should be able to claim their dividend from the contract
* use cases
* send ether/token to multiple addresses
* avoid paying transaction fees (push transaction)
* transaction initiator has to pay the transaction fee
* users pay transaction fees (pull transaction)
* access restriction
* restricts unauthorized function calls
* based on roles
* use modifiers to check for the access rights
* emergency stop
* ability to pause the contract functions in unwanted situations
* use cases
* contract to be handled differently in case of any emergency situations
* creational patterns
* factory
* create a new child contract from a parent contract
* https://eips.ethereum.org/EIPS/eip-1167
* example
* master contract can create a new child contract called Loan
* Loan contract has logic to handle contract terms and conditions along with the funds as well
* use case
* new contract is required for each request to be processed
* keep the funds separate in a different contract
* behavioral patterns
* state machine
* allows a contract to transition from different states
* enables certain functions to be executed in each state
* use cases
* contract needs to have different set of functions based on the state
* iterable map pattern
* example
```
mapping(uint256 => uint256) private data;
uint256[] private keys;
function removeValue(uint256 key) external {
require(data[key] != 0, "Key does not exist");
for (uint256 i = 0; i < keys.length; i++) {
if (keys[i] == key) {
// Swap the element to be removed with the last element
keys[i] = keys[keys.length - 1];
// Shorten the keys array by one
keys.pop();
break;
}
}
delete data[key];
}
```
* allows to iterate over the mapping entries
* iteration over the mapping entries should not cause an out-of-gas exception
* iteration should be used only in the view function
* does not support removal of elements
* use cases
* need to filter some data out of the mapping
* whitelisted addresses
* maintain a curated list of addresses by the owner
* use cases
* whitelisted address allowed/disallowed to perform a certain task
* gas-optimization
* worth to check: https://github.com/mtumilowicz/ethereum-gas-workshop
* keccak256 for equality check
* example: string equality
* use case
* gas-optimized solution for equality
* variable packing
* minimize slots used by storage
* each storage slot is 32 bytes
* use case
* gas-optimized solution for storage
* life cycle
* “Once a contract is destroyed, it cannot be recreated on the same address. ”
* mortal pattern allows a contract to be destroyed from the Ethereum blockchain.”
* “The mortal pattern should be used in the following cases, when:
* You do not want a contract to be present on the blockchain once its job is finished
* You want the ether held on the contract to be sent to the owner and the contract is not required further
* You do not need the contract state data after the contract reaches a specific state”
* auto deprecate
* allows time-based access to certain function calls
* example (using chainLink oracle)
```
modifier onlyPremium() {
require(subscriptionExpiry[msg.sender] >= getCurrentTime(), "Must be a premium member");
_;
}
function getCurrentTime() internal returns (uint256) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
req.add("get", "https://chain.link/v1/time");
req.add("path", "now");
return sendChainlinkRequestTo(oracle, req, fee);
}
```
* use cases
* allow/restrict a function call for a specified duration of time
* auto-expired contract
* periodic paid service-based model
* existing user can purchase a premium status for a limited duration
- stands for Interplanetary File System
- suppose humanity has colonises Mars and the first person on Mars tries to access internet services from Earth
- it would approximately take 1 hour for him to access a news website
- what if another person tries?
- he ends up taking another 1 hour and so on
- but IFPS is used, the second person on Mars shall be able to retrieve the content from the first person
- from there onwards data can spread like a wildfire
- three main principles
- unique identification via content addressing
- means that the content is going to determine the address
- once something is added it can’t be changed
- intuitive way to think about content for humans
- example: ask someone for their favorite cat video
- location answer: "the one on this server, at this sub-domain, under this file path, slash hilarious dash cat dot mp4"
- description answer: "the one where the cat knocks the glass off the counter"
- is generally not how we access content on the web today
- http
- refers objects (text files, pics, videos) by which server they are stored on
- called "location based addressing"
- location is the IP address or the domain name
- if location isn’t accessible (the server is down), you won’t get resources
- there is a high probability that someone else out there has downloaded resource and still has a copy of it
- yet your computer won’t be able to grab it from that other person
- there is a high probability that someone else out there has downloaded resource and still has a copy of it
- IPFS moves from "location based addressing" to "content based addressing"
- instead of creating an identifier that addresses things by location
- address it by some representation of the content itself
- if in your browser you want to access a particular page then IPFS will ask the entire network "does anyone have this file that corresponds to this hash?"
- instead of saying where to find a resource, you just say what it is you want
- instead of creating an identifier that addresses things by location
- called "location based addressing"
- is great for loading websites but it wasn’t designed for the transfer of large amounts data
- example: audio, video files
- files are downloaded from one server at a time
- IPFS retrieves pieces of files from multiple nodes at once
- enables bandwidth savings of up to 60% for things like videos
- IPFS retrieves pieces of files from multiple nodes at once
- enabled the emergence and mainstream success of alternative filesharing systems
- example: Napster (music) and BitTorrent (movies and pretty much anything)
- refers objects (text files, pics, videos) by which server they are stored on
- http
- example: ask someone for their favorite cat video
- mechanism
- take a file
- hash it cryptographically
- very small and secure representation of the file
- hashes are actually something called a multihash
- specifies which hash function it used
- specifies the length of the resultant hash in the first two bytes of the multihash
- example: hashes all seem to start with Qm
- 12 denotes that this is the SHA256
- output length is 20 in hex (or 32 bytes)
- we get the Qm from when we base58 encode the whole thing
- address usually starts with a hash that identifies some root object and then a path walking down
- instead of a server, you are talking to a specific object and then you are looking at a path within that object
- means that the content is going to determine the address
- content is linked via Merkle Directed Acyclic Graphs (DAGs)
- hashes are used to reference data blocks and objects in a DAG
- similar to Merkle tree
- difference: Merkle DAG non-leaf nodes are allowed to contain data
- need not be balanced
- example: https://explore.ipld.io/#/explore/QmWNj1pTSjbauDHpdyg5HQ26vYcNWnubg1JehmwAE9NnU9
- de-duplication by design
- we reference content (not the files themselves)
- adding a new file to the network will take less storage as the network gets larger
- assuming the file is relatively similar to others in the network
- content discovery system is facilitated via Distributed Hash Tables (DHTs)
- information about which node stores what blocks is organized as a distributed hash table
- split across the nodes just like data itself
- small values (equal to or less than 1KB) are stored directly on the DHT
- for larger values, the DHT stores references (NodeIds of peers who can serve the block)
- ask the network = query the distributed hash table
- based on Kademlia
- pretty common is p2p systems
- example: find peers that can provide a particular bit of content
ipfs dht findprovs QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ
- information about which node stores what blocks is organized as a distributed hash table
- unique identification via content addressing
- distributed file system that seeks to connect all computing devices with the same system of files
- small file (< 256 kB)
- represented by an IPFS object
- data = the file contents (plus a small header and footer)
- no links, i.e. the links array is empty
- represented by an IPFS object
- large file (> 256 kB)
- represented by an IPFS object
- data = specifying that this object represents a large file
- links = list of links to file chunks that are < 256 kB
- represented by an IPFS object
- directory is represented by a list of links to IPFS objects representing files or other directories
- IPFS object
- example
$ ipfs object get QmarHSr9aSNaPSR6G9KFPbuLV9aEqJfTk1y9B8pdwqK4Rq // normally referred to by their Base58 encoded hash { "Links": [ { "Name": "AnotherName", "Hash": "QmVtYjNij3KeyGmcgg7yVXWskLaBtov3UYL9pgcGK3MCWu", "Size": 18 }, { "Name": "SomeName", "Hash": "QmbUSy8HCn8J4TMDRRdxCbK2uCCtkQyZtY6XYv3y7kLgDC", "Size": 58 } ], "Data": "Hello World!" }
- Data - a blob of unstructured binary data of size < 256 kB
- Links - an array of Link structures (links to other IPFS objects)
- Name — the name of the Link
- Hash — the hash of the linked IPFS object
- Size — the cumulative size of the linked IPFS object, including following its links
- note that the file name is not part of the IPFS object
- two files with different names and the same content => same IPFS object representation
- and hence the same hash
- two files with different names and the same content => same IPFS object representation
- example
- small file (< 256 kB)
- is essentially a P2P system for retrieving and sharing IPFS objects
- acts as a decentralized source of data
- distributed: processing is shared across multiple nodes
- decisions may still be centralized and use complete system knowledge
- decentralized: no single point where the decision is made
- every node makes a decision for it’s own behaviour
- resulting system behaviour is the aggregate response
- distributed: processing is shared across multiple nodes
- self-certifying file system
- peer node can perform authentication and encryption
- when you initialize a new peer IPFS generates a pair of keys for it (private and public)
$ ipfs init initializing IPFS node at ~/.ipfs/ generating 2048-bit RSA keypair...done peer identity: Qm...
- when you initialize a new peer IPFS generates a pair of keys for it (private and public)
- peers are identifying each other via their peer ID
- is essentially a cryptographic hash of it’s public key
- enables peers to find each other and authenticate themselves once they get connected
- when two peers connect to each other they exchange public keys
- connections between peers are encrypted and authenticated by default
- authenticity of this data can be verified using the sender’s public key
- peer node can perform authentication and encryption
- operations
- retrieving
- when a node retrieves data from the network it keeps a local cache of that data for future usage
- nodes frequently clear this cache out in order to make room for new content
- HTTP -> IPFS gateway
- we can access any one of our files from their website
- https://ipfs.github.io/public-gateway-checker/
- example
- anyone can access your data provided that they know the hash
- use combination of symmetric and asymmetric encryption
- retrieving files from peer nodes and not some trusted centralized server
- trust: how can you be sure that the file you requested hasn’t been tampered with?
- since we are using a hash to request the file, you can verify what you received
- upon receiving a file, you can check if the hash of the file received matches the hash of the file requested
- any change to the file itself will change the hashed address
- when a node retrieves data from the network it keeps a local cache of that data for future usage
- adding
- each node chooses which file to store
- long-term IPFS storage: Filecoin
- each node chooses which file to store
- pinning
- act of saving data on a node
- prevents important data from being deleted from your node when the clearing process happens
- you can only control and pin data on your node(s)
- cannot force other nodes on the IPFS network to pin your content for you
- to guarantee your content stays pinned, you have to run your own IPFS nodes
- by pinning the file, other nodes on the network know they can access the file
- retrieving
- is similar to a single BitTorrent swarm exchanging git objects
- IPFS = one big swarm of peers for all data
- difference: in BitTorrent each file has a separate swarm of peers (forming a P2P network with each other)
- BitTorrent
- is a successful and widely implemented protocol used to share large data files in a distributed manner
- example: digital video sharing (TV shows, movies, video clips, etc)
- splits large data files into segments
- distributed over different nodes of a peer-to-peer network
- result: data is being shared from more than one source
- we are not exhausting a single server
- is a successful and widely implemented protocol used to share large data files in a distributed manner
- IPFS = one big swarm of peers for all data
- BitSwap
- their own exchange protocol
- data trading module for IPFS
- two primary jobs
- attempt to acquire blocks from the network that have been requested by the client peer (your local peer)
- judiciously (though strategically) send blocks of data that it already has in its possession to other peers who want those blocks
- example
- verify that wantlist is empty (we aren’t in the middle of requesting anything)
ipfs bitswap wantlist
- get a large file from the network
ipfs get QmdpAidwAsBGptFB3b6A9Pyi5coEbgjHrL3K2Qrsutmj9K // Big Buck Bunny video
- query wantlist again
ipfs bitswap wantlist // multiple hashes that are being requested from the network
- verify that wantlist is empty (we aren’t in the middle of requesting anything)
- consensus algorithm: Raft
- peers coordinate their state
- example: the list of CIDs which are pinned, their peer allocations and replication factor
- is used to commit log entries to a "distributed log" which every peer follows
- example: every "Pin" and "Unpin" requests are log entries in that log
- when a peer receives a log "Pin" operation, it updates its local copy of the shared state
- indicates that the CID is now pinned
- when a peer receives a log "Pin" operation, it updates its local copy of the shared state
- example: every "Pin" and "Unpin" requests are log entries in that log
- Leader
- election can only succeed if at least half of the nodes are online
- only peer allowed to commit entries to the log
- required for other parts of ipfs-cluster functionality (initialization, monitoring)
- peers coordinate their state
- blockchain
- problem: on the Ethereum platform you pay a rather large fee for storing data in the associated state database
- minimizes bloat of the state database ("blockchain bloat")
- solution: store not the data itself but hash of the data
- distinction between storing a hash on the blockchain and storing the data on the blockchain becomes somewhat blurred
- example: store an IPFS link in the blockchain
- we can seamlessly follow this link to access the data as if the data was stored in the blockchain itself
- problem: on the Ethereum platform you pay a rather large fee for storing data in the associated state database
- any asset that is digitally transferable between two people
- smart contract representing digital assets
- simple "databases" on the blockchain
- example
- ERC20: stores balances of Ethereum addresses
- ERC721: unique ID -> Ethereum address assignments
- example
- simple "databases" on the blockchain
- types
- fungible asset
- means that the individual units of an asset are interchangeable and essentially indistinguishable from one another
- not unique and divisable entity
- example: currency
- $50 is always $50
- non-fungible asset
- means that the individual units of an asset are distinct and unique
- often possessing specific attributes, characteristics, or properties that set them apart from one another
- cannot be exchanged on a one-to-one basis
- unique and indivisible entity
- examples
- education and certification
- Einstein diploma is not the same as Oppenheimer diploma
- diamonds
- aren’t interchangeable as they all have different cuts, colours and sizes
- can’t swap one diamond for another because they won’t be guaranteed to hold the same value
- education and certification
- means that the individual units of an asset are distinct and unique
- fungible asset
- history
- Bitcoin established the paradigm for other crypto projects: in order to issue any digital currency, a separate blockchain must be launched
- rule has been broken by Ethereum: smart contacts enabled to create a token and assign it unique useful functions within your own application
- ability for any developer to release their digital asset without the need for a separate blockchain has become a turning point in the history of cryptocurrencies
- coin
- native digital asset or cryptocurrency of a blockchain
- example:
- bitcoin blockchain => bitcoin (symbol: BTC)
- ethereum blockchain => ether (symbol: ETH)
- token
- digital asset or cryptocurrency that is built on top of an existing blockchain is called a token
- example (ERC20-compliant tokens built on the Ethereum blockchain)
- Maker (symbol: MKR)
- Augur (symbol: REP)
- Bitcoin established the paradigm for other crypto projects: in order to issue any digital currency, a separate blockchain must be launched
- problem: supporting an increasing number of tokens became increasingly difficult
- in order for the exchange or wallet to support the token, the creators had to write new code each time
- example: prior to ERC-20,there was a problem with token compatibility because each of them had a unique smart contract
- solution: standard protocol for all tokens (ERC)
- play a pivotal role in ensuring interoperability and compatibility among different smart contracts and decentralized applications (DApps)
- ERCs are analogous to RFCs
- Ethereum Improvement Proposals (EIPs): focused on the Ethereum protocol
- decentralization
- nearly half of the top 20 projects can have their token transfers completely frozen by an owner (a single key or a multisig contract)
- pausing can be valuable for future upgrades, swaps, and disaster mitigation but also leads to new risks
- nearly half of the top 20 projects can have their token transfers completely frozen by an owner (a single key or a multisig contract)
- example: Tether (USDT)
- value is pegged to the US dollar at a 1:1 ratio
- commonly used to move funds between exchanges quickly and easily
- use cases
- reputation points of any online platform
- lottery tickets and schemes
- financial assets such as shares, dividends, and stocks of a company
- fiat currencies, including USD
- gold ounce
- funglible
- six functions, two events, and three information
- functions
- totalSupply - returns the total supply of tokens that have been created for a particular project
- problem: Solidity and the Ethereum Virtual Machine do not support decimals: only integer numbers can be used
- solution: use larger integer values (the EVM supports 256-bit integers)
- example: total supply 1000 tokens, with 18 decimal places (like Ethereum) = 1000*10**18
_mint(msg.sender, 1000 * 10**18); // mining 1000 tokens transfer(recipient, 2 * 10**18); // sending 2 tokens
- example: total supply 1000 tokens, with 18 decimal places (like Ethereum) = 1000*10**18
- solution: use larger integer values (the EVM supports 256-bit integers)
- problem: Solidity and the Ethereum Virtual Machine do not support decimals: only integer numbers can be used
- balanceOf - returns the balance of tokens held by a particular address
- transfer - allows an address to send tokens to another address
- the only transaction that happens on the blockchain is the contract call
- transferring tokens from one account to another = simply update internal variable “_balances”
- problem: Solidity and the Ethereum Virtual Machine do not support decimals: only integer numbers can be used
- solution: token contract can use larger integer values (the EVM supports 256-bit integers)
- example: 1000000000000000000 represents 1 ETH (with 18 decimal places)
- transfer of 4000000000000000 will correspond to 0.004ETH being sent
- example: 1000000000000000000 represents 1 ETH (with 18 decimal places)
- solution: token contract can use larger integer values (the EVM supports 256-bit integers)
- token transfers are received at the contract silently
- smart contract does not explicitly notify or acknowledge the receipt of tokens
- solutions
- ERC223 standard provides
tokenFallback
method that will be called once the tokens are transferredfunction transfer(address to, uint value, bytes data) { uint codeLength; assembly { codeLength := extcodesize(_to) // user wallets do not have code associated with them } balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); if(codeLength>0) { // Require proper transaction handling. ERC223Receiver receiver = ERC223Receiver(_to); receiver.tokenFallback(msg.sender, _value, _data); } }
- approve plus transferFrom mechanism
- example
contract DEX { Erc20 public token; constructor(address _tokenAddress) { token = Erc20(_tokenAddress); } function tradeTokens(address _buyer, uint256 _amount) external { // Assume _amount is the number of tokens the buyer wants to purchase require(token.transferFrom(_buyer, address(this), _amount), "Token transfer failed"); // DEX now holds _amount of tokens // Perform trading logic here // ... } }
- example
- ERC223 standard provides
- approve - allows an address to approve another address to spend tokens on their behalf
- used by decentralized exchanges
- there are two types of exchanges
- centralized exchange
- example: Binance
- you have to send your cryptocurrency coin or token to the exchange's account
- then they allow you to trade on their platform
- trust issue: coins and tokens are held on an exchange's account
- if the exchange's account is hacked, you will lose your cryptocurrencies
- decentralized exchange
- example: Uniswap
- all coins/token are kept in your wallet only
- directly trade via exchange platform
- centralized exchange
- there are two types of exchanges
- front-running approval attack
- problem: you change your approve amount to a given contract
- example: reduce the amount approved from 1 ETH to 0.5 ETH
- approved contract can race to transfer the money you initially approved (the 1 ETH)
- and then also spend the money you just approved (0.5 ETH)
- approved contract can race to transfer the money you initially approved (the 1 ETH)
- example: reduce the amount approved from 1 ETH to 0.5 ETH
- solutions
- use the increaseApproval() or decreaseApproval() functions
- ensures that, before updating the value, it should be set to zero
require(_value == 0 || allowed[msg.sender][_spender] == 0);
- problem: you change your approve amount to a given contract
- used by decentralized exchanges
- transferFrom - allows an address to transfer tokens from another address that has approved them to do so
- prerequisite: approver must have called the approve() function prior
- if called from within a Solidity contract recommended to enclose with require
require(ERC20.transferFrom(from, to, value)) // in case of any transfer failure, the transaction should revert
- allowance - returns the amount of tokens that an approved address can spend on behalf of another address
- developers can also add additional functions and features
- example: OpenZeppelin implementation of ERC20 contracts
- _mint(), _burn(), _burnFrom(), etc
- example: OpenZeppelin implementation of ERC20 contracts
- totalSupply - returns the total supply of tokens that have been created for a particular project
- information
- name - returns the name of the token
- symbol - returns the symbol of the token
- usually a few letters or characters that represent the token
- decimals - returns the number of decimal places that the token can be divided into
- example: a token with 18 decimal places can be divided into 10^18 units
- event
- transfer(address indexed _from, address indexed _to, uint256 _value)
- triggered whenever tokens are transferred
- minting emits transfer event with the 0 address as the source
- approval(address indexed _owner, address indexed _spender, uint256 _value)
- triggered on any call to approve() function
- transfer(address indexed _from, address indexed _to, uint256 _value)
- functions
- example: domain name
- use cases
- unique digital content piece
- through royalty implementations in the smart contract, original creators can receive a predetermined percentage of sales whenever the NFT changes hands
- real estate property
- social media content Tweets, Videos, and pictures
- gaming assets and collectibles
- gaming characters
- unique digital content piece
- non-fungible
- each NFT has a numerical identifier (uint256) called TokenID
- is a digital receipt of something you purchased
- digital certificates of authenticity
- example: when you buy an NFT, you are buying a piece of data that points to a server that hosts that image
- so, what you own, is not the access to the server, and not the image itself, but rather that tiny piece of data that points to the server
- represents a class of assets, whereas an ERC20 token represents a particular type of asset
- entire collection comes from a single ERC-721 contract, with each item having its own TokenID
- smart contracts define the ownership and transfer rights of a particular digital asset
- case studies
- art
- artists don’t have to rely on gallery shows or live auctions to be able to sell their art
- monetize their projects without the physical resources needed to do that in the real world
- artists don’t have to rely on gallery shows or live auctions to be able to sell their art
- collectibles
- sports leagues including the NFL, MLB and NBA have all created digital collections
- gaming and virtual reality
- players will invest more when digital assets can be
- transferred between games or platforms
- traded on open markets, they will invest more of their hard-earned cash
- players will invest more when digital assets can be
- licenses and certifications
- easily verifiable with university smart contract
- art
- functions
- erc20 like
- balanceOf(address _owner)
- transferFrom(address _from, address _to, uint256 _tokenId)
- approve(address _approved, uint256 _tokenId)
- setApprovalForAll(address _operator, bool _approved)
- getApproved(uint256 _tokenId)
- isApprovedForAll(address _owner, address _operator)
- function ownerOf(uint256 _tokenId)
- function safeTransferFrom(address _from, address _to, uint256 _tokenId)
- ensure the destination address can handle the NFT, preventing accidental loss
- is used to check if the address receiving the token is an ERC-721 receiver or not
- ERC721TokenReceiver
- minimal acceptable feature for onERC721Received is to do nothing and this is how they have implemented it
- goal is to ensure that your NFT does not get locked up in an address from which you can never retrieve it
- ERC721TokenReceiver
- function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data)
- allows for additional data to be passed to the receiving contract
- example
then
{ // _data "rarity": "Rare", "strength": 80, "abilities": ["Flying", "Fire Breath"] }
contract MyCardRegistry { struct CardAttributes { string rarity; uint256 strength; string[] abilities; } mapping(uint256 => CardAttributes) public cardAttributes; function parseDataToCardAttributes(bytes calldata _data) internal pure returns (CardAttributes memory) { // Parse JSON data (string memory rarity, uint256 strength, string[] memory abilities) = abi.decode(_data, (string, uint256, string[])); return CardAttributes({ rarity: rarity, strength: strength, abilities: abilities }); } function onCardReceived(address _sender, uint256 _cardId, bytes calldata _data) external returns (bool) { // Parse the _data and update cardAttributes with the received attributes CardAttributes memory attributes = parseDataToCardAttributes(_data); // Add the card to the registry cardAttributes[_cardId] = attributes; return true; } }
- developers can also add additional functions and features
- example: OpenZeppelin implementation of ERC721 contracts
- tokenApprovals, operatorApprovals, etc
- example: OpenZeppelin implementation of ERC721 contracts
- erc20 like
- information
- erc20 like
- name()
- symbol()
- tokenURI(tokenId)
- points to the metadata
- allowing platforms and wallets to display the NFT’s distinct attributes
- problem: centralized nft
- returns the metadata location in the form of:
HTTP://centralizedserver.com/TokenID
- here is nothing stopping the image host from changing the image which the URL points to
- example: https://example.com/nft/1
{ "name": "One Ring to Rule Them All", "description": "The One Ring, forged in the fires of Mount Doom, grants immense power to its bearer.", "image": "https://example.com/one_ring.jpg", "attributes": [ { "trait_type": "Type", "value": "Artifact" }, { "trait_type": "Rarity", "value": "Legendary" }, { "trait_type": "Power", "value": "Dominion over all other rings" }, { "trait_type": "Owner", "value": "Sauron" } ], "external_url": "https://example.com/one_ring", "franchise": "The Lord of the Rings", "lore": "Forged by the Dark Lord Sauron to control the other Rings of Power, the One Ring is a malevolent artifact of great evil." }
- example: https://example.com/nft/1
- here is nothing stopping the image host from changing the image which the URL points to
- solution: IPFS
- problem: cannot set
ipfs://contendidentifierhash/TokenID
- IPFS can only provide an immutable hierarchical file system structure
- requirement: all tokens and TokenIDs are set at the time of the smart contract deployment
- solution: TokenID as a pointer itself to the metadata
TokenID=IPFScontentidentifierhash
- idea by Titusz Pan
- we would then just add "ipfs://" in front
- example
then convert it to hex “-b=base16”
$ ipfs add MetaDataIPFSToken.json cid-version=1 hash=blake2b-208 added bafkzvzacdkm3bu3t266ivacqjowxqi3hvpqsyijxhsb23rv7nj7a MetaDataIPFSToken.json
without f0 gives a token ID$ ipfs cid format -b=base16 bafkzvzacdkm3bu3t266ivacqjowxqi3hvpqsyijxhsb23rv7nj7a f01559ae4021a99b0d373d7bc8a80504bad782367abe12c21373c83adc6bf6a7e
constructor() public ERC1155("ipfs://f0{id}") { // "f0" is not part of the CID, but rather a representation of the format being used (base16)
- problem: cannot set
- returns the metadata location in the form of:
- erc20 like
- events
- erc20 like
- event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
- event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
- event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
- erc20 like
- OpenSea
- refers to itself as the "amazon of NFTs"
- has a large and wide variety of different types of NFTs
- before
- problem: game with 100,000 items = 100,000 smart contracts
- ERC-1155 developer Witek Radomski pointed out: that's like needing a different phone for each app you use
- solution: ERC1155
- allows for multiple token collections to be launched in just one smart contract
- problem: game with 100,000 items = 100,000 smart contracts
- called fungible-agnostic standard
- allows multiple token to be represented by a single contract, regardless of fungibility
- allows the creation of fungible, non-fungible, and semi-fungible tokens all in one contract
- semi-fungible tokens
- possesses characteristics of both fungible and non-fungible tokens
- certain units of the token may be interchangeable with other units
- others may have distinct characteristics, making them distinguishable from one another
- example: trading cards
- fungible: can still be traded within the same collection
- non-fungible: not completely interchangeable with each other
- possesses characteristics of both fungible and non-fungible tokens
- semi-fungible tokens
- makes use of TokenID
- each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes
- supply is only 1 <=> NFT
- each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes
- supports secure atomic swaps
- all tokens are inside one contract
- use case
- blockchain-based decentralized games, as games need coins and collectibles
- creating and managing digital art and collectible tokens with different levels of rarity, editions, and properties
- can save costs by managing tokens in batches (approval, transfer and balance) instead of individually
- functions
- safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes calldata _data)
- token transfers to other contracts may revert with the following message: ERC1155: transfer to non ERC1155Receiver implementer
- implementation: ERC165 - interface detection
- means that the recipient contract has not registered itself as aware of the ERC1155 protocol => transfers to it are disabled
- example: Golem contract
- currently holds over 350k GNT tokens (multiple tens of thousands of dollars) and lacks methods to get them out of there
- has happened to virtually every ERC20-backed project, usually due to user error
- currently holds over 350k GNT tokens (multiple tens of thousands of dollars) and lacks methods to get them out of there
- transfer call must revert if: _to address is 0
- token transfers to other contracts may revert with the following message: ERC1155: transfer to non ERC1155Receiver implementer
- safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data)
- any number of items can be sent in a single transaction to one or more recipients
- reducing transaction costs and complexity
- balanceOf(address _owner, uint256 _id)
- differs from ERC20: has an additional id argument for the identifier of the token that you want to query the balance of
- balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids)
- setApprovalForAll(address _operator, bool _approved)
- intentionally designed with simplicity in mind: can only approve everything for one address
- isApprovedForAll(address _owner, address _operator)
- safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes calldata _data)
- events
- TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value)
- TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values)
- ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved)
- must implement all of the functions in the ERC1155TokenReceiver interface to accept transfers
- onERC1155Received
- onERC1155BatchReceived
- before
- determining a smart contract’s supported interfaces was a challenging and gas-consuming process
- some interaction might involve sending test transactions to the contract and observing the behavior
- if the contract didn't support the desired interface, the transaction would fail or return an unexpected result
- problem: interfaces
- are not explicitly represented on the blockchain
- used by developers during the coding and development process
- applications must usually simply trust they are not making an incorrect call
- contract declaring its interface can be very helpful in preventing errors
- solution: ERC165
- known as the Standard Interface Detection
- we can publish and detect all interfaces a smart contract implements
- accounts simply declare their interfaces
- they are not required to actually implement them
- must not be relied on for security
- example
- publish
contract MyContract is ERC165 { function supportsInterface(bytes4 interfaceID) external view override returns (bool) { return interfaceID == type(ERC165).interfaceId; // defined as the XOR of all function (it implements) selectors } }
- verify
function checkIfContractSupportsERC165(address contractAddress) external view returns (bool) { ERC165 erc165 = ERC165(contractAddress); return erc165.supportsInterface(INTERFACE_ID_ERC165); }
- publish
- accounts simply declare their interfaces
- use case
- play a vital role in enabling seamless interactions between smart contracts and DApps
- example: OpenSea - NFT Marketplace
- uses ERC165 to check if a given contract supports the ERC721 standard, which is the most common standard for non-fungible tokens
- if a contract doesn't support ERC721 (as indicated by the supportsInterface call), OpenSea will handle it differently
- example: OpenSea - NFT Marketplace
- play a vital role in enabling seamless interactions between smart contracts and DApps
- an open-source library of protocols, templates, & utilities for smart contract development
- includes implementations for token standards, flexible role-based permissioning schemes, & reusable components
- in particular: offers implementations for ERC20 , ERC721, & ERC1155