From e0b675369e5b85a039d80c31dcbc661df651d625 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:46:03 +0300 Subject: [PATCH 01/29] Remove eslint addons --- .eslintrc | 2 +- package.json | 4 -- yarn.lock | 148 ++++----------------------------------------------- 3 files changed, 10 insertions(+), 144 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5e9d9c1..0574ca3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "extends": [ - "airbnb" + "eslint:recommended" ], "rules": { "indent": ["error", 4], diff --git a/package.json b/package.json index 9930af0..7e2bc26 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,6 @@ "chai": "^4.2.0", "coveralls": "^3.0.8", "eslint": "^6.7.1", - "eslint-config-airbnb": "^18.0.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-react": "^7.17.0", "ganache-core": "^2.6.1", "mocha": "^6.2.0", "solhint": "^3.2.0", diff --git a/yarn.lock b/yarn.lock index 5d12ae7..c6b96e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,21 +18,6 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/runtime-corejs3@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.3.tgz#a2445836d0699e5ba77eea2c790ad9ea51e2cd27" - integrity sha512-lrIU4aVbmlM/wQPzhEvzvNJskKyYptuXb0fGC0lTQTupTOYtR2Vqbu6/jf8vTr4M8Wt1nIzxVrSvPI5qESa/xA== - dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.2" - -"@babel/runtime@^7.4.5", "@babel/runtime@^7.7.4": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1" - integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w== - dependencies: - regenerator-runtime "^0.13.2" - "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -372,14 +357,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -aria-query@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" - integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= - dependencies: - ast-types-flow "0.0.7" - commander "^2.11.0" - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -419,7 +396,7 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-includes@^3.0.3, array-includes@^3.1.1: +array-includes@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== @@ -467,14 +444,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" - integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -511,11 +480,6 @@ ast-parents@0.0.1: resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= -ast-types-flow@0.0.7, ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -594,14 +558,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== -axobject-query@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.1.tgz#2a3b1271ec722d48a4cd4b3fcc20c853326a49a7" - integrity sha512-lF98xa/yvy6j3fBHAgQXIYl+J4eZadOSqsPojemUqClzNbBV38wWGpUbQbVEyf4eUF5yF7eHmGgGA2JiHyjeqw== - dependencies: - "@babel/runtime" "^7.7.4" - "@babel/runtime-corejs3" "^7.7.4" - babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -1758,11 +1714,6 @@ commander@2.18.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== -commander@^2.11.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - commander@~2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" @@ -1790,11 +1741,6 @@ concat-stream@^1.5.1, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -confusing-browser-globals@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" - integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== - contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" @@ -1847,7 +1793,7 @@ copy-props@^2.0.1: each-props "^1.3.0" is-plain-object "^2.0.1" -core-js-pure@^3.0.0, core-js-pure@^3.0.1: +core-js-pure@^3.0.1: version "3.6.4" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== @@ -1966,11 +1912,6 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" -damerau-levenshtein@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414" - integrity sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA== - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -2377,7 +2318,7 @@ elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.5.2: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -emoji-regex@^7.0.1, emoji-regex@^7.0.2: +emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -2515,24 +2456,6 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -eslint-config-airbnb-base@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz#8a7bcb9643d13c55df4dd7444f138bf4efa61e17" - integrity sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA== - dependencies: - confusing-browser-globals "^1.0.7" - object.assign "^4.1.0" - object.entries "^1.1.0" - -eslint-config-airbnb@^18.0.1: - version "18.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.0.1.tgz#a3a74cc29b46413b6096965025381df8fb908559" - integrity sha512-hLb/ccvW4grVhvd6CT83bECacc+s4Z3/AEyWQdIT2KeTsG9dR7nx1gs7Iw4tDmGKozCNHFn4yZmRm3Tgy+XxyQ== - dependencies: - eslint-config-airbnb-base "^14.0.0" - object.assign "^4.1.0" - object.entries "^1.1.0" - eslint-config-standard-jsx@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz#314c62a0e6f51f75547f89aade059bec140edfc7" @@ -2551,7 +2474,7 @@ eslint-import-resolver-node@^0.3.2: debug "^2.6.9" resolve "^1.13.1" -eslint-module-utils@^2.4.0, eslint-module-utils@^2.4.1: +eslint-module-utils@^2.4.0: version "2.5.2" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708" integrity sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q== @@ -2567,24 +2490,6 @@ eslint-plugin-es@^2.0.0: eslint-utils "^1.4.2" regexpp "^3.0.0" -eslint-plugin-import@^2.18.2: - version "2.20.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz#d749a7263fb6c29980def8e960d380a6aa6aecaa" - integrity sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ== - dependencies: - array-includes "^3.0.3" - array.prototype.flat "^1.2.1" - contains-path "^0.1.0" - debug "^2.6.9" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.1" - has "^1.0.3" - minimatch "^3.0.4" - object.values "^1.1.0" - read-pkg-up "^2.0.0" - resolve "^1.12.0" - eslint-plugin-import@~2.18.0: version "2.18.2" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" @@ -2602,21 +2507,6 @@ eslint-plugin-import@~2.18.0: read-pkg-up "^2.0.0" resolve "^1.11.0" -eslint-plugin-jsx-a11y@^6.2.3: - version "6.2.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" - integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== - dependencies: - "@babel/runtime" "^7.4.5" - aria-query "^3.0.0" - array-includes "^3.0.3" - ast-types-flow "^0.0.7" - axobject-query "^2.0.2" - damerau-levenshtein "^1.0.4" - emoji-regex "^7.0.2" - has "^1.0.3" - jsx-ast-utils "^2.2.1" - eslint-plugin-node@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz#fd1adbc7a300cf7eb6ac55cf4b0b6fc6e577f5a6" @@ -2634,21 +2524,6 @@ eslint-plugin-promise@~4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-plugin-react@^7.17.0: - version "7.18.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz#2317831284d005b30aff8afb7c4e906f13fa8e7e" - integrity sha512-p+PGoGeV4SaZRDsXqdj9OWcOrOpZn8gXoGPcIQTzo2IDMbAKhNDnME9myZWqO3Ic4R3YmwAZ1lDjWl2R2hMUVQ== - dependencies: - array-includes "^3.1.1" - doctrine "^2.1.0" - has "^1.0.3" - jsx-ast-utils "^2.2.3" - object.entries "^1.1.1" - object.fromentries "^2.0.2" - object.values "^1.1.1" - prop-types "^15.7.2" - resolve "^1.14.2" - eslint-plugin-react@~7.14.2: version "7.14.3" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" @@ -5047,7 +4922,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.1.0, jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: +jsx-ast-utils@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f" integrity sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA== @@ -6009,7 +5884,7 @@ object.defaults@^1.0.0, object.defaults@^1.1.0: for-own "^1.0.0" isobject "^3.0.0" -object.entries@^1.1.0, object.entries@^1.1.1: +object.entries@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== @@ -6019,7 +5894,7 @@ object.entries@^1.1.0, object.entries@^1.1.1: function-bind "^1.1.1" has "^1.0.3" -object.fromentries@^2.0.0, object.fromentries@^2.0.2: +object.fromentries@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== @@ -6060,7 +5935,7 @@ object.reduce@^1.0.0: for-own "^1.0.0" make-iterator "^1.0.0" -object.values@^1.1.0, object.values@^1.1.1: +object.values@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== @@ -6809,11 +6684,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - regenerator-transform@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" @@ -6999,7 +6869,7 @@ resolve@1.1.x: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.4.0, resolve@~1.14.2: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.13.1, resolve@^1.4.0, resolve@~1.14.2: version "1.14.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== From d96a5c0882cf724d7f8ad0e17bfce5cfb48a8ac1 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:49:09 +0300 Subject: [PATCH 02/29] Bump ganache-cli --- package.json | 2 +- yarn.lock | 112 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 105 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 7e2bc26..4f503fa 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ }, "dependencies": { "decimal.js": "^10.2.0", - "ganache-cli": "^6.7.0", + "ganache-cli": "^6.12.1", "global": "^4.4.0" } } diff --git a/yarn.lock b/yarn.lock index c6b96e5..d3c65cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -122,6 +122,20 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.25.tgz#792c0afb798f1dd681dce9c4b4c431f7245a0a42" integrity sha512-nf1LMGZvgFX186geVZR1xMZKKblJiRfiASTHw85zED2kI1yDKHDwTKMdkaCbTlXoRKlGKaDfYywt+V0As30q3w== +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" + integrity sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog== + dependencies: + "@types/node" "*" + "@web3-js/scrypt-shim@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@web3-js/scrypt-shim/-/scrypt-shim-0.1.0.tgz#0bf7529ab6788311d3e07586f7d89107c3bea2cc" @@ -1179,6 +1193,11 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + bluebird@^3.5.0: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -1251,7 +1270,7 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6: +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -1856,7 +1875,7 @@ create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2, create-hash@^1.2.0: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -2918,6 +2937,27 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + ethereumjs-abi@0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" @@ -3055,6 +3095,19 @@ ethereumjs-util@6.1.0, ethereumjs-util@~6.1.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" +ethereumjs-util@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" @@ -3697,12 +3750,12 @@ ganache-cli@6.9.0: source-map-support "0.5.12" yargs "13.2.4" -ganache-cli@^6.7.0: - version "6.8.2" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.8.2.tgz#357b5ea93f013a7f0fce4ede1b8bec8613e95dd3" - integrity sha512-FgsJx/hHh7A1/fmSQpNT5jxZ3dYEal4zQMqYyA8Bm7S6MgrVO48hIjnROn2JteubHY8Rob8LzxMkhEvoQce7WA== +ganache-cli@^6.12.1: + version "6.12.1" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.1.tgz#148cf6541494ef1691bd68a77e4414981910cb3e" + integrity sha512-zoefZLQpQyEJH9jgtVYgM+ENFLAC9iwys07IDCsju2Ieq9KSTLH89RxSP4bhizXKV/h/+qaWpfyCBGWnBfqgIQ== dependencies: - ethereumjs-util "6.1.0" + ethereumjs-util "6.2.1" source-map-support "0.5.12" yargs "13.2.4" @@ -4205,7 +4258,7 @@ hash.js@1.1.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" -hash.js@^1.0.0, hash.js@^1.0.3: +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -4955,6 +5008,14 @@ keccak@^2.0.0: nan "^2.14.0" safe-buffer "^5.2.0" +keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + keccakjs@^0.2.0: version "0.2.3" resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.3.tgz#5e4e969ce39689a3861f445d7752ee3477f9fe72" @@ -5729,6 +5790,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + node-emoji@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -5757,6 +5823,11 @@ node-fetch@~1.7.1: encoding "^0.1.11" is-stream "^1.0.1" +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -6248,6 +6319,17 @@ pathval@^1.1.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= +pbkdf2@^3.0.17: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + pbkdf2@^3.0.3, pbkdf2@^3.0.9: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -7028,6 +7110,11 @@ scrypt-js@2.0.4: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== +scrypt-js@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + "scrypt-shim@github:web3-js/scrypt-shim": version "0.1.0" resolved "https://codeload.github.com/web3-js/scrypt-shim/tar.gz/aafdadda13e660e25e1c525d1f5b2443f5eb1ebb" @@ -7077,6 +7164,15 @@ secp256k1@^3.0.1: nan "^2.14.0" safe-buffer "^5.1.2" +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + seedrandom@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" From f44bc96cdd838929d895fda6280e40d7b062adbb Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:55:33 +0300 Subject: [PATCH 03/29] Move test contracts into common/v1 --- .solcover.js | 7 ++++--- contracts/{ => common}/DSProxyFactory.sol | 0 contracts/{test => common}/IERC20.sol | 0 contracts/{test => common}/SafeApprove.sol | 0 contracts/{test => common}/TToken.sol | 0 contracts/{test => common}/TTokenFactory.sol | 0 contracts/{test => v1}/BColor.sol | 0 contracts/{test => v1}/BConst.sol | 0 contracts/{test => v1}/BFactory.sol | 0 contracts/{test => v1}/BMath.sol | 0 contracts/{test => v1}/BNum.sol | 0 contracts/{test => v1}/BPool.sol | 0 contracts/{test => v1}/BToken.sol | 0 contracts/{test => v1}/BalancerConstants.sol | 0 contracts/{test => v1}/BalancerOwnable.sol | 0 contracts/{test => v1}/BalancerReentrancyGuard.sol | 0 contracts/{test => v1}/BalancerSafeMath.sol | 0 contracts/{test => v1}/CRPFactory.sol | 0 contracts/{test => v1}/ConfigurableRightsPool.sol | 2 +- contracts/{test => v1}/IBFactory.sol | 0 contracts/{test => v1}/PCToken.sol | 2 +- contracts/{test => v1}/RightsManager.sol | 0 contracts/{test => v1}/SmartPoolManager.sol | 4 ++-- 23 files changed, 8 insertions(+), 7 deletions(-) rename contracts/{ => common}/DSProxyFactory.sol (100%) rename contracts/{test => common}/IERC20.sol (100%) rename contracts/{test => common}/SafeApprove.sol (100%) rename contracts/{test => common}/TToken.sol (100%) rename contracts/{test => common}/TTokenFactory.sol (100%) rename contracts/{test => v1}/BColor.sol (100%) rename contracts/{test => v1}/BConst.sol (100%) rename contracts/{test => v1}/BFactory.sol (100%) rename contracts/{test => v1}/BMath.sol (100%) rename contracts/{test => v1}/BNum.sol (100%) rename contracts/{test => v1}/BPool.sol (100%) rename contracts/{test => v1}/BToken.sol (100%) rename contracts/{test => v1}/BalancerConstants.sol (100%) rename contracts/{test => v1}/BalancerOwnable.sol (100%) rename contracts/{test => v1}/BalancerReentrancyGuard.sol (100%) rename contracts/{test => v1}/BalancerSafeMath.sol (100%) rename contracts/{test => v1}/CRPFactory.sol (100%) rename contracts/{test => v1}/ConfigurableRightsPool.sol (99%) rename contracts/{test => v1}/IBFactory.sol (100%) rename contracts/{test => v1}/PCToken.sol (99%) rename contracts/{test => v1}/RightsManager.sol (100%) rename contracts/{test => v1}/SmartPoolManager.sol (99%) diff --git a/.solcover.js b/.solcover.js index 1a3090b..72796c4 100644 --- a/.solcover.js +++ b/.solcover.js @@ -1,9 +1,10 @@ module.exports = { port: 8555, skipFiles: [ - 'Migrations.sol', - 'DSProxyFactory.sol', - 'test' + 'test', + 'common', + 'v1', + 'v2', ], testrpcOptions: "-p 8555 -d" }; diff --git a/contracts/DSProxyFactory.sol b/contracts/common/DSProxyFactory.sol similarity index 100% rename from contracts/DSProxyFactory.sol rename to contracts/common/DSProxyFactory.sol diff --git a/contracts/test/IERC20.sol b/contracts/common/IERC20.sol similarity index 100% rename from contracts/test/IERC20.sol rename to contracts/common/IERC20.sol diff --git a/contracts/test/SafeApprove.sol b/contracts/common/SafeApprove.sol similarity index 100% rename from contracts/test/SafeApprove.sol rename to contracts/common/SafeApprove.sol diff --git a/contracts/test/TToken.sol b/contracts/common/TToken.sol similarity index 100% rename from contracts/test/TToken.sol rename to contracts/common/TToken.sol diff --git a/contracts/test/TTokenFactory.sol b/contracts/common/TTokenFactory.sol similarity index 100% rename from contracts/test/TTokenFactory.sol rename to contracts/common/TTokenFactory.sol diff --git a/contracts/test/BColor.sol b/contracts/v1/BColor.sol similarity index 100% rename from contracts/test/BColor.sol rename to contracts/v1/BColor.sol diff --git a/contracts/test/BConst.sol b/contracts/v1/BConst.sol similarity index 100% rename from contracts/test/BConst.sol rename to contracts/v1/BConst.sol diff --git a/contracts/test/BFactory.sol b/contracts/v1/BFactory.sol similarity index 100% rename from contracts/test/BFactory.sol rename to contracts/v1/BFactory.sol diff --git a/contracts/test/BMath.sol b/contracts/v1/BMath.sol similarity index 100% rename from contracts/test/BMath.sol rename to contracts/v1/BMath.sol diff --git a/contracts/test/BNum.sol b/contracts/v1/BNum.sol similarity index 100% rename from contracts/test/BNum.sol rename to contracts/v1/BNum.sol diff --git a/contracts/test/BPool.sol b/contracts/v1/BPool.sol similarity index 100% rename from contracts/test/BPool.sol rename to contracts/v1/BPool.sol diff --git a/contracts/test/BToken.sol b/contracts/v1/BToken.sol similarity index 100% rename from contracts/test/BToken.sol rename to contracts/v1/BToken.sol diff --git a/contracts/test/BalancerConstants.sol b/contracts/v1/BalancerConstants.sol similarity index 100% rename from contracts/test/BalancerConstants.sol rename to contracts/v1/BalancerConstants.sol diff --git a/contracts/test/BalancerOwnable.sol b/contracts/v1/BalancerOwnable.sol similarity index 100% rename from contracts/test/BalancerOwnable.sol rename to contracts/v1/BalancerOwnable.sol diff --git a/contracts/test/BalancerReentrancyGuard.sol b/contracts/v1/BalancerReentrancyGuard.sol similarity index 100% rename from contracts/test/BalancerReentrancyGuard.sol rename to contracts/v1/BalancerReentrancyGuard.sol diff --git a/contracts/test/BalancerSafeMath.sol b/contracts/v1/BalancerSafeMath.sol similarity index 100% rename from contracts/test/BalancerSafeMath.sol rename to contracts/v1/BalancerSafeMath.sol diff --git a/contracts/test/CRPFactory.sol b/contracts/v1/CRPFactory.sol similarity index 100% rename from contracts/test/CRPFactory.sol rename to contracts/v1/CRPFactory.sol diff --git a/contracts/test/ConfigurableRightsPool.sol b/contracts/v1/ConfigurableRightsPool.sol similarity index 99% rename from contracts/test/ConfigurableRightsPool.sol rename to contracts/v1/ConfigurableRightsPool.sol index 72bb360..9b0333a 100644 --- a/contracts/test/ConfigurableRightsPool.sol +++ b/contracts/v1/ConfigurableRightsPool.sol @@ -14,9 +14,9 @@ import "./BalancerOwnable.sol"; // Interfaces // Libraries +import "../common/SafeApprove.sol"; import { RightsManager } from "./RightsManager.sol"; import "./SmartPoolManager.sol"; -import "./SafeApprove.sol"; // Contracts diff --git a/contracts/test/IBFactory.sol b/contracts/v1/IBFactory.sol similarity index 100% rename from contracts/test/IBFactory.sol rename to contracts/v1/IBFactory.sol diff --git a/contracts/test/PCToken.sol b/contracts/v1/PCToken.sol similarity index 99% rename from contracts/test/PCToken.sol rename to contracts/v1/PCToken.sol index a2c5b86..8fc081d 100644 --- a/contracts/test/PCToken.sol +++ b/contracts/v1/PCToken.sol @@ -3,9 +3,9 @@ pragma solidity 0.6.12; // Imports +import "../common/IERC20.sol"; import "./BalancerSafeMath.sol"; import "./BalancerConstants.sol"; -import "./IERC20.sol"; // Contracts diff --git a/contracts/test/RightsManager.sol b/contracts/v1/RightsManager.sol similarity index 100% rename from contracts/test/RightsManager.sol rename to contracts/v1/RightsManager.sol diff --git a/contracts/test/SmartPoolManager.sol b/contracts/v1/SmartPoolManager.sol similarity index 99% rename from contracts/test/SmartPoolManager.sol rename to contracts/v1/SmartPoolManager.sol index 30379c0..f092322 100644 --- a/contracts/test/SmartPoolManager.sol +++ b/contracts/v1/SmartPoolManager.sol @@ -6,12 +6,12 @@ pragma experimental ABIEncoderV2; // Imports -import "./IERC20.sol"; +import "../common/IERC20.sol"; +import "../common/SafeApprove.sol"; import "./ConfigurableRightsPool.sol"; import "./IBFactory.sol"; import "./BalancerConstants.sol"; import "./BalancerSafeMath.sol"; -import "./SafeApprove.sol"; /** From 0dcfa096f74e0e3ce2c0cfbd6f24db96627651ab Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:56:26 +0300 Subject: [PATCH 04/29] Remove unused function arg in migration scripts --- migrations/2_deploy_proxy.js | 2 +- migrations/3_test_factories.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/2_deploy_proxy.js b/migrations/2_deploy_proxy.js index e57e5f3..a91cd8d 100644 --- a/migrations/2_deploy_proxy.js +++ b/migrations/2_deploy_proxy.js @@ -5,7 +5,7 @@ const BalancerSafeMath = artifacts.require('BalancerSafeMath'); const BActions = artifacts.require('BActions'); const BFactory = artifacts.require('BFactory'); -module.exports = async function(deployer, network, accounts) { +module.exports = async function(deployer, network) { if (network == 'development' || network == 'soliditycoverage') { await deployer.deploy(RightsManager); await deployer.deploy(SmartPoolManager); diff --git a/migrations/3_test_factories.js b/migrations/3_test_factories.js index 3115af6..b0ab32d 100644 --- a/migrations/3_test_factories.js +++ b/migrations/3_test_factories.js @@ -1,7 +1,7 @@ const TTokenFactory = artifacts.require("TTokenFactory"); const DSProxyFactory = artifacts.require("DSProxyFactory"); -module.exports = async function(deployer, network, accounts) { +module.exports = async function(deployer, network) { if (network == 'development' || network == 'soliditycoverage') { deployer.deploy(TTokenFactory); deployer.deploy(DSProxyFactory); From 0fb14a6a94547ee71369244602f2cd379e5ebecf Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:57:09 +0300 Subject: [PATCH 05/29] Add exitPool to AbstractPool contract --- contracts/BActions.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 1ee7905..826108d 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -34,6 +34,7 @@ abstract contract AbstractPool is ERC20, BalancerOwnable { function joinswapExternAmountIn( address tokenIn, uint tokenAmountIn, uint minPoolAmountOut ) external virtual returns (uint poolAmountOut); + function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external virtual; } abstract contract BPool is AbstractPool { From c6a2e594a0669f5d0dd4478b8c97889e70b6a0f9 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 15:59:04 +0300 Subject: [PATCH 06/29] Add v2 BPool and Vault mocks --- contracts/BActions.sol | 6 +++ contracts/v2/BalancerPool.sol | 78 +++++++++++++++++++++++++++++++++++ contracts/v2/Vault.sol | 77 ++++++++++++++++++++++++++++++++++ migrations/2_deploy_proxy.js | 6 ++- 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 contracts/v2/BalancerPool.sol create mode 100644 contracts/v2/Vault.sol diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 826108d..614c10f 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -48,6 +48,12 @@ abstract contract BPool is AbstractPool { function getBalance(address token) external view virtual returns (uint); } +abstract contract BalancerPool is ERC20 { + function joinPool(uint256 poolAmountOut, uint128[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; +} + +abstract contract Vault {} + abstract contract BFactory { function newBPool() external virtual returns (BPool); } diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol new file mode 100644 index 0000000..6a3af79 --- /dev/null +++ b/contracts/v2/BalancerPool.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.6.12; + +import './Vault.sol'; + +// BalancerPool mock; does not represent the actual BalancerPool design +contract BalancerPool { + Vault _vault; + bytes32 _poolId; + + mapping(address => uint256) private _balance; + uint256 private _totalSupply; + + function init( + Vault vault, + bytes32 poolId, + uint128[] calldata amounts + ) external { + _vault = vault; + _poolId = poolId; + _totalSupply = 100 ether; + + IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + _vault.addLiquidity(_poolId, msg.sender, tokens, amounts, false); + _balance[msg.sender] = 100 ether; + } + + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address owner) public view returns (uint256){ + return _balance[owner]; + } + + function joinPool( + uint256 poolAmountOut, + uint128[] calldata maxAmountsIn, + bool transferTokens, + address beneficiary + ) external { + IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + + uint128[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); + + uint256 poolTotal = totalSupply(); + uint128 ratio = uint128(poolAmountOut / poolTotal); + require(ratio != 0, "ERR_MATH_APPROX"); + + require(maxAmountsIn.length == tokens.length, "Tokens and amounts length mismatch"); + + uint128[] memory amountsIn = new uint128[](tokens.length); + for (uint256 i = 0; i < tokens.length; i++) { + amountsIn[i] = uint128(balances[i] * ratio); + require(amountsIn[i] <= maxAmountsIn[i], "ERR_LIMIT_IN"); + } + + _vault.addLiquidity(_poolId, msg.sender, tokens, amountsIn, !transferTokens); + + _mintPoolTokens(beneficiary, poolAmountOut); + } + + function _mintPoolTokens(address recipient, uint256 amount) internal { + _balance[address(this)] = _balance[address(this)] + amount; + _totalSupply = _totalSupply + amount; + + _move(address(this), recipient, amount); + } + + function _move( + address sender, + address recipient, + uint256 amount + ) internal { + _balance[sender] = _balance[sender] - amount; + _balance[recipient] = _balance[recipient] + amount; + } +} diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol new file mode 100644 index 0000000..11742be --- /dev/null +++ b/contracts/v2/Vault.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.6.12; + +import '../common/IERC20.sol'; + +// Vault mock; does not represent the actual Vault design +contract Vault { + uint256 _poolCount; + mapping(bytes32 => IERC20[]) internal _poolTokens; + mapping(bytes32 => mapping(IERC20 => uint128)) internal _poolTokenBalance; + + function newPool(IERC20[] memory tokens) external returns (bytes32) { + bytes32 poolId = bytes32(_poolCount); + _poolTokens[poolId] = tokens; + _poolCount = _poolCount + 1; + return poolId; + } + + function getPoolTokens(bytes32 poolId) external returns (IERC20[] memory) { + return _poolTokens[poolId]; + } + + function getPoolTokenBalances(bytes32 poolId, IERC20[] calldata tokens) + external + view + returns (uint128[] memory) + { + uint128[] memory balances = new uint128[](tokens.length); + for (uint256 i = 0; i < tokens.length; ++i) { + balances[i] = _poolTokenBalance[poolId][tokens[i]]; + } + + return balances; + } + + function addLiquidity( + bytes32 poolId, + address from, + IERC20[] calldata tokens, + uint128[] calldata amounts, + bool withdrawFromUserBalance + ) external { + require(tokens.length == amounts.length, "Tokens and total amounts length mismatch"); + + for (uint256 i = 0; i < tokens.length; ++i) { + if (amounts[i] > 0) { + uint128 toReceive = amounts[i]; + uint128 received = _pullTokens(tokens[i], from, toReceive); + require(received == toReceive, "Not enough tokens received"); + _increasePoolCash(poolId, tokens[i], amounts[i]); + } + } + } + + function _pullTokens( + IERC20 token, + address from, + uint128 amount + ) internal returns (uint128) { + if (amount == 0) { + return 0; + } + uint256 currentBalance = token.balanceOf(address(this)); + token.transferFrom(from, address(this), amount); + uint256 newBalance = token.balanceOf(address(this)); + return uint128(newBalance - currentBalance); + } + + function _increasePoolCash( + bytes32 poolId, + IERC20 token, + uint128 amount + ) internal { + uint128 currentBalance = _poolTokenBalance[poolId][token]; + _poolTokenBalance[poolId][token] = currentBalance + amount; + } +} diff --git a/migrations/2_deploy_proxy.js b/migrations/2_deploy_proxy.js index a91cd8d..dd4c1af 100644 --- a/migrations/2_deploy_proxy.js +++ b/migrations/2_deploy_proxy.js @@ -4,6 +4,8 @@ const CRPFactory = artifacts.require('CRPFactory'); const BalancerSafeMath = artifacts.require('BalancerSafeMath'); const BActions = artifacts.require('BActions'); const BFactory = artifacts.require('BFactory'); +const BalancerPool = artifacts.require('BalancerPool'); +const Vault = artifacts.require('Vault'); module.exports = async function(deployer, network) { if (network == 'development' || network == 'soliditycoverage') { @@ -15,9 +17,11 @@ module.exports = async function(deployer, network) { await deployer.link(BalancerSafeMath, CRPFactory); await deployer.link(RightsManager, CRPFactory); await deployer.link(SmartPoolManager, CRPFactory); - await deployer.deploy(CRPFactory); + await deployer.deploy(Vault); + await deployer.deploy(BalancerPool); + await deployer.deploy(BActions, BFactory.address); } else if (network == 'kovan-fork' || network == 'kovan') { deployer.deploy(BActions, '0x8f7F78080219d4066A8036ccD30D588B416a40DB'); From eff3dd939b14d8c8e3f7de9e1668481b4b9dad3a Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 16:12:22 +0300 Subject: [PATCH 07/29] Add 'migrate' action --- contracts/BActions.sol | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 614c10f..277c58d 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -354,6 +354,36 @@ contract BActions { ) external { crp.removeWhitelistedLiquidityProvider(provider); } + + // --- Migration --- + + function migrate( + Vault vault, + BPool poolIn, + uint poolInAmount, + uint[] calldata minAmountsOut, + BalancerPool poolOut, + uint poolOutAmount, + uint128[] calldata maxAmountsIn + ) external { + address[] memory tokens = poolIn.getFinalTokens(); + // Transfer v1 BPTs to proxy + poolIn.transferFrom(msg.sender, address(this), poolInAmount); + // Exit v1 pool + poolIn.exitPool(poolInAmount, minAmountsOut); + // Approve each token to v2 vault + for (uint i = 0; i < tokens.length; i++) { + ERC20 token = ERC20(tokens[i]); + _safeApprove(token, address(vault), uint(-1)); + } + // Join v2 pool and transfer v2 BPTs to user + poolOut.joinPool(poolOutAmount, maxAmountsIn, true, msg.sender); + // Send dust back + for (uint i = 0; i < tokens.length; i++) { + ERC20 token = ERC20(tokens[i]); + require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); + } + } // --- Internals --- From 32af8adac1f319fed1ac688faf62fe4c0aab30ab Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 12 Jan 2021 16:12:38 +0300 Subject: [PATCH 08/29] Test migration --- test/migration.js | 169 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 test/migration.js diff --git a/test/migration.js b/test/migration.js new file mode 100644 index 0000000..c367d26 --- /dev/null +++ b/test/migration.js @@ -0,0 +1,169 @@ +const truffleAssert = require('truffle-assertions'); + +const TToken = artifacts.require('TToken'); +const TTokenFactory = artifacts.require('TTokenFactory'); +const BFactory = artifacts.require('BFactory'); +const BActions = artifacts.require('BActions'); +const DSProxyFactory = artifacts.require('DSProxyFactory'); +const DSProxy = artifacts.require('DSProxy'); +const BPool = artifacts.require('BPool'); +const Vault = artifacts.require('Vault'); +const BalancerPool = artifacts.require('BalancerPool'); + +contract('BActions', async (accounts) => { + const admin = accounts[0]; + const user1 = accounts[1]; + const { toHex, toWei, fromWei } = web3.utils; + + const MAX = web3.utils.toTwosComplement(-1); + + describe('Liquidity migration', () => { + let factory; + let FACTORY; + let proxyFactory; + let bactions; + let BACTIONS; + let tokens; + let USER_PROXY; + let userProxy; + let POOL_V1; + let VAULT; + let POOL_V2; + let dai; let mkr; let zrx; let weth; + let DAI; let MKR; let ZRX; let WETH; + + before(async () => { + proxyFactory = await DSProxyFactory.deployed(); + + bactions = await BActions.deployed(); + BACTIONS = bactions.address; + + await mintTokens(); + await setupProxy(); + await createV1Pool(); + await setupV2(); + }); + + async function mintTokens() { + tokens = await TTokenFactory.deployed(); + factory = await BFactory.deployed(); + FACTORY = factory.address; + + await tokens.build(toHex('DAI'), toHex('DAI'), 18); + await tokens.build(toHex('MKR'), toHex('MKR'), 18); + await tokens.build(toHex('ZRX'), toHex('ZRX'), 18); + await tokens.build(toHex('WETH'), toHex('WETH'), 18); + + DAI = await tokens.get.call(toHex('DAI')); + MKR = await tokens.get.call(toHex('MKR')); + ZRX = await tokens.get.call(toHex('ZRX')); + WETH = await tokens.get.call(toHex('WETH')); + + dai = await TToken.at(DAI); + mkr = await TToken.at(MKR); + zrx = await TToken.at(ZRX); + weth = await TToken.at(WETH); + + await dai.mint(admin, toWei('10000')); + await mkr.mint(admin, toWei('20')); + await zrx.mint(admin, toWei('100')); + await weth.mint(admin, toWei('40')); + + await dai.mint(user1, toWei('10000')); + await mkr.mint(user1, toWei('20')); + } + + async function setupProxy() { + USER_PROXY = await proxyFactory.build.call(); + await proxyFactory.build(); + userProxy = await DSProxy.at(USER_PROXY); + + await dai.approve(USER_PROXY, MAX); + await mkr.approve(USER_PROXY, MAX); + await zrx.approve(USER_PROXY, MAX); + await weth.approve(USER_PROXY, MAX); + } + + async function createV1Pool() { + const createTokens = [DAI, MKR, WETH]; + const createBalances = [toWei('400'), toWei('1'), toWei('2')]; + const createWeights = [toWei('5'), toWei('5'), toWei('5')]; + const swapFee = toWei('0.03'); + const finalize = true; + + const createInterface = BActions.abi.find((iface) => iface.name === 'create'); + + const params = [ + FACTORY, + createTokens, + createBalances, + createWeights, + swapFee, + finalize, + ]; + + const functionCall = web3.eth.abi.encodeFunctionCall(createInterface, params); + + const poolAddress = await userProxy.methods['execute(address,bytes)'].call( + BACTIONS, functionCall, + ); + POOL_V1 = `0x${poolAddress.slice(-40)}`; + + await userProxy.methods['execute(address,bytes)'](BACTIONS, functionCall); + } + + async function setupV2() { + const vault = await Vault.deployed(); + VAULT = vault.address; + vault.newPool([DAI, MKR, WETH]); + + const balancerPool = await BalancerPool.deployed(); + POOL_V2 = balancerPool.address; + // Approve each token to v2 vault + await dai.approve(VAULT, MAX); + await mkr.approve(VAULT, MAX); + await zrx.approve(VAULT, MAX); + await weth.approve(VAULT, MAX); + balancerPool.init(VAULT, '0x0', [toWei('200'), toWei('0.5'), toWei('1')]); + } + + it('Simple migration', async () => { + // Approve v1 BPT to proxy + const bpt = await BPool.at(POOL_V1); + await bpt.approve(USER_PROXY, MAX); + + const params = [ + VAULT, + POOL_V1, + toWei('100'), + [0, 0, 0], + POOL_V2, + toWei('200'), + [toWei('400'), toWei('1'), toWei('2')], + ]; + + const startV1Balance = await bpt.balanceOf(admin); // should go 100 -> 0 + const v2Pool = await BalancerPool.at(POOL_V2); + const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 + + const functionSig = web3.eth.abi.encodeFunctionSignature( + 'migrate(address,address,uint256,uint256[],address,uint256,uint128[])', + ); + const functionData = web3.eth.abi.encodeParameters( + ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256', 'uint128[]'], + params, + ); + + const argumentData = functionData.substring(2); + const inputData = `${functionSig}${argumentData}`; + + await userProxy.methods['execute(address,bytes)'](BACTIONS, inputData); + + const endV1Balance = await bpt.balanceOf(admin); + const endV2Balance = await v2Pool.balanceOf(admin); + + assert.equal(fromWei(startV1Balance.sub(endV1Balance)), '100'); + assert.equal(fromWei(endV2Balance.sub(startV2Balance)), '200'); + }); + }); +}); From bbad60a3d0abfcf036fe14fc68e7dbba5898613d Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 14 Jan 2021 13:26:39 +0300 Subject: [PATCH 09/29] Remove var docstring --- contracts/v1/BMath.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/contracts/v1/BMath.sol b/contracts/v1/BMath.sol index 060f809..a6019b2 100644 --- a/contracts/v1/BMath.sol +++ b/contracts/v1/BMath.sol @@ -114,10 +114,6 @@ contract BMath is BBronze, BConst, BNum { public pure returns (uint poolAmountOut) { - // Charge the trading fee for the proportion of tokenAi - /// which is implicitly traded to the other pool tokens. - // That proportion is (1- weightTokenIn) - // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee); uint normalizedWeight = bdiv(tokenWeightIn, totalWeight); uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz)); From cb6fd24197f93a731bbd4fe9963547b01b80eabd Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 14 Jan 2021 13:47:44 +0300 Subject: [PATCH 10/29] Switch Vault token balances to uint256 --- contracts/BActions.sol | 4 ++-- contracts/v2/BalancerPool.sol | 12 ++++++------ contracts/v2/Vault.sol | 22 +++++++++++----------- test/migration.js | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 277c58d..dc4e793 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -49,7 +49,7 @@ abstract contract BPool is AbstractPool { } abstract contract BalancerPool is ERC20 { - function joinPool(uint256 poolAmountOut, uint128[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; + function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; } abstract contract Vault {} @@ -364,7 +364,7 @@ contract BActions { uint[] calldata minAmountsOut, BalancerPool poolOut, uint poolOutAmount, - uint128[] calldata maxAmountsIn + uint256[] calldata maxAmountsIn ) external { address[] memory tokens = poolIn.getFinalTokens(); // Transfer v1 BPTs to proxy diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol index 6a3af79..fa7883a 100644 --- a/contracts/v2/BalancerPool.sol +++ b/contracts/v2/BalancerPool.sol @@ -14,7 +14,7 @@ contract BalancerPool { function init( Vault vault, bytes32 poolId, - uint128[] calldata amounts + uint256[] calldata amounts ) external { _vault = vault; _poolId = poolId; @@ -35,23 +35,23 @@ contract BalancerPool { function joinPool( uint256 poolAmountOut, - uint128[] calldata maxAmountsIn, + uint256[] calldata maxAmountsIn, bool transferTokens, address beneficiary ) external { IERC20[] memory tokens = _vault.getPoolTokens(_poolId); - uint128[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); + uint256[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); uint256 poolTotal = totalSupply(); - uint128 ratio = uint128(poolAmountOut / poolTotal); + uint256 ratio = uint256(poolAmountOut / poolTotal); require(ratio != 0, "ERR_MATH_APPROX"); require(maxAmountsIn.length == tokens.length, "Tokens and amounts length mismatch"); - uint128[] memory amountsIn = new uint128[](tokens.length); + uint256[] memory amountsIn = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { - amountsIn[i] = uint128(balances[i] * ratio); + amountsIn[i] = uint256(balances[i] * ratio); require(amountsIn[i] <= maxAmountsIn[i], "ERR_LIMIT_IN"); } diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol index 11742be..1b166c6 100644 --- a/contracts/v2/Vault.sol +++ b/contracts/v2/Vault.sol @@ -7,7 +7,7 @@ import '../common/IERC20.sol'; contract Vault { uint256 _poolCount; mapping(bytes32 => IERC20[]) internal _poolTokens; - mapping(bytes32 => mapping(IERC20 => uint128)) internal _poolTokenBalance; + mapping(bytes32 => mapping(IERC20 => uint256)) internal _poolTokenBalance; function newPool(IERC20[] memory tokens) external returns (bytes32) { bytes32 poolId = bytes32(_poolCount); @@ -23,9 +23,9 @@ contract Vault { function getPoolTokenBalances(bytes32 poolId, IERC20[] calldata tokens) external view - returns (uint128[] memory) + returns (uint256[] memory) { - uint128[] memory balances = new uint128[](tokens.length); + uint256[] memory balances = new uint256[](tokens.length); for (uint256 i = 0; i < tokens.length; ++i) { balances[i] = _poolTokenBalance[poolId][tokens[i]]; } @@ -37,15 +37,15 @@ contract Vault { bytes32 poolId, address from, IERC20[] calldata tokens, - uint128[] calldata amounts, + uint256[] calldata amounts, bool withdrawFromUserBalance ) external { require(tokens.length == amounts.length, "Tokens and total amounts length mismatch"); for (uint256 i = 0; i < tokens.length; ++i) { if (amounts[i] > 0) { - uint128 toReceive = amounts[i]; - uint128 received = _pullTokens(tokens[i], from, toReceive); + uint256 toReceive = amounts[i]; + uint256 received = _pullTokens(tokens[i], from, toReceive); require(received == toReceive, "Not enough tokens received"); _increasePoolCash(poolId, tokens[i], amounts[i]); } @@ -55,23 +55,23 @@ contract Vault { function _pullTokens( IERC20 token, address from, - uint128 amount - ) internal returns (uint128) { + uint256 amount + ) internal returns (uint256) { if (amount == 0) { return 0; } uint256 currentBalance = token.balanceOf(address(this)); token.transferFrom(from, address(this), amount); uint256 newBalance = token.balanceOf(address(this)); - return uint128(newBalance - currentBalance); + return newBalance - currentBalance; } function _increasePoolCash( bytes32 poolId, IERC20 token, - uint128 amount + uint256 amount ) internal { - uint128 currentBalance = _poolTokenBalance[poolId][token]; + uint256 currentBalance = _poolTokenBalance[poolId][token]; _poolTokenBalance[poolId][token] = currentBalance + amount; } } diff --git a/test/migration.js b/test/migration.js index c367d26..0a3457f 100644 --- a/test/migration.js +++ b/test/migration.js @@ -147,10 +147,10 @@ contract('BActions', async (accounts) => { const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 const functionSig = web3.eth.abi.encodeFunctionSignature( - 'migrate(address,address,uint256,uint256[],address,uint256,uint128[])', + 'migrate(address,address,uint256,uint256[],address,uint256,uint256[])', ); const functionData = web3.eth.abi.encodeParameters( - ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256', 'uint128[]'], + ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256', 'uint256[]'], params, ); From 71c9f7dca5e12a1a2f8e08f8842f7c53f04da468 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 14 Jan 2021 13:57:24 +0300 Subject: [PATCH 11/29] Add LogExpMath lib --- contracts/v2/LogExpMath.sol | 293 ++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 contracts/v2/LogExpMath.sol diff --git a/contracts/v2/LogExpMath.sol b/contracts/v2/LogExpMath.sol new file mode 100644 index 0000000..cf060d5 --- /dev/null +++ b/contracts/v2/LogExpMath.sol @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.6.12; + +// There's plenty of linter errors caused by this file, we'll eventually +// revisit it to make it more readable, verfiable and testable. +/* solhint-disable */ + +/** + * @title Ethereum library for logarithm and exponential functions with 18 decimal precision. + * @author Fernando Martinelli - @fernandomartinelli + * @author Sergio Yuhjtman - @sergioyuhjtman + * @author Daniel Fernandez - @dmf7z + */ +library LogExpMath { + int256 constant DECIMALS = 10**18; + int256 constant DOUBLE_DECIMALS = DECIMALS * DECIMALS; + int256 constant PRECISION = 10**20; + int256 constant DOUBLE_PRECISION = PRECISION * PRECISION; + int256 constant PRECISION_LOG_UNDER_BOUND = DECIMALS - 10**17; + int256 constant PRECISION_LOG_UPPER_BOUND = DECIMALS + 10**17; + int256 constant EXPONENT_LB = -41446531673892822312; + int256 constant EXPONENT_UB = 130700829182905140221; + uint256 constant MILD_EXPONENT_BOUND = 2**254 / uint256(PRECISION); + + int256 constant x0 = 128000000000000000000; //2ˆ7 + int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; //eˆ(x0) + int256 constant x1 = 64000000000000000000; //2ˆ6 + int256 constant a1 = 6235149080811616882910000000; //eˆ(x1) + int256 constant x2 = 3200000000000000000000; //2ˆ5 + int256 constant a2 = 7896296018268069516100000000000000; //eˆ(x2) + int256 constant x3 = 1600000000000000000000; //2ˆ4 + int256 constant a3 = 888611052050787263676000000; //eˆ(x3) + int256 constant x4 = 800000000000000000000; //2ˆ3 + int256 constant a4 = 298095798704172827474000; //eˆ(x4) + int256 constant x5 = 400000000000000000000; //2ˆ2 + int256 constant a5 = 5459815003314423907810; //eˆ(x5) + int256 constant x6 = 200000000000000000000; //2ˆ1 + int256 constant a6 = 738905609893065022723; //eˆ(x6) + int256 constant x7 = 100000000000000000000; //2ˆ0 + int256 constant a7 = 271828182845904523536; //eˆ(x7) + int256 constant x8 = 50000000000000000000; //2ˆ-1 + int256 constant a8 = 164872127070012814685; //eˆ(x8) + int256 constant x9 = 25000000000000000000; //2ˆ-2 + int256 constant a9 = 128402541668774148407; //eˆ(x9) + int256 constant x10 = 12500000000000000000; //2ˆ-3 + int256 constant a10 = 113314845306682631683; //eˆ(x10) + int256 constant x11 = 6250000000000000000; //2ˆ-4 + int256 constant a11 = 106449445891785942956; //eˆ(x11) + + /** + * Calculate the natural exponentiation of a number with 18 decimals precision. + * @param x Exponent with 18 decimal places. + * @notice Max x is log((2^255 - 1) / 10^20) = 130.700829182905140221 + * @notice Min x log(0.000000000000000001) = -41.446531673892822312 + * @return eˆx + */ + function n_exp(int256 x) internal pure returns (int256) { + require( + x >= EXPONENT_LB && x <= EXPONENT_UB, + "Natural exp argument must be between -41.446531673892822312 and 130.700829182905140221" + ); + if (x < 0) return (DOUBLE_DECIMALS / n_exp(-x)); + int256 ans = PRECISION; + int256 last = 1; + if (x >= x0) { + last = a0; + x -= x0; + } + if (x >= x1) { + last *= a1; + x -= x1; + } + x *= 100; + if (x >= x2) { + ans = (ans * a2) / PRECISION; + x -= x2; + } + if (x >= x3) { + ans = (ans * a3) / PRECISION; + x -= x3; + } + if (x >= x4) { + ans = (ans * a4) / PRECISION; + x -= x4; + } + if (x >= x5) { + ans = (ans * a5) / PRECISION; + x -= x5; + } + if (x >= x6) { + ans = (ans * a6) / PRECISION; + x -= x6; + } + if (x >= x7) { + ans = (ans * a7) / PRECISION; + x -= x7; + } + if (x >= x8) { + ans = (ans * a8) / PRECISION; + x -= x8; + } + if (x >= x9) { + ans = (ans * a9) / PRECISION; + x -= x9; + } + int256 s = PRECISION; + int256 t = x; + s += t; + t = ((t * x) / 2) / PRECISION; + s += t; + t = ((t * x) / 3) / PRECISION; + s += t; + t = ((t * x) / 4) / PRECISION; + s += t; + t = ((t * x) / 5) / PRECISION; + s += t; + t = ((t * x) / 6) / PRECISION; + s += t; + t = ((t * x) / 7) / PRECISION; + s += t; + t = ((t * x) / 8) / PRECISION; + s += t; + t = ((t * x) / 9) / PRECISION; + s += t; + t = ((t * x) / 10) / PRECISION; + s += t; + t = ((t * x) / 11) / PRECISION; + s += t; + t = ((t * x) / 12) / PRECISION; + s += t; + return (((ans * s) / PRECISION) * last) / 100; + } + + /** + * Calculate the natural logarithm of a number with 18 decimals precision. + * @param a Positive number with 18 decimal places. + * @return ln(x) + */ + function n_log(int256 a) internal pure returns (int256) { + require(a > 0, "Natural log argument must be positive"); + if (a < DECIMALS) return (-n_log(DOUBLE_DECIMALS / a)); + int256 ans = 0; + if (a >= a0 * DECIMALS) { + ans += x0; + a /= a0; + } + if (a >= a1 * DECIMALS) { + ans += x1; + a /= a1; + } + a *= 100; + ans *= 100; + if (a >= a2) { + ans += x2; + a = (a * PRECISION) / a2; + } + if (a >= a3) { + ans += x3; + a = (a * PRECISION) / a3; + } + if (a >= a4) { + ans += x4; + a = (a * PRECISION) / a4; + } + if (a >= a5) { + ans += x5; + a = (a * PRECISION) / a5; + } + if (a >= a6) { + ans += x6; + a = (a * PRECISION) / a6; + } + if (a >= a7) { + ans += x7; + a = (a * PRECISION) / a7; + } + if (a >= a8) { + ans += x8; + a = (a * PRECISION) / a8; + } + if (a >= a9) { + ans += x9; + a = (a * PRECISION) / a9; + } + if (a >= a10) { + ans += x10; + a = (a * PRECISION) / a10; + } + if (a >= a11) { + ans += x11; + a = (a * PRECISION) / a11; + } + int256 z = (PRECISION * (a - PRECISION)) / (a + PRECISION); + int256 s = z; + int256 z_squared = (z * z) / PRECISION; + int256 t = (z * z_squared) / PRECISION; + s += t / 3; + t = (t * z_squared) / PRECISION; + s += t / 5; + t = (t * z_squared) / PRECISION; + s += t / 7; + t = (t * z_squared) / PRECISION; + s += t / 9; + t = (t * z_squared) / PRECISION; + s += t / 11; + return (ans + 2 * s) / 100; + } + + /** + * Computes x to the power of y for numbers with 18 decimals precision. + * @param x Base with 18 decimal places. + * @param y Exponent with 18 decimal places. + * @notice Must fulfil: -41.446531673892822312 < (log(x) * y) < 130.700829182905140221 + * @return xˆy + */ + function pow(uint256 x, uint256 y) internal pure returns (uint256) { + if (y == 0) { + return uint256(DECIMALS); + } + + if (x == 0) { + return 0; + } + + require(x < 2**255, "x must be less than 2**255"); // uint256 can be casted to a positive int256 + require(y < MILD_EXPONENT_BOUND, "input y has to be less than 2**254 / 10**20"); + int256 x_int256 = int256(x); + int256 y_int256 = int256(y); + int256 logx_times_y; + if (PRECISION_LOG_UNDER_BOUND < x_int256 && x_int256 < PRECISION_LOG_UPPER_BOUND) { + int256 logbase = n_log_36(x_int256); + logx_times_y = ((logbase / DECIMALS) * y_int256 + ((logbase % DECIMALS) * y_int256) / DECIMALS); + } else { + logx_times_y = n_log(x_int256) * y_int256; + } + require( + EXPONENT_LB * DECIMALS <= logx_times_y && logx_times_y <= EXPONENT_UB * DECIMALS, + "log(x) times y must be between -41.446531673892822312 and 130.700829182905140221" + ); + logx_times_y /= DECIMALS; + return uint256(n_exp(logx_times_y)); + } + + /** + * Computes log of a number in base of another number, both numbers with 18 decimals precision. + * @param arg Argument with 18 decimal places. + * @param base Base with 18 decimal places. + * @notice Must fulfil: -41.446531673892822312 < (log(x) * y) < 130.700829182905140221 + * @return log[base](arg) + */ + function log(int256 arg, int256 base) internal pure returns (int256) { + int256 logbase; + if (PRECISION_LOG_UNDER_BOUND < base && base < PRECISION_LOG_UPPER_BOUND) { + logbase = n_log_36(base); + } else { + logbase = n_log(base) * DECIMALS; + } + int256 logarg; + if (PRECISION_LOG_UNDER_BOUND < arg && arg < PRECISION_LOG_UPPER_BOUND) { + logarg = n_log_36(arg); + } else { + logarg = n_log(arg) * DECIMALS; + } + return (logarg * DECIMALS) / logbase; + } + + /** + * Private function to calculate the natural logarithm of a number with 36 decimals precision. + * @param a Positive number with 18 decimal places. + * @return ln(x) + */ + function n_log_36(int256 a) private pure returns (int256) { + a *= DECIMALS; + int256 z = (DOUBLE_DECIMALS * (a - DOUBLE_DECIMALS)) / (a + DOUBLE_DECIMALS); + int256 s = z; + int256 z_squared = (z * z) / DOUBLE_DECIMALS; + int256 t = (z * z_squared) / DOUBLE_DECIMALS; + s += t / 3; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 5; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 7; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 9; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 11; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 13; + t = (t * z_squared) / DOUBLE_DECIMALS; + s += t / 15; + return 2 * s; + } +} From 513b9c2caeb6bd56dbe3789cfc04410b478185de Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 14 Jan 2021 14:32:00 +0300 Subject: [PATCH 12/29] Use unproportional join for migration --- contracts/BActions.sol | 14 ++++--- contracts/v2/BalancerPool.sol | 71 ++++++++++++++++++++++++++++++++++- test/migration.js | 21 ++++++----- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index dc4e793..8f3e6de 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -50,6 +50,9 @@ abstract contract BPool is AbstractPool { abstract contract BalancerPool is ERC20 { function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; + function joinPoolExactTokensInForBPTOut( + uint256 minBPTAmountOut, uint256[] calldata amountsIn, bool transferTokens, address beneficiary + ) external virtual returns (uint256 bptAmountOut); } abstract contract Vault {} @@ -363,8 +366,7 @@ contract BActions { uint poolInAmount, uint[] calldata minAmountsOut, BalancerPool poolOut, - uint poolOutAmount, - uint256[] calldata maxAmountsIn + uint256 minAmountOut ) external { address[] memory tokens = poolIn.getFinalTokens(); // Transfer v1 BPTs to proxy @@ -377,12 +379,12 @@ contract BActions { _safeApprove(token, address(vault), uint(-1)); } // Join v2 pool and transfer v2 BPTs to user - poolOut.joinPool(poolOutAmount, maxAmountsIn, true, msg.sender); - // Send dust back - for (uint i = 0; i < tokens.length; i++) { + uint256[] memory amountsIn = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; ++i) { ERC20 token = ERC20(tokens[i]); - require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); + amountsIn[i] = token.balanceOf(address(this)); } + poolOut.joinPoolExactTokensInForBPTOut(minAmountOut, amountsIn, true, msg.sender); } // --- Internals --- diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol index fa7883a..01e168f 100644 --- a/contracts/v2/BalancerPool.sol +++ b/contracts/v2/BalancerPool.sol @@ -2,19 +2,28 @@ pragma solidity 0.6.12; import './Vault.sol'; +import './LogExpMath.sol'; // BalancerPool mock; does not represent the actual BalancerPool design contract BalancerPool { Vault _vault; bytes32 _poolId; + + uint256 _swapFee; + mapping(address => uint256) private _weight; + uint256 private _totalWeight; mapping(address => uint256) private _balance; uint256 private _totalSupply; + uint128 internal constant ONE = 10**18; + function init( Vault vault, bytes32 poolId, - uint256[] calldata amounts + uint256 swapFee, + uint256[] calldata amounts, + uint256[] calldata weights ) external { _vault = vault; _poolId = poolId; @@ -23,6 +32,12 @@ contract BalancerPool { IERC20[] memory tokens = _vault.getPoolTokens(_poolId); _vault.addLiquidity(_poolId, msg.sender, tokens, amounts, false); _balance[msg.sender] = 100 ether; + + _swapFee = swapFee; + for (uint256 i = 0; i < weights.length; i++) { + _weight[address(tokens[i])] = weights[i]; + _totalWeight += weights[i]; + } } function totalSupply() public view returns (uint256) { @@ -60,6 +75,60 @@ contract BalancerPool { _mintPoolTokens(beneficiary, poolAmountOut); } + function joinPoolExactTokensInForBPTOut( + uint256 minBPTAmountOut, + uint256[] calldata amountsIn, + bool transferTokens, + address beneficiary + ) external returns (uint256 bptAmountOut) { + IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + + uint256[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); + + uint256[] memory normalizedWeights = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; ++i) { + normalizedWeights[i] = _weight[address(tokens[i])] / _totalWeight; + } + + bptAmountOut = _exactTokensInForBPTOut(balances, normalizedWeights, amountsIn, totalSupply(), _swapFee); + require(bptAmountOut >= minBPTAmountOut, "ERR_BPT_OUT_MIN_AMOUNT"); + + _vault.addLiquidity(_poolId, msg.sender, tokens, amountsIn, !transferTokens); + + _mintPoolTokens(beneficiary, bptAmountOut); + } + + function _exactTokensInForBPTOut( + uint256[] memory balances, + uint256[] memory normalizedWeights, + uint256[] memory amountsIn, + uint256 bptTotalSupply, + uint256 swapFee + ) internal pure returns (uint256) { + uint256[] memory tokenBalanceRatiosWithoutFee = new uint256[](amountsIn.length); + uint256 weightedBalanceRatio = 0; + for (uint256 i = 0; i < balances.length; i++) { + tokenBalanceRatiosWithoutFee[i] = (balances[i] + amountsIn[i]) / balances[i]; + weightedBalanceRatio = weightedBalanceRatio + (tokenBalanceRatiosWithoutFee[i] * normalizedWeights[i]); + } + uint256 invariantRatio = ONE; + for (uint256 i = 0; i < balances.length; i++) { + uint256 tokenBalancePercentageExcess; + if (weightedBalanceRatio >= tokenBalanceRatiosWithoutFee[i]) { + tokenBalancePercentageExcess = 0; + } else { + tokenBalancePercentageExcess = ((tokenBalanceRatiosWithoutFee[i] - weightedBalanceRatio) + / tokenBalanceRatiosWithoutFee[i]) - ONE; + } + + uint256 amountInAfterFee = amountsIn[i] * (ONE - (swapFee * tokenBalancePercentageExcess)); + uint256 tokenBalanceRatio = ONE + (amountInAfterFee / balances[i]); + invariantRatio = invariantRatio * LogExpMath.pow(tokenBalanceRatio, normalizedWeights[i]); + } + + return bptTotalSupply * (invariantRatio - ONE); + } + function _mintPoolTokens(address recipient, uint256 amount) internal { _balance[address(this)] = _balance[address(this)] + amount; _totalSupply = _totalSupply + amount; diff --git a/test/migration.js b/test/migration.js index 0a3457f..c496d13 100644 --- a/test/migration.js +++ b/test/migration.js @@ -124,7 +124,11 @@ contract('BActions', async (accounts) => { await mkr.approve(VAULT, MAX); await zrx.approve(VAULT, MAX); await weth.approve(VAULT, MAX); - balancerPool.init(VAULT, '0x0', [toWei('200'), toWei('0.5'), toWei('1')]); + + const balances = [toWei('200'), toWei('0.5'), toWei('1')]; + const weights = [toWei('5'), toWei('5'), toWei('5')]; + const swapFee = toWei('0.03'); + balancerPool.init(VAULT, '0x0', swapFee, balances, weights); } it('Simple migration', async () => { @@ -138,19 +142,18 @@ contract('BActions', async (accounts) => { toWei('100'), [0, 0, 0], POOL_V2, - toWei('200'), - [toWei('400'), toWei('1'), toWei('2')], + 0, ]; const startV1Balance = await bpt.balanceOf(admin); // should go 100 -> 0 - const v2Pool = await BalancerPool.at(POOL_V2); - const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 + // const v2Pool = await BalancerPool.at(POOL_V2); + // const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 const functionSig = web3.eth.abi.encodeFunctionSignature( - 'migrate(address,address,uint256,uint256[],address,uint256,uint256[])', + 'migrate(address,address,uint256,uint256[],address,uint256)', ); const functionData = web3.eth.abi.encodeParameters( - ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256', 'uint256[]'], + ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256'], params, ); @@ -160,10 +163,10 @@ contract('BActions', async (accounts) => { await userProxy.methods['execute(address,bytes)'](BACTIONS, inputData); const endV1Balance = await bpt.balanceOf(admin); - const endV2Balance = await v2Pool.balanceOf(admin); + // const endV2Balance = await v2Pool.balanceOf(admin); assert.equal(fromWei(startV1Balance.sub(endV1Balance)), '100'); - assert.equal(fromWei(endV2Balance.sub(startV2Balance)), '200'); + // assert.equal(fromWei(endV2Balance.sub(startV2Balance)), '200'); }); }); }); From fbf7759608509ddfeca1b30caeeba143f12e37fe Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Fri, 22 Jan 2021 13:19:56 +0300 Subject: [PATCH 13/29] Handle different token order in migration pool --- contracts/BActions.sol | 18 +++++++++++------- contracts/v2/BalancerPool.sol | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 8f3e6de..030359d 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -53,9 +53,12 @@ abstract contract BalancerPool is ERC20 { function joinPoolExactTokensInForBPTOut( uint256 minBPTAmountOut, uint256[] calldata amountsIn, bool transferTokens, address beneficiary ) external virtual returns (uint256 bptAmountOut); + function getPoolId() external view virtual returns (bytes32); } -abstract contract Vault {} +abstract contract Vault { + function getPoolTokens(bytes32 poolId) external virtual returns (address[] memory); +} abstract contract BFactory { function newBPool() external virtual returns (BPool); @@ -368,20 +371,21 @@ contract BActions { BalancerPool poolOut, uint256 minAmountOut ) external { - address[] memory tokens = poolIn.getFinalTokens(); + address[] memory inTokens = poolIn.getFinalTokens(); + address[] memory outTokens = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool poolIn.exitPool(poolInAmount, minAmountsOut); // Approve each token to v2 vault - for (uint i = 0; i < tokens.length; i++) { - ERC20 token = ERC20(tokens[i]); + for (uint i = 0; i < inTokens.length; i++) { + ERC20 token = ERC20(inTokens[i]); _safeApprove(token, address(vault), uint(-1)); } // Join v2 pool and transfer v2 BPTs to user - uint256[] memory amountsIn = new uint256[](tokens.length); - for (uint256 i = 0; i < tokens.length; ++i) { - ERC20 token = ERC20(tokens[i]); + uint256[] memory amountsIn = new uint256[](outTokens.length); + for (uint256 i = 0; i < outTokens.length; ++i) { + ERC20 token = ERC20(outTokens[i]); amountsIn[i] = token.balanceOf(address(this)); } poolOut.joinPoolExactTokensInForBPTOut(minAmountOut, amountsIn, true, msg.sender); diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol index 01e168f..a9c5f81 100644 --- a/contracts/v2/BalancerPool.sol +++ b/contracts/v2/BalancerPool.sol @@ -48,6 +48,10 @@ contract BalancerPool { return _balance[owner]; } + function getPoolId() external view returns (bytes32) { + return _poolId; + } + function joinPool( uint256 poolAmountOut, uint256[] calldata maxAmountsIn, From 293383d755a7b7d652dd966eee493620f14d958d Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 27 Jan 2021 17:25:14 +0300 Subject: [PATCH 14/29] Use uint128 for v2 pool amounts --- contracts/BActions.sol | 8 ++++---- contracts/v2/BalancerPool.sol | 10 +++++----- contracts/v2/Vault.sol | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 030359d..3ba8791 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -51,7 +51,7 @@ abstract contract BPool is AbstractPool { abstract contract BalancerPool is ERC20 { function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; function joinPoolExactTokensInForBPTOut( - uint256 minBPTAmountOut, uint256[] calldata amountsIn, bool transferTokens, address beneficiary + uint256 minBPTAmountOut, uint128[] calldata amountsIn, bool transferTokens, address beneficiary ) external virtual returns (uint256 bptAmountOut); function getPoolId() external view virtual returns (bytes32); } @@ -383,10 +383,10 @@ contract BActions { _safeApprove(token, address(vault), uint(-1)); } // Join v2 pool and transfer v2 BPTs to user - uint256[] memory amountsIn = new uint256[](outTokens.length); - for (uint256 i = 0; i < outTokens.length; ++i) { + uint128[] memory amountsIn = new uint128[](outTokens.length); + for (uint128 i = 0; i < outTokens.length; ++i) { ERC20 token = ERC20(outTokens[i]); - amountsIn[i] = token.balanceOf(address(this)); + amountsIn[i] = uint128(token.balanceOf(address(this))); } poolOut.joinPoolExactTokensInForBPTOut(minAmountOut, amountsIn, true, msg.sender); } diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol index a9c5f81..2ea9ac2 100644 --- a/contracts/v2/BalancerPool.sol +++ b/contracts/v2/BalancerPool.sol @@ -22,7 +22,7 @@ contract BalancerPool { Vault vault, bytes32 poolId, uint256 swapFee, - uint256[] calldata amounts, + uint128[] calldata amounts, uint256[] calldata weights ) external { _vault = vault; @@ -68,9 +68,9 @@ contract BalancerPool { require(maxAmountsIn.length == tokens.length, "Tokens and amounts length mismatch"); - uint256[] memory amountsIn = new uint256[](tokens.length); + uint128[] memory amountsIn = new uint128[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { - amountsIn[i] = uint256(balances[i] * ratio); + amountsIn[i] = uint128(balances[i] * ratio); require(amountsIn[i] <= maxAmountsIn[i], "ERR_LIMIT_IN"); } @@ -81,7 +81,7 @@ contract BalancerPool { function joinPoolExactTokensInForBPTOut( uint256 minBPTAmountOut, - uint256[] calldata amountsIn, + uint128[] calldata amountsIn, bool transferTokens, address beneficiary ) external returns (uint256 bptAmountOut) { @@ -105,7 +105,7 @@ contract BalancerPool { function _exactTokensInForBPTOut( uint256[] memory balances, uint256[] memory normalizedWeights, - uint256[] memory amountsIn, + uint128[] memory amountsIn, uint256 bptTotalSupply, uint256 swapFee ) internal pure returns (uint256) { diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol index 1b166c6..0206bbb 100644 --- a/contracts/v2/Vault.sol +++ b/contracts/v2/Vault.sol @@ -37,14 +37,14 @@ contract Vault { bytes32 poolId, address from, IERC20[] calldata tokens, - uint256[] calldata amounts, + uint128[] calldata amounts, bool withdrawFromUserBalance ) external { require(tokens.length == amounts.length, "Tokens and total amounts length mismatch"); for (uint256 i = 0; i < tokens.length; ++i) { if (amounts[i] > 0) { - uint256 toReceive = amounts[i]; + uint256 toReceive = uint256(amounts[i]); uint256 received = _pullTokens(tokens[i], from, toReceive); require(received == toReceive, "Not enough tokens received"); _increasePoolCash(poolId, tokens[i], amounts[i]); @@ -69,7 +69,7 @@ contract Vault { function _increasePoolCash( bytes32 poolId, IERC20 token, - uint256 amount + uint128 amount ) internal { uint256 currentBalance = _poolTokenBalance[poolId][token]; _poolTokenBalance[poolId][token] = currentBalance + amount; From 9bc25a4488880273998a93fcfc17633eed54ff02 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 3 Feb 2021 16:01:16 +0300 Subject: [PATCH 15/29] Remove redundant whitespace --- contracts/BActions.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 3ba8791..5badfad 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -392,14 +392,14 @@ contract BActions { } // --- Internals --- - + function _safeApprove(ERC20 token, address spender, uint amount) internal { if (token.allowance(address(this), spender) > 0) { token.approve(spender, 0); } token.approve(spender, amount); } - + function _join( AbstractPool pool, address[] memory tokens, From a9f04afd362363fa128f725b95b4ba7f13135a57 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 3 Feb 2021 16:21:34 +0300 Subject: [PATCH 16/29] Switch to latest v2 interface --- contracts/BActions.sol | 58 ++++++---- contracts/v2/BalancerPool.sol | 202 +++++++++++++++++++++++----------- contracts/v2/Vault.sol | 44 +++++--- test/migration.js | 20 ++-- 4 files changed, 218 insertions(+), 106 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 5badfad..58e125e 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -48,20 +48,28 @@ abstract contract BPool is AbstractPool { function getBalance(address token) external view virtual returns (uint); } +abstract contract BFactory { + function newBPool() external virtual returns (BPool); +} + abstract contract BalancerPool is ERC20 { - function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn, bool transferTokens, address beneficiary) external virtual; - function joinPoolExactTokensInForBPTOut( - uint256 minBPTAmountOut, uint128[] calldata amountsIn, bool transferTokens, address beneficiary - ) external virtual returns (uint256 bptAmountOut); function getPoolId() external view virtual returns (bytes32); -} -abstract contract Vault { - function getPoolTokens(bytes32 poolId) external virtual returns (address[] memory); + enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT } } -abstract contract BFactory { - function newBPool() external virtual returns (BPool); +abstract contract Vault { + function joinPool( + bytes32 poolId, + address recipient, + address[] memory tokens, + uint[] memory maxAmountsIn, + bool fromInternalBalance, + bytes memory userData + ) external virtual; + function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory); + function getPoolTokenBalances(bytes32 poolId, address[] memory tokens) + external view virtual returns (uint[] memory); } abstract contract ConfigurableRightsPool is AbstractPool { @@ -367,28 +375,36 @@ contract BActions { Vault vault, BPool poolIn, uint poolInAmount, - uint[] calldata minAmountsOut, + uint[] calldata tokenOutAmountsMin, BalancerPool poolOut, - uint256 minAmountOut + uint poolOutAmountMin ) external { - address[] memory inTokens = poolIn.getFinalTokens(); + address[] memory tokens = poolIn.getFinalTokens(); address[] memory outTokens = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool - poolIn.exitPool(poolInAmount, minAmountsOut); + poolIn.exitPool(poolInAmount, tokenOutAmountsMin); // Approve each token to v2 vault - for (uint i = 0; i < inTokens.length; i++) { - ERC20 token = ERC20(inTokens[i]); - _safeApprove(token, address(vault), uint(-1)); + for (uint i = 0; i < tokens.length; i++) { + _safeApprove(ERC20(tokens[i]), address(vault), uint(-1)); } // Join v2 pool and transfer v2 BPTs to user - uint128[] memory amountsIn = new uint128[](outTokens.length); - for (uint128 i = 0; i < outTokens.length; ++i) { - ERC20 token = ERC20(outTokens[i]); - amountsIn[i] = uint128(token.balanceOf(address(this))); + uint[] memory tokenInAmounts = new uint[](outTokens.length); + for (uint i = 0; i < outTokens.length; ++i) { + tokenInAmounts[i] = ERC20(outTokens[i]).balanceOf(address(this)); } - poolOut.joinPoolExactTokensInForBPTOut(minAmountOut, amountsIn, true, msg.sender); + vault.joinPool( + poolOut.getPoolId(), + msg.sender, + outTokens, + tokenInAmounts, + false, + abi.encode( + BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + poolOutAmountMin + ) + ); } // --- Internals --- diff --git a/contracts/v2/BalancerPool.sol b/contracts/v2/BalancerPool.sol index 2ea9ac2..46258d9 100644 --- a/contracts/v2/BalancerPool.sol +++ b/contracts/v2/BalancerPool.sol @@ -7,6 +7,8 @@ import './LogExpMath.sol'; // BalancerPool mock; does not represent the actual BalancerPool design contract BalancerPool { Vault _vault; + string _name; + string _symbol; bytes32 _poolId; uint256 _swapFee; @@ -18,121 +20,195 @@ contract BalancerPool { uint128 internal constant ONE = 10**18; - function init( + function create( Vault vault, bytes32 poolId, - uint256 swapFee, - uint128[] calldata amounts, - uint256[] calldata weights + string memory name, + string memory symbol, + IERC20[] memory tokens, + uint256[] memory weights, + uint256 swapFee ) external { _vault = vault; _poolId = poolId; - _totalSupply = 100 ether; - - IERC20[] memory tokens = _vault.getPoolTokens(_poolId); - _vault.addLiquidity(_poolId, msg.sender, tokens, amounts, false); - _balance[msg.sender] = 100 ether; + _name = name; + _symbol = symbol; _swapFee = swapFee; - for (uint256 i = 0; i < weights.length; i++) { - _weight[address(tokens[i])] = weights[i]; - _totalWeight += weights[i]; + + for (uint8 i = 0; i < weights.length; i++) { + _totalWeight = _totalWeight + weights[i]; } } - function totalSupply() public view returns (uint256) { - return _totalSupply; - } + enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT } - function balanceOf(address owner) public view returns (uint256){ - return _balance[owner]; - } + function onJoinPool( + bytes32 poolId, + address, // sender - potential whitelisting + address recipient, + uint256[] memory currentBalances, + uint256[] memory maxAmountsIn, + uint256 protocolFeePercentage, + bytes memory userData + ) external returns (uint256[] memory, uint256[] memory) { + require(msg.sender == address(_vault), "ERR_CALLER_NOT_VAULT"); + require(poolId == _poolId, "INVALID_POOL_ID"); + + IERC20[] memory tokens = _vault.getPoolTokens(poolId); + uint256[] memory normalizedWeights = new uint256[](tokens.length); + for (uint8 i = 0; i < tokens.length; i++) { + normalizedWeights[i] = _weight[address(tokens[i])] / _totalWeight; + } - function getPoolId() external view returns (bytes32) { - return _poolId; + // The Vault guarantees currentBalances and maxAmountsIn have the same length + + JoinKind kind = abi.decode(userData, (JoinKind)); + + if (kind == JoinKind.INIT) { + //Max amounts in are equal to amounts in. + return _joinInitial(normalizedWeights, recipient, maxAmountsIn); + } else { + // JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT + //Max amounts in are equal to exact amounts in. + (, uint256 minimumBPT) = abi.decode(userData, (JoinKind, uint256)); + return + _joinExactTokensInForBPTOut( + normalizedWeights, + currentBalances, + recipient, + maxAmountsIn, + minimumBPT, + protocolFeePercentage + ); + } } - function joinPool( - uint256 poolAmountOut, - uint256[] calldata maxAmountsIn, - bool transferTokens, - address beneficiary - ) external { - IERC20[] memory tokens = _vault.getPoolTokens(_poolId); - - uint256[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); + function _joinInitial( + uint256[] memory normalizedWeights, + address recipient, + uint256[] memory amountsIn + ) private returns (uint256[] memory, uint256[] memory) { + require(totalSupply() == 0, "ERR_ALREADY_INITIALIZED"); - uint256 poolTotal = totalSupply(); - uint256 ratio = uint256(poolAmountOut / poolTotal); - require(ratio != 0, "ERR_MATH_APPROX"); + // _lastInvariant should also be zero + uint256 invariantAfterJoin = _invariant(normalizedWeights, amountsIn); - require(maxAmountsIn.length == tokens.length, "Tokens and amounts length mismatch"); + _mintPoolTokens(recipient, invariantAfterJoin); - uint128[] memory amountsIn = new uint128[](tokens.length); - for (uint256 i = 0; i < tokens.length; i++) { - amountsIn[i] = uint128(balances[i] * ratio); - require(amountsIn[i] <= maxAmountsIn[i], "ERR_LIMIT_IN"); - } + IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + uint256[] memory dueProtocolFeeAmounts = new uint256[](tokens.length); // All zeroes + return (amountsIn, dueProtocolFeeAmounts); + } - _vault.addLiquidity(_poolId, msg.sender, tokens, amountsIn, !transferTokens); + function _joinExactTokensInForBPTOut( + uint256[] memory normalizedWeights, + uint256[] memory currentBalances, + address recipient, + uint256[] memory amountsIn, + uint256 minimumBPT, + uint256 protocolFeePercentage + ) private returns (uint256[] memory, uint256[] memory) { + uint256 currentBPT = totalSupply(); + require(currentBPT > 0, "ERR_UNINITIALIZED"); - _mintPoolTokens(beneficiary, poolAmountOut); - } + uint256 bptAmountOut = _exactTokensInForBPTOut( + currentBalances, + normalizedWeights, + amountsIn, + currentBPT, + _swapFee + ); - function joinPoolExactTokensInForBPTOut( - uint256 minBPTAmountOut, - uint128[] calldata amountsIn, - bool transferTokens, - address beneficiary - ) external returns (uint256 bptAmountOut) { - IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + require(bptAmountOut >= minimumBPT, "ERR_BPT_OUT_MIN_AMOUNT"); - uint256[] memory balances = _vault.getPoolTokenBalances(_poolId, tokens); + _mintPoolTokens(recipient, bptAmountOut); - uint256[] memory normalizedWeights = new uint256[](tokens.length); - for (uint256 i = 0; i < tokens.length; ++i) { - normalizedWeights[i] = _weight[address(tokens[i])] / _totalWeight; + IERC20[] memory tokens = _vault.getPoolTokens(_poolId); + for (uint8 i = 0; i < tokens.length; i++) { + currentBalances[i] = currentBalances[i] + amountsIn[i]; } - bptAmountOut = _exactTokensInForBPTOut(balances, normalizedWeights, amountsIn, totalSupply(), _swapFee); - require(bptAmountOut >= minBPTAmountOut, "ERR_BPT_OUT_MIN_AMOUNT"); - - _vault.addLiquidity(_poolId, msg.sender, tokens, amountsIn, !transferTokens); + uint256[] memory dueProtocolFeeAmounts = new uint256[](tokens.length); // All zeroes + return (amountsIn, dueProtocolFeeAmounts); + } - _mintPoolTokens(beneficiary, bptAmountOut); + // Computes the invariant given the current balances and normalized weights. + function _invariant(uint256[] memory normalizedWeights, uint256[] memory balances) + internal + pure + returns (uint256 invariant) + { + /********************************************************************************************** + // invariant _____ // + // wi = weight index i | | wi // + // bi = balance index i | | bi ^ = i // + // i = invariant // + **********************************************************************************************/ + require(normalizedWeights.length == balances.length, "ERR_BALANCES_LENGTH"); + + invariant = ONE; + for (uint8 i = 0; i < normalizedWeights.length; i++) { + invariant = invariant * (LogExpMath.pow(balances[i], normalizedWeights[i])); + } } function _exactTokensInForBPTOut( uint256[] memory balances, uint256[] memory normalizedWeights, - uint128[] memory amountsIn, + uint256[] memory amountsIn, uint256 bptTotalSupply, uint256 swapFee ) internal pure returns (uint256) { + // First loop to calculate the weighted balance ratio + // The increment `amountIn` represents for each token, as a quotient of new and current balances, + // not accounting swap fees uint256[] memory tokenBalanceRatiosWithoutFee = new uint256[](amountsIn.length); + // The weighted sum of token balance rations sans fee uint256 weightedBalanceRatio = 0; for (uint256 i = 0; i < balances.length; i++) { tokenBalanceRatiosWithoutFee[i] = (balances[i] + amountsIn[i]) / balances[i]; - weightedBalanceRatio = weightedBalanceRatio + (tokenBalanceRatiosWithoutFee[i] * normalizedWeights[i]); + weightedBalanceRatio = weightedBalanceRatio + tokenBalanceRatiosWithoutFee[i] * normalizedWeights[i]; } + + //Second loop to calculate new amounts in taking into account the fee on the % excess + // The growth of the invariant caused by the join, as a quotient of the new value and the current one uint256 invariantRatio = ONE; for (uint256 i = 0; i < balances.length; i++) { + // Percentage of the amount supplied that will be swapped for other tokens in the pool uint256 tokenBalancePercentageExcess; + // Some tokens might have amounts supplied in excess of a 'balanced' join: these are identified if + // the token's balance ratio sans fee is larger than the weighted balance ratio, and swap fees charged + // on the amount to swap if (weightedBalanceRatio >= tokenBalanceRatiosWithoutFee[i]) { tokenBalancePercentageExcess = 0; } else { - tokenBalancePercentageExcess = ((tokenBalanceRatiosWithoutFee[i] - weightedBalanceRatio) - / tokenBalanceRatiosWithoutFee[i]) - ONE; + tokenBalancePercentageExcess = (tokenBalanceRatiosWithoutFee[i] - weightedBalanceRatio) / + tokenBalanceRatiosWithoutFee[i] - ONE; } - uint256 amountInAfterFee = amountsIn[i] * (ONE - (swapFee * tokenBalancePercentageExcess)); - uint256 tokenBalanceRatio = ONE + (amountInAfterFee / balances[i]); + uint256 amountInAfterFee = amountsIn[i] * (ONE - swapFee * tokenBalancePercentageExcess); + + uint256 tokenBalanceRatio = ONE + amountInAfterFee / balances[i]; + invariantRatio = invariantRatio * LogExpMath.pow(tokenBalanceRatio, normalizedWeights[i]); } return bptTotalSupply * (invariantRatio - ONE); } + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + function balanceOf(address owner) public view returns (uint256){ + return _balance[owner]; + } + + function getPoolId() external view returns (bytes32) { + return _poolId; + } + function _mintPoolTokens(address recipient, uint256 amount) internal { _balance[address(this)] = _balance[address(this)] + amount; _totalSupply = _totalSupply + amount; diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol index 0206bbb..fb69091 100644 --- a/contracts/v2/Vault.sol +++ b/contracts/v2/Vault.sol @@ -2,16 +2,19 @@ pragma solidity 0.6.12; import '../common/IERC20.sol'; +import './BalancerPool.sol'; // Vault mock; does not represent the actual Vault design contract Vault { uint256 _poolCount; + mapping(bytes32 => address) internal _poolAddresses; mapping(bytes32 => IERC20[]) internal _poolTokens; mapping(bytes32 => mapping(IERC20 => uint256)) internal _poolTokenBalance; - function newPool(IERC20[] memory tokens) external returns (bytes32) { + function newPool(IERC20[] memory tokens, address poolAddress) external returns (bytes32) { bytes32 poolId = bytes32(_poolCount); _poolTokens[poolId] = tokens; + _poolAddresses[poolId] = poolAddress; _poolCount = _poolCount + 1; return poolId; } @@ -20,8 +23,8 @@ contract Vault { return _poolTokens[poolId]; } - function getPoolTokenBalances(bytes32 poolId, IERC20[] calldata tokens) - external + function getPoolTokenBalances(bytes32 poolId, IERC20[] memory tokens) + public view returns (uint256[] memory) { @@ -33,21 +36,32 @@ contract Vault { return balances; } - function addLiquidity( + function joinPool( bytes32 poolId, - address from, - IERC20[] calldata tokens, - uint128[] calldata amounts, - bool withdrawFromUserBalance + address recipient, + IERC20[] memory tokens, + uint256[] memory maxAmountsIn, + bool fromInternalBalance, + bytes memory userData ) external { - require(tokens.length == amounts.length, "Tokens and total amounts length mismatch"); + require(tokens.length == maxAmountsIn.length, "ERR_TOKENS_AMOUNTS_LENGTH_MISMATCH"); + + uint256[] memory currentBalances = getPoolTokenBalances(poolId, tokens); + + address pool = _poolAddresses[poolId]; + (uint256[] memory amountsIn,) = BalancerPool(pool).onJoinPool( + poolId, + msg.sender, + recipient, + currentBalances, + maxAmountsIn, + 0, + userData + ); for (uint256 i = 0; i < tokens.length; ++i) { - if (amounts[i] > 0) { - uint256 toReceive = uint256(amounts[i]); - uint256 received = _pullTokens(tokens[i], from, toReceive); - require(received == toReceive, "Not enough tokens received"); - _increasePoolCash(poolId, tokens[i], amounts[i]); + if (amountsIn[i] > 0) { + _increasePoolCash(poolId, tokens[i], amountsIn[i]); } } } @@ -69,7 +83,7 @@ contract Vault { function _increasePoolCash( bytes32 poolId, IERC20 token, - uint128 amount + uint256 amount ) internal { uint256 currentBalance = _poolTokenBalance[poolId][token]; _poolTokenBalance[poolId][token] = currentBalance + amount; diff --git a/test/migration.js b/test/migration.js index c496d13..7db27cf 100644 --- a/test/migration.js +++ b/test/migration.js @@ -113,22 +113,28 @@ contract('BActions', async (accounts) => { } async function setupV2() { + const balancerPool = await BalancerPool.deployed(); + POOL_V2 = balancerPool.address; + const vault = await Vault.deployed(); VAULT = vault.address; - vault.newPool([DAI, MKR, WETH]); - const balancerPool = await BalancerPool.deployed(); - POOL_V2 = balancerPool.address; - // Approve each token to v2 vault await dai.approve(VAULT, MAX); await mkr.approve(VAULT, MAX); await zrx.approve(VAULT, MAX); await weth.approve(VAULT, MAX); + const id = '0x0'; + const name = 'Balancer Pool'; + const symbol = 'BPT'; + const tokens = [DAI, MKR, WETH]; const balances = [toWei('200'), toWei('0.5'), toWei('1')]; const weights = [toWei('5'), toWei('5'), toWei('5')]; const swapFee = toWei('0.03'); - balancerPool.init(VAULT, '0x0', swapFee, balances, weights); + const initJoinData = '0x0000000000000000000000000000000000000000000000000000000000000000'; + vault.newPool(tokens, POOL_V2); + balancerPool.create(VAULT, id, name, symbol, tokens, weights, swapFee); + vault.joinPool(id, admin, tokens, balances, false, initJoinData); } it('Simple migration', async () => { @@ -139,7 +145,7 @@ contract('BActions', async (accounts) => { const params = [ VAULT, POOL_V1, - toWei('100'), + toWei('10'), [0, 0, 0], POOL_V2, 0, @@ -165,7 +171,7 @@ contract('BActions', async (accounts) => { const endV1Balance = await bpt.balanceOf(admin); // const endV2Balance = await v2Pool.balanceOf(admin); - assert.equal(fromWei(startV1Balance.sub(endV1Balance)), '100'); + assert.equal(fromWei(startV1Balance.sub(endV1Balance)), '10'); // assert.equal(fromWei(endV2Balance.sub(startV2Balance)), '200'); }); }); From c8e55eed7c555bd98ac5300e9203ab914bb008d7 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 3 Feb 2021 16:23:54 +0300 Subject: [PATCH 17/29] Add proportional migration --- contracts/BActions.sol | 55 +++++++++++++++++++++++++++++++++++++++++- test/migration.js | 42 ++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 58e125e..5f2827d 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -371,7 +371,60 @@ contract BActions { // --- Migration --- - function migrate( + function migrateProportionally( + Vault vault, + BPool poolIn, + uint poolInAmount, + uint[] calldata tokenOutAmountsMin, + BalancerPool poolOut, + uint poolOutAmountMin + ) external { + address[] memory tokens = poolIn.getFinalTokens(); + address[] memory outTokens = vault.getPoolTokens(poolOut.getPoolId()); + // Transfer v1 BPTs to proxy + poolIn.transferFrom(msg.sender, address(this), poolInAmount); + // Exit v1 pool + poolIn.exitPool(poolInAmount, tokenOutAmountsMin); + // Approve each token to v2 vault + for (uint i = 0; i < tokens.length; i++) { + _safeApprove(ERC20(tokens[i]), address(vault), uint(-1)); + } + // Calculate amounts for even join + // 1) find the lowest UserBalance-to-PoolBalance ratio + // 2) multiply by this ratio to get in amounts + uint lowestRatio = uint(-1); + uint[] memory tokenInAmounts = vault.getPoolTokenBalances(poolOut.getPoolId(), outTokens); + for (uint i = 0; i < outTokens.length; ++i) { + uint ratio = 1 ether * ERC20(outTokens[i]).balanceOf(address(this)) / tokenInAmounts[i]; + if (ratio < lowestRatio) { + lowestRatio = ratio; + } + } + for (uint i = 0; i < outTokens.length; ++i) { + tokenInAmounts[i] = tokenInAmounts[i] * lowestRatio / 1 ether; + } + // Join v2 pool and transfer v2 BPTs to user + vault.joinPool( + poolOut.getPoolId(), + msg.sender, + tokens, + tokenInAmounts, + false, + abi.encode( + BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + poolOutAmountMin + ) + ); + // Send dust back + for (uint i = 0; i < tokens.length; i++) { + ERC20 token = ERC20(tokens[i]); + if (token.balanceOf(address(this)) > 0) { + require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); + } + } + } + + function migrateAll( Vault vault, BPool poolIn, uint poolInAmount, diff --git a/test/migration.js b/test/migration.js index 7db27cf..fcc158e 100644 --- a/test/migration.js +++ b/test/migration.js @@ -137,7 +137,7 @@ contract('BActions', async (accounts) => { vault.joinPool(id, admin, tokens, balances, false, initJoinData); } - it('Simple migration', async () => { + it('Proportional migration', async () => { // Approve v1 BPT to proxy const bpt = await BPool.at(POOL_V1); await bpt.approve(USER_PROXY, MAX); @@ -156,7 +156,45 @@ contract('BActions', async (accounts) => { // const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 const functionSig = web3.eth.abi.encodeFunctionSignature( - 'migrate(address,address,uint256,uint256[],address,uint256)', + 'migrateProportionally(address,address,uint256,uint256[],address,uint256)', + ); + const functionData = web3.eth.abi.encodeParameters( + ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256'], + params, + ); + + const argumentData = functionData.substring(2); + const inputData = `${functionSig}${argumentData}`; + + await userProxy.methods['execute(address,bytes)'](BACTIONS, inputData); + + const endV1Balance = await bpt.balanceOf(admin); + // const endV2Balance = await v2Pool.balanceOf(admin); + + assert.equal(fromWei(startV1Balance.sub(endV1Balance)), '10'); + // assert.equal(fromWei(endV2Balance.sub(startV2Balance)), '200'); + }); + + it('Full migration', async () => { + // Approve v1 BPT to proxy + const bpt = await BPool.at(POOL_V1); + await bpt.approve(USER_PROXY, MAX); + + const params = [ + VAULT, + POOL_V1, + toWei('10'), + [0, 0, 0], + POOL_V2, + 0, + ]; + + const startV1Balance = await bpt.balanceOf(admin); // should go 100 -> 0 + // const v2Pool = await BalancerPool.at(POOL_V2); + // const startV2Balance = await v2Pool.balanceOf(admin); // should go 100 -> 300 + + const functionSig = web3.eth.abi.encodeFunctionSignature( + 'migrateAll(address,address,uint256,uint256[],address,uint256)', ); const functionData = web3.eth.abi.encodeParameters( ['address', 'address', 'uint256', 'uint256[]', 'address', 'uint256'], From 7ee60c117e568ced67836e4b1a68d8b13f07effb Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 3 Feb 2021 18:17:30 +0300 Subject: [PATCH 18/29] Fix v1 tokens used instead of v2 --- contracts/BActions.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 5f2827d..df77d19 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -407,7 +407,7 @@ contract BActions { vault.joinPool( poolOut.getPoolId(), msg.sender, - tokens, + outTokens, tokenInAmounts, false, abi.encode( @@ -415,7 +415,7 @@ contract BActions { poolOutAmountMin ) ); - // Send dust back + // Send "change" back for (uint i = 0; i < tokens.length; i++) { ERC20 token = ERC20(tokens[i]); if (token.balanceOf(address(this)) > 0) { From 11d5bbbef77e9bef342ac6032975b5a0baee0a45 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Wed, 10 Feb 2021 19:54:58 +0300 Subject: [PATCH 19/29] Send missing tokens back during full migration --- contracts/BActions.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index df77d19..021bd65 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -458,6 +458,13 @@ contract BActions { poolOutAmountMin ) ); + // Send missing tokens back + for (uint i = 0; i < tokens.length; i++) { + ERC20 token = ERC20(tokens[i]); + if (token.balanceOf(address(this)) > 0) { + require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); + } + } } // --- Internals --- From 7fb638f4e655a41c59c310ebc3bfc669625bd21f Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Fri, 12 Feb 2021 16:56:22 +0300 Subject: [PATCH 20/29] Change Vault's pool token interface --- contracts/BActions.sol | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 021bd65..6dd0dfa 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -67,9 +67,7 @@ abstract contract Vault { bool fromInternalBalance, bytes memory userData ) external virtual; - function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory); - function getPoolTokenBalances(bytes32 poolId, address[] memory tokens) - external view virtual returns (uint[] memory); + function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory, uint[] memory); } abstract contract ConfigurableRightsPool is AbstractPool { @@ -380,7 +378,8 @@ contract BActions { uint poolOutAmountMin ) external { address[] memory tokens = poolIn.getFinalTokens(); - address[] memory outTokens = vault.getPoolTokens(poolOut.getPoolId()); + (address[] memory outTokens, uint[] memory tokenInAmounts) = + vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool @@ -393,7 +392,6 @@ contract BActions { // 1) find the lowest UserBalance-to-PoolBalance ratio // 2) multiply by this ratio to get in amounts uint lowestRatio = uint(-1); - uint[] memory tokenInAmounts = vault.getPoolTokenBalances(poolOut.getPoolId(), outTokens); for (uint i = 0; i < outTokens.length; ++i) { uint ratio = 1 ether * ERC20(outTokens[i]).balanceOf(address(this)) / tokenInAmounts[i]; if (ratio < lowestRatio) { @@ -433,7 +431,7 @@ contract BActions { uint poolOutAmountMin ) external { address[] memory tokens = poolIn.getFinalTokens(); - address[] memory outTokens = vault.getPoolTokens(poolOut.getPoolId()); + (address[] memory outTokens,) = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool From c71c6b230376c41706e4db6a0c6ec16906bde699 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 16 Feb 2021 15:53:31 +0300 Subject: [PATCH 21/29] Fix "exact token in join" param encoding --- contracts/BActions.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 6dd0dfa..2e03a51 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -410,6 +410,7 @@ contract BActions { false, abi.encode( BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + tokenInAmounts, poolOutAmountMin ) ); @@ -453,6 +454,7 @@ contract BActions { false, abi.encode( BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + tokenInAmounts, poolOutAmountMin ) ); From 88320a28ee39d0ec7a5463a7fc4f3520853fafca Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Fri, 2 Apr 2021 18:59:39 +0300 Subject: [PATCH 22/29] Update Vault joinPool interface --- contracts/BActions.sol | 5 ++++- contracts/v2/Vault.sol | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 2e03a51..0949f53 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -61,8 +61,9 @@ abstract contract BalancerPool is ERC20 { abstract contract Vault { function joinPool( bytes32 poolId, + address sender, address recipient, - address[] memory tokens, + address[] memory assets, uint[] memory maxAmountsIn, bool fromInternalBalance, bytes memory userData @@ -404,6 +405,7 @@ contract BActions { // Join v2 pool and transfer v2 BPTs to user vault.joinPool( poolOut.getPoolId(), + address(this), msg.sender, outTokens, tokenInAmounts, @@ -448,6 +450,7 @@ contract BActions { } vault.joinPool( poolOut.getPoolId(), + address(this), msg.sender, outTokens, tokenInAmounts, diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol index fb69091..4406349 100644 --- a/contracts/v2/Vault.sol +++ b/contracts/v2/Vault.sol @@ -38,6 +38,7 @@ contract Vault { function joinPool( bytes32 poolId, + address sender, address recipient, IERC20[] memory tokens, uint256[] memory maxAmountsIn, @@ -51,7 +52,7 @@ contract Vault { address pool = _poolAddresses[poolId]; (uint256[] memory amountsIn,) = BalancerPool(pool).onJoinPool( poolId, - msg.sender, + sender, recipient, currentBalances, maxAmountsIn, From 139dcb252220dac4afeaa8dd32a32adeefc503b2 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 15 Apr 2021 22:23:01 +0300 Subject: [PATCH 23/29] Update join pool vault interface --- contracts/BActions.sol | 49 ++++++++++++++++++++++-------------------- contracts/v2/Vault.sol | 26 +++++++++++++--------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 0949f53..96409be 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -59,16 +59,20 @@ abstract contract BalancerPool is ERC20 { } abstract contract Vault { + struct JoinPoolRequest { + address[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + function joinPool( bytes32 poolId, address sender, address recipient, - address[] memory assets, - uint[] memory maxAmountsIn, - bool fromInternalBalance, - bytes memory userData + JoinPoolRequest calldata request ) external virtual; - function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory, uint[] memory); + function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory, uint[] memory, uint256); } abstract contract ConfigurableRightsPool is AbstractPool { @@ -379,7 +383,7 @@ contract BActions { uint poolOutAmountMin ) external { address[] memory tokens = poolIn.getFinalTokens(); - (address[] memory outTokens, uint[] memory tokenInAmounts) = + (address[] memory outTokens, uint[] memory tokenInAmounts, uint256 maxBlockNumber) = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); @@ -403,18 +407,17 @@ contract BActions { tokenInAmounts[i] = tokenInAmounts[i] * lowestRatio / 1 ether; } // Join v2 pool and transfer v2 BPTs to user + bytes memory userData = abi.encode( + BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + tokenInAmounts, + poolOutAmountMin + ); + Vault.JoinPoolRequest memory request = Vault.JoinPoolRequest(outTokens, tokenInAmounts, userData, false); vault.joinPool( poolOut.getPoolId(), address(this), msg.sender, - outTokens, - tokenInAmounts, - false, - abi.encode( - BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, - tokenInAmounts, - poolOutAmountMin - ) + request ); // Send "change" back for (uint i = 0; i < tokens.length; i++) { @@ -434,7 +437,7 @@ contract BActions { uint poolOutAmountMin ) external { address[] memory tokens = poolIn.getFinalTokens(); - (address[] memory outTokens,) = vault.getPoolTokens(poolOut.getPoolId()); + (address[] memory outTokens,,) = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool @@ -448,18 +451,18 @@ contract BActions { for (uint i = 0; i < outTokens.length; ++i) { tokenInAmounts[i] = ERC20(outTokens[i]).balanceOf(address(this)); } + + bytes memory userData = abi.encode( + BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + tokenInAmounts, + poolOutAmountMin + ); + Vault.JoinPoolRequest memory request = Vault.JoinPoolRequest(outTokens, tokenInAmounts, userData, false); vault.joinPool( poolOut.getPoolId(), address(this), msg.sender, - outTokens, - tokenInAmounts, - false, - abi.encode( - BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, - tokenInAmounts, - poolOutAmountMin - ) + request ); // Send missing tokens back for (uint i = 0; i < tokens.length; i++) { diff --git a/contracts/v2/Vault.sol b/contracts/v2/Vault.sol index 4406349..094b575 100644 --- a/contracts/v2/Vault.sol +++ b/contracts/v2/Vault.sol @@ -1,11 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + import '../common/IERC20.sol'; import './BalancerPool.sol'; // Vault mock; does not represent the actual Vault design contract Vault { + struct JoinPoolRequest { + IERC20[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + uint256 _poolCount; mapping(bytes32 => address) internal _poolAddresses; mapping(bytes32 => IERC20[]) internal _poolTokens; @@ -40,14 +49,11 @@ contract Vault { bytes32 poolId, address sender, address recipient, - IERC20[] memory tokens, - uint256[] memory maxAmountsIn, - bool fromInternalBalance, - bytes memory userData + JoinPoolRequest memory request ) external { - require(tokens.length == maxAmountsIn.length, "ERR_TOKENS_AMOUNTS_LENGTH_MISMATCH"); + require(request.assets.length == request.maxAmountsIn.length, "ERR_TOKENS_AMOUNTS_LENGTH_MISMATCH"); - uint256[] memory currentBalances = getPoolTokenBalances(poolId, tokens); + uint256[] memory currentBalances = getPoolTokenBalances(poolId, request.assets); address pool = _poolAddresses[poolId]; (uint256[] memory amountsIn,) = BalancerPool(pool).onJoinPool( @@ -55,14 +61,14 @@ contract Vault { sender, recipient, currentBalances, - maxAmountsIn, + request.maxAmountsIn, 0, - userData + request.userData ); - for (uint256 i = 0; i < tokens.length; ++i) { + for (uint256 i = 0; i < request.assets.length; ++i) { if (amountsIn[i] > 0) { - _increasePoolCash(poolId, tokens[i], amountsIn[i]); + _increasePoolCash(poolId, request.assets[i], amountsIn[i]); } } } From 8c30c7153ff328785356202194aa8fcf05e8e636 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Thu, 22 Apr 2021 19:55:24 +0300 Subject: [PATCH 24/29] Remove unused variable --- contracts/BActions.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 96409be..f5fef43 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -383,7 +383,7 @@ contract BActions { uint poolOutAmountMin ) external { address[] memory tokens = poolIn.getFinalTokens(); - (address[] memory outTokens, uint[] memory tokenInAmounts, uint256 maxBlockNumber) = + (address[] memory outTokens, uint[] memory tokenInAmounts,) = vault.getPoolTokens(poolOut.getPoolId()); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); From 24216bfc800a565e28288de8cd676586719cb687 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Tue, 27 Apr 2021 00:10:46 +0300 Subject: [PATCH 25/29] Keep original amount for bottleneck to avoid dust --- contracts/BActions.sol | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index f5fef43..101c4a6 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -397,14 +397,21 @@ contract BActions { // 1) find the lowest UserBalance-to-PoolBalance ratio // 2) multiply by this ratio to get in amounts uint lowestRatio = uint(-1); + uint lowestRatioToken = 0; for (uint i = 0; i < outTokens.length; ++i) { uint ratio = 1 ether * ERC20(outTokens[i]).balanceOf(address(this)) / tokenInAmounts[i]; if (ratio < lowestRatio) { lowestRatio = ratio; + lowestRatioToken = i; } } for (uint i = 0; i < outTokens.length; ++i) { - tokenInAmounts[i] = tokenInAmounts[i] * lowestRatio / 1 ether; + // Keep original amount for "bottleneck" token to avoid dust + if (lowestRatioToken == i) { + tokenInAmounts[i] = ERC20(outTokens[i]).balanceOf(address(this)); + } else { + tokenInAmounts[i] = tokenInAmounts[i] * lowestRatio / 1 ether; + } } // Join v2 pool and transfer v2 BPTs to user bytes memory userData = abi.encode( From 60b5344cdeeeb3f2435046e1e07d26b895f60da9 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Mon, 10 May 2021 17:44:42 +0300 Subject: [PATCH 26/29] Don't try to send change back for full migration --- contracts/BActions.sol | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 101c4a6..41e5247 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -471,13 +471,6 @@ contract BActions { msg.sender, request ); - // Send missing tokens back - for (uint i = 0; i < tokens.length; i++) { - ERC20 token = ERC20(tokens[i]); - if (token.balanceOf(address(this)) > 0) { - require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); - } - } } // --- Internals --- From 22daf7ee3c3740ca9b52ae2599496b771dc55097 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Mon, 10 May 2021 18:07:59 +0300 Subject: [PATCH 27/29] Check that tokens on both pools are the same --- contracts/BActions.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 41e5247..ed7bb53 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -385,6 +385,8 @@ contract BActions { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens, uint[] memory tokenInAmounts,) = vault.getPoolTokens(poolOut.getPoolId()); + require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]), "ERR_TOKENS_MISMATCH"); + require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]), "ERR_TOKENS_MISMATCH"); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool @@ -445,6 +447,8 @@ contract BActions { ) external { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens,,) = vault.getPoolTokens(poolOut.getPoolId()); + require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]), "ERR_TOKENS_MISMATCH"); + require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]), "ERR_TOKENS_MISMATCH"); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool From 6f304ea2dbcfbbf4beb5b908cf32b0cba8cdc2bc Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Mon, 10 May 2021 18:37:10 +0300 Subject: [PATCH 28/29] Strip out some revert reasons to reduce bytecode --- contracts/BActions.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index ed7bb53..806c8f6 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -385,8 +385,8 @@ contract BActions { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens, uint[] memory tokenInAmounts,) = vault.getPoolTokens(poolOut.getPoolId()); - require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]), "ERR_TOKENS_MISMATCH"); - require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]), "ERR_TOKENS_MISMATCH"); + require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1])); + require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1])); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool @@ -447,8 +447,8 @@ contract BActions { ) external { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens,,) = vault.getPoolTokens(poolOut.getPoolId()); - require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]), "ERR_TOKENS_MISMATCH"); - require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]), "ERR_TOKENS_MISMATCH"); + require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1])); + require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1])); // Transfer v1 BPTs to proxy poolIn.transferFrom(msg.sender, address(this), poolInAmount); // Exit v1 pool From c5a6e9178f705141b5ac87d5fb7196fe98c3e8d3 Mon Sep 17 00:00:00 2001 From: Timur Badretdinov Date: Mon, 10 May 2021 20:14:37 +0300 Subject: [PATCH 29/29] Check that both pools have 2 tokens --- contracts/BActions.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/BActions.sol b/contracts/BActions.sol index 806c8f6..ccc8d05 100644 --- a/contracts/BActions.sol +++ b/contracts/BActions.sol @@ -385,6 +385,8 @@ contract BActions { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens, uint[] memory tokenInAmounts,) = vault.getPoolTokens(poolOut.getPoolId()); + require(tokens.length == 2); + require(outTokens.length == 2); require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1])); require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1])); // Transfer v1 BPTs to proxy @@ -447,6 +449,8 @@ contract BActions { ) external { address[] memory tokens = poolIn.getFinalTokens(); (address[] memory outTokens,,) = vault.getPoolTokens(poolOut.getPoolId()); + require(tokens.length == 2); + require(outTokens.length == 2); require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1])); require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1])); // Transfer v1 BPTs to proxy