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

Rewrite the frontend to React #19

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
25 changes: 25 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

node_modules
dist
dist-ssr
*.local
pnpm-workspace.yaml
pnpm-lock.yaml

# Ignore backend autogenerated files
backend/abis
backend/artifacts
backend/cache
backend/src
backend/typechain-types
backend/lib
19 changes: 19 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-check

/** @type { import('eslint').Linter.ConfigType } */
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'eslint-config-prettier',
],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
indent: ['error', 2, { SwitchCase: 1 }],
},
}
18 changes: 0 additions & 18 deletions .prettierrc

This file was deleted.

30 changes: 30 additions & 0 deletions .prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @ts-check

/** @type { import("prettier").ConfigType } */
module.exports = {
printWidth: 110,
tabWidth: 2,
useTabs: false,
semi: false,
singleQuote: true,
trailingComma: 'es5',
arrowParens: 'avoid',
overrides: [
{
files: ['**/*.json'],
options: {
trailingComma: 'none',
},
},
{
files: '*.sol',
options: {
printWidth: 100,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
},
},
],
}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ deploy your dApp.
You can use one of the deployed test accounts and associated private key with
MetaMask. If you use the same MetaMask accounts on fresh local networks such as
Hardhat Node, Foundry Anvil or sapphire-dev docker image, don't forget to
*clear your account's activity* each time or manually specify the correct
_clear your account's activity_ each time or manually specify the correct
account nonce.

### Frontend Deployment
Expand Down Expand Up @@ -109,7 +109,7 @@ Then copy the `dist` folder to a place of your `/my/public/path` location.

## Troubleshooting

When click button Connect Wallet, in some case you will constantly get the error:
When click button Connect Wallet, in some case you will constantly get the error:
`Uncaught (in promise) Error: [useEthereumStore] Request account failed!`

To resolve it, try to open MetaMask and manually connect site:
Expand Down
123 changes: 63 additions & 60 deletions backend/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,99 +1,101 @@
import { promises as fs } from 'fs';
import path from 'path';
import { promises as fs } from 'fs'
import path from 'path'

import '@nomicfoundation/hardhat-ethers';
import '@oasisprotocol/sapphire-hardhat';
import '@typechain/hardhat';
import canonicalize from 'canonicalize';
import {JsonRpcProvider} from "ethers";
import 'hardhat-watcher';
import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names';
import { HardhatUserConfig, task } from 'hardhat/config';
import 'solidity-coverage';
import '@nomicfoundation/hardhat-ethers'
import '@oasisprotocol/sapphire-hardhat'
import '@typechain/hardhat'
import canonicalize from 'canonicalize'
import { JsonRpcProvider } from 'ethers'
import 'hardhat-watcher'
import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'
import { HardhatUserConfig, task } from 'hardhat/config'
import 'solidity-coverage'

const TASK_EXPORT_ABIS = 'export-abis';
const TASK_EXPORT_ABIS = 'export-abis'

task(TASK_COMPILE, async (_args, hre, runSuper) => {
await runSuper();
await hre.run(TASK_EXPORT_ABIS);
});
await runSuper()
await hre.run(TASK_EXPORT_ABIS)
})

task(TASK_EXPORT_ABIS, async (_args, hre) => {
const srcDir = path.basename(hre.config.paths.sources);
const outDir = path.join(hre.config.paths.root, 'abis');
const srcDir = path.basename(hre.config.paths.sources)
const outDir = path.join(hre.config.paths.root, 'abis')

const [artifactNames] = await Promise.all([
hre.artifacts.getAllFullyQualifiedNames(),
fs.mkdir(outDir, { recursive: true }),
]);
])

await Promise.all(
artifactNames.map(async (fqn) => {
const { abi, contractName, sourceName } = await hre.artifacts.readArtifact(fqn);
if (abi.length === 0 || !sourceName.startsWith(srcDir) || contractName.endsWith('Test'))
return;
await fs.writeFile(`${path.join(outDir, contractName)}.json`, `${canonicalize(abi)}\n`);
}),
);
});
artifactNames.map(async fqn => {
const { abi, contractName, sourceName } = await hre.artifacts.readArtifact(fqn)
if (abi.length === 0 || !sourceName.startsWith(srcDir) || contractName.endsWith('Test')) return
await fs.writeFile(`${path.join(outDir, contractName)}.json`, `${canonicalize(abi)}\n`)
})
)
})

// Unencrypted contract deployment.
task('deploy')
.setAction(async (args, hre) => {
await hre.run('compile');

// For deployment unwrap the provider to enable contract verification.
const uwProvider = new JsonRpcProvider(hre.network.config.url);
const MessageBox = await hre.ethers.getContractFactory('MessageBox', new hre.ethers.Wallet(accounts[0], uwProvider));
const messageBox = await MessageBox.deploy();
await messageBox.waitForDeployment();

console.log(`MessageBox address: ${await messageBox.getAddress()}`);
return messageBox;
});
task('deploy').setAction(async (args, hre) => {
await hre.run('compile')

// For deployment unwrap the provider to enable contract verification.
const uwProvider = new JsonRpcProvider(hre.network.config.url)
const MessageBox = await hre.ethers.getContractFactory(
'MessageBox',
new hre.ethers.Wallet(accounts[0], uwProvider)
)
const messageBox = await MessageBox.deploy()
await messageBox.waitForDeployment()

console.log(`MessageBox address: ${await messageBox.getAddress()}`)
return messageBox
})

// Read message from the MessageBox.
task('message')
.addPositionalParam('address', 'contract address')
.setAction(async (args, hre) => {
await hre.run('compile');
await hre.run('compile')

const messageBox = await hre.ethers.getContractAt('MessageBox', args.address);
const message = await messageBox.message();
const author = await messageBox.author();
console.log(`The message is: ${message}, author: ${author}`);
});
const messageBox = await hre.ethers.getContractAt('MessageBox', args.address)
const message = await messageBox.message()
const author = await messageBox.author()
console.log(`The message is: ${message}, author: ${author}`)
})

// Set message.
task('setMessage')
.addPositionalParam('address', 'contract address')
.addPositionalParam('message', 'message to set')
.setAction(async (args, hre) => {
await hre.run('compile');
await hre.run('compile')

let messageBox = await hre.ethers.getContractAt('MessageBox', args.address);
const tx = await messageBox.setMessage(args.message);
const receipt = await tx.wait();
console.log(`Success! Transaction hash: ${receipt!.hash}`);
});
let messageBox = await hre.ethers.getContractAt('MessageBox', args.address)
const tx = await messageBox.setMessage(args.message)
const receipt = await tx.wait()
console.log(`Success! Transaction hash: ${receipt!.hash}`)
})

// Hardhat Node and sapphire-dev test mnemonic.
const TEST_HDWALLET = {
mnemonic: "test test test test test test test test test test test junk",
mnemonic: 'test test test test test test test test test test test junk',
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
passphrase: "",
};
passphrase: '',
}

const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : TEST_HDWALLET;
const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : TEST_HDWALLET

const config: HardhatUserConfig = {
networks: {
hardhat: { // https://hardhat.org/metamask-issue.html
hardhat: {
// https://hardhat.org/metamask-issue.html
chainId: 1337,
},
'sapphire': {
sapphire: {
url: 'https://sapphire.oasis.io',
chainId: 0x5afe,
accounts,
Expand All @@ -103,7 +105,8 @@ const config: HardhatUserConfig = {
chainId: 0x5aff,
accounts,
},
'sapphire-localnet': { // docker run -it -p8545:8545 -p8546:8546 ghcr.io/oasisprotocol/sapphire-localnet -test-mnemonic
'sapphire-localnet': {
// docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet
url: 'http://localhost:8545',
chainId: 0x5afd,
accounts,
Expand Down Expand Up @@ -137,6 +140,6 @@ const config: HardhatUserConfig = {
require: ['ts-node/register/files'],
timeout: 50_000,
},
};
}

export default config;
export default config
28 changes: 14 additions & 14 deletions backend/test/MessageBox.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { expect } from "chai";
import { ethers } from "hardhat";
import { expect } from 'chai'
import { ethers } from 'hardhat'

describe("MessageBox", function () {
describe('MessageBox', function () {
async function deployMessageBox() {
const MessageBox_factory = await ethers.getContractFactory("MessageBox");
const messageBox = await MessageBox_factory.deploy();
await messageBox.waitForDeployment();
return { messageBox };
const MessageBox_factory = await ethers.getContractFactory('MessageBox')
const messageBox = await MessageBox_factory.deploy()
await messageBox.waitForDeployment()
return { messageBox }
}

it("Should set message", async function () {
const {messageBox} = await deployMessageBox();
it('Should set message', async function () {
const { messageBox } = await deployMessageBox()

await messageBox.setMessage("hello world");
await messageBox.setMessage('hello world')

expect(await messageBox.message()).to.equal("hello world");
expect(await messageBox.author()).to.equal(await (await ethers.provider.getSigner(0)).getAddress());
});
});
expect(await messageBox.message()).to.equal('hello world')
expect(await messageBox.author()).to.equal(await (await ethers.provider.getSigner(0)).getAddress())
})
})
1 change: 0 additions & 1 deletion frontend/.env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
VITE_NETWORK=0x5aff
VITE_WEB3_GATEWAY=https://testnet.sapphire.oasis.dev
VITE_MESSAGE_BOX_ADDR=0xca5B5E9371e6AedD850a74554a53BA91D2D3508d
BASE_DIR=/
15 changes: 0 additions & 15 deletions frontend/.eslintrc.cjs

This file was deleted.

30 changes: 0 additions & 30 deletions frontend/.gitignore

This file was deleted.

1 change: 0 additions & 1 deletion frontend/env.d.ts

This file was deleted.

10 changes: 5 additions & 5 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link href="favicon.svg" rel="icon" type="image/svg+xml" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo Starter</title>
<title>Demo starter</title>
</head>
<body class="bg-primary">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading