diff --git a/.gitignore b/.gitignore
index ee07ced40..816bf92c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,18 @@
packages/subgraph/subgraph.yaml
packages/subgraph/generated
-packages/subgraph/abis
+packages/subgraph/abis/*
packages/hardhat/*.txt
**/aws.json
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
**/node_modules
-packages/hardhat/artifacts
+packages/hardhat/artifacts*
packages/hardhat/deployments
packages/react-app/src/contracts/*
-!packages/react-app/src/contracts/contracts.js
-packages/hardhat/cache
+!packages/react-app/src/contracts/external_contracts.js
+packages/hardhat/cache*
-docker/**/data
+packages/**/data
packages/subgraph/config/config.json
tenderly.yaml
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..592fcfa19
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,8 @@
+[submodule "packages/services/arbitrum"]
+ path = packages/services/arbitrum
+ url = https://github.com/OffchainLabs/arbitrum
+ branch = master
+[submodule "packages/services/optimism"]
+ path = packages/services/optimism
+ url = https://github.com/ethereum-optimism/optimism
+ branch = regenesis/0.4.0
diff --git a/README.md b/README.md
index fdd91c18d..df0422211 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,9 @@
# ๐ scaffold-eth
-> is everything you need to get started building decentralized applications on Ethereum! ๐
+> is everything you need to get started building decentralized applications on Ethereum! ๐
---
-
#### [ ๐โโ๏ธ Quick Start ](https://github.com/austintgriffith/scaffold-eth#%EF%B8%8F-quick-start)
#### [ ๐ญ Learning Solidity ](https://github.com/austintgriffith/scaffold-eth#-learning-solidity)
@@ -12,22 +11,26 @@
#### [ ๐ก Deploy ](https://github.com/austintgriffith/scaffold-eth#-deploy)
#### [ ๐บ Frontend](https://github.com/austintgriffith/scaffold-eth#-frontend)
+
- [ ๐ฐ Providers ](https://github.com/austintgriffith/scaffold-eth#-providers)
- [ ๐ Hooks ](https://github.com/austintgriffith/scaffold-eth#-hooks)
- [ ๐ฆ Components ](https://github.com/austintgriffith/scaffold-eth#-components)
- [ ๐ฒ UI Library ](https://github.com/austintgriffith/scaffold-eth#-ui-library)
- [ โ Helpers ](https://github.com/austintgriffith/scaffold-eth#-helpers)
- [ ๐ Extras ](https://github.com/austintgriffith/scaffold-eth#-extras)
-- [ ๐ณ Ship it! ](https://github.com/austintgriffith/scaffold-eth#-ship-it)
+- [ ๐ณ Ship it! ](https://github.com/austintgriffith/scaffold-eth#-ship-it)
#### [ ๐ฉ Challenges ](https://github.com/austintgriffith/scaffold-eth#-challenges)
+
- [ ๐ฅฉ Staking App](https://github.com/austintgriffith/scaffold-eth/tree/challenge-1-decentralized-staking)
- [ ๐ต Token Vendor ](https://github.com/austintgriffith/scaffold-eth/tree/challenge-2-token-vendor)
#### [ ๐ฉโ๐ป Examples & Tutorials ](https://github.com/austintgriffith/scaffold-eth#-examples-and-tutorials)
+
- [ ๐ Simple NFT ](https://github.com/austintgriffith/scaffold-eth/tree/simple-nft-example)
#### [ Built with ๐ scaffold-eth ](https://github.com/austintgriffith/scaffold-eth#-built-with--scaffold-eth)
+
- [ ๐จ Nifty.ink ](https://nifty.ink) ([code](https://github.com/austintgriffith/scaffold-eth/tree/nifty-ink-dev))
- [ ๐งโ๐คPunkWallet.io ](https://punkwallet.io/) ([code](https://github.com/austintgriffith/scaffold-eth/tree/punk-wallet))
@@ -37,27 +40,26 @@
- [ ๐ฌ Tenderly ](https://github.com/austintgriffith/scaffold-eth#-using-tenderly)
- [ ๐ Etherscan ](https://github.com/austintgriffith/scaffold-eth#-etherscan)
- [ ๐ถ Infura ](https://github.com/austintgriffith/scaffold-eth#-using-infura)
-- ๐ช [ Blocknative ](https://github.com/austintgriffith/scaffold-eth#-blocknative)
+- ๐ช [ Blocknative ](https://github.com/austintgriffith/scaffold-eth#-blocknative)
-|- [ ๐ Legacy Content ](https://github.com/austintgriffith/scaffold-eth#-legacy-content) - | - [ ๐ฌ Support Chat ](https://github.com/austintgriffith/scaffold-eth#-support-chat) -|
+|- [ ๐ Legacy Content ](https://github.com/austintgriffith/scaffold-eth#-legacy-content) - | - [ ๐ฌ Support Chat ](https://github.com/austintgriffith/scaffold-eth#-support-chat) -|
[](https://gitpod.io/#https://github.com/austintgriffith/scaffold-eth)
-
---
[](https://youtu.be/33gnKe7ttCc?t=477)
-
---
+
---
+
---
# ๐โโ๏ธ Quick Start
required: [Node](https://nodejs.org/dist/latest-v12.x/) plus [Yarn](https://classic.yarnpkg.com/en/docs/install/) and [Git](https://git-scm.com/downloads)
-
```bash
git clone https://github.com/austintgriffith/scaffold-eth.git
cd scaffold-eth
@@ -83,7 +85,7 @@ yarn deploy
๐ Edit your frontend `App.jsx` in `packages/react-app/src`
-๐ผ Edit your deployment script `deploy.js` in `packages/hardhat/scripts`
+๐ผ Edit and add your deployment scripts in `packages/hardhat/deploy` - we are using [hardhat-deploy](https://www.npmjs.com/package/hardhat-deploy)
๐ฑ Open http://localhost:3000 to see the app
@@ -93,37 +95,26 @@ yarn deploy
๐งช It is a free standing dapp so you can learn by making small changes.
-
-> *After installing*, your dev environment should look like this:
+> _After installing_, your dev environment should look like this:

-
> React dev server, HardHat blockchain, deploy terminal, code IDE, and frontend browser.
โ๏ธ Make small changes to `YourContract.sol` and watch your app auto update!
-
๐ You can `yarn deploy` any time and get a fresh new contract in the frontend:
-

-
-๐ต Each browser has an account in the top right and you can use the faucet (bottom left) to get โฝ๏ธ testnet eth for gas:
-
+๐ต Each browser has an account in the top right and you can use the faucet (bottom left) to get โฝ๏ธ testnet eth for gas:

-
-
-
๐จOnce you have funds, you can call `setPurpose` on your contract and "write" to the `purpose` storage:
-

-
Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn chain` terminal:

@@ -135,6 +126,7 @@ Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn ch
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
+
---
# ๐ญ Learning Solidity
@@ -160,8 +152,7 @@ Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn ch

-
-๐ฌ What happens when you subtract 1 from 0? Try it out in the app to see what happens!
+๐ฌ What happens when you subtract 1 from 0? Try it out in the app to see what happens!

@@ -203,7 +194,7 @@ Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn ch
๐ณ Maybe an make an array `YourStructName[] public proposals;` that could call be voted on with `function vote() public {}`
-๐ญ Your dev environment is perfect for *testing assumptions* and learning by prototyping.
+๐ญ Your dev environment is perfect for _testing assumptions_ and learning by prototyping.
๐ Next learn about the [fallback function](https://solidity-by-example.org/0.6/fallback/)
@@ -216,12 +207,11 @@ Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn ch
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
----
+---
# ๐ก Deploy
-
๐ฐ Ready to deploy to a testnet? Change the `defaultNetwork` in `packages/hardhat/hardhat.config.js`
๐ Generate a deploy account with `yarn generate` and view it with `yarn account`
@@ -234,13 +224,24 @@ Look for the [HardHat](https://hardhat.org) console.log() output in the `yarn ch
yarn deploy
```
+## hardhat-deploy
+
+scaffold-eth now uses [hardhat-deploy](https://www.npmjs.com/package/hardhat-deploy), a hardhat plugin by [wighawag](https://twitter.com/wighawag?lang=en) that gives your hardhat deployments super-powers!
+
+When you run `yarn deploy`, the scripts in `/packages/hardhat/deploy` are run in alphabetical order (by default - more fine-grained controls in the hardhat-deploy docs). You can deploy contracts, interact with contracts & send ETH - whatever you want!
+
+Deployment metadata is stored in the `/deployments` folder, and automatically copied to `/packages/react-app/src/contracts/hardhat_contracts.json` via the `--export-all` flag in the `yarn deploy` command (see `/packages/hardhat/packagen.json`).
+
+Crucially, this information is stored by network, so if you redeploy contracts on a new network (e.g. on testnet, after running locally), your local deployments are still tracked.
+
---
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
-
---
+
---
+
# ๐บ Frontend
> Edit your frontend `App.jsx` in `packages/react-app/src`
@@ -249,17 +250,17 @@ yarn deploy

-
๐คก Adjust your debugging settings as needed:

-
---
-## ๐ Providers:
+## ๐ Providers & Signers:
+
+### Providers
-Providers are your connections to different blockchains.
+Providers are your connections to different blockchains. scaffold-eth uses [ethers.js providers](https://docs.ethers.io/v5/api/providers/).
The frontend has three different providers that provide different levels of access to different chains:
@@ -267,10 +268,23 @@ The frontend has three different providers that provide different levels of acce
`localProvider`: local [HardHat](https://hardhat.org) accounts, used to read from _your_ contracts (`.env` file points you at testnet or mainnet)
-`injectedProvider`: your personal [MetaMask](https://metamask.io/download.html), [WalletConnect](https://walletconnect.org/apps) via [Argent](https://www.argent.xyz/), or other injected wallet (generates [burner-provider](https://www.npmjs.com/package/burner-provider) on page load)
+We use `ethers.providers.StaticJsonRpcProvider` when instantiating providers from RPCs where we are confident that the chainId won't change to save on network calls :)
+
+`injectedProvider`: your personal [MetaMask](https://metamask.io/download.html), [WalletConnect](https://walletconnect.org/apps) via [Argent](https://www.argent.xyz/), connected using [web3modal](https://github.com/Web3Modal/web3modal).

+### Signers
+
+From the [ethers.js docs...](https://docs.ethers.io/v5/api/signer/)
+
+A Signer in ethers is an abstraction of an Ethereum Account, which can be used to sign messages and transactions and send signed transactions to the Ethereum Network to execute state changing operations.
+
+scaffold-eth now uses signers for user operations, either using injectedProvider.getSigner(), or using a Burner Signer created and stored in localStorage (all handled by the `useUserSigner` hook!)
+
+### When should I use a provider and when should I use a signer?
+
+If you are only reading data, use a provider. If you need to make transactions, or sign things, use a Signer.
---
@@ -278,10 +292,8 @@ The frontend has three different providers that provide different levels of acce

-
Commonly used Ethereum hooks located in `packages/react-app/src/`:
-
`usePoller(fn, delay)`: runs a function on app load and then on a custom interval
```jsx
@@ -290,6 +302,22 @@ usePoller(() => {
}, 3000);
```
+`useOnBlock(provider, fn, args)`: runs a function on app load and then on every new block for a provider
+
+```jsx
+useOnBlock(mainnetProvider, () => {
+ console.log(`โ A new mainnet block is here!`);
+});
+```
+
+
+
+`useUserSigner(injectedProviderOrSigner, localProvider)`: returns the signer associated with an injected web3 provider; if injectedProvider is null, generates a burner signer (stored in local storage)
+
+```js
+const userSigner = useUserSigner(injectedProvider, localProvider);
+```
+
`useBalance(address, provider, [pollTime])`: poll for the balance of an address from a provider
@@ -324,11 +352,47 @@ const price = useExchangePrice(mainnetProvider);
-`useContractLoader(provider)`: loads your smart contract interface
+`useContractLoader(provider)`: loads your smart contract interface, for contracts on the provider's chain.
+
+This will use contracts deployed from `packages/hardhat` (which are exported to `src/contracts/hardhat_contracts.json`), as well as external contract information, which can be added to `src/contracts/external_contracts.js`. Note that you can override both of these by passing `hardhatContracts` or `externalContracts` to the second config parameter of `useContractLoader` (see those files for the required format).
```js
const readContracts = useContractLoader(localProvider);
const writeContracts = useContractLoader(injectedProvider);
+const writeContracts = useContractLoader(injectedProvider, { chainId: 1 }); // fix the chainId (even if the provider is on a different chain)
+const writeContracts = useContractLoader(injectedProvider, {
+ networkName: "localhost",
+}); // fix the hardhat network name (even if the provider is on a different chain)
+const writeContracts = useContractLoader(injectedProvider, {
+ customAddresses: { EXAMPLE: "0xADDRESS" },
+}); // over-ride the address
+
+// Pass custom contracts
+const ERC20ABI = [
+ "function balanceOf(address owner) view returns (uint256)",
+ "function decimals() view returns (uint8)",
+ "function symbol() view returns (string)",
+ "function transfer(address to, uint amount) returns (boolean)",
+ "event Transfer(address indexed from, address indexed to, uint amount)",
+];
+const ERC20ContractMetadata = {
+ 1: {
+ contracts: {
+ DAI: {
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
+ abi: ERC20ABI,
+ },
+ UNI: {
+ address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
+ abi: ERC20ABI,
+ },
+ },
+ },
+};
+
+const writeContracts = useContractLoader(injectedProvider, { chainId: 1
+ externalContracts: ERC20ContractMetadata,
+});
```
@@ -360,7 +424,6 @@ const ownerUpdates = useEventListener(

-
Your commonly used React Ethereum components located in `packages/react-app/src/`:
@@ -475,7 +538,7 @@ tx(writeContracts["SmartContractWallet"].updateOwner(newOwner));
โฌ๏ธ Installing a new package to your frontend? You need to `cd packages/react-app` and then `yarn add PACKAGE`
-โฌ๏ธ Installing a new package to your backend? You need to `cd packages/harthat` and then `yarn add PACKAGE`
+โฌ๏ธ Installing a new package to your backend? You need to `cd packages/hardhat` and then `yarn add PACKAGE`
---
@@ -509,38 +572,169 @@ yarn ipfs
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
+
---
## ๐ฉ Challenges
1. [ ๐ฅฉ Decentralized Staking App ](https://github.com/austintgriffith/scaffold-eth/tree/challenge-1-decentralized-staking)
-2. [ ๐ต Token Vendor ](https://github.com/austintgriffith/scaffold-eth/tree/challenge-2-token-vendor)
+2. [ ๐ต Token Vendor ](https://github.com/austintgriffith/scaffold-eth/tree/challenge-2-token-vendor)
---
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
+
---
## ๐ก Examples and Tutorials
(todo: insert all the cool active branches)
-
-| [ tenderly ](https://github.com/austintgriffith/scaffold-eth/tree/tenderly) |
-| --- |
+| [ tenderly ](https://github.com/austintgriffith/scaffold-eth/tree/tenderly) |
+| ----------------------------------------------------------------------------------------------- |
| [ simple-nft-example ](https://github.com/austintgriffith/scaffold-eth/tree/simple-nft-example) |
-
^^^ โ PR your ๐ scaffold-eth branch!!! ๐๐๐ ^^^
---
+# Subgraph
+
+[The Graph](https://thegraph.com/docs/introduction) lets you process on-chain events to create a Subgraph, an easy to query graphQL endpoint!
+
+scaffold-eth comes with a built in demo subgraph, as well as a local docker setup to run a graph-node locally.
+
+[
+](https://youtu.be/T5ylzOTkn-Q)
+
+[ ๐ฅ here is another Graph speed run tutorial video ](https://youtu.be/T5ylzOTkn-Q)
+
+** [Requires Docker](https://www.docker.com/products/docker-desktop) **
+
+๐ฎ Clean up previous data:
+
+```
+yarn clean-graph-node
+```
+
+๐ก Spin up a local graph node by running
+
+```
+yarn run-graph-node
+```
+
+๐ Create your local subgraph by running
+
+```
+yarn graph-create-local
+```
+
+This is only required once!
+
+๐ข Deploy your local subgraph by running
+
+```
+yarn graph-ship-local
+```
+
+๐๏ธ Edit your local subgraph in `packages/subgraph/src`
+
+Learn more about subgraph definition [here](https://thegraph.com/docs/define-a-subgraph)
+
+๐คฉDeploy your contracts and your subgraph in one go by running:
+
+```
+yarn deploy-and-graph
+```
+
+---
+
+# Services:
+
+`/services` is a new (!) scaffold-eth package that pulls in backend services that you might need for local development, or even for production deployment.
+
+## Graph node
+
+graph-node lets you [run a node locally](https://thegraph.com/docs/quick-start#local-development).
+
+```
+run-graph-node // runs the graph node
+remove-graph-node // stops the graph node
+clean-graph-node // clears the local data
+```
+
+## Submodules
+
+scaffold-eth uses [submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to pull in other repositories. These first need to be initiated.
+
+```
+yarn workspace @scaffold-eth/services submodule-init
+```
+
+## Optimism
+
+[optimism.io](https://optimism.io) is an Optimistic Rollup. This submodule (of the [Optimism monorepo](https://github.com/ethereum-optimism/optimism)) runs a local chain, with an optimistic rollup.
+
+To run the local setup...
+
+** [Requires Docker](https://www.docker.com/products/docker-desktop) **
+
+```
+yarn workspace @scaffold-eth/services run-optimism
+```
+
+> The first time may take a while as the services build!
+
+The underlying services are run in the background, so you won't see anything in the terminal, but you can use Docker Desktop to inspect them.
+
+You can stop local optimism at any time by running:
+
+```
+yarn workspace @scaffold-eth/services stop-optimism
+```
+
+The local L1 and the Rollup are configured in both `/hardhat` and `/react-app` as `localOptimism` and `localOptimismL1`, so you can deploy and build out of the box!
+
+Learn more about building on Optimism [here](https://community.optimism.io/docs/).
+
+## Arbitrum
+
+[Arbitrum](https://developer.offchainlabs.com/docs/developer_quickstart) is an Optimistic Rollup. This submodule (of the [Arbitrum monorepo](https://github.com/OffchainLabs/arbitrum)) runs a local chain, with an optimistic rollup.
+
+To run the local setup...
+
+** [Requires Docker](https://www.docker.com/products/docker-desktop) **
+
+In one terminal:
+
+```
+yarn workspace @scaffold-eth/services arbitrum-init
+yarn workspace @scaffold-eth/services arbitrum-build-l1
+yarn workspace @scaffold-eth/services arbitrum-run-l1
+```
+
+In a second terminal:
+
+```
+yarn workspace @scaffold-eth/services arbitrum-init-l2
+yarn workspace @scaffold-eth/services arbitrum-run-l2
+```
+
+> The first time may take a while as the services build!
+
+To stop the processes, you can just run CTRL-C
+
+The local L1 and the Rollup are configured in both `/hardhat` and `/react-app` as `localArbitrum` and `localArbitrumL1`, so you can deploy and build out of the box!
+
+Learn more about building on Arbitrum [here](https://developer.offchainlabs.com/docs/developer_quickstart).
+
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
+
---
# ๐จ Built with ๐ scaffold-eth:
@@ -553,15 +747,12 @@ Paintings come to life as you "ink" new creations and trade them on Ethereum. A
[๐พ Source Code ](https://github.com/austintgriffith/scaffold-eth/tree/nifty-ink-dev)
-
[
๐งโโ๏ธ Instant Wallet
](https://instantwallet.io)
An instant wallet running on xDAI insired by [xdai.io](https://xdai.io).
-
[๐พ Source Code ](https://github.com/austintgriffith/scaffold-eth/tree/instantwallet-dev-session)
-
[
๐ณ Personal Token Voting
](https://medium.com/@austin_48503/personal-token-voting-73b44a598d8e)
Poll your holders! Build an example emoji voting system with ๐ scaffold-eth. ๐ Cryptographically signed votes but tracked off-chain with ๐ก Zapier and ๐ Google Sheets.
@@ -570,78 +761,75 @@ Poll your holders! Build an example emoji voting system with ๐ scaffold-et
[๐พ Source Code ](https://github.com/austintgriffith/scaffold-eth/tree/emoji-vote-dev)
-
^^^ โ PLEASE PR your ๐ scaffold-eth project in above!!! ๐๐๐ ^^^
---
+
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
----
---
-# ๐ Infrastructure
-
---
-## ๐ฐ Using The Graph
-
-[
-](https://youtu.be/T5ylzOTkn-Q)
-
-[ ๐ฅ here is another Graph speed run tutorial video ](https://youtu.be/T5ylzOTkn-Q)
-
+# ๐ Infrastructure
---
## ๐ฌ Using Tenderly
+
[Tenderly](https://tenderly.co) is a platform for monitoring, alerting and trouble-shooting smart contracts. They also have a hardhat plugin and CLI tool that can be helpful for local development!
Hardhat Tenderly [announcement blog](https://blog.tenderly.co/level-up-your-smart-contract-productivity-using-hardhat-and-tenderly/) for reference.
-
### Verifying contracts on Tenderly
+
scaffold-eth includes the hardhat-tenderly plugin. When deploying to any of the following networks:
+
```
["kovan","goerli","mainnet","rinkeby","ropsten","matic","mumbai","xDai","POA"]
```
-You can verify contracts as part of the `deploy.js` script. We have created a `tenderlyVerify()` helper function, which takes your contract name and its deployed address:
-```
-await tenderlyVerify(
- {contractName: "YourContract",
- contractAddress: yourContract.address
-})
-```
-Make sure your target network is present in the hardhat networks config, then either update the default network in `hardhat.config.js` to your network of choice or run:
+
+You can verify contracts as part of a deployment script.
+
```
-yarn deploy --network NETWORK_OF_CHOICE
+let verification = await tenderly.verify({
+ name: contractName,
+ address: contractAddress,
+ network: targetNetwork,
+});
```
-Once verified, they will then be available to view on Tenderly!
-
+Once verified, they will then be available to view on Tenderly!
[](https://www.youtube.com/watch?v=c04rrld1IiE&t=47s)
-
#### Exporting local Transactions
+
One of Tenderly's best features for builders is the ability to [upload local transactions](https://dashboard.tenderly.co/tx/main/0xb8f28a9cace2bdf6d10809b477c9c83e81ce1a1b2f75f35ddd19690bbc6612aa/local-transactions) so that you can use all of Tenderly's tools for analysis and debugging. You will need to create a [tenderly account](https://tenderly.co/) if you haven't already.
Exporting local transactions can be done using the [Tenderly CLI](https://github.com/tenderly/tenderly-cli). Installing the Tenderly CLI:
+
```
brew tap tenderly/tenderly
brew install tenderly
```
+
_See alternative installation steps [here](https://github.com/tenderly/tenderly-cli#installation)_
You need to log in and configure for your local chain (including any forking information) - this can be done from any directory, but it probably makes sense to do under `/packages/hardhat` to ensure that local contracts are also uploaded with the local transaction (see more below!)
+
```
cd packages/hardhat
tenderly login
tenderly export init
```
+
You can then take transaction hashes from your local chain and run the following from the `packages/hardhat` directory:
+
```
tenderly export
```
+
Which will upload them to tenderly.co/dashboard!
Tenderly also allows users to debug smart contracts deployed to a local fork of some network (see `yarn fork`). To let Tenderly know that we are dealing with a fork, run the following command:
@@ -658,9 +846,10 @@ tenderly export --export-network
Note that `tenderly.yaml` file stores information about all networks that you initialized for exporting transactions. There can be multiple of them in a single file. You only need the `--export-network` if you have more than one network in your tenderly.yaml config!
-**A quick note on local contracts:** if your local contracts are persisted in a place that Tenderly can find them, then they will also be uploaded as part of the local transaction `export`, which is one of the freshest features! We have added a call to `tenderly.persistArtifacts()` as part of the scaffold-eth deploy() script, which stores the contracts & meta-information in a `deployments` folder, so this should work out of the box.
+**A quick note on local contracts:** if your local contracts are persisted in a place that Tenderly can find them, then they will also be uploaded as part of the local transaction `export`, which is one of the freshest features! We are using hardhat-deploy, which stores the contracts & meta-information in a `deployments` folder, so this should work out of the box.
Another pitfall when dealing with a local network (fork or not) is that you will not see the transaction hash if it fails. This happens because the hardhat detects an error while `eth_estimateGas` is executed. To prevent such behaviour, you can skip this estimation by passing a `gasLimit` override when making a call - an example of this is demonstrated in the `FunctionForm.jsx` file of the Contract component:
+
```
let overrides = {}
// Uncomment the next line if you want to skip the gas estimation for each transaction
@@ -669,6 +858,7 @@ const returned = await tx(contractFunction(...args, overrides));
```
**One gotcha** - Tenderly does not (currently) support yarn workspaces, so any imported solidity contracts need to be local to `packages/hardhat` for your contracts to be exported. You can achieve this by using [`nohoist`](https://classic.yarnpkg.com/blog/2018/02/15/nohoist/) - this has been done for `hardhat` so that we can export `console.sol` - see the top-level `package.json` to see how!
+
```
"workspaces": {
"packages": [
@@ -681,30 +871,17 @@ const returned = await tx(contractFunction(...args, overrides));
}
```
-
---
## ๐ Etherscan
-Hardhat has a truly wonderful [`hardhat-etherscan` plugin](https://www.npmjs.com/package/@nomiclabs/hardhat-etherscan) that takes care of contract verification after deployment. You need to add the following to your `hardhat.config.js` imports:
-```
-require("@nomiclabs/hardhat-etherscan");
-```
-Then add your etherscan API key to the module.exports:
-```
-etherscan: {
- // Your API key for Etherscan
- // Obtain one at https://etherscan.io/
- apiKey: "YOUR-API-KEY-HERE"
-}
-```
-Verifying is simple, assuming you are verifying a contract that you have just deployed from your hardhat setup - you just need to run the verify script, passing constructor arguments as an array if necessary (there is an example commented out in the `deploy.js`):
+
+hardhat-deploy lets you easily verify contracts on Etherscan, and we have added a helper script to `/packages/hardhat` to let you do that. Simply run:
+
```
-await run("verify:verify", {
- address: yourContract.address,
- // constructorArguments: args // If your contract has constructor arguments, you can pass them as an array
-})
+yarn etherscan-verify --network
```
-You only have to pass the contract because the plugin figures out which of the locally compiled contracts is the right one to verify. Pretty cool stuff!
+
+And all hardhat's deployed contracts with matching ABIs for that network will be automatically verified. Neat!
---
@@ -719,17 +896,16 @@ You will need to update the `constants.js` in `packages/react-app/src` with [you
> update the `BLOCKNATIVE_DAPPID` in `packages/react-app/src/constants.js` with [your own Blocknative DappID](https://docs.blocknative.com/notify)
---
+
===================================================== [โซ back to the top โซ](https://github.com/austintgriffith/scaffold-eth#-scaffold-eth)
---
----
+---
## ๐ Legacy Content
-
-[
๐งซ Building on Ethereum in 2020 (research for this repo)
๐งซ Building on Ethereum in 2020 (research for this repo)
](https://medium.com/@austin_48503/building-on-ethereum-in-2020-dca52eda5f00)
[](https://www.youtube.com/watch?v=ShJZf5lsXiM&feature=youtu.be&t=19)
@@ -752,35 +928,32 @@ Learn about tokens. [coming soon] What is a token? Why is it cool? How can I dep
Learn the basics of Automated Market Makers like ๐ฆ Uniswap. Learn how ๐ฐReserves affect the ๐ price, โ๏ธ trading, and ๐ฆ slippage from low liquidity.
-[๐โโ๏ธ SpeedRun ๐น](https://youtu.be/eP5w6Ger1EQ)
+[๐โโ๏ธ SpeedRun ๐น](https://youtu.be/eP5w6Ger1EQ)
---
[
Tutorial 4: ๐ Connecting ETH to IPFS
](https://medium.com/@austin_48503/tl-dr-scaffold-eth-ipfs-20fa35b11c35)
Build a simple IPFS application in ๐ scaffold-eth to learn more about distributed file storage and content addressing.
- [๐ฅ Live Tutorial](https://youtu.be/vqrLr5eOjLo?t=342)
+[๐ฅ Live Tutorial](https://youtu.be/vqrLr5eOjLo?t=342)
---
Tutorial 5: โฝ๏ธGSN and Meta Transactions
-Learn about to provide your users with better UX by abstracting away gas fees and blockchain mechanics. (todo)
-
+Learn about to provide your users with better UX by abstracting away gas fees and blockchain mechanics. (todo)
---
-
[
Tutorial 6: ๐ฐ Decentralized Deployment
](https://medium.com/@austin_48503/decentralized-deployment-7d975c9d5016)
-Learn how to deploy your smart contract to a production blockchain. Then deploy your applicaton to Surge, S3, and IPFS. Finally, register an ENS and point it at the decentralized content! [๐ฅ Live Tutorial](https://youtu.be/vqrLr5eOjLo?t=1350)
+Learn how to deploy your smart contract to a production blockchain. Then deploy your applicaton to Surge, S3, and IPFS. Finally, register an ENS and point it at the decentralized content! [๐ฅ Live Tutorial](https://youtu.be/vqrLr5eOjLo?t=1350)
---
-
## ๐ฌ Support Chat
-Join the telegram [support chat ๐ฌ](https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA) to ask questions and find others building with ๐ scaffold-eth!
+Join the telegram [support chat ๐ฌ](https://t.me/joinchat/KByvmRe5wkR-8F_zz6AjpA) to ask questions and find others building with ๐ scaffold-eth!
---
diff --git a/package.json b/package.json
index 43b9674fa..5ab2f7175 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,9 @@
"fundedwallet": "cd packages/hardhat && npx hardhat fundedwallet",
"flatten": "cd packages/hardhat && npx hardhat flatten",
"clean": "cd packages/hardhat && npx hardhat clean",
- "graph-run-node": "cd docker/graph-node && docker-compose up",
- "graph-remove-node": "cd docker/graph-node && docker-compose down",
+ "run-graph-node": "yarn workspace @scaffold-eth/services run-graph-node",
+ "remove-graph-node": "yarn workspace @scaffold-eth/services remove-graph-node",
+ "clean-graph-node": "yarn workspace @scaffold-eth/services clean-graph-node",
"graph-prepare": "mustache packages/subgraph/config/config.json packages/subgraph/src/subgraph.template.yaml > packages/subgraph/subgraph.yaml",
"graph-codegen": "yarn workspace @scaffold-eth/subgraph graph codegen",
"graph-build": "yarn workspace @scaffold-eth/subgraph graph build",
diff --git a/packages/hardhat/deploy/00_deploy_your_contract.js b/packages/hardhat/deploy/00_deploy_your_contract.js
new file mode 100644
index 000000000..17885b302
--- /dev/null
+++ b/packages/hardhat/deploy/00_deploy_your_contract.js
@@ -0,0 +1,30 @@
+// deploy/00_deploy_your_contract.js
+
+module.exports = async ({ getNamedAccounts, deployments }) => {
+ const { deploy } = deployments;
+ const { deployer } = await getNamedAccounts();
+ await deploy("YourContract", {
+ // Learn more about args here: https://www.npmjs.com/package/hardhat-deploy#deploymentsdeploy
+ from: deployer,
+ // args: ["Hello"],
+ log: true,
+ });
+
+ /*
+ // Getting a previously deployed contract
+ const YourContract = await ethers.getContract("YourContract", deployer);
+ await YourContract.setPurpose("Hello");
+
+ //const yourContract = await ethers.getContractAt('YourContract', "0xaAC799eC2d00C013f1F11c37E654e59B0429DF6A") //<-- if you want to instantiate a version of a contract at a specific address!
+ */
+};
+module.exports.tags = ["YourContract"];
+
+/*
+Tenderly verification
+let verification = await tenderly.verify({
+ name: contractName,
+ address: contractAddress,
+ network: targetNetwork,
+});
+*/
diff --git a/packages/hardhat/hardhat.config.js b/packages/hardhat/hardhat.config.js
index 60ca12b2b..94cf720f1 100644
--- a/packages/hardhat/hardhat.config.js
+++ b/packages/hardhat/hardhat.config.js
@@ -3,9 +3,12 @@ const fs = require("fs");
const chalk = require("chalk");
require("@nomiclabs/hardhat-waffle");
-require("@tenderly/hardhat-tenderly")
+require("@tenderly/hardhat-tenderly");
-require("@nomiclabs/hardhat-etherscan");
+require("hardhat-deploy");
+
+require("@eth-optimism/hardhat-ovm");
+require("@nomiclabs/hardhat-ethers");
const { isAddress, getAddress, formatUnits, parseUnits } = utils;
@@ -28,7 +31,9 @@ function mnemonic() {
return fs.readFileSync("./mnemonic.txt").toString().trim();
} catch (e) {
if (defaultNetwork !== "localhost") {
- console.log("โข๏ธ WARNING: No mnemonic file created for a deploy account. Try `yarn run generate` and then `yarn run account`.")
+ console.log(
+ "โข๏ธ WARNING: No mnemonic file created for a deploy account. Try `yarn run generate` and then `yarn run account`."
+ );
}
}
return "";
@@ -51,49 +56,111 @@ module.exports = {
*/
},
rinkeby: {
- url: "https://rinkeby.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", //<---- YOUR INFURA ID! (or it won't work)
+ url: "https://rinkeby.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", // <---- YOUR INFURA ID! (or it won't work)
accounts: {
mnemonic: mnemonic(),
},
},
kovan: {
- url: "https://kovan.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", //<---- YOUR INFURA ID! (or it won't work)
+ url: "https://kovan.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", // <---- YOUR INFURA ID! (or it won't work)
accounts: {
mnemonic: mnemonic(),
},
},
mainnet: {
- url: "https://mainnet.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", //<---- YOUR INFURA ID! (or it won't work)
+ url: "https://mainnet.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", // <---- YOUR INFURA ID! (or it won't work)
accounts: {
mnemonic: mnemonic(),
},
},
ropsten: {
- url: "https://ropsten.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", //<---- YOUR INFURA ID! (or it won't work)
+ url: "https://ropsten.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", // <---- YOUR INFURA ID! (or it won't work)
accounts: {
mnemonic: mnemonic(),
},
},
goerli: {
- url: "https://goerli.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", //<---- YOUR INFURA ID! (or it won't work)
+ url: "https://goerli.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad", // <---- YOUR INFURA ID! (or it won't work)
accounts: {
mnemonic: mnemonic(),
},
},
xdai: {
- url: 'https://rpc.xdaichain.com/',
+ url: "https://rpc.xdaichain.com/",
gasPrice: 1000000000,
accounts: {
mnemonic: mnemonic(),
},
},
matic: {
- url: 'https://rpc-mainnet.maticvigil.com/',
+ url: "https://rpc-mainnet.maticvigil.com/",
gasPrice: 1000000000,
accounts: {
mnemonic: mnemonic(),
},
},
+ rinkebyArbitrum: {
+ url: "https://rinkeby.arbitrum.io/rpc",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ companionNetworks: {
+ l1: "rinkeby",
+ },
+ },
+ localArbitrum: {
+ url: "http://localhost:8547",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ companionNetworks: {
+ l1: "localArbitrumL1",
+ },
+ },
+ localArbitrumL1: {
+ url: "http://localhost:7545",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ companionNetworks: {
+ l2: "localArbitrum",
+ },
+ },
+ kovanOptimism: {
+ url: "https://kovan.optimism.io",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ ovm: true,
+ companionNetworks: {
+ l1: "kovan",
+ },
+ },
+ localOptimism: {
+ url: "http://localhost:8545",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ ovm: true,
+ companionNetworks: {
+ l1: "localOptimismL1",
+ },
+ },
+ localOptimismL1: {
+ url: "http://localhost:9545",
+ gasPrice: 0,
+ accounts: {
+ mnemonic: mnemonic(),
+ },
+ companionNetworks: {
+ l2: "localOptimism",
+ },
+ },
},
solidity: {
compilers: [
@@ -102,27 +169,29 @@ module.exports = {
settings: {
optimizer: {
enabled: true,
- runs: 200
- }
- }
+ runs: 200,
+ },
+ },
},
{
version: "0.6.7",
settings: {
optimizer: {
enabled: true,
- runs: 200
- }
- }
- }
+ runs: 200,
+ },
+ },
+ },
],
-
},
- etherscan: {
- // Your API key for Etherscan
- // Obtain one at https://etherscan.io/
- apiKey: "PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8"
- }
+ ovm: {
+ solcVersion: "0.7.6",
+ },
+ namedAccounts: {
+ deployer: {
+ default: 0, // here this will by default take the first account as deployer
+ },
+ },
};
const DEBUG = false;
@@ -134,171 +203,214 @@ function debug(text) {
}
task("wallet", "Create a wallet (pk) link", async (_, { ethers }) => {
- const randomWallet = ethers.Wallet.createRandom()
- const privateKey = randomWallet._signingKey().privateKey
- console.log("๐ WALLET Generated as " + randomWallet.address + "")
- console.log("๐ http://localhost:3000/pk#"+privateKey)
+ const randomWallet = ethers.Wallet.createRandom();
+ const privateKey = randomWallet._signingKey().privateKey;
+ console.log("๐ WALLET Generated as " + randomWallet.address + "");
+ console.log("๐ http://localhost:3000/pk#" + privateKey);
});
-
task("fundedwallet", "Create a wallet (pk) link and fund it with deployer?")
- .addOptionalParam("amount", "Amount of ETH to send to wallet after generating")
+ .addOptionalParam(
+ "amount",
+ "Amount of ETH to send to wallet after generating"
+ )
.addOptionalParam("url", "URL to add pk to")
.setAction(async (taskArgs, { network, ethers }) => {
+ const randomWallet = ethers.Wallet.createRandom();
+ const privateKey = randomWallet._signingKey().privateKey;
+ console.log("๐ WALLET Generated as " + randomWallet.address + "");
+ let url = taskArgs.url ? taskArgs.url : "http://localhost:3000";
- const randomWallet = ethers.Wallet.createRandom()
- const privateKey = randomWallet._signingKey().privateKey
- console.log("๐ WALLET Generated as " + randomWallet.address + "")
- let url = taskArgs.url?taskArgs.url:"http://localhost:3000"
-
- let localDeployerMnemonic
- try{
- localDeployerMnemonic = fs.readFileSync("./mnemonic.txt")
- localDeployerMnemonic = localDeployerMnemonic.toString().trim()
+ let localDeployerMnemonic;
+ try {
+ localDeployerMnemonic = fs.readFileSync("./mnemonic.txt");
+ localDeployerMnemonic = localDeployerMnemonic.toString().trim();
} catch (e) {
/* do nothing - this file isn't always there */
}
- let amount = taskArgs.amount?taskArgs.amount:"0.01"
+ let amount = taskArgs.amount ? taskArgs.amount : "0.01";
const tx = {
to: randomWallet.address,
- value: ethers.utils.parseEther(amount)
+ value: ethers.utils.parseEther(amount),
};
//SEND USING LOCAL DEPLOYER MNEMONIC IF THERE IS ONE
// IF NOT SEND USING LOCAL HARDHAT NODE:
- if(localDeployerMnemonic){
- let deployerWallet = new ethers.Wallet.fromMnemonic(localDeployerMnemonic)
- deployerWallet = deployerWallet.connect(ethers.provider)
- console.log("๐ต Sending "+amount+" ETH to "+randomWallet.address+" using deployer account");
- let sendresult = await deployerWallet.sendTransaction(tx)
- console.log("\n"+url+"/pk#"+privateKey+"\n")
- return
- }else{
- console.log("๐ต Sending "+amount+" ETH to "+randomWallet.address+" using local node");
- console.log("\n"+url+"/pk#"+privateKey+"\n")
+ if (localDeployerMnemonic) {
+ let deployerWallet = new ethers.Wallet.fromMnemonic(
+ localDeployerMnemonic
+ );
+ deployerWallet = deployerWallet.connect(ethers.provider);
+ console.log(
+ "๐ต Sending " +
+ amount +
+ " ETH to " +
+ randomWallet.address +
+ " using deployer account"
+ );
+ let sendresult = await deployerWallet.sendTransaction(tx);
+ console.log("\n" + url + "/pk#" + privateKey + "\n");
+ return;
+ } else {
+ console.log(
+ "๐ต Sending " +
+ amount +
+ " ETH to " +
+ randomWallet.address +
+ " using local node"
+ );
+ console.log("\n" + url + "/pk#" + privateKey + "\n");
return send(ethers.provider.getSigner(), tx);
}
+ });
-});
-
-
-task("generate", "Create a mnemonic for builder deploys", async (_, { ethers }) => {
- const bip39 = require("bip39")
- const hdkey = require('ethereumjs-wallet/hdkey');
- const mnemonic = bip39.generateMnemonic()
- if (DEBUG) console.log("mnemonic", mnemonic)
- const seed = await bip39.mnemonicToSeed(mnemonic)
- if (DEBUG) console.log("seed", seed)
- const hdwallet = hdkey.fromMasterSeed(seed);
- const wallet_hdpath = "m/44'/60'/0'/0/";
- const account_index = 0
- let fullPath = wallet_hdpath + account_index
- if (DEBUG) console.log("fullPath", fullPath)
- const wallet = hdwallet.derivePath(fullPath).getWallet();
- const privateKey = "0x" + wallet._privKey.toString('hex');
- if (DEBUG) console.log("privateKey", privateKey)
- var EthUtil = require('ethereumjs-util');
- const address = "0x" + EthUtil.privateToAddress(wallet._privKey).toString('hex')
- console.log("๐ Account Generated as " + address + " and set as mnemonic in packages/hardhat")
- console.log("๐ฌ Use 'yarn run account' to get more information about the deployment account.")
-
- fs.writeFileSync("./" + address + ".txt", mnemonic.toString())
- fs.writeFileSync("./mnemonic.txt", mnemonic.toString())
-});
+task(
+ "generate",
+ "Create a mnemonic for builder deploys",
+ async (_, { ethers }) => {
+ const bip39 = require("bip39");
+ const hdkey = require("ethereumjs-wallet/hdkey");
+ const mnemonic = bip39.generateMnemonic();
+ if (DEBUG) console.log("mnemonic", mnemonic);
+ const seed = await bip39.mnemonicToSeed(mnemonic);
+ if (DEBUG) console.log("seed", seed);
+ const hdwallet = hdkey.fromMasterSeed(seed);
+ const wallet_hdpath = "m/44'/60'/0'/0/";
+ const account_index = 0;
+ let fullPath = wallet_hdpath + account_index;
+ if (DEBUG) console.log("fullPath", fullPath);
+ const wallet = hdwallet.derivePath(fullPath).getWallet();
+ const privateKey = "0x" + wallet._privKey.toString("hex");
+ if (DEBUG) console.log("privateKey", privateKey);
+ var EthUtil = require("ethereumjs-util");
+ const address =
+ "0x" + EthUtil.privateToAddress(wallet._privKey).toString("hex");
+ console.log(
+ "๐ Account Generated as " +
+ address +
+ " and set as mnemonic in packages/hardhat"
+ );
+ console.log(
+ "๐ฌ Use 'yarn run account' to get more information about the deployment account."
+ );
+
+ fs.writeFileSync("./" + address + ".txt", mnemonic.toString());
+ fs.writeFileSync("./mnemonic.txt", mnemonic.toString());
+ }
+);
-task("mineContractAddress", "Looks for a deployer account that will give leading zeros")
+task(
+ "mineContractAddress",
+ "Looks for a deployer account that will give leading zeros"
+)
.addParam("searchFor", "String to search for")
.setAction(async (taskArgs, { network, ethers }) => {
+ let contract_address = "";
+ let address;
+
+ const bip39 = require("bip39");
+ const hdkey = require("ethereumjs-wallet/hdkey");
+
+ let mnemonic = "";
+ while (contract_address.indexOf(taskArgs.searchFor) != 0) {
+ mnemonic = bip39.generateMnemonic();
+ if (DEBUG) console.log("mnemonic", mnemonic);
+ const seed = await bip39.mnemonicToSeed(mnemonic);
+ if (DEBUG) console.log("seed", seed);
+ const hdwallet = hdkey.fromMasterSeed(seed);
+ const wallet_hdpath = "m/44'/60'/0'/0/";
+ const account_index = 0;
+ let fullPath = wallet_hdpath + account_index;
+ if (DEBUG) console.log("fullPath", fullPath);
+ const wallet = hdwallet.derivePath(fullPath).getWallet();
+ const privateKey = "0x" + wallet._privKey.toString("hex");
+ if (DEBUG) console.log("privateKey", privateKey);
+ var EthUtil = require("ethereumjs-util");
+ address =
+ "0x" + EthUtil.privateToAddress(wallet._privKey).toString("hex");
+
+ const rlp = require("rlp");
+ const keccak = require("keccak");
+
+ let nonce = 0x00; //The nonce must be a hex literal!
+ let sender = address;
+
+ let input_arr = [sender, nonce];
+ let rlp_encoded = rlp.encode(input_arr);
+
+ let contract_address_long = keccak("keccak256")
+ .update(rlp_encoded)
+ .digest("hex");
+
+ contract_address = contract_address_long.substring(24); //Trim the first 24 characters.
+ }
- let contract_address = ""
- let address;
-
- const bip39 = require("bip39")
- const hdkey = require('ethereumjs-wallet/hdkey');
-
- let mnemonic = ""
- while(contract_address.indexOf(taskArgs.searchFor)!=0){
+ console.log(
+ "โ Account Mined as " +
+ address +
+ " and set as mnemonic in packages/hardhat"
+ );
+ console.log(
+ "๐ This will create the first contract: " +
+ chalk.magenta("0x" + contract_address)
+ );
+ console.log(
+ "๐ฌ Use 'yarn run account' to get more information about the deployment account."
+ );
+
+ fs.writeFileSync(
+ "./" + address + "_produces" + contract_address + ".txt",
+ mnemonic.toString()
+ );
+ fs.writeFileSync("./mnemonic.txt", mnemonic.toString());
+ });
- mnemonic = bip39.generateMnemonic()
- if (DEBUG) console.log("mnemonic", mnemonic)
- const seed = await bip39.mnemonicToSeed(mnemonic)
- if (DEBUG) console.log("seed", seed)
+task(
+ "account",
+ "Get balance informations for the deployment account.",
+ async (_, { ethers }) => {
+ const hdkey = require("ethereumjs-wallet/hdkey");
+ const bip39 = require("bip39");
+ let mnemonic = fs.readFileSync("./mnemonic.txt").toString().trim();
+ if (DEBUG) console.log("mnemonic", mnemonic);
+ const seed = await bip39.mnemonicToSeed(mnemonic);
+ if (DEBUG) console.log("seed", seed);
const hdwallet = hdkey.fromMasterSeed(seed);
const wallet_hdpath = "m/44'/60'/0'/0/";
- const account_index = 0
- let fullPath = wallet_hdpath + account_index
- if (DEBUG) console.log("fullPath", fullPath)
+ const account_index = 0;
+ let fullPath = wallet_hdpath + account_index;
+ if (DEBUG) console.log("fullPath", fullPath);
const wallet = hdwallet.derivePath(fullPath).getWallet();
- const privateKey = "0x" + wallet._privKey.toString('hex');
- if (DEBUG) console.log("privateKey", privateKey)
- var EthUtil = require('ethereumjs-util');
- address = "0x" + EthUtil.privateToAddress(wallet._privKey).toString('hex')
-
-
- const rlp = require('rlp');
- const keccak = require('keccak');
-
- let nonce = 0x00; //The nonce must be a hex literal!
- let sender = address;
-
- let input_arr = [ sender, nonce ];
- let rlp_encoded = rlp.encode(input_arr);
-
- let contract_address_long = keccak('keccak256').update(rlp_encoded).digest('hex');
-
- contract_address = contract_address_long.substring(24); //Trim the first 24 characters.
-
-
- }
-
- console.log("โ Account Mined as " + address + " and set as mnemonic in packages/hardhat")
- console.log("๐ This will create the first contract: "+chalk.magenta("0x"+contract_address));
- console.log("๐ฌ Use 'yarn run account' to get more information about the deployment account.")
-
- fs.writeFileSync("./" + address + "_produces"+contract_address+".txt", mnemonic.toString())
- fs.writeFileSync("./mnemonic.txt", mnemonic.toString())
-});
-
-task("account", "Get balance informations for the deployment account.", async (_, { ethers }) => {
- const hdkey = require('ethereumjs-wallet/hdkey');
- const bip39 = require("bip39")
- let mnemonic = fs.readFileSync("./mnemonic.txt").toString().trim()
- if (DEBUG) console.log("mnemonic", mnemonic)
- const seed = await bip39.mnemonicToSeed(mnemonic)
- if (DEBUG) console.log("seed", seed)
- const hdwallet = hdkey.fromMasterSeed(seed);
- const wallet_hdpath = "m/44'/60'/0'/0/";
- const account_index = 0
- let fullPath = wallet_hdpath + account_index
- if (DEBUG) console.log("fullPath", fullPath)
- const wallet = hdwallet.derivePath(fullPath).getWallet();
- const privateKey = "0x" + wallet._privKey.toString('hex');
- if (DEBUG) console.log("privateKey", privateKey)
- var EthUtil = require('ethereumjs-util');
- const address = "0x" + EthUtil.privateToAddress(wallet._privKey).toString('hex')
-
- var qrcode = require('qrcode-terminal');
- qrcode.generate(address);
- console.log("โ๐ฌ Deployer Account is " + address)
- for (let n in config.networks) {
- //console.log(config.networks[n],n)
- try {
-
- let provider = new ethers.providers.JsonRpcProvider(config.networks[n].url)
- let balance = (await provider.getBalance(address))
- console.log(" -- " + n + " -- -- -- ๐ก ")
- console.log(" balance: " + ethers.utils.formatEther(balance))
- console.log(" nonce: " + (await provider.getTransactionCount(address)))
- } catch (e) {
- if (DEBUG) {
- console.log(e)
+ const privateKey = "0x" + wallet._privKey.toString("hex");
+ if (DEBUG) console.log("privateKey", privateKey);
+ var EthUtil = require("ethereumjs-util");
+ const address =
+ "0x" + EthUtil.privateToAddress(wallet._privKey).toString("hex");
+
+ var qrcode = require("qrcode-terminal");
+ qrcode.generate(address);
+ console.log("โ๐ฌ Deployer Account is " + address);
+ for (let n in config.networks) {
+ //console.log(config.networks[n],n)
+ try {
+ let provider = new ethers.providers.JsonRpcProvider(
+ config.networks[n].url
+ );
+ let balance = await provider.getBalance(address);
+ console.log(" -- " + n + " -- -- -- ๐ก ");
+ console.log(" balance: " + ethers.utils.formatEther(balance));
+ console.log(
+ " nonce: " + (await provider.getTransactionCount(address))
+ );
+ } catch (e) {
+ if (DEBUG) {
+ console.log(e);
+ }
}
}
}
-
-});
-
+);
async function addr(ethers, addr) {
if (isAddress(addr)) {
@@ -322,13 +434,13 @@ task("blockNumber", "Prints the block number", async (_, { ethers }) => {
});
task("balance", "Prints an account's balance")
- .addPositionalParam("account", "The account's address")
- .setAction(async (taskArgs, { ethers }) => {
- const balance = await ethers.provider.getBalance(
- await addr(ethers, taskArgs.account)
- );
- console.log(formatUnits(balance, "ether"), "ETH");
- });
+ .addPositionalParam("account", "The account's address")
+ .setAction(async (taskArgs, { ethers }) => {
+ const balance = await ethers.provider.getBalance(
+ await addr(ethers, taskArgs.account)
+ );
+ console.log(formatUnits(balance, "ether"), "ETH");
+ });
function send(signer, txparams) {
return signer.sendTransaction(txparams, (error, transactionHash) => {
@@ -363,13 +475,13 @@ task("send", "Send ETH")
from: await fromSigner.getAddress(),
to,
value: parseUnits(
- taskArgs.amount ? taskArgs.amount : "0",
- "ether"
+ taskArgs.amount ? taskArgs.amount : "0",
+ "ether"
).toHexString(),
nonce: await fromSigner.getTransactionCount(),
gasPrice: parseUnits(
- taskArgs.gasPrice ? taskArgs.gasPrice : "1.001",
- "gwei"
+ taskArgs.gasPrice ? taskArgs.gasPrice : "1.001",
+ "gwei"
).toHexString(),
gasLimit: taskArgs.gasLimit ? taskArgs.gasLimit : 24000,
chainId: network.config.chainId,
@@ -383,4 +495,4 @@ task("send", "Send ETH")
debug(JSON.stringify(txRequest, null, 2));
return send(fromSigner, txRequest);
-});
+ });
diff --git a/packages/hardhat/package.json b/packages/hardhat/package.json
index 3e548296a..eeb6b3ac4 100644
--- a/packages/hardhat/package.json
+++ b/packages/hardhat/package.json
@@ -11,32 +11,34 @@
"eslint-plugin-prettier": "^3.1.4"
},
"dependencies": {
- "@nomiclabs/hardhat-ethers": "^2.0.0",
- "@nomiclabs/hardhat-etherscan": "^2.1.1",
+ "@eth-optimism/hardhat-ovm": "^0.2.2",
+ "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers",
"@nomiclabs/hardhat-waffle": "^2.0.0",
"@openzeppelin/contracts": "^3.2.0",
"@tenderly/hardhat-tenderly": "^1.0.10",
"chai": "^4.2.0",
"chalk": "^4.1.0",
"ethereum-waffle": "^3.1.1",
- "ethers": "^5.0.17",
+ "ethers": "^5.3.0",
"hardhat": "^2.0.11",
+ "hardhat-deploy": "^0.8.6",
"node-watch": "^0.7.0",
"qrcode-terminal": "^0.12.0",
"ramda": "^0.27.1"
},
"scripts": {
- "chain": "hardhat node",
+ "chain": "hardhat node --network hardhat --no-deploy",
"fork": "hardhat node --fork https://mainnet.infura.io/v3/460f40a260564ac4a4f4b3fffb032dad",
"test": "hardhat test --network hardhat",
"compile": "hardhat compile",
- "deploy": "hardhat run scripts/deploy.js",
+ "deploy": "hardhat deploy --export-all ../react-app/src/contracts/hardhat_contracts.json",
"postdeploy": "hardhat run scripts/publish.js",
"watch": "node scripts/watch.js",
"accounts": "hardhat accounts",
"balance": "hardhat balance",
"send": "hardhat send",
"generate": "hardhat generate",
- "account": "hardhat account"
+ "account": "hardhat account",
+ "etherscan-verify": "hardhat etherscan-verify --api-key PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8"
}
}
diff --git a/packages/hardhat/scripts/deploy.js b/packages/hardhat/scripts/deploy.js
index 23ede244b..d87fc2929 100644
--- a/packages/hardhat/scripts/deploy.js
+++ b/packages/hardhat/scripts/deploy.js
@@ -5,11 +5,30 @@ const { config, ethers, tenderly, run } = require("hardhat");
const { utils } = require("ethers");
const R = require("ramda");
-const main = async () => {
+/*
+
+ _______ _________ _______ _______
+( ____ \\__ __/( ___ )( ____ )
+| ( \/ ) ( | ( ) || ( )|
+| (_____ | | | | | || (____)|
+(_____ ) | | | | | || _____)
+ ) | | | | | | || (
+/\____) | | | | (___) || )
+\_______) )_( (_______)|/
+
+This deploy script is no longer in use, but is left for reference purposes!
+scaffold-eth now uses hardhat-deploy to manage deployments, see the /deploy folder
+And learn more here: https://www.npmjs.com/package/hardhat-deploy
+
+*/
+
+const main = async () => {
console.log("\n\n ๐ก Deploying...\n");
- const yourContract = await deploy("YourContract") // <-- add in constructor args like line 19 vvvv
+ const yourContract = await deploy("YourContract"); // <-- add in constructor args like line 19 vvvv
+ // use for local token bridging
+ // const mockToken = await deploy("MockERC20") // <-- add in constructor args like line 19 vvvv
//const yourContract = await ethers.getContractAt('YourContract', "0xaAC799eC2d00C013f1F11c37E654e59B0429DF6A") //<-- if you want to instantiate a version of a contract at a specific address!
//const secondContract = await deploy("SecondContract")
@@ -27,7 +46,6 @@ const main = async () => {
})
*/
-
/*
//If you want to send some ETH to a contract on deploy (make your constructor payable!)
const yourContract = await deploy("YourContract", [], {
@@ -35,7 +53,6 @@ const main = async () => {
});
*/
-
/*
//If you want to link a library into your contract:
// reference: https://github.com/austintgriffith/scaffold-eth/blob/using-libraries-example/packages/hardhat/scripts/deploy.js#L19
@@ -44,7 +61,6 @@ const main = async () => {
});
*/
-
//If you want to verify your contract on tenderly.co (see setup details in the scaffold-eth README!)
/*
await tenderlyVerify(
@@ -53,16 +69,6 @@ const main = async () => {
})
*/
- // If you want to verify your contract on etherscan
- /*
- console.log(chalk.blue('verifying on etherscan'))
- await run("verify:verify", {
- address: yourContract.address,
- // contract: "contracts/Example.sol:ExampleContract" // If you are inheriting from multiple contracts in yourContract.sol, you can specify which to verify
- // constructorArguments: args // If your contract has constructor arguments, you can pass them as an array
- })
- */
-
console.log(
" ๐พ Artifacts (address, abi, and args) saved to: ",
chalk.blue("packages/hardhat/artifacts/"),
@@ -70,19 +76,30 @@ const main = async () => {
);
};
-const deploy = async (contractName, _args = [], overrides = {}, libraries = {}) => {
+const deploy = async (
+ contractName,
+ _args = [],
+ overrides = {},
+ libraries = {}
+) => {
console.log(` ๐ฐ Deploying: ${contractName}`);
const contractArgs = _args || [];
- const contractArtifacts = await ethers.getContractFactory(contractName,{libraries: libraries});
+ const contractArtifacts = await ethers.getContractFactory(contractName, {
+ libraries: libraries,
+ });
const deployed = await contractArtifacts.deploy(...contractArgs, overrides);
const encoded = abiEncodeArgs(deployed, contractArgs);
fs.writeFileSync(`artifacts/${contractName}.address`, deployed.address);
- let extraGasInfo = ""
- if(deployed&&deployed.deployTransaction){
- const gasUsed = deployed.deployTransaction.gasLimit.mul(deployed.deployTransaction.gasPrice)
- extraGasInfo = `${utils.formatEther(gasUsed)} ETH, tx hash ${deployed.deployTransaction.hash}`
+ let extraGasInfo = "";
+ if (deployed && deployed.deployTransaction) {
+ const gasUsed = deployed.deployTransaction.gasLimit.mul(
+ deployed.deployTransaction.gasPrice
+ );
+ extraGasInfo = `${utils.formatEther(gasUsed)} ETH, tx hash ${
+ deployed.deployTransaction.hash
+ }`;
}
console.log(
@@ -91,14 +108,11 @@ const deploy = async (contractName, _args = [], overrides = {}, libraries = {})
"deployed to:",
chalk.magenta(deployed.address)
);
- console.log(
- " โฝ",
- chalk.grey(extraGasInfo)
- );
+ console.log(" โฝ", chalk.grey(extraGasInfo));
await tenderly.persistArtifacts({
name: contractName,
- address: deployed.address
+ address: deployed.address,
});
if (!encoded || encoded.length <= 2) return deployed;
@@ -107,7 +121,6 @@ const deploy = async (contractName, _args = [], overrides = {}, libraries = {})
return deployed;
};
-
// ------ utils -------
// abi encodes contract arguments
@@ -131,7 +144,9 @@ const abiEncodeArgs = (deployed, contractArgs) => {
// checks if it is a Solidity file
const isSolidity = (fileName) =>
- fileName.indexOf(".sol") >= 0 && fileName.indexOf(".swp") < 0 && fileName.indexOf(".swap") < 0;
+ fileName.indexOf(".sol") >= 0 &&
+ fileName.indexOf(".swp") < 0 &&
+ fileName.indexOf(".swap") < 0;
const readArgsFile = (contractName) => {
let args = [];
@@ -146,34 +161,49 @@ const readArgsFile = (contractName) => {
};
function sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
+ return new Promise((resolve) => setTimeout(resolve, ms));
}
// If you want to verify on https://tenderly.co/
-const tenderlyVerify = async ({contractName, contractAddress}) => {
-
- let tenderlyNetworks = ["kovan","goerli","mainnet","rinkeby","ropsten","matic","mumbai","xDai","POA"]
- let targetNetwork = process.env.HARDHAT_NETWORK || config.defaultNetwork
-
- if(tenderlyNetworks.includes(targetNetwork)) {
- console.log(chalk.blue(` ๐ Attempting tenderly verification of ${contractName} on ${targetNetwork}`))
+const tenderlyVerify = async ({ contractName, contractAddress }) => {
+ let tenderlyNetworks = [
+ "kovan",
+ "goerli",
+ "mainnet",
+ "rinkeby",
+ "ropsten",
+ "matic",
+ "mumbai",
+ "xDai",
+ "POA",
+ ];
+ let targetNetwork = process.env.HARDHAT_NETWORK || config.defaultNetwork;
+
+ if (tenderlyNetworks.includes(targetNetwork)) {
+ console.log(
+ chalk.blue(
+ ` ๐ Attempting tenderly verification of ${contractName} on ${targetNetwork}`
+ )
+ );
await tenderly.persistArtifacts({
name: contractName,
- address: contractAddress
+ address: contractAddress,
});
let verification = await tenderly.verify({
- name: contractName,
- address: contractAddress,
- network: targetNetwork
- })
+ name: contractName,
+ address: contractAddress,
+ network: targetNetwork,
+ });
- return verification
+ return verification;
} else {
- console.log(chalk.grey(` ๐ง Contract verification not supported on ${targetNetwork}`))
+ console.log(
+ chalk.grey(` ๐ง Contract verification not supported on ${targetNetwork}`)
+ );
}
-}
+};
main()
.then(() => process.exit(0))
diff --git a/packages/hardhat/scripts/publish.js b/packages/hardhat/scripts/publish.js
index 6aef6943d..89b765e80 100644
--- a/packages/hardhat/scripts/publish.js
+++ b/packages/hardhat/scripts/publish.js
@@ -1,98 +1,80 @@
const fs = require("fs");
const chalk = require("chalk");
-const bre = require("hardhat");
+const graphDir = "../subgraph";
+const deploymentsDir = "./deployments";
const publishDir = "../react-app/src/contracts";
-const graphDir = "../subgraph"
-function publishContract(contractName) {
- console.log(
- " ๐ฝ Publishing",
- chalk.cyan(contractName),
- "to",
- chalk.gray(publishDir)
- );
+function publishContract(contractName, networkName) {
try {
let contract = fs
- .readFileSync(`${bre.config.paths.artifacts}/contracts/${contractName}.sol/${contractName}.json`)
- .toString();
- const address = fs
- .readFileSync(`${bre.config.paths.artifacts}/${contractName}.address`)
+ .readFileSync(`${deploymentsDir}/${networkName}/${contractName}.json`)
.toString();
contract = JSON.parse(contract);
- let graphConfigPath = `${graphDir}/config/config.json`
- let graphConfig
+ const graphConfigPath = `${graphDir}/config/config.json`;
+ let graphConfig;
try {
if (fs.existsSync(graphConfigPath)) {
- graphConfig = fs
- .readFileSync(graphConfigPath)
- .toString();
+ graphConfig = fs.readFileSync(graphConfigPath).toString();
} else {
- graphConfig = '{}'
- }
- } catch (e) {
- console.log(e)
+ graphConfig = "{}";
}
+ } catch (e) {
+ console.log(e);
+ }
- graphConfig = JSON.parse(graphConfig)
- graphConfig[contractName + "Address"] = address
- fs.writeFileSync(
- `${publishDir}/${contractName}.address.js`,
- `module.exports = "${address}";`
- );
- fs.writeFileSync(
- `${publishDir}/${contractName}.abi.js`,
- `module.exports = ${JSON.stringify(contract.abi, null, 2)};`
- );
- fs.writeFileSync(
- `${publishDir}/${contractName}.bytecode.js`,
- `module.exports = "${contract.bytecode}";`
- );
+ graphConfig = JSON.parse(graphConfig);
+ graphConfig[`${networkName}_${contractName}Address`] = contract.address;
- const folderPath = graphConfigPath.replace("/config.json","")
- if (!fs.existsSync(folderPath)){
+ const folderPath = graphConfigPath.replace("/config.json", "");
+ if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath);
}
+ fs.writeFileSync(graphConfigPath, JSON.stringify(graphConfig, null, 2));
+ if (!fs.existsSync(`${graphDir}/abis`)) fs.mkdirSync(`${graphDir}/abis`);
fs.writeFileSync(
- graphConfigPath,
- JSON.stringify(graphConfig, null, 2)
- );
- fs.writeFileSync(
- `${graphDir}/abis/${contractName}.json`,
+ `${graphDir}/abis/${networkName}_${contractName}.json`,
JSON.stringify(contract.abi, null, 2)
);
- console.log(" ๐ Published "+chalk.green(contractName)+" to the frontend.")
+ //Hardhat Deploy writes a file with all ABIs in react-app/src/contracts/contracts.json
+ //If you need the bytecodes and/or you want one file per ABIs, un-comment the following block.
+ //Write the contracts ABI, address and bytecodes in case the front-end needs them
+ // fs.writeFileSync(
+ // `${publishDir}/${contractName}.address.js`,
+ // `module.exports = "${contract.address}";`
+ // );
+ // fs.writeFileSync(
+ // `${publishDir}/${contractName}.abi.js`,
+ // `module.exports = ${JSON.stringify(contract.abi, null, 2)};`
+ // );
+ // fs.writeFileSync(
+ // `${publishDir}/${contractName}.bytecode.js`,
+ // `module.exports = "${contract.bytecode}";`
+ // );
return true;
} catch (e) {
- if(e.toString().indexOf("no such file or directory")>=0){
- console.log(chalk.yellow(" โ ๏ธ Can't publish "+contractName+" yet (make sure it getting deployed)."))
- }else{
- console.log(e);
- return false;
- }
+ console.log(
+ "Failed to publish " + chalk.red(contractName) + " to the subgraph."
+ );
+ console.log(e);
+ return false;
}
}
async function main() {
- if (!fs.existsSync(publishDir)) {
- fs.mkdirSync(publishDir);
- }
- const finalContractList = [];
- fs.readdirSync(bre.config.paths.sources).forEach((file) => {
- if (file.indexOf(".sol") >= 0) {
- const contractName = file.replace(".sol", "");
- // Add contract to list if publishing is successful
- if (publishContract(contractName)) {
- finalContractList.push(contractName);
+ const directories = fs.readdirSync(deploymentsDir);
+ directories.forEach(function (directory) {
+ const files = fs.readdirSync(`${deploymentsDir}/${directory}`);
+ files.forEach(function (file) {
+ if (file.indexOf(".json") >= 0) {
+ const contractName = file.replace(".json", "");
+ publishContract(contractName, directory);
}
- }
+ });
});
- fs.writeFileSync(
- `${publishDir}/contracts.js`,
- `module.exports = ${JSON.stringify(finalContractList)};`
- );
+ console.log("โ Published contracts to the subgraph package.");
}
main()
.then(() => process.exit(0))
diff --git a/packages/react-app/package.json b/packages/react-app/package.json
index b12bc92d3..ad988d414 100644
--- a/packages/react-app/package.json
+++ b/packages/react-app/package.json
@@ -17,13 +17,6 @@
"dependencies": {
"@ant-design/icons": "^4.2.2",
"@apollo/react-hooks": "^4.0.0",
- "@ethersproject/address": "^5.0.5",
- "@ethersproject/bignumber": "^5.0.8",
- "@ethersproject/bytes": "^5.0.5",
- "@ethersproject/contracts": "^5.0.5",
- "@ethersproject/providers": "^5.0.12",
- "@ethersproject/solidity": "^5.0.9",
- "@ethersproject/units": "^5.0.6",
"@ramp-network/ramp-instant-sdk": "^2.2.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
@@ -35,12 +28,12 @@
"apollo-boost": "^0.4.9",
"apollo-client": "^2.6.10",
"apollo-utilities": "^1.3.4",
+ "arb-ts": "^0.0.18",
"axios": "^0.20.0",
"bnc-notify": "^1.5.0",
- "burner-provider": "^1.0.38",
"dotenv": "^8.2.0",
"eth-hooks": "^1.1.2",
- "ethers": "^5.0.31",
+ "ethers": "^5.3.0",
"graphiql": "^1.0.5",
"graphql": "^15.3.0",
"isomorphic-fetch": "^3.0.0",
@@ -87,6 +80,7 @@
"scripts": {
"build": "react-scripts build",
"eject": "react-scripts eject",
+ "prestart": "node ./scripts/create_contracts.js",
"start": "react-scripts start",
"test": "react-scripts test",
"lint": "eslint --config ./.eslintrc.js --ignore-path ./.eslintignore ./src/**/*",
diff --git a/packages/react-app/scripts/create_contracts.js b/packages/react-app/scripts/create_contracts.js
new file mode 100644
index 000000000..539d2de93
--- /dev/null
+++ b/packages/react-app/scripts/create_contracts.js
@@ -0,0 +1,11 @@
+const fs = require("fs");
+
+if (!fs.existsSync("./src/contracts/hardhat_contracts.json")) {
+ try {
+ fs.writeFileSync("./src/contracts/hardhat_contracts.json", JSON.stringify({}));
+
+ console.log("src/contracts/hardhat_contracts.json created.");
+ } catch (error) {
+ console.log(error);
+ }
+}
diff --git a/packages/react-app/src/App.jsx b/packages/react-app/src/App.jsx
index b01456f18..2f93ab3c8 100644
--- a/packages/react-app/src/App.jsx
+++ b/packages/react-app/src/App.jsx
@@ -1,15 +1,12 @@
-import { StaticJsonRpcProvider, Web3Provider } from "@ethersproject/providers";
-import { formatEther, parseEther } from "@ethersproject/units";
import WalletConnectProvider from "@walletconnect/web3-provider";
import { Alert, Button, Col, Menu, Row } from "antd";
import "antd/dist/antd.css";
-import { useUserAddress } from "eth-hooks";
import React, { useCallback, useEffect, useState } from "react";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
import Web3Modal from "web3modal";
import "./App.css";
import { Account, Contract, Faucet, GasGauge, Header, Ramp, ThemeSwitch } from "./components";
-import { DAI_ABI, DAI_ADDRESS, INFURA_ID, NETWORK, NETWORKS } from "./constants";
+import { INFURA_ID, NETWORK, NETWORKS } from "./constants";
import { Transactor } from "./helpers";
import {
useBalance,
@@ -17,13 +14,14 @@ import {
useContractReader,
useEventListener,
useExchangePrice,
- useExternalContractLoader,
useGasPrice,
useOnBlock,
- useUserProvider,
+ useUserSigner,
} from "./hooks";
// import Hints from "./Hints";
import { ExampleUI, Hints, Subgraph } from "./views";
+
+const { ethers } = require("ethers");
/*
Welcome to ๐ scaffold-eth !
@@ -48,6 +46,7 @@ const targetNetwork = NETWORKS.localhost; // <------- select your target fronten
// ๐ฌ Sorry for all the console logging
const DEBUG = true;
+const NETWORKCHECK = false;
// ๐ฐ providers
if (DEBUG) console.log("๐ก Connecting to Mainnet Ethereum");
@@ -56,8 +55,8 @@ if (DEBUG) console.log("๐ก Connecting to Mainnet Ethereum");
//
// attempt to connect to our own scaffold eth rpc and if that fails fall back to infura...
// Using StaticJsonRpcProvider as the chainId won't change see https://github.com/ethers-io/ethers.js/issues/901
-const scaffoldEthProvider = new StaticJsonRpcProvider("https://rpc.scaffoldeth.io:48544");
-const mainnetInfura = new StaticJsonRpcProvider("https://mainnet.infura.io/v3/" + INFURA_ID);
+const scaffoldEthProvider = new ethers.providers.StaticJsonRpcProvider("https://rpc.scaffoldeth.io:48544");
+const mainnetInfura = new ethers.providers.StaticJsonRpcProvider("https://mainnet.infura.io/v3/" + INFURA_ID);
// ( โ ๏ธ Getting "failed to meet quorum" errors? Check your INFURA_I
// ๐ Your local provider is usually pointed at your local blockchain
@@ -65,7 +64,7 @@ const localProviderUrl = targetNetwork.rpcUrl;
// as you deploy to other networks you can set REACT_APP_PROVIDER=https://dai.poa.network in packages/react-app/.env
const localProviderUrlFromEnv = process.env.REACT_APP_PROVIDER ? process.env.REACT_APP_PROVIDER : localProviderUrl;
if (DEBUG) console.log("๐ Connecting to provider:", localProviderUrlFromEnv);
-const localProvider = new StaticJsonRpcProvider(localProviderUrlFromEnv);
+const localProvider = new ethers.providers.StaticJsonRpcProvider(localProviderUrlFromEnv);
// ๐ญ block explorer URL
const blockExplorer = targetNetwork.blockExplorer;
@@ -97,23 +96,34 @@ function App(props) {
const mainnetProvider = scaffoldEthProvider && scaffoldEthProvider._network ? scaffoldEthProvider : mainnetInfura;
const [injectedProvider, setInjectedProvider] = useState();
+ const [address, setAddress] = useState();
/* ๐ต This hook will get the price of ETH from ๐ฆ Uniswap: */
const price = useExchangePrice(targetNetwork, mainnetProvider);
/* ๐ฅ This hook will get the price of Gas from โฝ๏ธ EtherGasStation */
const gasPrice = useGasPrice(targetNetwork, "fast");
// Use your injected provider from ๐ฆ Metamask or if you don't have it then instantly generate a ๐ฅ burner wallet.
- const userProvider = useUserProvider(injectedProvider, localProvider);
- const address = useUserAddress(userProvider);
+ const userSigner = useUserSigner(injectedProvider, localProvider);
+
+ useEffect(() => {
+ async function getAddress() {
+ if (userSigner) {
+ const newAddress = await userSigner.getAddress();
+ setAddress(newAddress);
+ }
+ }
+ getAddress();
+ }, [userSigner]);
// You can warn the user if you would like them to be on a specific network
const localChainId = localProvider && localProvider._network && localProvider._network.chainId;
- const selectedChainId = userProvider && userProvider._network && userProvider._network.chainId;
+ const selectedChainId =
+ userSigner && userSigner.provider && userSigner.provider._network && userSigner.provider._network.chainId;
// For more hooks, check out ๐eth-hooks at: https://www.npmjs.com/package/eth-hooks
// The transactor wraps transactions and provides notificiations
- const tx = Transactor(userProvider, gasPrice);
+ const tx = Transactor(userSigner, gasPrice);
// Faucet Tx can be used to send funds from the faucet
const faucetTx = Transactor(localProvider, gasPrice);
@@ -127,13 +137,13 @@ function App(props) {
// Load in your local ๐ contract and read a value from it:
const readContracts = useContractLoader(localProvider);
- // If you want to make ๐ write transactions to your contracts, use the userProvider:
- const writeContracts = useContractLoader(userProvider);
+ // If you want to make ๐ write transactions to your contracts, use the userSigner:
+ const writeContracts = useContractLoader(userSigner, { chainId: localChainId });
// EXTERNAL CONTRACT EXAMPLE:
//
// If you want to bring in the mainnet DAI contract it would look like:
- const mainnetDAIContract = useExternalContractLoader(mainnetProvider, DAI_ADDRESS, DAI_ABI);
+ const mainnetContracts = useContractLoader(mainnetProvider);
// If you want to call a function on a new block
useOnBlock(mainnetProvider, () => {
@@ -141,7 +151,7 @@ function App(props) {
});
// Then read your DAI balance like:
- const myMainnetDAIBalance = useContractReader({ DAI: mainnetDAIContract }, "DAI", "balanceOf", [
+ const myMainnetDAIBalance = useContractReader(mainnetContracts, "DAI", "balanceOf", [
"0x34aA3F359A9D614239015126635CE7732c18fDF3",
]);
@@ -169,17 +179,18 @@ function App(props) {
yourMainnetBalance &&
readContracts &&
writeContracts &&
- mainnetDAIContract
+ mainnetContracts
) {
console.log("_____________________________________ ๐ scaffold-eth _____________________________________");
console.log("๐ mainnetProvider", mainnetProvider);
console.log("๐ localChainId", localChainId);
console.log("๐ฉโ๐ผ selected address:", address);
console.log("๐ต๐ปโโ๏ธ selectedChainId:", selectedChainId);
- console.log("๐ต yourLocalBalance", yourLocalBalance ? formatEther(yourLocalBalance) : "...");
- console.log("๐ต yourMainnetBalance", yourMainnetBalance ? formatEther(yourMainnetBalance) : "...");
+ console.log("๐ต yourLocalBalance", yourLocalBalance ? ethers.utils.formatEther(yourLocalBalance) : "...");
+ console.log("๐ต yourMainnetBalance", yourMainnetBalance ? ethers.utils.formatEther(yourMainnetBalance) : "...");
console.log("๐ readContracts", readContracts);
- console.log("๐ DAI contract on mainnet:", mainnetDAIContract);
+ console.log("๐ DAI contract on mainnet:", mainnetContracts);
+ console.log("๐ต yourMainnetDAIBalance", myMainnetDAIBalance);
console.log("๐ writeContracts", writeContracts);
}
}, [
@@ -190,11 +201,11 @@ function App(props) {
yourMainnetBalance,
readContracts,
writeContracts,
- mainnetDAIContract,
+ mainnetContracts,
]);
let networkDisplay = "";
- if (localChainId && selectedChainId && localChainId !== selectedChainId) {
+ if (NETWORKCHECK && localChainId && selectedChainId && localChainId !== selectedChainId) {
const networkSelected = NETWORK(selectedChainId);
const networkLocal = NETWORK(localChainId);
if (selectedChainId === 1337 && localChainId === 31337) {
@@ -241,7 +252,23 @@ function App(props) {
const loadWeb3Modal = useCallback(async () => {
const provider = await web3Modal.connect();
- setInjectedProvider(new Web3Provider(provider));
+ setInjectedProvider(new ethers.providers.Web3Provider(provider));
+
+ provider.on("chainChanged", chainId => {
+ console.log(`chain changed to ${chainId}! updating providers`);
+ setInjectedProvider(new ethers.providers.Web3Provider(provider));
+ });
+
+ provider.on("accountsChanged", () => {
+ console.log(`account changed!`);
+ setInjectedProvider(new ethers.providers.Web3Provider(provider));
+ });
+
+ // Subscribe to session disconnection
+ provider.on("disconnect", (code, reason) => {
+ console.log(code, reason);
+ logoutOfWeb3Modal();
+ });
}, [setInjectedProvider]);
useEffect(() => {
@@ -256,7 +283,7 @@ function App(props) {
}, [setRoute]);
let faucetHint = "";
- const faucetAvailable = localProvider && localProvider.connection && targetNetwork.name === "localhost";
+ const faucetAvailable = localProvider && localProvider.connection && targetNetwork.name.indexOf("local") !== -1;
const [faucetClicked, setFaucetClicked] = useState(false);
if (
@@ -265,7 +292,7 @@ function App(props) {
localProvider._network &&
localProvider._network.chainId === 31337 &&
yourLocalBalance &&
- formatEther(yourLocalBalance) <= 0
+ ethers.utils.formatEther(yourLocalBalance) <= 0
) {
faucetHint = (
Your Balance: {yourLocalBalance ? formatEther(yourLocalBalance) : "..."}
+ {/* use utils.formatEther to display a BigNumber: */}
+
Your Balance: {yourLocalBalance ? utils.formatEther(yourLocalBalance) : "..."}
OR
๐ณ Example Whale Balance:
-
+
- {/* use formatEther to display a BigNumber: */}
-
Your Balance: {yourLocalBalance ? formatEther(yourLocalBalance) : "..."}
+ {/* use utils.formatEther to display a BigNumber: */}
+
Your Balance: {yourLocalBalance ? utils.formatEther(yourLocalBalance) : "..."}
Your Contract Address:
@@ -110,7 +110,7 @@ export default function ExampleUI({
*/
tx({
to: writeContracts.YourContract.address,
- value: parseEther("0.001"),
+ value: utils.parseEther("0.001"),
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}
@@ -124,7 +124,7 @@ export default function ExampleUI({
/* look how we call setPurpose AND send some value along */
tx(
writeContracts.YourContract.setPurpose("๐ต Paying for this one!", {
- value: parseEther("0.001"),
+ value: utils.parseEther("0.001"),
}),
);
/* this will fail until you make the setPurpose function payable */
@@ -139,7 +139,7 @@ export default function ExampleUI({
/* you can also just craft a transaction and send it to the tx() transactor */
tx({
to: writeContracts.YourContract.address,
- value: parseEther("0.001"),
+ value: utils.parseEther("0.001"),
data: writeContracts.YourContract.interface.encodeFunctionData("setPurpose(string)", [
"๐ค Whoa so 1337!",
]),
@@ -164,7 +164,7 @@ export default function ExampleUI({
renderItem={item => {
return (
- =>
+
{item[1]}
);
diff --git a/packages/react-app/src/views/Hints.jsx b/packages/react-app/src/views/Hints.jsx
index cf2e44634..a1b6a2a1b 100644
--- a/packages/react-app/src/views/Hints.jsx
+++ b/packages/react-app/src/views/Hints.jsx
@@ -1,6 +1,6 @@
/* eslint-disable jsx-a11y/accessible-emoji */
-import { formatEther } from "@ethersproject/units";
+import { utils } from "ethers";
import { Select } from "antd";
import React, { useState } from "react";
import { Address, AddressInput } from "../components";
@@ -94,7 +94,7 @@ export default function Hints({ yourLocalBalance, mainnetProvider, price, addres
>
useBalance()
{" "}
- hook keeps track of your balance: {formatEther(yourLocalBalance || 0)}
+ hook keeps track of your balance: {utils.formatEther(yourLocalBalance || 0)}
- ๐ Check out your browser's developer console for more... (inpect -> console) ๐
+ ๐ Check out your browser's developer console for more... (inspect console) ๐