Returns a JSON object that contains metadata of NFT
JSON contains an image part which contains url of the image
This URL can be hosted on chain or IPFS or whereever
Pinata
It is a paid service which helps us pin our NFT data
It is centralized (not recommended)
NFT.Storage
Free storage to Pin our NFT Data
Based on FileCoin Blockchain
It is decentralized hence
Encoding & Opcodes
When we send a transaction, it is compiled down to bytecode and sent in data object of transaction
When a contract is created, its block's to address is null
EVM reads the bytecode using a reader
Each alphabet in the bytecode corresponds to an optcode instruction like 00 means HALT
abi.encode && abi.encodePacked
It is a globally available method of solidity
Use Case : String Concatenation
It returns a byte object
In versions ^0.8.12, we can do string.concat(stringA, stringB)
contract Encoding {
function combineStrings() public pure returns (string memory) {
return string(abi.encodePacked("Hello", "World"));
}
}
Use Case : Others
Can encode anything to its binary
function encodeNumber() public pure returns(bytes memory) {
bytes memory number = abi.encode(1);
return number;
}
function encodeString() public pure returns(bytes memory) {
bytes memory str = abi.encode("Hello");
return str;
// 00000000000000000000000x71231821289730129300000000000000000000000
}
Normal encode method takes a lot of memory i.e initial zeros, hence, we use encodePacked which sorts of works like a compressor
TypeCasting works similar but somewhat different behind the scenes
function encodeStringPacked() public pure returns(bytes memory) {
bytes memory str = abi.encodePacked("Hello");
return str;
// 0x712318212897301293
}
abi.decode
We can also decode stuff
function decodeString() public pure returns (string memory) {
string memory str = abi.decode(encodeString(), (string));
return str;
}
We can also do multi decoding
function multiEncode() public pure returns (bytes memory) {
bytes memory str = abi.encode("Hello", "World");
return str;
}
function multiDecode() public pure returns (string memory, string memory) {
(string memory str1, string memory str2) = abi.decode(multiEncode(), (string, string));
return (str1, str2);
}
In order to decode packed objects, we must cast
function multiEncodePacked() public pure returns(bytes memory) {
bytes memory str = abi.encodePacked("Hello", "World");
return str;
}
function multiStringCastPacked() public pure returns (string memory) {
bytes memory str = string(multiEncodePacked());
return str;
}
Encoding Function Calls
call
We use call functions to change the state of the blockchain
In our {}, we are able to pass specific fields of transactions like value
In our (), we can pass data to call a specific function
Withdraw Use Case
We used call with data field empty to send or receive ether
function withdraw(address winner) public {
(bool success, bytes memory response) = winner.call{value: address(this).balance}("");
require(success, string(response));
}
Calling Other Functions Use Case
We need to encode the function name and parameters down to binary level
Each function has its function id known as function selector
It is first 4 bytes of the function signature
Function signature is a string that defines the function name and params
contract CallAnything {
address public s_someAddress;
uint256 public s_amount;
function transfer(address someAddress, uint256 amount) public {
s_someAddress = someAddress;
s_amount = amount;
}
function getSelectorOne() public pure returns(bytes4 selector) {
selector = bytes4(keccak256(bytes("transfer(address,uint256)")));
}
function getDataToCallTransfer(address someAddress, uint256 amount) public pure returns(bytes memory) {
return abi.encodeWithSelector(getSelectorOne(), someAddress, amount);
}
function callTransferFunctionDirectly(address someAddress, uint256 amount) public returns(bytes4, bool) {
(bool success, bytes memory returnData) = address(this).call(
getDataToCallTransfer(someAddress, amount)
);
return(bytes4(returnData), success);
}