Skip to content

Commit

Permalink
feat: added vault factory, refactored app
Browse files Browse the repository at this point in the history
  • Loading branch information
ev-d committed Dec 19, 2024
1 parent 5ae8b83 commit da76599
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 46 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.env
.env.local
.env.production
.env.development
.env

# dependencies
/node_modules
Expand All @@ -11,6 +11,7 @@
.yalc
yalc.lock
tsconfig.tsbuildinfo
./deployed.json

# production
/build
Expand Down
110 changes: 86 additions & 24 deletions configs/deployed.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { lstatSync } from "fs";
import { zeroAddress, Address } from "viem";
import {zeroAddress, Address, Chain} from "viem";
import { resolve } from "path";
import { envs } from "./envs";
import { getValueByPath } from "@utils";
import {getValueByPath, resolvePath, validateConfig} from "@utils";
import { JSONConfig } from "@types";

export const importConfigFile = (path?: string) => {
const fullPath = resolve("configs", path ?? "");
let json: Record<string, Record<string, Address>> = {};
export const importDeployFile = () => {
const fullPath = resolve("configs", envs?.DEPLOYED ?? "");
if (!fullPath) {
throw new Error("Deployed contracts file is not set, check .env file");
}

let json: Record<string, number | string | Chain | any> = {};

if (lstatSync(fullPath).isFile()) {
json = structuredClone(require(fullPath));
Expand All @@ -15,17 +20,74 @@ export const importConfigFile = (path?: string) => {
return json;
};

export const getContracts = () => {
const deployedFile = envs?.DEPLOYED;
export const importConfigFile = () => {
const path = envs?.CONFIG ?? '';
const fullPath = resolvePath(path, __dirname);

if (!deployedFile) {
throw new Error("Deployed contracts file is not set, check .env file");
let json = {} as JSONConfig;

if (lstatSync(fullPath).isFile()) {
json = structuredClone(require(fullPath));
}

const mainDeployedJSON = importConfigFile(envs?.DEPLOYED);
const extraDeployedJSON = importConfigFile(`extra-${envs?.DEPLOYED}`);
return json;
};

export const getConfig = (() => {
const configJSON = importConfigFile();
const errors = validateConfig(configJSON as unknown as JSONConfig);
const errorKeys = Object.keys(errors);
if (errorKeys.length > 0) {
errorKeys.forEach((key) => console.error(`${errors[key as keyof JSONConfig]}`));
return () => null;
}

return () => configJSON;
})();

export const getDeployed = (() => {
const deployedJSON = importDeployFile();

return () => deployedJSON;
})();

export const getChainId = (() => {
let chainId: Chain;
const config = getConfig();
const deployed = getDeployed();

if (config) {
chainId = config.chainId as Chain;
} else if (deployed.chainId) {
chainId = deployed.chainId as Chain;
} else {
chainId = Number(process.env.CHAIN_ID) as unknown as Chain;
}

return () => chainId;
})();

export const getRpcUrl = (() => {
let rpcUrl: string;
const config = getConfig();

if (config) {
rpcUrl = config.rpcLink as string;
}

return () => rpcUrl;
})();

export const getContracts = () => {
const config = getConfig();
const deployedJSON = getDeployed();

if (config) {
const { lidoLocator, accounting } = config;
return { ...deployedJSON, lidoLocator, accounting }
}

return { ...mainDeployedJSON, ...extraDeployedJSON };
return { ...deployedJSON };
};

export const getContractDeploy = (path: string) => {
Expand All @@ -46,8 +108,8 @@ export const getDeployedAddress = (...contractKeys: string[]) => {
throw new Error(`Contracts by ${contractKeys} not found`);
}

if ("proxyAddress" in contract) {
return contract.proxyAddress as Address;
if ("proxy" in contract && typeof contract.proxy === 'object' && "address" in contract.proxy!) {
return contract.proxy.address as Address;
}

if ("address" in contract) {
Expand All @@ -69,22 +131,22 @@ export const getAddressMap = () => {
const contracts = getContracts();

return Object.entries(contracts).reduce((acc, [key, value]) => {
const name = value.contract || key;
const proxyAddress =
value.proxyAddress || (value.implementation && value.address);
const implementation = value.implementation;
const isNotProxy = !implementation && !proxyAddress;

if (proxyAddress) {
acc[proxyAddress.toLowerCase()] = `Proxy (${name})`;
const name = value?.contract || key;
const proxy =
value?.proxy || (value?.implementation && value?.address);
const implementation = value?.implementation.address;
const isNotProxy = !implementation && !proxy;

if (proxy) {
acc[proxy.toLowerCase()] = `Proxy (${name})`;
}

if (implementation) {
acc[implementation.toLowerCase()] = `Implementation (${name})`;
}

if (isNotProxy && value.address) {
acc[value.address.toLowerCase()] = name;
if (isNotProxy && value?.address) {
acc[value?.address.toLowerCase()] = name;
}

return acc;
Expand Down
1 change: 1 addition & 0 deletions configs/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { parsed } = dotenv.config();
export const envs = structuredClone(parsed);
if (envs) {
envs.DEPLOYED = envs?.DEPLOYED || (process.env.DEPLOYED as string);
envs.CONFIG = envs?.CONFIG || (process.env.CONFIG as string);
envs.RPC_URL_1 = envs?.RPC_URL_1 || (process.env.RPC_URL_1 as string);
envs.RPC_URL_17000 =
envs?.RPC_URL_17000 || (process.env.RPC_URL_17000 as string);
Expand Down
7 changes: 7 additions & 0 deletions deployed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rpcLink": "https://1rpc.io/holesky",
"privateKey": "0x93655e955c3639126b6d41355457f347488c298746e1ea823fed31df897af38d",
"chainId": 17000,
"lidoLocator": "0xBEC5b7D2eD56AA3040f9a80877cCF655c95F8D65",
"accounting": "0xa9843a9214595f97fBF3434FC0Ea408bC598f232"
}
3 changes: 2 additions & 1 deletion programs/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./vault-hub";
// export * from "./vault-hub";
export * from "./vault";
export * from "./vault-factory";
46 changes: 38 additions & 8 deletions programs/vault-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,46 @@ import { getVaultFactoryContract } from "@contracts";
import { program } from "@command";
import { getAccount } from "@providers";
import { ChainOption } from "@types";
import {getChainId, getDeployedAddress} from "@configs";

const vaulFactory = program.command("vf").description("vault factory contract");
const vaultFactory = program.command("vf").description("vault factory contract");

vaulFactory
vaultFactory
.command("create-vault")
.description("create vault contract and assign manager and operator")
.option("-c, --chainId <chainId>", "chainId")
.option("-m, --manager <manager>", "managerAddress")
.option("-o, --operator <operator>", "operatorAddress")
.action(async ({ chainId }: ChainOption) => {
const contract = getVaultFactoryContract(chainId);
const tx = contract.write
});
.option("-m, --manager <manager>", "manager address")
.option("-o, --operator <operator>", "operator address")
.argument("<ownerFee>", "Vault owner fee, for e.g. 100 == 1%")
.argument("<operatorFee>", "Node operator fee, for e.g. 100 == 1%")
.action(
async (
ownerFee: string,
operatorFee: string,
{ chainId, manager, operator }: ChainOption & { manager: Address, operator: Address }
) => {
const contract = getVaultFactoryContract(chainId);
const managementFee = BigInt(ownerFee);
const performanceFee = BigInt(operatorFee);
const chain = chainId ?? getChainId()

const tx = await contract.write.createVault(
[
'0x',
{
managementFee,
performanceFee,
manager,
operator,
},
getDeployedAddress('app:aragon-agent'),
],
{
account: getAccount(chain),
chain,
}
);

console.table({ 'Vault': tx });
}
);
17 changes: 9 additions & 8 deletions providers/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { Address, Chain, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { envs } from "@configs";
import {envs, getConfig, getChainId, getRpcUrl} from "@configs";

export const getWalletClient = (chainId?: Chain) => {
const rpcUrl = envs?.[`RPC_URL_${chainId || process.env.CHAIN_ID}`];
const chain = getChainId() ?? chainId;
const rpcUrl = getRpcUrl() ?? envs?.[`RPC_URL_${chain}`];

const client = createWalletClient({
chain: chainId,
chain,
transport: http(rpcUrl),
});

return client;
};

export const getAccount = (chainId?: Chain) => {
const privateKey = envs?.[`PRIVATE_KEY_${chainId || process.env.CHAIN_ID}`];
const config = getConfig();
const chain = chainId ?? getChainId();
const privateKey = config?.privateKey ?? envs?.[`PRIVATE_KEY_${chain}`];

if (!privateKey) {
throw new Error(`PRIVATE_KEY_${chainId} is not set`);
throw new Error(`Private key for ${chain} chain is not set`);
}

const account = privateKeyToAccount(privateKey as Address);

return account;
return privateKeyToAccount(privateKey as Address);
};

export const getWalletWithAccount = (chainId?: Chain) => {
Expand Down
3 changes: 3 additions & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

yarn --silent node ./dist/index.js "$@"
6 changes: 4 additions & 2 deletions types/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Chain } from "viem";

export interface JSONConfig {
rpcLink: string | undefined;
privateKey: string | undefined;
chainId: number | undefined;
rootLocatorAddress: string | undefined;
chainId: Chain | undefined;
lidoLocator: string | undefined;
accounting: string | undefined;
}
5 changes: 3 additions & 2 deletions utils/data-validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { JSONConfig } from "@types";
export const validateConfig = (config: JSONConfig) => {
const errors = {} as Record<keyof JSONConfig, string>;

console.log('validateConfig::config', config)
if (!isValidUrl(config.rpcLink)) {
errors.rpcLink = 'Invalid rpcLink: must be a valid URL.';
}
Expand All @@ -17,8 +18,8 @@ export const validateConfig = (config: JSONConfig) => {
errors.chainId = 'Invalid chainId: must be a string representing a number.';
}

if (typeof config.rootLocatorAddress !== 'string' || !isAddress(config.rootLocatorAddress)) {
errors.rootLocatorAddress = 'Invalid rootLocatorAddress: must be a valid Ethereum address.';
if (typeof config.lidoLocator !== 'string' || !isAddress(config.lidoLocator)) {
errors.lidoLocator = 'Invalid lidoLocator: must be a valid Ethereum address.';
}

if (typeof config.accounting !== 'string' || !isAddress(config.accounting)) {
Expand Down
1 change: 1 addition & 0 deletions utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./get-value";
export * from "./data-validators";
export * from "./resolve-path";
10 changes: 10 additions & 0 deletions utils/resolve-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { isAbsolute, join, resolve } from "path";
import { homedir } from "os";

export function resolvePath(path: string, dir: string ) {
return path.startsWith('~')
? join(homedir(), path.slice(1))
: isAbsolute(path)
? path
: resolve(dir, path);
}

0 comments on commit da76599

Please sign in to comment.