What happens when you combine nextjs + hardhat + gemcutter + typechain + useDapp
Gemcutter is a simple way to work on EIP-2545 Diamonds
The diamond.json is a temporary file that rappresents the state of the deployed diamond. The developer edits the diamond.json file and then uses gemcutter's tool to synchronize the changes to the deployed diamond. Gemcutter offers a series of functions to work with the diamond.json.
The diamond.json is not committed on github because its values depends on the local development environment. Instead the user will commit a DIAMONDFILE, a file containing all the operations used to generate the same diamond.json in every dev env.
npx hardhat diamond:init
Generates a diamond.json file starting from a DIAMONDFILE
npx hardhat diamond:deploy
Used to initialize a new diamond, deploying an empy diamond, and creating a diamond.json that rappresents that diamond
npx hardhat diamond:add --remote --save-metadata --address 0x3D004D6B32D5D6A5e7B7504c3dac672456ed95dB
Used to add a facet to the diamond.json file (not on the deployed contract).
--remote
to add a remote facet,--address
is mandatory. With--save-metadata
the metadata of the contracts are extracted from the bytecode and saved in the ./metadata/ folder.--local
to add a local facet,--name
is mandatory (the name of the facet in the ./contracts/ folder)
npx hardhat diamond:cut --init-facet MyToken --init-fn initMyToken --init-params "Habitat,HBT,8,0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
Used to synchronize the changes between the local diamond.json file and the deployed diaomond.
--init-facet
used to specify the facet from which to use the function to pass to the _init function in the cut.--init-fn
the function to pass to the _init function in the cut--init-params
the parameters to pass to the _init function in the cut
The DIAMONDFILE contains the set of instructions to deploy a diamond and cut all the necessary facets. While developing the developer will call diamond:add
and diamond:cut
but efore committing the developer must update the DIAMONDFILE so it includes all the adds and cuts she made.
Everything works only when the hardhat node
is running. If the developer publishes new facets with the --local
flag, each time it reruns the local node, it must recreate the diamond.json file redeploying the diamond on the local network.
Right now the basic facets (DiamondCutFacet, DiamondInit, DiamondLoupeFacet, OwnershipFacet) are deployed on rinkeby, in the future the system will use create2 to have always the same address so it will not be mandatory to fork rinkeby to work with Gemx.js.
Also in the future the diamon:add
task will have a --ens
parameter so the developer can add facets passing a meaningful name.
E.g.
npx hardhat diamond:add --remote --save-metadata --ens erc20.facets.eth
yarn start:node
yarn diamond:init
yarn diamond:add 0x3D004D6B32D5D6A5e7B7504c3dac672456ed95dB
yarn generate:typechain
When installing a facet the useDapp hooks are automatically available for the user, for example when add a ERC20 facet, this hooks will be available for the developer
const { account, library, activateBrowserWallet } = useEthers()
const useDiamond = generateUseDiamond(diamond.address,account ? (library?.getSigner() as any) : undefined)
const balance = useDiamond.balanceOf('0xDEC3e07D46c46C089a323D62E60826D03716d7a2')
const { state: stateInit, send: sendInitMyToken } = useDiamond.initMyToken
const initToken = () => sendInitMyToken('SD','sd',8,'0xDEC3e07D46c46C089a323D62E60826D03716d7a2')
When you add a facet using the flag --save-metadata
the metadata of the facet is saved, so it can be used by typechain to generate the factory for that facet. You can use the following command or the shortcut above.
typechain --target=ethers-v5 metadata/*.json
Gemx offers generateUseDiamond
, that automatically creates the useCall
and useContractFunction
for each function in the typechains factory specified.
In the util change the factories
object, including the factories to include
const factories: IFactories = {
MyToken: MyToken__factory,
}
First import the generateUseDiamond
util in the component.
import generateUseDiamond from '../utils/generateUseDiamond'
Finally call generateUseDiamond
passing
- the address of the diamond (you can take it using
import diamond from '../diamond.json'
) - the signer
const { account, library, activateBrowserWallet } = useEthers()
const useDiamond = generateUseDiamond(diamond.address,account ? (library?.getSigner() as any) : undefined)
In the useDiamond object you have:
- the useCall hooks for the view functions
const balance = useDiamond.balanceOf('0xDEC3e07D46c46C089a323D62E60826D03716d7a2')
- the useContractFunction hooks for the write functions
const { state: stateInit, send: sendInitMyToken } = useDiamond.initMyToken
- Install dependencies
yarn install
- Run local node (each time)
yarn node:start
- Deploy the diamond on the local environment (each time if working with new local facets)
yarn diamond:init
- Run next.js
yarn dev