Skip to content

Commit

Permalink
Feature/PP-278/PP-329/tasks-over-scripts (#75)
Browse files Browse the repository at this point in the history
* feat: implement hardhat tasks

* chore: lint

* chore: no .vscode folder

* fix: typo on function, relocate map creation

* chore: lint

Co-authored-by: Christos Otarola <christos@Christoss-MacBook-Pro.local>
  • Loading branch information
ironFe93 and Christos Otarola committed Oct 13, 2022
1 parent 3aa7137 commit 27f6402
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 80 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ artifacts
/build
/dist
/contracts-exposed

.vscode
6 changes: 3 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ The project is ready to be used at this point.

The contracts can be deployed in the following way:

1. Configure the `truffle.js` file on the root of the project to set your network
2. Run `npx truffle migrate --network <NETWORK_NAME>`
1. Configure the `hardhat.config.ts` file on the root of the project to set your network
2. Run `npm run deploy --network <NETWORK_NAME>`

This will start the migration on `<NETWORK_NAME>`; at the end of it you should see a summary with all the contract addresses.

Expand All @@ -62,7 +62,7 @@ This file also is being exported on the distributable version to provide the con

Once the smart contracts are deployed, tokens must be individually allowed to be able to work with the RIF Relay system. There are some helpful commands for this:

1. To allow a specific token, run `npm run allowTokens <TOKEN_ADDRESSES> <NETWORK_NAME>` where:
1. To allow a specific token, run `npm run allow-tokens <NETWORK_NAME> <TOKEN_ADDRESSES>` where:
- `<TOKEN_ADDRESSES>` is a comma-separated list of the token addresses to be allowed on the available verifiers
- `<NETWORK_NAME>` is an optional parameter for the network name, taken from the `truffle.js` file (default value is `regtest`) **important! This should be the same network name as the one used to deploy the contracts**
2. To query allowed tokens run `npm run allowedTokens <NETWORK_NAME>`. This will display them on the console.
Expand Down
1 change: 1 addition & 0 deletions contract-addresses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"hardhat.31337":{"Penalizer":"0x5FbDB2315678afecb367f032d93F642f64180aa3","RelayHub":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512","SmartWallet":"0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0","SmartWalletFactory":"0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9","DeployVerifier":"0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9","RelayVerifier":"0x5FC8d32690cc91D4c39d9d3abcBD16989F875707","CustomSmartWallet":"0x0165878A594ca255338adfa4d48449f69242Eb8F","CustomSmartWalletFactory":"0xa513E6E4b8f2a923D98304ec87F64353C4D5C853","CustomSmartWalletDeployVerifier":"0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6","UtilToken":"0x610178dA211FEF7D417bC0e6FeD39F05609AD788","VersionRegistry":"0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"},"regtest.33":{"Penalizer":"0x77A151FE2Ac2300Ccc89FD00330Add038e6e6B45","RelayHub":"0x05840bf34268900b255eFdeAb4E8b18A74Ec822E","SmartWallet":"0xE66d6095193f9Bec61F46d47EB2AC3C4315b01F1","SmartWalletFactory":"0x55898Faccf2D03C6B4d0564b33cC6D397Ca32A17","DeployVerifier":"0xe3d09A82a6f07Ca1B4C6964866075473531E426E","RelayVerifier":"0x9F54ffdB097e4c87C539bcA6DBaeE4BA5F74700C","CustomSmartWallet":"0x4F64cCeC8e2692Bf674f5EAa4E0170B14db8e709","CustomSmartWalletFactory":"0x151A4b94c288a6be9eE48d63949b1d44Dc4e2999","CustomSmartWalletDeployVerifier":"0x091E9C08dA594c831002309E139389d8eCa2A99e","UtilToken":"0xB3647664cD22BcC7B1defEbD3767Df9a0B3E4009","VersionRegistry":"0xBa755AEF160f11185Fc2726DAdd9D3B029aEE50f"}}
20 changes: 18 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import '@nomicfoundation/hardhat-toolbox';
import 'hardhat-contract-sizer';
import 'hardhat-docgen';
import 'hardhat-watcher';
import { HardhatUserConfig } from 'hardhat/config';
import '@nomiclabs/hardhat-ethers';
import { allowTokens } from './scripts/allowTokens';
import { deploy } from './scripts/deploy';
import { HardhatUserConfig, task } from 'hardhat/config';

const config: HardhatUserConfig = {
solidity: {
Expand Down Expand Up @@ -68,8 +71,21 @@ const config: HardhatUserConfig = {
runOnCompile: false,
},
mocha: {
timeout: 20000
timeout: 20000,
},
};

task('deploy', 'Deploys rif-relay contracts to selected network')
.setAction(async (args, hre) => {
await deploy(hre);
}
);

task('allow-tokens', 'allows a list of tokens')
.addPositionalParam('tokenlist', 'list of tokens')
.setAction(async (taskArgs: { tokenlist: string }, hre) => {
await allowTokens(taskArgs, hre);
}
);

export default config;
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@
],
"main": "dist/index.js",
"scripts": {
"allow-tokens": "hardhat allow-tokens --network",
"ci:format": "prettier -c contracts/**/*.sol && prettier -c **/*.ts",
"ci:lint": "solhint 'contracts/**/*.sol' && eslint test --ext .ts ",
"ci:test": "npm run compile && npm run test",
"clean": "rm -rf typechain-types cache bin dist artifacts contracts-exposed && hardhat clean",
"compile": "npm run clean && hardhat compile --force",
"deploy": "hardhat run scripts/deploy.ts --network",
"deploy-configure": "hardhat run scripts/deploy.ts --network",
"deploy": "hardhat deploy --network",
"deploy-configure": "hardhat deploy --network",
"docs": "hardhat docgen",
"format": "npm run format:sol && npm run format:ts",
"format:sol": "prettier -c -w --no-editorconfig contracts/**/*.sol contracts/**/**/*.sol",
Expand Down
83 changes: 83 additions & 0 deletions scripts/allowTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ContractTransaction } from 'ethers';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { getExistingConfig } from './deploy';

export const allowTokens = async (
taskArgs: { tokenlist: string },
hre: HardhatRuntimeEnvironment
) => {
const tokenAddresses = taskArgs.tokenlist.split(',');

const { ethers, network } = hre;

if (!network) {
throw new Error('Unknown Network');
}

const { chainId } = network.config;

if (!chainId) {
throw new Error('Unknown Chain Id');
}

const contractAddresses = getExistingConfig();

if (!contractAddresses) {
throw new Error('No contracts deployed');
}

const networkChainKey = `${network.name}.${chainId}`;

const deployVerifierAddress =
contractAddresses[networkChainKey].DeployVerifier;
const relayVerifierAddress = contractAddresses[networkChainKey].RelayVerifier;
const customDeployVerifierAddress =
contractAddresses[networkChainKey].CustomSmartWalletDeployVerifier;

if (!deployVerifierAddress) {
throw new Error('Could not obtain deploy verifier address');
}

if (!relayVerifierAddress) {
throw new Error('Could not obtain relay verifier address');
}

if (!customDeployVerifierAddress) {
throw new Error('Could not obtain custom deploy verifier address');
}

const deployVerifier = await ethers.getContractAt(
'DeployVerifier',
deployVerifierAddress
);
const relayVerifier = await ethers.getContractAt(
'RelayVerifier',
relayVerifierAddress
);
const customDeployVerifier = await ethers.getContractAt(
'CustomSmartWalletDeployVerifier',
customDeployVerifierAddress
);

const verifierMap: Map<
string,
{ acceptToken: (tokenAddress: string) => Promise<ContractTransaction> }
> = new Map();
verifierMap.set('deployVerifier', deployVerifier);
verifierMap.set('relayVerifier', relayVerifier);
verifierMap.set('customDeployVerifier', customDeployVerifier);

for (const tokenAddress of tokenAddresses) {
for (const [key, verifier] of verifierMap) {
try {
await verifier.acceptToken(tokenAddress);
} catch (error) {
console.error(
`Error adding token with address ${tokenAddress} to allowed tokens on ${key}`
);
throw error;
}
}
}
console.log('Tokens allowed successfully!');
};
91 changes: 34 additions & 57 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,37 @@
import {config, ethers, hardhatArguments} from 'hardhat';
import { HardhatEthersHelpers, HardhatRuntimeEnvironment } from 'hardhat/types';
import fs from 'node:fs';
import {contracts} from '../typechain-types/factories';
import { ContractAddresses, NetworkConfig } from '../utils/scripts/types';

const ADDRESS_FILE = process.env.ADDRESS_FILE || 'contract-addresses.json';

const factoryList = {
...contracts,
...contracts.factory,
...contracts.smartwallet,
...contracts.utils,
...contracts.verifier,
} as const;

export type FactoryName = Exclude<Extract<keyof typeof factoryList, `${string}__factory`>,
'Migrations__factory'>;

export type ContractName = FactoryName extends `${infer Prefix}__factory`
? Prefix
: never;

export type ContractAddresses = {
[key in ContractName]: string | undefined;
};

export type NetworkConfig = {
[key: `${number}`]: ContractAddresses;
};

export const getExistingConfig = () => {
try {
if (fs.existsSync(ADDRESS_FILE)) {
return JSON.parse(
fs.readFileSync(ADDRESS_FILE, {encoding: 'utf-8'})
) as ContractAddresses;
fs.readFileSync(ADDRESS_FILE, { encoding: 'utf-8' })
) as { [key: string]: ContractAddresses };
}
} catch (e) {
console.warn(e);
}
};

export const writeConfiigToDisk = (config: NetworkConfig) => {
export const writeConfigToDisk = (config: NetworkConfig) => {
fs.writeFileSync(ADDRESS_FILE, JSON.stringify(config));
console.log(`address file available at: ${ADDRESS_FILE}`);
};

export const updateConfig = (
contractAddresses: ContractAddresses
contractAddresses: ContractAddresses,
{ hardhatArguments, config: { networks } }: HardhatRuntimeEnvironment
): NetworkConfig => {
console.log('Generating network config...');

const {network} = hardhatArguments;
const { network } = hardhatArguments;
if (!network) {
throw new Error('Unknown Network');
}
const {chainId} = config.networks[network];
const { chainId } = networks[network];

if (!chainId) {
throw new Error('Unknown Chain Id');
Expand All @@ -64,7 +43,10 @@ export const updateConfig = (
};
};

export const deployContracts = async (): Promise<ContractAddresses> => {
export const deployContracts = async (
ethers: HardhatEthersHelpers,
networkName?: string
): Promise<ContractAddresses> => {
const relayHubF = await ethers.getContractFactory('RelayHub');
const penalizerF = await ethers.getContractFactory('Penalizer');
const smartWalletF = await ethers.getContractFactory('SmartWallet');
Expand All @@ -89,39 +71,39 @@ export const deployContracts = async (): Promise<ContractAddresses> => {
'VersionRegistry'
);

const {address: penalizerAddress} = await penalizerF.deploy();
const {address: relayHubAddress} = await relayHubF.deploy(
const { address: penalizerAddress } = await penalizerF.deploy();
const { address: relayHubAddress } = await relayHubF.deploy(
penalizerAddress,
1,
1,
1,
1
);
const {address: smartWalletAddress} = await smartWalletF.deploy();
const {address: smartWalletFactoryAddress} =
const { address: smartWalletAddress } = await smartWalletF.deploy();
const { address: smartWalletFactoryAddress } =
await smartWalletFactoryF.deploy(smartWalletAddress);
const {address: deployVerifierAddress} = await deployVerifierF.deploy(
const { address: deployVerifierAddress } = await deployVerifierF.deploy(
smartWalletFactoryAddress
);
const {address: relayVerifierAddress} = await relayVerifierF.deploy(
const { address: relayVerifierAddress } = await relayVerifierF.deploy(
smartWalletFactoryAddress
);

const {address: customSmartWalletAddress} =
const { address: customSmartWalletAddress } =
await customSmartWalletF.deploy();
const {address: customSmartWalletFactoryAddress} =
const { address: customSmartWalletFactoryAddress } =
await customSmartWalletFactoryF.deploy(customSmartWalletAddress);
const {address: customDeployVerifierAddress} =
const { address: customDeployVerifierAddress } =
await customSmartWalletDeployVerifierF.deploy(
customSmartWalletFactoryAddress
);

const {address: versionRegistryAddress} =
const { address: versionRegistryAddress } =
await versionRegistryFactory.deploy();

let utilTokenAddress;
if (hardhatArguments.network != 'mainnet') {
const {address} = await utilTokenF.deploy();
if (networkName != 'mainnet') {
const { address } = await utilTokenF.deploy();
utilTokenAddress = address;
}

Expand All @@ -140,18 +122,13 @@ export const deployContracts = async (): Promise<ContractAddresses> => {
};
};

const main = async () => {
if (process.env.TEST) return;

const contractAddresses = await deployContracts();
export const deploy = async (hre: HardhatRuntimeEnvironment) => {
const {
ethers,
hardhatArguments: { network },
} = hre;
const contractAddresses = await deployContracts(ethers, network);
console.table(contractAddresses);
const newConfig = updateConfig(contractAddresses);
writeConfiigToDisk(newConfig);
const newConfig = updateConfig(contractAddresses, hre);
writeConfigToDisk(newConfig);
};

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
65 changes: 65 additions & 0 deletions test/scripts/allowTokens.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { expect, use } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { Contract } from 'ethers';
import fs from 'fs';
import * as hre from 'hardhat';
import { ethers } from 'hardhat';
import sinon from 'sinon';
import {
allowTokens
} from '../../scripts/allowTokens';

use(chaiAsPromised);

describe('Allow Tokens Script', function () {
describe('allowTokens', function () {
const taskArgs = { tokenlist: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7' };

const contractAddresses = {
Penalizer: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
RelayHub: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
SmartWallet: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
SmartWalletFactory: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
DeployVerifier: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
RelayVerifier: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
CustomSmartWallet: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
CustomSmartWalletFactory: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
CustomSmartWalletDeployVerifier:
'0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
VersionRegistry: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
UtilToken: '0x145845fd06c85B7EA1AA2d030E1a747B3d8d15D7',
};

const chainContractAddresses = {
'hardhat.33': contractAddresses,
};

beforeEach(function () {
sinon.stub(fs, 'existsSync').returns(true);
sinon
.stub(fs, 'readFileSync')
.returns(JSON.stringify(chainContractAddresses));
hre.network.config.chainId = 33;
});

afterEach(function () {
sinon.restore();
});

it('should allow a list of tokens', async function () {
const stubContract = sinon.createStubInstance(Contract);
stubContract.acceptToken = () => undefined;
sinon.stub(ethers, 'getContractAt').resolves(stubContract);
await expect(allowTokens(taskArgs, hre)).to.not.be.rejected;
});

it('should throw error and print it if token cannot be allowed', async function () {
const stubContract = sinon.createStubInstance(Contract);
stubContract.acceptToken = () => {
throw new Error();
};
sinon.stub(ethers, 'getContractAt').resolves(stubContract);
await expect(allowTokens(taskArgs, hre)).to.be.rejected;
});
});
});
Loading

0 comments on commit 27f6402

Please sign in to comment.