diff --git a/.gitbook/assets/Editted.jpeg b/.gitbook/assets/Editted.jpeg new file mode 100644 index 0000000..08af04f Binary files /dev/null and b/.gitbook/assets/Editted.jpeg differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.08.46.png b/.gitbook/assets/Screenshot 2023-10-18 at 10.08.46.png new file mode 100644 index 0000000..2b9568b Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.08.46.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.jpeg b/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.jpeg new file mode 100644 index 0000000..a81e7b5 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.jpeg differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.png b/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.png new file mode 100644 index 0000000..30820b0 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.32.28.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.41.55.png b/.gitbook/assets/Screenshot 2023-10-18 at 10.41.55.png new file mode 100644 index 0000000..bfc7b2f Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.41.55.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.42.36.png b/.gitbook/assets/Screenshot 2023-10-18 at 10.42.36.png new file mode 100644 index 0000000..13cd6be Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.42.36.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 10.43.19.png b/.gitbook/assets/Screenshot 2023-10-18 at 10.43.19.png new file mode 100644 index 0000000..762b196 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 10.43.19.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.12.39.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.12.39.png new file mode 100644 index 0000000..28f9054 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.12.39.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.25.28.jpeg b/.gitbook/assets/Screenshot 2023-10-18 at 11.25.28.jpeg new file mode 100644 index 0000000..a78b82a Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.25.28.jpeg differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.35.26.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.35.26.png new file mode 100644 index 0000000..f1c6eb3 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.35.26.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.38.17.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.38.17.png new file mode 100644 index 0000000..ecba882 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.38.17.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.40.41.jpeg b/.gitbook/assets/Screenshot 2023-10-18 at 11.40.41.jpeg new file mode 100644 index 0000000..d448ba8 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.40.41.jpeg differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.43.06.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.43.06.png new file mode 100644 index 0000000..5fec745 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.43.06.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.48.03.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.48.03.png new file mode 100644 index 0000000..1bba526 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.48.03.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 11.55.47.png b/.gitbook/assets/Screenshot 2023-10-18 at 11.55.47.png new file mode 100644 index 0000000..38b657c Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 11.55.47.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-18 at 13.35.40.png b/.gitbook/assets/Screenshot 2023-10-18 at 13.35.40.png new file mode 100644 index 0000000..9b8d7a7 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-18 at 13.35.40.png differ diff --git a/.gitbook/assets/Screenshot 2023-10-19 at 11.51.55.jpeg b/.gitbook/assets/Screenshot 2023-10-19 at 11.51.55.jpeg new file mode 100644 index 0000000..abefb60 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-19 at 11.51.55.jpeg differ diff --git a/.gitbook/assets/Screenshot 2023-10-19 at 11.58.25.jpeg b/.gitbook/assets/Screenshot 2023-10-19 at 11.58.25.jpeg new file mode 100644 index 0000000..b92bf35 Binary files /dev/null and b/.gitbook/assets/Screenshot 2023-10-19 at 11.58.25.jpeg differ diff --git a/.gitbook/assets/Untitled (1).png b/.gitbook/assets/Untitled (1).png new file mode 100644 index 0000000..85b09c1 Binary files /dev/null and b/.gitbook/assets/Untitled (1).png differ diff --git a/.gitbook/assets/Untitled 1.png b/.gitbook/assets/Untitled 1.png new file mode 100644 index 0000000..f4bd05a Binary files /dev/null and b/.gitbook/assets/Untitled 1.png differ diff --git a/.gitbook/assets/Untitled 2.png b/.gitbook/assets/Untitled 2.png new file mode 100644 index 0000000..631a010 Binary files /dev/null and b/.gitbook/assets/Untitled 2.png differ diff --git a/.gitbook/assets/Untitled 3.png b/.gitbook/assets/Untitled 3.png new file mode 100644 index 0000000..d3f5c0b Binary files /dev/null and b/.gitbook/assets/Untitled 3.png differ diff --git a/.gitbook/assets/Untitled.png b/.gitbook/assets/Untitled.png new file mode 100644 index 0000000..85b09c1 Binary files /dev/null and b/.gitbook/assets/Untitled.png differ diff --git a/.gitbook/assets/telegram-cloud-document-5-6156712275945197082.jpg b/.gitbook/assets/telegram-cloud-document-5-6156712275945197082.jpg new file mode 100644 index 0000000..218b74f Binary files /dev/null and b/.gitbook/assets/telegram-cloud-document-5-6156712275945197082.jpg differ diff --git a/.gitbook/assets/telegram-cloud-document-5-6156712275945197084.jpg b/.gitbook/assets/telegram-cloud-document-5-6156712275945197084.jpg new file mode 100644 index 0000000..ad852d2 Binary files /dev/null and b/.gitbook/assets/telegram-cloud-document-5-6156712275945197084.jpg differ diff --git a/.gitbook/assets/telegram-cloud-photo-size-5-6156712276401437077-y.jpg b/.gitbook/assets/telegram-cloud-photo-size-5-6156712276401437077-y.jpg new file mode 100644 index 0000000..bd9a26b Binary files /dev/null and b/.gitbook/assets/telegram-cloud-photo-size-5-6156712276401437077-y.jpg differ diff --git a/SUMMARY.md b/SUMMARY.md index 9334961..f4368b5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -17,28 +17,23 @@ * [Staking Requirements](general/staking/staking-requirements.md) * [Staking Rewards](general/staking/staking-rewards.md) * [Developer Guide](developer-guide/README.md) + * [Library](developer-guide/library/README.md) + * [Web3JS](developer-guide/smart-contract-development/web3js.md) + * [thirdweb CLI](developer-guide/smart-contract-development/thirdweb-cli.md) + * [Ethers.js](developer-guide/library/ethers.js.md) * [Standards & Specification](developer-guide/standards-and-specification/README.md) * [An Overview of the Token Standards on TomoChain](developer-guide/standards-and-specification/an-overview-of-the-token-standard-on-tomochain.md) * [TRC20 Specification](developer-guide/standards-and-specification/trc20-specification.md) * [TRC25 Specification](developer-guide/standards-and-specification/trc25-specification.md) - * [Smart Contract Development](developer-guide/smart-contract-development/README.md) - * [Solidity](developer-guide/smart-contract-development/solidity/README.md) - * [A Simple Smart Contract](developer-guide/smart-contract-development/solidity/a-simple-smart-contract.md) - * [Installing Solidity compiler](developer-guide/smart-contract-development/solidity/installing-solidity-compiler.md) - * [Solidity by Example](developer-guide/smart-contract-development/solidity/solidity-by-example.md) - * [IDEs and Tools](developer-guide/smart-contract-development/ides-and-tools/README.md) - * [Remix](developer-guide/smart-contract-development/ides-and-tools/remix.md) - * [Web3JS](developer-guide/smart-contract-development/ides-and-tools/web3js.md) - * [Truffle](developer-guide/smart-contract-development/ides-and-tools/truffle.md) - * [Openzepplins](developer-guide/smart-contract-development/ides-and-tools/openzepplins.md) - * [thirdweb CLI](developer-guide/smart-contract-development/ides-and-tools/thirdweb-cli.md) + * [Deploy a Smart Contract](developer-guide/smart-contract-development/README.md) + * [Remix](developer-guide/smart-contract-development/remix.md) + * [Hardhat](developer-guide/smart-contract-development/hardhat.md) * [Build a Dapp on TomoChain](developer-guide/building-dapp-on-tomochain/README.md) * [Setup Environment](developer-guide/building-dapp-on-tomochain/setup-environment.md) * [Write the Smart Contract](developer-guide/building-dapp-on-tomochain/writing-the-smart-contract.md) - * [How to Migrate Dapps from Ethereum](developer-guide/building-dapp-on-tomochain/how-to-migrate-dapp-from-ethereum.md) * [Develop a Simple Web3 Frontend to interact with the contract](developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/README.md) * [Interacting with the Dapp in a browser](developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/interacting-with-the-dapp-in-a-browser.md) - * [Source Code](https://github.com/DuqueKarl/tomochain-pet-shop) + * [Source Code](https://github.com/NomicFoundation/hardhat-boilerplate) * [Integration](developer-guide/integration/README.md) * [TomoChain Staking Governance](developer-guide/integration/tomochain-staking-governance.md) * [Exchange/Wallet Integration](developer-guide/integration/exchange-wallet-integration.md) @@ -47,8 +42,8 @@ * [TomoChain Network](developer-guide/working-with-tomochain/README.md) * [TomoChain Mainnet](developer-guide/working-with-tomochain/tomochain-mainnet.md) * [TomoChain Testnet](developer-guide/working-with-tomochain/tomochain-testnet.md) + * [TomoChain Localnet setup](developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md) * [CLI Commands](developer-guide/working-with-tomochain/cli-commands.md) - * [TomoChain Private Testnet Setup](developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md) * [Tutorials](developer-guide/tutorials/README.md) * [How to Deploy a TRC20/TRC21 Token on TomoChain](developer-guide/tutorials/how-to-deploy-a-trc21-token-on-tomochain.md) * [How to deploy an ICO smart contract on TomoChain](developer-guide/tutorials/how-to-deploy-an-ico-smart-contract-on-tomochain.md) diff --git a/developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/interacting-with-the-dapp-in-a-browser.md b/developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/interacting-with-the-dapp-in-a-browser.md index 8adf49c..f9f2acb 100644 --- a/developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/interacting-with-the-dapp-in-a-browser.md +++ b/developer-guide/building-dapp-on-tomochain/develop-a-simple-web3-frontend-to-interact-with-the-contract/interacting-with-the-dapp-in-a-browser.md @@ -16,7 +16,7 @@ _Initiating MetaMask_ 5\. Let’s now connect MetaMask to TomoChain (testnet). Click the menu with the “Main Ethereum Network” and select **Custom RPC**. Use the [Networks data from TomoChain](../../../general/how-to-connect-to-tomochain-network/metamask.md) (testnet) and click **Save** -![](https://miro.medium.com/max/60/1\*Dm4qhGJOjnolRwxX-VN94w.png?q=20)![](https://miro.medium.com/max/1424/1\*Dm4qhGJOjnolRwxX-VN94w.png) +![](https://miro.medium.com/max/60/1\*Dm4qhGJOjnolRwxX-VN94w.png?q=20) ![](https://miro.medium.com/max/1424/1\*Dm4qhGJOjnolRwxX-VN94w.png) _Connecting MetaMask to TomoChain (testnet)_ @@ -26,48 +26,53 @@ We could use the TOMO wallet we created previously, but better **let’s create 7\. Once you have created your new TOMO wallet, **copy the private key**. Back to MetaMask, click on the top-right circle and select **Import Account.** Paste the private key and _voilà_! Your TOMO wallet is loaded in MetaMask -![](https://miro.medium.com/max/60/1\*AjEHidU-h0Ae0CXTsQUJ5Q.png?q=20)![](https://miro.medium.com/max/1298/1\*AjEHidU-h0Ae0CXTsQUJ5Q.png) +![](https://miro.medium.com/max/60/1\*AjEHidU-h0Ae0CXTsQUJ5Q.png?q=20) ![](https://miro.medium.com/max/1298/1\*AjEHidU-h0Ae0CXTsQUJ5Q.png) _Importing a wallet_ ### 2. Using the Dapp -We will now start a local web server and interact with the Dapp. We’re using the `lite-server`. This shipped with the `pet-shop` Truffle box. +If you want to get started with your dApp quickly or see what this whole project looks like with a frontend, you can use Hardhat's[ boilerplate repo](https://github.com/NomicFoundation/hardhat-boilerplate). -The settings for this are in the files `bs-config.json` and `package.json`, if you want to take a look. These tell npm to run our local install of `lite-server` when we execute `npm run dev` from the console. - -1. Start the local web server: +The first things you need to do are cloning this repository and installing its dependencies: ``` -npm run dev +git clone https://github.com/NomicFoundation/hardhat-boilerplate.git +cd hardhat-boilerplate +npm install ``` -The dev server will launch and automatically open a new browser tab containing your Dapp. - -![](https://miro.medium.com/max/2204/1\*gq766GpFC3UUCMoPW\_3Isw.png) - -_Pete’s Pet Shop_ +#### Front End App -Normally, a MetaMask notification automatically requests a connection. +In `frontend` you'll find a simple app that allows the user to do two things: -2\. To use the Dapp, click the **Adopt** button on the pet of your choice. +* Check the connected wallet's balance +* Send tokens to an address -3\. You’ll be automatically prompted to approve the transaction by MetaMask. Set some Gas and click **Confirm** to approve the transaction +It's a separate npm project and it was created using `create-react-app`, so this means that it uses webpack and babel. -![](https://miro.medium.com/max/60/1\*KNOJi0WwGoYF7jy\_AQz43Q.png?q=20)![](https://miro.medium.com/max/1378/1\*KNOJi0WwGoYF7jy\_AQz43Q.png) - -_Adoption transaction review_ +``` +npx hardhat node +``` -4\. You’ll see the button next to the adopted pet change to say **“Success”** and become disabled, just as we specified, because the pet has now been adopted. +Once installed, let's run Hardhat's testing network: -![](https://miro.medium.com/max/46/0\*iwMICrpZrGfJiNSY.png?q=20)![](https://miro.medium.com/max/988/0\*iwMICrpZrGfJiNSY.png) +``` +npx hardhat run scripts/deploy.js --network localhost +``` -_Adoption success_ +Then, on a new terminal, go to the repository's root folder and run this to deploy your contract: -And in MetaMask you’ll see the transaction listed +``` +npx hardhat run scripts/deploy.js --network localhost +``` -![](https://miro.medium.com/max/44/1\*iqZsMFlAA3NCkOO-xfEjiQ.png?q=20)![](https://miro.medium.com/max/746/1\*iqZsMFlAA3NCkOO-xfEjiQ.png) +Finally, we can run the frontend with: -_MetaMask transaction_ +``` +cd frontend +npm install +npm start +``` -**Congratulations!** You have taken a huge step to becoming a full-fledged Dapp developer. You have all the tools you need to start making more advanced Dapps and now you can make your Dapp live for others to use deploying to TomoChain. +Open [http://localhost:3000/](http://localhost:3000/) to see your Dapp. You will need to have [Coinbase Wallet](https://www.coinbase.com/wallet) or [Metamask](https://metamask.io/) installed and listening to`localhost 8545.`([Example](interacting-with-the-dapp-in-a-browser.md#4986)) diff --git a/developer-guide/building-dapp-on-tomochain/how-to-migrate-dapp-from-ethereum.md b/developer-guide/building-dapp-on-tomochain/how-to-migrate-dapp-from-ethereum.md deleted file mode 100644 index e2236f5..0000000 --- a/developer-guide/building-dapp-on-tomochain/how-to-migrate-dapp-from-ethereum.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -description: >- - Now that we’ve successfully compiled, it’s time to migrate your smart - contracts to TomoChain’s blockchain! ---- - -# How to Migrate Dapps from Ethereum - -**A migration is a deployment script meant to alter the state of your application’s contracts**, moving it from one state to the next. _(More about migrations in the_ [_Truffle documentation_](https://truffleframework.com/docs/truffle/getting-started/running-migrations)_)._ - -### Create the migration scripts - -Open the `migrations/` directory and you will see one JavaScript file: `1_initial_migration_js`. This handles deploying the `Migrations.sol` contract to observe subsequent smart contract migrations, and ensures we don't double-migrate unchanged contracts in the future. - -Now we are ready to create our own migration script. - -1. Create a new file named `2_deploy_contracts.js` in the `migrations/` directory. -2. Add the following content to the `2_deploy_contracts.js` file: - -``` -var Adoption = artifacts.require("Adoption");module.exports = function(deployer) { - deployer.deploy(Adoption); -}; -``` - -### Configure the migration networks in truffle.js - -Now we are almost ready to deploy to TomoChain. Let’s see [how to deploy your smart contract to a custom provider](https://truffleframework.com/tutorials/using-infura-custom-provider), any blockchain of your choice, like **TomoChain**. - -Before starting the migration, we need to specify the **blockchain** where we want to deploy our smart contracts, specify the **address** to deploy — the wallet we just created, and optionally the gas, gas price, etc. - -1\. Install Truffle’s `HDWalletProvider`, a separate npm package to find and sign transactions for addresses derived from a 12-word `mnemonic` — in a certain blockchain. ([Read more about HDWalletProvider](https://github.com/trufflesuite/truffle-hdwallet-provider).) - -``` -npm install truffle-hdwallet-provider -``` - -2\. Open `truffle.js` file (`truffle-config.js` on Windows). You can edit here the migration settings: networks, chain IDs, gas... The current file has only a single network defined, you can define multiple. We will add three networks to migrate our DApp: `development`, `tomotestnet` and `tomomainnet`. - -[The official TomoChain documentation — Networks](../working-with-tomochain/) is very handy. Both Testnet and Mainnet **network configurations** are described there. We need the `RPC endpoint`, the `Chain id` and the `HD derivation path`. - -Replace the `truffle.js` file with this new content: - -``` -'use strict'var HDWalletProvider = require("truffle-hdwallet-provider");var mnemonic = '';module.exports = { - networks: { - development: { - provider: () => new HDWalletProvider( - mnemonic, - "http://127.0.0.1:8545", - ), - host: "127.0.0.1", - port: "8545", - network_id: "*", // Match any network id - }, - ​tomotestnet: { - ​provider: () => new HDWalletProvider( - ​mnemonic, - ​"https://rpc.testnet.tomochain.com", - ​0, - ​1, - ​true, - ​"m/44'/889'/0'/0/" - ​) - ​network_id: "89", - ​gas: 2000000, - ​gasPrice: 10000000000000 - ​}, - tomomainnet: { - provider: () => new HDWalletProvider( - mnemonic, - "https://rpc.tomochain.com", - 0, - 1, - true, - "m/44'/889'/0'/0/", - ), - network_id: "88", - gas: 2000000, - gasPrice: 10000000000000, - } - } -}; -``` - -3\. Remember to **update the `truffle.js` file using your own wallet recovery phrase.** Copy the 12 words obtained previously and paste it as the value of the `mnemonic` variable. - -``` -var mnemonic = ''; -``` - -Done. Please, notice the `tomotestnet` network will be used to deploy our smart contract. We have also added the `tomomainnet` network, in case you want to deploy to TomoChain Mainnet. However, if you are familiar with [Ganache](https://truffleframework.com/ganache), you could use the `development` network to do the local test as well if you want to. [_Ganache_](https://truffleframework.com/ganache) _is a locally running personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests._ - -We have added the migration configuration so **we are now able to deploy to public blockchains like TomoChain** (both testnet and mainnet). - -> **Warning**: In production, we highly recommend storing the **mnemonic** in another secret file (loaded from environment variables or a secure secret management system), to reduce the risk of the mnemonic becoming known. If someone knows your mnemonic, they have all of your addresses and private keys! - -_Want to try? With npm package `dotenv` you can load an environment variable from a file `.env`, — then update your truffle.js to use this secret `mnemonic`._ - -### Start the migration - -You should have your smart contract already compiled. Otherwise, now it’s a good time to do it with `truffle compile`. - -Back in our terminal, migrate the contract to **TomoChain testnet** network: - -``` -truffle migrate --network tomotestnet -``` - -The migrations will start… - -``` -Starting migrations... -====================== -> Network name: 'tomotestnet' -> Network id: 89 -> Block gas limit: 840000001_initial_migration.js -======================Deploying 'Migrations' - ---------------------- - > transaction hash: 0x77d9cdf0fb810fd6cec8a5616a3519e7fa5d42ad07506802f0b6bc10fa9e8619 - > Blocks: 2 Seconds: 4 - > contract address: 0xA3919059C38b1783Ac41C336AAc6438ac5fd639d - > account: 0xc9b694877AcD4e2E100e095788A591249c38b9c5 - > balance: 27.15156 - > gas used: 284844 - > gas price: 10000 gwei - > value sent: 0 ETH - > total cost: 2.84844 ETH> Saving migration to chain. - > Saving artifacts - ------------------------------------- - > Total cost: 2.84844 ETH2_deploy_contracts.js -=====================Deploying 'Adoption' - -------------------- - > transaction hash: 0x1c48f603520147f8eebc984fadc944aa300ceab125cf40f77b1bb748460db272 - > Blocks: 2 Seconds: 4 - > contract address: 0xB4Bb4FebdA9ec02427767FFC86FfbC6C05Da2A73 - > account: 0xc9b694877AcD4e2E100e095788A591249c38b9c5 - > balance: 24.19238 - > gas used: 253884 - > gas price: 10000 gwei - > value sent: 0 ETH - > total cost: 2.53884 ETH> Saving migration to chain. - > Saving artifacts - ------------------------------------- - > Total cost: 2.53884 ETHSummary -======= -> Total deployments: 2 -> Final cost: 5.38728 ETH -``` - -The transaction ID is: - -``` -0x1c48f603520147f8eebc984fadc944aa300ceab125cf40f77b1bb748460db272 -``` - -The contract address is: - -``` -0xB4Bb4FebdA9ec02427767FFC86FfbC6C05Da2A73 -``` - -**Congratulations!** You have already deployed your smart contract to TomoChain. All this in just 8 seconds. We started with `30 TOMO` and the deployment has costed `5.38 TOMO` in gas fees. - -> **Note:** The command to deploy to **TomoChain mainnet** is very similar:\ -> `truffle migrate --network`` `**`tomomainnet`** - -### \*\*\* Troubleshooting \*\*\* - -* **Error: `smart contract creation cost is under allowance`**. **Why?** Increasing transaction fees for smart contract creation is one of the ways TomoChain offers to defend against spamming attacks. **Solution:** edit `truffle.js` and add more gas/gasPrice to deploy. -* **Error: `insufficient funds for gas * price + value`. Why?** You don’t have enough tokens in your wallet for gas fees. **Solution:** you need more funds in your wallet to deploy, go to [faucet](https://faucet.testnet.tomochain.com/) and get more tokens. - -### Check the deployment transaction - -If you want to verify that your contract was deployed successfully, you can check on **TomoScan** [testnet](https://scan.testnet.tomochain.com/) (or [mainnet](https://tomoscan.io/)). In the search field, type in the transaction ID for your new contract. - -You should see details about the transaction, including the block number where the transaction was secured - -.![](https://miro.medium.com/max/60/1\*7AlMGUJ6mz316IjIl85xSg.png?q=20)![](https://miro.medium.com/max/2512/1\*7AlMGUJ6mz316IjIl85xSg.png) - -TomoScan transaction - -You can also enter your wallet address on the TomoScan search bar. You will find 4 transactions out. Your contract has been successfully deployed to TomoChain. - -**Congratulations!** You’ve deployed your contract to TomoChain using Truffle. You have written your first smart contract and deployed it to a public blockchain. It’s time to interact with our smart contract now to make sure it does what we want. - -## Testing the smart contract - -It is a good idea to test your smart contracts. You can write some tests in the `test/` directory and execute with `truffle test`. Find more details on [Truffle’s Pet Shop tutorial](https://truffleframework.com/tutorials/pet-shop#testing-the-smart-contract). diff --git a/developer-guide/building-dapp-on-tomochain/setup-environment.md b/developer-guide/building-dapp-on-tomochain/setup-environment.md index 93619ff..471de74 100644 --- a/developer-guide/building-dapp-on-tomochain/setup-environment.md +++ b/developer-guide/building-dapp-on-tomochain/setup-environment.md @@ -11,41 +11,93 @@ To check that Node is installed properly, open a console (admin PowerShell on Wi To test npm, type `npm -v` and you should see the version number, like `6.4.1`. -## Getting Started: Installation +## Installing Node.js -[**Truffle Framework**](https://truffleframework.com/) is a great tool for developing Dapps. You can use Truffle to deploy your smart contracts to TomoChain. +{% hint style="info" %} +You can [skip](https://hardhat.org/tutorial/creating-a-new-hardhat-project) this section if you already have a working Node.js `>=16.0` installation. If not, here's how to install it on Ubuntu, MacOS and Windows. +{% endhint %} -We only need this single command to install Truffle, the popular development framework for Ethereum. +### Linux + +#### Ubuntu + +Copy and paste these commands in a terminal: ``` -npm install -g truffle +sudo apt update +sudo apt install curl git +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs ``` -You can verify that Truffle is correctly installed typing `truffle version`. +### No-brainer option -## Creating a Truffle project +Check out this [link](https://nodejs.org/en/download/current) for each platform installers. -Truffle initializes in the current directory, so first create a directory in your development folder of choice and then move inside it. +### MacOS + +Make sure you have `git` installed. ``` -mkdir pet-shop-tutorialcd pet-shop-tutorial +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash +nvm install 20 +nvm use 20 +nvm alias default 20 +npm install npm --global # Upgrade npm to the latest version ``` -Let’s see [**how to create a Truffle project**](https://truffleframework.com/docs/truffle/getting-started/creating-a-project). There are two options. You can create a bare new project from scratch with no smart contracts included, and the other option for those just getting started, you can use [**Truffle Boxes**](https://truffleframework.com/boxes), which are example applications and project templates. +### Windows + +If you are using Windows, we **strongly recommend** you use Windows Subsystem for Linux (also known as WSL 2). You can use Hardhat without it, but it will work better if you use it. + +To install Node.js using WSL 2, please read [this guide](https://docs.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl). + +### No-brainer option + +Check out this [link](https://nodejs.org/en/download/current) for each platform's installers. -![](https://miro.medium.com/max/60/1\*0iPGzZ\_MuACqNeSRw1ymUg.png?q=20)![](https://miro.medium.com/max/1024/1\*0iPGzZ\_MuACqNeSRw1ymUg.png) +## Creating a new Hardhat project -**Pet Shop Truffe Box** +We'll install Hardhat using the Node.js package manager (`npm`), which is both a package manager and an online repository for JavaScript code. -There is a special [Truffle Box](https://truffleframework.com/boxes) for this tutorial called **`pet-shop`**, which includes the basic project structure as well as code for the user interface. Use the **`truffle unbox`** command to unpack this Truffle Box: +You can use other package managers with Node.js, but we suggest you use npm 7 or higher to follow this guide. You should already have it if you followed the previous section's steps. + +Open a new terminal and run these commands to create a new folder: + +``` +mkdir hardhat-tutorial +cd hardhat-tutorial +``` ``` -truffle unbox pet-shop +npm init +npm install --save-dev hardhat +npx hardhat init ``` -The default Truffle directory structure contains a series of folders and files. If you want to know more, please check [Truffle tutorials](https://truffleframework.com/tutorials/pet-shop#directory-structure). +Select `Create an empty hardhat.config.js` with your keyboard and hit enter. + +``` +$ npx hardhat init +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Welcome to Hardhat v2.18.1 👷‍ + +? What do you want to do? … + Create a JavaScript project + Create a TypeScript project +❯ Create an empty hardhat.config.js + Quit +``` -> **Note:** This tutorial is focused on **the whole process to build a Dapp on TomoChain**, so we will not enter into all the details. +When Hardhat is run, it searches for the closest `hardhat.config.js` file starting from the current working directory. This file normally lives in the root of your project and an empty `hardhat.config.js` is enough for Hardhat to work. The entirety of your setup is contained in this file. ## Creating a TOMO Wallet diff --git a/developer-guide/building-dapp-on-tomochain/writing-the-smart-contract.md b/developer-guide/building-dapp-on-tomochain/writing-the-smart-contract.md index eb2b766..bc7ba8a 100644 --- a/developer-guide/building-dapp-on-tomochain/writing-the-smart-contract.md +++ b/developer-guide/building-dapp-on-tomochain/writing-the-smart-contract.md @@ -2,45 +2,96 @@ We’ll start our Dapp by writing the smart contract that acts as the back-end logic and storage. -1. Create a new file named `Adoption.sol` in the `contracts/` directory. +{% hint style="info" %} +You might have heard about ERC-20, which is a token standard in Ethereum. Tokens such as DAI and USDC implement the ERC-20 standard which allows them all to be compatible with any software that can deal with ERC-20 tokens. For the sake of simplicity, the token we're going to build does _not_ implement the ERC-20 standard. +{% endhint %} + +1. Create a new file named `Token.sol` in the `contracts/` directory. 2. Copy the following code: -```text -pragma solidity ^0.5.0;contract Adoption { - address[16] public adopters; // Adopting a pet - function adopt(uint petId) public returns (uint) { - // check that petId is in range of our adopters array - require(petId >= 0 && petId <= 15); // add the address who called this function to our adopter array - adopters[petId] = msg.sender; // return the petId provided as a confirmation - return petId; - } // Retrieving the adopters - function getAdopters() public view returns (address[16] memory) { - return adopters; - } -} -``` +```solidity +//SPDX-License-Identifier: UNLICENSED -> **Note:** Code from [Truffle’s Pet-Shop tutorial](https://truffleframework.com/tutorials/pet-shop#writing-the-smart-contract) — if you want to look deeper into the Solidity code, they slowly go through the Truffle link explaining the details. +// Solidity files have to start with this pragma. +// It will be used by the Solidity compiler to validate its version. +pragma solidity ^0.8.0; +// This is the main building block for smart contracts. +contract Token { + // Some string type variables to identify the token. + string public name = "My Hardhat Token"; + string public symbol = "MHT"; -## Compiling + // The fixed amount of tokens, stored in an unsigned integer type variable. + uint256 public totalSupply = 1000000; -Solidity is a compiled language, meaning we need to compile our Solidity to bytecode for the **Ethereum Virtual Machine \(EVM\)** to execute. Think of it as translating our human-readable Solidity into something the EVM understands. + // An address type variable is used to store ethereum accounts. + address public owner; -> TomoChain is EVM-compatible, which means that every contract written in Ethereum can be seamlessly ported to TomoChain without effort + // A mapping is a key/value map. Here we store each account's balance. + mapping(address => uint256) balances; -In a terminal, make sure you are in the root of the directory that contains the Dapp and type: + // The Transfer event helps off-chain applications understand + // what happens within your contract. + event Transfer(address indexed _from, address indexed _to, uint256 _value); -```text -truffle compile + /** + * Contract initialization. + */ + constructor() { + // The totalSupply is assigned to the transaction sender, which is the + // account that is deploying the contract. + balances[msg.sender] = totalSupply; + owner = msg.sender; + } + + /** + * A function to transfer tokens. + * + * The `external` modifier makes a function *only* callable from *outside* + * the contract. + */ + function transfer(address to, uint256 amount) external { + // Check if the transaction sender has enough tokens. + // If `require`'s first argument evaluates to `false` then the + // transaction will revert. + require(balances[msg.sender] >= amount, "Not enough tokens"); + + // Transfer the amount. + balances[msg.sender] -= amount; + balances[to] += amount; + + // Notify off-chain applications of the transfer. + emit Transfer(msg.sender, to, amount); + } + + /** + * Read only function to retrieve the token balance of a given account. + * + * The `view` modifier indicates that it doesn't modify the contract's + * state, which allows us to call it without executing a transaction. + */ + function balanceOf(address account) external view returns (uint256) { + return balances[account]; + } +} ``` +> **Note:** Code from [Truffle’s Pet-Shop tutorial](https://truffleframework.com/tutorials/pet-shop#writing-the-smart-contract) — if you want to look deeper into the Solidity code, they slowly go through the Truffle link explaining the details. + +## Compiling + +Solidity is a compiled language, meaning we need to compile our Solidity to bytecode for the **Ethereum Virtual Machine (EVM)** to execute. Think of it as translating our human-readable Solidity into something the EVM understands. + +> TomoChain is EVM-compatible, which means that every contract written in Ethereum can be seamlessly ported to TomoChain without effort + +To compile the contract run `npx hardhat compile` in your terminal. The `compile` task is one of the built-in tasks. + You should see output similar to the following: -```text -Compiling ./contracts/Migrations.sol... -Compiling ./contracts/Adoption.sol... -Writing artifacts to ./build/contracts ``` - +$ npx hardhat compile +Compiling 1 file with 0.8.19 +Compilation finished successfully +``` diff --git a/developer-guide/library/README.md b/developer-guide/library/README.md new file mode 100644 index 0000000..1b016ab --- /dev/null +++ b/developer-guide/library/README.md @@ -0,0 +1,2 @@ +# Library + diff --git a/developer-guide/library/ethers.js.md b/developer-guide/library/ethers.js.md new file mode 100644 index 0000000..587cc31 --- /dev/null +++ b/developer-guide/library/ethers.js.md @@ -0,0 +1,805 @@ +# Ethers.js + +### Introduction + +The [Ethers.js](https://docs.ethers.org/) library provides a set of tools to interact with Ethereum Nodes with JavaScript, similar to Web3.js. Moonbeam has an Ethereum-like API available that is fully compatible with Ethereum-style JSON-RPC invocations. Therefore, developers can leverage this compatibility and use the Ethers.js library to interact with a Moonbeam node as if they were doing so on Ethereum. For more information on Ethers.js, check their [documentation site](https://docs.ethers.org/v6/). + +In this guide, you'll learn how to use the Ethers.js library to send a transaction and deploy a contract on Tomochain. + +### Checking Prerequisites + +To test out the examples in this guide on Tomochain [mainnet](../working-with-tomochain/tomochain-mainnet.md) or [testnet](../working-with-tomochain/tomochain-testnet.md), you will need to have the corresponding RPC API endpoints and some native TOMO tokens. + +{% hint style="info" %} +The examples in this guide assumes you have a MacOS or Ubuntu 18.04-based environment and will need to be adapted accordingly for Windows. +{% endhint %} + +### Installing Ethers.js + +To get started, you'll need to start a basic JavaScript project. First, create a directory to store all of the files you'll be creating throughout this guide and initialize the project with the following command: + +```bash +mkdir ethers-examples && cd ethers-examples && npm init --y +``` + +For this guide, you'll need to install the Ethers.js library and the Solidity compiler. To install both NPM packages, you can run the following command: + +{% tabs %} +{% tab title="npm" %} +npm install ethers solc@0.8.0 +{% endtab %} + +{% tab title="yarn" %} +yarn add ethers solc@0.8.0 +{% endtab %} +{% endtabs %} + +### Setting up the Ethers Provider + +Throughout this guide, you'll be creating a bunch of scripts that provide different functionality such as sending a transaction, deploying a contract, and interacting with a deployed contract. In most of these scripts you'll need to create an [Ethers pro vider](https://docs.ethers.org/v6/api/providers/) to interact with the network. + +To create a provider, you can take the following steps: + +1. Import the `ethers` library +2. Define the `providerRPC` object, which can include the network configurations for any of the networks you want to send a transaction on. You'll include the `name`, `rpc`, and `chainId` for each network +3. Create the `provider` using the `ethers.JsonRpcProvider` method + +```javascript +// 1. Import ethers +const ethers = require('ethers'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); +``` + +Save this code snippet as you'll need it for the scripts that are used in the following sections. + +### Send a Transaction + +During this section, you'll be creating a couple of scripts. The first one will be to check the balances of your accounts before trying to send a transaction. The second script will actually send the transaction. + +You can also use the balance script to check the account balances after the transaction has been sent. + +#### Check Balances Script + +You'll only need one file to check the balances of both addresses before and after the transaction is sent. To get started, you can create a `balances.js` file by running: + +```bash +touch balances.js +``` + +Next, you will create the script for this file and complete the following steps: + +1. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +2. Define the `addressFrom` and `addressTo` variables +3. Create the asynchronous `balances` function which wraps the `provider.getBalance` method +4. Use the `provider.getBalance` function to fetch the balances for the `addressFrom` and `addressTo` addresses. You can also leverage the `ethers.formatEther` function to transform the balance into a more readable number in TOMO +5. Lastly, run the `balances` function + +```javascript +// 1. Add the Ethers provider logic here: +// {...} + +// 2. Create address variables +const addressFrom = 'INSERT_FROM_ADDRESS'; +const addressTo = 'INSERT_TO_ADDRESS'; + +// 3. Create balances function +const balances = async () => { + // 4. Fetch balances + const balanceFrom = ethers.formatEther(await provider.getBalance(addressFrom)); + const balanceTo = ethers.formatEther(await provider.getBalance(addressTo)); + + console.log(`The balance of ${addressFrom} is: ${balanceFrom} DEV`); + console.log(`The balance of ${addressTo} is: ${balanceTo} DEV`); +}; + +// 5. Call the balances function +balances(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Define addresses +const addressFrom = 'INSERT_FROM_ADDRESS'; +const addressTo = 'INSERT_TO_ADDRESS'; + +// Create balances function +const balances = async () => { + // Fetch balances + const balanceFrom = ethers.formatEther( + await provider.getBalance(addressFrom) + ); + const balanceTo = ethers.formatEther(await provider.getBalance(addressTo)); + + console.log(`The balance of ${addressFrom} is: ${balanceFrom} DEV`); + console.log(`The balance of ${addressTo} is: ${balanceTo} DEV`); +}; + +// Call the balances function +balances(); +``` + +
+ +To run the script and fetch the account balances, you can run the following command: + +```bash +node balances.js +``` + +If successful, the balances for the origin and receiving address will be displayed in your terminal. + +#### Send Transaction Script + +You'll only need one file for executing a transaction between accounts. For this example, you'll be transferring 1 TOMO token from an origin address (from which you hold the private key) to another address. To get started, you can create a `transaction.js` file by running: + +```bash +touch transaction.js +``` + +Next, you will create the script for this file and complete the following steps: + +1. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +2. Define the `privateKey` and the `addressTo` variables. The private key is required to create a wallet instance. **Note: This is for example purposes only. Never store your private keys in a JavaScript file** +3. Create a wallet using the `privateKey` and `provider` from the previous steps. The wallet instance is used to sign transactions +4. Create the asynchronous `send` function which wraps the transaction object and the `wallet.sendTransaction` method +5. Create the transaction object which only requires the recipient's address and the amount to send. Note that `ethers.parseEther` can be used, which handles the necessary unit conversions from Ether to Wei - similar to using `ethers.parseUnits(value, 'ether')` +6. Send the transaction using the `wallet.sendTransaction` method and then use `await` to wait until the transaction is processed and the transaction receipt is returned +7. Lastly, run the `send` function + +```javascript +// 1. Add the Ethers provider logic here: +// {...} + +// 2. Create account variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const addressTo = 'INSERT_TO_ADDRESS'; + +// 3. Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// 4. Create send function +const send = async () => { + console.log(`Attempting to send transaction from ${wallet.address} to ${addressTo}`); + + // 5. Create tx object + const tx = { + to: addressTo, + value: ethers.parseEther('1'), + }; + + // 6. Sign and send tx - wait for receipt + const createReceipt = await wallet.sendTransaction(tx); + await createReceipt.wait(); + console.log(`Transaction successful with hash: ${createReceipt.hash}`); +}; + +// 7. Call the send function +send(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Define accounts and wallet +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const addressTo = 'INSERT_TO_ADDRESS'; +const wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// Create send function +const send = async () => { + console.log( + `Attempting to send transaction from ${wallet.address} to ${addressTo}` + ); + + // Create transaction + const tx = { + to: addressTo, + value: ethers.parseEther('1'), + }; + + // Send transaction and get hash + const createReceipt = await wallet.sendTransaction(tx); + await createReceipt.wait(); + console.log(`Transaction successful with hash: ${createReceipt.hash}`); +}; + +// Call the send function +send(); +``` + +
+ +To run the script, you can run the following command in your terminal: + +```bash +node transaction.js +``` + +If the transaction was succesful, in your terminal you'll see the transaction hash has been printed out. + +You can also use the `balances.js` script to check that the balances for the origin and receiving accounts have changed. + +#### Deploy a Contract + +The contract you'll be compiling and deploying in the next couple of sections is a simple incrementer contract, arbitrarily named `Incrementer.sol`. You can get started by creating a file for the contract: + +```bash +touch Incrementer.sol +``` + +Next, you can add the Solidity code to the file: + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract Incrementer { + uint256 public number; + + constructor(uint256 _initialNumber) { + number = _initialNumber; + } + + function increment(uint256 _value) public { + number = number + _value; + } + + function reset() public { + number = 0; + } +} +``` + +The `constructor` function, which runs when the contract is deployed, sets the initial value of the number variable stored on-chain (the default is `0`). The `increment` function adds the `_value` provided to the current number, but a transaction needs to be sent, which modifies the stored data. Lastly, the `reset` function resets the stored value to zero. + +{% hint style="info" %} +This contract is a simple example for illustration purposes only and does not handle values wrapping around. +{% endhint %} + +#### Compile Contract Script + +In this section, you'll create a script that uses the Solidity compiler to output the bytecode and interface (ABI) for the `Incrementer.sol` contract. To get started, you can create a `compile.js` file by running: + +```bash +touch compile.js +``` + +Next, you will create the script for this file and complete the following steps: + +1. Import the `fs` and `solc` packages +2. Using the `fs.readFileSync` function, you'll read and save the file contents of `Incrementer.sol` to `source` +3. Build the `input` object for the Solidity compiler by specifying the `language`, `sources`, and `settings` to be used +4. Using the `input` object, you can compile the contract using `solc.compile` +5. Extract the compiled contract file and export it to be used in the deployment script + +```javascript +// 1. Import packages +const fs = require('fs'); +const solc = require('solc'); + +// 2. Get path and load contract +const source = fs.readFileSync('Incrementer.sol', 'utf8'); + +// 3. Create input object +const input = { + language: 'Solidity', + sources: { + 'Incrementer.sol': { + content: source, + }, + }, + settings: { + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, +}; +// 4. Compile the contract +const tempFile = JSON.parse(solc.compile(JSON.stringify(input))); +const contractFile = tempFile.contracts['Incrementer.sol']['Incrementer']; + +// 5. Export contract data +module.exports = contractFile; +``` + +#### Deploy Contract Script + +With the script for compiling the `Incrementer.sol` contract in place, you can then use the results to send a signed transaction that deploys it. To do so, you can create a file for the deployment script called `deploy.js`: + +```bash +touch deploy.js +``` + +Next, you will create the script for this file and complete the following steps: + +1. Import the contract file from `compile.js` +2. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +3. Define the `privateKey` for the origin account. The private key is required to create a wallet instance. **Note: This is for example purposes only. Never store your private keys in a JavaScript file** +4. Create a wallet using the `privateKey` and `provider` from the previous steps. The wallet instance is used to sign transactions +5. Load the contract `bytecode` and `abi` for the compiled contract +6. Create a contract instance with signer using the `ethers.ContractFactory` function, providing the `abi`, `bytecode`, and `wallet` as parameters +7. Create the asynchronous `deploy` function that will be used to deploy the contract +8. Within the `deploy` function, use the `incrementer` contract instance to call `deploy` and pass in the initial value. For this example, you can set the initial value to `5`. This will send the transaction for contract deployment. To wait for a transaction receipt you can use the `deployed` method of the contract deployment transaction +9. Lastly, run the `deploy` function + +```javascript +// 1. Import the contract file +const contractFile = require('./compile'); + +// 2. Add the Ethers provider logic here: +// {...} + +// 3. Create account variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; + +// 4. Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// 5. Load contract information +const bytecode = contractFile.evm.bytecode.object; +const abi = contractFile.abi; + +// 6. Create contract instance with signer +const incrementer = new ethers.ContractFactory(abi, bytecode, wallet); + +// 7. Create deploy function +const deploy = async () => { + console.log(`Attempting to deploy from account: ${wallet.address}`); + + // 8. Send tx (initial value set to 5) and wait for receipt + const contract = await incrementer.deploy(5); + const txReceipt = await contract.deploymentTransaction().wait(); + + console.log(`Contract deployed at address: ${txReceipt.contractAddress}`); +}; + +// 9. Call the deploy function +deploy(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Define accounts and wallet +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// Load contract info +const bytecode = contractFile.evm.bytecode.object; +const abi = contractFile.abi; + +// Create contract instance with signer +const incrementer = new ethers.ContractFactory(abi, bytecode, wallet); + +// Create deploy function +const deploy = async () => { + console.log(`Attempting to deploy from account: ${wallet.address}`); + + // Send tx (initial value set to 5) and wait for receipt + const contract = await incrementer.deploy(5); + const txReceipt = await contract.deploymentTransaction().wait(); + + console.log(`Contract deployed at address: ${txReceipt.contractAddress}`); +}; + +// Call the deploy function +deploy(); +``` + +
+ +To run the script, you can enter the following command into your terminal: + +```bash +node deploy.js +``` + +If successful, the contract's address will be displayed in the terminal. + +#### Read Contract Data (Call Methods) + +Call methods are the type of interaction that don't modify the contract's storage (change variables), meaning no transaction needs to be sent. They simply read various storage variables of the deployed contract. + +To get started, you can create a file and name it `get.js`: + +```bash +touch get.js +``` + +Then you can take the following steps to create the script: + +1. Import the `abi` from the `compile.js` file +2. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +3. Create the `contractAddress` variable using the address of the deployed contract +4. Create an instance of the contract using the `ethers.Contract` function and passing in the `contractAddress`, `abi`, and `provider` +5. Create the asynchronous `get` function +6. Use the contract instance to call one of the contract's methods and pass in any inputs if necessary. For this example, you will call the `number` method which doesn't require any inputs. You can use `await` which will return the value requested once the request promise resolves +7. Lastly, call the `get` function + +```javascript +// 1. Import the ABI +const { abi } = require('./compile'); + +// 2. Add the Ethers provider logic here: +// {...} + +// 3. Contract address variable +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; + +// 4. Create contract instance +const incrementer = new ethers.Contract(contractAddress, abi, provider); + +// 5. Create get function +const get = async () => { + console.log(`Making a call to contract at address: ${contractAddress}`); + + // 6. Call contract + const data = await incrementer.number(); + + console.log(`The current number stored is: ${data}`); +}; + +// 7. Call get function +get(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); +const { abi } = require('./compile'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Contract address variable +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; + +// Create contract instance +const incrementer = new ethers.Contract(contractAddress, abi, provider); + +// Create get function +const get = async () => { + console.log(`Making a call to contract at address: ${contractAddress}`); + + // Call contract + const data = await incrementer.number(); + + console.log(`The current number stored is: ${data}`); +}; + +// Call get function +get(); +``` + +
+ +To run the script, you can enter the following command in your terminal: + +```bash +node get.js +``` + +If successful, the value will be displayed in the terminal. + +#### Interact with Contract (Send Methods) + +Send methods are the type of interaction that modify the contract's storage (change variables), meaning a transaction needs to be signed and sent. In this section, you'll create two scripts: one to increment and one to reset the incrementer. To get started, you can create a file for each script and name them `increment.js` and `reset.js`: + +```bash +touch increment.js reset.js +``` + +Open the `increment.js` file and take the following steps to create the script: + +1. Import the `abi` from the `compile.js` file +2. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +3. Define the `privateKey` for the origin account, the `contractAddress` of the deployed contract, and the `_value` to increment by. The private key is required to create a wallet instance. **Note: This is for example purposes only. Never store your private keys in a JavaScript file** +4. Create a wallet using the `privateKey` and `provider` from the previous steps. The wallet instance is used to sign transactions +5. Create an instance of the contract using the `ethers.Contract` function and passing in the `contractAddress`, `abi`, and `provider` +6. Create the asynchronous `increment` function +7. Use the contract instance to call one of the contract's methods and pass in any inputs if necessary. For this example, you will call the `increment` method which requires the value to increment by as an input. You can use `await` which will return the value requested once the request promise resolves +8. Lastly, call the `increment` function + +```javascript +// 1. Import the contract ABI +const { abi } = require('./compile'); + +// 2. Add the Ethers provider logic here: +// {...} + +// 3. Create variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const _value = 3; + +// 4. Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// 5. Create contract instance with signer +const incrementer = new ethers.Contract(contractAddress, abi, wallet); + +// 6. Create increment function +const increment = async () => { + console.log( + `Calling the increment by ${_value} function in contract at address: ${contractAddress}` + ); + + // 7. Sign and send tx and wait for receipt + const createReceipt = await incrementer.increment(_value); + await createReceipt.wait(); + + console.log(`Tx successful with hash: ${createReceipt.hash}`); +}; + +// 8. Call the increment function +increment(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); +const { abi } = require('./compile'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Create variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; +const _value = 3; + +// 4. Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// 5. Create contract instance with signer +const incrementer = new ethers.Contract(contractAddress, abi, wallet); + +// 6. Create increment function +const increment = async () => { + console.log( + `Calling the increment by ${_value} function in contract at address: ${contractAddress}` + ); + + // 7. Sign and send tx and wait for receipt + const createReceipt = await incrementer.increment(_value); + await createReceipt.wait(); + + console.log(`Tx successful with hash: ${createReceipt.hash}`); +}; + +// 8. Call the increment function +increment(); +``` + +
+ +To run the script, you can enter the following command in your terminal: + +```bash +node increment.js +``` + +If successful, the transaction hash will be displayed in the terminal. You can use the `get.js` script alongside the `increment.js` script. Next you can open the `reset.js` file and take the following steps to create the script: + +1. Import the `abi` from the `compile.js` file +2. [Set up the Ethers provider](https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/#setting-up-the-ethers-provider) +3. Define the `privateKey` for the origin account and the `contractAddress` of the deployed contract. The private key is required to create a wallet instance. **Note: This is for example purposes only. Never store your private keys in a JavaScript file** +4. Create a wallet using the `privateKey` and `provider` from the previous steps. The wallet instance is used to sign transactions +5. Create an instance of the contract using the `ethers.Contract` function and passing in the `contractAddress`, `abi`, and `provider` +6. Create the asynchronous `reset` function +7. Use the contract instance to call one of the contract's methods and pass in any inputs if necessary. For this example, you will call the `reset` method which doesn't require any inputs. You can use `await` which will return the value requested once the request promise resolves +8. Lastly, call the `reset` function + +```javascript +// 1. Import the contract ABI +const { abi } = require('./compile'); + +// 2. Add the Ethers provider logic here: +// {...} + +// 3. Create variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; + +// 4. Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// 5. Create contract instance with signer +const incrementer = new ethers.Contract(contractAddress, abi, wallet); + +// 6. Create reset function +const reset = async () => { + console.log( + `Calling the reset function in contract at address: ${contractAddress}` + ); + + // 7. sign and send tx and wait for receipt + const createReceipt = await incrementer.reset(); + await createReceipt.wait(); + + console.log(`Tx successful with hash: ${createReceipt.hash}`); +}; + +// 8. Call the reset function +reset(); +``` + +
+ +View the complete script + +```javascript +// 1. Import ethers +const ethers = require('ethers'); +const { abi } = require('./compile'); + +// 2. Define network configurations +const providerRPC = { + mainnet: { + name: 'tomochain-mainnet', + rpc: 'https://rpc.tomochain.com', // Insert your RPC URL here + chainId: 88, + }, +}; +// 3. Create ethers provider +const provider = new ethers.JsonRpcProvider(providerRPC.mainnet.rpc, { + chainId: providerRPC.mainnet.chainId, + name: providerRPC.mainnet.name, +}); // Change to correct network + +// Create variables +const accountFrom = { + privateKey: 'INSERT_YOUR_PRIVATE_KEY', +}; +const contractAddress = 'INSERT_CONTRACT_ADDRESS'; + +// Create wallet +let wallet = new ethers.Wallet(accountFrom.privateKey, provider); + +// Create contract instance with signer +const incrementer = new ethers.Contract(contractAddress, abi, wallet); + +// Create reset function +const reset = async () => { + console.log( + `Calling the reset function in contract at address: ${contractAddress}` + ); + + // Sign and send tx and wait for receipt + const createReceipt = await incrementer.reset(); + await createReceipt.wait(); + + console.log(`Tx successful with hash: ${createReceipt.hash}`); +}; + +// Call the reset function +reset(); +``` + +
+ +To run the script, you can enter the following command in your terminal: + +```bash +node reset.js +``` + +If successful, the transaction hash will be displayed in the terminal. You can use the `get.js` script alongside the `reset.js` script to make sure that value is changing as expected.\ diff --git a/developer-guide/smart-contract-development/hardhat.md b/developer-guide/smart-contract-development/hardhat.md new file mode 100644 index 0000000..55992a9 --- /dev/null +++ b/developer-guide/smart-contract-development/hardhat.md @@ -0,0 +1,173 @@ +# Hardhat + +This section will guide you through deploying a smart contract on the Tomo network using [Hardhat](https://hardhat.org/). + +Hardhat is a developer tool that provides a simple way to deploy, test, and debug smart contracts. + +*** + +## Prerequisites[​](https://docs.linea.build/build-on-linea/quickstart/deploy-smart-contract/hardhat#prerequisites) + +Before you begin, ensure you've:[​](https://docs.base.org/guides/deploy-smart-contracts#node-v18) + +* Download [Node v18+](https://nodejs.org/en/download/). +* An ethereum wallet. +* Funded your wallet for carring gas fee of transactions. + +## Create a Hardhat project + +To create an empty Hardhat project, run the following commands: + +``` +mkdir hardhat-tomo-tutorial +cd hardhat-tomo-tutorial +npm init +npm install --save-dev hardhat +npx hardhat +``` + +Select `Create a TypeScript project` then press _enter_ to confirm the project root. + +Select `y` for both adding a `.gitignore` and loading the sample project. It will take a moment for the project setup process to complete. + +## Configuring hardhat with Tomo + +In order to deploy smart contracts to the Tomo network, you will need to configure your Hardhat project and add the Tomo network. + +To configure Hardhat to use Tomo, add Tomo as a network to your project's `hardhat.config.ts` file: + +```typescript +import { HardhatUserConfig } from 'hardhat/config'; +import '@nomicfoundation/hardhat-toolbox'; + +require('dotenv').config(); + +const config: HardhatUserConfig = { + solidity: { + version: '0.8.17', + }, + networks: { + // for mainnet + 'tomo-mainnet': { + url: 'https://rpc.tomochain.com', + accounts: [process.env.PRIVATE_KEY as string], + }, + // for testnet + 'tomo-testnet': { + url: 'https://rpc.testnet.tomochain.com', + accounts: [process.env.PRIVATE_KEY as string], + }, + }, + defaultNetwork: 'hardhat', +}; + +export default config; +``` + +## Install Hardhat toolbox[​](https://docs.base.org/guides/deploy-smart-contracts#install-hardhat-toolbox) + +The above configuration uses the `@nomicfoundation/hardhat-toolbox` plugin to bundle all the commonly used packages and Hardhat plugins recommended to start developing with Hardhat. + +To install `@nomicfoundation/hardhat-toolbox`, run: + +``` +npm install --save-dev @nomicfoundation/hardhat-toolbox +``` + +## Loading environment variables[​](https://docs.base.org/guides/deploy-smart-contracts#loading-environment-variables) + +The above configuration also uses [dotenv](https://www.npmjs.com/package/dotenv) to load the `PRIVATE_KEY` environment variable from a `.env` file to `process.env.PRIVATE_KEY`. You should use a similar method to avoid hardcoding your private keys within your source code. + +To install `dotenv`, run: + +``` +npm install --save-dev dotenv +``` + +Once you have `dotenv` installed, you can create a `.env` file with the following content: + +``` +PRIVATE_KEY= +``` + +Substituting `` with the private key for your wallet. + +## Compiling the smart contract[​](https://docs.base.org/guides/deploy-smart-contracts#compiling-the-smart-contract) + +Below is a simple token contract (ERC20) written in the Solidity programming language: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyToken is ERC20 { + constructor() ERC20("My Token", "MYT") {} + + function mint(address recipient, uint256 amount) + external + returns (uint256) + { + _mint(recipient, amount); + return amount; + } +} +``` + +The Solidity code above defines a smart contract named `ERC20`. The code uses the `ERC20` interface provided by the [OpenZeppelin Contracts library](https://docs.openzeppelin.com/contracts/4.x/) to create a token smart contract. OpenZeppelin allows developers to leverage battle-tested smart contract implementations that adhere to official ERC standards. + +To add the OpenZeppelin Contracts library to your project, run: + +``` +npm install --save @openzeppelin/contracts +``` + +\ +In your project, delete the `contracts/Lock.sol` contract that was generated with the project and add the above code in a new file called `contracts/MyToken.sol.` + +To compile the contract using Hardhat, run: + +``` +npx hardhat compile +``` + +## Deploying the smart contract[​](https://docs.base.org/guides/deploy-smart-contracts#deploying-the-smart-contract) + +Once your contract has been successfully compiled, you can deploy the contract to the Tomo networks. + +To deploy the contract to the Tomo test network, you'll need to modify the `scripts/deploy.ts` in your project: + +```typescript +import { ethers } from 'hardhat'; + +async function main() { + const gasLimit = 100_000_000; + const myToken = await ethers.deployContract('MyToken', { gasLimit }); + + await myToken.waitForDeployment(); + + console.log('Token Contract Deployed at ' + myToken.target); +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); +``` + +{% hint style="warning" %} +**Note** + +Gas limit is required when deploying a smart contract using Hardhat in Tomochain +{% endhint %} + +Finally, ensure your wallet has enough fund to cover gas fee and run script with command: + +``` +npx hardhat run scripts/deploy.ts --network tomo-testnet +``` + +The contract will be deployed on the Tomo test network. You can view the deployment status and contract by using [Tomoscan](https://testnet.tomoscan.io/) and searching for the address returned by your deploy script. If you've deployed an exact copy of the token contract above, it will already be verified and you'll be able to read and write to the contract using the web interface. diff --git a/developer-guide/smart-contract-development/ides-and-tools/README.md b/developer-guide/smart-contract-development/ides-and-tools/README.md deleted file mode 100644 index 024648a..0000000 --- a/developer-guide/smart-contract-development/ides-and-tools/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# IDEs and Tools - -{% page-ref page="remix.md" %} - -{% page-ref page="web3js.md" %} - -{% page-ref page="truffle.md" %} - -{% page-ref page="openzepplins.md" %} - - - diff --git a/developer-guide/smart-contract-development/ides-and-tools/openzepplins.md b/developer-guide/smart-contract-development/ides-and-tools/openzepplins.md deleted file mode 100644 index 787d09c..0000000 --- a/developer-guide/smart-contract-development/ides-and-tools/openzepplins.md +++ /dev/null @@ -1,94 +0,0 @@ -# Openzepplins - -OpenZeppelin ([https://openzeppelin.org](https://openzeppelin.org/)) is an amazing library of well documented smart contracts for Solidity development. You will find its contracts helpful for many of your projects. - -The most obvious use of OpenZeppelin is its implementations of ERC-20 and ERC-721 tokens. But there is a lot more here that can save us from reinventing the wheel. There are contracts for access control, crowdsales, and escrow payments — just to name a few. - -Truffle ([https://truffleframework.com](https://truffleframework.com/docs/truffle/overview)) is a development framework you can use for Solidity projects. It makes your life easier in a lot of ways; but the biggest value add is how it helps you test your contracts. - -Right out of the box, Truffle lets you compile and migrate your contracts to a local test network. From there you can either use the Truffle console to manually interact with the deployed contracts, or run unit tests written in Javascript with Mocha and Chai. - -## **Installation** - -First we install Truffle (requires Node.js v8.9.4 or later): - -``` -npm install -g truffle -``` - -Next we use Truffle to initialize a new project for us: - -``` -mkdir MyProject && cd MyProject -truffle init -``` - -Now we install OpenZeppelin in our new project: - -``` -npm init -y -npm install --save-exact openzeppelin-solidity -``` - -We use `--save-exact` because minor version updates to OpenZeppelin can introduce breaking changes. - -## **Configure Solidity compiler** - -OpenZeppelin contracts require the Solidity 0.5.2 compiler (solc), but Truffle ships with solc 0.5.0 by default. If we want to use OpenZeppelin with our own contracts, we must configure Truffle to use 0.5.2. - -Let’s add the following to `./truffle-config.js`: - -``` -module.exports = { - compilers: { - solc: { - version: “0.5.2” - } - } -} -``` - -Do not worry about installing the correct compiler version manually. Truffle will download it automatically with this configuration. - -## Write Smart Contracts - -When you initialize a project with Truffle, it creates a `./contracts` directory for your Solidity source code. Inside you will see `Migrations.sol` which is used to manage your contract migrations, so don’t delete it. - -Any contract created inside this directory will be automatically detected by Truffle. - -## **Import OpenZeppelin libraries** - -Once you start writing your own Solidity smart contracts, using OpenZeppelin is easy. Because Truffle understands NPM packages, Solidity libraries installed to `./node_modules` are readily available without any configuration. - -For example, importing OpenZeppelin’s ERC-20 implementation is as simple as: - -``` -import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol'; -``` - -## **Deploy with Truffle** - -Truffle’s deployment process is versatile, but if all we want to do is test any smart contracts we’ve created, it couldn’t be more straightforward. - -Open the Truffle console with: - -``` -truffle develop -``` - -This automatically connects to Truffle’s built-in local test network. - -In the console we run `compile` and then `migrate`. Compilation generates the contract bytecode and application binary interface (ABI). Migration sends transactions to the network that create instances of our smart contracts. - -The Truffle console also exposes contract abstractions and web3.js if we want to manually interact with our contracts after deployment. - -For example, if we have an ERC-20 token contract called `MyToken`, we could find out the total supply with: - -``` -const instance = await MyToken.deployed() -instance.totalSupply() -``` - -## **Where to go from here** - -We recommend looking through the OpenZeppelin documentation ([docs.openzeppelin.org/docs/get-started.html](https://docs.openzeppelin.com/contracts/2.x/)) to see how your projects can benefit from its library of smart contracts. diff --git a/developer-guide/smart-contract-development/ides-and-tools/remix.md b/developer-guide/smart-contract-development/ides-and-tools/remix.md deleted file mode 100644 index ca6393a..0000000 --- a/developer-guide/smart-contract-development/ides-and-tools/remix.md +++ /dev/null @@ -1,251 +0,0 @@ -# Remix - -**Remix is a Solidity IDE that’s used to write, compile and debug Solidity code.** [**Solidity**](https://solidity.readthedocs.io/) **is a high-level, contract-oriented programming language for writing smart contracts. It was influenced by popular languages such as C++, Python and JavaScript.** - -IDE stands for Integrated Development Environment and is an application with a set of tools designed to help programmers execute different tasks related to software development such as writing, compiling, executing and debugging code. - -Before you begin using Remix to develop smart contracts, make sure you’re familiar with some basic concepts. In particular, give these articles about [blockchain](https://www.sitepoint.com/blockchain-what-it-is-how-it-works-why-its-so-popular) and [Ethereum](https://bitfalls.com/2017/09/19/what-ethereum-compare-to-bitcoin/) a read. - -#### What’s a Smart Contract/Dapp? - -A smart contract is a trust-less agreement between two parties that makes use of blockchain technology, to enforce the parties to adhere to the terms, rather than relying on the traditional ways such as trusting a middleman or using laws to handle disputes. - -Using the Ethereum blockchain, you can create smart contracts with the Solidity language (among others). Ethereum is not the only platform that can be used to create smart contacts, but it’s the most popular choice, as it was designed from the start to support building them. - -[Dapp](http://ethdocs.org/en/latest/contracts-and-transactions/developer-tools.html#dapps) stands for _**de**_**centralized **_**app**_**lication** and is a web3 application that can have a front-end written in traditional languages such as JavaScript, HTML, CSS and a smart contract (as back-end code) which runs on the blockchain. So you can simply think of a Dapp as the front end plus the associated blockchain smart contract(s). - -Unlike the smart contract deployed on the blockchain itself, the front end of a Dapp can be either hosted on a centralized server like a CDN or on decentralized storage like [Swarm](http://ethdocs.org/en/latest/contracts-and-transactions/developer-tools.html#swarm). - -### Accessing the Remix IDE - -You can access the Remix IDE in different ways: online, via a web browser like Chrome, from a locally installed copy, or from Mist (the Ethereum Dapp browser). - -#### Using the In-Browser Remix IDE - -You can access the Remix IDE from your web browser without any special installation. Visit [https://remix.ethereum.org/](https://remix.ethereum.org/) and you’ll be presented with a complete IDE with a code editor and various panels for compiling, running and debugging your smart contracts. You’ll have a default example _Ballot_ contract that you can play with. - -![Remix IDE](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528071944remix-ide-1024x780.png) - -#### Starting Remix IDE from Mist - -You can start the Remix IDE from Mist by clicking on _Develop_, then _Open Remix IDE_. Remix will be opened in a new window. If this is your first time running the IDE, you’ll be presented with a simple example _Ballot_ contract. - -To get familiar with Mist, please see [this article](https://bitfalls.com/2018/02/12/explaining-ethereum-tools-geth-mist/). - -#### Running your Own Copy of Remix IDE - -You can also run your own copy of Remix IDE by executing the following commands: - -``` -npm install remix-ide -g -remix-ide -``` - -You need to have Node.js and npm installed. Check this [GitHub repository](https://github.com/ethereum/remix-ide) for more information. - -### Remix Panels - -After seeing how to open the Remix IDE, let’s now see the various panels composing the IDE. - -#### File Explorer - -The file explorer provides a view with the created files stored in the browser’s storage. You can rename or delete any file by right-clicking on it, then choosing the right operation from the context menu. - -![context menu options](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072165rename-delete.png) - -Please note that the file explorer uses the browser’s local storage by default, which means you can lose all your files if you clear or the operating system automatically clears the storage. For advanced work, it’s recommended to use [Remixd](https://github.com/ethereum/remixd) — a Node.js tool (available from npm `npm install -g remixd`) which allows the Remix IDE to access your computer’s file system. - -Now let’s see the different actions that you can perform using the buttons at the top of the explorer. - -![Remix buttons](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072282remix-buttons.png) - -**Creating/Opening Files in Remix** - -You can create a new file in the browser local storage using the first button with the `+` icon on the top left. You can then provide a name in the opened dialog and press _OK_. - -Using the second button from top left, you can open an existing Solidity file from your computer file system into the Remix IDE. The file will also be stored in the browser’s local storage. - -**Publishing Explorer Files as GitHub Gists** - -Using the third and fourth buttons from top left, you can publish files from the IDE as a public GitHub gist. - -**Copying Files to Another Instance of Remix IDE** - -Using the fifth button from top left, you can copy files from the local storage to another instance of Remix by providing the URL of the instance. - -**Connecting to the Local File System** - -The last button can be used to connect the Remix IDE to your local file system if you’re running the Remixd tool. - -#### Solidity Code Editor - -The Solidity code editor provides the interface where you can write your code with many features such as syntax highlighting, auto-recompling, auto-saving etc. You can open multiple tabs and also increase/decrease the font size using the _+/-_ button in the top-left corner. - -![Solidity Code Editor](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072494solidity-code-editor.png) - -#### Terminal - -The terminal window below the editor integrates a JavaScript interpreter and the `web3` object. You can execute JavaScript code in the current context, visualize the actions performed from the IDE, visualize all network transactions or transactions created from the Remix IDE etc. You can also search for data in the terminal and clear the logs. - -![Remix terminal](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072582remix-terminal.png) - -#### Tabs Panel - -The _Tabs_ panel provides many tabs for working with the IDE: - -* the _Compile_ tab: used for compiling a smart contract and publishing on Swarm -* the _Run_ tab: used for sending transactions to the configured environment -* the _Settings_ tab: used for updating settings like the compiler version and many general settings for the editor -* the _Debugger_ tab: used for debugging transactions -* the _Analysis_ tab: used for getting information about the latest compilation -* the _Support_ tab: used for connecting with the Remix community. - -![Remix tabs](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072805remix-tabs.png) - -### Remix Execution Environments - -The Remix IDE provides many environments for executing the transactions: - -* JavaScript VM: a sandbox blockchain implemented with JavaScript in the browser to emulate a real blockchain. -* Injected Web3: a provider that injects web3 such as `Mist` and `Metamask`, [connecting you to your private blockchain](https://bitfalls.com/2018/03/26/connecting-myetherwallet-mist-metamask-private-blockchain/). -* Web3 Provider: a remote node with geth, parity or any Ethereum client. Can be used to connect to the real network, or to your private blockchain directly without MetaMask in the middle. - -![Remix Execution Environments](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528072980remix-execution-environments.png) - -### Using Remix IDE to Compile and Deploy a Smart Contract - -For the sake of demonstrating what we can achieve using Remix IDE, we’ll use an example contract and we’ll see how we can: - -* compile the contract in Remix IDE -* see some warnings emitted by the compiler when best practices aren’t followed -* deploy the contract on the JavaScript EVM (Ethereum Virtual Machine) -* make transactions on the deployed contract -* see example reads and writes in the terminal IDE. - -We’ll use the following example contract [from this tutorial](https://bitfalls.com/2018/03/31/solidity-development-crash-course-building-blockchain-raffle/) which implements a blockchain raffle: - -``` -pragma solidity ^0.4.20; - -contract Blocksplit { - - address[] public players; - mapping (address => bool) public uniquePlayers; - address[] public winners; - - address public charity = 0xc39eA9DB33F510407D2C77b06157c3Ae57247c2A; - - function() external payable { - play(msg.sender); - } - - function play(address _participant) payable public { - require (msg.value >= 1000000000000000 && msg.value <= 100000000000000000); - require (uniquePlayers[_participant] == false); - - players.push(_participant); - uniquePlayers[_participant] = true; - } - -} -``` - -The contract declares some variables, such as: - -* The `players` array variable, which holds the addresses of the raffle participants. -* The `uniquePlayers` mapping, which is used to save unique players so players don’t participate multiple times from the same address. (An address will be mapped to a boolean, true or false, value indicating if the participant has already participated or not.) -* The `winners` array variable, which will hold the addresses of the winners. -* The `charity` variable, which holds a hardcoded address of a charity where profits will go. - -It also declares: - -* A fallback function marked as `payable`, which enables the smart contract to accept payments. -* A `play()` function that enables participants to enter the raffle by providing an address. - -You can read more details about this contract from this [tutorial](https://bitfalls.com/2018/03/31/solidity-development-crash-course-building-blockchain-raffle/) where all the code is explained in detail. - -Now, go ahead and open the Remix IDE from [remix.ethereum.org](https://remix.ethereum.org/). - -Next, create a new file by clicking on the button with the _+_ icon. - -![Creating a new file](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073262remix-buttons1.png) - -A new dialog will pop up Enter a name for your file (`blocksplit.sol`), then press _OK_: - -![Naming the file](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073351naming-file.png) - -A new tab will be opened in the code editor where you can start writing your contract. So just copy and paste the previous contract code in there. - -First start by compiling the contract. From the _Compile_ tab click _Start to compile_ button. - -We’re getting a message box with two warnings raised by Static Analysis of the code. - -![Two warnings](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073432two-warnings.png) - -If you click on the message box you’ll be taken to the _Analysis_ tab, which provides more information about the warnings: - -![warning info](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073508warning-info.png) - -The first warning is raised if the gas requirements of functions are too high, and the second one is raised if the `require()` or `assert()` functions are not used appropriately. - -You can also use the checkboxes in the _Analysis_ tab to determine when you want the compiler to emit warnings. - -![determine when you want the compiler to emit warnings](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073628when-to-warn.png) - -Next, let’s deploy the contract with our JavaScript VM. Switch to the _Run_ tab, and select _JavaScript VM_ from the dropdown menu. - -![Selecting the JavaScript VM](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073689js-vm.png) - -Next, click the _Deploy_ button below the contract name. - -![The Deploy button](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073758deploy.png) - -Once the contract is deployed successfully on the JavaScript VM, a box will be opened on the bottom of the _Run_ tab. - -![Run tab box](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073835run-tab-box.png) - -Under the name and address of the deployed contract, we have some buttons with red and blue colors. Red buttons refer to actions that cause a write to the blockchain (in our case the fallback and play functions) and need a transaction, where blue buttons refer to reading from blockchain (_charity, players, uniquePlayers and winners_ public variables we defined in the contract’s code). - -You’ll also see a similar message to the following screenshot in the IDE terminal. - -![Terminal message](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528073924terminal-message.png) - -For now, the only variable that holds a value is the _charity_ variable, because the address is hardcoded in the code so if you click on the corresponding button you’ll get the value of that address. - -![address value](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528074002address-value.png) - -The contract is built to allow a participant to enter the raffle by just sending ether to the address (using a payable callback function) or also call the `play()` function with the address of the participant. - -The JavaScript VM provides five fake accounts with _100_ ether each, which we can use to test the contract. You can select a current account from the dropdown menu with the name _Account_ below the _Environment_ dropdown. - -![Select a current account](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528074083select-account.png) - -Now, to send money to the contract, we first set the _Value_ variable (between 0.001 and 0.1) and the unit (ether) from the dropdown menu. Then we call the fallback function by simply clicking on its corresponding red button. - -![Calling the fallback function](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528074156calling-fallback.png) - -That’s it. We’ve sent money to the contract. The address of the selected account should be added to the `players` array. To check that, simply click on the blue _players_ button. - -![Clicking on the blue Players button](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528074260players-button.png) - -You can add other participants by selecting a new account and repeat the previous process (to check if an account is added to the array simply enter the index, from 0 to 4, for that account in the text-box next to _players_ button). - -If you add an account that has already participated, you should get a warning in the terminal and the transaction will fail with a message like the following: - -![Failure message](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2018/06/1528074354failure-message.png) - -### Remix Alternatives - -There are many alternatives for easy development and deployment of smart contracts, such as: - -* [Truffle](http://truffleframework.com/): advertised as the Ethereum Swiss army knife and claims to be the most popular development framework for Ethereum with a mission to make your life a whole lot easier. We’ll be working with Truffle a lot in upcoming tutorials. -* [Embark](https://github.com/embark-framework/embark): a framework that allows you to easily develop and deploy Decentralized Applications (DApps). -* [MetaMask](https://metamask.io/): a bridge that allows you to visit the distributed web of tomorrow in your browser today. It allows you to run Ethereum DApps right in your browser without running a full Ethereum node. For how to develop with MetaMask, check this [faq](https://github.com/MetaMask/faq/). -* [Dapp](https://dapp.readthedocs.io/en/latest/): Dapp is a simple command line tool for smart contract development. -* different plugins for adding Solidity support to popular IDEs such as [this Visual Code plugin](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity) and [this Atom plugin](https://atom.io/packages/language-ethereum) etc. - -### Conclusion - -We’ve introduced you to the Remix IDE for developing smart contracts for the Ethereum blockchain. You can find more detailed information in the [docs](https://remix-ide.readthedocs.io/en/latest/). - -With a basic introduction behind you, feel free to dive in deeper and experiment with changing the code and exploring the different functions and tabs the editor offers. diff --git a/developer-guide/smart-contract-development/ides-and-tools/truffle.md b/developer-guide/smart-contract-development/ides-and-tools/truffle.md deleted file mode 100644 index f37ec11..0000000 --- a/developer-guide/smart-contract-development/ides-and-tools/truffle.md +++ /dev/null @@ -1,117 +0,0 @@ -# Truffle - -In this tutorial, you'll learn how to deploy your smart contracts to a public Ethereum network using Truffle Teams. - -### Prerequisites - -This tutorial assumes that you've already done the following: - -* Created a Truffle Teams account -* Linked a repo that is a Truffle project (need a project? Grab our MetaCoin box with `truffle unbox metacoin` in an empty directory, and push that to your own repository) -* Got a passing build within Truffle Teams -* Grabbed some Ropsten Ether -* Ensured you'll have a stable network connection - -### The Deployments Page - -Head over to [Truffle Teams.](https://trufflesuite.com/) - -On the left navigation bar, click the item near the top labelled **DEPLOYMENTS** next to the parachute icon. - -![Deployments Navbar](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployments-navbar.png) - -Here you'll see a list of your repositories. Click on the one you'd like to deploy. - -![Repo List](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/repo-list.png) - -This will bring you to the deployments page. You'll notice there are three columns: **Commits**, **Staging**, and **Production**. - -#### Commits Column - -**Commits** is a list of all the commits that Truffle Teams is processing, or has processed, as a build. Each will have a status icon to show if the build is in progress, failed, or successful. Commits with a successful icon (a green box with a checkmark, as pictured) will be able to be deployed; the parachute icon in the top right of the commit indicates this. - -![Commit Card](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/commit-card.png) - -#### Staging and Production Columns - -**Staging** contains a list of all testnet (i.e. Ropsten, Görli, Rinkeby, and Kovan) deployments. **Production** contains a list of Mainnet deployments. We'll expand more on how these work later in the tutorial. - -### Your First Deployment - -Currently, Truffle Teams supports Ethereum testnets and Mainnet, and we'll be adding support for other targets over time. - -In this tutorial, we'll be deploying our application to Ropsten. - -Press the parachute icon on the commit you'd like to deploy. - -![Parachute Callout](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/parachute-callout.png) - -A wizard will pop up; under Destination Network, select Ropsten, and then press **Connect Wallet**. - -![Deployment Wizard Ropsten Select](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-wizard-ropsten-select.png) - -MetaMask will pop up asking you to login (if you haven't already). Then you will need to press **Connect** to confirm the connection with Truffle Teams. - -![MetaMask Connect](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/metamask-connect.png) - -If the wizard hasn't changed (i.e. you don't see a button to start deploying), you likely need to switch your MetaMask network to Ropsten. This may reload the page; if this happens, you'll need to repeat the steps above. - -![MetaMask Network](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/metamask-network.png) - -Before moving on, make sure you have the correct account selected in MetaMask. - -![MetaMask Account](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/metamask-account.png) - -Now you should see the wizard prompting for a **Deployment Context**. Leave it set to **Create a New Deployment**. We'll cover what this is later in the tutorial. Press the **OK, START DEPLOYING** button! - -![Deployment Wizard Ready](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-wizard-ready.png) - -From here on, it's important that you don't close the tab, refresh the page, or lose network connection. We're working on a more robust experience that will enable you to pick up unfinished deployments, but for now our current version requires the tab to stay open and connected. - -Truffle Teams will only process so many deployments at the same time, so you may see that your deployment has been queued. You'll have to wait (without closing/refreshing the tab) for your deployment to get to the front of the list. However, we're working diligently to make this experience more seamless going forward. - -Once your deployment has started processing, you'll see a list of steps Truffle Teams is doing to prepare for your deployment. - -![Deployment Wizard Preparing](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-wizard-preparing.png) - -Once the preparation steps are complete, you'll see a screen with a list of your migrations being processed. You should also get a pop-up from MetaMask for your first transaction. - -![First Transaction](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/first-transaction.png) - -You'll notice that this interface with MetaMask is like sending a transaction to any other dapp. It's your account that is sending this transaction, and you have complete control of it. Additionally, we highly recommend that you change the **GAS FEE** to be higher so that your transactions run quicker. For testnets like Ropsten, it's affordable to always select the **Fast** option. - -![MetaMask Gas](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/metamask-gas.png) - -Once you're happy with the transaction gas fee, press the **Confirm** button to send your transaction. Once the transaction is confirmed (the timing of confirmations from MetaMask and Truffle Teams may be slightly offset), you'll receive the next transaction. Repeat this process until you see a message that your deployment is being finalized. - -After a short wait, you'll see a window with your deployment results: - -![Deployment Results](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-results.png) - -Your contracts are deployed! That was easy. Go ahead and push the **Great! Go Back to Workflow** button or the **X** in the wizard. You'll now see a new card in the **Staging** column with the results of your deployment: - -![Deployment Card](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-card.png) - -You can click on the **+ Contracts** bar on the bottom of the card to see a list of your deployed contracts and their addresses: - -![Deployment Card Instance Details](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-card-instance-details.png) - -You can also click the vertical 3 dots in the top right of the card to find a menu. In this menu, you can download a **.zip** file of the Truffle **.json** artifacts used in your frontend webapp, graduate a deployment to production, or archive the deployment. **Note:** Be careful when using the archive feature; we haven't implemented a way to unarchive deployments yet. - -![Deployment Card Menu](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-card-menu.png) - -### Deployment Context - -Circling back to the Deployment Context feature I brushed over earlier when we were setting up our deployment: - -![Deployment Wizard Ready](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/deployment-wizard-ready.png) - -This setting will usually be used for more advanced Truffle applications that utilize the migration system to iteratively migrate more to the blockchain (rather than starting fresh each deployment). - -This option will let you select an existing deployment on the same network that you'd like to use the deployed artifacts for. Truffle supports the concept of migrating your application and will only run the new migration scripts from the last deployment (a.k.a. the deployment context). This becomes useful in a handful of scenarios: maybe you added an extra contract to work with your existing deployment, your Truffle project uses proxy contracts to upgrade your contracts, and more. Technically speaking, selecting the Deployment Context will put the Truffle artifacts from the deployment context you selected in the directory before running `truffle migrate`. - -### Graduating Deployments - -Happy with a particular deployment in **Staging**? You can select the **Graduate** option from the menu to use the same build as the basis of your deployment into Mainnet. Other than selecting a different network in MetaMask, the steps are the same! After you're done, you'll see a new deployment in the production section. - -![Mainnet Deployment](https://www.trufflesuite.com/img/tutorials/learn-how-to-deploy-with-truffle-teams/mainnet-deployment.png) diff --git a/developer-guide/smart-contract-development/remix.md b/developer-guide/smart-contract-development/remix.md new file mode 100644 index 0000000..0b02e3b --- /dev/null +++ b/developer-guide/smart-contract-development/remix.md @@ -0,0 +1,204 @@ +# Remix + +**Remix is a Solidity IDE that’s used to write, compile and debug Solidity code.** [**Solidity**](https://solidity.readthedocs.io/) **is a high-level, contract-oriented programming language for writing smart contracts. It was influenced by popular languages such as C++, Python and JavaScript.** + +IDE stands for Integrated Development Environment and is an application with a set of tools designed to help programmers execute different tasks related to software development such as writing, compiling, executing and debugging code. + +Before you begin using Remix to develop smart contracts, make sure you’re familiar with some basic concepts. In particular, give these articles about [blockchain](https://www.sitepoint.com/blockchain-what-it-is-how-it-works-why-its-so-popular) and [Ethereum](https://bitfalls.com/2017/09/19/what-ethereum-compare-to-bitcoin/) a read. + +#### What’s a Smart Contract/Dapp? + +A smart contract is a trust-less agreement between two parties that makes use of blockchain technology, to enforce the parties to adhere to the terms, rather than relying on the traditional ways such as trusting a middleman or using laws to handle disputes. + +Using the Ethereum blockchain, you can create smart contracts with the Solidity language (among others). Ethereum is not the only platform that can be used to create smart contacts, but it’s the most popular choice, as it was designed from the start to support building them. + +[Dapp](http://ethdocs.org/en/latest/contracts-and-transactions/developer-tools.html#dapps) stands for _**de**_**centralized \_app**\_**lication** and is a web3 application that can have a front-end written in traditional languages such as JavaScript, HTML, CSS and a smart contract (as back-end code) which runs on the blockchain. So you can simply think of a Dapp as the front end plus the associated blockchain smart contract(s). + +Unlike the smart contract deployed on the blockchain itself, the front end of a Dapp can be either hosted on a centralized server like a CDN or on decentralized storage like [Swarm](http://ethdocs.org/en/latest/contracts-and-transactions/developer-tools.html#swarm). + +### Accessing the Remix IDE + +You can access the Remix IDE in different ways: online, via a web browser like Chrome, from a locally installed copy, or from Mist (the Ethereum Dapp browser). + +#### Using the In-Browser Remix IDE + +You can access the Remix IDE from your web browser without any special installation. Visit [https://remix.ethereum.org/](https://remix.ethereum.org/) and you’ll be presented with a complete IDE with a code editor and various panels for compiling, running and debugging your smart contracts. You’ll have a default example _Ballot_ contract that you can play with. Beside you should refer [Remix](https://remix-ide.readthedocs.io/en/latest/) document if need more detail. + +
+ + + +### Remix Panels + +After seeing how to open the Remix IDE, let’s now see the various panels composing the IDE. + +#### File Explorer + +The file explorer provides a view with the created files stored in the browser’s storage. You can rename or delete any file by right-clicking on it, then choosing the right operation from the context menu. + +
+ +Please note that the file explorer uses the browser’s local storage by default, which means you can lose all your files if you clear or the operating system automatically clears the storage. For advanced work, it’s recommended to use [Remixd](https://remix-ide.readthedocs.io/en/latest/remixd.html) — a Node.js tool which allows the Remix IDE to access your computer’s file system. + +**Creating/Opening Files in Remix** + +You can create a new file in the browser local storage using the first button _file_ on the top left. You can then provide a name and press Enter. + +Using the second button from top left, you can open an existing Solidity file from your computer file system into the Remix IDE. The file will also be stored in the browser’s local storage. + +**Publishing Explorer Files as GitHub Gists** + +Using the third and fourth buttons from top left, you can publish files from the IDE as a public GitHub gist. + +**Copying Files to Another Instance of Remix IDE** + +Using the fifth button from top left, you can copy files from the local storage to another instance of Remix by providing the URL of the instance. + +**Connecting to the Local File System** + +The last button can be used to connect the Remix IDE to your local file system if you’re running the [Remixd](https://remix-ide.readthedocs.io/en/latest/remixd.html) tool. + +#### Solidity Code Editor + +The Solidity code editor provides the interface where you can write your code with many features such as syntax highlighting, auto-recompling, auto-saving etc. You can open multiple tabs and also increase/decrease the font size using the _+/-_ button in the top-left corner. + +
+ +#### Terminal + +The terminal window below the editor integrates a JavaScript interpreter and the `web3` object. You can execute JavaScript code in the current context, visualize the actions performed from the IDE, visualize all network transactions or transactions created from the Remix IDE etc. You can also search for data in the terminal and clear the logs. + +
+ +#### Tabs Panel + +The _Tabs_ panel provides many tabs for working with the IDE: + +* the _Compile_ tab: used for compiling a smart contract and publishing on Swarm and used for updating settings like the compiler version and many general settings for the editor +* the _Run_ and _Deploy_ tab: used for sending transactions to the configured environment +* the _Settings_ tab: used for updating settings like the compiler version and many general settings for the editor +* the _Plugin_ tab: used for install local plugin in Remix +* the _Search_ tab: used for search file which locates in Remix. +* the _File Explorer_ tab: used for display all files and folders in Remix +* the Debug tab: used for debug when execute contract + +
+ +### Remix Execution Environments + +The Remix IDE provides many environments for executing the transactions: + +* JavaScript VM: a sandbox blockchain implemented with JavaScript in the browser to emulate a real blockchain.› +* Injected Web3: a provider that injects web3 such as [`Metamask`](https://coinsbench.com/how-to-use-remix-ide-to-deploy-your-smart-contract-on-chain-5e37e1faa15a), connecting you to your private blockchain. +* Web3 Provider: a remote node with geth, parity or any Ethereum client. Can be used to connect to the real network, or to your private blockchain directly without MetaMask in the middle. + +
+ +### Using Remix IDE to Compile and Deploy a Smart Contract + +For the sake of demonstrating what we can achieve using Remix IDE, we’ll use an example contract and we’ll see how we can: + +* compile the contract in Remix IDE +* see some warnings emitted by the compiler when best practices aren’t followed +* deploy the contract on the JavaScript EVM (Ethereum Virtual Machine) +* make transactions on the deployed contract +* see example reads and writes in the terminal IDE. + +We’ll use the following example contract [from this tutorial ](https://docs.base.org/guides/deploy-with-remix/)which implements a blockchain : + +```` +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.8.2 <0.9.0; + +/** + * @title Storage + * @dev Store & retrieve value in a variable + * @custom:dev-run-script ./scripts/deploy_with_ethers.ts + */ +contract Storage { + + uint256 number; + + /** + * @dev Store value in variable + * @param num value to store + */ + function store(uint256 num) public { + number = num; + } + + /** + * @dev Return value + * @return value of 'number' + */ + function retrieve() public view returns (uint256){ + return number; + } +} +``` +```` + +Now, go ahead and open the Remix IDE from [remix.ethereum.org](https://remix.ethereum.org/). + +Next, create a new file by clicking on the button with the _+_ icon. + +
+ +And then you must enter a name , then press Enter: + +
+ +A new tab will be opened in the code editor where you can start writing your contract. So just copy and paste the previous contract code in there. + +
+ +Next, let’s deploy the contract with our RemixVM. Switch to the _Deploy and_ _Run Transaction_ tab, and select _Remix VM_ from the dropdown menu. + +
+ +Next, click the _Deploy_ button below the contract name. + +
+ +Once the contract is deployed successfully on the Remix VM, a box will be opened on the bottom as below . + +
+ +Under the name and address of the deployed contract, we have some buttons with red and blue colors. Red buttons refer to actions that cause a write to the blockchain , where blue buttons refer to reading from blockchain + +The Remix VM provides 15 fake accounts with _100_ ether each, which we can use to test the contract. You can select a current account from the dropdown menu with the name _Account_ below the _Environment_ dropdown. + +
+ +You can see we have 2 function , Therefore we interact with smart contract easily . As below , We just called function store to change global variable number of contract. + +
+ +After call a function in smart contract , you can check log in terminal as below: + +
+ +When compile contract , we might run into several errors , Therefore , Remix support developer by using ChatGPT , Please see below: + + + +
+ + + +### Remix Alternatives + +There are many alternatives for easy development and deployment of smart contracts, such as: + +* [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#overview): Hardhat is a development environment for Ethereum software. It consists of different components for editing, compiling, debugging and deploying your smart contracts and dApps, all of which work together to create a complete development environment. +* [Embark](https://github.com/embark-framework/embark): a framework that allows you to easily develop and deploy Decentralized Applications (DApps). +* [MetaMask](https://metamask.io/): a bridge that allows you to visit the distributed web of tomorrow in your browser today. It allows you to run Ethereum DApps right in your browser without running a full Ethereum node. For how to develop with MetaMask, check [this](https://docs.metamask.io/). +* [Dapp](https://dapp.readthedocs.io/en/latest/): Dapp is a simple command line tool for smart contract development. +* different plugins for adding Solidity support to popular IDEs such as [this Visual Code plugin](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity) and [this Atom plugin](https://atom.io/packages/language-ethereum) etc. + +### Conclusion + +We’ve introduced you to the Remix IDE for developing smart contracts for the Ethereum blockchain. You can find more detailed information in the [docs](https://remix-ide.readthedocs.io/en/latest/). + +With a basic introduction behind you, feel free to dive in deeper and experiment with changing the code and exploring the different functions and tabs the editor offers. diff --git a/developer-guide/smart-contract-development/solidity/README.md b/developer-guide/smart-contract-development/solidity/README.md deleted file mode 100644 index 630c059..0000000 --- a/developer-guide/smart-contract-development/solidity/README.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: >- - This page is a reduced version of the [Solidity docs - site](https://solidity.readthedocs.io/en/v0.4.24/) that is adapted to - TomoChain network to avoid overwhelming information. ---- - -# Solidity - -Solidity is a contract-oriented, high-level language for implementing smart contracts. It was influenced by Python and JavaScript and is designed to target the Ethereum Virtual Machine (EVM) both Ethereum and TomoChain. - -Solidity is statically typed, supports inheritance, libraries and complex user-defined types among other features. - -As you will see, it is possible to create contracts for voting, crowdfunding, blind auctions, multi-signature wallets and more. - -{% hint style="info" %} -The best way to try out Solidity right now is using [Remix](https://remix.ethereum.org/) (it can take a while to load, please be patient). Remix is a web browser based IDE that allows you to write Solidity smart contracts, then deploy and run the smart contracts. -{% endhint %} - -Once you're strong enough, on the next pages, we will first see a [simple smart contract](https://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html#simple-smart-contract) written in Solidity followed by the basics about [blockchains](https://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html#blockchain-basics) and the [Ethereum Virtual Machine](https://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html#the-ethereum-virtual-machine). - -The next section will explain several _features_ of Solidity by giving useful [example contracts](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting). Remember that you can always try out the contracts [in your browser](https://remix.ethereum.org/)! - -The last and most extensive section will cover all aspects of Solidity in depth. - -If you still have questions, you can try searching or asking on the [Ethereum Stackexchange](https://ethereum.stackexchange.com/) site, or come to our [Gitter channel](https://gitter.im/ethereum/solidity/). Ideas for improving Solidity or this documentation are always welcome! the world: - -{% code title="hello.sh" %} -```bash -# Ain't no code for that yet, sorry -echo 'You got to trust me on this, I saved the world' -``` -{% endcode %} - diff --git a/developer-guide/smart-contract-development/solidity/a-simple-smart-contract.md b/developer-guide/smart-contract-development/solidity/a-simple-smart-contract.md deleted file mode 100644 index 4c282dc..0000000 --- a/developer-guide/smart-contract-development/solidity/a-simple-smart-contract.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -description: >- - A basic example that sets the value of a variable and exposes it for other - contracts to access ---- - -# A Simple Smart Contract - -#### Storage Example - - - -```text -pragma solidity >=0.4.0 <=0.5.0; - -contract SimpleStorage { - uint storedData; - - function set(uint x) public { - storedData = x; - } - - function get() public view returns (uint) { - return storedData; - } -} -``` - -The first line tells you that the source code is written for Solidity version 0.4.0, or a newer version of the language up to 0.5.0. This is to ensure that the contract is not compilable with the current EVM version in TomoChain. - -A contract in the sense of Solidity is a collection of code \(its _functions_\) and data \(its _state_\) that resides at a specific address on the TomoChain blockchain. The line `uint storedData;` declares a state variable called `storedData` of type `uint` \(_u_nsigned _int_eger of _256_ bits\). You can think of it as a single slot in a database that you can query and alter by calling functions of the code that manages the database. In this example, the contract defines the functions `set` and `get` that can be used to modify or retrieve the value of the variable. - -To access a state variable, you do not need the prefix `this.` as is common in other languages. - -This contract does not do much yet apart from allowing anyone to store a single number that is accessible by anyone in the world without a \(feasible\) way to prevent you from publishing this number. Anyone could call `set` again with a different value and overwrite your number, but the number is still stored in the history of the blockchain. Later, you will see how you can impose access restrictions so that only you can alter the number. - -#### Subcurrency Example - -The following contract implements the simplest form of a cryptocurrency. The contract allows only its creator to create new coins \(different issuance schemes are possible\). Anyone can send coins to each other without a need for registering with a username and password, all you need is a TomoChain private key. - -```text -pragma solidity 0.5.0; - -contract Coin { - // The keyword "public" makes variables - // accessible from other contracts - address public minter; - mapping (address => uint) public balances; - - // Events allow clients to react to specific - // contract changes you declare - event Sent(address from, address to, uint amount); - - // Constructor code is only run when the contract - // is created - constructor() public { - minter = msg.sender; - } - - // Sends an amount of newly created coins to an address - // Can only be called by the contract creator - function mint(address receiver, uint amount) public { - require(msg.sender == minter); - require(amount < 1e60); - balances[receiver] += amount; - } - - // Sends an amount of existing coins - // from any caller to an address - function send(address receiver, uint amount) public { - require(amount <= balances[msg.sender], "Insufficient balance."); - balances[msg.sender] -= amount; - balances[receiver] += amount; - emit Sent(msg.sender, receiver, amount); - } -} -``` - -This contract introduces some new concepts, let us go through them one by one. - -The line `address public minter;` declares a state variable of type [address](https://solidity.readthedocs.io/en/v0.6.3/types.html#address). The `address` type is a 160-bit value that does not allow any arithmetic operations. It is suitable for storing addresses of contracts, or a hash of the public half of a keypair belonging to [external accounts](https://solidity.readthedocs.io/en/v0.6.3/introduction-to-smart-contracts.html#accounts). - -The keyword `public` automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is equivalent to the following \(ignore `external` and `view` for now\): - -```text -function minter() external view returns (address) { return minter; } -``` - -You could add a function like the above yourself, but you would have a function and state variable with the same name. You do not need to do this, the compiler figures it out for you. - -The next line, `mapping (address => uint) public balances;` also creates a public state variable, but it is a more complex datatype. The [mapping](https://solidity.readthedocs.io/en/v0.6.3/types.html#mapping-types) type maps addresses to [unsigned integers](https://solidity.readthedocs.io/en/v0.6.3/types.html#integers). - -Mappings can be seen as [hash tables](https://en.wikipedia.org/wiki/Hash_table) which are virtually initialised such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. However, it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. Record what you added to the mapping, or use it in a context where this is not needed. Or even better, keep a list, or use a more suitable data type. - -The [getter function](https://solidity.readthedocs.io/en/v0.6.3/contracts.html#getter-functions) created by the `public` keyword is more complex in the case of a mapping. It looks like the following: - -```text -function balances(address _account) external view returns (uint) { - return balances[_account]; -} -``` - -You can use this function to query the balance of a single account. - -The line `event Sent(address from, address to, uint amount);` declares an [“event”](https://solidity.readthedocs.io/en/v0.6.3/contracts.html#events), which is emitted in the last line of the function `send`. TomoChain clients such as web applications can listen for these events emitted on the blockchain without much cost. As soon as it is emitted, the listener receives the arguments `from`, `to` and `amount`, which makes it possible to track transactions. - -To listen for this event, you could use the following JavaScript code, which uses [web3.js](https://github.com/ethereum/web3.js/) to create the `Coin` contract object, and any user interface calls the automatically generated `balances` function from above: - -```text -Coin.Sent().watch({}, '', function(error, result) { - if (!error) { - console.log("Coin transfer: " + result.args.amount + - " coins were sent from " + result.args.from + - " to " + result.args.to + "."); - console.log("Balances now:\n" + - "Sender: " + Coin.balances.call(result.args.from) + - "Receiver: " + Coin.balances.call(result.args.to)); - } -}) -``` - -The [constructor](https://solidity.readthedocs.io/en/v0.6.3/contracts.html#constructor) is a special function run during the creation of the contract and cannot be called afterwards. In this case, it permanently stores the address of the person creating the contract. The `msg` variable \(together with `tx` and `block`\) is a [special global variable](https://solidity.readthedocs.io/en/v0.6.3/units-and-global-variables.html#special-variables-functions) that contains properties which allow access to the blockchain. `msg.sender` is always the address where the current \(external\) function call came from. - -The functions that make up the contract, and that users and contracts can call are `mint` and `send`. - -The `mint` function sends an amount of newly created coins to another address. The [require](https://solidity.readthedocs.io/en/v0.6.3/control-structures.html#assert-and-require) function call defines conditions that reverts all changes if not met. In this example, `require(msg.sender == minter);` ensures that only the creator of the contract can call `mint`, and `require(amount < 1e60);` ensures a maximum amount of tokens. This ensures that there are no overflow errors in the future. - -The `send` function can be used by anyone \(who already has some of these coins\) to send coins to anyone else. If the sender does not have enough coins to send, the `require` call fails and provides the sender with an appropriate error message string. - diff --git a/developer-guide/smart-contract-development/solidity/installing-solidity-compiler.md b/developer-guide/smart-contract-development/solidity/installing-solidity-compiler.md deleted file mode 100644 index d8bda81..0000000 --- a/developer-guide/smart-contract-development/solidity/installing-solidity-compiler.md +++ /dev/null @@ -1,25 +0,0 @@ -# Installing Solidity compiler - -### Versioning - -Solidity versions follow [semantic versioning](https://semver.org/) and in addition to releases, **nightly development builds** are also made available. However, we recommend to only install Solidity compiler versions <= 0.5.0 as higher versions introduce breaking changes resulting in opcodes that are not available in that current EVM version of TomoChain. - -### Remix - -_We recommend Remix for small contracts and for quickly learning Solidity._ - -[Access Remix online](https://remix.ethereum.org/), you do not need to install anything. If you want to use it without connection to the Internet, go to [https://github.com/ethereum/remix-live/tree/gh-pages](https://github.com/ethereum/remix-live/tree/gh-pages) and download the `.zip` file as explained on that page. Remix is also a convenient option for testing nightly builds without installing multiple Solidity versions. - -Further options on this page detail installing commandline Solidity compiler software on your computer. Choose a commandline compiler if you are working on a larger contract or if you require more compilation options. - -To use Remix with TomoChain, you only need to connect your Coin98 Wallet to TomoChain using TomoChain's RPC and following [this tutorial](../../../general/how-to-connect-to-tomochain-network/metamask.md). - -### npm / Node.js - -Use npm for a convenient and portable way to install solcjs, a Solidity compiler. The solcjs program has fewer features than the ways to access the compiler described further down this page. The [Using the Commandline Compiler](https://solidity.readthedocs.io/en/v0.6.3/using-the-compiler.html#commandline-compiler) documentation assumes you are using the full-featured compiler, solc. The usage of solcjs is documented inside its own [repository](https://github.com/ethereum/solc-js). - -Note: The solc-js project is derived from the C++ solc by using Emscripten which means that both use the same compiler source code. solc-js can be used in JavaScript projects directly (such as Remix). Please refer to the solc-js repository for instructions. - -``` -npm install -g solc -``` diff --git a/developer-guide/smart-contract-development/solidity/solidity-by-example.md b/developer-guide/smart-contract-development/solidity/solidity-by-example.md deleted file mode 100644 index 53b3b18..0000000 --- a/developer-guide/smart-contract-development/solidity/solidity-by-example.md +++ /dev/null @@ -1,1002 +0,0 @@ -# Solidity by Example - -### Voting - -The following contract showcases a lot of Solidity’s features. It implements a voting contract. Of course, the main problems of electronic voting is how to assign voting rights to the correct persons and how to prevent manipulation. We will not solve all problems here, but at least we will show how delegated voting can be done so that vote counting is **automatic and completely transparent** at the same time. - -The idea is to create one contract per ballot, providing a short name for each option. Then the creator of the contract who serves as chairperson will give the right to vote to each address individually. - -The persons behind the addresses can then choose to either vote themselves or to delegate their vote to a person they trust. - -At the end of the voting time, `winningProposal()` will return the proposal with the largest number of votes. - -``` -pragma solidity >=0.4.22 <=0.5.0; - -/// @title Voting with delegation. -contract Ballot { - // This declares a new complex type which will - // be used for variables later. - // It will represent a single voter. - struct Voter { - uint weight; // weight is accumulated by delegation - bool voted; // if true, that person already voted - address delegate; // person delegated to - uint vote; // index of the voted proposal - } - - // This is a type for a single proposal. - struct Proposal { - bytes32 name; // short name (up to 32 bytes) - uint voteCount; // number of accumulated votes - } - - address public chairperson; - - // This declares a state variable that - // stores a `Voter` struct for each possible address. - mapping(address => Voter) public voters; - - // A dynamically-sized array of `Proposal` structs. - Proposal[] public proposals; - - /// Create a new ballot to choose one of `proposalNames`. - constructor(bytes32[] memory proposalNames) public { - chairperson = msg.sender; - voters[chairperson].weight = 1; - - // For each of the provided proposal names, - // create a new proposal object and add it - // to the end of the array. - for (uint i = 0; i < proposalNames.length; i++) { - // `Proposal({...})` creates a temporary - // Proposal object and `proposals.push(...)` - // appends it to the end of `proposals`. - proposals.push(Proposal({ - name: proposalNames[i], - voteCount: 0 - })); - } - } - - // Give `voter` the right to vote on this ballot. - // May only be called by `chairperson`. - function giveRightToVote(address voter) public { - // If the first argument of `require` evaluates - // to `false`, execution terminates and all - // changes to the state and to TOMO balances - // are reverted. - // This used to consume all gas in old EVM versions, but - // not anymore. - // It is often a good idea to use `require` to check if - // functions are called correctly. - // As a second argument, you can also provide an - // explanation about what went wrong. - require( - msg.sender == chairperson, - "Only chairperson can give right to vote." - ); - require( - !voters[voter].voted, - "The voter already voted." - ); - require(voters[voter].weight == 0); - voters[voter].weight = 1; - } - - /// Delegate your vote to the voter `to`. - function delegate(address to) public { - // assigns reference - Voter storage sender = voters[msg.sender]; - require(!sender.voted, "You already voted."); - - require(to != msg.sender, "Self-delegation is disallowed."); - - // Forward the delegation as long as - // `to` also delegated. - // In general, such loops are very dangerous, - // because if they run too long, they might - // need more gas than is available in a block. - // In this case, the delegation will not be executed, - // but in other situations, such loops might - // cause a contract to get "stuck" completely. - while (voters[to].delegate != address(0)) { - to = voters[to].delegate; - - // We found a loop in the delegation, not allowed. - require(to != msg.sender, "Found loop in delegation."); - } - - // Since `sender` is a reference, this - // modifies `voters[msg.sender].voted` - sender.voted = true; - sender.delegate = to; - Voter storage delegate_ = voters[to]; - if (delegate_.voted) { - // If the delegate already voted, - // directly add to the number of votes - proposals[delegate_.vote].voteCount += sender.weight; - } else { - // If the delegate did not vote yet, - // add to her weight. - delegate_.weight += sender.weight; - } - } - - /// Give your vote (including votes delegated to you) - /// to proposal `proposals[proposal].name`. - function vote(uint proposal) public { - Voter storage sender = voters[msg.sender]; - require(sender.weight != 0, "Has no right to vote"); - require(!sender.voted, "Already voted."); - sender.voted = true; - sender.vote = proposal; - - // If `proposal` is out of the range of the array, - // this will throw automatically and revert all - // changes. - proposals[proposal].voteCount += sender.weight; - } - - /// @dev Computes the winning proposal taking all - /// previous votes into account. - function winningProposal() public view - returns (uint winningProposal_) - { - uint winningVoteCount = 0; - for (uint p = 0; p < proposals.length; p++) { - if (proposals[p].voteCount > winningVoteCount) { - winningVoteCount = proposals[p].voteCount; - winningProposal_ = p; - } - } - } - - // Calls winningProposal() function to get the index - // of the winner contained in the proposals array and then - // returns the name of the winner - function winnerName() public view - returns (bytes32 winnerName_) - { - winnerName_ = proposals[winningProposal()].name; - } -} -``` - -#### Possible Improvements - -Currently, many transactions are needed to assign the rights to vote to all participants. Can you think of a better way? - -### Blind Auction - -In this section, we will show how easy it is to create a completely blind auction contract on TomoChain network. We will start with an open auction where everyone can see the bids that are made and then extend this contract into a blind auction where it is not possible to see the actual bid until the bidding period ends. - -#### Simple Open Auction - -The general idea of the following simple auction contract is that everyone can send their bids during a bidding period. The bids already include sending money / TOMO in order to bind the bidders to their bid. If the highest bid is raised, the previously highest bidder gets their money back. After the end of the bidding period, the contract has to be called manually for the beneficiary to receive their money - contracts cannot activate themselves. - -``` -pragma solidity >=0.4.22 <=0.5.0; - -contract SimpleAuction { - // Parameters of the auction. Times are either - // absolute unix timestamps (seconds since 1970-01-01) - // or time periods in seconds. - address payable public beneficiary; - uint public auctionEndTime; - - // Current state of the auction. - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - // Set to true at the end, disallows any change. - // By default initialized to `false`. - bool ended; - - // Events that will be emitted on changes. - event HighestBidIncreased(address bidder, uint amount); - event AuctionEnded(address winner, uint amount); - - // The following is a so-called natspec comment, - // recognizable by the three slashes. - // It will be shown when the user is asked to - // confirm a transaction. - - /// Create a simple auction with `_biddingTime` - /// seconds bidding time on behalf of the - /// beneficiary address `_beneficiary`. - constructor( - uint _biddingTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - auctionEndTime = now + _biddingTime; - } - - /// Bid on the auction with the value sent - /// together with this transaction. - /// The value will only be refunded if the - /// auction is not won. - function bid() public payable { - // No arguments are necessary, all - // information is already part of - // the transaction. The keyword payable - // is required for the function to - // be able to receive TOMO. - - // Revert the call if the bidding - // period is over. - require( - now <= auctionEndTime, - "Auction already ended." - ); - - // If the bid is not higher, send the - // money back (the failing require - // will revert all changes in this - // function execution including - // it having received the money). - require( - msg.value > highestBid, - "There already is a higher bid." - ); - - if (highestBid != 0) { - // Sending back the money by simply using - // highestBidder.send(highestBid) is a security risk - // because it could execute an untrusted contract. - // It is always safer to let the recipients - // withdraw their money themselves. - pendingReturns[highestBidder] += highestBid; - } - highestBidder = msg.sender; - highestBid = msg.value; - emit HighestBidIncreased(msg.sender, msg.value); - } - - /// Withdraw a bid that was overbid. - function withdraw() public returns (bool) { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `send` returns. - pendingReturns[msg.sender] = 0; - - if (!msg.sender.send(amount)) { - // No need to call throw here, just reset the amount owing - pendingReturns[msg.sender] = amount; - return false; - } - } - return true; - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() public { - // It is a good guideline to structure functions that interact - // with other contracts (i.e. they call functions or send TOMO) - // into three phases: - // 1. checking conditions - // 2. performing actions (potentially changing conditions) - // 3. interacting with other contracts - // If these phases are mixed up, the other contract could call - // back into the current contract and modify the state or cause - // effects (TOMO payout) to be performed multiple times. - // If functions called internally include interaction with external - // contracts, they also have to be considered interaction with - // external contracts. - - // 1. Conditions - require(now >= auctionEndTime, "Auction not yet ended."); - require(!ended, "auctionEnd has already been called."); - - // 2. Effects - ended = true; - emit AuctionEnded(highestBidder, highestBid); - - // 3. Interaction - beneficiary.transfer(highestBid); - } -} -``` - -#### Blind Auction - -The previous open auction is extended to a blind auction in the following. The advantage of a blind auction is that there is no time pressure towards the end of the bidding period. Creating a blind auction on a transparent computing platform might sound like a contradiction, but cryptography comes to the rescue. - -During the **bidding period**, a bidder does not actually send their bid, but only a hashed version of it. Since it is currently considered practically impossible to find two (sufficiently long) values whose hash values are equal, the bidder commits to the bid by that. After the end of the bidding period, the bidders have to reveal their bids: They send their values unencrypted and the contract checks that the hash value is the same as the one provided during the bidding period. - -Another challenge is how to make the auction **binding and blind** at the same time: The only way to prevent the bidder from just not sending the money after they won the auction is to make them send it together with the bid. Since value transfers cannot be blinded in TomoChain, anyone can see the value. - -The following contract solves this problem by accepting any value that is larger than the highest bid. Since this can of course only be checked during the reveal phase, some bids might be **invalid**, and this is on purpose (it even provides an explicit flag to place invalid bids with high value transfers): Bidders can confuse competition by placing several high or low invalid bids. - -``` -pragma solidity >0.4.23 <=0.5.0; - -contract BlindAuction { - struct Bid { - bytes32 blindedBid; - uint deposit; - } - - address payable public beneficiary; - uint public biddingEnd; - uint public revealEnd; - bool public ended; - - mapping(address => Bid[]) public bids; - - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - event AuctionEnded(address winner, uint highestBid); - - /// Modifiers are a convenient way to validate inputs to - /// functions. `onlyBefore` is applied to `bid` below: - /// The new function body is the modifier's body where - /// `_` is replaced by the old function body. - modifier onlyBefore(uint _time) { require(now < _time); _; } - modifier onlyAfter(uint _time) { require(now > _time); _; } - - constructor( - uint _biddingTime, - uint _revealTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - biddingEnd = now + _biddingTime; - revealEnd = biddingEnd + _revealTime; - } - - /// Place a blinded bid with `_blindedBid` = - /// keccak256(abi.encodePacked(value, fake, secret)). - /// The sent TOMO is only refunded if the bid is correctly - /// revealed in the revealing phase. The bid is valid if the - /// TOMO sent together with the bid is at least "value" and - /// "fake" is not true. Setting "fake" to true and sending - /// not the exact amount are ways to hide the real bid but - /// still make the required deposit. The same address can - /// place multiple bids. - function bid(bytes32 _blindedBid) - public - payable - onlyBefore(biddingEnd) - { - bids[msg.sender].push(Bid({ - blindedBid: _blindedBid, - deposit: msg.value - })); - } - - /// Reveal your blinded bids. You will get a refund for all - /// correctly blinded invalid bids and for all bids except for - /// the totally highest. - function reveal( - uint[] memory _values, - bool[] memory _fake, - bytes32[] memory _secret - ) - public - onlyAfter(biddingEnd) - onlyBefore(revealEnd) - { - uint length = bids[msg.sender].length; - require(_values.length == length); - require(_fake.length == length); - require(_secret.length == length); - - uint refund; - for (uint i = 0; i < length; i++) { - Bid storage bidToCheck = bids[msg.sender][i]; - (uint value, bool fake, bytes32 secret) = - (_values[i], _fake[i], _secret[i]); - if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) { - // Bid was not actually revealed. - // Do not refund deposit. - continue; - } - refund += bidToCheck.deposit; - if (!fake && bidToCheck.deposit >= value) { - if (placeBid(msg.sender, value)) - refund -= value; - } - // Make it impossible for the sender to re-claim - // the same deposit. - bidToCheck.blindedBid = bytes32(0); - } - msg.sender.transfer(refund); - } - - /// Withdraw a bid that was overbid. - function withdraw() public { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `transfer` returns (see the remark above about - // conditions -> effects -> interaction). - pendingReturns[msg.sender] = 0; - - msg.sender.transfer(amount); - } - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() - public - onlyAfter(revealEnd) - { - require(!ended); - emit AuctionEnded(highestBidder, highestBid); - ended = true; - beneficiary.transfer(highestBid); - } - - // This is an "internal" function which means that it - // can only be called from the contract itself (or from - // derived contracts). - function placeBid(address bidder, uint value) internal - returns (bool success) - { - if (value <= highestBid) { - return false; - } - if (highestBidder != address(0)) { - // Refund the previously highest bidder. - pendingReturns[highestBidder] += highestBid; - } - highestBid = value; - highestBidder = bidder; - return true; - } -} -``` - -### Safe Remote Purchase - -Purchasing goods remotely currently requires multiple parties that need to trust each other. The simplest configuration involves a seller and a buyer. The buyer would like to receive an item from the seller and the seller would like to get money (or an equivalent) in return. The problematic part is the shipment here: There is no way to determine for sure that the item arrived at the buyer. - -There are multiple ways to solve this problem, but all fall short in one or the other way. In the following example, both parties have to put twice the value of the item into the contract as escrow. As soon as this happened, the money will stay locked inside the contract until the buyer confirms that they received the item. After that, the buyer is returned the value (half of their deposit) and the seller gets three times the value (their deposit plus the value). The idea behind this is that both parties have an incentive to resolve the situation or otherwise their money is locked forever. - -This contract of course does not solve the problem, but gives an overview of how you can use state machine-like constructs inside a contract. - -``` -pragma solidity >=0.4.22 <=0.5.0; - -contract Purchase { - uint public value; - address payable public seller; - address payable public buyer; - - enum State { Created, Locked, Release, Inactive } - // The state variable has a default value of the first member, `State.created` - State public state; - - modifier condition(bool _condition) { - require(_condition); - _; - } - - modifier onlyBuyer() { - require( - msg.sender == buyer, - "Only buyer can call this." - ); - _; - } - - modifier onlySeller() { - require( - msg.sender == seller, - "Only seller can call this." - ); - _; - } - - modifier inState(State _state) { - require( - state == _state, - "Invalid state." - ); - _; - } - - event Aborted(); - event PurchaseConfirmed(); - event ItemReceived(); - event SellerRefunded(); - - // Ensure that `msg.value` is an even number. - // Division will truncate if it is an odd number. - // Check via multiplication that it wasn't an odd number. - constructor() public payable { - seller = msg.sender; - value = msg.value / 2; - require((2 * value) == msg.value, "Value has to be even."); - } - - /// Abort the purchase and reclaim the TOMO. - /// Can only be called by the seller before - /// the contract is locked. - function abort() - public - onlySeller - inState(State.Created) - { - emit Aborted(); - state = State.Inactive; - // We use transfer here directly. It is - // reentrancy-safe, because it is the - // last call in this function and we - // already changed the state. - seller.transfer(address(this).balance); - } - - /// Confirm the purchase as buyer. - /// Transaction has to include `2 * value` TOMO. - /// The TOMO will be locked until confirmReceived - /// is called. - function confirmPurchase() - public - inState(State.Created) - condition(msg.value == (2 * value)) - payable - { - emit PurchaseConfirmed(); - buyer = msg.sender; - state = State.Locked; - } - - /// Confirm that you (the buyer) received the item. - /// This will release the locked TOMO. - function confirmReceived() - public - onlyBuyer - inState(State.Locked) - { - emit ItemReceived(); - // It is important to change the state first because - // otherwise, the contracts called using `send` below - // can call in again here. - state = State.Release; - - buyer.transfer(value); - } - - /// This function refunds the seller, i.e. - /// pays back the locked funds of the seller. - function refundSeller() - public - onlySeller - inState(State.Release) - { - emit SellerRefunded(); - // It is important to change the state first because - // otherwise, the contracts called using `send` below - // can call in again here. - state = State.Inactive; - - seller.transfer(3 * value); - } -} -``` - -### Micropayment Channel - -In this section we will learn how to build an example implementation of a payment channel. It uses cryptographic signatures to make repeated transfers of TOMO between the same parties secure, instantaneous, and without transaction fees. For the example, we need to understand how to sign and verify signatures, and setup the payment channel. - -#### Creating and verifying signatures - -Imagine Alice wants to send a quantity of TOMO to Bob, i.e. Alice is the sender and the Bob is the recipient. - -Alice only needs to send cryptographically signed messages off-chain (e.g. via email) to Bob and it is similar to writing checks. - -Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on TomoChain. Alice will build a simple smart contract that lets her transmit TOMO, but instead of calling a function herself to initiate a payment, she will let Bob do that, and therefore pay the transaction fee. - -The contract will work as follows: - -> 1. Alice deploys the `ReceiverPays` contract, attaching enough TOMO to cover the payments that will be made. -> 2. Alice authorises a payment by signing a message with their private key. -> 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret (explained later), and the mechanism for sending it does not matter. -> 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the authenticity of the message and then releases the funds. - -**Creating the signature** - -Alice does not need to interact with the TomoChain network to sign the transaction, the process is completely offline. In this tutorial, we will sign messages in the browser using [web3.js](https://github.com/ethereum/web3.js) and [MetaMask](https://metamask.io/), using the method described in [EIP-762](https://github.com/ethereum/EIPs/pull/712), as it provides a number of other security benefits. - -``` -/// Hashing first makes things easier -var hash = web3.utils.sha3("message to sign"); -web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); }); -``` - -Note - -The `web3.eth.personal.sign` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same. - -**What to Sign** - -For a contract that fulfils payments, the signed message must include: - -> 1. The recipient’s address. -> 2. The amount to be transferred. -> 3. Protection against replay attacks. - -A replay attack is when a signed message is reused to claim authorization for a second action. To avoid replay attacks we use the same as in TomoChain transactions themselves, a so-called nonce, which is the number of transactions sent by an account. The smart contract checks if a nonce is used multiple times. - -Another type of replay attack can occur when the owner deploys a `ReceiverPays` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the `RecipientPays` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again. - -Alice can protect against this attack by including the contract’s address in the message, and only messages containing the contract’s address itself will be accepted. You can find an example of this in the first two lines of the `claimPayment()` function of the full contract at the end of this section. - -**Packing arguments** - -Now that we have identified what information to include in the signed message, we are ready to put the message together, hash it, and sign it. For simplicity, we concatenate the data. The [ethereumjs-abi](https://github.com/ethereumjs/ethereumjs-abi) library provides a function called `soliditySHA3` that mimics the behaviour of Solidity’s `keccak256` function applied to arguments encoded using `abi.encodePacked`. Here is a JavaScript function that creates the proper signature for the `ReceiverPays` example: - -``` -// recipient is the address that should be paid. -// amount, in wei, specifies how much TOMO should be sent. -// nonce can be any unique number to prevent replay attacks -// contractAddress is used to prevent cross-contract replay attacks -function signPayment(recipient, amount, nonce, contractAddress, callback) { - var hash = "0x" + abi.soliditySHA3( - ["address", "uint256", "uint256", "address"], - [recipient, amount, nonce, contractAddress] - ).toString("hex"); - - web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback); -} -``` - -**Recovering the Message Signer in Solidity** - -In general, ECDSA signatures consist of two parameters, `r` and `s`. Signatures in TomoChain include a third parameter called `v`, that you can use to verify which account’s private key was used to sign the message, and the transaction’s sender. Solidity provides a built-in function [ecrecover](https://solidity.readthedocs.io/en/v0.6.3/mathematical-and-cryptographic-functions) that accepts a message along with the `r`, `s` and `v` parameters and returns the address that was used to sign the message. - -**Extracting the Signature Parameters** - -Signatures produced by web3.js are the concatenation of `r`, `s` and `v`, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into its constituent parts is a mess, so we use [inline assembly](https://solidity.readthedocs.io/en/v0.6.3/assembly) to do the job in the `splitSignature` function (the third function in the full contract at the end of this section). - -**Computing the Message Hash** - -The smart contract needs to know exactly what parameters were signed, and so it must recreate the message from the parameters and use that for signature verification. The functions `prefixed` and `recoverSigner` do this in the `claimPayment` function. - -**The full contract** - -``` -pragma solidity >=0.4.24 <=0.5.0; - -contract ReceiverPays { - address owner = msg.sender; - - mapping(uint256 => bool) usedNonces; - - constructor() public payable {} - - function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { - require(!usedNonces[nonce]); - usedNonces[nonce] = true; - - // this recreates the message that was signed on the client - bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); - - require(recoverSigner(message, signature) == owner); - - msg.sender.transfer(amount); - } - - /// destroy the contract and reclaim the leftover funds. - function shutdown() public { - require(msg.sender == owner); - selfdestruct(msg.sender); - } - - /// signature methods. - function splitSignature(bytes memory sig) - internal - pure - returns (uint8 v, bytes32 r, bytes32 s) - { - require(sig.length == 65); - - assembly { - // first 32 bytes, after the length prefix. - r := mload(add(sig, 32)) - // second 32 bytes. - s := mload(add(sig, 64)) - // final byte (first byte of the next 32 bytes). - v := byte(0, mload(add(sig, 96))) - } - - return (v, r, s); - } - - function recoverSigner(bytes32 message, bytes memory sig) - internal - pure - returns (address) - { - (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - - return ecrecover(message, v, r, s); - } - - /// builds a prefixed hash to mimic the behavior of eth_sign. - function prefixed(bytes32 hash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } -} -``` - -#### Writing a Simple Payment Channel - -Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of TOMO securely, instantaneously, and without transaction fees. - -**What is a Payment Channel?** - -Payment channels allow participants to make repeated transfers of TOMO without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps: - -> 1. Alice funds a smart contract with TOMO. This “opens” the payment channel. -> 2. Alice signs messages that specify how much of that TOMO is owed to the recipient. This step is repeated for each payment. -> 3. Bob “closes” the payment channel, withdrawing their portion of the TOMO and sending the remainder back to the sender. - -Note - -Only steps 1 and 3 require TomoChain transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers. - -Bob is guaranteed to receive their funds because the smart contract escrows the TOMO and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, the payment channel may be kept open for a limited duration. On the other hand, for a recurring payment, such as paying an employee an hourly wage, the payment channel may be kept open for several months or years. - -**Opening the Payment Channel** - -To open the payment channel, Alice deploys the smart contract, attaching the TOMO to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function `SimplePaymentChannel` in the contract, at the end of this section. - -**Making Payments** - -Alice makes payments by sending signed messages to Bob. This step is performed entirely outside of the TomoChain network. Messages are cryptographically signed by the sender and then transmitted directly to the recipient. - -Each message includes the following information: - -> * The smart contract’s address, used to prevent cross-contract replay attacks. -> * The total amount of TOMO that is owed the recipient so far. - -A payment channel is closed just once, at the end of a series of transfers. Because of this, only one of the messages sent is redeemed. This is why each message specifies a cumulative total amount of TOMO owed, rather than the amount of the individual micropayment. The recipient will naturally choose to redeem the most recent message because that is the one with the highest total. The nonce per-message is not needed anymore, because the smart contract only honours a single message. The address of the smart contract is still used to prevent a message intended for one payment channel from being used for a different channel. - -Here is the modified JavaScript code to cryptographically sign a message from the previous section: - -``` -function constructPaymentMessage(contractAddress, amount) { - return abi.soliditySHA3( - ["address", "uint256"], - [contractAddress, amount] - ); -} - -function signMessage(message, callback) { - web3.eth.personal.sign( - "0x" + message.toString("hex"), - web3.eth.defaultAccount, - callback - ); -} - -// contractAddress is used to prevent cross-contract replay attacks. -// amount, in wei, specifies how much TOMO should be sent. - -function signPayment(contractAddress, amount, callback) { - var message = constructPaymentMessage(contractAddress, amount); - signMessage(message, callback); -} -``` - -**Closing the Payment Channel** - -When Bob is ready to receive their funds, it is time to close the payment channel by calling a `close` function on the smart contract. Closing the channel pays the recipient the TOMO they are owed and destroys the contract, sending any remaining TOMO back to Alice. To close the channel, Bob needs to provide a message signed by Alice. - -The smart contract must verify that the message contains a valid signature from the sender. The process for doing this verification is the same as the process the recipient uses. The Solidity functions `isValidSignature` and `recoverSigner` work just like their JavaScript counterparts in the previous section, with the latter function borrowed from the `ReceiverPays` contract. - -Only the payment channel recipient can call the `close` function, who naturally passes the most recent payment message because that message carries the highest total owed. If the sender were allowed to call this function, they could provide a message with a lower amount and cheat the recipient out of what they are owed. - -The function verifies the signed message matches the given parameters. If everything checks out, the recipient is sent their portion of the TOMO, and the sender is sent the rest via a `selfdestruct`. You can see the `close` function in the full contract. - -**Channel Expiration** - -Bob can close the payment channel at any time, but if they fail to do so, Alice needs a way to recover their escrowed funds. An _expiration_ time was set at the time of contract deployment. Once that time is reached, Alice can call `claimTimeout` to recover their funds. You can see the `claimTimeout` function in the full contract. - -After this function is called, Bob can no longer receive any TOMO, so it is important that Bob closes the channel before the expiration is reached. - -**The full contract** - -``` -pragma solidity >=0.4.24 <=0.5.0; - -contract SimplePaymentChannel { - address payable public sender; // The account sending payments. - address payable public recipient; // The account receiving the payments. - uint256 public expiration; // Timeout in case the recipient never closes. - - constructor (address payable _recipient, uint256 duration) - public - payable - { - sender = msg.sender; - recipient = _recipient; - expiration = now + duration; - } - - /// the recipient can close the channel at any time by presenting a - /// signed amount from the sender. the recipient will be sent that amount, - /// and the remainder will go back to the sender - function close(uint256 amount, bytes memory signature) public { - require(msg.sender == recipient); - require(isValidSignature(amount, signature)); - - recipient.transfer(amount); - selfdestruct(sender); - } - - /// the sender can extend the expiration at any time - function extend(uint256 newExpiration) public { - require(msg.sender == sender); - require(newExpiration > expiration); - - expiration = newExpiration; - } - - /// if the timeout is reached without the recipient closing the channel, - /// then the TOMO is released back to the sender. - function claimTimeout() public { - require(now >= expiration); - selfdestruct(sender); - } - - function isValidSignature(uint256 amount, bytes memory signature) - internal - view - returns (bool) - { - bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); - - // check that the signature is from the payment sender - return recoverSigner(message, signature) == sender; - } - - /// All functions below this are just taken from the chapter - /// 'creating and verifying signatures' chapter. - - function splitSignature(bytes memory sig) - internal - pure - returns (uint8 v, bytes32 r, bytes32 s) - { - require(sig.length == 65); - - assembly { - // first 32 bytes, after the length prefix - r := mload(add(sig, 32)) - // second 32 bytes - s := mload(add(sig, 64)) - // final byte (first byte of the next 32 bytes) - v := byte(0, mload(add(sig, 96))) - } - - return (v, r, s); - } - - function recoverSigner(bytes32 message, bytes memory sig) - internal - pure - returns (address) - { - (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - - return ecrecover(message, v, r, s); - } - - /// builds a prefixed hash to mimic the behavior of eth_sign. - function prefixed(bytes32 hash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } -} -``` - -Note - -The function `splitSignature` does not use all security checks. A real implementation should use a more rigorously tested library, such as openzepplin’s [version](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol) of this code. - -**Verifying Payments** - -Unlike in the previous section, messages in a payment channel aren’t redeemed right away. The recipient keeps track of the latest message and redeems it when it’s time to close the payment channel. This means it’s critical that the recipient perform their own verification of each message. Otherwise there is no guarantee that the recipient will be able to get paid in the end. - -The recipient should verify each message using the following process: - -> 1. Verify that the contact address in the message matches the payment channel. -> 2. Verify that the new total is the expected amount. -> 3. Verify that the new total does not exceed the amount of TOMO escrowed. -> 4. Verify that the signature is valid and comes from the payment channel sender. - -We’ll use the [ethereumjs-util](https://github.com/ethereumjs/ethereumjs-util) library to write this verification. The final step can be done a number of ways, and we use JavaScript. The following code borrows the constructMessage function from the signing **JavaScript code** above: - -``` -// this mimics the prefixing behavior of the eth_sign JSON-RPC method. -function prefixed(hash) { - return ethereumjs.ABI.soliditySHA3( - ["string", "bytes32"], - ["\x19Ethereum Signed Message:\n32", hash] - ); -} - -function recoverSigner(message, signature) { - var split = ethereumjs.Util.fromRpcSig(signature); - var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s); - var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex"); - return signer; -} - -function isValidSignature(contractAddress, amount, signature, expectedSigner) { - var message = prefixed(constructPaymentMessage(contractAddress, amount)); - var signer = recoverSigner(message, signature); - return signer.toLowerCase() == - ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase(); -} -``` - -### Modular Contracts - -A modular approach to building your contracts helps you reduce the complexity and improve the readability which will help to identify bugs and vulnerabilities during development and code review. If you specify and control the behaviour or each module in isolation, the interactions you have to consider are only those between the module specifications and not every other moving part of the contract. In the example below, the contract uses the `move` method of the `Balances` [library](https://solidity.readthedocs.io/en/v0.6.3/contracts.html#libraries) to check that balances sent between addresses match what you expect. In this way, the `Balances` library provides an isolated component that properly tracks balances of accounts. It is easy to verify that the `Balances` library never produces negative balances or overflows and the sum of all balances is an invariant across the lifetime of the contract. - -``` -pragma solidity >=0.4.22 <=0.5.0; - -library Balances { - function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { - require(balances[from] >= amount); - require(balances[to] + amount >= balances[to]); - balances[from] -= amount; - balances[to] += amount; - } -} - -contract Token { - mapping(address => uint256) balances; - using Balances for *; - mapping(address => mapping (address => uint256)) allowed; - - event Transfer(address from, address to, uint amount); - event Approval(address owner, address spender, uint amount); - - function transfer(address to, uint amount) public returns (bool success) { - balances.move(msg.sender, to, amount); - emit Transfer(msg.sender, to, amount); - return true; - - } - - function transferFrom(address from, address to, uint amount) public returns (bool success) { - require(allowed[from][msg.sender] >= amount); - allowed[from][msg.sender] -= amount; - balances.move(from, to, amount); - emit Transfer(from, to, amount); - return true; - } - - function approve(address spender, uint tokens) public returns (bool success) { - require(allowed[msg.sender][spender] == 0, ""); - allowed[msg.sender][spender] = tokens; - emit Approval(msg.sender, spender, tokens); - return true; - } - - function balanceOf(address tokenOwner) public view returns (uint balance) { - return balances[tokenOwner]; - } -} -``` - -[Next ](https://solidity.readthedocs.io/en/v0.6.3/solidity-in-depth.html)[ Previous](https://solidity.readthedocs.io/en/v0.6.3/installing-solidity.html)\ diff --git a/developer-guide/smart-contract-development/ides-and-tools/thirdweb-cli.md b/developer-guide/smart-contract-development/thirdweb-cli.md similarity index 99% rename from developer-guide/smart-contract-development/ides-and-tools/thirdweb-cli.md rename to developer-guide/smart-contract-development/thirdweb-cli.md index fe8b964..6f7d3f0 100644 --- a/developer-guide/smart-contract-development/ides-and-tools/thirdweb-cli.md +++ b/developer-guide/smart-contract-development/thirdweb-cli.md @@ -5,4 +5,3 @@ thirdweb provides an interactive command line interface, allowing you to create, You can use the thirdweb CLI to create and deploy smart contracts to the Tomochain EVM network. Visit [https://thirdweb.com/tomochain](https://thirdweb.com/tomochain) for more details. - diff --git a/developer-guide/smart-contract-development/ides-and-tools/web3js.md b/developer-guide/smart-contract-development/web3js.md similarity index 99% rename from developer-guide/smart-contract-development/ides-and-tools/web3js.md rename to developer-guide/smart-contract-development/web3js.md index c4ac269..6955510 100644 --- a/developer-guide/smart-contract-development/ides-and-tools/web3js.md +++ b/developer-guide/smart-contract-development/web3js.md @@ -93,7 +93,7 @@ web3.eth.getBalance(address, (err, wei) => { First, we use check the balance by calling `web3.eth.getBalance()`, which accepts a callback function with two arguments, an error and the balance itself. We'll ignore the error argument for now, and reference the balance with the `wei` argument. Ethereum expresses balances in Wei, which is the smallest subdivision of Ether, kind of like a tiny penny. We can convert this balance to Ether with `web3.utils.fromWei(wei, 'ether')`. -Now you've seen what the Web3.js library is and you can get started using it to check Ethereum account balances. Here is a summary of the code +Now you've seen what the Web3.js library is and you can get started using it to check Ethereum account balances. Here is a summary of the code ``` const Web3 = require('web3') @@ -130,7 +130,7 @@ A smart contract ABI stands for "Abstract Binary Interface", and is a JSON array const abi = [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_releaseTime","type":"uint256"}],"name":"mintTimelocked","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] ``` -This example is the ABI for the OmiseGo token, which implements the ERC-20 token standard. You can find more details about this token, including its abi and address on [Etherscan](https://etherscan.io/address/0xd26114cd6EE289AccF82350c8d8487fedB8A0C07). +This example is the ABI for the OmiseGo token, which implements the ERC-20 token standard. You can find more details about this token, including its abi and address on [Etherscan](https://etherscan.io/address/0xd26114cd6EE289AccF82350c8d8487fedB8A0C07). Go ahead and store the address to the OMG token from the Ethereum main net: diff --git a/developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md b/developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md index 3a360af..7c3e399 100644 --- a/developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md +++ b/developer-guide/working-with-tomochain/tomochain-private-testnet-setup.md @@ -9,194 +9,234 @@ description: >- The following will walk you step-by-step to setup a TomoChain private net with three Masternodes. -### Install Golang +## Install + +### Golang * Reference: [https://go.dev/doc/install](https://go.dev/doc/install) -* Set environment variables +* Install Golang: + +{% hint style="info" %} +MacOS - M series: amd64 +{% endhint %} +```bash +curl -o golang.pkg https://dl.google.com/go/go1.20.4.darwin-amd64.pkg +sudo open golang.pkg ``` + +* Set environment variables (according to computer) + +```bash set GOROOT=$HOME/usr/local/go set GOPATH=$HOME/go ``` -### Prepare TomoChain Client Software +### Tomo -* `cd $GOPATH/src/github.com/ethereum/go-ethereum` -* Download source code and build +* Create Tomo folder: -``` -git init -git remote add git@github.com:tomochain/tomochain.git -git pull origin master -make all +```bash +mkdir tomo +cd tomo ``` -* Create shortcuts/alias for easier access +* Download source code Tomo and install library: -``` -alias tomo=$GOPATH/src/github.com/ethereum/go-ethereum/build/bin/tomo -alias bootnode=$GOPATH/src/github.com/ethereum/go-ethereum/build/bin/bootnode -alias puppeth=$GOPATH/src/github.com/ethereum/go-ethereum/build/bin/puppeth +```bash +git clone https://github.com/tomochain/tomochain/ +cd tomochain +go mod tidy -e +make all +cd .. ``` -### Setup Chain Fata Folders `datadir` and Corresponding `keystore` Folders for 3 Masternodes +* Alias for Tomo: -``` -mkdir $HOME/tomochain -mkdir $HOME/tomochain/nodes -mkdir $HOME/tomochain/nodes/1 $HOME/tomochain/nodes/2 $HOME/tomochain/nodes/3 -mkdir $HOME/tomochain/keystore/1 $HOME/tomochain/keystore/2 $HOME/tomochain/keystore/3 +```bash +alias tomo=$PWD/tomochain/build/bin/tomo +alias puppeth=$PWD/tomochain/build/bin/puppeth +alias bootnode=$PWD/tomochain/build/bin/bootnode ``` -### Initialize / Import Accounts For the Masternodes's Keystore +## Setup Node and Account -* Initialize new accounts: If there are existing accounts and a preference to import them, please ignore this step and go to `Import Accounts` +* Create nodes folder: -``` -tomo account new \ - --password [YOUR_PASSWORD_FILE_TO_LOCK_YOUR_ACCOUNT] \ - --keystore $HOME/tomochain/keystore/1 +```bash +mkdir nodes +cd nodes +mkdir 1 +cd .. ``` -``` -tomo account new \ - --password [YOUR_PASSWORD_FILE_TO_LOCK_YOUR_ACCOUNT] \ - --keystore $HOME/tomochain/keystore/2 -``` +* Create account or import account (with your private key): + * Create a Keystore directory: `mkdir keystore` + * Create a Password directory: `mkdir $HOME/pw` + * Create a New account: -``` -tomo account new \ - --password [YOUR_PASSWORD_FILE_TO_LOCK_YOUR_ACCOUNT] \ - --keystore $HOME/tomochain/keystore/3 -``` + * Create a new password file (example): -* Import accounts + ```bash + touch $PASSWORD_DIRECTORY/pw.txt + echo [YOUR_PASSWORD] >> $PASSWORD_DIRECTORY/pw.txt + ``` -``` -tomo account import [PRIVATE_KEY_FILE_OF_YOUR_ACCOUNT] \ - --keystore $HOME/tomochain/keystore/1 \ - --password [YOUR_PASSWORD_FILE_TO_LOCK_YOUR_ACCOUNT] -``` + * Create a new keystore file: -Repeat this step to import two more private keys for our three masternodes. + ```bash + tomo account new \ + --password $PASSWORD_DIRECTORY/pw.txt \ + --keystore $PWD/keystore/1 + ``` +* Import account: + * Create a Private key directory: `mkdir $HOME/pk` + * Export environment: `export PRIVATE_KEY_DIRECTORY=[DIRECTORY TO STORE PRIVATE KEY]` + * Create a Private key file (example): -### Customize Genesis Block by Using the `puppeth` Tool + ```bash + touch $PRIVATE_KEY_DIRECTORY/pk.txt + echo [YOUR_PRIVATE_KEY] >> $PRIVATE_KEY_DIRECTORY/pk.txt + ``` + * Create a Tomo account with the private key file: -* Run puppeth command and answer questions about your private chain as follows: +
tomo account import $PRIVATE_KEY_DIRECTORY/pk.txt \
+          --keystore $PWD/keystore/1 \
+          --password $PRIVATE_KEY_DIRECTORY/pk1txt
+      
+* Do the same things with Node 2, 3, 4 -``` -puppeth -``` +## Customize Genesis Block by Using the `puppeth` Tool -* Set chain name +* Run puppeth command and answer questions about your private chain as follows: +```bash +puppeth ``` - > localtomo -``` - -![Private chain name](https://user-images.githubusercontent.com/17243442/57121919-bcbbd000-6da4-11e9-8a0e-dea3a15f3fc1.png) - -* Enter 2 to configure new genesis -* Enter 3 to select `POSV` consensus -* Set blocktime (default 2 seconds) -* Set reward of each epoch -* Set addresses to be initial masternodes -* Set number of blocks of each epoch (default 900). If you would like to customize epoch number, please update code here `common/constants.go:14` `EpocBlockRandomize = 900` -* Set gap (How many blocks before checkpoint need prepare new masternodes set ?)`suggestedGap = 5` -* Enter foundation address which you hold private key - -![POSV configurations](https://user-images.githubusercontent.com/17243442/57122012-2f2cb000-6da5-11e9-8b1e-7fc1c034226a.png) - -* Enter accounts which you control private keys to unlock MultiSig wallet - -![MultiSig wallet setting](https://user-images.githubusercontent.com/17243442/57122031-453a7080-6da5-11e9-92d6-49fba3a4c1ea.png) +* Set chain name: `Tomo` +* Configure new genesis: `2` +* Select `POSV` consensus: `3` +* Set block time (default 2 seconds): `Enter` +* Set reward of each epoch: `250` +* Set addresses to be first masternodes: Account address of Node 1 +* Set account to seal: Account address of Node 1, Node 2, Node 3, Node 4 +* Set the number of blocks of each epoch (default 900): `Enter` +* Set gap (How many blocks before checkpoint need to prepare new masternodes set ?): `5` +* Set foundation wallet address: `Enter` +* Account confirm Foundation MultiSignWallet: Account address created before +* Require for confirm tx in Foundation MultiSignWallet: `1` +* Account confirm Team MultiSignWallet: Account address created before +* Require for confirm tx in Team MultiSignWallet: `1` * Enter swap wallet address for fund 55 million TOMO - -![Initial funds](https://user-images.githubusercontent.com/17243442/57122062-7024c480-6da5-11e9-98f1-4ce90b2941d6.png) - -* Export genesis file - Select `2. Manage existing genesis` - Select `2. Export genesis configuration` - Enter genesis filename - -![Export genesis file](https://user-images.githubusercontent.com/17243442/57122075-82066780-6da5-11e9-89b2-e0369ec528f5.png) - +* Enter account be pre-funded +* Enter Network ID +* Export genesis file - + * Select `2. Manage existing genesis` + * Select `2. Export genesis configuration` + * Enter genesis filename (example): `genesis.json` * `Control + C` to exit -### Initialize Your Private Chain with Above Genesis Block +## Initialize Your Private Chain with Above Genesis Block ``` -tomo --datadir $HOME/tomochain/nodes/1 init [PATH/TO/GENESIS_FILE] -tomo --datadir $HOME/tomochain/nodes/2 init [PATH/TO/GENESIS_FILE] -tomo --datadir $HOME/tomochain/nodes/3 init [PATH/TO/GENESIS_FILE] +tomo --datadir $HOME/tomochain/nodes/1 init genesis.json +tomo --datadir $HOME/tomochain/nodes/2 init genesis.json +tomo --datadir $HOME/tomochain/nodes/3 init genesis.json ``` -### Setup Bootnode +## Setup Bootnode * Initialize bootnode key -``` +```bash bootnode -genkey bootnode.key ``` * Start bootnode and copy bootnode information -``` +```bash bootnode -nodekey ./bootnode.key ``` -`enode://7e59324b1e54f8c282719465eb96786fb3a04a0265deee2cdb0f62e912337ca` +![Example Bootnode](<../../.gitbook/assets/image (26).png>) + +* Set BOOTNODE\_INFO environment: `"enode://372853cfc9cc509bdd79db961cf791e8b2c8fdbadd5b4a25b0e59187f3be9a6e1d26e381f8ed4ae71d81c72ad7f53430af605955293df66660232ad235633880@[::]:30301"` -`6f118d0c91f7ebfae6f5c17825205279249cf7ff65ae54d0a1a8908ef16f80f63@[::]:30301` +## Run node -![](<../../.gitbook/assets/image (26).png>) +{% hint style="info" %} +In a different terminal with bootnode +{% endhint %} + +* Set Password directory environment: + + `export PASSWORD_DIRECTORY=[DIRECTORY TO STORE PASSWORD OF KEYSTORE FILE]` +* Set `tomo` command line: + + `alias tomo=$PWD/tomochain/build/bin/tomo` +* Example address: `YOUR_ACCOUNT_ADDRESS=0x79d3620f9379d043eaea262f1cac689fc906d5a1` ### Start Masternodes * Start Masternode 1 ``` -tomo --syncmode "full" \ - --datadir $HOME/tomochain/nodes/1 --networkid [YOUR_NETWORK_ID] --port 10303 \ - --keystore $HOME/tomochain/keystore/1 --password [YOUR_PASSWORD_FILE_TO_UNLOCK_YOUR_ACCOUNT] \ - --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 1545 --rpcvhosts "*" \ - --rpcapi "db,eth,net,web3,personal,debug" \ - --gcmode "archive" \ - --ws --wsaddr 0.0.0.0 --wsport 1546 --wsorigins "*" --unlock "[ADDRESS_MASTERNODE_1]" \ - --identity "NODE1" \ - --mine --gasprice 2500 \ - --bootnodes [YOUR_BOOTNODE_INFORMATION] \ - console +tomo --syncmode "full" \ +--datadir nodes/1 --networkid 3172 --port 10303 \ +--keystore keystore/1 --password $PASSWORD_DIRECTORY/pw.txt \ +--rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 1545 --rpcvhosts "*" \ +--rpcapi "admin,db,eth,net,web3,personal,debug" \ +--gcmode "archive" \ +--ws --wsaddr 0.0.0.0 --wsport 1546 --wsorigins "*" --unlock [YOUR_ACCOUNT_ADDRESS] \ +--identity "NODE1" \ +--mine --gasprice 2500 \ --bootnodesv5 [BOOTNODE_INFO] \ +console ``` * Start Masternode 2 ``` -tomo --syncmode "full" \ - --datadir $HOME/tomochain/nodes/2 --networkid [YOUR_NETWORK_ID] --port 20303 \ - --keystore $HOME/tomochain/keystore/2 --password [YOUR_PASSWORD_FILE_TO_UNLOCK_YOUR_ACCOUNT] \ - --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 2545 --rpcvhosts "*" \ - --rpcapi "db,eth,net,web3,personal,debug" \ - --gcmode "archive" \ - --ws --wsaddr 0.0.0.0 --wsport 2546 --wsorigins "*" --unlock "[ADDRESS_MASTERNODE_2]" \ - --identity "NODE2" \ - --mine --gasprice 2500 \ - --bootnodes [YOUR_BOOTNODE_INFORMATION] \ - console +tomo --syncmode "full" \ +--datadir nodes/1 --networkid 3172 --port 20303 \ +--keystore keystore/1 --password $PASSWORD_DIRECTORY/pw.txt \ +--rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 2545 --rpcvhosts "*" \ +--rpcapi "admin,db,eth,net,web3,personal,debug" \ +--gcmode "archive" \ +--ws --wsaddr 0.0.0.0 --wsport 2546 --wsorigins "*" --unlock [YOUR_ACCOUNT_ADDRESS] \ +--identity "NODE1" \ +--mine --gasprice 2500 \ --bootnodesv5 [BOOTNODE_INFO] \ +console ``` * Start Masternode 3 ``` -tomo --syncmode "full" \ - --datadir $HOME/tomochain/nodes/3 --networkid [YOUR_NETWORK_ID] --port 30303 \ - --keystore $HOME/tomochain/keystore/3 --password [YOUR_PASSWORD_FILE_TO_UNLOCK_YOUR_ACCOUNT] \ - --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 3545 --rpcvhosts "*" \ - --rpcapi "db,eth,net,web3,personal,debug" \ - --gcmode "archive" \ - --ws --wsaddr 0.0.0.0 --wsport 3546 --wsorigins "*" --unlock "[ADDRESS_MASTERNODE_3]" \ - --identity "NODE3" \ - --mine --gasprice 2500 \ - --bootnodes [YOUR_BOOTNODE_INFORMATION] \ - console +tomo --syncmode "full" \ +--datadir nodes/1 --networkid 3172 --port 30303 \ +--keystore keystore/1 --password $PASSWORD_DIRECTORY/pw.txt \ +--rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 3545 --rpcvhosts "*" \ +--rpcapi "admin,db,eth,net,web3,personal,debug" \ +--gcmode "archive" \ +--ws --wsaddr 0.0.0.0 --wsport 3546 --wsorigins "*" --unlock [YOUR_ACCOUNT_ADDRESS] \ +--identity "NODE1" \ +--mine --gasprice 2500 \ --bootnodesv5 [BOOTNODE_INFO] \ +console +``` + +* Start Masternode 4 + +``` +tomo --syncmode "full" \ +--datadir nodes/1 --networkid 3172 --port 40303 \ +--keystore keystore/1 --password $PASSWORD_DIRECTORY/pw.txt \ +--rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 --rpcport 4545 --rpcvhosts "*" \ +--rpcapi "admin,db,eth,net,web3,personal,debug" \ +--gcmode "archive" \ +--ws --wsaddr 0.0.0.0 --wsport 4546 --wsorigins "*" --unlock [YOUR_ACCOUNT_ADDRESS] \ +--identity "NODE1" \ +--mine --gasprice 2500 \ --bootnodesv5 [BOOTNODE_INFO] \ +console ``` * Some explanations on the flags @@ -225,15 +265,59 @@ To see all flags usage tomo --help ``` -### Check Your Private Chain +### Connect Masternode to chain -* Connect ipc +* Open new terminal +* Connect to Masternode 1: + +```bash +tomo attach nodes/1/tomo.ipc +``` + +* Get Node 1 info: + +```bash +admin.nodeInfo +``` + +

Node 1 Info

+ +* We get enode id of masternode 1 +* Example: `enode://89a5e91d30461641c4ede519c065b2f1e6d23a36d45e2d953e0ec9fa75e516517a0d4964489e07b73a7fa48e67d42fb2498ba46fe1a814c42ec0ad3257ced47a@[::]:10303` +* Open new terminal +* Connect to Masternode 2 + +```bash +tomo attach nodes/2/tomo.ipc +``` + +* Add node 1 peer to node 2 +```bash +admin.addPeer("enode://89a5e91d30461641c4ede519c065b2f1e6d23a36d45e2d953e0ec9fa75e516517a0d4964489e07b73a7fa48e67d42fb2498ba46fe1a814c42ec0ad3257ced47a@[::]:10303") ``` -tomo attach $HOME/tomochain/nodes/1/tomo.ipc + +
+ +* Checking adding peer sucessful: + +```bash +admin.peers ``` +

Success adding

+ +* Do same things with node 3, 4 + +## Check Your Private Chain + +* Connect ipc + +```bash +tomo attach nodes/1/tomo.ipc ``` + +```bash admin.nodeInfo eth.getBlock(0) eth.getBlock(1) @@ -241,11 +325,11 @@ eth.getBlock(1) * Connect rpc -``` +```bash tomo attach tomo attach http://localhost:1545 ``` -``` +```bash eth.getBlock(0) eth.getBlock(1) ``` @@ -256,27 +340,25 @@ eth.getBlock(1) Wait about 30 minutes to see if your chain passes the first checkpoint ``` - - ![](<../../.gitbook/assets/image (69).png>) -``` +```bash tomo attach http://0.0.0.0:1545 ``` -``` +```bash eth.getBlock(900) ``` -### Troubleshooting +## Troubleshooting * Reset your chain -``` -rm -rf $HOME/tomochain/nodes/1/tomo $HOME/tomochain/nodes/2/tomo $HOME/tomochain/nodes/3/tomo -tomo --datadir $HOME/tomochain/nodes/1 init genesis.json -tomo --datadir $HOME/tomochain/nodes/2 init genesis.json -tomo --datadir $HOME/tomochain/nodes/3 init genesis.json +```bash +rm -rf nodes/1/tomo nodes/2/tomo nodes/3/tomo +tomo --datadir nodes/1 init genesis.json +tomo --datadir nodes/2 init genesis.json +tomo --datadir nodes/3 init genesis.json ``` Note: we use the Gnosis Multisig Wallet: https://github.com/gnosis/MultiSigWallet