diff --git a/.prettierignore b/.prettierignore index 87a767ce5e..a187712db3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,5 @@ src/assets/font src/types/contracts src/clients/subgraph/gql/mainnet.ts src/clients/subgraph/gql/testnet.ts +src/packages/contracts/types/contracts +src/storybook-static \ No newline at end of file diff --git a/package.json b/package.json index 37ed60dd76..6e67855065 100755 --- a/package.json +++ b/package.json @@ -22,10 +22,11 @@ "generate-subgraph-types:testnet": "yarn graphql-codegen --config ./src/config/codegen.ts --project testnet", "generate-subgraph-types": "npm-run-all --parallel generate-subgraph-types:mainnet generate-subgraph-types:testnet", "generate-contract-types": "rimraf src/types/contracts && typechain --target=ethers-v5 --out-dir=src/types/contracts 'src/constants/contracts/abis/*.json'", + "generate-contract-types:contract-package": "rimraf ./src/types/contracts && typechain --target=ethers-v5 --out-dir=./src/packages/contracts/types/contracts/isolatedPools './node_modules/@venusprotocol/isolated-pools/artifacts/contracts/!(Factories)/**/*[!.dbg].json' && typechain --target=ethers-v5 --out-dir=./src/packages/contracts/types/contracts/oracle './node_modules/@venusprotocol/oracle/artifacts/contracts/!(Factories)/**/*[!.dbg].json' && typechain --target=ethers-v5 --out-dir=./src/packages/contracts/types/contracts/venusProtocol './node_modules/@venusprotocol/venus-protocol/artifacts/contracts/!(Factories)/**/*[!.dbg].json' && typechain --target=ethers-v5 --out-dir=./src/packages/contracts/types/contracts/xvsVesting './node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Tokens/XVS/XVSVesting.sol/XVSVesting.json' && typechain --target=ethers-v5 --out-dir=./src/packages/contracts/types/contracts/others './src/packages/contracts/contractInfos/externalAbis/*.json'", "generate-pancake-swap-tokens": "node ./scripts/generatePancakeSwapTokens.mjs && yarn prettier --write ./src/constants/tokens/swap/mainnetPancakeSwapTokens.ts", "generate-version-file": "genversion --es6 --semi src/constants/version.ts", "husky:install": "husky install", - "postinstall": "npm-run-all --parallel husky:install generate-contract-types generate-subgraph-types generate-pancake-swap-tokens generate-version-file", + "postinstall": "npm-run-all --parallel husky:install generate-contract-types generate-contract-types:contract-package generate-subgraph-types generate-version-file && yarn generate-pancake-swap-tokens", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "regression": "reg-suit run" @@ -47,8 +48,10 @@ "@sentry/tracing": "^7.43.0", "@uiw/react-markdown-preview": "^4.1.13", "@uiw/react-md-editor": "^3.14.3", + "@venusprotocol/governance-contracts": "^1.0.0", "@venusprotocol/isolated-pools": "^1.1.0", "@venusprotocol/oracle": "^1.7.0", + "@venusprotocol/venus-protocol": "^3.0.0-dev.13", "bignumber.js": "^9.0.0", "copy-to-clipboard": "^3.3.1", "date-fns": "^2.28.0", @@ -80,6 +83,8 @@ }, "devDependencies": { "@commitlint/config-conventional": "^17.6.6", + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", "@graphql-codegen/add": "^5.0.0", "@graphql-codegen/cli": "^4.0.1", "@graphql-codegen/client-preset": "^4.0.1", @@ -103,7 +108,7 @@ "@testing-library/react": "^12.1.4", "@testing-library/react-hooks": "^8.0.1", "@trivago/prettier-plugin-sort-imports": "^3.2.0", - "@typechain/ethers-v5": "^10.2.0", + "@typechain/ethers-v5": "^11.1.0", "@types/lodash": "^4.14.178", "@types/react": "^17.0.01", "@types/react-dom": "^17.0.11", @@ -156,7 +161,7 @@ "stylelint-config-prettier": "^9.0.3", "stylelint-config-standard": "^18.3.0", "stylelint-scss": "^3.9.2", - "typechain": "^8.1.1", + "typechain": "^8.3.0", "typescript": "^4.5.5", "validate-branch-name": "^1.3.0", "vite": "^4.3.9", diff --git a/src/config/index.ts b/src/config/index.ts index 0a461de07c..c8d0610d61 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -68,7 +68,7 @@ const isLocalServer = import.meta.env.DEV && environment !== 'ci' && environment const isOnTestnet = environment === 'testnet' || environment === 'storybook' || environment === 'ci'; -const chainId: BscChainId = isOnTestnet ? 97 : 56; +const chainId: BscChainId = isOnTestnet ? BscChainId.TESTNET : BscChainId.MAINNET; const localRpcUrl = environment === 'mainnet' || environment === 'preview' diff --git a/src/packages/contracts/.gitignore b/src/packages/contracts/.gitignore new file mode 100644 index 0000000000..b8a7f9ccc0 --- /dev/null +++ b/src/packages/contracts/.gitignore @@ -0,0 +1,2 @@ +# Contract types, automatically generated by TypeChain (https://github.com/dethcrypto/TypeChain) +/types/contracts/ diff --git a/src/packages/contracts/contractInfos/externalAbis/maximillion.json b/src/packages/contracts/contractInfos/externalAbis/maximillion.json new file mode 100644 index 0000000000..08d6005520 --- /dev/null +++ b/src/packages/contracts/contractInfos/externalAbis/maximillion.json @@ -0,0 +1,38 @@ +[ + { + "inputs": [{ "internalType": "contract VBNB", "name": "vBnb_", "type": "address" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "constant": false, + "inputs": [{ "internalType": "address", "name": "borrower", "type": "address" }], + "name": "repayBehalf", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "contract VBNB", "name": "vBnb_", "type": "address" } + ], + "name": "repayBehalfExplicit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "vBnb", + "outputs": [{ "internalType": "contract VBNB", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/packages/contracts/contractInfos/externalAbis/multicall.json b/src/packages/contracts/contractInfos/externalAbis/multicall.json new file mode 100644 index 0000000000..d4f9b8c41b --- /dev/null +++ b/src/packages/contracts/contractInfos/externalAbis/multicall.json @@ -0,0 +1,236 @@ +[ + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate", + "outputs": [ + { "internalType": "uint256", "name": "blockNumber", "type": "uint256" }, + { "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bool", "name": "allowFailure", "type": "bool" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call3[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "success", "type": "bool" }, + { "internalType": "bytes", "name": "returnData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bool", "name": "allowFailure", "type": "bool" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call3Value[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3Value", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "success", "type": "bool" }, + { "internalType": "bytes", "name": "returnData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "blockAndAggregate", + "outputs": [ + { "internalType": "uint256", "name": "blockNumber", "type": "uint256" }, + { "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }, + { + "components": [ + { "internalType": "bool", "name": "success", "type": "bool" }, + { "internalType": "bytes", "name": "returnData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getBasefee", + "outputs": [{ "internalType": "uint256", "name": "basefee", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }], + "name": "getBlockHash", + "outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumber", + "outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [{ "internalType": "uint256", "name": "chainid", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockCoinbase", + "outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockDifficulty", + "outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockGasLimit", + "outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockTimestamp", + "outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "addr", "type": "address" }], + "name": "getEthBalance", + "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastBlockHash", + "outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "requireSuccess", "type": "bool" }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryAggregate", + "outputs": [ + { + "components": [ + { "internalType": "bool", "name": "success", "type": "bool" }, + { "internalType": "bytes", "name": "returnData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bool", "name": "requireSuccess", "type": "bool" }, + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "bytes", "name": "callData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryBlockAndAggregate", + "outputs": [ + { "internalType": "uint256", "name": "blockNumber", "type": "uint256" }, + { "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }, + { + "components": [ + { "internalType": "bool", "name": "success", "type": "bool" }, + { "internalType": "bytes", "name": "returnData", "type": "bytes" } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/src/packages/contracts/contractInfos/fixedAddressContractInfos.ts b/src/packages/contracts/contractInfos/fixedAddressContractInfos.ts new file mode 100644 index 0000000000..452f245ab7 --- /dev/null +++ b/src/packages/contracts/contractInfos/fixedAddressContractInfos.ts @@ -0,0 +1,161 @@ +import type { JsonFragment } from '@ethersproject/abi'; +import { abi as poolLensAbi } from '@venusprotocol/isolated-pools/artifacts/contracts/Lens/PoolLens.sol/PoolLens.json'; +import isolatedPoolsMainnetDeployments from '@venusprotocol/isolated-pools/deployments/bscmainnet.json'; +import isolatedPoolsTestnetDeployments from '@venusprotocol/isolated-pools/deployments/bsctestnet.json'; +import { abi as mainPoolComptrollerAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Comptroller/Comptroller.sol/Comptroller.json'; +import { abi as unitrollerAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Comptroller/Unitroller.sol/Unitroller.json'; +import { abi as governorBravoDelegatorAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Governance/GovernorBravoDelegator.sol/GovernorBravoDelegator.json'; +import { abi as venusLensAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Lens/VenusLens.sol/VenusLens.json'; +import { abi as vrtConverterAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VRT/VRTConverter.sol/VRTConverter.json'; +import { abi as xvsVestingAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Tokens/XVS/XVSVesting.sol/XVSVesting.json'; +import { abi as vaiVaultAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Vault/VAIVault.sol/VAIVault.json'; +import { abi as xvsVaultAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/XVSVault/XVSVault.sol/XVSVault.json'; +import venusProtocolMainnetDeployments from '@venusprotocol/venus-protocol/networks/mainnet.json'; +import venusProtocolTestnetDeployments from '@venusprotocol/venus-protocol/networks/testnet.json'; + +import { PoolLens } from '../types/contracts/isolatedPools'; +import { Maximillion, Multicall } from '../types/contracts/others'; +import { + GovernorBravoDelegator, + Comptroller as MainPoolComptroller, + Unitroller, + VAIVault, + VRTConverter, + VenusLens, + XVSVault, +} from '../types/contracts/venusProtocol'; +import { XVSVesting } from '../types/contracts/xvsVesting'; + +import { ChainId } from '../types'; +import maximillionAbi from './externalAbis/maximillion.json'; +import multicallAbi from './externalAbis/multicall.json'; + +export interface FixedAddressContractInfo { + abi: JsonFragment[]; + address: Partial<{ + [chainId in ChainId]: string; + }>; +} + +const venusLens: FixedAddressContractInfo = { + abi: venusLensAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.VenusLens, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.VenusLens, + }, +}; + +const poolLens: FixedAddressContractInfo = { + abi: poolLensAbi, + address: { + [ChainId.BSC_TESTNET]: isolatedPoolsMainnetDeployments.contracts.PoolLens.address, + [ChainId.BSC_MAINNET]: isolatedPoolsTestnetDeployments.contracts.PoolLens.address, + }, +}; + +const mainPoolComptroller: FixedAddressContractInfo = { + abi: mainPoolComptrollerAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.Comptroller, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.Comptroller, + }, +}; + +const vaiUnitrollerController: FixedAddressContractInfo = { + abi: unitrollerAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.VaiUnitroller, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.VaiUnitroller, + }, +}; + +const vaiVault: FixedAddressContractInfo = { + abi: vaiVaultAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.VAIVaultProxy, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.VAIVaultProxy, + }, +}; + +const xvsVault: FixedAddressContractInfo = { + abi: xvsVaultAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.XVSVaultProxy, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.XVSVaultProxy, + }, +}; + +const governorBravoDelegator: FixedAddressContractInfo = { + abi: governorBravoDelegatorAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.GovernorBravoDelegator, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.GovernorBravoDelegator, + }, +}; + +const xvsVesting: FixedAddressContractInfo = { + abi: xvsVestingAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.XVSVestingProxy, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.XVSVestingProxy, + }, +}; + +const vrtConverter: FixedAddressContractInfo = { + abi: vrtConverterAbi, + address: { + [ChainId.BSC_TESTNET]: venusProtocolTestnetDeployments.Contracts.VRTConverterProxy, + [ChainId.BSC_MAINNET]: venusProtocolMainnetDeployments.Contracts.VRTConverterProxy, + }, +}; + +const maximillion: FixedAddressContractInfo = { + abi: maximillionAbi, + address: { + [ChainId.BSC_TESTNET]: '0xF3a34e06015e019D6154a0f1089f695B27122f50', + [ChainId.BSC_MAINNET]: '0x5efA1e46F4Fd738FF721F5AebC895b970F13E8A1', + }, +}; + +const multicall: FixedAddressContractInfo = { + abi: multicallAbi, + address: { + [ChainId.BSC_TESTNET]: '0xca11bde05977b3631167028862be2a173976ca11', + [ChainId.BSC_MAINNET]: '0xca11bde05977b3631167028862be2a173976ca11', + }, +}; + +const fixedAddressContractInfos = { + venusLens, + poolLens, + mainPoolComptroller, + vaiUnitrollerController, + vaiVault, + xvsVault, + xvsVesting, + governorBravoDelegator, + vrtConverter, + maximillion, + multicall, +}; + +export type FixedAddressContractName = keyof typeof fixedAddressContractInfos; + +export type FixedAddressContractTypes = { + venusLens: VenusLens; + poolLens: PoolLens; + mainPoolComptroller: MainPoolComptroller; + vaiUnitrollerController: Unitroller; + vaiVault: VAIVault; + xvsVault: XVSVault; + xvsVesting: XVSVesting; + governorBravoDelegator: GovernorBravoDelegator; + vrtConverter: VRTConverter; + maximillion: Maximillion; + multicall: Multicall; +}; + +export type FixedAddressContractTypeByName = + FixedAddressContractTypes[TContractName]; + +export default fixedAddressContractInfos; diff --git a/src/packages/contracts/contractInfos/genericContractInfos.ts b/src/packages/contracts/contractInfos/genericContractInfos.ts new file mode 100644 index 0000000000..5767ca7906 --- /dev/null +++ b/src/packages/contracts/contractInfos/genericContractInfos.ts @@ -0,0 +1,27 @@ +import type { JsonFragment } from '@ethersproject/abi'; +import { abi as isolatedPoolComptrollerAbi } from '@venusprotocol/isolated-pools/artifacts/contracts/Comptroller.sol/Comptroller.json'; + +import { Comptroller as IsolatedPoolComptroller } from '../types/contracts/isolatedPools'; + +export interface GenericContractInfo { + abi: JsonFragment[]; +} + +const isolatedPoolComptroller: GenericContractInfo = { + abi: isolatedPoolComptrollerAbi, +}; + +const genericContractInfos = { + isolatedPoolComptroller, +}; + +export type GenericContractName = keyof typeof genericContractInfos; + +export type GenericContractTypes = { + isolatedPoolComptroller: IsolatedPoolComptroller; +}; + +export type GenericContractTypeByName = + GenericContractTypes[TContractName]; + +export default genericContractInfos; diff --git a/src/packages/contracts/contractInfos/swapRouterContractInfos.ts b/src/packages/contracts/contractInfos/swapRouterContractInfos.ts new file mode 100644 index 0000000000..eb457474c7 --- /dev/null +++ b/src/packages/contracts/contractInfos/swapRouterContractInfos.ts @@ -0,0 +1,62 @@ +import type { JsonFragment } from '@ethersproject/abi'; +import isolatedPoolsMainnetDeployments from '@venusprotocol/isolated-pools/deployments/bscmainnet.json'; +import isolatedPoolsTestnetDeployments from '@venusprotocol/isolated-pools/deployments/bsctestnet.json'; +import { abi as swapRouterAbi } from '@venusprotocol/venus-protocol/artifacts/contracts/Swap/SwapRouter.sol/SwapRouter.json'; +import venusProtocolMainnetDeployments from '@venusprotocol/venus-protocol/networks/mainnet.json'; +import venusProtocolTestnetDeployments from '@venusprotocol/venus-protocol/networks/testnet.json'; + +import { SwapRouter } from '../types/contracts/venusProtocol'; + +import { ChainId } from '../types'; + +export interface SwapRouterContractInfo { + abi: JsonFragment[]; + address: Partial<{ + [chainId in ChainId]: { + [poolComptrollerAddress: string]: string; + }; + }>; +} + +const swapRouter: SwapRouterContractInfo = { + abi: swapRouterAbi, + address: { + [ChainId.BSC_TESTNET]: { + // Main pool + [venusProtocolMainnetDeployments.Comptroller.Comptroller.address.toLowerCase()]: + venusProtocolMainnetDeployments.Contracts.SwapRouterCorePool, + // Isolated pools + [isolatedPoolsTestnetDeployments.contracts.Comptroller_StableCoins.address.toLowerCase()]: + isolatedPoolsTestnetDeployments.contracts.SwapRouter_StableCoins.address, + [isolatedPoolsTestnetDeployments.contracts.Comptroller_Tron.address.toLowerCase()]: + isolatedPoolsTestnetDeployments.contracts.SwapRouter_Tron.address, + [isolatedPoolsTestnetDeployments.contracts.Comptroller_GameFi.address.toLowerCase()]: + isolatedPoolsTestnetDeployments.contracts.SwapRouter_GameFi.address, + [isolatedPoolsTestnetDeployments.contracts.Comptroller_DeFi.address.toLowerCase()]: + isolatedPoolsTestnetDeployments.contracts.SwapRouter_DeFi.address, + [isolatedPoolsTestnetDeployments.contracts.Comptroller_LiquidStakedBNB.address.toLowerCase()]: + isolatedPoolsTestnetDeployments.contracts.SwapRouter_LiquidStakedBNB.address, + }, + [ChainId.BSC_MAINNET]: { + // Main pool + [venusProtocolTestnetDeployments.Comptroller.Comptroller.address.toLowerCase()]: + venusProtocolTestnetDeployments.Contracts.SwapRouterCorePool, + // Isolated Pools + [isolatedPoolsMainnetDeployments.contracts.Comptroller_Stablecoins.address.toLowerCase()]: + isolatedPoolsMainnetDeployments.contracts.SwapRouter_Stablecoins.address, + [isolatedPoolsMainnetDeployments.contracts.Comptroller_Tron.address.toLowerCase()]: + isolatedPoolsMainnetDeployments.contracts.SwapRouter_Tron.address, + [isolatedPoolsMainnetDeployments.contracts.Comptroller_GameFi.address.toLowerCase()]: + isolatedPoolsMainnetDeployments.contracts.SwapRouter_GameFi.address, + [isolatedPoolsMainnetDeployments.contracts.Comptroller_DeFi.address.toLowerCase()]: + isolatedPoolsMainnetDeployments.contracts.SwapRouter_DeFi.address, + [isolatedPoolsMainnetDeployments.contracts.Comptroller_LiquidStakedBNB.address.toLowerCase()]: + isolatedPoolsMainnetDeployments.contracts.SwapRouter_LiquidStakedBNB.address, + }, + }, +}; + +export type SwapRouterContractName = 'swapRouter'; +export type SwapRouterContractType = SwapRouter; + +export default swapRouter; diff --git a/src/packages/contracts/getContract/__tests__/index.spec.ts b/src/packages/contracts/getContract/__tests__/index.spec.ts new file mode 100644 index 0000000000..5345859682 --- /dev/null +++ b/src/packages/contracts/getContract/__tests__/index.spec.ts @@ -0,0 +1,39 @@ +import { ethers } from 'ethers'; + +import getContract from '..'; +import fixedAddressContractInfos from '../../contractInfos/fixedAddressContractInfos'; + +const fakeProvider = new ethers.providers.JsonRpcProvider(); + +describe('getContract', () => { + it('returns Contract instance of fixed address contract', () => { + const contract = getContract('mainPoolComptroller', { + signerOrProvider: fakeProvider, + chainId: 56, + }); + + expect(contract).toBeInstanceOf(ethers.Contract); + expect(contract?.address).toBe(fixedAddressContractInfos.mainPoolComptroller.address[56]); + }); + + it('returns new Contract instance of generic contract', () => { + const fakeAddress = '0x3d759121234cd36F8124C21aFe1c6852d2bEd848'; + const contract = getContract('isolatedPoolComptroller', { + signerOrProvider: fakeProvider, + address: fakeAddress, + }); + + expect(contract).toBeInstanceOf(ethers.Contract); + expect(contract?.address).toBe(fakeAddress); + }); + + it('returns undefined when no corresponding contract address could be found', () => { + const contract = getContract('swapRouter', { + signerOrProvider: fakeProvider, + chainId: 1, + comptrollerAddress: '', + }); + + expect(contract).toBe(undefined); + }); +}); diff --git a/src/packages/contracts/getContract/index.ts b/src/packages/contracts/getContract/index.ts new file mode 100644 index 0000000000..71bc67ef00 --- /dev/null +++ b/src/packages/contracts/getContract/index.ts @@ -0,0 +1,78 @@ +import type { Provider } from '@ethersproject/abstract-provider'; +import { Contract, Signer } from 'ethers'; + +import fixedAddressContractInfos, { + FixedAddressContractName, + FixedAddressContractTypeByName, +} from '../contractInfos/fixedAddressContractInfos'; +import genericContractInfos, { + GenericContractName, + GenericContractTypeByName, +} from '../contractInfos/genericContractInfos'; +import swapRouter, { + SwapRouterContractName, + SwapRouterContractType, +} from '../contractInfos/swapRouterContractInfos'; +import getContractAddress from '../getContractAddress'; +import { ChainId } from '../types'; + +const contractInfos = { + ...fixedAddressContractInfos, + ...genericContractInfos, + swapRouter, +}; + +type ContractName = keyof typeof contractInfos; + +type ContractTypeByName = + TContractName extends FixedAddressContractName + ? FixedAddressContractTypeByName + : TContractName extends GenericContractName + ? GenericContractTypeByName + : TContractName extends SwapRouterContractName + ? SwapRouterContractType + : never; + +interface VariablesBase { + signerOrProvider: Signer | Provider; +} + +type Variables = TContractName extends SwapRouterContractName + ? VariablesBase & { + comptrollerAddress: string; + chainId: ChainId; + } + : TContractName extends FixedAddressContractName + ? VariablesBase & { + chainId: ChainId; + } + : VariablesBase & { + address: string; + }; + +export default function getContract( + name: TContractName, + variables: Variables, +) { + const contractInfo = contractInfos[name]; + let address: string | undefined; + + if ('address' in variables) { + // Handle generic contracts + ({ address } = variables); + } else { + // Handle other contracts + address = getContractAddress(name as FixedAddressContractName, variables); + } + + // Return undefined if no address was passed or if no record could be retrieved using chainId + if (!address) { + return undefined; + } + + return new Contract( + address, + contractInfo.abi, + variables.signerOrProvider, + ) as ContractTypeByName; +} diff --git a/src/packages/contracts/getContractAddress/__tests__/index.spec.ts b/src/packages/contracts/getContractAddress/__tests__/index.spec.ts new file mode 100644 index 0000000000..73c9baef89 --- /dev/null +++ b/src/packages/contracts/getContractAddress/__tests__/index.spec.ts @@ -0,0 +1,32 @@ +import getContractAddress from '..'; +import fixedAddressContractInfos from '../../contractInfos/fixedAddressContractInfos'; + +describe('getContractAddress', () => { + it('returns address when contract exists on chain', () => { + const fakeChainId = 56; + const address = getContractAddress('mainPoolComptroller', { + chainId: fakeChainId, + }); + + expect(address).toBe(fixedAddressContractInfos.mainPoolComptroller.address[fakeChainId]); + }); + + it('returns undefined when contract does not exist for the chainId argument passed', () => { + const fakeChainId = 1; + const address = getContractAddress('mainPoolComptroller', { + chainId: fakeChainId, + }); + + expect(address).toBe(undefined); + }); + + it('returns undefined when swap router contract does not exist for the comptrollerAddress argument passed', () => { + const fakeChainId = 56; + const address = getContractAddress('swapRouter', { + chainId: fakeChainId, + comptrollerAddress: '', + }); + + expect(address).toBe(undefined); + }); +}); diff --git a/src/packages/contracts/getContractAddress/index.ts b/src/packages/contracts/getContractAddress/index.ts new file mode 100644 index 0000000000..729fe39b1a --- /dev/null +++ b/src/packages/contracts/getContractAddress/index.ts @@ -0,0 +1,38 @@ +import fixedAddressContractInfos, { + FixedAddressContractName, +} from '../contractInfos/fixedAddressContractInfos'; +import swapRouter, { SwapRouterContractName } from '../contractInfos/swapRouterContractInfos'; +import { ChainId } from '../types'; + +const contractInfos = { + // We don't list generic contracts since by definition they don't have a fixed address defined + ...fixedAddressContractInfos, + swapRouter, +}; + +type ContractName = keyof typeof contractInfos; + +type Variables = TContractName extends SwapRouterContractName + ? { + comptrollerAddress: string; + chainId: ChainId; + } + : { + chainId: ChainId; + }; + +export default function getContractAddress< + TContractName extends FixedAddressContractName | SwapRouterContractName, +>(name: TContractName, variables: Variables) { + const contractAddress = contractInfos[name].address[variables.chainId]; + + if (typeof contractAddress === 'string') { + return contractAddress; + } + + if (!contractAddress || !('comptrollerAddress' in variables)) { + return undefined; + } + + return contractAddress[variables.comptrollerAddress]; +} diff --git a/src/packages/contracts/index.ts b/src/packages/contracts/index.ts new file mode 100644 index 0000000000..11a3848853 --- /dev/null +++ b/src/packages/contracts/index.ts @@ -0,0 +1,13 @@ +export * from './types'; + +export { default as fixedAddressContractInfos } from './contractInfos/fixedAddressContractInfos'; +export * from './contractInfos/fixedAddressContractInfos'; + +export { default as genericContractInfos } from './contractInfos/genericContractInfos'; +export * from './contractInfos/genericContractInfos'; + +export { default as swapRouter } from './contractInfos/swapRouterContractInfos'; +export * from './contractInfos/swapRouterContractInfos'; + +export { default as getContract } from './getContract'; +export { default as getContractAddress } from './getContractAddress'; diff --git a/src/packages/contracts/types/index.ts b/src/packages/contracts/types/index.ts new file mode 100644 index 0000000000..14284812fb --- /dev/null +++ b/src/packages/contracts/types/index.ts @@ -0,0 +1,5 @@ +export enum ChainId { + 'ETH_MAINNET' = 1, // This isn't currently used by any contract, but we keep it here to make the package multi-chain ready + 'BSC_MAINNET' = 56, + 'BSC_TESTNET' = 97, +} diff --git a/src/utilities/formatCentsToReadableValue/index.ts b/src/utilities/formatCentsToReadableValue/index.ts index c3cf723339..f35c3095a8 100644 --- a/src/utilities/formatCentsToReadableValue/index.ts +++ b/src/utilities/formatCentsToReadableValue/index.ts @@ -42,7 +42,7 @@ const formatCentsToReadableValue = ({ const absoluteValueDollars = wrappedValueDollars.absoluteValue(); const isNegative = wrappedValueDollars.isLessThan(0); - // If the value exceeds the MAX_VALUEimum threshold + // If the value exceeds the maximum threshold if (absoluteValueDollars.isGreaterThan(threshold.MAX_VALUE)) { return `${isNegative ? '< -$' : '> $'}${shortenValueWithSuffix({ value: threshold.MAX_VALUE, diff --git a/yarn.lock b/yarn.lock index 3529e522b2..3142b8d46f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6340,10 +6340,10 @@ "@tufjs/canonical-json" "1.0.0" minimatch "^9.0.0" -"@typechain/ethers-v5@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.2.0.tgz#68f5963efb5214cb2d881477228e4b5b315473e1" - integrity sha512-ikaq0N/w9fABM+G01OFmU3U3dNnyRwEahkdvi9mqy1a3XwKiPZaF/lu54OcNaEWnpvEYyhhS0N7buCtLQqC92w== +"@typechain/ethers-v5@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-11.1.0.tgz#98cac1788ef8b22c0482bb6797edcd22d69b8f0f" + integrity sha512-7P+fLol3faUi/WPFwUo9tfo+IHSsvMBvSM/ECNU9mFHcF8eFI84ygauCdwPgP41k8bhsPb29XhwZiYDTUDXU8Q== dependencies: lodash "^4.17.15" ts-essentials "^7.0.1" @@ -7225,6 +7225,11 @@ resolved "https://registry.yarnpkg.com/@venusprotocol/governance-contracts/-/governance-contracts-0.0.2.tgz#0e85616a95b482e7c2060d4d9a2876b3cea38891" integrity sha512-vXZDXxGGQdw9rWWGGAeCHH8iiHa4s5JP6MQomvt8XaPvOFGBFD4paz473/VI0wGeh+wg0LWYAgQsZjGyn51Bkw== +"@venusprotocol/governance-contracts@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@venusprotocol/governance-contracts/-/governance-contracts-1.0.0.tgz#264966365b75bb05219ce42de21c0af022d462a8" + integrity sha512-SHF8In2HPM3zbWWvPSQrD7omKRfwInecFrDldMir9yqPdKrJBXDlmjAOZLyGLK9wiH+GQPBHpVEZoxiBLmwKVw== + "@venusprotocol/isolated-pools@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@venusprotocol/isolated-pools/-/isolated-pools-1.1.0.tgz#ef66debe5ba4d55ba9854cf7e4d21173b05c5450" @@ -7264,6 +7269,16 @@ "@openzeppelin/contracts-upgradeable" "^4.8.0" dotenv "^16.0.1" +"@venusprotocol/venus-protocol@^3.0.0-dev.13": + version "3.0.0-dev.13" + resolved "https://registry.yarnpkg.com/@venusprotocol/venus-protocol/-/venus-protocol-3.0.0-dev.13.tgz#cfad8eb1efa9c67278c3a507fd37227af6e8e1f2" + integrity sha512-OxkCg7rPi5JiGt6Yr/NMDJwWHs+Lx1b/264Dzq3px0k02dzt/eXsgmcmgv3G3fh9XHvRuN0EWGtKXM9X8SSxlQ== + dependencies: + "@openzeppelin/contracts" "^4.8.3" + "@openzeppelin/contracts-upgradeable" "^4.8.0" + dotenv "^16.0.1" + module-alias "^2.2.2" + "@vitejs/plugin-react@^3.0.1": version "3.1.0" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz#d1091f535eab8b83d6e74034d01e27d73c773240" @@ -22046,10 +22061,10 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typechain@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.1.1.tgz#9c2e8012c2c4c586536fc18402dcd7034c4ff0bd" - integrity sha512-uF/sUvnXTOVF2FHKhQYnxHk4su4JjZR8vr4mA2mBaRwHTbwh0jIlqARz9XJr1tA0l7afJGvEa1dTSi4zt039LQ== +typechain@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.0.tgz#d95566de822332651152bd682ed1ec7b0e1b8610" + integrity sha512-AxtAYyOA7f2p28/JkcqrF+gnzam94VNTIbXcaUKodkrKzMX6P/XqBaP6d/OPuBZOi0WgOOmkg1zOSojX8uGkOg== dependencies: "@types/prettier" "^2.1.1" debug "^4.3.1"