Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hardhat erc721 #2

Merged
merged 4 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
bguiz marked this conversation as resolved.
Show resolved Hide resolved
and a starting point for projects.

## Compile

```shell
npm run compile
bguiz marked this conversation as resolved.
Show resolved Hide resolved
```

## Test

```shell
npm run test
```

## Deploy

```shell
npm run deploy
a-ridley marked this conversation as resolved.
Show resolved Hide resolved
```

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]
bguiz marked this conversation as resolved.
Show resolved Hide resolved
*/
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();
28 changes: 28 additions & 0 deletions erc721-hardhat/scripts/verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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';
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);
});
});