Hardhat Smartcontract Lottery (Raffle) Using TypeScript and latest versions of everything
Tags: smartcontract, solidity, typescript, blockchain
Published: 2023-07-02 16:07:30


I am in the progress of learning Patrick Collins' tutorial. This tutorial provided a fantastic introduction to blockchain and web3 development using JavaScript, Solidity, and Hardhat. One of the lessons aimed to develop and deploy a lottery(Raffle) smart contract using hardhat-deploy, hardhat dev testnet, and the Rinkeby testnet. Patrick also implement a typescript version of his code.

This article aims to build upon that work by updating the application to TypeScript and using the latest versions of all dependencies. I would like to use TypeScript in a stronger way. We'll also switch from the Rinkeyby testnet to the Sepolia testnet, which is more up-to-date.

> It's not a complete tutorial of creating a hardhat application. It's just an extension to Patrick Collins' ultra-ultimate tutorial for year 2023 when we have ehters.js version 6.

This is the typescript branch Patric Collins' repository [https://github.com/PatrickAlphaC/hardhat-smartcontract-lottery-fcc/tree/typescript ](https://github.com/PatrickAlphaC/hardhat-smartcontract-lottery-fcc/tree/typescript )

We will be making a few small corrections in line with the rapidly changing worlds of JS and blockchain.

## Installation & preparations

Hardhat has great documentation [https://hardhat.org/hardhat-runner/docs/getting-started#installation](https://hardhat.org/hardhat-runner/docs/getting-started#installation). I will describe hardhat things in a nutshell.

Installation and setting up

You can install hardhat globally or locally. I prefer the second option but if you desire to be a super-skilled senior smart-contract developer you probably need to have it installed globally. Anyway, we have to install hardhat somehow.

Then we should initiate the project

```
hardhat
npx hardhat
```
Or after installing the hardhat-shorthand you can use just
```
hh
```

To simplify the process we chose the Typescript project option:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mls4a99bf3qsp3hggpgp.png)

Agree with everything and set it up.

We don't need contracts, scripts, and tests generated by Hardhat.

```
rm -rf contract/ scripts/ test/
```

We will have the same Solidity contract as Patrik. [https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts/blob/main/contracts/Raffle.sol](https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts/blob/main/contracts/Raffle.sol)
There are no changes here.

We also have to install a couple of dependencies

```
npm i @nomicfoundation/hardhat-toolbox @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers dotenv hardhat-gas-reporter -D
```

I usually use `eslint`. Patrick used `prettier`. It doesn't matter which tool you choose. But it would be good practice to choose something because JS allows you too much and you should have borders to have your code in great condition. 

For `eslint` I need to make 

```
npm i eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
```

and turn it on in the IDE.

We can make 'hh compile to have contract typings to write our code. But it is not necessary. The Hardhat executes this command right before the deploy.

## Config

We want to use TypeScript. And we want to use it in the right way. Patrik Collins has `helper-hardhat-config.ts` in his repo we won't. Instead of it, we will extend the Hardhat config.

I don't want to use any additional config files. And I really want to have all configurations of the current network in the `network` option. This is a difference between Patrics' solution and the current solution. In the following config file you can see `blockConfirmations`, `entranceFee`, `gasLane`, `subscriptionId`, `callbackGasLimit`, `interval` option right in the network object.


```ts
import '@typechain/hardhat';
import '@nomicfoundation/hardhat-toolbox';
import '@nomiclabs/hardhat-ethers';
import '@nomicfoundation/hardhat-ethers';
import 'hardhat-deploy';
import 'hardhat-gas-reporter';
import './type-extensions';
import 'dotenv/config';
import { HardhatUserConfig } from 'hardhat/config';
import { ethers } from 'ethers';


const config: HardhatUserConfig = {
    solidity: '0.8.18',
    defaultNetwork: 'hardhat',
    networks: {
        hardhat: {
            chainId: 31337,
            blockConfirmations: 1,
            entranceFee: ethers.parseEther('0.01'),
            gasLane: '0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c',
            subscriptionId: '',
            callbackGasLimit: '500000',
            interval: 30
        },
        sepolia: {
            chainId: 11155111,
            url: process.env.SEPOLIA_RPC_URL,
            accounts: [process.env.SEPOLIA_PRIVATE_KEY as string],
            blockConfirmations: 6,
            vrfCoordinator: '0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625',
            entranceFee: ethers.parseEther('0.01'),
            gasLane: '0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c',
            subscriptionId: '3092',
            callbackGasLimit: '500000',
            interval: 30
        }
    },
    etherscan: {
        apiKey: {
            sepolia: process.env.ETHERSCAN_API_KEY || ''
        },
    },
    namedAccounts: {
        deployer: {
            default: 0
        },
        player: {
            default: 1
        }
    },
    developmentChains: ['hardhat', 'localhost'],
    organizerFee: 10,
    mocha: {
        timeout: 40000
    }
};

export default config;

```

But it's not enough. TypeScript/Eslint will show the errors

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pp7csb29mxq6x6wvtke4.png)

One more important thing.
In ethers 6 you can no longer use `ethers.utils.parseEther('0.01')` the function doesn't exist. `Utils` isn't exit. We should use just `ethers.parseEther('0.01')`.


I created `type-extensions.ts` file to extend the default config. 

```ts
import 'hardhat/types/config';

declare module 'hardhat/types/config' {
    export interface HttpNetworkUserConfig {
        blockConfirmations?: number;
        vrfCoordinator?: string;
        entranceFee: bigint;
        gasLane: string;
        subscriptionId?: string;
        callbackGasLimit: string;
        interval: number;
    }

    export interface HardhatNetworkUserConfig {
        blockConfirmations?: number;
        entranceFee: bigint;
        gasLane: string;
        subscriptionId?: string;
        callbackGasLimit?: string;
        interval: number;
    }

    export interface HardhatUserConfig {
        developmentChains: string[];
        organizerFee: number;
    }

    export interface HttpNetworkConfig {
        blockConfirmations: number;
        vrfCoordinator: string;
        entranceFee: bigint;
        gasLane: string;
        subscriptionId: string;
        callbackGasLimit: string;
        interval: number;
    }

    export interface HardhatNetworkConfig {
        blockConfirmations: number;
        vrfCoordinator?: string;
        entranceFee: bigint;
        gasLane: string;
        subscriptionId: string;
        callbackGasLimit: string;
        interval: number;
    }

    export interface HardhatConfig {
        developmentChains: string[];
        organizerFee: number;
    }
}
```

I believe that structure is very clear. Declarations with `UserConfig` suffix are for hardhat config. Declarations with `Config` suffix are for exposing these properties in your scripts.

Let see it. The `deploy/01-deploy-raffle.ts` should be similar to

```ts
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { config, network } from 'hardhat';
import { DeployFunction } from 'hardhat-deploy/dist/types';
import verify from '../utils/verify';
import { VRFCoordinatorV2Mock } from '../typechain-types';
import { EventLog } from 'ethers';

const deployRaffle: DeployFunction = async function ({ getNamedAccounts, deployments, ethers }: HardhatRuntimeEnvironment) {
    const { deploy, log } = deployments;
    const { deployer } = await getNamedAccounts();
    let vrfCoordinatorV2Address = network.config.vrfCoordinator;
    let subscriptionId = network.config.subscriptionId;
    let vrfCoordinatorV2Mock: VRFCoordinatorV2Mock;

    if (config.developmentChains.includes(network.name)) {
        vrfCoordinatorV2Mock = await ethers.getContract('VRFCoordinatorV2Mock');
        vrfCoordinatorV2Address = await vrfCoordinatorV2Mock.getAddress();
        const transactionReceipt = await (await vrfCoordinatorV2Mock.createSubscription())
            .wait(1);

        subscriptionId = (transactionReceipt?.logs[0] as EventLog).args.subId;
        await vrfCoordinatorV2Mock.fundSubscription(subscriptionId, ethers.parseEther('2'));
    }

    const args = [
        vrfCoordinatorV2Address,
        network.config.entranceFee,
        network.config.gasLane,
        subscriptionId,
        network.config.callbackGasLimit,
        network.config.interval,
        config.organizerFee
    ];

    const raffle = await deploy('Raffle', {
        from: deployer,
        args,
        log: true,
        waitConfirmations: network.config.blockConfirmations || 1
    });

    if (config.developmentChains.includes(network.name)) {
        await vrfCoordinatorV2Mock!.addConsumer(subscriptionId, raffle.address);
    }

    if (!config.developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) {
        await verify(raffle.address, args, log);
    }

    log('----------------------------');
};

deployRaffle.tags = ['all', 'raffle'];
export default deployRaffle;

```

We've imported the `network` from the `hardhat` and I can use `network.config.vrfCoordinator`, `network.config.subscriptionId`. Awesome!

Another awesome thing is that we have hintings for our contract object.

We can get contract using TypeSctipt generics thing

```ts
import { Raffle } from '../typechain-types';

const raffle = await ethers.getContract<Raffle>('Raffle');
```

And now we can use this kind of magic:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8s1ad093wlzfmwyozhdf.png)


All other things of the application are the same. I will attach the link to the repository in the end of this article.

## Tests


Tests are almost the same. We have the difference in case of `bigint` interactions. But it is so easy:

```ts
const contractBalance = BigInt(raffleEntranceFee) * BigInt(additionalEntrances) + BigInt(raffleEntranceFee);
```

Also, we should use instead of 

```ts
raffle.callStatic.checkUpkeep("0x")
```
a bit different

```ts
raffle.checkUpkeep.staticCall('0x');
```

And we can replace this bulky time-machine functionality 

```ts
await network.provider.send('evm_increaseTime', [interval + 1]);
                await network.provider.request({ method: 'evm_mine', params: [] });
```

with laconic 

```ts
import { time } from '@nomicfoundation/hardhat-toolbox/network-helpers';
...
await time.increase(interval + 1);
```


I have two skipped tests because at the time of writing this article events firing doesn't work properly in the latest version. [Link to the github issue](https://github.com/NomicFoundation/hardhat/issues/4098)

## Sepolia 

Patric Collins works with Rinkeby in his tutorial. Using of Sepolia is quite similar.

Creating subscription [https://docs.chain.link/vrf/v2/subscription/supported-networks/#sepolia-testnet](https://docs.chain.link/vrf/v2/subscription/supported-networks/#sepolia-testnet)

VRF things [https://vrf.chain.link/](https://vrf.chain.link/)

Upkeep registering [https://automation.chain.link/sepolia](https://automation.chain.link/sepolia)

Etherscan [https://sepolia.etherscan.io/](https://sepolia.etherscan.io/)


## Conclusion

We can deploy and enter the lottery

```
hh deploy --network sepolia
hh run scripts/enterRaffle.ts --network sepolia
```

We used the latest version on everything and bit rewrite Freecodecamp Patrick Collins' Raffle application 

We have a problem with a subscription to the events in the latest version of hardhat. But I believe that it will be fixed soon.

There you have it, a lottery smart contract built with Hardhat, TypeScript, and the latest versions of all dependencies, and deployed locally or on the Sepolia testnet. 


The link to the repository of this article is 

[https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts](https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts)

- 
Follow me 

https://t.me/awesomeprog
https://twitter.com/shogenoff
http://facebook.com/shogentle