├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .solcover.js ├── .solhint.json ├── LICENSE ├── README.md ├── contracts ├── ExchangeProxy.sol ├── Migrations.sol └── test │ ├── BColor.sol │ ├── BConst.sol │ ├── BFactory.sol │ ├── BMath.sol │ ├── BNum.sol │ ├── BPool.sol │ ├── BToken.sol │ ├── TToken.sol │ ├── TTokenFactory.sol │ └── WETH9.sol ├── lib └── calc_comparisons.js ├── migrations ├── 1_initial_migration.js ├── 2_deploy_proxy.js └── 3_test_factories.js ├── package.json ├── test └── swap.js ├── truffle-config.js └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | checkout_and_install: 4 | docker: 5 | - image: circleci/node:11 6 | working_directory: ~/balancer 7 | steps: 8 | - checkout 9 | - restore_cache: 10 | keys: 11 | - dependency-cache-{{ checksum "package.json" }} 12 | - run: 13 | name: Install Dependencies 14 | command: yarn install --quiet 15 | - save_cache: 16 | key: dependency-cache-{{ checksum "package.json" }} 17 | paths: 18 | - node_modules 19 | - save_cache: 20 | key: balancer-{{ .Environment.CIRCLE_SHA1 }} 21 | paths: 22 | - ~/balancer 23 | lint: 24 | docker: 25 | - image: circleci/node:11 26 | working_directory: ~/balancer 27 | steps: 28 | - restore_cache: 29 | key: balancer-{{ .Environment.CIRCLE_SHA1 }} 30 | - run: 31 | name: Lint contracts 32 | command: yarn lint:contracts 33 | - run: 34 | name: Lint tests 35 | command: yarn lint 36 | build: 37 | docker: 38 | - image: circleci/node:11 39 | - image: ethereum/solc:0.5.11 40 | working_directory: ~/balancer 41 | steps: 42 | - restore_cache: 43 | key: balancer-{{ .Environment.CIRCLE_SHA1 }} 44 | - run: 45 | name: Compile contracts 46 | command: yarn compile 47 | - save_cache: 48 | key: balancer-contracts-build-{{ .Environment.CIRCLE_SHA1 }} 49 | paths: 50 | - ~/balancer 51 | test: 52 | docker: 53 | - image: circleci/node:11 54 | - image: trufflesuite/ganache-cli 55 | command: ganache-cli -d -l 4294967295 --allowUnlimitedContractSize 56 | working_directory: ~/balancer 57 | steps: 58 | - restore_cache: 59 | key: balancer-contracts-build-{{ .Environment.CIRCLE_SHA1 }} 60 | - run: 61 | name: Run tests 62 | command: yarn test 63 | 64 | coverage: 65 | docker: 66 | - image: circleci/node:11 67 | working_directory: ~/balancer 68 | steps: 69 | - setup_remote_docker: 70 | docker_layer_caching: true 71 | - run: 72 | name: Fetch solc version 73 | command: docker pull ethereum/solc:0.5.11 74 | - restore_cache: 75 | key: balancer-contracts-build-{{ .Environment.CIRCLE_SHA1 }} 76 | - run: 77 | name: Coverage 78 | command: yarn coverage && cat coverage/lcov.info | ./node_modules/.bin/coveralls 79 | 80 | slither: 81 | docker: 82 | - image: trailofbits/eth-security-toolbox 83 | working_directory: ~/balancer 84 | steps: 85 | - checkout 86 | - run: 87 | name: Compile 88 | command: truffle compile 89 | - run: 90 | name: Slither 91 | command: slither . --filter-paths test --exclude=naming-convention,unused-state,solc-version 92 | 93 | workflows: 94 | version: 2 95 | build_and_test: 96 | jobs: 97 | - checkout_and_install 98 | - lint: 99 | requires: 100 | - checkout_and_install 101 | - build: 102 | requires: 103 | - checkout_and_install 104 | - test: 105 | requires: 106 | - build 107 | - coverage: 108 | requires: 109 | - build 110 | - slither: 111 | requires: 112 | - build 113 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/* 2 | coverage/* 3 | migrations/* -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb" 4 | ], 5 | "rules": { 6 | "indent": ["error", 4], 7 | "max-len": ["error", { "code": 120 }], 8 | "no-console": "off" 9 | }, 10 | "globals" : { 11 | "artifacts": false, 12 | "contract": false, 13 | "assert": false, 14 | "it": false, 15 | "before": false, 16 | "describe": false, 17 | "web3": false 18 | } 19 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | .idea/* 4 | .env 5 | coverage.json 6 | coverage/ 7 | yarn-error.log -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8555, 3 | skipFiles: [ 4 | 'Migrations.sol', 5 | 'test' 6 | ] 7 | }; -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "mark-callable-contracts": "off", 5 | "event-name-camelcase": "off", 6 | "const-name-snakecase": "off", 7 | "max-line-length": ["warn", 120], 8 | "indent": ["error", 4] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | 13 |

exchange proxy

14 | 15 | Forwarding proxy that allows users to batch execute swaps recommended by the SOR. 16 | 17 | 18 | ### Development 19 | 20 | This project follows the standard Truffle project structure. 21 | 22 | ``` 23 | yarn compile # build artifacts to `build/contracts` 24 | yarn testrpc # run ganache 25 | yarn test # run the tests 26 | ``` -------------------------------------------------------------------------------- /contracts/ExchangeProxy.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | pragma experimental ABIEncoderV2; 16 | 17 | 18 | contract PoolInterface { 19 | function swapExactAmountIn(address, uint, address, uint, uint) external returns (uint, uint); 20 | function swapExactAmountOut(address, uint, address, uint, uint) external returns (uint, uint); 21 | } 22 | 23 | contract TokenInterface { 24 | function balanceOf(address) public returns (uint); 25 | function allowance(address, address) public returns (uint); 26 | function approve(address, uint) public returns (bool); 27 | function transfer(address, uint) public returns (bool); 28 | function transferFrom(address, address, uint) public returns (bool); 29 | function deposit() public payable; 30 | function withdraw(uint) public; 31 | } 32 | 33 | contract ExchangeProxy { 34 | 35 | struct Swap { 36 | address pool; 37 | uint tokenInParam; // tokenInAmount / maxAmountIn / limitAmountIn 38 | uint tokenOutParam; // minAmountOut / tokenAmountOut / limitAmountOut 39 | uint maxPrice; 40 | } 41 | 42 | event LOG_CALL( 43 | bytes4 indexed sig, 44 | address indexed caller, 45 | bytes data 46 | ) anonymous; 47 | 48 | modifier _logs_() { 49 | emit LOG_CALL(msg.sig, msg.sender, msg.data); 50 | _; 51 | } 52 | 53 | modifier _lock_() { 54 | require(!_mutex, "ERR_REENTRY"); 55 | _mutex = true; 56 | _; 57 | _mutex = false; 58 | } 59 | 60 | bool private _mutex; 61 | TokenInterface weth; 62 | 63 | constructor(address _weth) public { 64 | weth = TokenInterface(_weth); 65 | } 66 | 67 | function add(uint a, uint b) internal pure returns (uint) { 68 | uint c = a + b; 69 | require(c >= a, "ERR_ADD_OVERFLOW"); 70 | return c; 71 | } 72 | 73 | function batchSwapExactIn( 74 | Swap[] memory swaps, 75 | address tokenIn, 76 | address tokenOut, 77 | uint totalAmountIn, 78 | uint minTotalAmountOut 79 | ) 80 | public 81 | _logs_ 82 | _lock_ 83 | returns (uint totalAmountOut) 84 | { 85 | TokenInterface TI = TokenInterface(tokenIn); 86 | TokenInterface TO = TokenInterface(tokenOut); 87 | require(TI.transferFrom(msg.sender, address(this), totalAmountIn), "ERR_TRANSFER_FAILED"); 88 | for (uint i = 0; i < swaps.length; i++) { 89 | Swap memory swap = swaps[i]; 90 | 91 | PoolInterface pool = PoolInterface(swap.pool); 92 | if (TI.allowance(address(this), swap.pool) < totalAmountIn) { 93 | TI.approve(swap.pool, uint(-1)); 94 | } 95 | (uint tokenAmountOut,) = pool.swapExactAmountIn( 96 | tokenIn, 97 | swap.tokenInParam, 98 | tokenOut, 99 | swap.tokenOutParam, 100 | swap.maxPrice 101 | ); 102 | totalAmountOut = add(tokenAmountOut, totalAmountOut); 103 | } 104 | require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT"); 105 | require(TO.transfer(msg.sender, TO.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 106 | require(TI.transfer(msg.sender, TI.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 107 | return totalAmountOut; 108 | } 109 | 110 | function batchSwapExactOut( 111 | Swap[] memory swaps, 112 | address tokenIn, 113 | address tokenOut, 114 | uint maxTotalAmountIn 115 | ) 116 | public 117 | _logs_ 118 | _lock_ 119 | returns (uint totalAmountIn) 120 | { 121 | TokenInterface TI = TokenInterface(tokenIn); 122 | TokenInterface TO = TokenInterface(tokenOut); 123 | require(TI.transferFrom(msg.sender, address(this), maxTotalAmountIn), "ERR_TRANSFER_FAILED"); 124 | for (uint i = 0; i < swaps.length; i++) { 125 | Swap memory swap = swaps[i]; 126 | PoolInterface pool = PoolInterface(swap.pool); 127 | if (TI.allowance(address(this), swap.pool) < maxTotalAmountIn) { 128 | TI.approve(swap.pool, uint(-1)); 129 | } 130 | (uint tokenAmountIn,) = pool.swapExactAmountOut( 131 | tokenIn, 132 | swap.tokenInParam, 133 | tokenOut, 134 | swap.tokenOutParam, 135 | swap.maxPrice 136 | ); 137 | totalAmountIn = add(tokenAmountIn, totalAmountIn); 138 | } 139 | require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN"); 140 | require(TO.transfer(msg.sender, TO.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 141 | require(TI.transfer(msg.sender, TI.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 142 | return totalAmountIn; 143 | } 144 | 145 | function batchEthInSwapExactIn( 146 | Swap[] memory swaps, 147 | address tokenOut, 148 | uint minTotalAmountOut 149 | ) 150 | public payable 151 | _logs_ 152 | _lock_ 153 | returns (uint totalAmountOut) 154 | { 155 | TokenInterface TO = TokenInterface(tokenOut); 156 | weth.deposit.value(msg.value)(); 157 | for (uint i = 0; i < swaps.length; i++) { 158 | Swap memory swap = swaps[i]; 159 | PoolInterface pool = PoolInterface(swap.pool); 160 | if (weth.allowance(address(this), swap.pool) < msg.value) { 161 | weth.approve(swap.pool, uint(-1)); 162 | } 163 | (uint tokenAmountOut,) = pool.swapExactAmountIn( 164 | address(weth), 165 | swap.tokenInParam, 166 | tokenOut, 167 | swap.tokenOutParam, 168 | swap.maxPrice 169 | ); 170 | totalAmountOut = add(tokenAmountOut, totalAmountOut); 171 | } 172 | require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT"); 173 | require(TO.transfer(msg.sender, TO.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 174 | uint wethBalance = weth.balanceOf(address(this)); 175 | if (wethBalance > 0) { 176 | weth.withdraw(wethBalance); 177 | (bool xfer,) = msg.sender.call.value(wethBalance)(""); 178 | require(xfer, "ERR_ETH_FAILED"); 179 | } 180 | return totalAmountOut; 181 | } 182 | 183 | function batchEthOutSwapExactIn( 184 | Swap[] memory swaps, 185 | address tokenIn, 186 | uint totalAmountIn, 187 | uint minTotalAmountOut 188 | ) 189 | public 190 | _logs_ 191 | _lock_ 192 | returns (uint totalAmountOut) 193 | { 194 | TokenInterface TI = TokenInterface(tokenIn); 195 | require(TI.transferFrom(msg.sender, address(this), totalAmountIn), "ERR_TRANSFER_FAILED"); 196 | for (uint i = 0; i < swaps.length; i++) { 197 | Swap memory swap = swaps[i]; 198 | PoolInterface pool = PoolInterface(swap.pool); 199 | if (TI.allowance(address(this), swap.pool) < totalAmountIn) { 200 | TI.approve(swap.pool, uint(-1)); 201 | } 202 | (uint tokenAmountOut,) = pool.swapExactAmountIn( 203 | tokenIn, 204 | swap.tokenInParam, 205 | address(weth), 206 | swap.tokenOutParam, 207 | swap.maxPrice 208 | ); 209 | 210 | totalAmountOut = add(tokenAmountOut, totalAmountOut); 211 | } 212 | require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT"); 213 | uint wethBalance = weth.balanceOf(address(this)); 214 | weth.withdraw(wethBalance); 215 | (bool xfer,) = msg.sender.call.value(wethBalance)(""); 216 | require(xfer, "ERR_ETH_FAILED"); 217 | require(TI.transfer(msg.sender, TI.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 218 | return totalAmountOut; 219 | } 220 | 221 | function batchEthInSwapExactOut( 222 | Swap[] memory swaps, 223 | address tokenOut 224 | ) 225 | public payable 226 | _logs_ 227 | _lock_ 228 | returns (uint totalAmountIn) 229 | { 230 | TokenInterface TO = TokenInterface(tokenOut); 231 | weth.deposit.value(msg.value)(); 232 | for (uint i = 0; i < swaps.length; i++) { 233 | Swap memory swap = swaps[i]; 234 | PoolInterface pool = PoolInterface(swap.pool); 235 | if (weth.allowance(address(this), swap.pool) < msg.value) { 236 | weth.approve(swap.pool, uint(-1)); 237 | } 238 | (uint tokenAmountIn,) = pool.swapExactAmountOut( 239 | address(weth), 240 | swap.tokenInParam, 241 | tokenOut, 242 | swap.tokenOutParam, 243 | swap.maxPrice 244 | ); 245 | 246 | totalAmountIn = add(tokenAmountIn, totalAmountIn); 247 | } 248 | require(TO.transfer(msg.sender, TO.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 249 | uint wethBalance = weth.balanceOf(address(this)); 250 | if (wethBalance > 0) { 251 | weth.withdraw(wethBalance); 252 | (bool xfer,) = msg.sender.call.value(wethBalance)(""); 253 | require(xfer, "ERR_ETH_FAILED"); 254 | } 255 | return totalAmountIn; 256 | } 257 | 258 | function batchEthOutSwapExactOut( 259 | Swap[] memory swaps, 260 | address tokenIn, 261 | uint maxTotalAmountIn 262 | ) 263 | public 264 | _logs_ 265 | _lock_ 266 | returns (uint totalAmountIn) 267 | { 268 | TokenInterface TI = TokenInterface(tokenIn); 269 | require(TI.transferFrom(msg.sender, address(this), maxTotalAmountIn), "ERR_TRANSFER_FAILED"); 270 | for (uint i = 0; i < swaps.length; i++) { 271 | Swap memory swap = swaps[i]; 272 | PoolInterface pool = PoolInterface(swap.pool); 273 | if (TI.allowance(address(this), swap.pool) < maxTotalAmountIn) { 274 | TI.approve(swap.pool, uint(-1)); 275 | } 276 | (uint tokenAmountIn,) = pool.swapExactAmountOut( 277 | tokenIn, 278 | swap.tokenInParam, 279 | address(weth), 280 | swap.tokenOutParam, 281 | swap.maxPrice 282 | ); 283 | 284 | totalAmountIn = add(tokenAmountIn, totalAmountIn); 285 | } 286 | require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN"); 287 | require(TI.transfer(msg.sender, TI.balanceOf(address(this))), "ERR_TRANSFER_FAILED"); 288 | uint wethBalance = weth.balanceOf(address(this)); 289 | weth.withdraw(wethBalance); 290 | (bool xfer,) = msg.sender.call.value(wethBalance)(""); 291 | require(xfer, "ERR_ETH_FAILED"); 292 | return totalAmountIn; 293 | } 294 | 295 | function() external payable {} 296 | } -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public lastCompletedMigration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) external restricted { 16 | lastCompletedMigration = completed; 17 | } 18 | 19 | function upgrade(address new_address) external restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(lastCompletedMigration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/BColor.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | contract BColor { 17 | function getColor() 18 | external view 19 | returns (bytes32); 20 | } 21 | 22 | contract BBronze is BColor { 23 | function getColor() 24 | external view 25 | returns (bytes32) { 26 | return bytes32("BRONZE"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test/BConst.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./BColor.sol"; 17 | 18 | contract BConst is BBronze { 19 | uint public constant BONE = 10**18; 20 | 21 | uint public constant MAX_BOUND_TOKENS = 8; 22 | uint public constant BPOW_PRECISION = BONE / 10**10; 23 | 24 | uint public constant MIN_FEE = BONE / 10**6; 25 | uint public constant MAX_FEE = BONE / 10; 26 | uint public constant EXIT_FEE = BONE / 10000; 27 | 28 | uint public constant MIN_WEIGHT = BONE; 29 | uint public constant MAX_WEIGHT = BONE * 50; 30 | uint public constant MAX_TOTAL_WEIGHT = BONE * 50; 31 | uint public constant MIN_BALANCE = BONE / 10**12; 32 | uint public constant MAX_BALANCE = BONE * 10**12; 33 | 34 | uint public constant MIN_POOL_SUPPLY = BONE; 35 | 36 | uint public constant MIN_BPOW_BASE = 1 wei; 37 | uint public constant MAX_BPOW_BASE = (2 * BONE) - 1 wei; 38 | 39 | uint public constant MAX_IN_RATIO = BONE / 2; 40 | uint public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /contracts/test/BFactory.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is disstributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | // Builds new BPools, logging their addresses and providing `isBPool(address) -> (bool)` 17 | 18 | import "./BPool.sol"; 19 | 20 | contract BFactory is BBronze { 21 | event LOG_NEW_POOL( 22 | address indexed caller, 23 | address indexed pool 24 | ); 25 | 26 | mapping(address=>bool) private _isBPool; 27 | 28 | function isBPool(address b) 29 | external view returns (bool) 30 | { 31 | return _isBPool[b]; 32 | } 33 | 34 | function newBPool() 35 | external returns (BPool) 36 | { 37 | BPool bpool = new BPool(); 38 | _isBPool[address(bpool)] = true; 39 | emit LOG_NEW_POOL(msg.sender, address(bpool)); 40 | bpool.setController(msg.sender); 41 | return bpool; 42 | } 43 | 44 | address private _blabs; 45 | constructor() public { 46 | _blabs = msg.sender; 47 | } 48 | function getBLabs() external view returns (address) { 49 | return _blabs; 50 | } 51 | function setBLabs(address b) external { 52 | require(msg.sender == _blabs, "ERR_NOT_BLABS"); 53 | _blabs = b; 54 | } 55 | function collect(BPool pool) 56 | external 57 | { 58 | require(msg.sender == _blabs, "ERR_NOT_BLABS"); 59 | uint collected = IERC20(pool).balanceOf(address(this)); 60 | bool xfer = pool.transfer(_blabs, collected); 61 | require(xfer, "ERR_ERC20_FAILED"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /contracts/test/BMath.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./BNum.sol"; 17 | 18 | 19 | contract BMath is BBronze, BConst, BNum { 20 | /********************************************************************************************** 21 | // calcSpotPrice // 22 | // sP = spotPrice // 23 | // bI = tokenBalanceIn ( bI / wI ) 1 // 24 | // bO = tokenBalanceOut sP = ----------- * ---------- // 25 | // wI = tokenWeightIn ( bO / wO ) ( 1 - sF ) // 26 | // wO = tokenWeightOut // 27 | // sF = swapFee // 28 | **********************************************************************************************/ 29 | function calcSpotPrice( 30 | uint tokenBalanceIn, 31 | uint tokenWeightIn, 32 | uint tokenBalanceOut, 33 | uint tokenWeightOut, 34 | uint swapFee 35 | ) 36 | public pure 37 | returns (uint spotPrice) 38 | { 39 | uint numer = bdiv(tokenBalanceIn, tokenWeightIn); 40 | uint denom = bdiv(tokenBalanceOut, tokenWeightOut); 41 | uint ratio = bdiv(numer, denom); 42 | uint scale = bdiv(BONE, bsub(BONE, swapFee)); 43 | return (spotPrice = bmul(ratio, scale)); 44 | } 45 | 46 | /********************************************************************************************** 47 | // calcOutGivenIn // 48 | // aO = tokenAmountOut // 49 | // bO = tokenBalanceOut // 50 | // bI = tokenBalanceIn / / bI \ (wI / wO) \ // 51 | // aI = tokenAmountIn aO = bO * | 1 - | -------------------------- | ^ | // 52 | // wI = tokenWeightIn \ \ ( bI + ( aI * ( 1 - sF )) / / // 53 | // wO = tokenWeightOut // 54 | // sF = swapFee // 55 | **********************************************************************************************/ 56 | function calcOutGivenIn( 57 | uint tokenBalanceIn, 58 | uint tokenWeightIn, 59 | uint tokenBalanceOut, 60 | uint tokenWeightOut, 61 | uint tokenAmountIn, 62 | uint swapFee 63 | ) 64 | public pure 65 | returns (uint tokenAmountOut) 66 | { 67 | uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut); 68 | uint adjustedIn = bsub(BONE, swapFee); 69 | adjustedIn = bmul(tokenAmountIn, adjustedIn); 70 | uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn)); 71 | uint foo = bpow(y, weightRatio); 72 | uint bar = bsub(BONE, foo); 73 | tokenAmountOut = bmul(tokenBalanceOut, bar); 74 | return tokenAmountOut; 75 | } 76 | 77 | /********************************************************************************************** 78 | // calcInGivenOut // 79 | // aI = tokenAmountIn // 80 | // bO = tokenBalanceOut / / bO \ (wO / wI) \ // 81 | // bI = tokenBalanceIn bI * | | ------------ | ^ - 1 | // 82 | // aO = tokenAmountOut aI = \ \ ( bO - aO ) / / // 83 | // wI = tokenWeightIn -------------------------------------------- // 84 | // wO = tokenWeightOut ( 1 - sF ) // 85 | // sF = swapFee // 86 | **********************************************************************************************/ 87 | function calcInGivenOut( 88 | uint tokenBalanceIn, 89 | uint tokenWeightIn, 90 | uint tokenBalanceOut, 91 | uint tokenWeightOut, 92 | uint tokenAmountOut, 93 | uint swapFee 94 | ) 95 | public pure 96 | returns (uint tokenAmountIn) 97 | { 98 | uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn); 99 | uint diff = bsub(tokenBalanceOut, tokenAmountOut); 100 | uint y = bdiv(tokenBalanceOut, diff); 101 | uint foo = bpow(y, weightRatio); 102 | foo = bsub(foo, BONE); 103 | tokenAmountIn = bsub(BONE, swapFee); 104 | tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn); 105 | return tokenAmountIn; 106 | } 107 | 108 | /********************************************************************************************** 109 | // calcPoolOutGivenSingleIn // 110 | // pAo = poolAmountOut / \ // 111 | // tAi = tokenAmountIn /// / // wI \ \\ \ wI \ // 112 | // wI = tokenWeightIn //| tAi *| 1 - || 1 - -- | * sF || + tBi \ -- \ // 113 | // tW = totalWeight pAo=|| \ \ \\ tW / // | ^ tW | * pS - pS // 114 | // tBi = tokenBalanceIn \\ ------------------------------------- / / // 115 | // pS = poolSupply \\ tBi / / // 116 | // sF = swapFee \ / // 117 | **********************************************************************************************/ 118 | function calcPoolOutGivenSingleIn( 119 | uint tokenBalanceIn, 120 | uint tokenWeightIn, 121 | uint poolSupply, 122 | uint totalWeight, 123 | uint tokenAmountIn, 124 | uint swapFee 125 | ) 126 | public pure 127 | returns (uint poolAmountOut) 128 | { 129 | // Charge the trading fee for the proportion of tokenAi 130 | /// which is implicitly traded to the other pool tokens. 131 | // That proportion is (1- weightTokenIn) 132 | // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee); 133 | uint normalizedWeight = bdiv(tokenWeightIn, totalWeight); 134 | uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 135 | uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz)); 136 | 137 | uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee); 138 | uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn); 139 | 140 | // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply; 141 | uint poolRatio = bpow(tokenInRatio, normalizedWeight); 142 | uint newPoolSupply = bmul(poolRatio, poolSupply); 143 | poolAmountOut = bsub(newPoolSupply, poolSupply); 144 | return poolAmountOut; 145 | } 146 | 147 | /********************************************************************************************** 148 | // calcSingleInGivenPoolOut // 149 | // tAi = tokenAmountIn //(pS + pAo)\ / 1 \\ // 150 | // pS = poolSupply || --------- | ^ | --------- || * bI - bI // 151 | // pAo = poolAmountOut \\ pS / \(wI / tW)// // 152 | // bI = balanceIn tAi = -------------------------------------------- // 153 | // wI = weightIn / wI \ // 154 | // tW = totalWeight | 1 - ---- | * sF // 155 | // sF = swapFee \ tW / // 156 | **********************************************************************************************/ 157 | function calcSingleInGivenPoolOut( 158 | uint tokenBalanceIn, 159 | uint tokenWeightIn, 160 | uint poolSupply, 161 | uint totalWeight, 162 | uint poolAmountOut, 163 | uint swapFee 164 | ) 165 | public pure 166 | returns (uint tokenAmountIn) 167 | { 168 | uint normalizedWeight = bdiv(tokenWeightIn, totalWeight); 169 | uint newPoolSupply = badd(poolSupply, poolAmountOut); 170 | uint poolRatio = bdiv(newPoolSupply, poolSupply); 171 | 172 | //uint newBalTi = poolRatio^(1/weightTi) * balTi; 173 | uint boo = bdiv(BONE, normalizedWeight); 174 | uint tokenInRatio = bpow(poolRatio, boo); 175 | uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn); 176 | uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn); 177 | // Do reverse order of fees charged in joinswap_ExternAmountIn, this way 178 | // ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ``` 179 | //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ; 180 | uint zar = bmul(bsub(BONE, normalizedWeight), swapFee); 181 | tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar)); 182 | return tokenAmountIn; 183 | } 184 | 185 | /********************************************************************************************** 186 | // calcSingleOutGivenPoolIn // 187 | // tAo = tokenAmountOut / / \\ // 188 | // bO = tokenBalanceOut / // pS - (pAi * (1 - eF)) \ / 1 \ \\ // 189 | // pAi = poolAmountIn | bO - || ----------------------- | ^ | --------- | * b0 || // 190 | // ps = poolSupply \ \\ pS / \(wO / tW)/ // // 191 | // wI = tokenWeightIn tAo = \ \ // // 192 | // tW = totalWeight / / wO \ \ // 193 | // sF = swapFee * | 1 - | 1 - ---- | * sF | // 194 | // eF = exitFee \ \ tW / / // 195 | **********************************************************************************************/ 196 | function calcSingleOutGivenPoolIn( 197 | uint tokenBalanceOut, 198 | uint tokenWeightOut, 199 | uint poolSupply, 200 | uint totalWeight, 201 | uint poolAmountIn, 202 | uint swapFee 203 | ) 204 | public pure 205 | returns (uint tokenAmountOut) 206 | { 207 | uint normalizedWeight = bdiv(tokenWeightOut, totalWeight); 208 | // charge exit fee on the pool token side 209 | // pAiAfterExitFee = pAi*(1-exitFee) 210 | uint poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE)); 211 | uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee); 212 | uint poolRatio = bdiv(newPoolSupply, poolSupply); 213 | 214 | // newBalTo = poolRatio^(1/weightTo) * balTo; 215 | uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight)); 216 | uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut); 217 | 218 | uint tokenAmountOutBeforeSwapFee = bsub(tokenBalanceOut, newTokenBalanceOut); 219 | 220 | // charge swap fee on the output token side 221 | //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee) 222 | uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee); 223 | tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz)); 224 | return tokenAmountOut; 225 | } 226 | 227 | /********************************************************************************************** 228 | // calcPoolInGivenSingleOut // 229 | // pAi = poolAmountIn // / tAo \\ / wO \ \ // 230 | // bO = tokenBalanceOut // | bO - -------------------------- |\ | ---- | \ // 231 | // tAo = tokenAmountOut pS - || \ 1 - ((1 - (tO / tW)) * sF)/ | ^ \ tW / * pS | // 232 | // ps = poolSupply \\ -----------------------------------/ / // 233 | // wO = tokenWeightOut pAi = \\ bO / / // 234 | // tW = totalWeight ------------------------------------------------------------- // 235 | // sF = swapFee ( 1 - eF ) // 236 | // eF = exitFee // 237 | **********************************************************************************************/ 238 | function calcPoolInGivenSingleOut( 239 | uint tokenBalanceOut, 240 | uint tokenWeightOut, 241 | uint poolSupply, 242 | uint totalWeight, 243 | uint tokenAmountOut, 244 | uint swapFee 245 | ) 246 | public pure 247 | returns (uint poolAmountIn) 248 | { 249 | 250 | // charge swap fee on the output token side 251 | uint normalizedWeight = bdiv(tokenWeightOut, totalWeight); 252 | //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ; 253 | uint zoo = bsub(BONE, normalizedWeight); 254 | uint zar = bmul(zoo, swapFee); 255 | uint tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar)); 256 | 257 | uint newTokenBalanceOut = bsub(tokenBalanceOut, tokenAmountOutBeforeSwapFee); 258 | uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut); 259 | 260 | //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply; 261 | uint poolRatio = bpow(tokenOutRatio, normalizedWeight); 262 | uint newPoolSupply = bmul(poolRatio, poolSupply); 263 | uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply); 264 | 265 | // charge exit fee on the pool token side 266 | // pAi = pAiAfterExitFee/(1-exitFee) 267 | poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE)); 268 | return poolAmountIn; 269 | } 270 | 271 | 272 | } 273 | -------------------------------------------------------------------------------- /contracts/test/BNum.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./BConst.sol"; 17 | 18 | contract BNum is BConst { 19 | 20 | function btoi(uint a) 21 | internal pure 22 | returns (uint) 23 | { 24 | return a / BONE; 25 | } 26 | 27 | function bfloor(uint a) 28 | internal pure 29 | returns (uint) 30 | { 31 | return btoi(a) * BONE; 32 | } 33 | 34 | function badd(uint a, uint b) 35 | internal pure 36 | returns (uint) 37 | { 38 | uint c = a + b; 39 | require(c >= a, "ERR_ADD_OVERFLOW"); 40 | return c; 41 | } 42 | 43 | function bsub(uint a, uint b) 44 | internal pure 45 | returns (uint) 46 | { 47 | (uint c, bool flag) = bsubSign(a, b); 48 | require(!flag, "ERR_SUB_UNDERFLOW"); 49 | return c; 50 | } 51 | 52 | function bsubSign(uint a, uint b) 53 | internal pure 54 | returns (uint, bool) 55 | { 56 | if (a >= b) { 57 | return (a - b, false); 58 | } else { 59 | return (b - a, true); 60 | } 61 | } 62 | 63 | function bmul(uint a, uint b) 64 | internal pure 65 | returns (uint) 66 | { 67 | uint c0 = a * b; 68 | require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW"); 69 | uint c1 = c0 + (BONE / 2); 70 | require(c1 >= c0, "ERR_MUL_OVERFLOW"); 71 | uint c2 = c1 / BONE; 72 | return c2; 73 | } 74 | 75 | function bdiv(uint a, uint b) 76 | internal pure 77 | returns (uint) 78 | { 79 | require(b != 0, "ERR_DIV_ZERO"); 80 | uint c0 = a * BONE; 81 | require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow 82 | uint c1 = c0 + (b / 2); 83 | require(c1 >= c0, "ERR_DIV_INTERNAL"); // badd require 84 | uint c2 = c1 / b; 85 | return c2; 86 | } 87 | 88 | // DSMath.wpow 89 | function bpowi(uint a, uint n) 90 | internal pure 91 | returns (uint) 92 | { 93 | uint z = n % 2 != 0 ? a : BONE; 94 | 95 | for (n /= 2; n != 0; n /= 2) { 96 | a = bmul(a, a); 97 | 98 | if (n % 2 != 0) { 99 | z = bmul(z, a); 100 | } 101 | } 102 | return z; 103 | } 104 | 105 | // Compute b^(e.w) by splitting it into (b^e)*(b^0.w). 106 | // Use `bpowi` for `b^e` and `bpowK` for k iterations 107 | // of approximation of b^0.w 108 | function bpow(uint base, uint exp) 109 | internal pure 110 | returns (uint) 111 | { 112 | require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW"); 113 | require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH"); 114 | 115 | uint whole = bfloor(exp); 116 | uint remain = bsub(exp, whole); 117 | 118 | uint wholePow = bpowi(base, btoi(whole)); 119 | 120 | if (remain == 0) { 121 | return wholePow; 122 | } 123 | 124 | uint partialResult = bpowApprox(base, remain, BPOW_PRECISION); 125 | return bmul(wholePow, partialResult); 126 | } 127 | 128 | function bpowApprox(uint base, uint exp, uint precision) 129 | internal pure 130 | returns (uint) 131 | { 132 | // term 0: 133 | uint a = exp; 134 | (uint x, bool xneg) = bsubSign(base, BONE); 135 | uint term = BONE; 136 | uint sum = term; 137 | bool negative = false; 138 | 139 | 140 | // term(k) = numer / denom 141 | // = (product(a - i - 1, i=1-->k) * x^k) / (k!) 142 | // each iteration, multiply previous term by (a-(k-1)) * x / k 143 | // continue until term is less than precision 144 | for (uint i = 1; term >= precision; i++) { 145 | uint bigK = i * BONE; 146 | (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE)); 147 | term = bmul(term, bmul(c, x)); 148 | term = bdiv(term, bigK); 149 | if (term == 0) break; 150 | 151 | if (xneg) negative = !negative; 152 | if (cneg) negative = !negative; 153 | if (negative) { 154 | sum = bsub(sum, term); 155 | } else { 156 | sum = badd(sum, term); 157 | } 158 | } 159 | 160 | return sum; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /contracts/test/BPool.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./BToken.sol"; 17 | import "./BMath.sol"; 18 | 19 | contract BPool is BBronze, BToken, BMath { 20 | 21 | struct Record { 22 | bool bound; // is token bound to pool 23 | uint index; // private 24 | uint denorm; // denormalized weight 25 | uint balance; 26 | } 27 | 28 | event LOG_SWAP( 29 | address indexed caller, 30 | address indexed tokenIn, 31 | address indexed tokenOut, 32 | uint256 tokenAmountIn, 33 | uint256 tokenAmountOut 34 | ); 35 | 36 | event LOG_JOIN( 37 | address indexed caller, 38 | address indexed tokenIn, 39 | uint256 tokenAmountIn 40 | ); 41 | 42 | event LOG_EXIT( 43 | address indexed caller, 44 | address indexed tokenOut, 45 | uint256 tokenAmountOut 46 | ); 47 | 48 | event LOG_CALL( 49 | bytes4 indexed sig, 50 | address indexed caller, 51 | bytes data 52 | ) anonymous; 53 | 54 | modifier _logs_() { 55 | emit LOG_CALL(msg.sig, msg.sender, msg.data); 56 | _; 57 | } 58 | 59 | modifier _lock_() { 60 | require(!_mutex, "ERR_REENTRY"); 61 | _mutex = true; 62 | _; 63 | _mutex = false; 64 | } 65 | 66 | modifier _viewlock_() { 67 | require(!_mutex, "ERR_REENTRY"); 68 | _; 69 | } 70 | 71 | bool private _mutex; 72 | 73 | address private _factory; // BFactory address to push token exitFee to 74 | address private _controller; // has CONTROL role 75 | bool private _publicSwap; // true if PUBLIC can call SWAP functions 76 | 77 | // `setSwapFee` and `finalize` require CONTROL 78 | // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN` 79 | uint private _swapFee; 80 | bool private _finalized; 81 | 82 | address[] private _tokens; 83 | mapping(address=>Record) private _records; 84 | uint private _totalWeight; 85 | 86 | constructor() public { 87 | _controller = msg.sender; 88 | _factory = msg.sender; 89 | _swapFee = MIN_FEE; 90 | _publicSwap = false; 91 | _finalized = false; 92 | } 93 | 94 | function isPublicSwap() 95 | external view 96 | returns (bool) 97 | { 98 | return _publicSwap; 99 | } 100 | 101 | function isFinalized() 102 | external view 103 | returns (bool) 104 | { 105 | return _finalized; 106 | } 107 | 108 | function isBound(address t) 109 | external view 110 | returns (bool) 111 | { 112 | return _records[t].bound; 113 | } 114 | 115 | function getNumTokens() 116 | external view 117 | returns (uint) 118 | { 119 | return _tokens.length; 120 | } 121 | 122 | function getCurrentTokens() 123 | external view _viewlock_ 124 | returns (address[] memory tokens) 125 | { 126 | return _tokens; 127 | } 128 | 129 | function getFinalTokens() 130 | external view 131 | _viewlock_ 132 | returns (address[] memory tokens) 133 | { 134 | require(_finalized, "ERR_NOT_FINALIZED"); 135 | return _tokens; 136 | } 137 | 138 | function getDenormalizedWeight(address token) 139 | external view 140 | _viewlock_ 141 | returns (uint) 142 | { 143 | 144 | require(_records[token].bound, "ERR_NOT_BOUND"); 145 | return _records[token].denorm; 146 | } 147 | 148 | function getTotalDenormalizedWeight() 149 | external view 150 | _viewlock_ 151 | returns (uint) 152 | { 153 | return _totalWeight; 154 | } 155 | 156 | function getNormalizedWeight(address token) 157 | external view 158 | _viewlock_ 159 | returns (uint) 160 | { 161 | 162 | require(_records[token].bound, "ERR_NOT_BOUND"); 163 | uint denorm = _records[token].denorm; 164 | return bdiv(denorm, _totalWeight); 165 | } 166 | 167 | function getBalance(address token) 168 | external view 169 | _viewlock_ 170 | returns (uint) 171 | { 172 | 173 | require(_records[token].bound, "ERR_NOT_BOUND"); 174 | return _records[token].balance; 175 | } 176 | 177 | function getSwapFee() 178 | external view 179 | _viewlock_ 180 | returns (uint) 181 | { 182 | return _swapFee; 183 | } 184 | 185 | function getController() 186 | external view 187 | _viewlock_ 188 | returns (address) 189 | { 190 | return _controller; 191 | } 192 | 193 | function setSwapFee(uint swapFee) 194 | external 195 | _logs_ 196 | _lock_ 197 | { 198 | require(!_finalized, "ERR_IS_FINALIZED"); 199 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 200 | require(swapFee >= MIN_FEE, "ERR_MIN_FEE"); 201 | require(swapFee <= MAX_FEE, "ERR_MAX_FEE"); 202 | _swapFee = swapFee; 203 | } 204 | 205 | function setController(address manager) 206 | external 207 | _logs_ 208 | _lock_ 209 | { 210 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 211 | _controller = manager; 212 | } 213 | 214 | function setPublicSwap(bool public_) 215 | external 216 | _logs_ 217 | _lock_ 218 | { 219 | require(!_finalized, "ERR_IS_FINALIZED"); 220 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 221 | _publicSwap = public_; 222 | } 223 | 224 | function finalize(uint initSupply) 225 | external 226 | _logs_ 227 | _lock_ 228 | { 229 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 230 | require(!_finalized, "ERR_IS_FINALIZED"); 231 | require(initSupply >= MIN_POOL_SUPPLY, "ERR_MIN_POOL_SUPPLY"); 232 | 233 | _finalized = true; 234 | _publicSwap = true; 235 | 236 | _mintPoolShare(initSupply); 237 | _pushPoolShare(msg.sender, initSupply); 238 | } 239 | 240 | 241 | function bind(address token, uint balance, uint denorm) 242 | external 243 | _logs_ 244 | // _lock_ Bind does not lock because it jumps to `rebind`, which does 245 | { 246 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 247 | require(!_records[token].bound, "ERR_IS_BOUND"); 248 | require(!_finalized, "ERR_IS_FINALIZED"); 249 | 250 | require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS"); 251 | 252 | _records[token] = Record({ 253 | bound: true, 254 | index: _tokens.length, // 1-indexed (0 is 'unbound' state) 255 | denorm: 0, // balance and denorm will be validated 256 | balance: 0 // and set by `rebind` 257 | }); 258 | _tokens.push(token); 259 | rebind(token, balance, denorm); 260 | } 261 | 262 | function rebind(address token, uint balance, uint denorm) 263 | public 264 | _logs_ 265 | _lock_ 266 | { 267 | 268 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 269 | require(_records[token].bound, "ERR_NOT_BOUND"); 270 | require(!_finalized, "ERR_IS_FINALIZED"); 271 | 272 | require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT"); 273 | require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT"); 274 | require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE"); 275 | require(balance <= MAX_BALANCE, "ERR_MAX_BALANCE"); 276 | 277 | // Adjust the denorm and totalWeight 278 | uint oldWeight = _records[token].denorm; 279 | if (denorm > oldWeight) { 280 | _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight)); 281 | require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT"); 282 | } else if (denorm < oldWeight) { 283 | _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm)); 284 | } 285 | _records[token].denorm = denorm; 286 | 287 | // Adjust the balance record and actual token balance 288 | uint oldBalance = _records[token].balance; 289 | _records[token].balance = balance; 290 | if (balance > oldBalance) { 291 | _pullUnderlying(token, msg.sender, bsub(balance, oldBalance)); 292 | } else if (balance < oldBalance) { 293 | // In this case liquidity is being withdrawn, so charge EXIT_FEE 294 | uint tokenBalanceWithdrawn = bsub(oldBalance, balance); 295 | uint tokenExitFee = bmul(tokenBalanceWithdrawn, EXIT_FEE); 296 | _pushUnderlying(token, msg.sender, bsub(tokenBalanceWithdrawn, tokenExitFee)); 297 | _pushUnderlying(token, _factory, tokenExitFee); 298 | } 299 | } 300 | 301 | function unbind(address token) 302 | external 303 | _logs_ 304 | _lock_ 305 | { 306 | 307 | require(msg.sender == _controller, "ERR_NOT_CONTROLLER"); 308 | require(_records[token].bound, "ERR_NOT_BOUND"); 309 | require(!_finalized, "ERR_IS_FINALIZED"); 310 | 311 | uint tokenBalance = _records[token].balance; 312 | uint tokenExitFee = bmul(tokenBalance, EXIT_FEE); 313 | 314 | _totalWeight = bsub(_totalWeight, _records[token].denorm); 315 | 316 | // Swap the token-to-unbind with the last token, 317 | // then delete the last token 318 | uint index = _records[token].index; 319 | uint last = _tokens.length - 1; 320 | _tokens[index] = _tokens[last]; 321 | _records[_tokens[index]].index = index; 322 | _tokens.pop(); 323 | _records[token] = Record({ 324 | bound: false, 325 | index: 0, 326 | denorm: 0, 327 | balance: 0 328 | }); 329 | 330 | _pushUnderlying(token, msg.sender, bsub(tokenBalance, tokenExitFee)); 331 | _pushUnderlying(token, _factory, tokenExitFee); 332 | } 333 | 334 | // Absorb any tokens that have been sent to this contract into the pool 335 | function gulp(address token) 336 | external 337 | _logs_ 338 | _lock_ 339 | { 340 | require(_records[token].bound, "ERR_NOT_BOUND"); 341 | _records[token].balance = IERC20(token).balanceOf(address(this)); 342 | } 343 | 344 | function getSpotPrice(address tokenIn, address tokenOut) 345 | external view 346 | _viewlock_ 347 | returns (uint spotPrice) 348 | { 349 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 350 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 351 | Record storage inRecord = _records[tokenIn]; 352 | Record storage outRecord = _records[tokenOut]; 353 | return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, _swapFee); 354 | } 355 | 356 | function getSpotPriceSansFee(address tokenIn, address tokenOut) 357 | external view 358 | _viewlock_ 359 | returns (uint spotPrice) 360 | { 361 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 362 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 363 | Record storage inRecord = _records[tokenIn]; 364 | Record storage outRecord = _records[tokenOut]; 365 | return calcSpotPrice(inRecord.balance, inRecord.denorm, outRecord.balance, outRecord.denorm, 0); 366 | } 367 | 368 | function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) 369 | external 370 | _logs_ 371 | _lock_ 372 | { 373 | require(_finalized, "ERR_NOT_FINALIZED"); 374 | 375 | uint poolTotal = totalSupply(); 376 | uint ratio = bdiv(poolAmountOut, poolTotal); 377 | require(ratio != 0, "ERR_MATH_APPROX"); 378 | 379 | for (uint i = 0; i < _tokens.length; i++) { 380 | address t = _tokens[i]; 381 | uint bal = _records[t].balance; 382 | uint tokenAmountIn = bmul(ratio, bal); 383 | require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN"); 384 | _records[t].balance = badd(_records[t].balance, tokenAmountIn); 385 | emit LOG_JOIN(msg.sender, t, tokenAmountIn); 386 | _pullUnderlying(t, msg.sender, tokenAmountIn); 387 | } 388 | _mintPoolShare(poolAmountOut); 389 | _pushPoolShare(msg.sender, poolAmountOut); 390 | } 391 | 392 | function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) 393 | external 394 | _logs_ 395 | _lock_ 396 | { 397 | require(_finalized, "ERR_NOT_FINALIZED"); 398 | 399 | uint poolTotal = totalSupply(); 400 | uint exitFee = bmul(poolAmountIn, EXIT_FEE); 401 | uint pAiAfterExitFee = bsub(poolAmountIn, exitFee); 402 | uint ratio = bdiv(pAiAfterExitFee, poolTotal); 403 | require(ratio != 0, "ERR_MATH_APPROX"); 404 | 405 | _pullPoolShare(msg.sender, poolAmountIn); 406 | _pushPoolShare(_factory, exitFee); 407 | _burnPoolShare(pAiAfterExitFee); 408 | 409 | for (uint i = 0; i < _tokens.length; i++) { 410 | address t = _tokens[i]; 411 | uint bal = _records[t].balance; 412 | uint tokenAmountOut = bmul(ratio, bal); 413 | require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT"); 414 | _records[t].balance = bsub(_records[t].balance, tokenAmountOut); 415 | emit LOG_EXIT(msg.sender, t, tokenAmountOut); 416 | _pushUnderlying(t, msg.sender, tokenAmountOut); 417 | } 418 | 419 | } 420 | 421 | 422 | function swapExactAmountIn( 423 | address tokenIn, 424 | uint tokenAmountIn, 425 | address tokenOut, 426 | uint minAmountOut, 427 | uint maxPrice 428 | ) 429 | external 430 | _logs_ 431 | _lock_ 432 | returns (uint tokenAmountOut, uint spotPriceAfter) 433 | { 434 | 435 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 436 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 437 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 438 | 439 | Record storage inRecord = _records[address(tokenIn)]; 440 | Record storage outRecord = _records[address(tokenOut)]; 441 | 442 | require(tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO), "ERR_MAX_IN_RATIO"); 443 | 444 | uint spotPriceBefore = calcSpotPrice( 445 | inRecord.balance, 446 | inRecord.denorm, 447 | outRecord.balance, 448 | outRecord.denorm, 449 | _swapFee 450 | ); 451 | require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); 452 | 453 | tokenAmountOut = calcOutGivenIn( 454 | inRecord.balance, 455 | inRecord.denorm, 456 | outRecord.balance, 457 | outRecord.denorm, 458 | tokenAmountIn, 459 | _swapFee 460 | ); 461 | require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); 462 | 463 | inRecord.balance = badd(inRecord.balance, tokenAmountIn); 464 | outRecord.balance = bsub(outRecord.balance, tokenAmountOut); 465 | 466 | spotPriceAfter = calcSpotPrice( 467 | inRecord.balance, 468 | inRecord.denorm, 469 | outRecord.balance, 470 | outRecord.denorm, 471 | _swapFee 472 | ); 473 | require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); 474 | require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); 475 | require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); 476 | 477 | emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut); 478 | 479 | _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); 480 | _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); 481 | 482 | return (tokenAmountOut, spotPriceAfter); 483 | } 484 | 485 | function swapExactAmountOut( 486 | address tokenIn, 487 | uint maxAmountIn, 488 | address tokenOut, 489 | uint tokenAmountOut, 490 | uint maxPrice 491 | ) 492 | external 493 | _logs_ 494 | _lock_ 495 | returns (uint tokenAmountIn, uint spotPriceAfter) 496 | { 497 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 498 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 499 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 500 | 501 | Record storage inRecord = _records[address(tokenIn)]; 502 | Record storage outRecord = _records[address(tokenOut)]; 503 | 504 | require(tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO), "ERR_MAX_OUT_RATIO"); 505 | 506 | uint spotPriceBefore = calcSpotPrice( 507 | inRecord.balance, 508 | inRecord.denorm, 509 | outRecord.balance, 510 | outRecord.denorm, 511 | _swapFee 512 | ); 513 | require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE"); 514 | 515 | tokenAmountIn = calcInGivenOut( 516 | inRecord.balance, 517 | inRecord.denorm, 518 | outRecord.balance, 519 | outRecord.denorm, 520 | tokenAmountOut, 521 | _swapFee 522 | ); 523 | require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); 524 | 525 | inRecord.balance = badd(inRecord.balance, tokenAmountIn); 526 | outRecord.balance = bsub(outRecord.balance, tokenAmountOut); 527 | 528 | spotPriceAfter = calcSpotPrice( 529 | inRecord.balance, 530 | inRecord.denorm, 531 | outRecord.balance, 532 | outRecord.denorm, 533 | _swapFee 534 | ); 535 | require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX"); 536 | require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE"); 537 | require(spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut), "ERR_MATH_APPROX"); 538 | 539 | emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut); 540 | 541 | _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); 542 | _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); 543 | 544 | return (tokenAmountIn, spotPriceAfter); 545 | } 546 | 547 | 548 | function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) 549 | external 550 | _logs_ 551 | _lock_ 552 | returns (uint poolAmountOut) 553 | { 554 | 555 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 556 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 557 | 558 | Record storage inRecord = _records[tokenIn]; 559 | 560 | poolAmountOut = calcPoolOutGivenSingleIn( 561 | inRecord.balance, 562 | inRecord.denorm, 563 | _totalSupply, 564 | _totalWeight, 565 | tokenAmountIn, 566 | _swapFee 567 | ); 568 | 569 | require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT"); 570 | 571 | inRecord.balance = badd(inRecord.balance, tokenAmountIn); 572 | 573 | emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn); 574 | 575 | _mintPoolShare(poolAmountOut); 576 | _pushPoolShare(msg.sender, poolAmountOut); 577 | _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); 578 | 579 | return poolAmountOut; 580 | } 581 | 582 | function joinswapPoolAmountOut(uint poolAmountOut, address tokenIn, uint maxAmountIn) 583 | external 584 | _logs_ 585 | _lock_ 586 | returns (uint tokenAmountIn) 587 | { 588 | require(_records[tokenIn].bound, "ERR_NOT_BOUND"); 589 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 590 | 591 | Record storage inRecord = _records[tokenIn]; 592 | 593 | tokenAmountIn = calcSingleInGivenPoolOut( 594 | inRecord.balance, 595 | inRecord.denorm, 596 | _totalSupply, 597 | _totalWeight, 598 | poolAmountOut, 599 | _swapFee 600 | ); 601 | 602 | require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN"); 603 | 604 | inRecord.balance = badd(inRecord.balance, tokenAmountIn); 605 | 606 | emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn); 607 | 608 | _mintPoolShare(poolAmountOut); 609 | _pushPoolShare(msg.sender, poolAmountOut); 610 | _pullUnderlying(tokenIn, msg.sender, tokenAmountIn); 611 | 612 | return tokenAmountIn; 613 | } 614 | 615 | function exitswapPoolAmountIn(uint poolAmountIn, address tokenOut, uint minAmountOut) 616 | external 617 | _logs_ 618 | _lock_ 619 | returns (uint tokenAmountOut) 620 | { 621 | 622 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 623 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 624 | 625 | Record storage outRecord = _records[tokenOut]; 626 | 627 | tokenAmountOut = calcSingleOutGivenPoolIn( 628 | outRecord.balance, 629 | outRecord.denorm, 630 | _totalSupply, 631 | _totalWeight, 632 | poolAmountIn, 633 | _swapFee 634 | ); 635 | 636 | require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT"); 637 | 638 | outRecord.balance = bsub(outRecord.balance, tokenAmountOut); 639 | 640 | uint exitFee = bmul(poolAmountIn, EXIT_FEE); 641 | 642 | emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut); 643 | 644 | _pullPoolShare(msg.sender, poolAmountIn); 645 | _burnPoolShare(bsub(poolAmountIn, exitFee)); 646 | _pushPoolShare(_factory, exitFee); 647 | _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); 648 | 649 | return tokenAmountOut; 650 | } 651 | 652 | function exitswapExternAmountOut(address tokenOut, uint tokenAmountOut, uint maxPoolAmountIn) 653 | external 654 | _logs_ 655 | _lock_ 656 | returns (uint poolAmountIn) 657 | { 658 | 659 | require(_records[tokenOut].bound, "ERR_NOT_BOUND"); 660 | require(_publicSwap, "ERR_SWAP_NOT_PUBLIC"); 661 | 662 | Record storage outRecord = _records[tokenOut]; 663 | 664 | poolAmountIn = calcPoolInGivenSingleOut( 665 | outRecord.balance, 666 | outRecord.denorm, 667 | _totalSupply, 668 | _totalWeight, 669 | tokenAmountOut, 670 | _swapFee 671 | ); 672 | 673 | require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN"); 674 | 675 | outRecord.balance = bsub(outRecord.balance, tokenAmountOut); 676 | 677 | uint exitFee = bmul(poolAmountIn, EXIT_FEE); 678 | 679 | emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut); 680 | 681 | _pullPoolShare(msg.sender, poolAmountIn); 682 | _burnPoolShare(bsub(poolAmountIn, exitFee)); 683 | _pushPoolShare(_factory, exitFee); 684 | _pushUnderlying(tokenOut, msg.sender, tokenAmountOut); 685 | 686 | return poolAmountIn; 687 | } 688 | 689 | 690 | // == 691 | // 'Underlying' token-manipulation functions make external calls but are NOT locked 692 | // You must `_lock_` or otherwise ensure reentry-safety 693 | 694 | function _pullUnderlying(address erc20, address from, uint amount) 695 | internal 696 | { 697 | bool xfer = IERC20(erc20).transferFrom(from, address(this), amount); 698 | require(xfer, "ERR_ERC20_FALSE"); 699 | } 700 | 701 | function _pushUnderlying(address erc20, address to, uint amount) 702 | internal 703 | { 704 | bool xfer = IERC20(erc20).transfer(to, amount); 705 | require(xfer, "ERR_ERC20_FALSE"); 706 | } 707 | 708 | function _pullPoolShare(address from, uint amount) 709 | internal 710 | { 711 | _pull(from, amount); 712 | } 713 | 714 | function _pushPoolShare(address to, uint amount) 715 | internal 716 | { 717 | _push(to, amount); 718 | } 719 | 720 | function _mintPoolShare(uint amount) 721 | internal 722 | { 723 | _mint(amount); 724 | } 725 | 726 | function _burnPoolShare(uint amount) 727 | internal 728 | { 729 | _burn(amount); 730 | } 731 | 732 | } 733 | -------------------------------------------------------------------------------- /contracts/test/BToken.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./BNum.sol"; 17 | 18 | // Highly opinionated token implementation 19 | 20 | interface IERC20 { 21 | event Approval(address indexed src, address indexed dst, uint amt); 22 | event Transfer(address indexed src, address indexed dst, uint amt); 23 | 24 | function totalSupply() external view returns (uint); 25 | function balanceOf(address whom) external view returns (uint); 26 | function allowance(address src, address dst) external view returns (uint); 27 | 28 | function approve(address dst, uint amt) external returns (bool); 29 | function transfer(address dst, uint amt) external returns (bool); 30 | function transferFrom( 31 | address src, address dst, uint amt 32 | ) external returns (bool); 33 | } 34 | 35 | contract BTokenBase is BNum { 36 | 37 | mapping(address => uint) internal _balance; 38 | mapping(address => mapping(address=>uint)) internal _allowance; 39 | uint internal _totalSupply; 40 | 41 | event Approval(address indexed src, address indexed dst, uint amt); 42 | event Transfer(address indexed src, address indexed dst, uint amt); 43 | 44 | function _mint(uint amt) internal { 45 | _balance[address(this)] = badd(_balance[address(this)], amt); 46 | _totalSupply = badd(_totalSupply, amt); 47 | emit Transfer(address(0), address(this), amt); 48 | } 49 | 50 | function _burn(uint amt) internal { 51 | require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL"); 52 | _balance[address(this)] = bsub(_balance[address(this)], amt); 53 | _totalSupply = bsub(_totalSupply, amt); 54 | emit Transfer(address(this), address(0), amt); 55 | } 56 | 57 | function _move(address src, address dst, uint amt) internal { 58 | require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL"); 59 | _balance[src] = bsub(_balance[src], amt); 60 | _balance[dst] = badd(_balance[dst], amt); 61 | emit Transfer(src, dst, amt); 62 | } 63 | 64 | function _push(address to, uint amt) internal { 65 | _move(address(this), to, amt); 66 | } 67 | 68 | function _pull(address from, uint amt) internal { 69 | _move(from, address(this), amt); 70 | } 71 | } 72 | 73 | contract BToken is BTokenBase, IERC20 { 74 | 75 | string private _name = "Balancer Pool Token"; 76 | string private _symbol = "BPT"; 77 | uint8 private _decimals = 18; 78 | 79 | function name() public view returns (string memory) { 80 | return _name; 81 | } 82 | 83 | function symbol() public view returns (string memory) { 84 | return _symbol; 85 | } 86 | 87 | function decimals() public view returns(uint8) { 88 | return _decimals; 89 | } 90 | 91 | function allowance(address src, address dst) external view returns (uint) { 92 | return _allowance[src][dst]; 93 | } 94 | 95 | function balanceOf(address whom) external view returns (uint) { 96 | return _balance[whom]; 97 | } 98 | 99 | function totalSupply() public view returns (uint) { 100 | return _totalSupply; 101 | } 102 | 103 | function approve(address dst, uint amt) external returns (bool) { 104 | _allowance[msg.sender][dst] = amt; 105 | emit Approval(msg.sender, dst, amt); 106 | return true; 107 | } 108 | 109 | function increaseApproval(address dst, uint amt) external returns (bool) { 110 | _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt); 111 | emit Approval(msg.sender, dst, _allowance[msg.sender][dst]); 112 | return true; 113 | } 114 | 115 | function decreaseApproval(address dst, uint amt) external returns (bool) { 116 | uint oldValue = _allowance[msg.sender][dst]; 117 | if (amt > oldValue) { 118 | _allowance[msg.sender][dst] = 0; 119 | } else { 120 | _allowance[msg.sender][dst] = bsub(oldValue, amt); 121 | } 122 | emit Approval(msg.sender, dst, _allowance[msg.sender][dst]); 123 | return true; 124 | } 125 | 126 | function transfer(address dst, uint amt) external returns (bool) { 127 | _move(msg.sender, dst, amt); 128 | return true; 129 | } 130 | 131 | function transferFrom(address src, address dst, uint amt) external returns (bool) { 132 | require(msg.sender == src || amt <= _allowance[src][msg.sender], "ERR_BTOKEN_BAD_CALLER"); 133 | _move(src, dst, amt); 134 | if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) { 135 | _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt); 136 | emit Approval(msg.sender, dst, _allowance[src][msg.sender]); 137 | } 138 | return true; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /contracts/test/TToken.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | // Test Token 17 | // Public mint and burn functions! 18 | 19 | contract TToken { 20 | 21 | bytes32 private _name; 22 | bytes32 private _symbol; 23 | uint8 private _decimals; 24 | 25 | uint internal _totalSupply; 26 | 27 | mapping(address => uint) private _balance; 28 | mapping(address => mapping(address=>uint)) private _allowance; 29 | 30 | event Approval(address indexed src, address indexed dst, uint amt); 31 | event Transfer(address indexed src, address indexed dst, uint amt); 32 | 33 | // Math 34 | function add(uint a, uint b) internal pure returns (uint c) { 35 | require((c = a + b) >= a); 36 | } 37 | function sub(uint a, uint b) internal pure returns (uint c) { 38 | require((c = a - b) <= a); 39 | } 40 | 41 | constructor( 42 | bytes32 name, 43 | bytes32 symbol, 44 | uint8 decimals 45 | ) public { 46 | _name = name; 47 | _symbol = symbol; 48 | _decimals = decimals; 49 | } 50 | 51 | function name() public view returns (bytes32) { 52 | return _name; 53 | } 54 | 55 | function symbol() public view returns (bytes32) { 56 | return _symbol; 57 | } 58 | 59 | function decimals() public view returns(uint8) { 60 | return _decimals; 61 | } 62 | 63 | function _move(address src, address dst, uint amt) internal { 64 | require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL"); 65 | _balance[src] = sub(_balance[src], amt); 66 | _balance[dst] = add(_balance[dst], amt); 67 | emit Transfer(src, dst, amt); 68 | } 69 | 70 | function _push(address to, uint amt) internal { 71 | _move(address(this), to, amt); 72 | } 73 | 74 | function _pull(address from, uint amt) internal { 75 | _move(from, address(this), amt); 76 | } 77 | 78 | function _mint(address dst, uint amt) internal { 79 | _balance[dst] = add(_balance[dst], amt); 80 | _totalSupply = add(_totalSupply, amt); 81 | emit Transfer(address(0), dst, amt); 82 | } 83 | 84 | function allowance(address src, address dst) external view returns (uint) { 85 | return _allowance[src][dst]; 86 | } 87 | 88 | function balanceOf(address whom) external view returns (uint) { 89 | return _balance[whom]; 90 | } 91 | 92 | function totalSupply() public view returns (uint) { 93 | return _totalSupply; 94 | } 95 | 96 | function approve(address dst, uint amt) external returns (bool) { 97 | _allowance[msg.sender][dst] = amt; 98 | emit Approval(msg.sender, dst, amt); 99 | return true; 100 | } 101 | 102 | function mint(address dst, uint amt) public returns (bool) { 103 | _mint(dst, amt); 104 | return true; 105 | } 106 | 107 | function burn(uint amt) public returns (bool) { 108 | require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL"); 109 | _balance[address(this)] = sub(_balance[address(this)], amt); 110 | _totalSupply = sub(_totalSupply, amt); 111 | emit Transfer(address(this), address(0), amt); 112 | return true; 113 | } 114 | 115 | function transfer(address dst, uint amt) external returns (bool) { 116 | _move(msg.sender, dst, amt); 117 | return true; 118 | } 119 | 120 | function transferFrom(address src, address dst, uint amt) external returns (bool) { 121 | require(msg.sender == src || amt <= _allowance[src][msg.sender], "ERR_BTOKEN_BAD_CALLER"); 122 | _move(src, dst, amt); 123 | if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) { 124 | _allowance[src][msg.sender] = sub(_allowance[src][msg.sender], amt); 125 | emit Approval(msg.sender, dst, _allowance[src][msg.sender]); 126 | } 127 | return true; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /contracts/test/TTokenFactory.sol: -------------------------------------------------------------------------------- 1 | // This program is free software: you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation, either version 3 of the License, or 4 | // (at your option) any later version. 5 | 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program. If not, see . 13 | 14 | pragma solidity 0.5.12; 15 | 16 | import "./TToken.sol"; 17 | 18 | contract TTokenFactory { 19 | mapping(bytes32=>TToken) tokens; 20 | function get(bytes32 name) external view returns (TToken) { 21 | return tokens[name]; 22 | } 23 | function build(bytes32 name, bytes32 symbol, uint8 decimals) external returns (TToken) { 24 | tokens[name] = new TToken(name, symbol, decimals); 25 | return tokens[name]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/test/WETH9.sol: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015, 2016, 2017 Dapphub 2 | 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | 16 | pragma solidity 0.5.12; 17 | 18 | contract WETH9 { 19 | string public name = "Wrapped Ether"; 20 | string public symbol = "WETH"; 21 | uint8 public decimals = 18; 22 | 23 | event Approval(address indexed src, address indexed guy, uint wad); 24 | event Transfer(address indexed src, address indexed dst, uint wad); 25 | event Deposit(address indexed dst, uint wad); 26 | event Withdrawal(address indexed src, uint wad); 27 | 28 | mapping (address => uint) public balanceOf; 29 | mapping (address => mapping (address => uint)) public allowance; 30 | 31 | function() external payable { 32 | deposit(); 33 | } 34 | function deposit() public payable { 35 | balanceOf[msg.sender] += msg.value; 36 | emit Deposit(msg.sender, msg.value); 37 | } 38 | function withdraw(uint wad) public { 39 | require(balanceOf[msg.sender] >= wad); 40 | balanceOf[msg.sender] -= wad; 41 | msg.sender.transfer(wad); 42 | emit Withdrawal(msg.sender, wad); 43 | } 44 | 45 | function totalSupply() public view returns (uint) { 46 | return address(this).balance; 47 | } 48 | 49 | function approve(address guy, uint wad) public returns (bool) { 50 | allowance[msg.sender][guy] = wad; 51 | emit Approval(msg.sender, guy, wad); 52 | return true; 53 | } 54 | 55 | function transfer(address dst, uint wad) public returns (bool) { 56 | return transferFrom(msg.sender, dst, wad); 57 | } 58 | 59 | function transferFrom(address src, address dst, uint wad) 60 | public 61 | returns (bool) 62 | { 63 | require(balanceOf[src] >= wad); 64 | 65 | if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { 66 | require(allowance[src][msg.sender] >= wad); 67 | allowance[src][msg.sender] -= wad; 68 | } 69 | 70 | balanceOf[src] -= wad; 71 | balanceOf[dst] += wad; 72 | 73 | emit Transfer(src, dst, wad); 74 | 75 | return true; 76 | } 77 | } 78 | 79 | 80 | /* 81 | GNU GENERAL PUBLIC LICENSE 82 | Version 3, 29 June 2007 83 | 84 | Copyright (C) 2007 Free Software Foundation, Inc. 85 | Everyone is permitted to copy and distribute verbatim copies 86 | of this license document, but changing it is not allowed. 87 | 88 | Preamble 89 | 90 | The GNU General Public License is a free, copyleft license for 91 | software and other kinds of works. 92 | 93 | The licenses for most software and other practical works are designed 94 | to take away your freedom to share and change the works. By contrast, 95 | the GNU General Public License is intended to guarantee your freedom to 96 | share and change all versions of a program--to make sure it remains free 97 | software for all its users. We, the Free Software Foundation, use the 98 | GNU General Public License for most of our software; it applies also to 99 | any other work released this way by its authors. You can apply it to 100 | your programs, too. 101 | 102 | When we speak of free software, we are referring to freedom, not 103 | price. Our General Public Licenses are designed to make sure that you 104 | have the freedom to distribute copies of free software (and charge for 105 | them if you wish), that you receive source code or can get it if you 106 | want it, that you can change the software or use pieces of it in new 107 | free programs, and that you know you can do these things. 108 | 109 | To protect your rights, we need to prevent others from denying you 110 | these rights or asking you to surrender the rights. Therefore, you have 111 | certain responsibilities if you distribute copies of the software, or if 112 | you modify it: responsibilities to respect the freedom of others. 113 | 114 | For example, if you distribute copies of such a program, whether 115 | gratis or for a fee, you must pass on to the recipients the same 116 | freedoms that you received. You must make sure that they, too, receive 117 | or can get the source code. And you must show them these terms so they 118 | know their rights. 119 | 120 | Developers that use the GNU GPL protect your rights with two steps: 121 | (1) assert copyright on the software, and (2) offer you this License 122 | giving you legal permission to copy, distribute and/or modify it. 123 | 124 | For the developers' and authors' protection, the GPL clearly explains 125 | that there is no warranty for this free software. For both users' and 126 | authors' sake, the GPL requires that modified versions be marked as 127 | changed, so that their problems will not be attributed erroneously to 128 | authors of previous versions. 129 | 130 | Some devices are designed to deny users access to install or run 131 | modified versions of the software inside them, although the manufacturer 132 | can do so. This is fundamentally incompatible with the aim of 133 | protecting users' freedom to change the software. The systematic 134 | pattern of such abuse occurs in the area of products for individuals to 135 | use, which is precisely where it is most unacceptable. Therefore, we 136 | have designed this version of the GPL to prohibit the practice for those 137 | products. If such problems arise substantially in other domains, we 138 | stand ready to extend this provision to those domains in future versions 139 | of the GPL, as needed to protect the freedom of users. 140 | 141 | Finally, every program is threatened constantly by software patents. 142 | States should not allow patents to restrict development and use of 143 | software on general-purpose computers, but in those that do, we wish to 144 | avoid the special danger that patents applied to a free program could 145 | make it effectively proprietary. To prevent this, the GPL assures that 146 | patents cannot be used to render the program non-free. 147 | 148 | The precise terms and conditions for copying, distribution and 149 | modification follow. 150 | 151 | TERMS AND CONDITIONS 152 | 153 | 0. Definitions. 154 | 155 | "This License" refers to version 3 of the GNU General Public License. 156 | 157 | "Copyright" also means copyright-like laws that apply to other kinds of 158 | works, such as semiconductor masks. 159 | 160 | "The Program" refers to any copyrightable work licensed under this 161 | License. Each licensee is addressed as "you". "Licensees" and 162 | "recipients" may be individuals or organizations. 163 | 164 | To "modify" a work means to copy from or adapt all or part of the work 165 | in a fashion requiring copyright permission, other than the making of an 166 | exact copy. The resulting work is called a "modified version" of the 167 | earlier work or a work "based on" the earlier work. 168 | 169 | A "covered work" means either the unmodified Program or a work based 170 | on the Program. 171 | 172 | To "propagate" a work means to do anything with it that, without 173 | permission, would make you directly or secondarily liable for 174 | infringement under applicable copyright law, except executing it on a 175 | computer or modifying a private copy. Propagation includes copying, 176 | distribution (with or without modification), making available to the 177 | public, and in some countries other activities as well. 178 | 179 | To "convey" a work means any kind of propagation that enables other 180 | parties to make or receive copies. Mere interaction with a user through 181 | a computer network, with no transfer of a copy, is not conveying. 182 | 183 | An interactive user interface displays "Appropriate Legal Notices" 184 | to the extent that it includes a convenient and prominently visible 185 | feature that (1) displays an appropriate copyright notice, and (2) 186 | tells the user that there is no warranty for the work (except to the 187 | extent that warranties are provided), that licensees may convey the 188 | work under this License, and how to view a copy of this License. If 189 | the interface presents a list of user commands or options, such as a 190 | menu, a prominent item in the list meets this criterion. 191 | 192 | 1. Source Code. 193 | 194 | The "source code" for a work means the preferred form of the work 195 | for making modifications to it. "Object code" means any non-source 196 | form of a work. 197 | 198 | A "Standard Interface" means an interface that either is an official 199 | standard defined by a recognized standards body, or, in the case of 200 | interfaces specified for a particular programming language, one that 201 | is widely used among developers working in that language. 202 | 203 | The "System Libraries" of an executable work include anything, other 204 | than the work as a whole, that (a) is included in the normal form of 205 | packaging a Major Component, but which is not part of that Major 206 | Component, and (b) serves only to enable use of the work with that 207 | Major Component, or to implement a Standard Interface for which an 208 | implementation is available to the public in source code form. A 209 | "Major Component", in this context, means a major essential component 210 | (kernel, window system, and so on) of the specific operating system 211 | (if any) on which the executable work runs, or a compiler used to 212 | produce the work, or an object code interpreter used to run it. 213 | 214 | The "Corresponding Source" for a work in object code form means all 215 | the source code needed to generate, install, and (for an executable 216 | work) run the object code and to modify the work, including scripts to 217 | control those activities. However, it does not include the work's 218 | System Libraries, or general-purpose tools or generally available free 219 | programs which are used unmodified in performing those activities but 220 | which are not part of the work. For example, Corresponding Source 221 | includes interface definition files associated with source files for 222 | the work, and the source code for shared libraries and dynamically 223 | linked subprograms that the work is specifically designed to require, 224 | such as by intimate data communication or control flow between those 225 | subprograms and other parts of the work. 226 | 227 | The Corresponding Source need not include anything that users 228 | can regenerate automatically from other parts of the Corresponding 229 | Source. 230 | 231 | The Corresponding Source for a work in source code form is that 232 | same work. 233 | 234 | 2. Basic Permissions. 235 | 236 | All rights granted under this License are granted for the term of 237 | copyright on the Program, and are irrevocable provided the stated 238 | conditions are met. This License explicitly affirms your unlimited 239 | permission to run the unmodified Program. The output from running a 240 | covered work is covered by this License only if the output, given its 241 | content, constitutes a covered work. This License acknowledges your 242 | rights of fair use or other equivalent, as provided by copyright law. 243 | 244 | You may make, run and propagate covered works that you do not 245 | convey, without conditions so long as your license otherwise remains 246 | in force. You may convey covered works to others for the sole purpose 247 | of having them make modifications exclusively for you, or provide you 248 | with facilities for running those works, provided that you comply with 249 | the terms of this License in conveying all material for which you do 250 | not control copyright. Those thus making or running the covered works 251 | for you must do so exclusively on your behalf, under your direction 252 | and control, on terms that prohibit them from making any copies of 253 | your copyrighted material outside their relationship with you. 254 | 255 | Conveying under any other circumstances is permitted solely under 256 | the conditions stated below. Sublicensing is not allowed; section 10 257 | makes it unnecessary. 258 | 259 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 260 | 261 | No covered work shall be deemed part of an effective technological 262 | measure under any applicable law fulfilling obligations under article 263 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 264 | similar laws prohibiting or restricting circumvention of such 265 | measures. 266 | 267 | When you convey a covered work, you waive any legal power to forbid 268 | circumvention of technological measures to the extent such circumvention 269 | is effected by exercising rights under this License with respect to 270 | the covered work, and you disclaim any intention to limit operation or 271 | modification of the work as a means of enforcing, against the work's 272 | users, your or third parties' legal rights to forbid circumvention of 273 | technological measures. 274 | 275 | 4. Conveying Verbatim Copies. 276 | 277 | You may convey verbatim copies of the Program's source code as you 278 | receive it, in any medium, provided that you conspicuously and 279 | appropriately publish on each copy an appropriate copyright notice; 280 | keep intact all notices stating that this License and any 281 | non-permissive terms added in accord with section 7 apply to the code; 282 | keep intact all notices of the absence of any warranty; and give all 283 | recipients a copy of this License along with the Program. 284 | 285 | You may charge any price or no price for each copy that you convey, 286 | and you may offer support or warranty protection for a fee. 287 | 288 | 5. Conveying Modified Source Versions. 289 | 290 | You may convey a work based on the Program, or the modifications to 291 | produce it from the Program, in the form of source code under the 292 | terms of section 4, provided that you also meet all of these conditions: 293 | 294 | a) The work must carry prominent notices stating that you modified 295 | it, and giving a relevant date. 296 | 297 | b) The work must carry prominent notices stating that it is 298 | released under this License and any conditions added under section 299 | 7. This requirement modifies the requirement in section 4 to 300 | "keep intact all notices". 301 | 302 | c) You must license the entire work, as a whole, under this 303 | License to anyone who comes into possession of a copy. This 304 | License will therefore apply, along with any applicable section 7 305 | additional terms, to the whole of the work, and all its parts, 306 | regardless of how they are packaged. This License gives no 307 | permission to license the work in any other way, but it does not 308 | invalidate such permission if you have separately received it. 309 | 310 | d) If the work has interactive user interfaces, each must display 311 | Appropriate Legal Notices; however, if the Program has interactive 312 | interfaces that do not display Appropriate Legal Notices, your 313 | work need not make them do so. 314 | 315 | A compilation of a covered work with other separate and independent 316 | works, which are not by their nature extensions of the covered work, 317 | and which are not combined with it such as to form a larger program, 318 | in or on a volume of a storage or distribution medium, is called an 319 | "aggregate" if the compilation and its resulting copyright are not 320 | used to limit the access or legal rights of the compilation's users 321 | beyond what the individual works permit. Inclusion of a covered work 322 | in an aggregate does not cause this License to apply to the other 323 | parts of the aggregate. 324 | 325 | 6. Conveying Non-Source Forms. 326 | 327 | You may convey a covered work in object code form under the terms 328 | of sections 4 and 5, provided that you also convey the 329 | machine-readable Corresponding Source under the terms of this License, 330 | in one of these ways: 331 | 332 | a) Convey the object code in, or embodied in, a physical product 333 | (including a physical distribution medium), accompanied by the 334 | Corresponding Source fixed on a durable physical medium 335 | customarily used for software interchange. 336 | 337 | b) Convey the object code in, or embodied in, a physical product 338 | (including a physical distribution medium), accompanied by a 339 | written offer, valid for at least three years and valid for as 340 | long as you offer spare parts or customer support for that product 341 | model, to give anyone who possesses the object code either (1) a 342 | copy of the Corresponding Source for all the software in the 343 | product that is covered by this License, on a durable physical 344 | medium customarily used for software interchange, for a price no 345 | more than your reasonable cost of physically performing this 346 | conveying of source, or (2) access to copy the 347 | Corresponding Source from a network server at no charge. 348 | 349 | c) Convey individual copies of the object code with a copy of the 350 | written offer to provide the Corresponding Source. This 351 | alternative is allowed only occasionally and noncommercially, and 352 | only if you received the object code with such an offer, in accord 353 | with subsection 6b. 354 | 355 | d) Convey the object code by offering access from a designated 356 | place (gratis or for a charge), and offer equivalent access to the 357 | Corresponding Source in the same way through the same place at no 358 | further charge. You need not require recipients to copy the 359 | Corresponding Source along with the object code. If the place to 360 | copy the object code is a network server, the Corresponding Source 361 | may be on a different server (operated by you or a third party) 362 | that supports equivalent copying facilities, provided you maintain 363 | clear directions next to the object code saying where to find the 364 | Corresponding Source. Regardless of what server hosts the 365 | Corresponding Source, you remain obligated to ensure that it is 366 | available for as long as needed to satisfy these requirements. 367 | 368 | e) Convey the object code using peer-to-peer transmission, provided 369 | you inform other peers where the object code and Corresponding 370 | Source of the work are being offered to the general public at no 371 | charge under subsection 6d. 372 | 373 | A separable portion of the object code, whose source code is excluded 374 | from the Corresponding Source as a System Library, need not be 375 | included in conveying the object code work. 376 | 377 | A "User Product" is either (1) a "consumer product", which means any 378 | tangible personal property which is normally used for personal, family, 379 | or household purposes, or (2) anything designed or sold for incorporation 380 | into a dwelling. In determining whether a product is a consumer product, 381 | doubtful cases shall be resolved in favor of coverage. For a particular 382 | product received by a particular user, "normally used" refers to a 383 | typical or common use of that class of product, regardless of the status 384 | of the particular user or of the way in which the particular user 385 | actually uses, or expects or is expected to use, the product. A product 386 | is a consumer product regardless of whether the product has substantial 387 | commercial, industrial or non-consumer uses, unless such uses represent 388 | the only significant mode of use of the product. 389 | 390 | "Installation Information" for a User Product means any methods, 391 | procedures, authorization keys, or other information required to install 392 | and execute modified versions of a covered work in that User Product from 393 | a modified version of its Corresponding Source. The information must 394 | suffice to ensure that the continued functioning of the modified object 395 | code is in no case prevented or interfered with solely because 396 | modification has been made. 397 | 398 | If you convey an object code work under this section in, or with, or 399 | specifically for use in, a User Product, and the conveying occurs as 400 | part of a transaction in which the right of possession and use of the 401 | User Product is transferred to the recipient in perpetuity or for a 402 | fixed term (regardless of how the transaction is characterized), the 403 | Corresponding Source conveyed under this section must be accompanied 404 | by the Installation Information. But this requirement does not apply 405 | if neither you nor any third party retains the ability to install 406 | modified object code on the User Product (for example, the work has 407 | been installed in ROM). 408 | 409 | The requirement to provide Installation Information does not include a 410 | requirement to continue to provide support service, warranty, or updates 411 | for a work that has been modified or installed by the recipient, or for 412 | the User Product in which it has been modified or installed. Access to a 413 | network may be denied when the modification itself materially and 414 | adversely affects the operation of the network or violates the rules and 415 | protocols for communication across the network. 416 | 417 | Corresponding Source conveyed, and Installation Information provided, 418 | in accord with this section must be in a format that is publicly 419 | documented (and with an implementation available to the public in 420 | source code form), and must require no special password or key for 421 | unpacking, reading or copying. 422 | 423 | 7. Additional Terms. 424 | 425 | "Additional permissions" are terms that supplement the terms of this 426 | License by making exceptions from one or more of its conditions. 427 | Additional permissions that are applicable to the entire Program shall 428 | be treated as though they were included in this License, to the extent 429 | that they are valid under applicable law. If additional permissions 430 | apply only to part of the Program, that part may be used separately 431 | under those permissions, but the entire Program remains governed by 432 | this License without regard to the additional permissions. 433 | 434 | When you convey a copy of a covered work, you may at your option 435 | remove any additional permissions from that copy, or from any part of 436 | it. (Additional permissions may be written to require their own 437 | removal in certain cases when you modify the work.) You may place 438 | additional permissions on material, added by you to a covered work, 439 | for which you have or can give appropriate copyright permission. 440 | 441 | Notwithstanding any other provision of this License, for material you 442 | add to a covered work, you may (if authorized by the copyright holders of 443 | that material) supplement the terms of this License with terms: 444 | 445 | a) Disclaiming warranty or limiting liability differently from the 446 | terms of sections 15 and 16 of this License; or 447 | 448 | b) Requiring preservation of specified reasonable legal notices or 449 | author attributions in that material or in the Appropriate Legal 450 | Notices displayed by works containing it; or 451 | 452 | c) Prohibiting misrepresentation of the origin of that material, or 453 | requiring that modified versions of such material be marked in 454 | reasonable ways as different from the original version; or 455 | 456 | d) Limiting the use for publicity purposes of names of licensors or 457 | authors of the material; or 458 | 459 | e) Declining to grant rights under trademark law for use of some 460 | trade names, trademarks, or service marks; or 461 | 462 | f) Requiring indemnification of licensors and authors of that 463 | material by anyone who conveys the material (or modified versions of 464 | it) with contractual assumptions of liability to the recipient, for 465 | any liability that these contractual assumptions directly impose on 466 | those licensors and authors. 467 | 468 | All other non-permissive additional terms are considered "further 469 | restrictions" within the meaning of section 10. If the Program as you 470 | received it, or any part of it, contains a notice stating that it is 471 | governed by this License along with a term that is a further 472 | restriction, you may remove that term. If a license document contains 473 | a further restriction but permits relicensing or conveying under this 474 | License, you may add to a covered work material governed by the terms 475 | of that license document, provided that the further restriction does 476 | not survive such relicensing or conveying. 477 | 478 | If you add terms to a covered work in accord with this section, you 479 | must place, in the relevant source files, a statement of the 480 | additional terms that apply to those files, or a notice indicating 481 | where to find the applicable terms. 482 | 483 | Additional terms, permissive or non-permissive, may be stated in the 484 | form of a separately written license, or stated as exceptions; 485 | the above requirements apply either way. 486 | 487 | 8. Termination. 488 | 489 | You may not propagate or modify a covered work except as expressly 490 | provided under this License. Any attempt otherwise to propagate or 491 | modify it is void, and will automatically terminate your rights under 492 | this License (including any patent licenses granted under the third 493 | paragraph of section 11). 494 | 495 | However, if you cease all violation of this License, then your 496 | license from a particular copyright holder is reinstated (a) 497 | provisionally, unless and until the copyright holder explicitly and 498 | finally terminates your license, and (b) permanently, if the copyright 499 | holder fails to notify you of the violation by some reasonable means 500 | prior to 60 days after the cessation. 501 | 502 | Moreover, your license from a particular copyright holder is 503 | reinstated permanently if the copyright holder notifies you of the 504 | violation by some reasonable means, this is the first time you have 505 | received notice of violation of this License (for any work) from that 506 | copyright holder, and you cure the violation prior to 30 days after 507 | your receipt of the notice. 508 | 509 | Termination of your rights under this section does not terminate the 510 | licenses of parties who have received copies or rights from you under 511 | this License. If your rights have been terminated and not permanently 512 | reinstated, you do not qualify to receive new licenses for the same 513 | material under section 10. 514 | 515 | 9. Acceptance Not Required for Having Copies. 516 | 517 | You are not required to accept this License in order to receive or 518 | run a copy of the Program. Ancillary propagation of a covered work 519 | occurring solely as a consequence of using peer-to-peer transmission 520 | to receive a copy likewise does not require acceptance. However, 521 | nothing other than this License grants you permission to propagate or 522 | modify any covered work. These actions infringe copyright if you do 523 | not accept this License. Therefore, by modifying or propagating a 524 | covered work, you indicate your acceptance of this License to do so. 525 | 526 | 10. Automatic Licensing of Downstream Recipients. 527 | 528 | Each time you convey a covered work, the recipient automatically 529 | receives a license from the original licensors, to run, modify and 530 | propagate that work, subject to this License. You are not responsible 531 | for enforcing compliance by third parties with this License. 532 | 533 | An "entity transaction" is a transaction transferring control of an 534 | organization, or substantially all assets of one, or subdividing an 535 | organization, or merging organizations. If propagation of a covered 536 | work results from an entity transaction, each party to that 537 | transaction who receives a copy of the work also receives whatever 538 | licenses to the work the party's predecessor in interest had or could 539 | give under the previous paragraph, plus a right to possession of the 540 | Corresponding Source of the work from the predecessor in interest, if 541 | the predecessor has it or can get it with reasonable efforts. 542 | 543 | You may not impose any further restrictions on the exercise of the 544 | rights granted or affirmed under this License. For example, you may 545 | not impose a license fee, royalty, or other charge for exercise of 546 | rights granted under this License, and you may not initiate litigation 547 | (including a cross-claim or counterclaim in a lawsuit) alleging that 548 | any patent claim is infringed by making, using, selling, offering for 549 | sale, or importing the Program or any portion of it. 550 | 551 | 11. Patents. 552 | 553 | A "contributor" is a copyright holder who authorizes use under this 554 | License of the Program or a work on which the Program is based. The 555 | work thus licensed is called the contributor's "contributor version". 556 | 557 | A contributor's "essential patent claims" are all patent claims 558 | owned or controlled by the contributor, whether already acquired or 559 | hereafter acquired, that would be infringed by some manner, permitted 560 | by this License, of making, using, or selling its contributor version, 561 | but do not include claims that would be infringed only as a 562 | consequence of further modification of the contributor version. For 563 | purposes of this definition, "control" includes the right to grant 564 | patent sublicenses in a manner consistent with the requirements of 565 | this License. 566 | 567 | Each contributor grants you a non-exclusive, worldwide, royalty-free 568 | patent license under the contributor's essential patent claims, to 569 | make, use, sell, offer for sale, import and otherwise run, modify and 570 | propagate the contents of its contributor version. 571 | 572 | In the following three paragraphs, a "patent license" is any express 573 | agreement or commitment, however denominated, not to enforce a patent 574 | (such as an express permission to practice a patent or covenant not to 575 | sue for patent infringement). To "grant" such a patent license to a 576 | party means to make such an agreement or commitment not to enforce a 577 | patent against the party. 578 | 579 | If you convey a covered work, knowingly relying on a patent license, 580 | and the Corresponding Source of the work is not available for anyone 581 | to copy, free of charge and under the terms of this License, through a 582 | publicly available network server or other readily accessible means, 583 | then you must either (1) cause the Corresponding Source to be so 584 | available, or (2) arrange to deprive yourself of the benefit of the 585 | patent license for this particular work, or (3) arrange, in a manner 586 | consistent with the requirements of this License, to extend the patent 587 | license to downstream recipients. "Knowingly relying" means you have 588 | actual knowledge that, but for the patent license, your conveying the 589 | covered work in a country, or your recipient's use of the covered work 590 | in a country, would infringe one or more identifiable patents in that 591 | country that you have reason to believe are valid. 592 | 593 | If, pursuant to or in connection with a single transaction or 594 | arrangement, you convey, or propagate by procuring conveyance of, a 595 | covered work, and grant a patent license to some of the parties 596 | receiving the covered work authorizing them to use, propagate, modify 597 | or convey a specific copy of the covered work, then the patent license 598 | you grant is automatically extended to all recipients of the covered 599 | work and works based on it. 600 | 601 | A patent license is "discriminatory" if it does not include within 602 | the scope of its coverage, prohibits the exercise of, or is 603 | conditioned on the non-exercise of one or more of the rights that are 604 | specifically granted under this License. You may not convey a covered 605 | work if you are a party to an arrangement with a third party that is 606 | in the business of distributing software, under which you make payment 607 | to the third party based on the extent of your activity of conveying 608 | the work, and under which the third party grants, to any of the 609 | parties who would receive the covered work from you, a discriminatory 610 | patent license (a) in connection with copies of the covered work 611 | conveyed by you (or copies made from those copies), or (b) primarily 612 | for and in connection with specific products or compilations that 613 | contain the covered work, unless you entered into that arrangement, 614 | or that patent license was granted, prior to 28 March 2007. 615 | 616 | Nothing in this License shall be construed as excluding or limiting 617 | any implied license or other defenses to infringement that may 618 | otherwise be available to you under applicable patent law. 619 | 620 | 12. No Surrender of Others' Freedom. 621 | 622 | If conditions are imposed on you (whether by court order, agreement or 623 | otherwise) that contradict the conditions of this License, they do not 624 | excuse you from the conditions of this License. If you cannot convey a 625 | covered work so as to satisfy simultaneously your obligations under this 626 | License and any other pertinent obligations, then as a consequence you may 627 | not convey it at all. For example, if you agree to terms that obligate you 628 | to collect a royalty for further conveying from those to whom you convey 629 | the Program, the only way you could satisfy both those terms and this 630 | License would be to refrain entirely from conveying the Program. 631 | 632 | 13. Use with the GNU Affero General Public License. 633 | 634 | Notwithstanding any other provision of this License, you have 635 | permission to link or combine any covered work with a work licensed 636 | under version 3 of the GNU Affero General Public License into a single 637 | combined work, and to convey the resulting work. The terms of this 638 | License will continue to apply to the part which is the covered work, 639 | but the special requirements of the GNU Affero General Public License, 640 | section 13, concerning interaction through a network will apply to the 641 | combination as such. 642 | 643 | 14. Revised Versions of this License. 644 | 645 | The Free Software Foundation may publish revised and/or new versions of 646 | the GNU General Public License from time to time. Such new versions will 647 | be similar in spirit to the present version, but may differ in detail to 648 | address new problems or concerns. 649 | 650 | Each version is given a distinguishing version number. If the 651 | Program specifies that a certain numbered version of the GNU General 652 | Public License "or any later version" applies to it, you have the 653 | option of following the terms and conditions either of that numbered 654 | version or of any later version published by the Free Software 655 | Foundation. If the Program does not specify a version number of the 656 | GNU General Public License, you may choose any version ever published 657 | by the Free Software Foundation. 658 | 659 | If the Program specifies that a proxy can decide which future 660 | versions of the GNU General Public License can be used, that proxy's 661 | public statement of acceptance of a version permanently authorizes you 662 | to choose that version for the Program. 663 | 664 | Later license versions may give you additional or different 665 | permissions. However, no additional obligations are imposed on any 666 | author or copyright holder as a result of your choosing to follow a 667 | later version. 668 | 669 | 15. Disclaimer of Warranty. 670 | 671 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 672 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 673 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 674 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 675 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 676 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 677 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 678 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 679 | 680 | 16. Limitation of Liability. 681 | 682 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 683 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 684 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 685 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 686 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 687 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 688 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 689 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 690 | SUCH DAMAGES. 691 | 692 | 17. Interpretation of Sections 15 and 16. 693 | 694 | If the disclaimer of warranty and limitation of liability provided 695 | above cannot be given local legal effect according to their terms, 696 | reviewing courts shall apply local law that most closely approximates 697 | an absolute waiver of all civil liability in connection with the 698 | Program, unless a warranty or assumption of liability accompanies a 699 | copy of the Program in return for a fee. 700 | 701 | END OF TERMS AND CONDITIONS 702 | 703 | How to Apply These Terms to Your New Programs 704 | 705 | If you develop a new program, and you want it to be of the greatest 706 | possible use to the public, the best way to achieve this is to make it 707 | free software which everyone can redistribute and change under these terms. 708 | 709 | To do so, attach the following notices to the program. It is safest 710 | to attach them to the start of each source file to most effectively 711 | state the exclusion of warranty; and each file should have at least 712 | the "copyright" line and a pointer to where the full notice is found. 713 | 714 | 715 | Copyright (C) 716 | 717 | This program is free software: you can redistribute it and/or modify 718 | it under the terms of the GNU General Public License as published by 719 | the Free Software Foundation, either version 3 of the License, or 720 | (at your option) any later version. 721 | 722 | This program is distributed in the hope that it will be useful, 723 | but WITHOUT ANY WARRANTY; without even the implied warranty of 724 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 725 | GNU General Public License for more details. 726 | 727 | You should have received a copy of the GNU General Public License 728 | along with this program. If not, see . 729 | 730 | Also add information on how to contact you by electronic and paper mail. 731 | 732 | If the program does terminal interaction, make it output a short 733 | notice like this when it starts in an interactive mode: 734 | 735 | Copyright (C) 736 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 737 | This is free software, and you are welcome to redistribute it 738 | under certain conditions; type `show c' for details. 739 | 740 | The hypothetical commands `show w' and `show c' should show the appropriate 741 | parts of the General Public License. Of course, your program's commands 742 | might be different; for a GUI interface, you would use an "about box". 743 | 744 | You should also get your employer (if you work as a programmer) or school, 745 | if any, to sign a "copyright disclaimer" for the program, if necessary. 746 | For more information on this, and how to apply and follow the GNU GPL, see 747 | . 748 | 749 | The GNU General Public License does not permit incorporating your program 750 | into proprietary programs. If your program is a subroutine library, you 751 | may consider it more useful to permit linking proprietary applications with 752 | the library. If this is what you want to do, use the GNU Lesser General 753 | Public License instead of this License. But first, please read 754 | . 755 | 756 | */ -------------------------------------------------------------------------------- /lib/calc_comparisons.js: -------------------------------------------------------------------------------- 1 | const Decimal = require('decimal.js'); 2 | 3 | function calcRelativeDiff(expected, actual) { 4 | return ((Decimal(expected).minus(Decimal(actual))).div(expected)).abs(); 5 | } 6 | 7 | function calcSpotPrice(tokenBalanceIn, tokenWeightIn, tokenBalanceOut, tokenWeightOut, swapFee) { 8 | const numer = Decimal(tokenBalanceIn).div(Decimal(tokenWeightIn)); 9 | const denom = Decimal(tokenBalanceOut).div(Decimal(tokenWeightOut)); 10 | const ratio = numer.div(denom); 11 | const scale = Decimal(1).div(Decimal(1).sub(Decimal(swapFee))); 12 | const spotPrice = ratio.mul(scale); 13 | return spotPrice; 14 | } 15 | 16 | function calcOutGivenIn(tokenBalanceIn, tokenWeightIn, tokenBalanceOut, tokenWeightOut, tokenAmountIn, swapFee) { 17 | const weightRatio = Decimal(tokenWeightIn).div(Decimal(tokenWeightOut)); 18 | const adjustedIn = Decimal(tokenAmountIn).times((Decimal(1).minus(Decimal(swapFee)))); 19 | const y = Decimal(tokenBalanceIn).div(Decimal(tokenBalanceIn).plus(adjustedIn)); 20 | const foo = y.pow(weightRatio); 21 | const bar = Decimal(1).minus(foo); 22 | const tokenAmountOut = Decimal(tokenBalanceOut).times(bar); 23 | return tokenAmountOut; 24 | } 25 | 26 | function calcInGivenOut(tokenBalanceIn, tokenWeightIn, tokenBalanceOut, tokenWeightOut, tokenAmountOut, swapFee) { 27 | const weightRatio = Decimal(tokenWeightOut).div(Decimal(tokenWeightIn)); 28 | const diff = Decimal(tokenBalanceOut).minus(tokenAmountOut); 29 | const y = Decimal(tokenBalanceOut).div(diff); 30 | const foo = y.pow(weightRatio).minus(Decimal(1)); 31 | const tokenAmountIn = (Decimal(tokenBalanceIn).times(foo)).div(Decimal(1).minus(Decimal(swapFee))); 32 | return tokenAmountIn; 33 | } 34 | 35 | function calcPoolOutGivenSingleIn(tokenBalanceIn, tokenWeightIn, poolSupply, totalWeight, tokenAmountIn, swapFee) { 36 | const normalizedWeight = Decimal(tokenWeightIn).div(Decimal(totalWeight)); 37 | const zaz = Decimal(1).sub(Decimal(normalizedWeight)).mul(Decimal(swapFee)); 38 | const tokenAmountInAfterFee = Decimal(tokenAmountIn).mul(Decimal(1).sub(zaz)); 39 | const newTokenBalanceIn = Decimal(tokenBalanceIn).add(tokenAmountInAfterFee); 40 | const tokenInRatio = newTokenBalanceIn.div(Decimal(tokenBalanceIn)); 41 | const poolRatio = tokenInRatio.pow(normalizedWeight); 42 | const newPoolSupply = poolRatio.mul(Decimal(poolSupply)); 43 | const poolAmountOut = newPoolSupply.sub(Decimal(poolSupply)); 44 | return poolAmountOut; 45 | } 46 | 47 | function calcSingleInGivenPoolOut(tokenBalanceIn, tokenWeightIn, poolSupply, totalWeight, poolAmountOut, swapFee) { 48 | const normalizedWeight = Decimal(tokenWeightIn).div(Decimal(totalWeight)); 49 | const newPoolSupply = Decimal(poolSupply).plus(Decimal(poolAmountOut)); 50 | const poolRatio = newPoolSupply.div(Decimal(poolSupply)); 51 | const boo = Decimal(1).div(normalizedWeight); 52 | const tokenInRatio = poolRatio.pow(boo); 53 | const newTokenBalanceIn = tokenInRatio.mul(Decimal(tokenBalanceIn)); 54 | const tokenAmountInAfterFee = newTokenBalanceIn.sub(Decimal(tokenBalanceIn)); 55 | const zar = (Decimal(1).sub(normalizedWeight)).mul(Decimal(swapFee)); 56 | const tokenAmountIn = tokenAmountInAfterFee.div(Decimal(1).sub(zar)); 57 | return tokenAmountIn; 58 | } 59 | 60 | module.exports = { 61 | calcSpotPrice, 62 | calcOutGivenIn, 63 | calcInGivenOut, 64 | calcPoolOutGivenSingleIn, 65 | calcSingleInGivenPoolOut, 66 | calcRelativeDiff, 67 | }; 68 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require('Migrations'); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_proxy.js: -------------------------------------------------------------------------------- 1 | const ExchangeProxy = artifacts.require("ExchangeProxy"); 2 | const WETH9 = artifacts.require("WETH9"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | if (network == 'development' || network == 'coverage') { 6 | await deployer.deploy(WETH9); 7 | await deployer.deploy(ExchangeProxy, WETH9.address); 8 | } else if (network == 'kovan-fork' || network == 'kovan') { 9 | deployer.deploy(ExchangeProxy, '0xd0A1E359811322d97991E03f863a0C30C2cF029C'); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /migrations/3_test_factories.js: -------------------------------------------------------------------------------- 1 | const TTokenFactory = artifacts.require("TTokenFactory"); 2 | const BFactory = artifacts.require("BFactory"); 3 | 4 | module.exports = async function(deployer, network, accounts) { 5 | if (network == 'development' || network == 'coverage') { 6 | deployer.deploy(TTokenFactory); 7 | deployer.deploy(BFactory); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "balancer-exchange-proxy", 4 | "version": "0.0.7", 5 | "license": "GPL-3.0-only", 6 | "description": "SOR exchange proxy", 7 | "scripts": { 8 | "compile": "truffle compile", 9 | "testrpc": "ganache-cli --deterministic --gasLimit 10000000 --defaultBalanceEther 500", 10 | "test": "truffle test", 11 | "test:verbose": "VERBOSE=true truffle test", 12 | "coverage": "yarn solidity-coverage", 13 | "lint": "eslint .", 14 | "lint:contracts": "solhint contracts/*.sol" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/balancer-labs/exchange-proxy.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/balancer-labs/exchange-proxy/issues" 22 | }, 23 | "homepage": "https://github.com/balancer-labs/exchange-proxy#readme", 24 | "devDependencies": { 25 | "chai": "^4.2.0", 26 | "coveralls": "^3.0.8", 27 | "eslint": "^6.7.1", 28 | "eslint-config-airbnb": "^18.0.1", 29 | "eslint-plugin-import": "^2.18.2", 30 | "eslint-plugin-jsx-a11y": "^6.2.3", 31 | "eslint-plugin-react": "^7.17.0", 32 | "ganache-core": "^2.6.1", 33 | "mocha": "^6.2.0", 34 | "solhint": "^2.3.0", 35 | "solidity-coverage": "^0.6.7", 36 | "standard": "^14.0.2", 37 | "truffle": "^5.0.41", 38 | "truffle-assertions": "^0.9.1", 39 | "web3": "^1.2.0" 40 | }, 41 | "dependencies": { 42 | "decimal.js": "^10.2.0", 43 | "ganache-cli": "^6.7.0", 44 | "global": "^4.4.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/swap.js: -------------------------------------------------------------------------------- 1 | const Decimal = require('decimal.js'); 2 | const { calcOutGivenIn, calcInGivenOut, calcRelativeDiff } = require('../lib/calc_comparisons'); 3 | 4 | const ExchangeProxy = artifacts.require('ExchangeProxy'); 5 | const TToken = artifacts.require('TToken'); 6 | const TTokenFactory = artifacts.require('TTokenFactory'); 7 | const BFactory = artifacts.require('BFactory'); 8 | const BPool = artifacts.require('BPool'); 9 | const Weth9 = artifacts.require('WETH9'); 10 | const errorDelta = 10 ** -8; 11 | const verbose = process.env.VERBOSE; 12 | 13 | contract('ExchangeProxy', async (accounts) => { 14 | const admin = accounts[0]; 15 | const nonAdmin = accounts[1]; 16 | const { toHex } = web3.utils; 17 | const { toWei } = web3.utils; 18 | const { fromWei } = web3.utils; 19 | 20 | const MAX = web3.utils.toTwosComplement(-1); 21 | 22 | describe('Batch Swaps', () => { 23 | let factory; 24 | let proxy; let PROXY; 25 | let tokens; 26 | let pool1; let pool2; let pool3; 27 | let POOL1; let POOL2; let POOL3; 28 | let weth; let dai; let mkr; 29 | let WETH; let DAI; let MKR; 30 | 31 | before(async () => { 32 | weth = await Weth9.deployed(); 33 | WETH = weth.address; 34 | 35 | proxy = await ExchangeProxy.deployed(); 36 | PROXY = proxy.address; 37 | tokens = await TTokenFactory.deployed(); 38 | factory = await BFactory.deployed(); 39 | 40 | await tokens.build(toHex('DAI'), toHex('DAI'), 18); 41 | await tokens.build(toHex('MKR'), toHex('MKR'), 18); 42 | 43 | DAI = await tokens.get.call(toHex('DAI')); 44 | MKR = await tokens.get.call(toHex('MKR')); 45 | 46 | dai = await TToken.at(DAI); 47 | mkr = await TToken.at(MKR); 48 | 49 | await weth.deposit({ value: toWei('25') }); 50 | await dai.mint(admin, toWei('10000')); 51 | await mkr.mint(admin, toWei('20')); 52 | 53 | await weth.deposit({ from: nonAdmin, value: toWei('25') }); 54 | await dai.mint(nonAdmin, toWei('10000')); 55 | await mkr.mint(nonAdmin, toWei('20')); 56 | 57 | POOL1 = await factory.newBPool.call(); // this works fine in clean room 58 | await factory.newBPool(); 59 | pool1 = await BPool.at(POOL1); 60 | 61 | POOL2 = await factory.newBPool.call(); // this works fine in clean room 62 | await factory.newBPool(); 63 | pool2 = await BPool.at(POOL2); 64 | 65 | POOL3 = await factory.newBPool.call(); // this works fine in clean room 66 | await factory.newBPool(); 67 | pool3 = await BPool.at(POOL3); 68 | 69 | await weth.approve(PROXY, MAX, { from: nonAdmin }); 70 | await dai.approve(PROXY, MAX, { from: nonAdmin }); 71 | await mkr.approve(PROXY, MAX, { from: nonAdmin }); 72 | 73 | await weth.approve(POOL1, MAX); 74 | await dai.approve(POOL1, MAX); 75 | await mkr.approve(POOL1, MAX); 76 | 77 | await weth.approve(POOL2, MAX); 78 | await dai.approve(POOL2, MAX); 79 | await mkr.approve(POOL2, MAX); 80 | 81 | await weth.approve(POOL3, MAX); 82 | await dai.approve(POOL3, MAX); 83 | await mkr.approve(POOL3, MAX); 84 | 85 | await pool1.bind(WETH, toWei('6'), toWei('5')); 86 | await pool1.bind(DAI, toWei('1200'), toWei('5')); 87 | await pool1.bind(MKR, toWei('2'), toWei('5')); 88 | await pool1.finalize(toWei('100')); 89 | 90 | await pool2.bind(WETH, toWei('2'), toWei('10')); 91 | await pool2.bind(DAI, toWei('800'), toWei('20')); 92 | await pool2.finalize(toWei('100')); 93 | 94 | await pool3.bind(WETH, toWei('15'), toWei('5')); 95 | await pool3.bind(DAI, toWei('2500'), toWei('5')); 96 | await pool3.bind(MKR, toWei('5'), toWei('5')); 97 | await pool3.finalize(toWei('100')); 98 | }); 99 | 100 | it('batchSwapExactIn dry', async () => { 101 | const swaps = [ 102 | [ 103 | POOL1, 104 | toWei('0.5'), 105 | toWei('0'), 106 | MAX, 107 | ], 108 | [ 109 | POOL2, 110 | toWei('0.5'), 111 | toWei('0'), 112 | MAX, 113 | ], 114 | [ 115 | POOL3, 116 | toWei('1'), 117 | toWei('0'), 118 | MAX, 119 | ], 120 | ]; 121 | const swapFee = fromWei(await pool1.getSwapFee()); 122 | const totalAmountOut = await proxy.batchSwapExactIn.call( 123 | swaps, WETH, DAI, toWei('2'), toWei('0'), 124 | { from: nonAdmin }, 125 | ); 126 | 127 | const pool1Out = calcOutGivenIn(6, 5, 1200, 5, 0.5, swapFee); 128 | const pool2Out = calcOutGivenIn(2, 10, 800, 20, 0.5, swapFee); 129 | const pool3Out = calcOutGivenIn(15, 5, 2500, 5, 1, swapFee); 130 | 131 | const expectedTotalOut = pool1Out.plus(pool2Out).plus(pool3Out); 132 | 133 | const relDif = calcRelativeDiff(expectedTotalOut, Decimal(fromWei(totalAmountOut))); 134 | 135 | if (verbose) { 136 | console.log('batchSwapExactIn'); 137 | console.log(`expected: ${expectedTotalOut})`); 138 | console.log(`actual : ${fromWei(totalAmountOut)})`); 139 | console.log(`relDif : ${relDif})`); 140 | } 141 | 142 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 143 | }); 144 | 145 | it('batchSwapExactOut dry', async () => { 146 | const swaps = [ 147 | [ 148 | POOL1, 149 | toWei('1'), 150 | toWei('100'), 151 | MAX, 152 | ], 153 | [ 154 | POOL2, 155 | toWei('1'), 156 | toWei('100'), 157 | MAX, 158 | ], 159 | [ 160 | POOL3, 161 | toWei('5'), 162 | toWei('500'), 163 | MAX, 164 | ], 165 | ]; 166 | 167 | const swapFee = fromWei(await pool1.getSwapFee()); 168 | const totalAmountIn = await proxy.batchSwapExactOut.call( 169 | swaps, WETH, DAI, toWei('7'), 170 | { from: nonAdmin }, 171 | ); 172 | 173 | const pool1In = calcInGivenOut(6, 5, 1200, 5, 100, swapFee); 174 | const pool2In = calcInGivenOut(2, 10, 800, 20, 100, swapFee); 175 | const pool3In = calcInGivenOut(15, 5, 2500, 5, 500, swapFee); 176 | 177 | const expectedTotalIn = pool1In.plus(pool2In).plus(pool3In); 178 | 179 | const relDif = calcRelativeDiff(expectedTotalIn, Decimal(fromWei(totalAmountIn))); 180 | if (verbose) { 181 | console.log('batchSwapExactOut'); 182 | console.log(`expected: ${expectedTotalIn})`); 183 | console.log(`actual : ${fromWei(totalAmountIn)})`); 184 | console.log(`relDif : ${relDif})`); 185 | } 186 | 187 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 188 | }); 189 | 190 | it('batchEthInSwapExactIn dry', async () => { 191 | const swaps = [ 192 | [ 193 | POOL1, 194 | toWei('0.5'), 195 | toWei('0'), 196 | MAX, 197 | ], 198 | [ 199 | POOL2, 200 | toWei('0.5'), 201 | toWei('0'), 202 | MAX, 203 | ], 204 | [ 205 | POOL3, 206 | toWei('1'), 207 | toWei('0'), 208 | MAX, 209 | ], 210 | ]; 211 | 212 | const swapFee = fromWei(await pool1.getSwapFee()); 213 | const totalAmountOut = await proxy.batchEthInSwapExactIn.call( 214 | swaps, DAI, toWei('0'), 215 | { from: nonAdmin, value: toWei('2') }, 216 | ); 217 | 218 | const pool1Out = calcOutGivenIn(6, 5, 1200, 5, 0.5, swapFee); 219 | const pool2Out = calcOutGivenIn(2, 10, 800, 20, 0.5, swapFee); 220 | const pool3Out = calcOutGivenIn(15, 5, 2500, 5, 1, swapFee); 221 | 222 | const expectedTotalOut = pool1Out.plus(pool2Out).plus(pool3Out); 223 | 224 | const relDif = calcRelativeDiff(expectedTotalOut, Decimal(fromWei(totalAmountOut))); 225 | if (verbose) { 226 | console.log('batchEthInSwapExactIn'); 227 | console.log(`expected: ${expectedTotalOut})`); 228 | console.log(`actual : ${fromWei(totalAmountOut)})`); 229 | console.log(`relDif : ${relDif})`); 230 | } 231 | 232 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 233 | }); 234 | 235 | it('batchEthOutSwapExactIn dry', async () => { 236 | const swaps = [ 237 | [ 238 | POOL1, 239 | toWei('30'), 240 | toWei('0'), 241 | MAX, 242 | ], 243 | [ 244 | POOL2, 245 | toWei('45'), 246 | toWei('0'), 247 | MAX, 248 | ], 249 | [ 250 | POOL3, 251 | toWei('75'), 252 | toWei('0'), 253 | MAX, 254 | ], 255 | ]; 256 | 257 | const swapFee = fromWei(await pool1.getSwapFee()); 258 | const totalAmountOut = await proxy.batchEthOutSwapExactIn.call( 259 | swaps, DAI, toWei('150'), toWei('0.5'), 260 | { from: nonAdmin }, 261 | ); 262 | 263 | const pool1Out = calcOutGivenIn(1200, 5, 6, 5, 30, swapFee); 264 | const pool2Out = calcOutGivenIn(800, 20, 2, 10, 45, swapFee); 265 | const pool3Out = calcOutGivenIn(2500, 5, 15, 5, 75, swapFee); 266 | 267 | const expectedTotalOut = pool1Out.plus(pool2Out).plus(pool3Out); 268 | 269 | const relDif = calcRelativeDiff(expectedTotalOut, Decimal(fromWei(totalAmountOut))); 270 | if (verbose) { 271 | console.log('batchEthOutSwapExactIn'); 272 | console.log(`expected: ${expectedTotalOut})`); 273 | console.log(`actual : ${fromWei(totalAmountOut)})`); 274 | console.log(`relDif : ${relDif})`); 275 | } 276 | 277 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 278 | }); 279 | 280 | it('batchEthInSwapExactOut dry', async () => { 281 | const swaps = [ 282 | [ 283 | POOL1, 284 | toWei('1'), 285 | toWei('100'), 286 | MAX, 287 | ], 288 | [ 289 | POOL2, 290 | toWei('1'), 291 | toWei('100'), 292 | MAX, 293 | ], 294 | [ 295 | POOL3, 296 | toWei('5'), 297 | toWei('500'), 298 | MAX, 299 | ], 300 | ]; 301 | 302 | const swapFee = fromWei(await pool1.getSwapFee()); 303 | const totalAmountIn = await proxy.batchEthInSwapExactOut.call( 304 | swaps, DAI, 305 | { from: nonAdmin, value: toWei('7.5') }, 306 | ); 307 | 308 | const pool1In = calcInGivenOut(6, 5, 1200, 5, 100, swapFee); 309 | const pool2In = calcInGivenOut(2, 10, 800, 20, 100, swapFee); 310 | const pool3In = calcInGivenOut(15, 5, 2500, 5, 500, swapFee); 311 | 312 | const expectedTotalIn = pool1In.plus(pool2In).plus(pool3In); 313 | 314 | const relDif = calcRelativeDiff(expectedTotalIn, Decimal(fromWei(totalAmountIn))); 315 | if (verbose) { 316 | console.log('batchEthInSwapExactOut'); 317 | console.log(`expected: ${expectedTotalIn})`); 318 | console.log(`actual : ${fromWei(totalAmountIn)})`); 319 | console.log(`relDif : ${relDif})`); 320 | } 321 | 322 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 323 | }); 324 | 325 | it('batchEthOutSwapExactOut dry', async () => { 326 | const swaps = [ 327 | [ 328 | POOL1, 329 | toWei('150'), 330 | toWei('0.5'), 331 | MAX, 332 | ], 333 | [ 334 | POOL2, 335 | toWei('150'), 336 | toWei('0.5'), 337 | MAX, 338 | ], 339 | [ 340 | POOL3, 341 | toWei('550'), 342 | toWei('2.5'), 343 | MAX, 344 | ], 345 | ]; 346 | 347 | const swapFee = fromWei(await pool1.getSwapFee()); 348 | const totalAmountIn = await proxy.batchEthOutSwapExactOut.call( 349 | swaps, DAI, toWei('750'), 350 | { from: nonAdmin }, 351 | ); 352 | 353 | const pool1In = calcInGivenOut(1200, 5, 6, 5, 0.5, swapFee); 354 | const pool2In = calcInGivenOut(800, 20, 2, 10, 0.5, swapFee); 355 | const pool3In = calcInGivenOut(2500, 5, 15, 5, 2.5, swapFee); 356 | 357 | const expectedTotalIn = pool1In.plus(pool2In).plus(pool3In); 358 | 359 | const relDif = calcRelativeDiff(expectedTotalIn, Decimal(fromWei(totalAmountIn))); 360 | if (verbose) { 361 | console.log('batchEthOutSwapExactOut'); 362 | console.log(`expected: ${expectedTotalIn})`); 363 | console.log(`actual : ${fromWei(totalAmountIn)})`); 364 | console.log(`relDif : ${relDif})`); 365 | } 366 | 367 | assert.isAtMost(relDif.toNumber(), (errorDelta * swaps.length)); 368 | }); 369 | }); 370 | }); 371 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | module.exports = { 22 | /** 23 | * Networks define how you connect to your ethereum client and let you set the 24 | * defaults web3 uses to send transactions. If you don't specify one truffle 25 | * will spin up a development blockchain for you on port 9545 when you 26 | * run `develop` or `test`. You can ask a truffle command to use a specific 27 | * network from the command line, e.g 28 | * 29 | * $ truffle test --network 30 | */ 31 | 32 | networks: { 33 | development: { 34 | host: 'localhost', // Localhost (default: none) 35 | port: 8545, // Standard Ethereum port (default: none) 36 | network_id: '*', // Any network (default: none) 37 | gas: 10000000, 38 | }, 39 | kovan: { 40 | host: 'localhost', 41 | port: 8545, 42 | network_id: 42, 43 | gasPrice: 10000000000, // 10 gwei 44 | gas: 6900000, 45 | from: process.env.ETH_FROM, 46 | }, 47 | }, 48 | 49 | // Set default mocha options here, use special reporters etc. 50 | mocha: { 51 | // timeout: 100000 52 | }, 53 | 54 | // Configure your compilers 55 | compilers: { 56 | solc: { 57 | version: '0.5.12', 58 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 59 | settings: { // See the solidity docs for advice about optimization and evmVersion 60 | optimizer: { 61 | enabled: true, 62 | runs: 200, 63 | }, 64 | evmVersion: 'byzantium', 65 | }, 66 | }, 67 | }, 68 | }; 69 | --------------------------------------------------------------------------------