This folder contains all the tools to manage the Unlock Protocol (contracts, DAO, etc).
To get the list of all available tasks use yarn hardhat --help
.
First add your network to the list of supported networks:
- create a
<your-network>.ts
file topackages/networks/src
- add
export * from './<your-network>'
topackages/networks/src/index.ts
Then you will need to edit the values for chain id, block explorer URLs, etc.
When you are done, rebuild the networks package with
yarn workspace @unlock-protocol/networks build
- add a
<your-network>
key to theapiKey
object inpackages/hardhat-helpers/src/etherscan.js
- optionally can add support for env variable
When you are done, rebuild the helpers package with
yarn workspace @unlock-protocol/hardhat-helpers build
Now you need to deploy and configure the two main contracts
- The factory contract
Unlock
- The template contract
PublicLock
This can be done with the following command:
yarn hardhat deploy --unlock-version 13 --public-lock-version 14 --network <your-network>
You can resume a pre-existing deployment by using the contract addresses instead of version number with the --unlock-address
or --public-lock-address
.
Once the deployments are done, you need to add to the packages/networks/src/<your-network>.ts
package
unlockAddress
: the address of the deployed Unlock contractstartBlock
: the block number to start indexing data (usually right before Unlock contract creation)
The contracts should have been configured correctly during the deployment step. However you do the configuration manually
yarn hardhat set:template
Run configUnlock
on the Unlock contract with the following params
yarn hardhat set:unlock-config
-
First you will need to create a new graph on The Graph studio.
-
In the
packages/networks/src/<your-network>.ts
config file, fill thesubgraph
object as follow:
subgraph: {
endpoint: '<>', // this is given to you by the graph after deploying
networkName: 'base-sepolia', // the graph name of the network see https://thegraph.com/docs/en/developing/supported-networks/
studioName: 'unlock-protocol-<your-network>', // the name of the graph
},
- Rebuild the networks package
yarn workspace @unlock-protocol/networks build
- Deploy the graph by using the following commands
# got to the subgraph folder
cd subgraph
# create the subgraph.yaml and generate the files
yarn build <your-network>
yarn deploy <your-subgraph-name>
The graph is now deployed. Add the URL that is shown to the network file.
Contracts can be verified on all supported networks using the command line.
# example
yarn hardhat verify <address> --network <your-network>
There is a dedicated script to verify the proxy used by the Unlock factory contract to create locks. You need to pass the hash of the tx creation as a param to the following function.
yarn hardhat verify-proxy
Forks on Tenderly can be very useful to test behaviours of new settings and contracts before actually deploying them. A fork of a network allows to simulate the execution of multiple successive txs - which is very useful while working on a batch of changes.
export TENDERLY_FORK=https://rpc.tenderly.co/fork/xxx-xxx-xxx-xx
# then use any command with tenderly as a network
yarn hardhat deploy:template --network tenderly
- First, create a file in the
proposals
folder that describes the proposal itself. The file should contain either a js async function or a literal object and should export as default an object containing: 1. the title of the proposal and 2. an array with the description of the calls to send - in following format:
module.exports = {
proposalName: `My Proposal`, // a string describing the proposal
calls: [
{
contractNameOrAbi, // the contract name or an ABI - ex. `UnlockDiscountTokenV3`
functionName, // the name or signature of the function to be executed - `transfer`
functionArgs, // the args of the function - ex. [ 0x0000..., 10000 ]
value, // (optional) payable value in native tokens
},
],
}
check ./proposals/000-example.js
for an example.
When using an async function to parse a proposal, you can pass params to the function through positional args in cli scripts.
CLI call
RUN_FORK=8453 yarn hardhat gov --gov-address 0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9 \
--proposal proposals/up/my-proposal.js
0x000000 1000
JS async proposal
import { ethers } from 'hardhat'
module.exports = async function (params) {
// unpack params from the command line i.e. [0x00000, 1000]
const [myAddress, myAmount] = params
// do whatever you need with await calls and your params...
const contract = await ethers.getContractAt('MyContract', myAddress)
const amount = await contract.someFunc(myAmount)
// the returned format
return {
proposalName: `My async proposal`, // a string describing the proposal
calls: [
{
contractNameOrAbi, // the contract name or an ABI - ex. `UnlockDiscountTokenV3`
functionName, // the name or signature of the function to be executed - `transfer`
functionArgs: [amount], // the args of the function - ex. [ 0x0000..., 10000 ]
value, // (optional) payable value in native tokens
},
],
}
}
check ./proposals/002-set-protocol-fee.js
for an example.
- Test your proposal locally on a base fork
RUN_FORK=8453 yarn hardhat gov --gov-address 0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9 \
--proposal proposals/up/<your-proposal>.js
Additionally, you can pass arguments to your proposal script via CLI positional args.
- When things are ready, post it to the DAO!
yarn hardhat gov:submit --proposal proposals/<your-proposal>.js --network base
- Head to Tally to see your proposal. NB: this may take some time as it requires X block confirmations
Edit the pool fee directly in the script
yarn run scripts/uniswap/createPool.js
Edit directly the amounts and prices in the script
yarn run scripts/uniswap/addLiquidity.js
To maintain the integrity of the protocol across various chains, we use a pattern of DAO proposals that allows execution on multiple chains. Messaging is sent across the Connext bridge to all supported chains.
Read the explanations and follow the template in ./proposals/006-cross-bridge-proposal.js
to submit a cross-chain proposal to the DAO.
To make sure all calls can be executed properly, you can use Tenderly forks to test execution of calls on each destination chains.
There is test DAO deployed with the entire cross-chain pipeline configured between Gnosis chain and Polygon mainnet - available at 0x530ff2daed410ca7d70c25f18dc770f106201151. The address of the multisig on destination (Polygon) is 0x6ff837695B120A8f533498584249131F1c6fC4a8
A test version of the Unlock factory contract is deployed on Polygon at 0x2411336105D4451713d23B5156038A48569EcE3a and can be used for testing cross-chain proposals execution.
Once the proposal has been through the timelock embargo period and has been executed on the DAO, there are still a few steps for all calls to cross the bridges and get executed properly on all destination chains. These steps can be executed by anyone.
- pay the Connext bridge fee for all the calls
- wait for the calls to cross the bridge (usually ~2-5h)
- wait for the safe (multisig) cooldown period (2 days)
- execute the calls on each chain
To execute these commands, you will need the hash from the transaction that executed the proposal on the DAO. The transaction contains the IDs of the bridge calls that will be used by the scripts to fetch information.
NB: On Tally.xyz, an etherscan link containing the hash can be found on the upper left button next to Proposal Executed.
export PROPOSAL_EXECUTION_TX=<0x....>
Calls for each chains are sent from the proposal separately to the bridge of the destination chain. A bridge fee needs to be paid on origin chain for the txs to proceed and cross to the other side.
Instead of having to pay each fee separately, we batch all fee payments in a single multicall executed by a Safe. To create the batched call in a multisig, you can use the script below. Then you need to go the multisig and execute it.
# set the execution tx id
export PROPOSAL_EXECUTION_TX=<0x....>
# create a batched multisig tx to pay the bridge fees
yarn hardhat run scripts/bridge/payFee.js --network mainnet
You can check the status of all calls on various chains manually with the Connext explorer or directly parse calls from the execution tx using the script below. You will need an API key from The Graph to query the Connext bridge subgraph and get the status of all transactions.
# set the execution tx id
export PROPOSAL_EXECUTION_TX=<0x....>
# export the subgraph api key
export SUBGRAPH_QUERY_API_KEY=<...>
# check the status of the calls on all bridges
yarn hardhat run scripts/bridge/status.js --network mainnet
NB: This will create a temporary JSON file named xcalled.tmp.json
with the info and statuses of all tx.
Once all calls have crossed the bridges they stay in cooldown in multisigs. Once cooldown ends, they can be executed. To execute the calls, use the following command for each network:
yarn hardhat run scripts/bridge/execTx.js --network optimism
NB: The tmp file with all txs statuses is required, so you need to first run the "Check status" step above
As the Connext bridge supports more networks, they need to be added to the cross chain gov process (see supported networks on Connext docs).
The process is as follow:
- deploy the delayMod and connextMod for the Safe multisig by running
yarn hardhat run scripts/multisig/deployMods.js --network <network_name>
- add both modules addresses to the
dao.governanceBridge
section in the networks package file (see an example) - setup modules correctly in network SAFE by running the command
yarn hardhat safe:submit --proposal proposals/enableModule.js --network <xxx>