diff --git a/evm/10_quick-start/03_evm-tokens.md b/evm/10_quick-start/03_evm-tokens.md index 4a5aa51e..5f96f0e1 100644 --- a/evm/10_quick-start/03_evm-tokens.md +++ b/evm/10_quick-start/03_evm-tokens.md @@ -78,21 +78,6 @@ Most EOS exchanges also require a `memo` field, so make sure you enter it there ## Other known tokens -> ⚠ **Disclaimer:** -> -> Please note that none of these tokens or smart contracts are endorsed by the EOS Network Foundation. -> -> Engaging with these must be done at the risk of the user. -> All the resources within this document are to be taken as educational material and NOT financial advice. Trading and/or investing in -> cryptocurrency and/or any related commodities/securities/derivatives/instruments is extremely high risk and you can very easily -> lose all of your investment capital! - | Symbol | Token Name | Address | |-----------|-----------------|------------------------------------------------------------------------| -| USDT | Multichain USDT | 0xfA9343C3897324496A05fC75abeD6bAC29f8A40f | -| WETH | Multichain WETH | 0x922D641a426DcFFaeF11680e5358F34d97d112E1 | -| WBTC | Multichain WBTC | 0xeFAeeE334F0Fd1712f9a8cc375f427D9Cdd40d73 | -| USDC | Multichain USDC | 0x765277EebeCA2e31912C9946eAe1021199B39C61 | -| DAI | Multichain DAI | 0x818ec0A7Fe18Ff94269904fCED6AE3DaE6d6dC0b | -| BNB | Multichain BNB | 0x461d52769884ca6235B685EF2040F47d30C94EB5 | | WEOS | Wrapped EOS | 0xc00592aA41D32D137dC480d9f6d0Df19b860104F | diff --git a/evm/999_miscellaneous/10_endpoints.md b/evm/10_quick-start/10_endpoints.md similarity index 100% rename from evm/999_miscellaneous/10_endpoints.md rename to evm/10_quick-start/10_endpoints.md diff --git a/evm/23_advanced-guides/20_using-multicall3.md b/evm/23_advanced-guides/20_using-multicall3.md new file mode 100644 index 00000000..cd578b1c --- /dev/null +++ b/evm/23_advanced-guides/20_using-multicall3.md @@ -0,0 +1,538 @@ +--- +title: Using Multicall3 +--- + +Multicall3 is a contract deployed to the EOS EVM that allows you to batch multiple calls into a single call. This means +that you can make multiple calls to one or more contracts in a single transaction. + +This is useful for reducing the number of transactions you need to make to either read or write data. For instance, if +you need to read data from multiple contracts, you can use Multicall3 to read all of the data in a single transaction so that +you can ensure: + +- You pay the lowest cost possible for the transaction +- You get the data form the same block +- You don't have to wait for multiple transactions to complete +- You don't have to worry about the order of the transactions (race conditions) +- You don't have to worry about **one** of the transactions failing (all or nothing) + +## Using it + +Because Multicall3 is a contract, you can use it with any EVM compatible JavaScript library. In this example we will be using +[Ethers](https://docs.ethers.io/). + +### Grabbing the ABI + +In order to use the Multicall3 contract, you will need to grab the ABI. You can find the ABI from the [Multicall3 website](https://www.multicall3.com/abi#json) +or in this expandable section: + +
+ Multicall3 ABI + +```json +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "returnData", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowFailure", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call3[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowFailure", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call3Value[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3Value", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "blockAndAggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getBasefee", + "outputs": [ + { + "internalType": "uint256", + "name": "basefee", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getBlockHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "chainid", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockCoinbase", + "outputs": [ + { + "internalType": "address", + "name": "coinbase", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockDifficulty", + "outputs": [ + { + "internalType": "uint256", + "name": "difficulty", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockGasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "gaslimit", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "getEthBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastBlockHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "requireSuccess", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryAggregate", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "requireSuccess", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryBlockAndAggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + } +] +``` +
+ + +### Writing the code + +Once you've saved the JSON ABI, you can now use it with Ethers. + +```javascript +import { Contract, Interface, JsonRpcProvider } from 'ethers'; + +// Import the ABI you copied +import MULTICALL3_ABI from "./multicall3.json"; + +// This is the address of the contract on mainnet +const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11'; + +const RPC_URL = 'https://api.evm.eosnetwork.com/'; + +const test = async () => { + const provider = new JsonRpcProvider(RPC_URL, undefined, { + batchMaxCount: 1 + }); + const multicall = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider); + const _interface = new Interface(MULTICALL3_ABI); + + const blockNumber = await provider.getBlockNumber(); + console.log('blockNumber', blockNumber); + const block = await provider.getBlock(blockNumber); + const blockHash = block?.hash; + + const calls = [ + { + target: MULTICALL3_ADDRESS, + allowFailure: false, + callData: _interface.encodeFunctionData('getEthBalance', [MULTICALL3_ADDRESS]), + }, + { + target: MULTICALL3_ADDRESS, + allowFailure: false, + // WEOS address + callData: _interface.encodeFunctionData('getEthBalance', ['0xc00592aA41D32D137dC480d9f6d0Df19b860104F']), + } + ]; + + type Aggregate3Response = { success: boolean; returnData: string }; + const results: Aggregate3Response[] = await multicall.aggregate3.staticCall(calls); + + for (const { success, returnData } of results) { + console.log('success', success); + console.log('returnData', returnData); + + // Decode the returnData + const decoded = _interface.decodeFunctionResult('getEthBalance', returnData); + console.log('decoded', decoded); + } +} + +test(); +``` + +The code above sets up a `provider`, instantiates the `multicall` contract, and then calls the `aggregate3` function with the `calls` array. + +The `aggregate3` function returns an array of `success` and `returnData` values. + +The `returnData` is encoded, so you need to decode it using the `decodeFunctionResult` function from the `Interface` class. + diff --git a/evm/23_advanced-guides/index.md b/evm/23_advanced-guides/index.md new file mode 100644 index 00000000..8b3e428b --- /dev/null +++ b/evm/23_advanced-guides/index.md @@ -0,0 +1,4 @@ +--- +title: Advanced Guides +sidebar_class_name: sidebarhidden +--- diff --git a/evm/25_configure-tooling/10_ethers.md b/evm/25_configure-tooling/10_ethers.md new file mode 100644 index 00000000..1404bcbf --- /dev/null +++ b/evm/25_configure-tooling/10_ethers.md @@ -0,0 +1,19 @@ +--- +title: Ethers +--- + + + +[Ethers](https://github.com/ethers-io/ethers.js) is by far the most used JavaScript SDK for EVM. + +Using Ethers with EOS EVM is the same as using it with any other EVM compatible chain. +One thing to note is that as of writing this doc, our RPC endpoint does not support batch requests, so you must disable +batching when creating your provider: + +```javascript +import { JsonRpcProvider } from "ethers"; + +const provider = new JsonRpcProvider("https://api.evm.eosnetwork.com/", undefined, { + batchMaxCount: 1 +}); +``` diff --git a/evm/40_architecture/20_token-economy.md b/evm/40_architecture/20_token-economy.md index 3ab329b9..097a18d3 100644 --- a/evm/40_architecture/20_token-economy.md +++ b/evm/40_architecture/20_token-economy.md @@ -59,11 +59,6 @@ like MetaMask and connect it to the EVM. Then, they are able to purchase EOS nat in and out of the EOS EVM through utilizing the trustless bridge. This makes for a seamless user experience, where the end user is not required to interact with EOS native at all. -The long term goal for EOS EVM is to enable functionality to support multiple tokens in the future besides EOS. In the -meantime, the Multichain bridge will make it easy for dApps to bring their tokens into the EOS EVM economy. - -![EOS EVM Bridge To EOS](/images/EOS-EVM_bridge_to_EOS.png) - ## EOS EVM Gas Model The gas model is another important part of the EOS EVM economy. All EVMs require a native token to cover the transaction