From 0bbece0ad8361e1419f7abd310b16bed6fa8d096 Mon Sep 17 00:00:00 2001 From: Nathan James Date: Wed, 27 Mar 2024 07:10:20 -0700 Subject: [PATCH] Testing and local development (#237) * added docs for fuckyea and vert * remove tabs import * fix hyphen --------- Co-authored-by: nsjames --- native/01_quick-start/02_write-a-contract.md | 23 +- native/01_quick-start/06_local-development.md | 222 ++++++++++++++++++ .../{04_endpoints.md => 10_endpoints.md} | 0 native/03_smart-contracts/50_testing.md | 183 +++++++++++++++ 4 files changed, 417 insertions(+), 11 deletions(-) create mode 100644 native/01_quick-start/06_local-development.md rename native/01_quick-start/{04_endpoints.md => 10_endpoints.md} (100%) create mode 100644 native/03_smart-contracts/50_testing.md diff --git a/native/01_quick-start/02_write-a-contract.md b/native/01_quick-start/02_write-a-contract.md index 6968b4d7..daba03ee 100644 --- a/native/01_quick-start/02_write-a-contract.md +++ b/native/01_quick-start/02_write-a-contract.md @@ -1,23 +1,16 @@ --- -title: Write a Smart Contract +title: Quickstart --- In this guide we're going to create a simple smart contract that will allow us to store a string in the blockchain. -We'll be using a **[Web IDE](https://ide.eosnetwork.com/)** to write, and deploy our smart contract to the EOS Testnet. +This will teach you some of the basics of smart contract development on the EOS network. -## What is a Smart Contract? +## Create your first Smart Contract -You can think of a Smart Contract like a function that runs on the blockchain. It must be **deterministic**, meaning +You can think of a Smart Contract like a function that runs on the blockchain. It must be **deterministic**, meaning that it will always produce the same output given the same input. This is required so that all nodes on the network can agree on the output of the function. -## What is a Web IDE? - -A Web IDE is an Integrated Development Environment that runs in your browser. It allows you to write, compile, and -deploy your smart contract to the blockchain, without ever leaving your browser or installing any software. - -## Enough talk, let's begin! - Open up the [EOS Web IDE](https://ide.eosnetwork.com/) in your browser. You will be presented with a dummy contract which shows you the basic structure of a smart contract. @@ -52,9 +45,17 @@ CONTRACT mycontract : public contract { Take a look at the code and see if you can figure out what it's doing. +Here's the basic gist of it: +- You created a new contract called `mycontract` +- A table model called `StoredData` +- A table called `mytable` to store your `StoredData` records +- An action called `save` that will allow you to save a string to the table + If you're having trouble understanding the code, don't worry, you can head over to the [Smart Contract Basics](/docs/03_smart-contracts/01_contract-anatomy.md) section to learn more about the various parts of a smart contract and how they work. Your screen should look like this now: ![EOS Web IDE](/images/native-web-ide-basics.png) + +Head over to the next section to see how we can deploy this to a testnet with a few clicks. diff --git a/native/01_quick-start/06_local-development.md b/native/01_quick-start/06_local-development.md new file mode 100644 index 00000000..c1ce3e42 --- /dev/null +++ b/native/01_quick-start/06_local-development.md @@ -0,0 +1,222 @@ +--- +title: Local Development +--- + +Developing using the [Web IDE](https://ide.eosnetwork.com) will only take you so far. +Eventually, you will want to develop locally on your machine, so that you can easily use version control, your +favorite editor, and other tools that you are used to. + +This guide will walk you through setting up your local development environment using [FuckYea](https://github.com/nsjames/fuckyea). + +## Creating a new project + +You don't need to install the `fuckyea` CLI locally, you can use `npx` for all commands. + +```bash +fuckyea create [optional_directory] +``` + +This will create a project structure that looks like this: + +```bash +📂 contracts + 📄 contract.cpp +📂 deployments + 📄 jungle.ts +📂 tests + 📄 contract.spec.ts +🔐 .env +📄 .gitignore +📄 fuckyea.config.js +📄 package.json +``` + +## Developing contracts + +The `contract.cpp` file inside of `contracts` already has a simple contract that you can use to get started. + +### Creating new contracts + +You can either manually create a contract, copy an existing one, or use the scaffold CLI. + +```bash +npx fuckyea scaffold contract [optional_directory] +``` + +### Building contracts + +In order to test or deploy your contracts, you will need the `.wasm` and `.abi` files. To get them from your C++ files, you can +use the CLI build command from your directory root. + +```bash +npx fuckyea build +``` + +All build files will be saved to the `build/` directory. + +```bash +📂 build + 📄 contract.abi + 📄 contract.wasm +📂 contracts + 📄 contract.cpp +``` + +### Testing contracts + +Testing using FuckYea uses VeRT, an emulator for EOS. You can head over to the [testing guide](../03_smart-contracts/50_testing.md) if you want to learn about writing tests. + +```bash +npx fuckyea test [--build] +``` + +Using the build option will simply batch both a build and test job together. It is no different than running build before test. + +You can also scaffold a new test: + +```bash +npx fuckyea scaffold test [optional_directory] +``` + + + + +## Deploying contracts + +FuckYea is able to deploy contracts to any Antelope network. The default that comes with new projects is the `Jungle4` network, a common testnet. + +### The config file + +A lot of the setup for deployments is done in the `fuckyea.config.js` file at the root of your project. + +It exports a `JSON` object that includes `network` property which defines the chain, and accounts you need to deploy contracts. + +```json +networks: { + jungle: { + // node_url: 'https://eos.greymass.com', + chain: 'Jungle4', + accounts: [ + { + name: 'youraccount', + permission: 'owner', + private_key: process.env.PRIVATE_KEY + } + ] + } +} +``` + +#### The key for the network + +The name of your deployment file in the `deployments` directory must always match the name of the key in the `networks` object. +For instance, above we have defined the `jungle` network, and we also have a `deployments/jungle.ts` file. + +If you wanted to have a Mainnet file, you would add both the `mainnet` key in `networks` and a `deployments/mainnet.ts` deployment file. + +#### Specifying a node + +You can either use the `chain` property to specify a chain, or you can use the `node_url` property to specify a specific node endpoint. + +If you want to use `chain`, you can refer to the [WharfKit Chains definition](https://github.com/wharfkit/common/blob/b9cfe061b2619e297b2ead8dbe7f543617ebb455/src/common/chains.ts#L168) for a list +of available chains. + +Two common ones are: +- `Jungle4` +- `EOS` + +#### Registering accounts + +In order for the deployment script to know what keys belong to which accounts, you need to specify them here. +The `accounts` property is an array of account definitions that include the following properties. + +| Property | Description | +| --- |----------------------------------------------------------| +| name | The name of the account | +| permission | The permission level of the account (defaults to `active`) | +| private_key | The private key of the account | + +##### Using environment variables + +The project includes a `.env` file that you can use to store your private keys. This file is ignored by git, so you can safely store your keys here. + +```bash +PRIVATE_KEY=your_private_key +``` + +**Please make sure to never commit your `.env` file to a public repository, or use private keys in plain text in the config file.** + +### Deployment files + +The deployment files are written in JavaScript and are used to deploy contracts to the network. + +They are injected with a `deployer` object that has the following properties: + +| Property | Description | +| --- |----------------------------------------------------------------------------------| +| accounts | An array of account definitions | +| sessions | An object that holds the current wharfkit session for each account | +| deploy | A function that you can use to deploy a contract and returns a wharfkit contract | + + +```javascript +module.exports = async (deployer) => { + + const contract = await deployer.deploy('someaccount', 'build/mycontract', { + // adds the `eosio.code` permission to the contract account's active permission + // so that you can send inline actions from the contract in its name + addCode: true + }).catch(err => { + console.error(err) + process.exit(1); + }) + + // do other stuff here... +} +``` + +### Creating deployments + +You can either manually create a deployment, copy an existing one, or use the scaffold CLI. + +```bash +npx fuckyea scaffold deployment [optional_directory] +``` + +### Deploying contracts + +To deploy a contract, you can use the CLI deploy command. + +```bash +npx fuckyea deploy [--build] +``` + + + + + + + + + + +## Troubleshooting + +Sometimes you run into problems. If you have anything that isn't on this list, please reach out in the [Developers Telegram](https://t.me/antelopedevs) group. + +### Multi-contract support + +If you have multiple contracts in your project, then the compiler won't know which `.cpp` file is the entry file into that specific contract. + +To fix this, you can change the suffix to `.entry.cpp` for each contract, and you will then get back named builds for each. + +```bash +📂 build + 📄 game.abi + 📄 game.wasm + 📄 token.abi + 📄 token.wasm +📂 contracts + 📄 game.entry.cpp + 📄 token.entry.cpp +``` diff --git a/native/01_quick-start/04_endpoints.md b/native/01_quick-start/10_endpoints.md similarity index 100% rename from native/01_quick-start/04_endpoints.md rename to native/01_quick-start/10_endpoints.md diff --git a/native/03_smart-contracts/50_testing.md b/native/03_smart-contracts/50_testing.md new file mode 100644 index 00000000..0710edf5 --- /dev/null +++ b/native/03_smart-contracts/50_testing.md @@ -0,0 +1,183 @@ +--- +title: Testing +--- + +The easiest way to test EOS Smart Contracts is using VeRT (VM emulation RunTime for WASM-based blockchain contracts). + +It is a JavaScript library that allows you to run EOS smart contracts in a Node.js environment. +You use it along-side other testing libraries like Mocha, Chai, and Sinon. + +This guide will use Mocha as the testing framework, and assumes you already know how mocha works, as well as JavaScript. + +## Installation + +If you're using [FuckYea](https://github.com/nsjames/fuckyea) you already have everything installed, otherwise follow +the installation steps below. + +We're going to install +- VeRT +- Mocha +- Chai + +```shell +npm install -D @eosnetwork/vert mocha chai +``` + +You should also add `"type": "module"` to your `package.json`. + +To make life easier, add a test script to your `package.json` so that we can easily run tests from mocha. (change `.js` to `.ts` if you're using TypeScript) + +```json +"scripts": { + "test": "mocha tests/**/*.spec.js" +}, +``` + +Your `package.json` will look something like this now: + +```json +{ + "name": "your-project", + "version": "1.0.0", + "type": "module", + "scripts": { + "test": "mocha tests/**/*.spec.js" + }, + "devDependencies": { + "@eosnetwork/vert": "^0.3.24", + "chai": "^4.3.10", + "mocha": "^10.2.0" + } +} +``` + +## Testing + +Create a `tests` directory and a test file that ends with `.spec.js` (or `.spec.ts` if you're using typescript). + +### Setup your test file + +Let's look at how to import our dependencies, setup the emulator and some accounts, and define a test. + +```javascript +// tests/mycontract.spec.js + +import { Blockchain, nameToBigInt, expectToThrow } from "@eosnetwork/vert"; +import { assert } from "chai"; + +// instantiate the blockchain emulator +const blockchain = new Blockchain() + +// Load a contract +const contract = blockchain.createContract( + // The account to set the contract on + 'accountname', + // The path to the contract's wasm file + 'build/yourcontract' +) + +// Create some accounts to work with +const [alice, bob] = blockchain.createAccounts('alice', 'bob') + +// You can clear the tables in the +// contract before each test +beforeEach(async () => { + blockchain.resetTables() +}) + +describe('Testing Suite', () => { + it('should do X', async () => { + + // Your test goes here... + + }); +}); +``` + +### Sending transactions + +You can send transactions to your contract like this: + +```javascript +const result = await contract.actions.youraction( + // Parameters are passed as an array, and must match the types in the contract + ['yourparams', 1] +).send( + // To send the transaction you need to pass the name and permission + // of the account that is sending it within .send() + 'alice@active' +); +``` + +### Getting table data + +You can get the data from a table in your contract like this: + +```javascript +const rows = contract.tables.yourtable( + // Set the scope of the table + nameToBigInt('accountname') +).getTableRow( + // Find the row using the primary index + nameToBigInt('alice') +); + +// Make sure the row exists or fail the test +assert(!!rows, "User not found") +``` + +### Logging console output + +If you're printing to the console in your contract, you can access the logs in your test files like this: + +```javascript +console.log(contract.bc.console); +```` + +### Catching errors + +If you want to test that your contract throws a specific error, you can use the `expectToThrow` function like this: + +```javascript +expectToThrow( + contract.actions.throwserror([]).send('bob@active'), + 'This will be the error inside check()' +) +``` + + + +## Troubleshooting + +Sometimes you run into problems. If you have anything that isn't on this list, please reach out in the [Developers Telegram](https://t.me/antelopedevs) group. + +### Seeing table deltas + +Sometimes it's helpful to see the changes in tables after a transaction. +You can enable storage deltas and then print them out to see what has changed. + +```javascript +blockchain.enableStorageDeltas() + +contract.actions.youraction([]).send(...) + +blockchain.printStorageDeltas() +blockchain.disableStorageDeltas() +``` + + +### Exported memory + +VeRT requires exported memory in your contract. + +If you are using CDT to compile your contracts, you need to export memory in your contract manually prior to version 4.1.0. + +```bash +# if you don't have wabt: +apt-get install wabt +# export memory +wasm2wat FILENAME.wasm | sed -e 's|(memory |(memory (export "memory") |' > TMP_FILE.wat +wat2wasm -o FILENAME.wasm TMP_FILE.wat +rm TMP_FILE.wat +``` +