From 646f499741e1982efd4212760e4657a60e600ca3 Mon Sep 17 00:00:00 2001 From: Pablo Veyrat Date: Tue, 20 Dec 2022 16:35:58 +0100 Subject: [PATCH 1/2] adding smart contract changes --- .eslintrc.js | 81 +++++ .prettierrc | 20 ++ .../oracle/OracleChainlinkMultiEfficient.sol | 4 +- ...racleChainlinkMultiEfficientWithKeeper.sol | 4 +- .../OracleUSDCEURChainlink.sol | 2 +- .../OracleUSDCEURChainlinkWithKeeper.sol | 2 +- contracts/staking/BoostV2.vy | 340 ++++++++++++++++++ hardhat.config.ts | 18 +- requirements.txt | 92 +++++ tsconfig.json | 12 + vyperAbi.ts | 55 +++ vyperCompile.ts | 277 -------------- vyperTypesGenerator.ts | 47 --- 13 files changed, 623 insertions(+), 331 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .prettierrc create mode 100644 contracts/staking/BoostV2.vy create mode 100644 requirements.txt create mode 100644 tsconfig.json create mode 100644 vyperAbi.ts delete mode 100644 vyperCompile.ts delete mode 100644 vyperTypesGenerator.ts diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3c2fc7d --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,81 @@ +module.exports = { + parser: '@typescript-eslint/parser', + extends: [ + 'standard', + 'plugin:promise/recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + plugins: ['mocha-no-only', 'promise', 'prettier', '@typescript-eslint'], + env: { + browser: true, + node: true, + mocha: true, + jest: true, + }, + globals: { + artifacts: false, + contract: false, + assert: false, + web3: false, + usePlugin: false, + extendEnvironment: false, + }, + rules: { + // Strict mode + strict: ['error', 'global'], + 'prettier/prettier': 'error', + // Code style + 'array-bracket-spacing': ['off'], + camelcase: [ + 'error', + { properties: 'always', ignoreImports: true, allow: ['(.*?)__factory'] }, + ], + 'comma-dangle': ['error', 'always-multiline'], + 'comma-spacing': ['error', { before: false, after: true }], + 'dot-notation': ['error', { allowKeywords: true, allowPattern: '' }], + 'eol-last': ['error', 'always'], + eqeqeq: ['error', 'smart'], + 'generator-star-spacing': ['error', 'before'], + 'linebreak-style': ['error', 'unix'], + 'max-len': ['error', 150, 2, { ignoreComments: true }], + 'no-debugger': 'off', + 'no-dupe-args': 'error', + 'no-dupe-keys': 'error', + 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'], + 'no-redeclare': ['error', { builtinGlobals: true }], + 'no-trailing-spaces': ['error', { skipBlankLines: false }], + 'no-undef': 'error', + 'no-use-before-define': 'off', + 'no-var': 'error', + 'object-curly-spacing': ['error', 'always'], + 'prefer-const': 'error', + quotes: ['error', 'single'], + semi: ['error', 'always'], + 'space-before-function-paren': 0, + '@typescript-eslint/no-non-null-assertion': 0, + + 'mocha-no-only/mocha-no-only': ['error'], + + 'promise/always-return': 'off', + 'promise/avoid-new': 'off', + }, + overrides: [ + { + files: ['*.js'], + rules: { + '@typescript-eslint/no-var-requires': [0], + }, + }, + { + files: ['./test/**/*'], + rules: { + camelcase: [0], + }, + }, + ], + parserOptions: { + ecmaVersion: 2018, + }, +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..11ad568 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,20 @@ +{ + "arrowParens": "avoid", + "jsxSingleQuote": true, + "singleQuote": true, + "printWidth": 120, + "semi": true, + "trailingComma": "all", + "useTabs": false, + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 120, + "singleQuote": false, + "bracketSpacing": true, + "explicitTypes": "always" + } + } + ] +} diff --git a/contracts/oracle/OracleChainlinkMultiEfficient.sol b/contracts/oracle/OracleChainlinkMultiEfficient.sol index 6a8dc63..cfd33b4 100644 --- a/contracts/oracle/OracleChainlinkMultiEfficient.sol +++ b/contracts/oracle/OracleChainlinkMultiEfficient.sol @@ -77,7 +77,7 @@ abstract contract OracleChainlinkMultiEfficient is ChainlinkUtils { /// @return The `quoteAmount` converted in EUR /// @dev If `quoteAmount` is `BASE_TOKENS`, the output is the oracle rate function _quoteChainlink(uint256 quoteAmount) internal view returns (uint256) { - AggregatorV3Interface[2] memory circuitChainlink = _circuitChainlink(); + AggregatorV3Interface[2] memory circuitChainlink = circuitChainlink(); uint8[2] memory circuitChainIsMultiplied = _circuitChainIsMultiplied(); uint8[2] memory chainlinkDecimals = _chainlinkDecimals(); for (uint256 i = 0; i < circuitChainlink.length; i++) { @@ -100,7 +100,7 @@ abstract contract OracleChainlinkMultiEfficient is ChainlinkUtils { } /// @notice Returns the array of the Chainlink feeds to look at - function _circuitChainlink() internal pure virtual returns (AggregatorV3Interface[2] memory); + function circuitChainlink() public pure virtual returns (AggregatorV3Interface[2] memory); /// @notice Base of the inToken function _inBase() internal pure virtual returns (uint256); diff --git a/contracts/oracle/OracleChainlinkMultiEfficientWithKeeper.sol b/contracts/oracle/OracleChainlinkMultiEfficientWithKeeper.sol index c7bac41..9df294a 100644 --- a/contracts/oracle/OracleChainlinkMultiEfficientWithKeeper.sol +++ b/contracts/oracle/OracleChainlinkMultiEfficientWithKeeper.sol @@ -83,7 +83,7 @@ abstract contract OracleChainlinkMultiEfficientWithKeeper is ChainlinkUtilsWithK /// @return The `quoteAmount` converted in EUR /// @dev If `quoteAmount` is `BASE_TOKENS`, the output is the oracle rate function _quoteChainlink(uint256 quoteAmount) internal view returns (uint256) { - AggregatorV3Interface[2] memory circuitChainlink = _circuitChainlink(); + AggregatorV3Interface[2] memory circuitChainlink = circuitChainlink(); uint8[2] memory circuitChainlinkIsPausable = _circuitChainlinkIsPausable(); uint8[2] memory circuitChainIsMultiplied = _circuitChainIsMultiplied(); uint8[2] memory chainlinkDecimals = _chainlinkDecimals(); @@ -108,7 +108,7 @@ abstract contract OracleChainlinkMultiEfficientWithKeeper is ChainlinkUtilsWithK } /// @notice Returns the array of the Chainlink feeds to look at - function _circuitChainlink() internal pure virtual returns (AggregatorV3Interface[2] memory); + function circuitChainlink() public pure virtual returns (AggregatorV3Interface[2] memory); /// @notice Base of the inToken function _inBase() internal pure virtual returns (uint256); diff --git a/contracts/oracle/implementations/OracleUSDCEURChainlink.sol b/contracts/oracle/implementations/OracleUSDCEURChainlink.sol index fd39a8f..19ae9fa 100644 --- a/contracts/oracle/implementations/OracleUSDCEURChainlink.sol +++ b/contracts/oracle/implementations/OracleUSDCEURChainlink.sol @@ -15,7 +15,7 @@ contract OracleUSDCEURChainlink is OracleChainlinkMultiEfficient { OracleChainlinkMultiEfficient(_stalePeriod, guardians) {} - function _circuitChainlink() internal pure override returns (AggregatorV3Interface[2] memory) { + function circuitChainlink() public pure override returns (AggregatorV3Interface[2] memory) { return [ // Oracle USDC/USD AggregatorV3Interface(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6), diff --git a/contracts/oracle/implementations/OracleUSDCEURChainlinkWithKeeper.sol b/contracts/oracle/implementations/OracleUSDCEURChainlinkWithKeeper.sol index 1b84019..0fca628 100644 --- a/contracts/oracle/implementations/OracleUSDCEURChainlinkWithKeeper.sol +++ b/contracts/oracle/implementations/OracleUSDCEURChainlinkWithKeeper.sol @@ -21,7 +21,7 @@ contract OracleUSDCEURChainlinkWithKeeper is OracleChainlinkMultiEfficientWithKe ) OracleChainlinkMultiEfficientWithKeeper(_stalePeriod, _pausingPeriod, _coreBorrow, _keeperRegistry) {} /// @inheritdoc OracleChainlinkMultiEfficientWithKeeper - function _circuitChainlink() internal pure override returns (AggregatorV3Interface[2] memory) { + function circuitChainlink() public pure override returns (AggregatorV3Interface[2] memory) { return [ // Oracle USDC/USD AggregatorV3Interface(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6), diff --git a/contracts/staking/BoostV2.vy b/contracts/staking/BoostV2.vy new file mode 100644 index 0000000..2f3d807 --- /dev/null +++ b/contracts/staking/BoostV2.vy @@ -0,0 +1,340 @@ +# @version 0.3.3 +""" +@title Boost Delegation V2 +@author CurveFi +""" + + +event Approval: + _owner: indexed(address) + _spender: indexed(address) + _value: uint256 + +event Transfer: + _from: indexed(address) + _to: indexed(address) + _value: uint256 + +event Boost: + _from: indexed(address) + _to: indexed(address) + _bias: uint256 + _slope: uint256 + _start: uint256 + +interface VotingEscrow: + def balanceOf(_user: address) -> uint256: view + def totalSupply() -> uint256: view + def locked__end(_user: address) -> uint256: view + + +struct Point: + bias: uint256 + slope: uint256 + ts: uint256 + + +NAME: constant(String[32]) = "Vote-Escrowed Boost" +SYMBOL: constant(String[8]) = "veBoost" +VERSION: constant(String[8]) = "v2.0.0" + +EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)") +PERMIT_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") + +WEEK: constant(uint256) = 86400 * 7 + +DOMAIN_SEPARATOR: immutable(bytes32) +VE: immutable(address) + +allowance: public(HashMap[address, HashMap[address, uint256]]) +nonces: public(HashMap[address, uint256]) + +delegated: public(HashMap[address, Point]) +delegated_slope_changes: public(HashMap[address, HashMap[uint256, uint256]]) + +received: public(HashMap[address, Point]) +received_slope_changes: public(HashMap[address, HashMap[uint256, uint256]]) + +migrated: public(HashMap[uint256, bool]) + + +@external +def __init__(): + DOMAIN_SEPARATOR = keccak256(_abi_encode(EIP712_TYPEHASH, keccak256(NAME), keccak256(VERSION), chain.id, self, block.prevhash)) + VE = 0x0C462Dbb9EC8cD1630f1728B2CFD2769d09f0dd5 + + log Transfer(ZERO_ADDRESS, msg.sender, 0) + + +@view +@internal +def _checkpoint_read(_user: address, _delegated: bool) -> Point: + point: Point = empty(Point) + + if _delegated: + point = self.delegated[_user] + else: + point = self.received[_user] + + if point.ts == 0: + point.ts = block.timestamp + + if point.ts == block.timestamp: + return point + + ts: uint256 = (point.ts / WEEK) * WEEK + for _ in range(255): + ts += WEEK + + dslope: uint256 = 0 + if block.timestamp < ts: + ts = block.timestamp + else: + if _delegated: + dslope = self.delegated_slope_changes[_user][ts] + else: + dslope = self.received_slope_changes[_user][ts] + + point.bias -= point.slope * (ts - point.ts) + point.slope -= dslope + point.ts = ts + + if ts == block.timestamp: + break + + return point + + +@internal +def _checkpoint_write(_user: address, _delegated: bool) -> Point: + point: Point = empty(Point) + + if _delegated: + point = self.delegated[_user] + else: + point = self.received[_user] + + if point.ts == 0: + point.ts = block.timestamp + + if point.ts == block.timestamp: + return point + + dbias: uint256 = 0 + ts: uint256 = (point.ts / WEEK) * WEEK + for _ in range(255): + ts += WEEK + + dslope: uint256 = 0 + if block.timestamp < ts: + ts = block.timestamp + else: + if _delegated: + dslope = self.delegated_slope_changes[_user][ts] + else: + dslope = self.received_slope_changes[_user][ts] + + amount: uint256 = point.slope * (ts - point.ts) + + dbias += amount + point.bias -= amount + point.slope -= dslope + point.ts = ts + + if ts == block.timestamp: + break + + if _delegated == False and dbias != 0: # received boost + log Transfer(_user, ZERO_ADDRESS, dbias) + + return point + + +@view +@internal +def _balance_of(_user: address) -> uint256: + amount: uint256 = VotingEscrow(VE).balanceOf(_user) + + point: Point = self._checkpoint_read(_user, True) + amount -= (point.bias - point.slope * (block.timestamp - point.ts)) + + point = self._checkpoint_read(_user, False) + amount += (point.bias - point.slope * (block.timestamp - point.ts)) + return amount + + +@internal +def _boost(_from: address, _to: address, _amount: uint256, _endtime: uint256): + assert _to not in [_from, ZERO_ADDRESS] + assert _amount != 0 + assert _endtime > block.timestamp + assert _endtime % WEEK == 0 + assert _endtime <= VotingEscrow(VE).locked__end(_from) + + # checkpoint delegated point + point: Point = self._checkpoint_write(_from, True) + assert _amount <= VotingEscrow(VE).balanceOf(_from) - (point.bias - point.slope * (block.timestamp - point.ts)) + + # calculate slope and bias being added + slope: uint256 = _amount / (_endtime - block.timestamp) + bias: uint256 = slope * (_endtime - block.timestamp) + + # update delegated point + point.bias += bias + point.slope += slope + + # store updated values + self.delegated[_from] = point + self.delegated_slope_changes[_from][_endtime] += slope + + # update received amount + point = self._checkpoint_write(_to, False) + point.bias += bias + point.slope += slope + + # store updated values + self.received[_to] = point + self.received_slope_changes[_to][_endtime] += slope + + log Transfer(_from, _to, _amount) + log Boost(_from, _to, bias, slope, block.timestamp) + + # also checkpoint received and delegated + self.received[_from] = self._checkpoint_write(_from, False) + self.delegated[_to] = self._checkpoint_write(_to, True) + + +@external +def boost(_to: address, _amount: uint256, _endtime: uint256, _from: address = msg.sender): + # reduce approval if necessary + if _from != msg.sender: + allowance: uint256 = self.allowance[_from][msg.sender] + if allowance != MAX_UINT256: + self.allowance[_from][msg.sender] = allowance - _amount + log Approval(_from, msg.sender, allowance - _amount) + + self._boost(_from, _to, _amount, _endtime) + + +@external +def checkpoint_user(_user: address): + self.delegated[_user] = self._checkpoint_write(_user, True) + self.received[_user] = self._checkpoint_write(_user, False) + + +@external +def approve(_spender: address, _value: uint256) -> bool: + self.allowance[msg.sender][_spender] = _value + + log Approval(msg.sender, _spender, _value) + return True + + +@external +def permit(_owner: address, _spender: address, _value: uint256, _deadline: uint256, _v: uint8, _r: bytes32, _s: bytes32) -> bool: + assert _owner != ZERO_ADDRESS + assert block.timestamp <= _deadline + + nonce: uint256 = self.nonces[_owner] + digest: bytes32 = keccak256( + concat( + b"\x19\x01", + DOMAIN_SEPARATOR, + keccak256(_abi_encode(PERMIT_TYPEHASH, _owner, _spender, _value, nonce, _deadline)) + ) + ) + + assert ecrecover(digest, convert(_v, uint256), convert(_r, uint256), convert(_s, uint256)) == _owner + + self.allowance[_owner][_spender] = _value + self.nonces[_owner] = nonce + 1 + + log Approval(_owner, _spender, _value) + return True + + +@external +def increaseAllowance(_spender: address, _added_value: uint256) -> bool: + allowance: uint256 = self.allowance[msg.sender][_spender] + _added_value + self.allowance[msg.sender][_spender] = allowance + + log Approval(msg.sender, _spender, allowance) + return True + + +@external +def decreaseAllowance(_spender: address, _subtracted_value: uint256) -> bool: + allowance: uint256 = self.allowance[msg.sender][_spender] - _subtracted_value + self.allowance[msg.sender][_spender] = allowance + + log Approval(msg.sender, _spender, allowance) + return True + + +@view +@external +def balanceOf(_user: address) -> uint256: + return self._balance_of(_user) + + +@view +@external +def adjusted_balance_of(_user: address) -> uint256: + return self._balance_of(_user) + + +@view +@external +def totalSupply() -> uint256: + return VotingEscrow(VE).totalSupply() + + +@view +@external +def delegated_balance(_user: address) -> uint256: + point: Point = self._checkpoint_read(_user, True) + return point.bias - point.slope * (block.timestamp - point.ts) + + +@view +@external +def received_balance(_user: address) -> uint256: + point: Point = self._checkpoint_read(_user, False) + return point.bias - point.slope * (block.timestamp - point.ts) + + +@view +@external +def delegable_balance(_user: address) -> uint256: + point: Point = self._checkpoint_read(_user, True) + return VotingEscrow(VE).balanceOf(_user) - (point.bias - point.slope * (block.timestamp - point.ts)) + + +@pure +@external +def name() -> String[32]: + return NAME + + +@pure +@external +def symbol() -> String[8]: + return SYMBOL + + +@pure +@external +def decimals() -> uint8: + return 18 + + +@pure +@external +def DOMAIN_SEPARATOR() -> bytes32: + return DOMAIN_SEPARATOR + + +@pure +@external +def VE() -> address: + return VE \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 56f626e..c56e196 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -59,6 +59,15 @@ const config: HardhatUserConfig = { }, }, }, + 'contracts/deprecated/StableMasterFrontOld.sol': { + version: '0.8.7', + settings: { + optimizer: { + enabled: true, + runs: 830, + }, + }, + }, 'contracts/perpetualManager/PerpetualManagerFront.sol': { version: '0.8.7', settings: { @@ -89,7 +98,14 @@ const config: HardhatUserConfig = { }, }, vyper: { - version: '0.2.16', + compilers: [ + { + version: '0.3.3', + }, + { + version: '0.2.16', + }, + ], }, defaultNetwork: 'hardhat', networks: { diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6287e9f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,92 @@ +aiohttp==3.8.1 +apipkg==1.5 +appdirs==1.4.4 +asttokens==2.0.5 +async-timeout==4.0.2 +attrs==21.4.0 +base58==2.1.1 +bitarray==1.2.2 +black==22.3.0 +certifi==2021.10.8 +chardet==4.0.0 +charset-normalizer==2.0.12 +click==8.1.3 +cytoolz==0.11.2 +dataclassy==0.11.1 +eip712==0.1.0 +eth-abi==2.1.1 +eth-account==0.5.7 +eth-brownie==1.18.2 +eth-event==1.2.3 +eth-hash==0.3.2 +eth-keyfile==0.5.1 +eth-keys==0.3.4 +eth-rlp==0.2.1 +eth-typing==2.3.0 +eth-utils==1.10.0 +execnet==1.9.0 +frozenlist==1.3.0 +hexbytes==0.2.2 +hypothesis==6.27.3 +idna==3.3 +importlib-metadata==4.8.2 +inflection==0.5.0 +iniconfig==1.1.1 +ipfshttpclient==0.8.0a2 +jsonschema==3.2.0 +lazy-object-proxy==1.7.1 +lru-dict==1.1.7 +more-itertools==8.6.0 +multiaddr==0.0.9 +multidict==6.0.2 +mythx-models==1.9.1 +mypy-extensions==0.4.3 +netaddr==0.8.0 +packaging==21.3 +parsimonious==0.8.1 +pathspec==0.9.0 +platformdirs==2.5.2 +pluggy==1.0.0 +prompt-toolkit==3.0.29 +protobuf==3.20.1 +psutil==5.9.0 +py==1.11.0 +py-solc-ast==1.2.9 +py-solc-x==1.1.1 +pycryptodome==3.14.1 +Pygments==2.12.0 +pygments-lexer-solidity==0.7.0 +PyJWT==1.7.1 +pyparsing==3.0.9 +pyrsistent==0.18.1 +pytest==6.2.5 +pytest-forked==1.4.0 +pytest-xdist==1.34.0 +python-dateutil==2.8.1 +python-dotenv==0.16.0 +pythx==1.6.1 +PyYAML==5.4.1 +regex==2021.11.10 +requests==2.27.1 +rlp==2.0.1 +semantic-version==2.8.5 +six==1.16.0 +sortedcontainers==2.4.0 +toml==0.10.2 +tomli==2.0.1 +toolz==0.11.2 +tqdm==4.64.0 +typed-ast==1.5.1 +typing-extensions==3.10.0.2 +urllib3==1.26.9 +varint==1.0.2 +vvm==0.1.0 +vyper==0.3.3 +wcwidth==0.2.5 +web3==5.29.0 +websockets==9.1 +wrapt==1.14.1 +yarl==1.7.2 +flake8==3.8.4 +isort==5.7.0 +brownie-token-tester>=0.2.2 \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2df3fa6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "resolveJsonModule": true + }, + "include": ["./scripts", "./e2e", "test", "./deploy", "./deploy/rinkeby"], + "files": ["./hardhat.config.ts"] +} diff --git a/vyperAbi.ts b/vyperAbi.ts new file mode 100644 index 0000000..25d9df3 --- /dev/null +++ b/vyperAbi.ts @@ -0,0 +1,55 @@ +import { glob } from 'typechain'; +import path from 'path'; +import fse from 'fs-extra'; + +type abiExtend = { + name: string; + inputs: { + name: string; + type: string; + indexed: boolean; + }[]; + anonymous: boolean; + type: string; + stateMutability?: undefined; + outputs?: undefined; + gas?: undefined; +}; + +const ARTIFACTS_PATH = path.join(__dirname, 'artifacts', 'contracts'); +const ABIS_PATH = path.join(__dirname, 'export', 'abi'); + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export async function main() { + console.log('Removing gas estimation in abis'); + + const cwd = process.cwd(); + const allFilesArtifacts = glob(cwd, [`${ARTIFACTS_PATH}/!(build-info)/**.vy/+([a-zA-Z0-9_]).json`]); + const allFilesAbis = glob(cwd, [`${ABIS_PATH}/**.json`]); + + if ((!allFilesArtifacts || allFilesArtifacts.length === 0) && (!allFilesAbis || allFilesAbis.length === 0)) return; + + for (let i = 0; i < allFilesArtifacts.length; i++) { + const JSONprop = JSON.parse((await fse.readFile(allFilesArtifacts[i])).toString()); + let abi: abiExtend[] = JSONprop.abi; + abi = abi.map(obj => { + delete obj.gas; + return obj; + }); + JSONprop.abi = abi; + await fse.writeFile(allFilesArtifacts[i], JSON.stringify(JSONprop)); + } + for (let i = 0; i < allFilesAbis.length; i++) { + const JSONprop: abiExtend[] = JSON.parse((await fse.readFile(allFilesAbis[i])).toString()); + JSONprop.map(obj => { + delete obj.gas; + return obj; + }); + await fse.writeFile(allFilesAbis[i], JSON.stringify(JSONprop)); + } +} + +main().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/vyperCompile.ts b/vyperCompile.ts deleted file mode 100644 index 0560e98..0000000 --- a/vyperCompile.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { - DockerBadGatewayError, - DockerHubConnectionError, - DockerNotRunningError, - DockerServerError, - HardhatDocker, - Image, - ImageDoesntExistError, - ProcessResult, -} from '@nomiclabs/hardhat-docker'; -import fsExtra from 'fs-extra'; -import { NomicLabsHardhatPluginError } from 'hardhat/plugins'; -import { Artifact, Artifacts, ProjectPathsConfig } from 'hardhat/types'; -import { localPathToSourceName } from 'hardhat/utils/source-names'; -import path from 'path'; - -import { VyperConfig } from '@nomiclabs/hardhat-vyper/src/types'; - -const VYPER_DOCKER_REPOSITORY = 'vyperlang/vyper'; -const LAST_VYPER_VERSION_USED_FILENAME = 'last-vyper-version-used.txt'; -const VYPER_DOCKER_IMAGES_LAST_UPDATE_CHECK_FILE = 'vyper-docker-updates.json'; -const CHECK_UPDATES_INTERVAL = 3600000; - -const ARTIFACT_FORMAT_VERSION = 'hh-vyper-artifact-1'; - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export async function compile(vyperConfig: VyperConfig, paths: ProjectPathsConfig, artifacts: Artifacts) { - const vyperVersion = vyperConfig.version; - - const dockerImage = { - repository: VYPER_DOCKER_REPOSITORY, - tag: vyperVersion, - }; - - await validateDockerIsInstalled(); - - const docker = await handleCommonErrors(HardhatDocker.create()); - - await handleCommonErrors(pullImageIfNecessary(docker, dockerImage, paths.cache)); - - const files = await getVyperSources(paths); - - let someContractFailed = false; - - for (const file of files) { - const pathFromCWD = path.relative(process.cwd(), file); - const pathFromSources = path.relative(paths.sources, file); - - if (await isAlreadyCompiled(file, paths, vyperVersion)) { - console.log(pathFromCWD, 'is already compiled'); - continue; - } - - console.log('Compiling', pathFromCWD); - - const processResult = await handleCommonErrors(compileWithDocker(file, docker, dockerImage, paths)); - - if (processResult.statusCode === 0) { - const vyperOutput = JSON.parse(processResult.stdout.toString('utf8'))[pathFromSources]; - - const sourceName = await localPathToSourceName(paths.root, file); - const artifact = getArtifactFromVyperOutput(sourceName, vyperOutput); - - await artifacts.saveArtifactAndDebugFile(artifact); - } else { - console.error(processResult.stderr.toString('utf8').trim(), '\n'); - - someContractFailed = true; - } - } - - if (someContractFailed) { - throw new NomicLabsHardhatPluginError('@nomiclabs/hardhat-vyper', 'Compilation failed'); - } - - await saveLastVyperVersionUsed(vyperVersion, paths); -} - -async function isAlreadyCompiled(sourceFile: string, paths: ProjectPathsConfig, vyperVersion: string) { - const lastVyperVersionUsed = await getLastVyperVersionUsed(paths); - if (lastVyperVersionUsed !== vyperVersion) { - return false; - } - - const contractName = pathToContractName(sourceFile); - const sourceName = path.relative(paths.root, sourceFile); - const artifactPath = path.join(paths.artifacts, sourceName, `${contractName}.json`); - if (!(await fsExtra.pathExists(artifactPath))) { - return false; - } - const lastSourcesCtime = (await fsExtra.stat(sourceFile)).ctimeMs; - const artifactCtime = (await fsExtra.stat(artifactPath)).ctimeMs; - - return lastSourcesCtime < artifactCtime; -} - -async function getVyperSources(paths: ProjectPathsConfig) { - const glob = await import('glob'); - const vyFiles = glob.sync(path.join(paths.sources, '**', '*.vy')); - const vpyFiles = glob.sync(path.join(paths.sources, '**', '*.v.py')); - - return [...vyFiles, ...vpyFiles]; -} - -function pathToContractName(file: string) { - const sourceName = path.basename(file); - return sourceName.substring(0, sourceName.indexOf('.')); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function getArtifactFromVyperOutput(sourceName: string, output: any): Artifact { - const contractName = pathToContractName(sourceName); - - return { - _format: ARTIFACT_FORMAT_VERSION, - contractName, - sourceName, - abi: output.abi, - bytecode: add0xPrefixIfNecessary(output.bytecode), - deployedBytecode: add0xPrefixIfNecessary(output.bytecode_runtime), - linkReferences: {}, - deployedLinkReferences: {}, - }; -} - -function add0xPrefixIfNecessary(hex: string): string { - hex = hex.toLowerCase(); - - if (hex.slice(0, 2) === '0x') { - return hex; - } - - return `0x${hex}`; -} - -async function getLastVyperVersionUsed(paths: ProjectPathsConfig) { - const filePath = path.join(paths.cache, LAST_VYPER_VERSION_USED_FILENAME); - if (!(await fsExtra.pathExists(filePath))) { - return undefined; - } - - return fsExtra.readFile(filePath, 'utf8'); -} - -async function saveLastVyperVersionUsed(version: string, paths: ProjectPathsConfig) { - const filePath = path.join(paths.cache, LAST_VYPER_VERSION_USED_FILENAME); - await fsExtra.ensureDir(path.dirname(filePath)); - return fsExtra.writeFile(filePath, version, 'utf8'); -} - -async function validateDockerIsInstalled() { - if (!(await HardhatDocker.isInstalled())) { - throw new NomicLabsHardhatPluginError( - '@nomiclabs/hardhat-vyper', - `Docker Desktop is not installed. - Please install it by following the instructions on https://www.docker.com/get-started`, - ); - } -} - -async function pullImageIfNecessary(docker: HardhatDocker, image: Image, cachePath: string) { - if (!(await docker.hasPulledImage(image))) { - console.log(`Pulling Docker image ${HardhatDocker.imageToRepoTag(image)}...`); - - await docker.pullImage(image); - - console.log('Image pulled'); - } else { - await checkForImageUpdates(docker, image, cachePath); - } -} - -async function checkForImageUpdates(docker: HardhatDocker, image: Image, cachePath: string) { - if (!(await shouldCheckForUpdates(image, cachePath))) { - return; - } - - if (!(await docker.isImageUpToDate(image))) { - console.log(`Updating Docker image ${HardhatDocker.imageToRepoTag(image)}...`); - - await docker.pullImage(image); - - console.log('Image updated'); - } - - await saveLastUpdateCheckDate(image, cachePath); -} - -async function shouldCheckForUpdates(image: Image, cachePath: string) { - const lastDate = await getLastUpdateCheckDate(image, cachePath); - if (lastDate === undefined) { - return true; - } - - return lastDate + CHECK_UPDATES_INTERVAL < +new Date(); -} - -async function getLastUpdateCheckDate(image: Image, cachePath: string): Promise { - const file = path.join(cachePath, VYPER_DOCKER_IMAGES_LAST_UPDATE_CHECK_FILE); - if (!(await fsExtra.pathExists(file))) { - return undefined; - } - - const updates = await fsExtra.readJSON(file); - return updates[HardhatDocker.imageToRepoTag(image)]; -} - -async function saveLastUpdateCheckDate(image: Image, cachePath: string) { - let updates: { [repoTag: string]: number }; - - const file = path.join(cachePath, VYPER_DOCKER_IMAGES_LAST_UPDATE_CHECK_FILE); - if (!(await fsExtra.pathExists(file))) { - updates = {}; - } else { - updates = await fsExtra.readJSON(file); - } - - updates[HardhatDocker.imageToRepoTag(image)] = +new Date(); - - await fsExtra.ensureDir(path.dirname(file)); - await fsExtra.writeJSON(file, updates, { - spaces: 2, - }); -} - -async function compileWithDocker( - filePath: string, - docker: HardhatDocker, - dockerImage: Image, - paths: ProjectPathsConfig, -): Promise { - const pathFromSources = path.relative(paths.sources, filePath); - - return docker.runContainer(dockerImage, ['vyper', '-f', 'combined_json', pathFromSources], { - binds: { - [paths.sources]: '/code', - }, - workingDirectory: '/code', - }); -} - -async function handleCommonErrors(promise: Promise): Promise { - try { - return await promise; - } catch (error) { - if (error instanceof DockerNotRunningError || error instanceof DockerBadGatewayError) { - throw new NomicLabsHardhatPluginError( - '@nomiclabs/hardhat-vyper', - 'Docker Desktop is not running.\nPlease open it and wait until it finishes booting.', - error, - ); - } - - if (error instanceof DockerHubConnectionError) { - throw new NomicLabsHardhatPluginError( - '@nomiclabs/hardhat-vyper', - `Error connecting to Docker Hub. - Please check your internet connection.`, - error, - ); - } - - if (error instanceof DockerServerError) { - throw new NomicLabsHardhatPluginError('@nomiclabs/hardhat-vyper', 'Docker error', error); - } - - if (error instanceof ImageDoesntExistError) { - throw new NomicLabsHardhatPluginError( - '@nomiclabs/hardhat-vyper', - `Docker image ${HardhatDocker.imageToRepoTag(error.image)} doesn't exist. - Make sure you chose a valid Vyper version.`, - ); - } - - throw error; - } -} diff --git a/vyperTypesGenerator.ts b/vyperTypesGenerator.ts deleted file mode 100644 index 6f66608..0000000 --- a/vyperTypesGenerator.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { runTypeChain, glob } from 'typechain'; -import path from 'path'; -import fse from 'fs-extra'; -import util from 'util'; -import { exec as childExec } from 'child_process'; - -const exec = util.promisify(childExec); - -const ARTIFACTS_PATH = path.join(__dirname, 'artifacts', 'contracts'); -const VYPER_TYPES_TEMP_DIR = 'vyperTypesTemp'; -const TYPECHAIN_DIR = 'typechain'; - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export async function generateVyperTypes() { - console.log('Generating typings for Vyper'); - - const cwd = process.cwd(); - const allFiles = glob(cwd, [`${ARTIFACTS_PATH}/!(build-info)/**.vy/+([a-zA-Z0-9_]).json`]); - - if (!allFiles || allFiles.length === 0) return; - - await runTypeChain({ - cwd, - filesToProcess: allFiles, - allFiles, - outDir: VYPER_TYPES_TEMP_DIR, - target: 'ethers-v5', - }); - - await exec( - `mv ${VYPER_TYPES_TEMP_DIR}/index.ts ${VYPER_TYPES_TEMP_DIR}/index; mv ${VYPER_TYPES_TEMP_DIR}/*.ts ${TYPECHAIN_DIR}`, - ); - await exec(`mv ${VYPER_TYPES_TEMP_DIR}/factories/* ${TYPECHAIN_DIR}/factories`); - - const typechainIndexFile = (await fse.readFile(`${TYPECHAIN_DIR}/index.ts`)).toString(); - const vyperIndexFile = (await fse.readFile(`${VYPER_TYPES_TEMP_DIR}/index`)).toString(); - const indexStartCurve = typechainIndexFile.indexOf('/* start curve */'); - let indexFile = typechainIndexFile; - if (indexStartCurve >= 0) { - indexFile = typechainIndexFile.substr(0, indexStartCurve); - } - indexFile += '/* start curve */\n'; - indexFile += vyperIndexFile; - await fse.writeFile(`${TYPECHAIN_DIR}/index.ts`, indexFile); - - await fse.remove(path.join(__dirname, VYPER_TYPES_TEMP_DIR)); -} From 5b28a69e3395c36c47e11593b3ead2ac6425bd5f Mon Sep 17 00:00:00 2001 From: Pablo Veyrat Date: Mon, 26 Dec 2022 17:16:18 +0100 Subject: [PATCH 2/2] changing ref to borrow contracts --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac9f288..8fb79a0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ For a broader overview of the protocol and its different modules, you can also c Other Angle-related smart contracts can be found in the following repositories: -- [Angle Borrowing module contracts](https://github.com/AngleProtocol/angle-borrow) +- [Angle Borrowing module contracts](https://github.com/AngleProtocol/borrow-contracts) - [Angle Strategies](https://github.com/AngleProtocol/angle-strategies) Otherwise, for more info about the protocol, check out [this portal](https://linktr.ee/angleprotocol) of resources. @@ -43,7 +43,7 @@ Some smart contracts of the protocol, beyond strategy contracts, are used across Here are some cross-module contracts and the repos in which you should look for their correct and latest version: - [`angle-core`](https://github.com/AngleProtocol/angle-core): All DAO-related contracts (`ANGLE`, `veANGLE`, gauges, surplus distribution, ...), `AngleRouter` contract -- [`angle-borrow`](https://github.com/AngleProtocol/angle-borrow): `agToken` contract +- [`borrow-contracts`](https://github.com/AngleProtocol/borrow-contracts): `agToken` contract - [`angle-strategies`](https://github.com/AngleProtocol/angle-strategies): Yield strategies of the protocol ### Error Messages