Your First Contract

Your First Contract

mkdir jpegdegens
cd jpegdegens
git init
yarn init -y
yarn add -D hardhat
npx hardhat














Typical Structure

Before we create our first contract, lets talk about folder structure.

jpegdegens
  - contracts
    - YourContracs.sol
    ...
  - scripts
    - deploy.ts
    ...
  - test
    - sometest.js
    ...

HardHat

Its a tool for building and deploying contracts to any ethereum network.















Don't forget the .gitignore















Create first contract.

Create a file in contracts folder named HelloWorld.sol

.sol != shit out of luck

solidity, which is the programming language of ethereum.















Contract code

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

contract HelloWorld {
    function hello() public pure returns (string memory) {
        return "Hello, World";
    }
}














Compile time

npx hardhat compile














Did you get an error?

Did you get this error?

Error HH606: The project cannot be compiled, see reasons below.

The Solidity version pragma statement in these files don't match any of the configured compilers in your config. Change the pragma or configure additional compiler versions in your hardhat config.

  * contracts/HelloWorld.sol (^0.8.0)

To learn more, run the command again with --verbose

Read about compiler configuration at https://hardhat.org/config














Fix the version and lets recompile.

npx hardhat compile
Compiling 1 file with 0.8.4
Compilation finished successfully














We compiled... Now what?















LETS TEST!!!!

yarn add -D @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai

Typescript

yarn add -D ts-node typescript

Testing types

yarn add -D chai @types/node @types/mocha @types/chai

Yes... its a lot of dependencies.















Before we write a test there are some things to do.

  1. hardhat.config.js -> hardhat.config.ts

    Or else this...

    ➜  jpegdegens git:(master) ✗ npx hardhat test
    
      0 passing (1ms)
  2. add import "@nomiclabs/hardhat-waffle"; to the top of hardhat.config.ts.















Lets write a test!!!

one of the primary reasons why I love using hardhat so much is the fact that there is a testing suite.

  1. First create a test folder.
  2. Second, create the test...
import _ from "@nomiclabs/hardhat-ethers";

import { ethers } from "hardhat";
import { expect } from "chai";

describe("Hello World", () => {
    it("should get the hello world", async () => {
        const HW = await ethers.getContractFactory("HelloWorld");
        const hello = await HW.deploy();

        expect(await hello.hello()).to.equal("Hello, World");
    });
});














Execute the test

npx hardhat test














You will see this in some of the hardhat.config.js files and its very important. It creates ethers as a global object and as something that can be important AND with the appropriate methods on this.

➜  jpegdegens git:(master) ✗ npx hardhat test


  Testing that contract.
    ✓ should return a hello world (516ms)


  1 passing (519ms)














Questions?















A Break Point

Build some foundation

Lets look at the test code one more time and ask the question, what happened?















Lets create a deploy script.

Lets create this into a deploy script that can actually deploy our contract onto a network.

import "@nomiclabs/hardhat-ethers";
import { ethers } from "hardhat";

async function deploy() {
    const HelloWorld = await ethers.getContractFactory("HelloWorld");
    const hello = await HelloWorld.deploy();
    await hello.deployed();

    return hello;
}

// @ts-ignore
async function sayHello(hello) {
    console.log("Say Hello:", await hello.hello());
}

deploy().then(sayHello);














Deploy it like so

npx hardhat run scripts/deploy.ts --network localhost














What the Error?

(node:394882) UnhandledPromiseRejectionWarning: HardhatError: HH108: Cannot connect to the network localhost.
Please make sure your node is running, and check your internet connection and networks config
    at HttpProvider._fetchJsonRpcResponse (/home/mpaulson/personal/yayayaya/node_modules/hardhat/src/internal/core/providers/http.ts:176:15)
    at processTicksAndRejections (internal/process/task_queues.js:94:5)
(node:394882) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:394882) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code














The Cloud

ITS SOMEONE ELSES COMPUTER...















Lets make one local now.

npx hardhat node














Lets go deeper.

  1. Lets inspect the our local network
  2. Lets call our contract that is now deployed on the network.














Questions?















Lets go even deeper















Some would say don't do it















"Please, no, its too fast, too much coconut oil (free ranged, organic)"















I say no, deeper we go















Lets communicate from the browser.

  1. Go and get the MetaMask Extension
  2. Go and get a positive attitude
  3. Go and consider watching my other courses on FEM














Course on developer productivity...

Its awesome, Tmux, ansible, everything, etc. etc. Developer Productivity


Vim Fundamentals















Create an account

I'll give you a moment















Import a test account into your metamask

  • Copy any of the accounts from the initial output.
    • Consistent. Meaning you can keep this in your account.
  • Make sure metamask is pointed to "localhost"














Ok, we are not going this deep.

I just wanted you to get that done. Its important for later.















Jokes on you

We are actually doing it. So copy this webpack config.

I'll do A lot of live coding, but not webpack config live coding. No, Sir.

const dotenv = require("dotenv");
dotenv.config();

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");

module.exports = {
    entry: "./src/index.ts", // bundle"s entry point
    output: {
        path: path.resolve(__dirname, "dist"), // output directory
        filename: "[name].js", // name of the generated bundle
    },
    resolve: {
        extensions: [".js", ".ts", ".json"],
    },

    mode: "development",

    module: {
        rules: [
            {
                test: /\.ts$/,
                loader: "ts-loader",
                exclude: /node_modules/,
            },

            {
                test: /\.css$/i,
                use: ["style-loader", "css-loader"],
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html",
            inject: "body",
        }),
        new webpack.DefinePlugin({
            'process.env.CONTRACT_ADDRESS': JSON.stringify(process.env.CONTRACT_ADDRESS),
            'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
        }),
    ],

    watch: true,

    devServer: {
        historyApiFallback: true,
        contentBase: './',
        watchOptions: {
            aggregateTimeout: 300,
            poll: 1000
        }
    }
};

You also need some dependencies

yarn add -D webpack webpack-cli ts-loader html-webpack-plugin dotenv

You will also need this, tsconfig.json

{
    "compilerOptions": {
        "resolveJsonModule": true,
        "esModuleInterop": true
    }
}














Complete code

import { ethers } from "ethers";

async function hasSigners(): Promise<boolean> {
    //@ts-ignore
    const metamask = window.ethereum;
    const signers = await (metamask.request({method: 'eth_accounts'}) as Promise<string[]>);
    return signers.length > 0;
}

async function requestAccess(): Promise<boolean> {
    //@ts-ignore
    const result = (await window.ethereum.request({ method: 'eth_requestAccounts' })) as string[];
    return result && result.length > 0;
}

async function getContract() {
    const address = process.env.CONTRACT_ADDRESS;

    if (!(await hasSigners()) && !(await requestAccess())) {
        console.log("You are in trouble, no one wants to play");
    }

    // @ts-ignore
    const provider = new ethers.providers.Web3Provider(window.ethereum)
    const contract = new ethers.Contract(
        address,
        [
            "function hello() public pure returns(string memory)",
        ], // abi
        provider
    );

    console.log("We have done it, time to call");
    console.log(await contract.hello());
}


getContract();














Take a moment and just think about it.















There are a few gaps in your knowledge

First, questions?

Lets go over some basics