# 12. NFTs | Encoding

### NFTs

* Stands for Non Fungible Token
  * 1 token is unique from all others
* Based on **ERC-721** standard
* Contains a TokenURI
  * 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**
    * More details at [evm.codes](https://evm.codes)

#### 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)`

```sol
contract Encoding {
	function combineStrings() public pure returns (string memory) {
		return string(abi.encodePacked("Hello", "World"));
	}
}
```

**Use Case : Others**

* Can encode anything to its binary

```sol
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

```sol
function encodeStringPacked() public pure returns(bytes memory) {
	bytes memory str = abi.encodePacked("Hello");
	return str;
	// 0x712318212897301293
}
```

#### abi.decode

* We can also **decode** stuff

```sol
function decodeString() public pure returns (string memory) {
	string memory str = abi.decode(encodeString(), (string));
	return str;
}
```

* We can also do multi decoding

```sol
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

```sol
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

```sol
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

```solidity
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);
	}
```

```sol
	function callTransferFunctionDirectlySig(address someAddress, uint256 amount) public returns(bytes4, bool) {
		(bool success, bytes memory returnData) = address(this).call(
			abi.encodeWithSignature("transfer(address,uint256)", someAddress, amount)
		);
		return(bytes4(returnData), success);
	}
}
```

#### staticcall

* We use staticcall to call our `view` or `pure` functions

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://notes.nomanaziz.me/development/blockchain/freecodecamp-course/12.-nfts-or-encoding.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
