Skip to content

Commit

Permalink
feat: hardhat project that uses OZ ERC721 implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bguiz committed Jan 30, 2024
1 parent 6e5a3f2 commit fe2e8b3
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 0 deletions.
2 changes: 2 additions & 0 deletions erc721-hardhat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/artifacts
/cache
54 changes: 54 additions & 0 deletions erc721-hardhat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ERC721 Hardhat Demo

This project contains a [non-fungible token](https://eips.ethereum.org/EIPS/eip-721) implementation
that inherits from [OpenZeppelin's ERC721 implementation](https://docs.openzeppelin.com/contracts/5.x/api/token/erc721).

It uses the hardhat development framework to:

- Compile
- Test
- Deploy
- Verify

the smart contract.

Compilation and testing occur on `localhost`;
whereas deployment and verification occur on Hedera Testnet (a public network).

This project has been designed as a minimal example demonstration;
and a starting point for projects.

## Compile

```shell
npm run compile
```

## Test

```shell
npm run test
```

## Deploy

```shell
npm run deploy
```

Then visit Hashscan (a Hedera network explorer) at the URL that is output, for example:
[`https://hashscan.io/testnet/contract/0x6eae9247C122b3e3CDC621F61F757B809bF7455a`](https://hashscan.io/testnet/contract/0x6eae9247C122b3e3CDC621F61F757B809bF7455a).

Under the *Contract Bytecode* section, you should see the EVM bytecode for this smart contract.

## Verify

```shell
npm run verify
```

Then visit Hashscan (a Hedera network explorer) at the URL that is output, for example:
[`https://hashscan.io/testnet/contract/0x6eae9247C122b3e3CDC621F61F757B809bF7455a`](https://hashscan.io/testnet/contract/0x6eae9247C122b3e3CDC621F61F757B809bF7455a).

Under the *Contract Bytecode* section, you should still see the EVM bytecode for this smart contract;
and you show also see the Solidity source code as well.
21 changes: 21 additions & 0 deletions erc721-hardhat/contracts/MyNonFungibleToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MyNonFungibleToken is ERC721 {
uint256 public tokenId = 0;

constructor() ERC721("bguiz non fungible token", "BGZNFT") {
create();
}

function create()
public
returns (uint256)
{
tokenId += 1;
_mint(msg.sender, tokenId);
return tokenId;
}
}
95 changes: 95 additions & 0 deletions erc721-hardhat/hardhat.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const path = require('node:path');

const dotEnvPath = path.resolve(__dirname, '../.env');
require('dotenv').config({
path: dotEnvPath,
});

// populates hre.ethers, hre.waffle, enables typechain, etc
require('@nomicfoundation/hardhat-toolbox');
require('@nomicfoundation/hardhat-chai-matchers');

/*
Set up a JSON-RPC endpoint for this project to connect to Hedera Testnet.
Ref: https://docs.hedera.com/hedera/tutorials/more-tutorials/json-rpc-connections/
*/
const rpcUrlHederatestnet = process.env.RPC_URL_HEDERATESTNET;
if (!rpcUrlHederatestnet || !rpcUrlHederatestnet.startsWith('http')) {
throw new Error(
'Missing RPC URL in RPC_URL_HEDERATESTNET env var',
);
}

/*
Issue the following command to generate a BIP-39 seed phrase
and save it in the env file:
npx [email protected]
*/
const seedPhrase = process.env.BIP39_SEED_PHRASE;
if (!seedPhrase || seedPhrase.split(' ').length < 12) {
throw new Error(
'Missing BIP-39 seed phrase in BIP39_SEED_PHRASE env var',
);
}

const accounts = {
mnemonic: seedPhrase,
// Ref: https://github.com/hashgraph/hedera-sdk-js/blob/1a73f3f1329a48702f2a5170260bd05f186e0ca3/packages/cryptography/src/Mnemonic.js#L34
path: "m/44'/60'/0'/0",
// path: "m/44'/3030'/0'/0",
initialIndex: 0,
count: 10,
};

const hardhatConfig = {
solidity: {
version: '0.8.20',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks: {
hardhat: {
accounts,
},
hederatestnet: {
chainId: 296,
url: rpcUrlHederatestnet,
gasMultiplier: 1.1,
accounts,
},
},
sourcify: {
enabled: true,
apiUrl: 'https://server-verify.hashscan.io',
browserUrl: 'https://repository-verify.hashscan.io',
},
etherscan: {
enabled: false,
},
mocha: {
timeout: 6_000_000,
},
};

module.exports = hardhatConfig;

/*
To verify that we're able to connect to Hedera Testnet successfully:
npx hardhat console --network hederatestnet
OR
npm run console
// latest block number
(await require('hardhat').network.provider.send('eth_getBlockByNumber', ['latest', false])).number
// the default EOA that will be used in deployment transactions
(await hre.ethers.getSigners())[0].address
.exit
*/
30 changes: 30 additions & 0 deletions erc721-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "erc20-hardhat-demo",
"private": true,
"type": "module",
"version": "0.0.0",
"description": "ERC20 Hardhat Demo",
"main": "index.js",
"scripts": {
"compile": "npx hardhat compile",
"test": "npx hardhat test",
"console:hederatestnet": "npx hardhat console --network hederatestnet",
"console": "npm run console:hederatestnet",
"deploy:hederatestnet": "npx hardhat run --network hederatestnet scripts/deploy.js",
"deploy": "npm run deploy:hederatestnet",
"verify:hederatestnet": "npx hardhat run --network hederatestnet scripts/verify.js",
"verify": "npm run verify:hederatestnet"
},
"keywords": [
"hedera"
],
"author": "bguiz",
"license": "MIT",
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "2.0.3",
"@nomicfoundation/hardhat-toolbox": "4.0.0",
"@openzeppelin/contracts": "5.0.1",
"dotenv": "16.3.1",
"hardhat": "2.19.4"
}
}
32 changes: 32 additions & 0 deletions erc721-hardhat/scripts/deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import path from 'node:path';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import hre from 'hardhat';

async function main() {
const scName = 'MyNonFungibleToken';
const MyFungibleToken = await ethers.getContractFactory(scName);
console.log('Deploying...');
const myFungibleToken = await MyFungibleToken.deploy();
await myFungibleToken.waitForDeployment();
const deployedAddress = await myFungibleToken.getAddress();
console.log('Deployed:', deployedAddress);
try {
const dirName = path.dirname(fileURLToPath(import.meta.url));
const filePath = path.resolve(dirName, '../cache/deploy.json');
const fileContents = {
[hre.network.name]: {
[scName]: {
deployedAddress,
},
},
};
await fs.writeFile(filePath, JSON.stringify(fileContents, undefined, 2));
} catch (ex) {
console.error(ex);
}
const hashscanNetworkName = (hre.network.name).replace('hedera', '');
console.log(`${scName} - https://hashscan.io/${hashscanNetworkName}/contract/${deployedAddress}`);
}

main();
32 changes: 32 additions & 0 deletions erc721-hardhat/scripts/verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import path from 'node:path';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import hre from 'hardhat';
import util from 'node:util';
import { exec } from 'node:child_process';

const childProcessExecAsPromise = util.promisify(exec);

async function main() {
const scName = 'MyNonFungibleToken';
try {
const dirName = path.dirname(fileURLToPath(import.meta.url));
const filePath = path.resolve(dirName, '../cache/deploy.json');
const fileContentsRaw = await fs.readFile(filePath);
const fileContents = JSON.parse(fileContentsRaw);
const deployments = fileContents[hre.network.name];
Object.keys(deployments).forEach(async (scName) => {
const deployedAddress = deployments[scName].deployedAddress;
console.log('Verifying', scName, 'at', deployedAddress, 'on', hre.network.name, '...');
await hre.run('verify:sourcify', {
address: deployedAddress,
});
const hashscanNetworkName = (hre.network.name).replace('hedera', '');
console.log(`${scName} - https://hashscan.io/${hashscanNetworkName}/contract/${deployedAddress}`);
});
} catch (ex) {
console.error(ex);
}
}

main();
13 changes: 13 additions & 0 deletions erc721-hardhat/test/my-non-fungible-token.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { expect } from 'chai';
import hre from 'hardhat';

describe('MyNonFungibleToken', function () {
it('should have an initial supply', async function () {
const [account1] = await hre.ethers.getSigners();
const acount1Adress = await account1.getAddress();
const myNonFungibleToken = await hre.ethers.deployContract('MyNonFungibleToken');

const [ownerOfNft1] = await myNonFungibleToken.ownerOf.staticCallResult(1n);
expect(ownerOfNft1).to.equal(acount1Adress);
});
});

0 comments on commit fe2e8b3

Please sign in to comment.