From 102dd1852e2290634b2f2c8ab130ddce88d59bd7 Mon Sep 17 00:00:00 2001 From: Niv Mimran Date: Fri, 23 Dec 2022 16:38:52 +0300 Subject: [PATCH 1/4] Write CDP CLI Write commands + Write read overview --- addresses.json | 16 ++-- cli/abis.ts | 6 ++ cli/cli.ts | 3 +- cli/commands/deploy.ts | 14 +-- cli/commands/evm/mine.ts | 27 ++++++ cli/commands/index.ts | 3 +- cli/commands/modules/cdp/index.ts | 28 ++++++ cli/commands/modules/cdp/read/overview.ts | 44 +++++++++ .../modules/cdp/write/addCollateral.ts | 94 +++++++++++++++++++ cli/commands/modules/cdp/write/addDebt.ts | 70 ++++++++++++++ cli/commands/modules/cdp/write/close.ts | 65 +++++++++++++ cli/commands/modules/cdp/write/liquidate.ts | 85 +++++++++++++++++ cli/commands/modules/cdp/write/open.ts | 92 ++++++++++++++++++ .../modules/cdp/write/removeCollateral.ts | 67 +++++++++++++ cli/commands/modules/cdp/write/removeDebt.ts | 83 ++++++++++++++++ cli/commands/modules/index.ts | 13 +++ cli/defaults.ts | 18 +++- cli/deployParams.json | 64 +++++++++++-- cli/env.ts | 2 +- cli/helpers.ts | 17 +++- cli/types.ts | 25 ++++- deployments/addresses_last.example.json | 4 +- package-lock.json | 31 ++++++ package.json | 1 + scripts/DeployCDPModule.s.sol | 34 +++++++ scripts/DeployCDPModuleWETH.s.sol | 33 ------- scripts/DeployDummyOracle.s.sol | 19 ++++ scripts/DeployWstETHCDPWrapper.s.sol | 23 +++++ src/modules/cdpModule/wstETHCDPWrapper.sol | 2 + src/oracle/DummyOracle.sol | 7 ++ 30 files changed, 926 insertions(+), 64 deletions(-) create mode 100644 cli/commands/evm/mine.ts create mode 100644 cli/commands/modules/cdp/index.ts create mode 100644 cli/commands/modules/cdp/read/overview.ts create mode 100644 cli/commands/modules/cdp/write/addCollateral.ts create mode 100644 cli/commands/modules/cdp/write/addDebt.ts create mode 100644 cli/commands/modules/cdp/write/close.ts create mode 100644 cli/commands/modules/cdp/write/liquidate.ts create mode 100644 cli/commands/modules/cdp/write/open.ts create mode 100644 cli/commands/modules/cdp/write/removeCollateral.ts create mode 100644 cli/commands/modules/cdp/write/removeDebt.ts create mode 100644 cli/commands/modules/index.ts create mode 100644 scripts/DeployCDPModule.s.sol delete mode 100644 scripts/DeployCDPModuleWETH.s.sol create mode 100644 scripts/DeployDummyOracle.s.sol create mode 100644 scripts/DeployWstETHCDPWrapper.s.sol diff --git a/addresses.json b/addresses.json index e80ae62..29c2fce 100644 --- a/addresses.json +++ b/addresses.json @@ -1,11 +1,11 @@ { "42069": { "core": { - "PHO": "0x8be34D7830d446747B95Aa3ace0eaA4FF19b2007", - "TON": "0x45D97250E4F261c4116b2ebDBA1b963e55B068d3", - "Kernel": "0xB49BafEc7095d2d337A9e7DaaAE2DAA0F73d5b5A", - "ModuleManager": "0xB56Ab229bD4d9459e61BC7Bb34E394Ed6c1a8e39", - "ChainlinkPriceFeed": "0x36A7f9ca3EBD83E00c1bca3A1db378bcF039Bf85", + "PHO": "0xB7ca895F81F20e05A5eb11B05Cbaab3DAe5e23cd", + "TON": "0xd0EC100F1252a53322051a95CF05c32f0C174354", + "Kernel": "0x2d13826359803522cCe7a4Cfa2c1b582303DD0B4", + "ModuleManager": "0xCa57C1d3c2c35E667745448Fef8407dd25487ff8", + "ChainlinkPriceFeed": "0xaB837301d12cDc4b97f1E910FC56C9179894d9cf", "CurvePool": "0xe9123CBC5d1EA65301D417193c40A72Ac8D53501" }, "modules": { @@ -14,7 +14,9 @@ "StablecoinDepositModuleFRAX": "0x70F804060040bAb7E443b0F4334d356B7a6D4bAc", "StablecoinDepositModuleLUSD": "0x009eb8A8a1B7C4d48C721E47894346477d2f6647", "MapleDepositModuleUSDC": "0x7511fAE41153Fad8A569d7Ebdcc76c120D3d5AAb", - "ZCBModuleUSDC": "0x7c098E457DA8108527bdba11da981100d5293A92" + "ZCBModuleUSDC": "0x7c098E457DA8108527bdba11da981100d5293A92", + "CDPPool_wstETH": "0x4ff1f64683785E0460c24A4EF78D582C2488704f", + "wstETHCDPWrapper": "0x0F527785e39B22911946feDf580d87a4E00465f0" } }, "11155111": { @@ -27,4 +29,4 @@ }, "modules": {} } -} \ No newline at end of file +} diff --git a/cli/abis.ts b/cli/abis.ts index b4177c4..775c530 100644 --- a/cli/abis.ts +++ b/cli/abis.ts @@ -6,6 +6,8 @@ import abiMM from '../build/abis/ModuleManager.sol/ModuleManager.json' import abiKernel from '../build/abis/Kernel.sol/Kernel.json' import abiCLPF from '../build/abis/ChainlinkPriceFeed.sol/ChainlinkPriceFeed.json' import abiCP from '../build/abis/ICurvePool.sol/ICurvePool.json' +import abiWstETHCDPWrapper from '../build/abis/wstETHCDPWrapper.sol/wstETHCDPWrapper.json' +import abiCDPPool from '../build/abis/CDPPool.sol/CDPPool.json' export const loadABI = (name: string): ContractInterface => { switch (name) { @@ -21,6 +23,10 @@ export const loadABI = (name: string): ContractInterface => { return abiCLPF.abi case 'CurvePool': return abiCP.abi + case 'wstETHCDPWrapper': + return abiWstETHCDPWrapper.abi + case 'CDPPool': + return abiCDPPool.abi default: return 'ERROR_NO_ABI_FOUND' // TODO - DK - Improve error } diff --git a/cli/cli.ts b/cli/cli.ts index 861036a..9d00ad5 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -1,7 +1,7 @@ #!/usr/bin/env ts-node import * as dotenv from 'dotenv' import yargs from 'yargs' -import { evmCommand, coreCommand, deployCommand } from './commands' +import { evmCommand, coreCommand, deployCommand, modulesCommand } from './commands' import { adminCommand } from './commands/admin' import { cliOpts } from './defaults' @@ -24,5 +24,6 @@ yargs .command(evmCommand) .command(adminCommand) .command(deployCommand) + .command(modulesCommand) .demandCommand(1, 'Choose a command from the above list') .help().argv diff --git a/cli/commands/deploy.ts b/cli/commands/deploy.ts index 398fc03..8907f41 100644 --- a/cli/commands/deploy.ts +++ b/cli/commands/deploy.ts @@ -42,7 +42,7 @@ export const deploy = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise { - await copyFile('deployments/addresses_last.example.json', 'deployments/addresses_last.json', 0) - const tempAddresses = await import('../../deployments/addresses_last.json') + // await copyFile('deployments/addresses_last.example.json', 'deployments/addresses_last.json', 0) + const tempAddresses = require('../../deployments/addresses_last.json') const updated: MasterAddresses = prepareAddressesJson(addresses, p.networkId) const latestLog: string | undefined = getMostRecentFile( `broadcast/${p.contractName}.s.sol/${getCorrectNetworkId(p.networkId)}/` @@ -107,7 +107,7 @@ export async function updateAddresses(p: AddressParams): Promise { const { transactionType, address } = trx.additionalContracts[0] if (transactionType === 'CREATE') { updated[p.networkId].core.CurvePool = address - tempAddresses.CurvePool = address + tempAddresses['CurvePool'] = address } } else { if (trx.transactionType === 'CREATE') { @@ -124,15 +124,15 @@ export async function updateAddresses(p: AddressParams): Promise { resolve({ updated, tempAddresses }) }).then((res) => { const { updated, tempAddresses } = res - writeFileSync('addresses.json', JSON.stringify(updated), { flag: 'w+' }) - writeFileSync('deployments/addresses_last.json', JSON.stringify(tempAddresses), { flag: 'w+' }) + writeFileSync('addresses.json', JSON.stringify(updated)) + writeFileSync('deployments/addresses_last.json', JSON.stringify(tempAddresses)) if (!existsSync(`deployments/${p.networkId}`)) { mkdirSync(`deployments/${p.networkId}`) } writeFileSync( `deployments/${p.networkId}/addresses_latest.json`, JSON.stringify(tempAddresses), - { flag: 'w+' } + { flag: 'w' } ) }) } diff --git a/cli/commands/evm/mine.ts b/cli/commands/evm/mine.ts new file mode 100644 index 0000000..e4d611b --- /dev/null +++ b/cli/commands/evm/mine.ts @@ -0,0 +1,27 @@ +import yargs, { Argv } from 'yargs' +import { loadEnv } from '../../env' +import { CLIArgs, CLIEnvironment } from '../../types' +import { execute } from '../deploy' +import { logger } from '../../logging' + +const buildHelp = (): string => { + const help = 'To fast forward -> evm fast-forward [seconds] [minutes] [hours]' + return help +} + +export const mine = async (cli: CLIEnvironment): Promise => { + await execute( + `curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"evm_mine","params":[],"id":67}' ${cli.providerUrl}` + ) +} + +export const mineCommand = { + command: 'mine', + describe: 'deploy contracts from deployParams.json', + builder: (yargs: Argv): yargs.Argv => { + return yargs.usage(buildHelp()) + }, + handler: async (argv: CLIArgs): Promise => { + return await mine(await loadEnv(argv)) + } +} diff --git a/cli/commands/index.ts b/cli/commands/index.ts index 56c0ecb..37a58b1 100644 --- a/cli/commands/index.ts +++ b/cli/commands/index.ts @@ -1,5 +1,6 @@ import { coreCommand } from './core' import { deployCommand } from './deploy' import { evmCommand } from './evm' +import { modulesCommand } from './modules' -export { coreCommand, deployCommand, evmCommand } +export { coreCommand, deployCommand, evmCommand, modulesCommand } diff --git a/cli/commands/modules/cdp/index.ts b/cli/commands/modules/cdp/index.ts new file mode 100644 index 0000000..8b8ed15 --- /dev/null +++ b/cli/commands/modules/cdp/index.ts @@ -0,0 +1,28 @@ +import yargs, { Argv, command } from 'yargs' +import { overviewCommand } from './read/overview' +import { addCollateralCommand } from './write/addCollateral' +import { addDebtCommand } from './write/addDebt' +import { closeCommand } from './write/close' +import { liquidateCommand } from './write/liquidate' +import { openCommand } from './write/open' +import { removeCollateralCommand } from './write/removeCollateral' +import { removeDebtCommand } from './write/removeDebt' + +export const cdpCommand = { + command: 'cdp', + describe: 'Photon protocol modules', + builder: (yargs: Argv): yargs.Argv => { + return yargs + .command(openCommand) + .command(addCollateralCommand) + .command(removeCollateralCommand) + .command(addDebtCommand) + .command(removeDebtCommand) + .command(closeCommand) + .command(liquidateCommand) + .command(overviewCommand) + }, + handler: (): void => { + yargs.showHelp() + } +} diff --git a/cli/commands/modules/cdp/read/overview.ts b/cli/commands/modules/cdp/read/overview.ts new file mode 100644 index 0000000..285b9cb --- /dev/null +++ b/cli/commands/modules/cdp/read/overview.ts @@ -0,0 +1,44 @@ +import Table from 'cli-table3' +import { BigNumber, logger } from 'ethers'; +import { moduleDictionary } from '../../../../defaults'; +import { loadEnv } from "../../../../env" +import { getModuleAddress } from "../../../../helpers" +import { CLIArgs, CLIEnvironment } from "../../../../types" +import { execute } from "../../../deploy"; + + +const getOverview = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let moduleName = moduleDictionary.cdp[cliArgs.tokenType].default; + let cdpAddress = getModuleAddress(cliArgs.c, "cdp", cliArgs.tokenType, "default"); + + let moduleData = await cli.contracts.ModuleManager.modules(cdpAddress); + let [phoMinted, startTime, status] = moduleData.slice(-3); + let balances = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "pool()((uint256,uint256))"`); + let feesCollected = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "feesCollected()(uint256)"`); + + const table = new Table({ + head: [moduleName, 'Result'], + colWidths: [30, 50] + }) + + let [totalDebt, totalCollateral] = balances.substring(1, balances.length - 1).split(","); + let collRatio = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${totalCollateral} ${totalDebt}`); + + table.push(["Address", cdpAddress]); + table.push(["PHO Mined", phoMinted.toLocaleString()]); + table.push(["startTime", startTime.toString()]); + table.push(["status", status.toString()]); + table.push(["Total Collateral", totalCollateral]); + table.push(["Total Debt", totalDebt]); + table.push(["Collateral Ratio", collRatio.toString()]) + logger.info(table.toString()) + +} + +export const overviewCommand = { + command: 'overview [tokenType]', + describe: 'CDP Mechanism overview', + handler: async (argv: CLIArgs): Promise => { + return await getOverview(await loadEnv(argv), argv) + } + } \ No newline at end of file diff --git a/cli/commands/modules/cdp/write/addCollateral.ts b/cli/commands/modules/cdp/write/addCollateral.ts new file mode 100644 index 0000000..1d42bbe --- /dev/null +++ b/cli/commands/modules/cdp/write/addCollateral.ts @@ -0,0 +1,94 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPCollateralParams, CDPOpenParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('addCollateral: Collateral token does not have a corresponding CDP') + return {} as CDPCollateralParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "deposit") + + if (collateralToken == 'wsteth') { + parameters = parameters.slice(1) + if (["steth", "weth", "eth"].indexOf(parameters[0]) == -1) { + logger.error("Deposit token is not supported"); + return {} as CDPCollateralParams; + } + } + + if (parameters.length != 2) { + logger.error('addCollateral: Not enough parameters were supplied') + return {} as CDPCollateralParams + } + + if (isNaN(parameters[1])) { + logger.error('addCollateral: parameters supplied are in the wrong type') + return {} as CDPCollateralParams + } + + let collateralAmount = ethers.utils.parseUnits(parameters[1], 18) + + return { + contractAddress, + collateralToken, + depositToken: parameters[0], + collateralAmount + } +} + +const addCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPCollateralParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('addCollateral: bad parameters') + return + } + if (params.collateralToken === 'wsteth') { + return await depositWithWrapper(params, cli) + } +} + +const depositWithWrapper = async (params: CDPCollateralParams, cli: CLIEnvironment) => { + let depositTokenAddress = tokenAddresses[params.depositToken] + console.log(depositTokenAddress) + if (!depositTokenAddress) { + logger.error('addCollateral: deposit token address not found') + return + } + + if (cli.argv.c === 42069) { + let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${depositTokenAddress} "approve(address,uint256)" ${params.contractAddress} ${params.collateralAmount} --from ${cli.wallet.address} --json` + let res = JSON.parse(await execute(approveCommand)) + if (res.status == '0x1') { + logger.info( + `${cli.wallet.address} approved ${params.collateralAmount} for ${depositTokenAddress}` + ) + } + } + + let addCollateralCommand = `cast send --rpc-url ${cli.providerUrl} ${params.contractAddress} "addCollateral(uint256,address)" ${params.collateralAmount} ${depositTokenAddress} --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(addCollateralCommand)); + if (receipt.status == '0x1') { + logger.info(`Added collateral to position for ${cli.wallet.address} successfully.`) + let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH'] + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionReceipt = await execute(positionCommand) + logger.info(positionReceipt) + } +} + +export const addCollateralCommand = { + command: 'add-collateral', + describe: 'Add collateral to a position', + handler: async (argv: CLIArgs): Promise => { + return await addCollateral(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/addDebt.ts b/cli/commands/modules/cdp/write/addDebt.ts new file mode 100644 index 0000000..e4f1216 --- /dev/null +++ b/cli/commands/modules/cdp/write/addDebt.ts @@ -0,0 +1,70 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPDebtParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPDebtParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('addDebt: Collateral token does not have a corresponding CDP') + return {} as CDPDebtParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + + if (collateralToken == 'wsteth') { + parameters = parameters.slice(1) + } + + if (parameters.length != 1) { + logger.error('addDebt: Not enough parameters were supplied') + return {} as CDPDebtParams + } + + if (isNaN(parameters[0])) { + logger.error('addDebt: parameters supplied are in the wrong type') + return {} as CDPDebtParams + } + + let debtAmount = ethers.utils.parseUnits(parameters[0], 18) + + return { + contractAddress, + collateralToken, + depositToken: "", + debtAmount + } +} + +const addDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPDebtParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('addDebt: bad parameters') + return + } + let moduleName = moduleDictionary.cdp[params.collateralToken].default; + let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + + let addDebtCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "addDebt(uint256)" ${params.debtAmount} --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(addDebtCommand)); + if (receipt.status == '0x1') { + logger.info(`Added debt to position for ${cli.wallet.address} successfully.`) + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionReceipt = await execute(positionCommand) + logger.info(positionReceipt) + } +} + + +export const addDebtCommand = { + command: 'add-debt', + describe: 'Add debt to a position', + handler: async (argv: CLIArgs): Promise => { + return await addDebt(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/close.ts b/cli/commands/modules/cdp/write/close.ts new file mode 100644 index 0000000..eb3ba9d --- /dev/null +++ b/cli/commands/modules/cdp/write/close.ts @@ -0,0 +1,65 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPBaseParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPBaseParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('close: Collateral token does not have a corresponding CDP') + return {} as CDPBaseParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + + return { + contractAddress, + collateralToken, + depositToken: "" + } +} + +const close = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPBaseParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('close: bad parameters') + return + } + let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; + let moduleName = moduleDictionary.cdp[params.collateralToken].default; + let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionResponse = await execute(positionCommand); + let debtAmount = positionResponse.substring(1,positionResponse.length - 1).split(",")[0]; + + if (cli.argv.c === 42069) { + let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${cli.wallet.address} --json` + let res = JSON.parse(await execute(approveCommand)) + if (res.status == '0x1') { + logger.info( + `${cli.wallet.address} approved ${debtAmount} for ${params.contractAddress}` + ) + } + } + + let closeCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "close()" --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(closeCommand)); + if (receipt.status == '0x1') { + logger.info(`Closed position for ${cli.wallet.address} successfully.`) + } +} + + +export const closeCommand = { + command: 'close', + describe: 'close a position', + handler: async (argv: CLIArgs): Promise => { + return await close(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/liquidate.ts b/cli/commands/modules/cdp/write/liquidate.ts new file mode 100644 index 0000000..706c846 --- /dev/null +++ b/cli/commands/modules/cdp/write/liquidate.ts @@ -0,0 +1,85 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPLiquidationParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' +import { cli } from 'winston/lib/winston/config' +import { number } from 'yargs' + +const getParams = (cli: CLIEnvironment, cliArgs: CLIArgs): CDPLiquidationParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('liquidate: Collateral token does not have a corresponding CDP') + return {} as CDPLiquidationParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + let cdpOwner = parameters[1]; + + if (!cdpOwner) { + logger.error('liquidate: missing cdp owner address'); + return {} as CDPLiquidationParams; + } + + return { + contractAddress, + collateralToken, + depositToken: "", + cdpOwner: cdpOwner, + liquidator: cli.wallet.address + } +} + +const liquidate = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPLiquidationParams = getParams(cli, cliArgs) + if (!params.collateralToken) { + logger.error('liquidate: bad parameters') + return + } + let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; + let moduleName = moduleDictionary.cdp[params.collateralToken].default; + let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${params.cdpOwner}` + let positionResponse = await execute(positionCommand); + let [ debtAmount, collateralAmount ] = positionResponse.substring(1, positionResponse.length - 1).split(","); + + let collRatio = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${collateralAmount} ${debtAmount}`); + let minCR = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "minCR()(uint256)"`); + + if (Number(collRatio) >= Number(minCR)) { + logger.error(`Collateral ratio is ${collRatio.substring(0,3)}% and not in liquidation zone. Liquidation aborted`); + return; + } + + logger.info(`Collateral Ratio: ${collRatio.substring(0,3)}%. Executing liquidation...`) + + if (cli.argv.c === 42069) { + let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${params.liquidator} --json` + let res = JSON.parse(await execute(approveCommand)) + if (res.status == '0x1') { + logger.info( + `${params.liquidator} approved ${debtAmount} for ${params.contractAddress}` + ) + } + } + + let liquidateCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "liquidate(address)" ${params.cdpOwner} --from ${params.liquidator} --json` + let receipt = JSON.parse(await execute(liquidateCommand)); + if (receipt.status == '0x1') { + logger.info(`Liquidated position for ${cli.wallet.address} by ${params.liquidator} successfully.`) + } +} + + +export const liquidateCommand = { + command: 'liquidate', + describe: 'liquidate a position', + handler: async (argv: CLIArgs): Promise => { + return await liquidate(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/open.ts b/cli/commands/modules/cdp/write/open.ts new file mode 100644 index 0000000..db8b46a --- /dev/null +++ b/cli/commands/modules/cdp/write/open.ts @@ -0,0 +1,92 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPOpenParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPOpenParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('openPosition: Collateral token does not have a corresponding CDP') + return {} as CDPOpenParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "deposit") + + if (collateralToken == 'wsteth') { + parameters = parameters.slice(1) + } + + if (parameters.length != 3) { + logger.error('openPosition: Not enough parameters were supplied') + return {} as CDPOpenParams + } + + if (isNaN(parameters[1]) || isNaN(parameters[2])) { + logger.error('openPositions: parameters supplied are in the wrong type') + return {} as CDPOpenParams + } + + let collateralAmount = ethers.utils.parseUnits(parameters[1], 18) + let debtAmount = ethers.utils.parseUnits(parameters[2], 18) + + return { + contractAddress, + collateralToken, + depositToken: parameters[0], + collateralAmount, + debtAmount + } +} + +const openPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPOpenParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('openPosition: bad parameters') + return + } + if (params.collateralToken === 'wsteth') { + return await depositWithWrapper(params, cli) + } +} + +const depositWithWrapper = async (params: CDPOpenParams, cli: CLIEnvironment) => { + let depositTokenAddress = tokenAddresses[params.depositToken] + console.log(depositTokenAddress) + if (!depositTokenAddress) { + logger.error('openPosition: deposit token address not found') + return + } + + if (cli.argv.c === 42069) { + let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${depositTokenAddress} "approve(address,uint256)" ${params.contractAddress} ${params.collateralAmount} --from ${cli.wallet.address} --json` + let res = JSON.parse(await execute(approveCommand)) + if (res.status == '0x1') { + logger.info( + `${cli.wallet.address} approved ${params.collateralAmount} for ${depositTokenAddress}` + ) + } + } + + let openCommand = `cast send --rpc-url ${cli.providerUrl} ${params.contractAddress} "open(uint256,uint256,address)" ${params.collateralAmount} ${params.debtAmount} ${depositTokenAddress} --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(openCommand)); + if (receipt.status == '0x1') { + logger.info(`Open a new debt position for ${cli.wallet.address} successfully.`) + let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH'] + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionReceipt = await execute(positionCommand) + logger.info(positionReceipt) + } +} + +export const openCommand = { + command: 'open', + describe: 'opens a position', + handler: async (argv: CLIArgs): Promise => { + return await openPosition(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/removeCollateral.ts b/cli/commands/modules/cdp/write/removeCollateral.ts new file mode 100644 index 0000000..40f9ce3 --- /dev/null +++ b/cli/commands/modules/cdp/write/removeCollateral.ts @@ -0,0 +1,67 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPCollateralParams, CDPOpenParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('removeCollateral: Collateral token does not have a corresponding CDP') + return {} as CDPCollateralParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + + if (collateralToken == 'wsteth') { + parameters = parameters.slice(1) + } + + if (parameters.length != 1) { + logger.error('removeCollateral: Not enough parameters were supplied') + return {} as CDPCollateralParams + } + + if (isNaN(parameters[0])) { + logger.error('removeCollateral: parameters supplied are in the wrong type') + return {} as CDPCollateralParams + } + + let collateralAmount = ethers.utils.parseUnits(parameters[0], 18) + + return { + contractAddress, + collateralToken, + depositToken: parameters[0], + collateralAmount + } +} + +const removeCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPCollateralParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('removeCollateral: bad parameters') + return + } + let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH']; + let removeCollateralCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "removeCollateral(uint256)" ${params.collateralAmount} --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(removeCollateralCommand)); + if (receipt.status == '0x1') { + logger.info(`Remove collateral from position for ${cli.wallet.address} successfully.`) + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionReceipt = await execute(positionCommand) + logger.info(positionReceipt) + } +} + +export const removeCollateralCommand = { + command: 'remove-collateral', + describe: 'Remove collateral from a position', + handler: async (argv: CLIArgs): Promise => { + return await removeCollateral(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/removeDebt.ts b/cli/commands/modules/cdp/write/removeDebt.ts new file mode 100644 index 0000000..d25bf98 --- /dev/null +++ b/cli/commands/modules/cdp/write/removeDebt.ts @@ -0,0 +1,83 @@ +import { ethers } from 'ethers' +import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { logger } from '../../../../logging' +import { CDPDebtParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import addresses from '../../../../../addresses.json' + +const getParams = (cliArgs: CLIArgs): CDPDebtParams => { + let { _: parameters, c: networkId } = cliArgs + parameters = parameters.slice(3) + let collateralToken = parameters[0] + if (!moduleDictionary.cdp[collateralToken]) { + logger.error('removeDebt: Collateral token does not have a corresponding CDP') + return {} as CDPDebtParams + } + let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + console.log(contractAddress) + + if (collateralToken == 'wsteth') { + parameters = parameters.slice(1) + } + + if (parameters.length != 1) { + logger.error('removeDebt: Not enough parameters were supplied') + return {} as CDPDebtParams + } + + if (isNaN(parameters[0])) { + logger.error('removeDebt: parameters supplied are in the wrong type') + return {} as CDPDebtParams + } + + let debtAmount = ethers.utils.parseUnits(parameters[0], 18) + + return { + contractAddress, + collateralToken, + depositToken: "", + debtAmount + } +} + +const removeDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { + let params: CDPDebtParams = getParams(cliArgs) + if (!params.collateralToken) { + logger.error('removeDebt: bad parameters') + return + } + let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; + let moduleName = moduleDictionary.cdp[params.collateralToken].default; + let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + + + if (cli.argv.c === 42069) { + let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${params.debtAmount} --from ${cli.wallet.address} --json` + let res = JSON.parse(await execute(approveCommand)) + if (res.status == '0x1') { + logger.info( + `${cli.wallet.address} approved ${params.debtAmount} for ${params.contractAddress}` + ) + } + } + + let removeDebtCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "removeDebt(uint256)" ${params.debtAmount} --from ${cli.wallet.address} --json` + let receipt = JSON.parse(await execute(removeDebtCommand)); + if (receipt.status == '0x1') { + logger.info(`Removed debt from position for ${cli.wallet.address} successfully.`) + let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + let positionReceipt = await execute(positionCommand) + logger.info(positionReceipt) + } +} + + +export const removeDebtCommand = { + command: 'remove-debt', + describe: 'Remove debt from a position', + handler: async (argv: CLIArgs): Promise => { + return await removeDebt(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/index.ts b/cli/commands/modules/index.ts new file mode 100644 index 0000000..2b89f1c --- /dev/null +++ b/cli/commands/modules/index.ts @@ -0,0 +1,13 @@ +import yargs, { Argv } from 'yargs' +import { cdpCommand } from './cdp' + +export const modulesCommand = { + command: 'modules', + describe: 'Photon protocol modules', + builder: (yargs: Argv): yargs.Argv => { + return yargs.command(cdpCommand) + }, + handler: (): void => { + yargs.showHelp() + } +} diff --git a/cli/defaults.ts b/cli/defaults.ts index 7094ff5..58f994e 100644 --- a/cli/defaults.ts +++ b/cli/defaults.ts @@ -1,5 +1,5 @@ import { Options } from 'yargs' -import { Overrides } from 'ethers' +import { ethers, Overrides } from 'ethers' import dotenv from 'dotenv' dotenv.config() @@ -53,3 +53,19 @@ export const coreContracts = [ 'ChainlinkPriceFeed', 'CurvePool' ] + +export const moduleDictionary = { + cdp: { + wsteth: { + deposit: 'wstETHCDPWrapper', + default: "CDPPool_wstETH" + } + } +} + +export const tokenAddresses = { + eth: ethers.constants.AddressZero, + weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + steth: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', + wsteth: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0' +} diff --git a/cli/deployParams.json b/cli/deployParams.json index 791abc5..cda6427 100644 --- a/cli/deployParams.json +++ b/cli/deployParams.json @@ -3,7 +3,7 @@ { "name": "protocol", "description": "deploys main protocol contracts: TON, PHO, Kernel, ModuleManager, ChainlinkPriceFeed", - "deploy": true, + "deploy": false, "contractName": "DeployProtocol", "sigParams": [ { @@ -17,7 +17,7 @@ { "name": "CurvePool", "description": "Deploys a new FraxBP-PHO curve pool", - "deploy": true, + "deploy": false, "contractName": "DeployCurvePool", "sigParams": [], "isCore": true, @@ -26,7 +26,7 @@ { "name": "Price Controller", "description": "Deploys price controller module", - "deploy": true, + "deploy": false, "contractName": "DeployPriceController", "sigParams": [ { @@ -52,7 +52,7 @@ { "name": "USDC stablecoin module", "description": "Deploys USDC Stablecoin module", - "deploy": true, + "deploy": false, "contractName": "DeployStablecoinDepositModule", "sigParams": [ { @@ -66,7 +66,7 @@ { "name": "FRAX stablecoin module", "description": "Deploys FRAX Stablecoin module", - "deploy": true, + "deploy": false, "contractName": "DeployStablecoinDepositModule", "sigParams": [ { @@ -80,7 +80,7 @@ { "name": "LUSD stablecoin module", "description": "Deploys LUSD Stablecoin module", - "deploy": true, + "deploy": false, "contractName": "DeployStablecoinDepositModule", "sigParams": [ { @@ -94,7 +94,7 @@ { "name": "Maple deposit module", "description": "Deploys maple deposit module", - "deploy": true, + "deploy": false, "contractName": "DeployMapleDepositModule", "sigParams": [ { @@ -108,7 +108,7 @@ { "name": "USDC Zero Coupon Bond module", "description": "Deploys Zero coupon bonds module with USDC", - "deploy": true, + "deploy": false, "contractName": "DeployZCBModule", "sigParams": [ { @@ -138,6 +138,54 @@ ], "isCore": false, "contractLabel": "ZCBModuleUSDC" + }, + { + "name": "Dummy oracle deployment", + "description": "Deploy dummy oracle", + "deploy": false, + "contractName": "DeployDummyOracle", + "sigParams": [], + "isCore": true, + "contractLabel": "ChainlinkPriceFeed" + }, + { + "name": "wstETH CDP mechanism", + "description": "CDP Mechanism deployment using wstETH as collateral", + "deploy": true, + "contractName": "DeployCDPModule", + "sigParams": [ + { + "type": "address", + "value": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0" + }, + { + "type": "uint256", + "value": "170000" + }, + { + "type": "uint256", + "value": "150000" + }, + { + "type": "uint256", + "value": 1000 + }, + { + "type": "uint256", + "value": 500 + } + ], + "isCore": false, + "contractLabel": "CDPPool_wstETH" + }, + { + "name": "wstETH CDP wrapper", + "description": "wrapper contract for wstETH based CDP", + "deploy": true, + "contractName": "DeployWstETHCDPWrapper", + "sigParams": [], + "isCore": false, + "contractLabel": "wstETHCDPWrapper" } ] } diff --git a/cli/env.ts b/cli/env.ts index b7e17b8..416eaa0 100644 --- a/cli/env.ts +++ b/cli/env.ts @@ -26,7 +26,7 @@ export const loadEnv = async (argv: CLIArgs): Promise => { const chainId = (await wallet.provider.getNetwork()).chainId const nonce = await wallet.getTransactionCount() const walletAddress = await wallet.getAddress() - const { c: networkId }: { c: number } = argv + const { c: networkId } = argv if (!verifyNetwork(networkId)) { logger.info(`Network id ${networkId} is invalid`) } diff --git a/cli/helpers.ts b/cli/helpers.ts index a2d575c..86faa6f 100644 --- a/cli/helpers.ts +++ b/cli/helpers.ts @@ -1,7 +1,7 @@ import { logger } from './logging' import { NetworkContracts } from './types' import addresses from '../addresses.json' -import { rpcUrls } from './defaults' +import { moduleDictionary, rpcUrls } from './defaults' export const verifyModule = (networkId: number, moduleId: string): boolean => { try { @@ -25,7 +25,7 @@ export const verifyNetwork = (networkId: number): boolean => { logger.info('First parameter should be the network name') return false } else if (!getNetworkRPC(networkId)) { - logger.info(`Network with ID ${networkId} does not have a RPC_URK record in the .env file`) + logger.error(`Network with ID ${networkId} does not have a RPC_URK record in the .env file`) return false } return true @@ -42,3 +42,16 @@ export const getNetworkRPC = (networkId: number): string => { export const getNetworkContractAddresses = (networkId: number): NetworkContracts => { return addresses[networkId] } + +export const getModuleAddress = (networkId: number, type: string, identifier: string, action: string): string => { + let contractName = moduleDictionary[type][identifier][action] + if (!contractName || !verifyNetwork(networkId)) { + logger.error('getModuleAddress: No module was found with the given params') + return '' + } + let modules = addresses[networkId].modules + if (!modules) { + logger.error('getModuleAddress: Network does not have deployed contracts') + } + return modules[contractName] +} diff --git a/cli/types.ts b/cli/types.ts index 186eca0..98abbb6 100644 --- a/cli/types.ts +++ b/cli/types.ts @@ -9,7 +9,7 @@ import { Argv } from 'yargs' export interface SignatureParam { type: string - value: string | number + value: string | number | BigNumber } export interface DeployParams { @@ -78,3 +78,26 @@ export interface ProtocolFunction { contract: string name: string } + +export interface CDPBaseParams { + contractAddress: string + collateralToken: string + depositToken: string +} +export interface CDPOpenParams extends CDPBaseParams { + collateralAmount: BigNumber + debtAmount: BigNumber +} + +export interface CDPCollateralParams extends CDPBaseParams { + collateralAmount: BigNumber +} + +export interface CDPDebtParams extends CDPBaseParams { + debtAmount: BigNumber +} + +export interface CDPLiquidationParams extends CDPBaseParams { + cdpOwner: string + liquidator: string +} \ No newline at end of file diff --git a/deployments/addresses_last.example.json b/deployments/addresses_last.example.json index ba123dc..b5f1d07 100644 --- a/deployments/addresses_last.example.json +++ b/deployments/addresses_last.example.json @@ -1,4 +1,4 @@ { - "phoGovernance": "0x4ce45ECaa645B0fc00250C62D20F14003707B471", - "tonGovernance": "0x4ce45ECaa645B0fc00250C62D20F14003707B471" + "phoGovernance": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "tonGovernance": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" } diff --git a/package-lock.json b/package-lock.json index c9dc850..d3b86f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "devDependencies": { "@typechain/ethers-v5": "^10.1.1", "@types/node": "^18.11.9", + "@types/yargs": "^17.0.17", "@typescript-eslint/eslint-plugin": "^5.46.1", "dotenv": "^16.0.3", "eslint": "^8.29.0", @@ -1067,6 +1068,21 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "node_modules/@types/yargs": { + "version": "17.0.17", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz", + "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.46.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", @@ -6168,6 +6184,21 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "@types/yargs": { + "version": "17.0.17", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.17.tgz", + "integrity": "sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.46.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz", diff --git a/package.json b/package.json index e6507be..b7c936d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "devDependencies": { "@typechain/ethers-v5": "^10.1.1", "@types/node": "^18.11.9", + "@types/yargs": "^17.0.17", "@typescript-eslint/eslint-plugin": "^5.46.1", "dotenv": "^16.0.3", "eslint": "^8.29.0", diff --git a/scripts/DeployCDPModule.s.sol b/scripts/DeployCDPModule.s.sol new file mode 100644 index 0000000..1eca771 --- /dev/null +++ b/scripts/DeployCDPModule.s.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "@modules/cdpModule/CDPPool.sol"; +import "./Addresses.s.sol"; + +/// Script to deploy protocol (PHO, TON, Kernel, ModuleManager) +contract DeployCDPModule is Script, Addresses { + + CDPPool public pool; + + function run(address depositToken, uint256 minCR, uint256 liquidationCR, uint256 minDebt, uint256 protocolFee) external { + vm.startBroadcast(); + + address moduleManagerAddress = getAddress(".ModuleManager"); + address TONTimelock = getAddress(".tonGovernance"); + address chainlinkPriceOracle = getAddress(".ChainlinkPriceFeed"); + + pool = new CDPPool( + moduleManagerAddress, + chainlinkPriceOracle, + depositToken, + TONTimelock, + minCR, + liquidationCR, + minDebt * 10 ** 18, + protocolFee + ); + + vm.stopBroadcast(); + } +} diff --git a/scripts/DeployCDPModuleWETH.s.sol b/scripts/DeployCDPModuleWETH.s.sol deleted file mode 100644 index 4677692..0000000 --- a/scripts/DeployCDPModuleWETH.s.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "@modules/cdpModule/CDPPool.sol"; -import "./Addresses.s.sol"; - -/// Script to deploy protocol (PHO, TON, Kernel, ModuleManager) -contract DeployCDPModuleWETH is Script, Addresses { - - CDPPool public wethPool; - address public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - - function run() external { - vm.startBroadcast(); - - address moduleManagerAddress = getAddress(".ModuleManager"); - address chainlinkPriceFeedAddress = getAddress(".ChainlinkPriceFeed"); - - wethPool = new CDPPool( - moduleManagerAddress, - chainlinkPriceFeedAddress, - WETH_ADDRESS, - 170000, - 150000, - 1000 * 10 ** 18, - 500 - ); - - vm.stopBroadcast(); - } -} diff --git a/scripts/DeployDummyOracle.s.sol b/scripts/DeployDummyOracle.s.sol new file mode 100644 index 0000000..e8828b9 --- /dev/null +++ b/scripts/DeployDummyOracle.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "@oracle/DummyOracle.sol"; + +contract DeployDummyOracle is Script { + + DummyOracle public oracle; + + function run() external { + vm.startBroadcast(); + + oracle = new DummyOracle(); + + vm.stopBroadcast(); + } +} diff --git a/scripts/DeployWstETHCDPWrapper.s.sol b/scripts/DeployWstETHCDPWrapper.s.sol new file mode 100644 index 0000000..0f10f4f --- /dev/null +++ b/scripts/DeployWstETHCDPWrapper.s.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "@modules/cdpModule/wstETHCDPWrapper.sol"; +import "./Addresses.s.sol"; + +/// Script to deploy protocol (PHO, TON, Kernel, ModuleManager) +contract DeployWstETHCDPWrapper is Script, Addresses { + + wstETHCDPWrapper public wrapper; + + function run() external { + vm.startBroadcast(); + + address cdpAddress = getAddress(".CDPPool_wstETH"); + + wrapper = new wstETHCDPWrapper(cdpAddress); + + vm.stopBroadcast(); + } +} diff --git a/src/modules/cdpModule/wstETHCDPWrapper.sol b/src/modules/cdpModule/wstETHCDPWrapper.sol index a808e9e..7ebc262 100644 --- a/src/modules/cdpModule/wstETHCDPWrapper.sol +++ b/src/modules/cdpModule/wstETHCDPWrapper.sol @@ -26,6 +26,8 @@ contract wstETHCDPWrapper { constructor(address _pool) { pool = ICDPPool(_pool); + STETH.approve(address(WSTETH), type(uint256).max); + WSTETH.approve(_pool, type(uint256).max); } receive() external payable {} diff --git a/src/oracle/DummyOracle.sol b/src/oracle/DummyOracle.sol index 8bd6047..82a75b5 100644 --- a/src/oracle/DummyOracle.sol +++ b/src/oracle/DummyOracle.sol @@ -12,6 +12,7 @@ contract DummyOracle is IPriceOracle { uint256 public pho_usd_price; uint256 public usdc_usd_price; + address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address public constant STETH_ADDRESS = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; address public constant WSTETH_ADDRESS = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; @@ -27,6 +28,7 @@ contract DummyOracle is IPriceOracle { pho_usd_price = 10 ** 6; usdc_usd_price = 10 ** 6; + priceFeeds[ETH_ADDRESS] = weth_usd_price; priceFeeds[WETH_ADDRESS] = weth_usd_price; priceFeeds[STETH_ADDRESS] = weth_usd_price; priceFeeds[WSTETH_ADDRESS] = weth_usd_price * 11 / 10; @@ -85,6 +87,7 @@ contract DummyOracle is IPriceOracle { } function setWethUSDPrice(uint256 _price) public { + priceFeeds[ETH_ADDRESS] = _price; priceFeeds[WETH_ADDRESS] = _price; priceFeeds[STETH_ADDRESS] = _price; priceFeeds[WSTETH_ADDRESS] = _price * 11 / 10; @@ -95,4 +98,8 @@ contract DummyOracle is IPriceOracle { function getPrice(address baseToken) external view returns (uint256) { return priceFeeds[baseToken]; } + + function addPriceFeed(address baseToken, uint256 price) external { + priceFeeds[baseToken] = price; + } } From ca001d1d4c7ffbdb0558aeae2fa51c1fb7f2c117 Mon Sep 17 00:00:00 2001 From: Niv Mimran Date: Fri, 23 Dec 2022 16:39:05 +0300 Subject: [PATCH 2/4] ESLint corrections --- .eslintrc.json | 1 + cli/cli.ts | 2 +- cli/commands/deploy.ts | 3 +- cli/commands/evm/mine.ts | 1 - cli/commands/modules/cdp/index.ts | 2 +- cli/commands/modules/cdp/read/overview.ts | 87 ++++++++++--------- .../modules/cdp/write/addCollateral.ts | 60 ++++++++----- cli/commands/modules/cdp/write/addDebt.ts | 37 ++++---- cli/commands/modules/cdp/write/close.ts | 47 +++++----- cli/commands/modules/cdp/write/liquidate.ts | 80 +++++++++-------- cli/commands/modules/cdp/write/open.ts | 51 ++++++----- .../modules/cdp/write/removeCollateral.ts | 34 ++++---- cli/commands/modules/cdp/write/removeDebt.ts | 55 +++++++----- cli/defaults.ts | 2 +- cli/env.ts | 2 +- cli/helpers.ts | 11 ++- cli/types.ts | 2 +- 17 files changed, 267 insertions(+), 210 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 291fa48..58c2f6f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,6 +17,7 @@ "@typescript-eslint/consistent-type-assertions": "off", "@typescript-eslint/no-unused-expressions": "off", "@typescript-eslint/space-before-function-paren": "off", + "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/member-delimiter-style": [ "error", { diff --git a/cli/cli.ts b/cli/cli.ts index 9d00ad5..c81292f 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -26,4 +26,4 @@ yargs .command(deployCommand) .command(modulesCommand) .demandCommand(1, 'Choose a command from the above list') - .help().argv + .help().argv.catch diff --git a/cli/commands/deploy.ts b/cli/commands/deploy.ts index 8907f41..46dc033 100644 --- a/cli/commands/deploy.ts +++ b/cli/commands/deploy.ts @@ -1,5 +1,4 @@ import { exec } from 'child_process' -import { copyFile } from 'fs/promises' import { writeFileSync, readdirSync, lstatSync, existsSync, mkdirSync } from 'fs' import path from 'path' import { logger } from '../logging' @@ -107,7 +106,7 @@ export async function updateAddresses(p: AddressParams): Promise { const { transactionType, address } = trx.additionalContracts[0] if (transactionType === 'CREATE') { updated[p.networkId].core.CurvePool = address - tempAddresses['CurvePool'] = address + tempAddresses.CurvePool = address } } else { if (trx.transactionType === 'CREATE') { diff --git a/cli/commands/evm/mine.ts b/cli/commands/evm/mine.ts index e4d611b..fdda2f0 100644 --- a/cli/commands/evm/mine.ts +++ b/cli/commands/evm/mine.ts @@ -2,7 +2,6 @@ import yargs, { Argv } from 'yargs' import { loadEnv } from '../../env' import { CLIArgs, CLIEnvironment } from '../../types' import { execute } from '../deploy' -import { logger } from '../../logging' const buildHelp = (): string => { const help = 'To fast forward -> evm fast-forward [seconds] [minutes] [hours]' diff --git a/cli/commands/modules/cdp/index.ts b/cli/commands/modules/cdp/index.ts index 8b8ed15..4af9316 100644 --- a/cli/commands/modules/cdp/index.ts +++ b/cli/commands/modules/cdp/index.ts @@ -1,4 +1,4 @@ -import yargs, { Argv, command } from 'yargs' +import yargs, { Argv } from 'yargs' import { overviewCommand } from './read/overview' import { addCollateralCommand } from './write/addCollateral' import { addDebtCommand } from './write/addDebt' diff --git a/cli/commands/modules/cdp/read/overview.ts b/cli/commands/modules/cdp/read/overview.ts index 285b9cb..515a982 100644 --- a/cli/commands/modules/cdp/read/overview.ts +++ b/cli/commands/modules/cdp/read/overview.ts @@ -1,44 +1,51 @@ import Table from 'cli-table3' -import { BigNumber, logger } from 'ethers'; -import { moduleDictionary } from '../../../../defaults'; -import { loadEnv } from "../../../../env" -import { getModuleAddress } from "../../../../helpers" -import { CLIArgs, CLIEnvironment } from "../../../../types" -import { execute } from "../../../deploy"; - - -const getOverview = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let moduleName = moduleDictionary.cdp[cliArgs.tokenType].default; - let cdpAddress = getModuleAddress(cliArgs.c, "cdp", cliArgs.tokenType, "default"); - - let moduleData = await cli.contracts.ModuleManager.modules(cdpAddress); - let [phoMinted, startTime, status] = moduleData.slice(-3); - let balances = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "pool()((uint256,uint256))"`); - let feesCollected = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "feesCollected()(uint256)"`); - - const table = new Table({ - head: [moduleName, 'Result'], - colWidths: [30, 50] - }) - - let [totalDebt, totalCollateral] = balances.substring(1, balances.length - 1).split(","); - let collRatio = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${totalCollateral} ${totalDebt}`); - - table.push(["Address", cdpAddress]); - table.push(["PHO Mined", phoMinted.toLocaleString()]); - table.push(["startTime", startTime.toString()]); - table.push(["status", status.toString()]); - table.push(["Total Collateral", totalCollateral]); - table.push(["Total Debt", totalDebt]); - table.push(["Collateral Ratio", collRatio.toString()]) - logger.info(table.toString()) - +import { logger } from 'ethers' +import { moduleDictionary } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' + +const getOverview = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const moduleName = moduleDictionary.cdp[cliArgs.tokenType].default + const cdpAddress = getModuleAddress(cliArgs.c, 'cdp', cliArgs.tokenType, 'default') + + const moduleData = await cli.contracts.ModuleManager.modules(cdpAddress) + const [phoMinted, startTime, status] = moduleData.slice(-3) + const balances = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "pool()((uint256,uint256))"` + ) + const feesCollected = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "feesCollected()(uint256)"` + ) + + const table = new Table({ + head: [moduleName, 'Result'], + colWidths: [30, 50] + }) + + const [totalDebt, totalCollateral]: string[] = balances + .substring(1, balances.length - 1) + .split(',') + const collRatio: string = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${totalCollateral} ${totalDebt}` + ) + + table.push(['Address', cdpAddress]) + table.push(['PHO Mined', phoMinted.toLocaleString()]) + table.push(['startTime', startTime.toString()]) + table.push(['status', status.toString()]) + table.push(['Total Collateral', totalCollateral]) + table.push(['Total Debt', totalDebt]) + table.push(['Collateral Ratio', collRatio.toString()]) + table.push(['feesCollected', feesCollected]) + logger.info(table.toString()) } export const overviewCommand = { - command: 'overview [tokenType]', - describe: 'CDP Mechanism overview', - handler: async (argv: CLIArgs): Promise => { - return await getOverview(await loadEnv(argv), argv) - } - } \ No newline at end of file + command: 'overview [tokenType]', + describe: 'CDP Mechanism overview', + handler: async (argv: CLIArgs): Promise => { + return await getOverview(await loadEnv(argv), argv) + } +} diff --git a/cli/commands/modules/cdp/write/addCollateral.ts b/cli/commands/modules/cdp/write/addCollateral.ts index 1d42bbe..cabaeaf 100644 --- a/cli/commands/modules/cdp/write/addCollateral.ts +++ b/cli/commands/modules/cdp/write/addCollateral.ts @@ -3,29 +3,29 @@ import { moduleDictionary, tokenAddresses } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' -import { CDPCollateralParams, CDPOpenParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { CDPCollateralParams, CLIArgs, CLIEnvironment } from '../../../../types' import { execute } from '../../../deploy' import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('addCollateral: Collateral token does not have a corresponding CDP') return {} as CDPCollateralParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "deposit") + const contractAddress: string = getModuleAddress(networkId, 'cdp', collateralToken, 'deposit') - if (collateralToken == 'wsteth') { + if (collateralToken === 'wsteth') { parameters = parameters.slice(1) - if (["steth", "weth", "eth"].indexOf(parameters[0]) == -1) { - logger.error("Deposit token is not supported"); - return {} as CDPCollateralParams; + if (!['steth', 'weth', 'eth'].includes(parameters[0])) { + logger.error('Deposit token is not supported') + return {} as CDPCollateralParams } } - if (parameters.length != 2) { + if (parameters.length !== 2) { logger.error('addCollateral: Not enough parameters were supplied') return {} as CDPCollateralParams } @@ -35,7 +35,7 @@ const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { return {} as CDPCollateralParams } - let collateralAmount = ethers.utils.parseUnits(parameters[1], 18) + const collateralAmount = ethers.utils.parseUnits(parameters[1], 18) return { contractAddress, @@ -45,8 +45,8 @@ const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { } } -const addCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPCollateralParams = getParams(cliArgs) +const addCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPCollateralParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('addCollateral: bad parameters') return @@ -56,31 +56,43 @@ const addCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { } } -const depositWithWrapper = async (params: CDPCollateralParams, cli: CLIEnvironment) => { - let depositTokenAddress = tokenAddresses[params.depositToken] - console.log(depositTokenAddress) +const depositWithWrapper = async ( + params: CDPCollateralParams, + cli: CLIEnvironment +): Promise => { + const depositTokenAddress: string = tokenAddresses[params.depositToken] if (!depositTokenAddress) { logger.error('addCollateral: deposit token address not found') return } if (cli.argv.c === 42069) { - let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${depositTokenAddress} "approve(address,uint256)" ${params.contractAddress} ${params.collateralAmount} --from ${cli.wallet.address} --json` - let res = JSON.parse(await execute(approveCommand)) - if (res.status == '0x1') { + const approveCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${depositTokenAddress} "approve(address,uint256)" ${ + params.contractAddress + } ${params.collateralAmount.toString()} --from ${cli.wallet.address} --json` + const res = JSON.parse(await execute(approveCommand)) + if (res.status === '0x1') { logger.info( - `${cli.wallet.address} approved ${params.collateralAmount} for ${depositTokenAddress}` + `${ + cli.wallet.address + } approved ${params.collateralAmount.toString()} for ${depositTokenAddress}` ) } } - let addCollateralCommand = `cast send --rpc-url ${cli.providerUrl} ${params.contractAddress} "addCollateral(uint256,address)" ${params.collateralAmount} ${depositTokenAddress} --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(addCollateralCommand)); - if (receipt.status == '0x1') { + const addCollateralCommand = `cast send --rpc-url ${cli.providerUrl} ${ + params.contractAddress + } "addCollateral(uint256,address)" ${params.collateralAmount.toString()} ${depositTokenAddress} --from ${ + cli.wallet.address + } --json` + const receipt = JSON.parse(await execute(addCollateralCommand)) + if (receipt.status === '0x1') { logger.info(`Added collateral to position for ${cli.wallet.address} successfully.`) - let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH'] - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionReceipt = await execute(positionCommand) + const cdpAddress: string = addresses[cli.argv.c].modules.CDPPool_wstETH + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionReceipt: string = await execute(positionCommand) logger.info(positionReceipt) } } diff --git a/cli/commands/modules/cdp/write/addDebt.ts b/cli/commands/modules/cdp/write/addDebt.ts index e4f1216..f0a1942 100644 --- a/cli/commands/modules/cdp/write/addDebt.ts +++ b/cli/commands/modules/cdp/write/addDebt.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' @@ -10,18 +10,18 @@ import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPDebtParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('addDebt: Collateral token does not have a corresponding CDP') return {} as CDPDebtParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + const contractAddress: string = getModuleAddress(networkId, 'cdp', collateralToken, 'default') - if (collateralToken == 'wsteth') { + if (collateralToken === 'wsteth') { parameters = parameters.slice(1) } - if (parameters.length != 1) { + if (parameters.length !== 1) { logger.error('addDebt: Not enough parameters were supplied') return {} as CDPDebtParams } @@ -31,36 +31,39 @@ const getParams = (cliArgs: CLIArgs): CDPDebtParams => { return {} as CDPDebtParams } - let debtAmount = ethers.utils.parseUnits(parameters[0], 18) + const debtAmount = ethers.utils.parseUnits(parameters[0], 18) return { contractAddress, collateralToken, - depositToken: "", + depositToken: '', debtAmount } } -const addDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPDebtParams = getParams(cliArgs) +const addDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPDebtParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('addDebt: bad parameters') return } - let moduleName = moduleDictionary.cdp[params.collateralToken].default; - let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + const moduleName: string = moduleDictionary.cdp[params.collateralToken].default + const cdpAddress: string = addresses[cli.argv.c].modules[moduleName] - let addDebtCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "addDebt(uint256)" ${params.debtAmount} --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(addDebtCommand)); - if (receipt.status == '0x1') { + const addDebtCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${cdpAddress} "addDebt(uint256)" ${params.debtAmount.toString()} --from ${ + cli.wallet.address + } --json` + const receipt = JSON.parse(await execute(addDebtCommand)) + if (receipt.status === '0x1') { logger.info(`Added debt to position for ${cli.wallet.address} successfully.`) - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionReceipt = await execute(positionCommand) + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionReceipt = await execute(positionCommand) logger.info(positionReceipt) } } - export const addDebtCommand = { command: 'add-debt', describe: 'Add debt to a position', diff --git a/cli/commands/modules/cdp/write/close.ts b/cli/commands/modules/cdp/write/close.ts index eb3ba9d..d9e81ad 100644 --- a/cli/commands/modules/cdp/write/close.ts +++ b/cli/commands/modules/cdp/write/close.ts @@ -1,5 +1,4 @@ -import { ethers } from 'ethers' -import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' @@ -10,52 +9,52 @@ import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPBaseParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('close: Collateral token does not have a corresponding CDP') return {} as CDPBaseParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + const contractAddress: string = getModuleAddress(networkId, 'cdp', collateralToken, 'default') return { contractAddress, collateralToken, - depositToken: "" + depositToken: '' } } -const close = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPBaseParams = getParams(cliArgs) +const close = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPBaseParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('close: bad parameters') return } - let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; - let moduleName = moduleDictionary.cdp[params.collateralToken].default; - let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + const { PHO: phoAddress, Kernel: kernelAddress }: Record = + addresses[cli.argv.c].core + const moduleName: string = moduleDictionary.cdp[params.collateralToken].default + const cdpAddress: string = addresses[cli.argv.c].modules[moduleName] + + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionResponse = await execute(positionCommand) + const debtAmount: string = positionResponse + .substring(1, positionResponse.length - 1) + .split(',')[0] - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionResponse = await execute(positionCommand); - let debtAmount = positionResponse.substring(1,positionResponse.length - 1).split(",")[0]; - if (cli.argv.c === 42069) { - let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${cli.wallet.address} --json` - let res = JSON.parse(await execute(approveCommand)) - if (res.status == '0x1') { - logger.info( - `${cli.wallet.address} approved ${debtAmount} for ${params.contractAddress}` - ) + const approveCommand: string = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${cli.wallet.address} --json` + const res = JSON.parse(await execute(approveCommand)) + if (res.status === '0x1') { + logger.info(`${cli.wallet.address} approved ${debtAmount} for ${params.contractAddress}`) } } - let closeCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "close()" --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(closeCommand)); - if (receipt.status == '0x1') { + const closeCommand: string = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "close()" --from ${cli.wallet.address} --json` + const receipt = JSON.parse(await execute(closeCommand)) + if (receipt.status === '0x1') { logger.info(`Closed position for ${cli.wallet.address} successfully.`) } } - export const closeCommand = { command: 'close', describe: 'close a position', diff --git a/cli/commands/modules/cdp/write/liquidate.ts b/cli/commands/modules/cdp/write/liquidate.ts index 706c846..f96955d 100644 --- a/cli/commands/modules/cdp/write/liquidate.ts +++ b/cli/commands/modules/cdp/write/liquidate.ts @@ -1,81 +1,91 @@ -import { ethers } from 'ethers' -import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' import { CDPLiquidationParams, CLIArgs, CLIEnvironment } from '../../../../types' import { execute } from '../../../deploy' import addresses from '../../../../../addresses.json' -import { cli } from 'winston/lib/winston/config' -import { number } from 'yargs' const getParams = (cli: CLIEnvironment, cliArgs: CLIArgs): CDPLiquidationParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('liquidate: Collateral token does not have a corresponding CDP') return {} as CDPLiquidationParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") - let cdpOwner = parameters[1]; + const contractAddress: string = getModuleAddress(networkId, 'cdp', collateralToken, 'default') + const cdpOwner = parameters[1] if (!cdpOwner) { - logger.error('liquidate: missing cdp owner address'); - return {} as CDPLiquidationParams; + logger.error('liquidate: missing cdp owner address') + return {} as CDPLiquidationParams } return { contractAddress, collateralToken, - depositToken: "", - cdpOwner: cdpOwner, + depositToken: '', + cdpOwner, liquidator: cli.wallet.address } } -const liquidate = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPLiquidationParams = getParams(cli, cliArgs) +const liquidate = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPLiquidationParams = getParams(cli, cliArgs) if (!params.collateralToken) { logger.error('liquidate: bad parameters') return } - let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; - let moduleName = moduleDictionary.cdp[params.collateralToken].default; - let cdpAddress = addresses[cli.argv.c].modules[moduleName]; + const { PHO: phoAddress, Kernel: kernelAddress }: Record = + addresses[cli.argv.c].core + const moduleName: string = moduleDictionary.cdp[params.collateralToken].default + const cdpAddress: string = addresses[cli.argv.c].modules[moduleName] + + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${params.cdpOwner}` + const positionResponse = await execute(positionCommand) + const [debtAmount, collateralAmount]: string[] = positionResponse + .substring(1, positionResponse.length - 1) + .split(',') - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${params.cdpOwner}` - let positionResponse = await execute(positionCommand); - let [ debtAmount, collateralAmount ] = positionResponse.substring(1, positionResponse.length - 1).split(","); - - let collRatio = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${collateralAmount} ${debtAmount}`); - let minCR = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "minCR()(uint256)"`); + const collRatio: string = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${collateralAmount} ${debtAmount}` + ) + const minCR: string = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "minCR()(uint256)"` + ) if (Number(collRatio) >= Number(minCR)) { - logger.error(`Collateral ratio is ${collRatio.substring(0,3)}% and not in liquidation zone. Liquidation aborted`); - return; + logger.error( + `Collateral ratio is ${collRatio.substring( + 0, + 3 + )}% and not in liquidation zone. Liquidation aborted` + ) + return } - - logger.info(`Collateral Ratio: ${collRatio.substring(0,3)}%. Executing liquidation...`) + + logger.info(`Collateral Ratio: ${collRatio.substring(0, 3)}%. Executing liquidation...`) if (cli.argv.c === 42069) { - let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${params.liquidator} --json` - let res = JSON.parse(await execute(approveCommand)) - if (res.status == '0x1') { + const approveCommand: string = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${debtAmount} --from ${params.liquidator} --json` + const res = JSON.parse(await execute(approveCommand)) + if (res.status === '0x1') { logger.info( - `${params.liquidator} approved ${debtAmount} for ${params.contractAddress}` + `${params.liquidator} approved ${debtAmount.toString()} for ${params.contractAddress}` ) } } - let liquidateCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "liquidate(address)" ${params.cdpOwner} --from ${params.liquidator} --json` - let receipt = JSON.parse(await execute(liquidateCommand)); - if (receipt.status == '0x1') { - logger.info(`Liquidated position for ${cli.wallet.address} by ${params.liquidator} successfully.`) + const liquidateCommand: string = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "liquidate(address)" ${params.cdpOwner} --from ${params.liquidator} --json` + const receipt = JSON.parse(await execute(liquidateCommand)) + if (receipt.status === '0x1') { + logger.info( + `Liquidated position for ${cli.wallet.address} by ${params.liquidator} successfully.` + ) } } - export const liquidateCommand = { command: 'liquidate', describe: 'liquidate a position', diff --git a/cli/commands/modules/cdp/write/open.ts b/cli/commands/modules/cdp/write/open.ts index db8b46a..72b23ee 100644 --- a/cli/commands/modules/cdp/write/open.ts +++ b/cli/commands/modules/cdp/write/open.ts @@ -10,18 +10,18 @@ import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPOpenParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('openPosition: Collateral token does not have a corresponding CDP') return {} as CDPOpenParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "deposit") + const contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, 'deposit') - if (collateralToken == 'wsteth') { + if (collateralToken === 'wsteth') { parameters = parameters.slice(1) } - if (parameters.length != 3) { + if (parameters.length !== 3) { logger.error('openPosition: Not enough parameters were supplied') return {} as CDPOpenParams } @@ -31,8 +31,8 @@ const getParams = (cliArgs: CLIArgs): CDPOpenParams => { return {} as CDPOpenParams } - let collateralAmount = ethers.utils.parseUnits(parameters[1], 18) - let debtAmount = ethers.utils.parseUnits(parameters[2], 18) + const collateralAmount = ethers.utils.parseUnits(parameters[1], 18) + const debtAmount = ethers.utils.parseUnits(parameters[2], 18) return { contractAddress, @@ -43,8 +43,8 @@ const getParams = (cliArgs: CLIArgs): CDPOpenParams => { } } -const openPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPOpenParams = getParams(cliArgs) +const openPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPOpenParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('openPosition: bad parameters') return @@ -54,31 +54,40 @@ const openPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { } } -const depositWithWrapper = async (params: CDPOpenParams, cli: CLIEnvironment) => { - let depositTokenAddress = tokenAddresses[params.depositToken] - console.log(depositTokenAddress) +const depositWithWrapper = async (params: CDPOpenParams, cli: CLIEnvironment): Promise => { + const depositTokenAddress: string = tokenAddresses[params.depositToken] if (!depositTokenAddress) { logger.error('openPosition: deposit token address not found') return } if (cli.argv.c === 42069) { - let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${depositTokenAddress} "approve(address,uint256)" ${params.contractAddress} ${params.collateralAmount} --from ${cli.wallet.address} --json` - let res = JSON.parse(await execute(approveCommand)) - if (res.status == '0x1') { + const approveCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${depositTokenAddress} "approve(address,uint256)" ${ + params.contractAddress + } ${params.collateralAmount.toString()} --from ${cli.wallet.address} --json` + const res = JSON.parse(await execute(approveCommand)) + if (res.status === '0x1') { logger.info( - `${cli.wallet.address} approved ${params.collateralAmount} for ${depositTokenAddress}` + `${ + cli.wallet.address + } approved ${params.collateralAmount.toString()} for ${depositTokenAddress}` ) } } - let openCommand = `cast send --rpc-url ${cli.providerUrl} ${params.contractAddress} "open(uint256,uint256,address)" ${params.collateralAmount} ${params.debtAmount} ${depositTokenAddress} --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(openCommand)); - if (receipt.status == '0x1') { + const openCommand: string = `cast send --rpc-url ${cli.providerUrl} ${ + params.contractAddress + } "open(uint256,uint256,address)" ${params.collateralAmount.toString()} ${params.debtAmount.toString()} ${depositTokenAddress} --from ${ + cli.wallet.address + } --json` + const receipt = JSON.parse(await execute(openCommand)) + if (receipt.status === '0x1') { logger.info(`Open a new debt position for ${cli.wallet.address} successfully.`) - let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH'] - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionReceipt = await execute(positionCommand) + const cdpAddress: string = addresses[cli.argv.c].modules.CDPPool_wstETH + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionReceipt = await execute(positionCommand) logger.info(positionReceipt) } } diff --git a/cli/commands/modules/cdp/write/removeCollateral.ts b/cli/commands/modules/cdp/write/removeCollateral.ts index 40f9ce3..7176c49 100644 --- a/cli/commands/modules/cdp/write/removeCollateral.ts +++ b/cli/commands/modules/cdp/write/removeCollateral.ts @@ -1,27 +1,27 @@ import { ethers } from 'ethers' -import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' -import { CDPCollateralParams, CDPOpenParams, CLIArgs, CLIEnvironment } from '../../../../types' +import { CDPCollateralParams, CLIArgs, CLIEnvironment } from '../../../../types' import { execute } from '../../../deploy' import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('removeCollateral: Collateral token does not have a corresponding CDP') return {} as CDPCollateralParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + const contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, 'default') - if (collateralToken == 'wsteth') { + if (collateralToken === 'wsteth') { parameters = parameters.slice(1) } - if (parameters.length != 1) { + if (parameters.length !== 1) { logger.error('removeCollateral: Not enough parameters were supplied') return {} as CDPCollateralParams } @@ -31,7 +31,7 @@ const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { return {} as CDPCollateralParams } - let collateralAmount = ethers.utils.parseUnits(parameters[0], 18) + const collateralAmount = ethers.utils.parseUnits(parameters[0], 18) return { contractAddress, @@ -41,19 +41,23 @@ const getParams = (cliArgs: CLIArgs): CDPCollateralParams => { } } -const removeCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPCollateralParams = getParams(cliArgs) +const removeCollateral = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPCollateralParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('removeCollateral: bad parameters') return } - let cdpAddress = addresses[cli.argv.c].modules['CDPPool_wstETH']; - let removeCollateralCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "removeCollateral(uint256)" ${params.collateralAmount} --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(removeCollateralCommand)); - if (receipt.status == '0x1') { + const cdpAddress: string = addresses[cli.argv.c].modules.CDPPool_wstETH + const removeCollateralCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${cdpAddress} "removeCollateral(uint256)" ${params.collateralAmount.toString()} --from ${ + cli.wallet.address + } --json` + const receipt = JSON.parse(await execute(removeCollateralCommand)) + if (receipt.status === '0x1') { logger.info(`Remove collateral from position for ${cli.wallet.address} successfully.`) - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionReceipt = await execute(positionCommand) + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionReceipt = await execute(positionCommand) logger.info(positionReceipt) } } diff --git a/cli/commands/modules/cdp/write/removeDebt.ts b/cli/commands/modules/cdp/write/removeDebt.ts index d25bf98..86c5325 100644 --- a/cli/commands/modules/cdp/write/removeDebt.ts +++ b/cli/commands/modules/cdp/write/removeDebt.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { moduleDictionary, tokenAddresses } from '../../../../defaults' +import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' import { getModuleAddress } from '../../../../helpers' import { logger } from '../../../../logging' @@ -10,19 +10,19 @@ import addresses from '../../../../../addresses.json' const getParams = (cliArgs: CLIArgs): CDPDebtParams => { let { _: parameters, c: networkId } = cliArgs parameters = parameters.slice(3) - let collateralToken = parameters[0] + const collateralToken = parameters[0] if (!moduleDictionary.cdp[collateralToken]) { logger.error('removeDebt: Collateral token does not have a corresponding CDP') return {} as CDPDebtParams } - let contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, "default") + const contractAddress = getModuleAddress(networkId, 'cdp', collateralToken, 'default') console.log(contractAddress) - if (collateralToken == 'wsteth') { + if (collateralToken === 'wsteth') { parameters = parameters.slice(1) } - if (parameters.length != 1) { + if (parameters.length !== 1) { logger.error('removeDebt: Not enough parameters were supplied') return {} as CDPDebtParams } @@ -32,48 +32,57 @@ const getParams = (cliArgs: CLIArgs): CDPDebtParams => { return {} as CDPDebtParams } - let debtAmount = ethers.utils.parseUnits(parameters[0], 18) + const debtAmount = ethers.utils.parseUnits(parameters[0], 18) return { contractAddress, collateralToken, - depositToken: "", + depositToken: '', debtAmount } } -const removeDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs) => { - let params: CDPDebtParams = getParams(cliArgs) +const removeDebt = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const params: CDPDebtParams = getParams(cliArgs) if (!params.collateralToken) { logger.error('removeDebt: bad parameters') return } - let { PHO: phoAddress, Kernel: kernelAddress} = addresses[cli.argv.c].core; - let moduleName = moduleDictionary.cdp[params.collateralToken].default; - let cdpAddress = addresses[cli.argv.c].modules[moduleName]; - + const { PHO: phoAddress, Kernel: kernelAddress }: Record = + addresses[cli.argv.c].core + const moduleName: string = moduleDictionary.cdp[params.collateralToken].default + const cdpAddress: string = addresses[cli.argv.c].modules[moduleName] if (cli.argv.c === 42069) { - let approveCommand = `cast send --rpc-url ${cli.providerUrl} ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${params.debtAmount} --from ${cli.wallet.address} --json` - let res = JSON.parse(await execute(approveCommand)) - if (res.status == '0x1') { + const approveCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${phoAddress} "approve(address,uint256)" ${kernelAddress} ${params.debtAmount.toString()} --from ${ + cli.wallet.address + } --json` + const res = JSON.parse(await execute(approveCommand)) + if (res.status === '0x1') { logger.info( - `${cli.wallet.address} approved ${params.debtAmount} for ${params.contractAddress}` + `${cli.wallet.address} approved ${params.debtAmount.toString()} for ${ + params.contractAddress + }` ) } } - let removeDebtCommand = `cast send --rpc-url ${cli.providerUrl} ${cdpAddress} "removeDebt(uint256)" ${params.debtAmount} --from ${cli.wallet.address} --json` - let receipt = JSON.parse(await execute(removeDebtCommand)); - if (receipt.status == '0x1') { + const removeDebtCommand: string = `cast send --rpc-url ${ + cli.providerUrl + } ${cdpAddress} "removeDebt(uint256)" ${params.debtAmount.toString()} --from ${ + cli.wallet.address + } --json` + const receipt = JSON.parse(await execute(removeDebtCommand)) + if (receipt.status === '0x1') { logger.info(`Removed debt from position for ${cli.wallet.address} successfully.`) - let positionCommand = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` - let positionReceipt = await execute(positionCommand) + const positionCommand: string = `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cli.wallet.address}` + const positionReceipt = await execute(positionCommand) logger.info(positionReceipt) } } - export const removeDebtCommand = { command: 'remove-debt', describe: 'Remove debt from a position', diff --git a/cli/defaults.ts b/cli/defaults.ts index 58f994e..c010405 100644 --- a/cli/defaults.ts +++ b/cli/defaults.ts @@ -58,7 +58,7 @@ export const moduleDictionary = { cdp: { wsteth: { deposit: 'wstETHCDPWrapper', - default: "CDPPool_wstETH" + default: 'CDPPool_wstETH' } } } diff --git a/cli/env.ts b/cli/env.ts index 416eaa0..9d2674d 100644 --- a/cli/env.ts +++ b/cli/env.ts @@ -28,7 +28,7 @@ export const loadEnv = async (argv: CLIArgs): Promise => { const walletAddress = await wallet.getAddress() const { c: networkId } = argv if (!verifyNetwork(networkId)) { - logger.info(`Network id ${networkId} is invalid`) + logger.info(`Network id ${networkId as string} is invalid`) } const { core: coreContracts, modules: modulesContracts } = diff --git a/cli/helpers.ts b/cli/helpers.ts index 86faa6f..d02cb2d 100644 --- a/cli/helpers.ts +++ b/cli/helpers.ts @@ -43,13 +43,18 @@ export const getNetworkContractAddresses = (networkId: number): NetworkContracts return addresses[networkId] } -export const getModuleAddress = (networkId: number, type: string, identifier: string, action: string): string => { - let contractName = moduleDictionary[type][identifier][action] +export const getModuleAddress = ( + networkId: number, + type: string, + identifier: string, + action: string +): string => { + const contractName = moduleDictionary[type][identifier][action] if (!contractName || !verifyNetwork(networkId)) { logger.error('getModuleAddress: No module was found with the given params') return '' } - let modules = addresses[networkId].modules + const modules = addresses[networkId].modules if (!modules) { logger.error('getModuleAddress: Network does not have deployed contracts') } diff --git a/cli/types.ts b/cli/types.ts index 98abbb6..2f79ea5 100644 --- a/cli/types.ts +++ b/cli/types.ts @@ -100,4 +100,4 @@ export interface CDPDebtParams extends CDPBaseParams { export interface CDPLiquidationParams extends CDPBaseParams { cdpOwner: string liquidator: string -} \ No newline at end of file +} From d505792d179a726394012f8987a5f8cdaee7b3fa Mon Sep 17 00:00:00 2001 From: Niv Mimran Date: Fri, 23 Dec 2022 17:30:06 +0300 Subject: [PATCH 3/4] Write CDP Position data fetcher --- cli/commands/modules/cdp/index.ts | 2 ++ cli/commands/modules/cdp/read/overview.ts | 16 +++++----- cli/commands/modules/cdp/read/position.ts | 39 +++++++++++++++++++++++ cli/helpers.ts | 4 +++ 4 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 cli/commands/modules/cdp/read/position.ts diff --git a/cli/commands/modules/cdp/index.ts b/cli/commands/modules/cdp/index.ts index 4af9316..82f4feb 100644 --- a/cli/commands/modules/cdp/index.ts +++ b/cli/commands/modules/cdp/index.ts @@ -1,5 +1,6 @@ import yargs, { Argv } from 'yargs' import { overviewCommand } from './read/overview' +import { positionCommand } from './read/position' import { addCollateralCommand } from './write/addCollateral' import { addDebtCommand } from './write/addDebt' import { closeCommand } from './write/close' @@ -21,6 +22,7 @@ export const cdpCommand = { .command(closeCommand) .command(liquidateCommand) .command(overviewCommand) + .command(positionCommand) }, handler: (): void => { yargs.showHelp() diff --git a/cli/commands/modules/cdp/read/overview.ts b/cli/commands/modules/cdp/read/overview.ts index 515a982..1e103a4 100644 --- a/cli/commands/modules/cdp/read/overview.ts +++ b/cli/commands/modules/cdp/read/overview.ts @@ -2,7 +2,7 @@ import Table from 'cli-table3' import { logger } from 'ethers' import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' -import { getModuleAddress } from '../../../../helpers' +import { getModuleAddress, toReadablePrice } from '../../../../helpers' import { CLIArgs, CLIEnvironment } from '../../../../types' import { execute } from '../../../deploy' @@ -30,15 +30,15 @@ const getOverview = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise const collRatio: string = await execute( `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${totalCollateral} ${totalDebt}` ) - + table.push(['Address', cdpAddress]) - table.push(['PHO Mined', phoMinted.toLocaleString()]) - table.push(['startTime', startTime.toString()]) + table.push(['PHO Mined', toReadablePrice(phoMinted.toLocaleString())]) + table.push(['startTime', startTime.toString().concat(` (${new Date(Number(startTime) * 1000).toLocaleDateString()})`)]) table.push(['status', status.toString()]) - table.push(['Total Collateral', totalCollateral]) - table.push(['Total Debt', totalDebt]) - table.push(['Collateral Ratio', collRatio.toString()]) - table.push(['feesCollected', feesCollected]) + table.push(['Total Collateral', toReadablePrice(totalCollateral)]) + table.push(['Total Debt', toReadablePrice(totalDebt)]) + table.push(['Collateral Ratio', collRatio.slice(0, -3).concat("%")]) + table.push(['feesCollected', toReadablePrice(feesCollected)]) logger.info(table.toString()) } diff --git a/cli/commands/modules/cdp/read/position.ts b/cli/commands/modules/cdp/read/position.ts new file mode 100644 index 0000000..028ed87 --- /dev/null +++ b/cli/commands/modules/cdp/read/position.ts @@ -0,0 +1,39 @@ +import Table from 'cli-table3' +import { logger } from 'ethers' +import { moduleDictionary } from '../../../../defaults' +import { loadEnv } from '../../../../env' +import { getModuleAddress } from '../../../../helpers' +import { CLIArgs, CLIEnvironment } from '../../../../types' +import { execute } from '../../../deploy' +import { toReadablePrice } from '../../../../helpers' + +const getPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + const cdpOwner: string = cliArgs.cdpOwner; + const moduleName = moduleDictionary.cdp[cliArgs.tokenType].default + const cdpAddress = getModuleAddress(cliArgs.c, 'cdp', cliArgs.tokenType, 'default') + + let cdpData: string = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cdpOwner}`); + let [ debt, collateral ]: string[] = cdpData.substring(1, cdpData.length -1).split(",") + const collRatio: string = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${collateral} ${debt}` + ) + + const table = new Table({ + head: [moduleName, 'Result'], + colWidths: [30, 50] + }) + + table.push(["CDP Owner", cdpOwner]) + table.push(["Debt", toReadablePrice(debt)]); + table.push(["Collateral", toReadablePrice(collateral)]) + table.push(["Collateral Ratio", collRatio.slice(0, -3).concat("%")]) + logger.info(table.toString()); +} + +export const positionCommand = { + command: 'position [tokenType] [cdpOwner]', + describe: 'Get information regarding an open position', + handler: async (argv: CLIArgs): Promise => { + return await getPosition(await loadEnv(argv), argv) + } +} diff --git a/cli/helpers.ts b/cli/helpers.ts index d02cb2d..a64bfc1 100644 --- a/cli/helpers.ts +++ b/cli/helpers.ts @@ -60,3 +60,7 @@ export const getModuleAddress = ( } return modules[contractName] } + +export const toReadablePrice = (value: string): string => { + return value.slice(0, value.length - 18) + "." + value.slice(-18); +} \ No newline at end of file From 41433a628b1861e09411583be6ae19202282080a Mon Sep 17 00:00:00 2001 From: Niv Mimran Date: Fri, 23 Dec 2022 17:30:48 +0300 Subject: [PATCH 4/4] Run ESLinter --- cli/commands/modules/cdp/read/overview.ts | 9 ++++++--- cli/commands/modules/cdp/read/position.ts | 21 +++++++++++---------- cli/helpers.ts | 4 ++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/cli/commands/modules/cdp/read/overview.ts b/cli/commands/modules/cdp/read/overview.ts index 1e103a4..16b5991 100644 --- a/cli/commands/modules/cdp/read/overview.ts +++ b/cli/commands/modules/cdp/read/overview.ts @@ -30,14 +30,17 @@ const getOverview = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise const collRatio: string = await execute( `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${totalCollateral} ${totalDebt}` ) - + table.push(['Address', cdpAddress]) table.push(['PHO Mined', toReadablePrice(phoMinted.toLocaleString())]) - table.push(['startTime', startTime.toString().concat(` (${new Date(Number(startTime) * 1000).toLocaleDateString()})`)]) + table.push([ + 'startTime', + startTime.toString().concat(` (${new Date(Number(startTime) * 1000).toLocaleDateString()})`) + ]) table.push(['status', status.toString()]) table.push(['Total Collateral', toReadablePrice(totalCollateral)]) table.push(['Total Debt', toReadablePrice(totalDebt)]) - table.push(['Collateral Ratio', collRatio.slice(0, -3).concat("%")]) + table.push(['Collateral Ratio', collRatio.slice(0, -3).concat('%')]) table.push(['feesCollected', toReadablePrice(feesCollected)]) logger.info(table.toString()) } diff --git a/cli/commands/modules/cdp/read/position.ts b/cli/commands/modules/cdp/read/position.ts index 028ed87..3373c53 100644 --- a/cli/commands/modules/cdp/read/position.ts +++ b/cli/commands/modules/cdp/read/position.ts @@ -2,18 +2,19 @@ import Table from 'cli-table3' import { logger } from 'ethers' import { moduleDictionary } from '../../../../defaults' import { loadEnv } from '../../../../env' -import { getModuleAddress } from '../../../../helpers' +import { getModuleAddress, toReadablePrice } from '../../../../helpers' import { CLIArgs, CLIEnvironment } from '../../../../types' import { execute } from '../../../deploy' -import { toReadablePrice } from '../../../../helpers' const getPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { - const cdpOwner: string = cliArgs.cdpOwner; + const cdpOwner: string = cliArgs.cdpOwner const moduleName = moduleDictionary.cdp[cliArgs.tokenType].default const cdpAddress = getModuleAddress(cliArgs.c, 'cdp', cliArgs.tokenType, 'default') - let cdpData: string = await execute(`cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cdpOwner}`); - let [ debt, collateral ]: string[] = cdpData.substring(1, cdpData.length -1).split(",") + const cdpData: string = await execute( + `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "cdps(address)((uint256,uint256))" ${cdpOwner}` + ) + const [debt, collateral]: string[] = cdpData.substring(1, cdpData.length - 1).split(',') const collRatio: string = await execute( `cast call --rpc-url ${cli.providerUrl} ${cdpAddress} "computeCR(uint256,uint256)(uint256)" ${collateral} ${debt}` ) @@ -23,11 +24,11 @@ const getPosition = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise colWidths: [30, 50] }) - table.push(["CDP Owner", cdpOwner]) - table.push(["Debt", toReadablePrice(debt)]); - table.push(["Collateral", toReadablePrice(collateral)]) - table.push(["Collateral Ratio", collRatio.slice(0, -3).concat("%")]) - logger.info(table.toString()); + table.push(['CDP Owner', cdpOwner]) + table.push(['Debt', toReadablePrice(debt)]) + table.push(['Collateral', toReadablePrice(collateral)]) + table.push(['Collateral Ratio', collRatio.slice(0, -3).concat('%')]) + logger.info(table.toString()) } export const positionCommand = { diff --git a/cli/helpers.ts b/cli/helpers.ts index a64bfc1..229f667 100644 --- a/cli/helpers.ts +++ b/cli/helpers.ts @@ -62,5 +62,5 @@ export const getModuleAddress = ( } export const toReadablePrice = (value: string): string => { - return value.slice(0, value.length - 18) + "." + value.slice(-18); -} \ No newline at end of file + return value.slice(0, value.length - 18) + '.' + value.slice(-18) +}