├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── readme.md ├── scripts ├── build-env-addresses.sh └── deploy.sh ├── src ├── RwaConduit.sol ├── RwaLiquidationOracle.sol ├── RwaSpell.sol ├── RwaToken.sol ├── RwaUrn.sol ├── interfaces │ ├── CatAbstract.sol │ ├── ChainlogAbstract.sol │ ├── DSChiefAbstract.sol │ ├── DSPauseAbstract.sol │ ├── DSSpellAbstract.sol │ ├── DSTokenAbstract.sol │ ├── DSValueAbstract.sol │ ├── DaiAbstract.sol │ ├── DaiJoinAbstract.sol │ ├── EndAbstract.sol │ ├── FlipAbstract.sol │ ├── FlipperMomAbstract.sol │ ├── GemJoinAbstract.sol │ ├── IlkRegistryAbstract.sol │ ├── JugAbstract.sol │ ├── OsmMomAbstract.sol │ ├── PotAbstract.sol │ ├── SpotAbstract.sol │ ├── VatAbstract.sol │ └── VowAbstract.sol ├── invariants │ ├── RwaTokenInvariant.sol │ └── config.yaml └── test │ ├── RwaSpell.t.sol │ ├── RwaUrn.t.sol │ ├── addresses_kovan.sol │ └── rates.sol └── test-rwaspell.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/ds-test"] 2 | path = lib/ds-test 3 | url = https://github.com/dapphub/ds-test 4 | [submodule "lib/ds-math"] 5 | path = lib/ds-math 6 | url = https://github.com/dapphub/ds-math 7 | [submodule "lib/dss-gem-joins"] 8 | path = lib/dss-gem-joins 9 | url = https://github.com/makerdao/dss-gem-joins 10 | [submodule "lib/ds-value"] 11 | path = lib/ds-value 12 | url = https://github.com/dapphub/ds-value 13 | [submodule "lib/dss"] 14 | path = lib/dss 15 | url = https://github.com/makerdao/dss 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 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 Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all :; dapp --use solc:0.6.12 build 2 | clean :; dapp clean 3 | test :; ./test-rwaspell.sh ${match} 4 | deploy :; echo "use deploy-mainnet, deploy-kovan, or deploy-goerli" 5 | deploy-kovan :; ./scripts/deploy.sh kovan 6 | deploy-goerli :; ./scripts/deploy.sh goerli 7 | deploy-mainnet :; ./scripts/deploy.sh ethlive 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # equipment for off-chain asset backed lending in MakerDAO 2 | 3 | ## components: 4 | 5 | - `RwaLiquidationOracle`: which acts as a liquidation beacon for an off-chain enforcer. 6 | - `RwaUrn`: which facilitates borrowing of DAI, delivering to a designated account. 7 | - `RwaOutputConduit` and `RwaInputConduit`: which disburse and repay DAI 8 | - `RwaSpell`: which deploys and activates a new collateral type 9 | - `RwaToken`: which represents the RWA collateral in the system 10 | 11 | ## spells: 12 | 13 | The following can be found in `src/RwaSpell.sol`: 14 | - `RwaSpell`: which deploys and configures the RWA collateral in MakerDAO in accordance with MIP21 15 | 16 | The following can be found in `src/test/RwaSpell.t.sol`: 17 | 18 | - `TellSpell`: which allows MakerDAO governance to initiate liquidation proceedings. 19 | - `CureSpell`: which allows MakerDAO governance to dismiss liquidation proceedings. 20 | - `CullSpell`: which allows MakerDAO governance to write off a loan which was in liquidation. 21 | 22 | ## deploy 23 | 24 | ### kovan 25 | ``` 26 | make deploy-kovan 27 | ``` 28 | 29 | ### goerli 30 | ``` 31 | make deploy-goerli 32 | ``` 33 | 34 | ### mainnet 35 | ``` 36 | make deploy-mainnet 37 | ``` 38 | -------------------------------------------------------------------------------- /scripts/build-env-addresses.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # build-env-addresses.sh 3 | 4 | # Loads an address file from a URL and adds checksummed contract addresses to the environment 5 | # Source this script to set envvars 6 | # `. ./scripts/build-env-addresses.sh [ URL | network ]` 7 | # Run as script and write to file to save exports to source file 8 | # `./scripts/build-env-addresses.sh [ URL | network ] > env-addresses-network` 9 | 10 | function validate_url() { 11 | if [[ $(curl -I ${1} 2>&1 | grep -E 'HTTP/(1.1|2) [23][0-9]+') ]]; then 12 | return 0 13 | else 14 | return 1 15 | fi 16 | } 17 | 18 | if [[ $_ != "${0}" ]]; then 19 | # Script was run as source 20 | SOURCED=1 21 | fi 22 | 23 | if [ -z "${1}" ]; then 24 | echo "Please specify the network [ ethlive, kovan, goerli ] or a file path as an argument." 25 | [ -z "${PS1}" ] && exit || return 26 | fi 27 | 28 | if [ "${1}" == "kovan" ]; then 29 | URL="https://changelog.makerdao.com/releases/kovan/active/contracts.json" 30 | elif [ "${1}" == "goerli" ]; then 31 | URL="https://gist.githubusercontent.com/gbalabasquer/b26dbda6c228f412bbcc5d34560f7241/raw/d57a914e6129ffe7d99b9394ab746ec5931372e8/goerli_addresses.json" 32 | elif [ "${1}" == "ethlive" ]; then 33 | URL="https://changelog.makerdao.com/releases/mainnet/active/contracts.json" 34 | else 35 | URL="${1}" 36 | fi 37 | 38 | if validate_url "${URL}"; then 39 | echo "# Deployment addresses generated from:" 40 | echo "# ${URL}" 41 | ADDRESSES_RAW="$(curl -Ls "${URL}")" 42 | else 43 | echo "# Invalid URL ${URL}" 44 | [ -z "${PS1}" ] && exit || return 45 | fi 46 | 47 | OUTPUT=$(jq -r 'to_entries | map(.key + "|" + (.value | tostring)) | .[]' <<<"${ADDRESSES_RAW}" | \ 48 | while IFS='|' read -r key value; do 49 | PAIR="${key}=$(seth --to-checksum-address "${value}")" 50 | echo "${PAIR}" 51 | done 52 | ) 53 | 54 | for pair in $OUTPUT 55 | do 56 | if [[ $SOURCED == 1 ]]; then 57 | echo "${pair}" 58 | export "${pair?}" 59 | else 60 | echo "export ${pair}" 61 | fi 62 | done 63 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # ./scripts/deploy.sh 4 | 5 | set -e 6 | 7 | [[ "$1" == "ethlive" || "$1" == "kovan" || "$1" == "goerli" ]] || { 8 | echo "Please specify the network [ ethlive, kovan, goerli ]."; 9 | exit 1; 10 | } 11 | [[ "$ETH_RPC_URL" && "$(seth chain)" == "$1" ]] || { 12 | echo "Please set a $1 ETH_RPC_URL"; 13 | exit 1; 14 | } 15 | 16 | # shellcheck disable=SC1091 17 | source ./scripts/build-env-addresses.sh "$1" > /dev/null 2>&1 18 | 19 | export ETH_GAS=6000000 20 | 21 | [[ -z "$NAME" ]] && NAME="RWA-001"; 22 | [[ -z "$SYMBOL" ]] && SYMBOL="RWA001"; 23 | # 24 | # WARNING (2021-09-08): The system cannot currently accomodate any LETTER beyond 25 | # "A". To add more letters, we will need to update the PIP naming convention 26 | # to include the letter. Unfortunately, while fixing this on-chain and in our 27 | # code would be easy, RWA001 integrations may already be using the old PIP 28 | # naming convention. So, before we can have new letters we must: 29 | # 1. Change the existing PIP naming convention 30 | # 2. Change all the places that depend on that convention (this script included) 31 | # 3. Make sure all integrations are ready to accomodate that new PIP name. 32 | # 33 | [[ -z "$LETTER" ]] && LETTER="A"; 34 | [[ -z "$OPERATOR" ]] && OPERATOR="0xD23beB204328D7337e3d2Fb9F150501fDC633B0e" 35 | # [[ -z "$MIP21_LIQUIDATION_ORACLE" ]] && MIP21_LIQUIDATION_ORACLE="0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF" 36 | 37 | # for the TinlakeManager 38 | # RWA_OUTPUT_CONDUIT=$OPERATOR 39 | # RWA_INPUT_CONDUIT=$OPERATOR 40 | 41 | # kovan, goerli only 42 | TRUST1="0xDA0FaB0700A4389F6E6679aBAb1692B4601ce9bf" 43 | TRUST2="0xdA0C0de01d90A5933692Edf03c7cE946C7c50445" 44 | 45 | ILK="${SYMBOL}-${LETTER}" 46 | ILK_ENCODED=$(seth --to-bytes32 "$(seth --from-ascii ${ILK})") 47 | 48 | # build it 49 | dapp --use solc:0.5.12 build 50 | 51 | # tokenize it 52 | RWA_TOKEN=$(dapp create "src/RwaToken.sol:RwaToken" \"$NAME\" \"$SYMBOL\") 53 | seth send "${RWA_TOKEN}" 'transfer(address,uint256)' "$OPERATOR" "$(seth --to-wei 1.0 ether)" 54 | 55 | # route it 56 | [[ -z "$RWA_OUTPUT_CONDUIT" ]] && RWA_OUTPUT_CONDUIT=$(dapp create RwaOutputConduit "${MCD_GOV}" "${MCD_DAI}") 57 | 58 | if [ "$RWA_OUTPUT_CONDUIT" != "$OPERATOR" ]; then 59 | seth send "${RWA_OUTPUT_CONDUIT}" 'rely(address)' "${MCD_PAUSE_PROXY}" 60 | if [ "$1" == "kovan" ]; then 61 | seth send "${RWA_OUTPUT_CONDUIT}" 'kiss(address)' "${TRUST1}" 62 | seth send "${RWA_OUTPUT_CONDUIT}" 'kiss(address)' "${TRUST2}" 63 | fi 64 | seth send "${RWA_OUTPUT_CONDUIT}" 'deny(address)' "${ETH_FROM}" 65 | fi 66 | 67 | # join it 68 | RWA_JOIN=$(dapp create AuthGemJoin "${MCD_VAT}" "${ILK_ENCODED}" "${RWA_TOKEN}") 69 | seth send "${RWA_JOIN}" 'rely(address)' "${MCD_PAUSE_PROXY}" 70 | 71 | # urn it 72 | RWA_URN=$(dapp create RwaUrn "${MCD_VAT}" "${MCD_JUG}" "${RWA_JOIN}" "${MCD_JOIN_DAI}" "${RWA_OUTPUT_CONDUIT}") 73 | seth send "${RWA_URN}" 'rely(address)' "${MCD_PAUSE_PROXY}" 74 | seth send "${RWA_URN}" 'deny(address)' "${ETH_FROM}" 75 | 76 | # rely it 77 | seth send "${RWA_JOIN}" 'rely(address)' "${RWA_URN}" 78 | 79 | # deny it 80 | seth send "${RWA_JOIN}" 'deny(address)' "${ETH_FROM}" 81 | 82 | # connect it 83 | [[ -z "$RWA_INPUT_CONDUIT" ]] && RWA_INPUT_CONDUIT=$(dapp create RwaInputConduit "${MCD_GOV}" "${MCD_DAI}" "${RWA_URN}") 84 | 85 | # price it 86 | if [ -z "$MIP21_LIQUIDATION_ORACLE" ]; then 87 | MIP21_LIQUIDATION_ORACLE=$(dapp create RwaLiquidationOracle "${MCD_VAT}" "${MCD_VOW}") 88 | seth send "${MIP21_LIQUIDATION_ORACLE}" 'rely(address)' "${MCD_PAUSE_PROXY}" 89 | seth send "${MIP21_LIQUIDATION_ORACLE}" 'deny(address)' "${ETH_FROM}" 90 | fi 91 | 92 | # print it 93 | echo "OPERATOR: ${OPERATOR}" 94 | if [ "$1" == "kovan" ]; then 95 | echo "TRUST1: ${TRUST1}" 96 | echo "TRUST2: ${TRUST2}" 97 | fi 98 | echo "ILK: ${ILK}" 99 | echo "${SYMBOL}: ${RWA_TOKEN}" 100 | echo "MCD_JOIN_${SYMBOL}_${LETTER}: ${RWA_JOIN}" 101 | echo "${SYMBOL}_${LETTER}_URN: ${RWA_URN}" 102 | echo "${SYMBOL}_${LETTER}_INPUT_CONDUIT: ${RWA_INPUT_CONDUIT}" 103 | echo "${SYMBOL}_${LETTER}_OUTPUT_CONDUIT: ${RWA_OUTPUT_CONDUIT}" 104 | echo "MIP21_LIQUIDATION_ORACLE: ${MIP21_LIQUIDATION_ORACLE}" 105 | 106 | # technologic 107 | # https://www.youtube.com/watch?v=D8K90hX4PrE 108 | -------------------------------------------------------------------------------- /src/RwaConduit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaConduit.sol -- In and out conduits for Dai 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "./interfaces/DSTokenAbstract.sol"; 24 | 25 | contract RwaInputConduit { 26 | DSTokenAbstract public gov; 27 | DSTokenAbstract public dai; 28 | address public to; 29 | 30 | event Push(address indexed to, uint256 wad); 31 | 32 | constructor(address _gov, address _dai, address _to) public { 33 | gov = DSTokenAbstract(_gov); 34 | dai = DSTokenAbstract(_dai); 35 | to = _to; 36 | } 37 | 38 | function push() external { 39 | require(gov.balanceOf(msg.sender) > 0, "RwaConduit/no-gov"); 40 | uint256 balance = dai.balanceOf(address(this)); 41 | emit Push(to, balance); 42 | dai.transfer(to, balance); 43 | } 44 | } 45 | 46 | contract RwaOutputConduit { 47 | // --- auth --- 48 | mapping (address => uint256) public wards; 49 | mapping (address => uint256) public can; 50 | function rely(address usr) external auth { 51 | wards[usr] = 1; 52 | emit Rely(usr); 53 | } 54 | function deny(address usr) external auth { 55 | wards[usr] = 0; 56 | emit Deny(usr); 57 | } 58 | modifier auth { 59 | require(wards[msg.sender] == 1, "RwaConduit/not-authorized"); 60 | _; 61 | } 62 | function hope(address usr) external auth { 63 | can[usr] = 1; 64 | emit Hope(usr); 65 | } 66 | function nope(address usr) external auth { 67 | can[usr] = 0; 68 | emit Nope(usr); 69 | } 70 | modifier operator { 71 | require(can[msg.sender] == 1, "RwaConduit/not-operator"); 72 | _; 73 | } 74 | 75 | DSTokenAbstract public gov; 76 | DSTokenAbstract public dai; 77 | 78 | address public to; 79 | mapping (address => uint256) public bud; 80 | 81 | // Events 82 | event Rely(address indexed usr); 83 | event Deny(address indexed usr); 84 | event Hope(address indexed usr); 85 | event Nope(address indexed usr); 86 | event Kiss(address indexed who); 87 | event Diss(address indexed who); 88 | event Pick(address indexed who); 89 | event Push(address indexed to, uint256 wad); 90 | 91 | constructor(address _gov, address _dai) public { 92 | wards[msg.sender] = 1; 93 | gov = DSTokenAbstract(_gov); 94 | dai = DSTokenAbstract(_dai); 95 | emit Rely(msg.sender); 96 | } 97 | 98 | // --- administration --- 99 | function kiss(address who) public auth { 100 | bud[who] = 1; 101 | emit Kiss(who); 102 | } 103 | function diss(address who) public auth { 104 | if (to == who) to = address(0); 105 | bud[who] = 0; 106 | emit Diss(who); 107 | } 108 | 109 | // --- routing --- 110 | function pick(address who) public operator { 111 | require(bud[who] == 1 || who == address(0), "RwaConduit/not-bud"); 112 | to = who; 113 | emit Pick(who); 114 | } 115 | function push() external { 116 | require(to != address(0), "RwaConduit/to-not-set"); 117 | require(gov.balanceOf(msg.sender) > 0, "RwaConduit/no-gov"); 118 | uint256 balance = dai.balanceOf(address(this)); 119 | emit Push(to, balance); 120 | dai.transfer(to, balance); 121 | to = address(0); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/RwaLiquidationOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaLiquidationOracle.sol -- Pricing data for RWA 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "./interfaces/VatAbstract.sol"; 24 | import "ds-value/value.sol"; 25 | 26 | contract RwaLiquidationOracle { 27 | // --- auth --- 28 | mapping (address => uint256) public wards; 29 | function rely(address usr) external auth { 30 | wards[usr] = 1; 31 | emit Rely(usr); 32 | } 33 | function deny(address usr) external auth { 34 | wards[usr] = 0; 35 | emit Deny(usr); 36 | } 37 | modifier auth { 38 | require(wards[msg.sender] == 1, "RwaOracle/not-authorized"); 39 | _; 40 | } 41 | 42 | // --- math --- 43 | function add(uint48 x, uint48 y) internal pure returns (uint48 z) { 44 | require((z = x + y) >= x); 45 | } 46 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 47 | require(y == 0 || (z = x * y) / y == x); 48 | } 49 | 50 | VatAbstract public vat; 51 | address public vow; 52 | struct Ilk { 53 | string doc; // hash of borrower's agreement with MakerDAO 54 | address pip; // DSValue tracking nominal loan value 55 | uint48 tau; // pre-agreed remediation period 56 | uint48 toc; // timestamp when liquidation initiated 57 | } 58 | mapping (bytes32 => Ilk) public ilks; 59 | 60 | // Events 61 | event Rely(address indexed usr); 62 | event Deny(address indexed usr); 63 | event File(bytes32 indexed what, address data); 64 | event Init(bytes32 indexed ilk, uint256 val, string doc, uint48 tau); 65 | event Bump(bytes32 indexed ilk, uint256 val); 66 | event Tell(bytes32 indexed ilk); 67 | event Cure(bytes32 indexed ilk); 68 | event Cull(bytes32 indexed ilk, address indexed urn); 69 | 70 | constructor(address vat_, address vow_) public { 71 | vat = VatAbstract(vat_); 72 | vow = vow_; 73 | wards[msg.sender] = 1; 74 | emit Rely(msg.sender); 75 | emit File("vow", vow_); 76 | } 77 | 78 | // --- administration --- 79 | function file(bytes32 what, address data) external auth { 80 | if (what == "vow") { vow = data; } 81 | else revert("RwaOracle/unrecognised-param"); 82 | emit File(what, data); 83 | } 84 | 85 | function init(bytes32 ilk, uint256 val, string calldata doc, uint48 tau) external auth { 86 | // doc, and tau can be amended, but tau cannot decrease 87 | require(tau >= ilks[ilk].tau, "RwaOracle/decreasing-tau"); 88 | ilks[ilk].doc = doc; 89 | ilks[ilk].tau = tau; 90 | if (ilks[ilk].pip == address(0)) { 91 | DSValue pip = new DSValue(); 92 | ilks[ilk].pip = address(pip); 93 | pip.poke(bytes32(val)); 94 | } else { 95 | val = uint256(DSValue(ilks[ilk].pip).read()); 96 | } 97 | emit Init(ilk, val, doc, tau); 98 | } 99 | 100 | // --- valuation adjustment --- 101 | function bump(bytes32 ilk, uint256 val) external auth { 102 | DSValue pip = DSValue(ilks[ilk].pip); 103 | require(address(pip) != address(0), "RwaOracle/unknown-ilk"); 104 | require(ilks[ilk].toc == 0, "RwaOracle/in-remediation"); 105 | // only cull can decrease 106 | require(val >= uint256(pip.read()), "RwaOracle/decreasing-val"); 107 | pip.poke(bytes32(val)); 108 | emit Bump(ilk, val); 109 | } 110 | // --- liquidation --- 111 | function tell(bytes32 ilk) external auth { 112 | (,,,uint256 line,) = vat.ilks(ilk); 113 | // DC must be set to zero first 114 | require(line == 0, "RwaOracle/nonzero-line"); 115 | require(ilks[ilk].pip != address(0), "RwaOracle/unknown-ilk"); 116 | ilks[ilk].toc = uint48(block.timestamp); 117 | emit Tell(ilk); 118 | } 119 | // --- remediation --- 120 | function cure(bytes32 ilk) external auth { 121 | require(ilks[ilk].pip != address(0), "RwaOracle/unknown-ilk"); 122 | require(ilks[ilk].toc > 0, "RwaOracle/not-in-remediation"); 123 | ilks[ilk].toc = 0; 124 | emit Cure(ilk); 125 | } 126 | // --- write-off --- 127 | function cull(bytes32 ilk, address urn) external auth { 128 | require(ilks[ilk].pip != address(0), "RwaOracle/unknown-ilk"); 129 | require(block.timestamp >= add(ilks[ilk].toc, ilks[ilk].tau), "RwaOracle/early-cull"); 130 | 131 | DSValue(ilks[ilk].pip).poke(bytes32(uint256(0))); 132 | 133 | (uint256 ink, uint256 art) = vat.urns(ilk, urn); 134 | require(ink <= 2 ** 255, "RwaOracle/overflow"); 135 | require(art <= 2 ** 255, "RwaOracle/overflow"); 136 | 137 | vat.grab(ilk, 138 | address(urn), 139 | address(this), 140 | address(vow), 141 | -int256(ink), 142 | -int256(art)); 143 | emit Cull(ilk, urn); 144 | } 145 | 146 | // --- liquidation check --- 147 | // to be called by off-chain parties (e.g. a trustee) to check the standing of the loan 148 | function good(bytes32 ilk) external view returns (bool) { 149 | require(ilks[ilk].pip != address(0), "RwaOracle/unknown-ilk"); 150 | // tell not called or still in remediation period 151 | return (ilks[ilk].toc == 0 || block.timestamp < add(ilks[ilk].toc, ilks[ilk].tau)); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/RwaSpell.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaSpell.sol -- Spell for onboarding RWA collateral 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "./interfaces/VatAbstract.sol"; 24 | import "./interfaces/DSPauseAbstract.sol"; 25 | import "./interfaces/JugAbstract.sol"; 26 | import "./interfaces/SpotAbstract.sol"; 27 | import "./interfaces/GemJoinAbstract.sol"; 28 | import "./interfaces/DSTokenAbstract.sol"; 29 | import "./interfaces/ChainlogAbstract.sol"; 30 | 31 | interface RwaLiquidationLike { 32 | function wards(address) external returns (uint256); 33 | function ilks(bytes32) external returns (string memory,address,uint48,uint48); 34 | function rely(address) external; 35 | function deny(address) external; 36 | function init(bytes32, uint256, string calldata, uint48) external; 37 | function tell(bytes32) external; 38 | function cure(bytes32) external; 39 | function cull(bytes32) external; 40 | function good(bytes32) external view; 41 | } 42 | 43 | interface RwaOutputConduitLike { 44 | function wards(address) external returns (uint256); 45 | function can(address) external returns (uint256); 46 | function rely(address) external; 47 | function deny(address) external; 48 | function hope(address) external; 49 | function nope(address) external; 50 | function bud(address) external returns (uint256); 51 | function kiss(address) external; 52 | function diss(address) external; 53 | function pick(address) external; 54 | function push() external; 55 | } 56 | 57 | interface RwaUrnLike { 58 | function hope(address) external; 59 | } 60 | 61 | contract SpellAction { 62 | // KOVAN ADDRESSES 63 | // 64 | // The contracts in this list should correspond to MCD core contracts, verify 65 | // against the current release list at: 66 | // https://changelog.makerdao.com/releases/kovan/latest/contracts.json 67 | ChainlogAbstract constant CHANGELOG = 68 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 69 | 70 | /* 71 | OPERATOR: 0xD23beB204328D7337e3d2Fb9F150501fDC633B0e 72 | TRUST1: 0xda0fab060e6cc7b1C0AA105d29Bd50D71f036711 73 | TRUST2: 0xDA0111100cb6080b43926253AB88bE719C60Be13 74 | ILK: RWA001-A 75 | RWA001: 0x8F9A8cbBdfb93b72d646c8DEd6B4Fe4D86B315cB 76 | MCD_JOIN_RWA001_A: 0x029A554f252373e146f76Fa1a7455f73aBF4d38e 77 | RWA001_A_URN: 0x3Ba90D86f7E3218C48b7E0FCa959EcF43d9A30F4 78 | RWA001_A_INPUT_CONDUIT: 0xe37673730F03060922a2Bd8eC5987AfE3eA16a05 79 | RWA001_A_OUTPUT_CONDUIT: 0xc54fEee07421EAB8000AC8c921c0De9DbfbE780B 80 | MIP21_LIQUIDATION_ORACLE: 0x2881c5dF65A8D81e38f7636122aFb456514804CC 81 | */ 82 | address constant RWA001_OPERATOR = 0xD23beB204328D7337e3d2Fb9F150501fDC633B0e; 83 | address constant RWA001_GEM = 0x8F9A8cbBdfb93b72d646c8DEd6B4Fe4D86B315cB; 84 | address constant MCD_JOIN_RWA001_A = 0x029A554f252373e146f76Fa1a7455f73aBF4d38e; 85 | address constant RWA001_A_URN = 0x3Ba90D86f7E3218C48b7E0FCa959EcF43d9A30F4; 86 | address constant RWA001_A_INPUT_CONDUIT = 0xe37673730F03060922a2Bd8eC5987AfE3eA16a05; 87 | address constant RWA001_A_OUTPUT_CONDUIT = 0xc54fEee07421EAB8000AC8c921c0De9DbfbE780B; 88 | address constant MIP21_LIQUIDATION_ORACLE = 0x2881c5dF65A8D81e38f7636122aFb456514804CC; 89 | 90 | uint256 constant THREE_PCT_RATE = 1000000000937303470807876289; 91 | 92 | // precision 93 | uint256 constant public THOUSAND = 10 ** 3; 94 | uint256 constant public MILLION = 10 ** 6; 95 | uint256 constant public WAD = 10 ** 18; 96 | uint256 constant public RAY = 10 ** 27; 97 | uint256 constant public RAD = 10 ** 45; 98 | 99 | uint256 constant RWA001_A_INITIAL_DC = 1000 * RAD; 100 | uint256 constant RWA001_A_INITIAL_PRICE = 1060 * WAD; 101 | 102 | // MIP13c3-SP4 Declaration of Intent & Commercial Points - 103 | // Off-Chain Asset Backed Lender to onboard Real World Assets 104 | // as Collateral for a DAI loan 105 | // 106 | // https://ipfs.io/ipfs/QmdmAUTU3sd9VkdfTZNQM6krc9jsKgF2pz7W1qvvfJo1xk 107 | string constant DOC = "QmdmAUTU3sd9VkdfTZNQM6krc9jsKgF2pz7W1qvvfJo1xk"; 108 | 109 | function execute() external { 110 | address MCD_VAT = ChainlogAbstract(CHANGELOG).getAddress("MCD_VAT"); 111 | address MCD_JUG = ChainlogAbstract(CHANGELOG).getAddress("MCD_JUG"); 112 | address MCD_SPOT = ChainlogAbstract(CHANGELOG).getAddress("MCD_SPOT"); 113 | 114 | // RWA001-A collateral deploy 115 | 116 | // Set ilk bytes32 variable 117 | bytes32 ilk = "RWA001-A"; 118 | 119 | // add RWA-001 contract to the changelog 120 | CHANGELOG.setAddress("RWA001", RWA001_GEM); 121 | CHANGELOG.setAddress("MCD_JOIN_RWA001_A", MCD_JOIN_RWA001_A); 122 | CHANGELOG.setAddress("MIP21_LIQUIDATION_ORACLE", MIP21_LIQUIDATION_ORACLE); 123 | CHANGELOG.setAddress("RWA001_A_URN", RWA001_A_URN); 124 | CHANGELOG.setAddress("RWA001_A_INPUT_CONDUIT", RWA001_A_INPUT_CONDUIT); 125 | CHANGELOG.setAddress("RWA001_A_OUTPUT_CONDUIT", RWA001_A_OUTPUT_CONDUIT); 126 | 127 | // bump changelog version 128 | // TODO make sure to update this version on mainnet 129 | // CHANGELOG.setVersion("1.2.9"); 130 | 131 | // Sanity checks 132 | require(GemJoinAbstract(MCD_JOIN_RWA001_A).vat() == MCD_VAT, "join-vat-not-match"); 133 | require(GemJoinAbstract(MCD_JOIN_RWA001_A).ilk() == ilk, "join-ilk-not-match"); 134 | require(GemJoinAbstract(MCD_JOIN_RWA001_A).gem() == RWA001_GEM, "join-gem-not-match"); 135 | require(GemJoinAbstract(MCD_JOIN_RWA001_A).dec() == DSTokenAbstract(RWA001_GEM).decimals(), "join-dec-not-match"); 136 | 137 | // init the RwaLiquidationOracle 138 | // doc: "doc" 139 | // tau: 5 minutes 140 | RwaLiquidationLike(MIP21_LIQUIDATION_ORACLE).init( 141 | ilk, RWA001_A_INITIAL_PRICE, DOC, 300 142 | ); 143 | (,address pip,,) = RwaLiquidationLike(MIP21_LIQUIDATION_ORACLE).ilks(ilk); 144 | CHANGELOG.setAddress("PIP_RWA001", pip); 145 | 146 | // Set price feed for RWA001 147 | SpotAbstract(MCD_SPOT).file(ilk, "pip", pip); 148 | 149 | // Init RWA-001 in Vat 150 | VatAbstract(MCD_VAT).init(ilk); 151 | // Init RWA-001 in Jug 152 | JugAbstract(MCD_JUG).init(ilk); 153 | 154 | // Allow RWA-001 Join to modify Vat registry 155 | VatAbstract(MCD_VAT).rely(MCD_JOIN_RWA001_A); 156 | 157 | // Allow RwaLiquidationOracle to modify Vat registry 158 | VatAbstract(MCD_VAT).rely(MIP21_LIQUIDATION_ORACLE); 159 | 160 | // 1000 debt ceiling 161 | VatAbstract(MCD_VAT).file(ilk, "line", RWA001_A_INITIAL_DC); 162 | VatAbstract(MCD_VAT).file("Line", VatAbstract(MCD_VAT).Line() + RWA001_A_INITIAL_DC); 163 | 164 | // No dust 165 | // VatAbstract(MCD_VAT).file(ilk, "dust", 0) 166 | 167 | // 3% stability fee 168 | JugAbstract(MCD_JUG).file(ilk, "duty", THREE_PCT_RATE); 169 | 170 | // collateralization ratio 100% 171 | SpotAbstract(MCD_SPOT).file(ilk, "mat", RAY); 172 | 173 | // poke the spotter to pull in a price 174 | SpotAbstract(MCD_SPOT).poke(ilk); 175 | 176 | // give the urn permissions on the join adapter 177 | GemJoinAbstract(MCD_JOIN_RWA001_A).rely(RWA001_A_URN); 178 | 179 | // set up the urn 180 | RwaUrnLike(RWA001_A_URN).hope(RWA001_OPERATOR); 181 | 182 | // set up output conduit 183 | RwaOutputConduitLike(RWA001_A_OUTPUT_CONDUIT).hope(RWA001_OPERATOR); 184 | // could potentially kiss some BD addresses if they are available 185 | } 186 | } 187 | 188 | contract RwaSpell { 189 | 190 | ChainlogAbstract constant CHANGELOG = 191 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 192 | 193 | DSPauseAbstract public pause = 194 | DSPauseAbstract(CHANGELOG.getAddress("MCD_PAUSE")); 195 | address public action; 196 | bytes32 public tag; 197 | uint256 public eta; 198 | bytes public sig; 199 | uint256 public expiration; 200 | bool public done; 201 | 202 | string constant public description = "Kovan Spell Deploy"; 203 | 204 | constructor() public { 205 | sig = abi.encodeWithSignature("execute()"); 206 | action = address(new SpellAction()); 207 | bytes32 _tag; 208 | address _action = action; 209 | assembly { _tag := extcodehash(_action) } 210 | tag = _tag; 211 | expiration = block.timestamp + 30 days; 212 | } 213 | 214 | function schedule() public { 215 | require(block.timestamp <= expiration, "This contract has expired"); 216 | require(eta == 0, "This spell has already been scheduled"); 217 | eta = block.timestamp + DSPauseAbstract(pause).delay(); 218 | pause.plot(action, tag, sig, eta); 219 | } 220 | 221 | function cast() public { 222 | require(!done, "spell-already-cast"); 223 | done = true; 224 | pause.exec(action, tag, sig, eta); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/RwaToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaToken.sol -- Collateral token for RWA 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | contract RwaToken { 24 | // --- ERC20 Data --- 25 | string public name; 26 | string public symbol; 27 | 28 | uint8 public constant decimals = 18; 29 | uint256 public totalSupply; 30 | 31 | mapping (address => uint256) public balanceOf; 32 | mapping (address => mapping (address => uint256)) public allowance; 33 | 34 | event Approval(address indexed src, address indexed guy, uint256 wad); 35 | event Transfer(address indexed src, address indexed dst, uint256 wad); 36 | 37 | // --- Math --- 38 | uint256 constant WAD = 10 ** 18; 39 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 40 | require((z = x + y) >= x); 41 | } 42 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 43 | require((z = x - y) <= x); 44 | } 45 | 46 | constructor(string memory name_, string memory symbol_) public { 47 | balanceOf[msg.sender] = 1 * WAD; 48 | name = name_; 49 | symbol = symbol_; 50 | totalSupply = 1 * WAD; 51 | } 52 | 53 | // --- Token --- 54 | function transfer(address dst, uint256 wad) external returns (bool) { 55 | return transferFrom(msg.sender, dst, wad); 56 | } 57 | function transferFrom(address src, address dst, uint256 wad) 58 | public returns (bool) 59 | { 60 | require(balanceOf[src] >= wad, "RwaToken/insufficient-balance"); 61 | if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) { 62 | require(allowance[src][msg.sender] >= wad, "RwaToken/insufficient-allowance"); 63 | allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); 64 | } 65 | balanceOf[src] = sub(balanceOf[src], wad); 66 | balanceOf[dst] = add(balanceOf[dst], wad); 67 | emit Transfer(src, dst, wad); 68 | return true; 69 | } 70 | function approve(address usr, uint256 wad) external returns (bool) { 71 | allowance[msg.sender][usr] = wad; 72 | emit Approval(msg.sender, usr, wad); 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/RwaUrn.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaUrn.sol -- RWA urn manager 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "./interfaces/VatAbstract.sol"; 24 | import "./interfaces/JugAbstract.sol"; 25 | import "./interfaces/DSTokenAbstract.sol"; 26 | import "./interfaces/GemJoinAbstract.sol"; 27 | import "./interfaces/DaiJoinAbstract.sol"; 28 | import "./interfaces/DaiAbstract.sol"; 29 | 30 | contract RwaUrn { 31 | // --- auth --- 32 | mapping (address => uint256) public wards; 33 | mapping (address => uint256) public can; 34 | function rely(address usr) external auth { 35 | wards[usr] = 1; 36 | emit Rely(usr); 37 | } 38 | function deny(address usr) external auth { 39 | wards[usr] = 0; 40 | emit Deny(usr); 41 | } 42 | modifier auth { 43 | require(wards[msg.sender] == 1, "RwaUrn/not-authorized"); 44 | _; 45 | } 46 | function hope(address usr) external auth { 47 | can[usr] = 1; 48 | emit Hope(usr); 49 | } 50 | function nope(address usr) external auth { 51 | can[usr] = 0; 52 | emit Nope(usr); 53 | } 54 | modifier operator { 55 | require(can[msg.sender] == 1, "RwaUrn/not-operator"); 56 | _; 57 | } 58 | 59 | VatAbstract public vat; 60 | JugAbstract public jug; 61 | GemJoinAbstract public gemJoin; 62 | DaiJoinAbstract public daiJoin; 63 | address public outputConduit; 64 | 65 | // Events 66 | event Rely(address indexed usr); 67 | event Deny(address indexed usr); 68 | event Hope(address indexed usr); 69 | event Nope(address indexed usr); 70 | event File(bytes32 indexed what, address data); 71 | event Lock(address indexed usr, uint256 wad); 72 | event Free(address indexed usr, uint256 wad); 73 | event Draw(address indexed usr, uint256 wad); 74 | event Wipe(address indexed usr, uint256 wad); 75 | event Quit(address indexed usr, uint256 wad); 76 | 77 | // --- math --- 78 | uint256 constant RAY = 10 ** 27; 79 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 80 | require((z = x + y) >= x); 81 | } 82 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 83 | require((z = x - y) <= x); 84 | } 85 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 86 | require(y == 0 || (z = x * y) / y == x); 87 | } 88 | function divup(uint256 x, uint256 y) internal pure returns (uint256 z) { 89 | z = add(x, sub(y, 1)) / y; 90 | } 91 | 92 | // --- init --- 93 | constructor( 94 | address vat_, address jug_, address gemJoin_, address daiJoin_, address outputConduit_ 95 | ) public { 96 | // requires in urn that outputConduit isn't address(0) 97 | vat = VatAbstract(vat_); 98 | jug = JugAbstract(jug_); 99 | gemJoin = GemJoinAbstract(gemJoin_); 100 | daiJoin = DaiJoinAbstract(daiJoin_); 101 | outputConduit = outputConduit_; 102 | wards[msg.sender] = 1; 103 | DSTokenAbstract(gemJoin.gem()).approve(address(gemJoin), uint256(-1)); 104 | DaiAbstract(daiJoin.dai()).approve(address(daiJoin), uint256(-1)); 105 | VatAbstract(vat_).hope(address(daiJoin)); 106 | emit Rely(msg.sender); 107 | emit File("outputConduit", outputConduit_); 108 | emit File("jug", jug_); 109 | } 110 | 111 | // --- administration --- 112 | function file(bytes32 what, address data) external auth { 113 | if (what == "outputConduit") { outputConduit = data; } 114 | else if (what == "jug") { jug = JugAbstract(data); } 115 | else revert("RwaUrn/unrecognised-param"); 116 | emit File(what, data); 117 | } 118 | 119 | // --- cdp operation --- 120 | // n.b. that the operator must bring the gem 121 | function lock(uint256 wad) external operator { 122 | require(wad <= 2**255 - 1, "RwaUrn/overflow"); 123 | DSTokenAbstract(gemJoin.gem()).transferFrom(msg.sender, address(this), wad); 124 | // join with address this 125 | gemJoin.join(address(this), wad); 126 | vat.frob(gemJoin.ilk(), address(this), address(this), address(this), int(wad), 0); 127 | emit Lock(msg.sender, wad); 128 | } 129 | // n.b. that the operator takes the gem 130 | // and might not be the same operator who brought the gem 131 | function free(uint256 wad) external operator { 132 | require(wad <= 2**255, "RwaUrn/overflow"); 133 | vat.frob(gemJoin.ilk(), address(this), address(this), address(this), -int(wad), 0); 134 | gemJoin.exit(msg.sender, wad); 135 | emit Free(msg.sender, wad); 136 | } 137 | // n.b. DAI can only go to the output conduit 138 | function draw(uint256 wad) external operator { 139 | require(outputConduit != address(0)); 140 | bytes32 ilk = gemJoin.ilk(); 141 | jug.drip(ilk); 142 | (,uint256 rate,,,) = vat.ilks(ilk); 143 | uint256 dart = divup(mul(RAY, wad), rate); 144 | require(dart <= 2**255 - 1, "RwaUrn/overflow"); 145 | vat.frob(ilk, address(this), address(this), address(this), 0, int(dart)); 146 | daiJoin.exit(outputConduit, wad); 147 | emit Draw(msg.sender, wad); 148 | } 149 | // n.b. anyone can wipe 150 | function wipe(uint256 wad) external { 151 | daiJoin.join(address(this), wad); 152 | bytes32 ilk = gemJoin.ilk(); 153 | jug.drip(ilk); 154 | (,uint256 rate,,,) = vat.ilks(ilk); 155 | uint256 dart = mul(RAY, wad) / rate; 156 | require(dart <= 2 ** 255, "RwaUrn/overflow"); 157 | vat.frob(ilk, address(this), address(this), address(this), 0, -int(dart)); 158 | emit Wipe(msg.sender, wad); 159 | } 160 | 161 | // If Dai is sitting here after ES that should be sent back 162 | function quit() external { 163 | require(outputConduit != address(0)); 164 | require(vat.live() == 0, "RwaUrn/vat-still-live"); 165 | DSTokenAbstract dai = DSTokenAbstract(daiJoin.dai()); 166 | uint256 wad = dai.balanceOf(address(this)); 167 | dai.transfer(outputConduit, wad); 168 | emit Quit(msg.sender, wad); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/interfaces/CatAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/cat.sol 5 | interface CatAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function box() external view returns (uint256); 10 | function litter() external view returns (uint256); 11 | function ilks(bytes32) external view returns (address, uint256, uint256); 12 | function live() external view returns (uint256); 13 | function vat() external view returns (address); 14 | function vow() external view returns (address); 15 | function file(bytes32, address) external; 16 | function file(bytes32, uint256) external; 17 | function file(bytes32, bytes32, uint256) external; 18 | function file(bytes32, bytes32, address) external; 19 | function bite(bytes32, address) external returns (uint256); 20 | function claw(uint256) external; 21 | function cage() external; 22 | } 23 | -------------------------------------------------------------------------------- /src/interfaces/ChainlogAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss-chain-log 5 | interface ChainlogAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function keys() external view returns (bytes32[] memory); 10 | function version() external view returns (string memory); 11 | function ipfs() external view returns (string memory); 12 | function setVersion(string calldata) external; 13 | function setSha256sum(string calldata) external; 14 | function setIPFS(string calldata) external; 15 | function setAddress(bytes32,address) external; 16 | function removeAddress(bytes32) external; 17 | function count() external view returns (uint256); 18 | function get(uint256) external view returns (bytes32,address); 19 | function list() external view returns (bytes32[] memory); 20 | function getAddress(bytes32) external view returns (address); 21 | } 22 | 23 | // Helper function for returning address or abstract of Chainlog 24 | // Valid on Mainnet, Kovan, Rinkeby, Ropsten, and Goerli 25 | contract ChainlogHelper { 26 | address public constant ADDRESS = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; 27 | ChainlogAbstract public constant ABSTRACT = ChainlogAbstract(ADDRESS); 28 | } 29 | -------------------------------------------------------------------------------- /src/interfaces/DSChiefAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/dapphub/ds-chief 5 | interface DSChiefAbstract { 6 | function live() external view returns (uint256); 7 | function launch() external; 8 | function slates(bytes32) external view returns (address[] memory); 9 | function votes(address) external view returns (bytes32); 10 | function approvals(address) external view returns (uint256); 11 | function deposits(address) external view returns (address); 12 | function GOV() external view returns (address); 13 | function IOU() external view returns (address); 14 | function hat() external view returns (address); 15 | function MAX_YAYS() external view returns (uint256); 16 | function lock(uint256) external; 17 | function free(uint256) external; 18 | function etch(address[] calldata) external returns (bytes32); 19 | function vote(address[] calldata) external returns (bytes32); 20 | function vote(bytes32) external; 21 | function lift(address) external; 22 | function setOwner(address) external; 23 | function setAuthority(address) external; 24 | function isUserRoot(address) external view returns (bool); 25 | function setRootUser(address, bool) external; 26 | function _root_users(address) external view returns (bool); 27 | function _user_roles(address) external view returns (bytes32); 28 | function _capability_roles(address, bytes4) external view returns (bytes32); 29 | function _public_capabilities(address, bytes4) external view returns (bool); 30 | function getUserRoles(address) external view returns (bytes32); 31 | function getCapabilityRoles(address, bytes4) external view returns (bytes32); 32 | function isCapabilityPublic(address, bytes4) external view returns (bool); 33 | function hasUserRole(address, uint8) external view returns (bool); 34 | function canCall(address, address, bytes4) external view returns (bool); 35 | function setUserRole(address, uint8, bool) external; 36 | function setPublicCapability(address, bytes4, bool) external; 37 | function setRoleCapability(uint8, address, bytes4, bool) external; 38 | } 39 | 40 | interface DSChiefFabAbstract { 41 | function newChief(address, uint256) external returns (address); 42 | } 43 | -------------------------------------------------------------------------------- /src/interfaces/DSPauseAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/dapphub/ds-pause 5 | interface DSPauseAbstract { 6 | function owner() external view returns (address); 7 | function authority() external view returns (address); 8 | function setOwner(address) external; 9 | function setAuthority(address) external; 10 | function setDelay(uint256) external; 11 | function plans(bytes32) external view returns (bool); 12 | function proxy() external view returns (address); 13 | function delay() external view returns (uint256); 14 | function plot(address, bytes32, bytes calldata, uint256) external; 15 | function drop(address, bytes32, bytes calldata, uint256) external; 16 | function exec(address, bytes32, bytes calldata, uint256) external returns (bytes memory); 17 | } 18 | -------------------------------------------------------------------------------- /src/interfaces/DSSpellAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/dapphub/ds-spell 5 | interface DSSpellAbstract { 6 | function whom() external view returns (address); 7 | function mana() external view returns (uint256); 8 | function data() external view returns (bytes memory); 9 | function done() external view returns (bool); 10 | function cast() external; 11 | } 12 | -------------------------------------------------------------------------------- /src/interfaces/DSTokenAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/dapphub/ds-token/blob/master/src/token.sol 5 | interface DSTokenAbstract { 6 | function name() external view returns (bytes32); 7 | function symbol() external view returns (bytes32); 8 | function decimals() external view returns (uint256); 9 | function totalSupply() external view returns (uint256); 10 | function balanceOf(address) external view returns (uint256); 11 | function transfer(address, uint256) external returns (bool); 12 | function allowance(address, address) external view returns (uint256); 13 | function approve(address, uint256) external returns (bool); 14 | function approve(address) external returns (bool); 15 | function transferFrom(address, address, uint256) external returns (bool); 16 | function push(address, uint256) external; 17 | function pull(address, uint256) external; 18 | function move(address, address, uint256) external; 19 | function mint(uint256) external; 20 | function mint(address,uint) external; 21 | function burn(uint256) external; 22 | function burn(address,uint) external; 23 | function setName(bytes32) external; 24 | function authority() external view returns (address); 25 | function owner() external view returns (address); 26 | function setOwner(address) external; 27 | function setAuthority(address) external; 28 | } 29 | -------------------------------------------------------------------------------- /src/interfaces/DSValueAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/dapphub/ds-value/blob/master/src/value.sol 5 | interface DSValueAbstract { 6 | function has() external view returns (bool); 7 | function val() external view returns (bytes32); 8 | function peek() external view returns (bytes32, bool); 9 | function read() external view returns (bytes32); 10 | function poke(bytes32) external; 11 | function void() external; 12 | function authority() external view returns (address); 13 | function owner() external view returns (address); 14 | function setOwner(address) external; 15 | function setAuthority(address) external; 16 | } 17 | -------------------------------------------------------------------------------- /src/interfaces/DaiAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/dai.sol 5 | interface DaiAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function name() external view returns (string memory); 10 | function symbol() external view returns (string memory); 11 | function version() external view returns (string memory); 12 | function decimals() external view returns (uint8); 13 | function totalSupply() external view returns (uint256); 14 | function balanceOf(address) external view returns (uint256); 15 | function allowance(address, address) external view returns (uint256); 16 | function nonces(address) external view returns (uint256); 17 | function DOMAIN_SEPARATOR() external view returns (bytes32); 18 | function PERMIT_TYPEHASH() external view returns (bytes32); 19 | function transfer(address, uint256) external returns (bool); 20 | function transferFrom(address, address, uint256) external returns (bool); 21 | function mint(address, uint256) external; 22 | function burn(address, uint256) external; 23 | function approve(address, uint256) external returns (bool); 24 | function push(address, uint256) external; 25 | function pull(address, uint256) external; 26 | function move(address, address, uint256) external; 27 | function permit(address, address, uint256, uint256, bool, uint8, bytes32, bytes32) external; 28 | } 29 | -------------------------------------------------------------------------------- /src/interfaces/DaiJoinAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/join.sol 5 | interface DaiJoinAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address usr) external; 8 | function deny(address usr) external; 9 | function vat() external view returns (address); 10 | function dai() external view returns (address); 11 | function live() external view returns (uint256); 12 | function cage() external; 13 | function join(address, uint256) external; 14 | function exit(address, uint256) external; 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/EndAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/end.sol 5 | interface EndAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function vat() external view returns (address); 10 | function cat() external view returns (address); 11 | function dog() external view returns (address); 12 | function vow() external view returns (address); 13 | function pot() external view returns (address); 14 | function spot() external view returns (address); 15 | function live() external view returns (uint256); 16 | function when() external view returns (uint256); 17 | function wait() external view returns (uint256); 18 | function debt() external view returns (uint256); 19 | function tag(bytes32) external view returns (uint256); 20 | function gap(bytes32) external view returns (uint256); 21 | function Art(bytes32) external view returns (uint256); 22 | function fix(bytes32) external view returns (uint256); 23 | function bag(address) external view returns (uint256); 24 | function out(bytes32, address) external view returns (uint256); 25 | function WAD() external view returns (uint256); 26 | function RAY() external view returns (uint256); 27 | function file(bytes32, address) external; 28 | function file(bytes32, uint256) external; 29 | function cage() external; 30 | function cage(bytes32) external; 31 | function skip(bytes32, uint256) external; 32 | function snip(bytes32, uint256) external; 33 | function skim(bytes32, address) external; 34 | function free(bytes32) external; 35 | function thaw() external; 36 | function flow(bytes32) external; 37 | function pack(uint256) external; 38 | function cash(bytes32, uint256) external; 39 | } 40 | -------------------------------------------------------------------------------- /src/interfaces/FlipAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/flip.sol 5 | interface FlipAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address usr) external; 8 | function deny(address usr) external; 9 | function bids(uint256) external view returns (uint256, uint256, address, uint48, uint48, address, address, uint256); 10 | function vat() external view returns (address); 11 | function cat() external view returns (address); 12 | function ilk() external view returns (bytes32); 13 | function beg() external view returns (uint256); 14 | function ttl() external view returns (uint48); 15 | function tau() external view returns (uint48); 16 | function kicks() external view returns (uint256); 17 | function file(bytes32, uint256) external; 18 | function kick(address, address, uint256, uint256, uint256) external returns (uint256); 19 | function tick(uint256) external; 20 | function tend(uint256, uint256, uint256) external; 21 | function dent(uint256, uint256, uint256) external; 22 | function deal(uint256) external; 23 | function yank(uint256) external; 24 | } 25 | -------------------------------------------------------------------------------- /src/interfaces/FlipperMomAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/flipper-mom/blob/master/src/FlipperMom.sol 5 | interface FlipperMomAbstract { 6 | function owner() external view returns (address); 7 | function authority() external view returns (address); 8 | function setOwner(address) external; 9 | function setAuthority(address) external; 10 | function cat() external returns (address); 11 | function rely(address) external; 12 | function deny(address) external; 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/GemJoinAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/join.sol 5 | interface GemJoinAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function vat() external view returns (address); 10 | function ilk() external view returns (bytes32); 11 | function gem() external view returns (address); 12 | function dec() external view returns (uint256); 13 | function live() external view returns (uint256); 14 | function cage() external; 15 | function join(address, uint256) external; 16 | function exit(address, uint256) external; 17 | } 18 | -------------------------------------------------------------------------------- /src/interfaces/IlkRegistryAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/ilk-registry 5 | interface IlkRegistryAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function vat() external view returns (address); 10 | function dog() external view returns (address); 11 | function cat() external view returns (address); 12 | function spot() external view returns (address); 13 | function ilkData(bytes32) external view returns ( 14 | uint96, address, address, uint8, uint96, address, address, string memory, string memory 15 | ); 16 | function ilks() external view returns (bytes32[] memory); 17 | function ilks(uint) external view returns (bytes32); 18 | function add(address) external; 19 | function remove(bytes32) external; 20 | function update(bytes32) external; 21 | function removeAuth(bytes32) external; 22 | function file(bytes32, address) external; 23 | function file(bytes32, bytes32, address) external; 24 | function file(bytes32, bytes32, uint256) external; 25 | function file(bytes32, bytes32, string calldata) external; 26 | function count() external view returns (uint256); 27 | function list() external view returns (bytes32[] memory); 28 | function list(uint256, uint256) external view returns (bytes32[] memory); 29 | function get(uint256) external view returns (bytes32); 30 | function info(bytes32) external view returns ( 31 | string memory, string memory, uint256, uint256, address, address, address, address 32 | ); 33 | function pos(bytes32) external view returns (uint256); 34 | function class(bytes32) external view returns (uint256); 35 | function gem(bytes32) external view returns (address); 36 | function pip(bytes32) external view returns (address); 37 | function join(bytes32) external view returns (address); 38 | function xlip(bytes32) external view returns (address); 39 | function dec(bytes32) external view returns (uint256); 40 | function symbol(bytes32) external view returns (string memory); 41 | function name(bytes32) external view returns (string memory); 42 | function put(bytes32, address, address, uint256, uint256, address, address, string calldata, string calldata) external; 43 | } 44 | -------------------------------------------------------------------------------- /src/interfaces/JugAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/jug.sol 5 | interface JugAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function ilks(bytes32) external view returns (uint256, uint256); 10 | function vat() external view returns (address); 11 | function vow() external view returns (address); 12 | function base() external view returns (uint256); 13 | function init(bytes32) external; 14 | function file(bytes32, bytes32, uint256) external; 15 | function file(bytes32, uint256) external; 16 | function file(bytes32, address) external; 17 | function drip(bytes32) external returns (uint256); 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/OsmMomAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/osm-mom 5 | interface OsmMomAbstract { 6 | function owner() external view returns (address); 7 | function authority() external view returns (address); 8 | function osms(bytes32) external view returns (address); 9 | function setOsm(bytes32, address) external; 10 | function setOwner(address) external; 11 | function setAuthority(address) external; 12 | function stop(bytes32) external; 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/PotAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/pot.sol 5 | interface PotAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function pie(address) external view returns (uint256); 10 | function Pie() external view returns (uint256); 11 | function dsr() external view returns (uint256); 12 | function chi() external view returns (uint256); 13 | function vat() external view returns (address); 14 | function vow() external view returns (address); 15 | function rho() external view returns (uint256); 16 | function live() external view returns (uint256); 17 | function file(bytes32, uint256) external; 18 | function file(bytes32, address) external; 19 | function cage() external; 20 | function drip() external returns (uint256); 21 | function join(uint256) external; 22 | function exit(uint256) external; 23 | } 24 | -------------------------------------------------------------------------------- /src/interfaces/SpotAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/spot.sol 5 | interface SpotAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function ilks(bytes32) external view returns (address, uint256); 10 | function vat() external view returns (address); 11 | function par() external view returns (uint256); 12 | function live() external view returns (uint256); 13 | function file(bytes32, bytes32, address) external; 14 | function file(bytes32, uint256) external; 15 | function file(bytes32, bytes32, uint256) external; 16 | function poke(bytes32) external; 17 | function cage() external; 18 | } 19 | -------------------------------------------------------------------------------- /src/interfaces/VatAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/vat.sol 5 | interface VatAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address) external; 8 | function deny(address) external; 9 | function can(address, address) external view returns (uint256); 10 | function hope(address) external; 11 | function nope(address) external; 12 | function ilks(bytes32) external view returns (uint256, uint256, uint256, uint256, uint256); 13 | function urns(bytes32, address) external view returns (uint256, uint256); 14 | function gem(bytes32, address) external view returns (uint256); 15 | function dai(address) external view returns (uint256); 16 | function sin(address) external view returns (uint256); 17 | function debt() external view returns (uint256); 18 | function vice() external view returns (uint256); 19 | function Line() external view returns (uint256); 20 | function live() external view returns (uint256); 21 | function init(bytes32) external; 22 | function file(bytes32, uint256) external; 23 | function file(bytes32, bytes32, uint256) external; 24 | function cage() external; 25 | function slip(bytes32, address, int256) external; 26 | function flux(bytes32, address, address, uint256) external; 27 | function move(address, address, uint256) external; 28 | function frob(bytes32, address, address, address, int256, int256) external; 29 | function fork(bytes32, address, address, int256, int256) external; 30 | function grab(bytes32, address, address, address, int256, int256) external; 31 | function heal(uint256) external; 32 | function suck(address, address, uint256) external; 33 | function fold(bytes32, address, int256) external; 34 | } 35 | -------------------------------------------------------------------------------- /src/interfaces/VowAbstract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.5.12; 3 | 4 | // https://github.com/makerdao/dss/blob/master/src/vow.sol 5 | interface VowAbstract { 6 | function wards(address) external view returns (uint256); 7 | function rely(address usr) external; 8 | function deny(address usr) external; 9 | function vat() external view returns (address); 10 | function flapper() external view returns (address); 11 | function flopper() external view returns (address); 12 | function sin(uint256) external view returns (uint256); 13 | function Sin() external view returns (uint256); 14 | function Ash() external view returns (uint256); 15 | function wait() external view returns (uint256); 16 | function dump() external view returns (uint256); 17 | function sump() external view returns (uint256); 18 | function bump() external view returns (uint256); 19 | function hump() external view returns (uint256); 20 | function live() external view returns (uint256); 21 | function file(bytes32, uint256) external; 22 | function file(bytes32, address) external; 23 | function fess(uint256) external; 24 | function flog(uint256) external; 25 | function heal(uint256) external; 26 | function kiss(uint256) external; 27 | function flop() external returns (uint256); 28 | function flap() external returns (uint256); 29 | function cage() external; 30 | } 31 | -------------------------------------------------------------------------------- /src/invariants/RwaTokenInvariant.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaTokenInvariant.sol -- RWA Token Invariant testing 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "../RwaToken.sol"; 24 | 25 | /// @dev A contract that will receive RWA001, and allows for it to be retrieved. 26 | contract MockHolder { 27 | // --- Math --- 28 | uint256 constant WAD = 10 ** 18; 29 | 30 | constructor (address rwa, address usr) public { 31 | RwaToken(rwa).approve(usr, 1 * WAD); 32 | } 33 | } 34 | 35 | /// @dev Invariant testing 36 | contract RwaTokenInvariant { 37 | RwaToken internal rwa; 38 | address internal holder; 39 | 40 | string public constant name = "RWA-001"; 41 | string public constant symbol = "RWA001"; 42 | 43 | /// @dev Instantiate the RwaToken contract, and a holder address that will return rwa when asked to. 44 | constructor () public { 45 | rwa = new RwaToken(name, symbol); 46 | holder = address(new MockHolder(address(rwa), address(this))); 47 | } 48 | 49 | // --- Math --- 50 | uint256 constant WAD = 10 ** 18; 51 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 52 | require((z = x + y) >= x); 53 | } 54 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 55 | require((z = x - y) <= x); 56 | } 57 | 58 | /// @dev Test that supply and balance hold on transfer. 59 | function transfer(uint wad) public { 60 | uint thisBalance = rwa.balanceOf(address(this)); 61 | uint holderBalance = rwa.balanceOf(holder); 62 | rwa.transfer(holder, wad); 63 | assert(rwa.balanceOf(address(this)) == sub(thisBalance, wad)); 64 | assert(rwa.balanceOf(holder) == add(holderBalance, wad)); 65 | assert(address(rwa).balance == address(rwa).balance); 66 | } 67 | 68 | /// @dev Test that supply and balance hold on transferFrom. 69 | function transferFrom(uint wad) public { 70 | uint thisBalance = rwa.balanceOf(address(this)); 71 | uint holderBalance = rwa.balanceOf(holder); 72 | rwa.transferFrom(holder, address(this), wad); 73 | assert(rwa.balanceOf(address(this)) == add(thisBalance, wad)); 74 | assert(rwa.balanceOf(holder) == sub(holderBalance, wad)); 75 | assert(address(rwa).balance == address(rwa).balance); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/invariants/config.yaml: -------------------------------------------------------------------------------- 1 | seqLen: 50 2 | testLimit: 20000 3 | prefix: "crytic_" 4 | deployer: "0x41414141" 5 | sender: ["0x42424242", "0x43434343"] 6 | coverage: true 7 | checkAsserts: true 8 | -------------------------------------------------------------------------------- /src/test/RwaSpell.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaSpell.t.sol -- Tests for the spell contract 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | // hax: needed for the deploy scripts 24 | import "dss-gem-joins/join-auth.sol"; 25 | import "ds-value/value.sol"; 26 | 27 | import "ds-math/math.sol"; 28 | import "ds-test/test.sol"; 29 | import "./rates.sol"; 30 | import "./addresses_kovan.sol"; 31 | import "../interfaces/ChainlogAbstract.sol"; 32 | import "../interfaces/EndAbstract.sol"; 33 | import "../interfaces/DSPauseAbstract.sol"; 34 | import "../interfaces/VatAbstract.sol"; 35 | import "../interfaces/DSChiefAbstract.sol"; 36 | import "../interfaces/DSValueAbstract.sol"; 37 | import "../interfaces/CatAbstract.sol"; 38 | import "../interfaces/JugAbstract.sol"; 39 | import "../interfaces/VowAbstract.sol"; 40 | import "../interfaces/PotAbstract.sol"; 41 | import "../interfaces/SpotAbstract.sol"; 42 | import "../interfaces/DSTokenAbstract.sol"; 43 | import "../interfaces/IlkRegistryAbstract.sol"; 44 | import "../interfaces/OsmMomAbstract.sol"; 45 | import "../interfaces/FlipperMomAbstract.sol"; 46 | import "../interfaces/FlipAbstract.sol"; 47 | import "../interfaces/GemJoinAbstract.sol"; 48 | import "../interfaces/DSSpellAbstract.sol"; 49 | 50 | import {RwaSpell, SpellAction} from "../RwaSpell.sol"; 51 | 52 | interface Hevm { 53 | function warp(uint256) external; 54 | function store(address,bytes32,bytes32) external; 55 | } 56 | 57 | interface RwaInputConduitLike { 58 | function push() external; 59 | } 60 | 61 | interface RwaOutputConduitLike { 62 | function wards(address) external returns (uint); 63 | function can(address) external returns (uint); 64 | function rely(address) external; 65 | function deny(address) external; 66 | function hope(address) external; 67 | function nope(address) external; 68 | function bud(address) external returns (uint); 69 | function kiss(address) external; 70 | function diss(address) external; 71 | function pick(address) external; 72 | function push() external; 73 | } 74 | 75 | interface RwaUrnLike { 76 | function can(address) external returns (uint); 77 | function rely(address) external; 78 | function deny(address) external; 79 | function hope(address) external; 80 | function nope(address) external; 81 | function file(bytes32, address) external; 82 | function lock(uint256) external; 83 | function free(uint256) external; 84 | function draw(uint256) external; 85 | function wipe(uint256) external; 86 | } 87 | 88 | interface RwaLiquidationLike { 89 | function wards(address) external returns (uint256); 90 | function rely(address) external; 91 | function deny(address) external; 92 | function ilks(bytes32) external returns (bytes32, address, uint48, uint48); 93 | function init(bytes32, uint256, string calldata, uint48) external; 94 | function bump(bytes32, uint256) external; 95 | function tell(bytes32) external; 96 | function cure(bytes32) external; 97 | function cull(bytes32, address) external; 98 | function good(bytes32) external view returns (bool); 99 | } 100 | 101 | contract EndSpellAction { 102 | ChainlogAbstract constant CHANGELOG = 103 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 104 | 105 | function execute() public { 106 | EndAbstract(CHANGELOG.getAddress("MCD_END")).cage(); 107 | } 108 | } 109 | 110 | contract TestSpell { 111 | ChainlogAbstract constant CHANGELOG = 112 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 113 | DSPauseAbstract public pause = 114 | DSPauseAbstract(CHANGELOG.getAddress("MCD_PAUSE")); 115 | address public action; 116 | bytes32 public tag; 117 | uint256 public eta; 118 | bytes public sig; 119 | uint256 public expiration; 120 | bool public done; 121 | 122 | constructor() public { 123 | sig = abi.encodeWithSignature("execute()"); 124 | } 125 | 126 | function setTag() internal { 127 | bytes32 _tag; 128 | address _action = action; 129 | assembly { _tag := extcodehash(_action) } 130 | tag = _tag; 131 | } 132 | 133 | function schedule() public { 134 | require(eta == 0, "This spell has already been scheduled"); 135 | eta = block.timestamp + DSPauseAbstract(pause).delay(); 136 | pause.plot(action, tag, sig, eta); 137 | } 138 | 139 | function cast() public { 140 | require(!done, "spell-already-cast"); 141 | done = true; 142 | pause.exec(action, tag, sig, eta); 143 | } 144 | } 145 | 146 | contract EndSpell is TestSpell { 147 | constructor() public { 148 | action = address(new EndSpellAction()); 149 | setTag(); 150 | } 151 | } 152 | 153 | contract CullSpellAction { 154 | ChainlogAbstract constant CHANGELOG = 155 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 156 | bytes32 constant ilk = "RWA001-A"; 157 | 158 | function execute() public { 159 | RwaLiquidationLike( 160 | CHANGELOG.getAddress("MIP21_LIQUIDATION_ORACLE") 161 | ).cull(ilk, CHANGELOG.getAddress("RWA001_A_URN")); 162 | } 163 | } 164 | 165 | contract CullSpell is TestSpell { 166 | constructor() public { 167 | action = address(new CullSpellAction()); 168 | setTag(); 169 | } 170 | } 171 | 172 | contract CureSpellAction { 173 | ChainlogAbstract constant CHANGELOG = 174 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 175 | bytes32 constant ilk = "RWA001-A"; 176 | 177 | function execute() public { 178 | RwaLiquidationLike( 179 | CHANGELOG.getAddress("MIP21_LIQUIDATION_ORACLE") 180 | ).cure(ilk); 181 | } 182 | } 183 | 184 | contract CureSpell is TestSpell { 185 | constructor() public { 186 | action = address(new CureSpellAction()); 187 | setTag(); 188 | } 189 | } 190 | 191 | contract TellSpellAction { 192 | ChainlogAbstract constant CHANGELOG = 193 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 194 | bytes32 constant ilk = "RWA001-A"; 195 | 196 | function execute() public { 197 | VatAbstract(CHANGELOG.getAddress("MCD_VAT")).file(ilk, "line", 0); 198 | RwaLiquidationLike( 199 | CHANGELOG.getAddress("MIP21_LIQUIDATION_ORACLE") 200 | ).tell(ilk); 201 | } 202 | } 203 | 204 | contract TellSpell is TestSpell { 205 | constructor() public { 206 | action = address(new TellSpellAction()); 207 | setTag(); 208 | } 209 | } 210 | 211 | contract BumpSpellAction { 212 | ChainlogAbstract constant CHANGELOG = 213 | ChainlogAbstract(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F); 214 | bytes32 constant ilk = "RWA001-A"; 215 | uint256 constant WAD = 10 ** 18; 216 | 217 | function execute() public { 218 | RwaLiquidationLike( 219 | CHANGELOG.getAddress("MIP21_LIQUIDATION_ORACLE") 220 | ).bump(ilk, 1070 * WAD); 221 | } 222 | } 223 | 224 | contract BumpSpell is TestSpell { 225 | constructor() public { 226 | action = address(new BumpSpellAction()); 227 | setTag(); 228 | } 229 | } 230 | 231 | contract DssSpellTest is DSTest, DSMath { 232 | // populate with mainnet spell if needed 233 | address constant KOVAN_SPELL = address(0xdCB87e8149F7bE368ec077b0D92C7ADAC8bB919e); 234 | // this needs to be updated 235 | uint256 constant SPELL_CREATED = 1614270940; 236 | 237 | struct CollateralValues { 238 | uint256 line; 239 | uint256 dust; 240 | uint256 chop; 241 | uint256 dunk; 242 | uint256 pct; 243 | uint256 mat; 244 | uint256 beg; 245 | uint48 ttl; 246 | uint48 tau; 247 | uint256 liquidations; 248 | } 249 | 250 | struct SystemValues { 251 | uint256 pot_dsr; 252 | uint256 vat_Line; 253 | uint256 pause_delay; 254 | uint256 vow_wait; 255 | uint256 vow_dump; 256 | uint256 vow_sump; 257 | uint256 vow_bump; 258 | uint256 vow_hump; 259 | uint256 cat_box; 260 | address osm_mom_authority; 261 | address flipper_mom_authority; 262 | uint256 ilk_count; 263 | mapping (bytes32 => CollateralValues) collaterals; 264 | } 265 | 266 | SystemValues afterSpell; 267 | 268 | Hevm hevm; 269 | Rates rates; 270 | Addresses addr = new Addresses(); 271 | 272 | // KOVAN ADDRESSES 273 | DSPauseAbstract pause = DSPauseAbstract( addr.addr("MCD_PAUSE")); 274 | address pauseProxy = addr.addr("MCD_PAUSE_PROXY"); 275 | 276 | DSChiefAbstract chief = DSChiefAbstract( addr.addr("MCD_ADM")); 277 | VatAbstract vat = VatAbstract( addr.addr("MCD_VAT")); 278 | 279 | CatAbstract cat = CatAbstract( addr.addr("MCD_CAT")); 280 | JugAbstract jug = JugAbstract( addr.addr("MCD_JUG")); 281 | 282 | VowAbstract vow = VowAbstract( addr.addr("MCD_VOW")); 283 | PotAbstract pot = PotAbstract( addr.addr("MCD_POT")); 284 | 285 | SpotAbstract spot = SpotAbstract( addr.addr("MCD_SPOT")); 286 | DSTokenAbstract gov = DSTokenAbstract( addr.addr("MCD_GOV")); 287 | 288 | EndAbstract end = EndAbstract( addr.addr("MCD_END")); 289 | IlkRegistryAbstract reg = IlkRegistryAbstract( addr.addr("ILK_REGISTRY")); 290 | 291 | OsmMomAbstract osmMom = OsmMomAbstract( addr.addr("OSM_MOM")); 292 | FlipperMomAbstract flipMom = FlipperMomAbstract( addr.addr("FLIPPER_MOM")); 293 | 294 | DSTokenAbstract dai = DSTokenAbstract( addr.addr("MCD_DAI")); 295 | 296 | ChainlogAbstract chainlog = ChainlogAbstract( addr.addr("CHANGELOG")); 297 | 298 | bytes32 constant ilk = "RWA001-A"; 299 | DSTokenAbstract rwagem = DSTokenAbstract( addr.addr("RWA001")); 300 | GemJoinAbstract rwajoin = GemJoinAbstract( addr.addr("MCD_JOIN_RWA001_A")); 301 | RwaLiquidationLike oracle = RwaLiquidationLike( addr.addr("MIP21_LIQUIDATION_ORACLE")); 302 | RwaUrnLike rwaurn = RwaUrnLike( addr.addr("RWA001_A_URN")); 303 | RwaInputConduitLike rwaconduitin = RwaInputConduitLike( addr.addr("RWA001_A_INPUT_CONDUIT")); 304 | RwaOutputConduitLike rwaconduitout = RwaOutputConduitLike(addr.addr("RWA001_A_OUTPUT_CONDUIT")); 305 | 306 | address makerDeployer06 = 0xda0fab060e6cc7b1C0AA105d29Bd50D71f036711; 307 | 308 | RwaSpell spell; 309 | BumpSpell bumpSpell; 310 | TellSpell tellSpell; 311 | CureSpell cureSpell; 312 | CullSpell cullSpell; 313 | EndSpell endSpell; 314 | 315 | // CHEAT_CODE = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D 316 | bytes20 constant CHEAT_CODE = 317 | bytes20(uint160(uint256(keccak256('hevm cheat code')))); 318 | 319 | uint256 constant HUNDRED = 10 ** 2; 320 | uint256 constant THOUSAND = 10 ** 3; 321 | uint256 constant MILLION = 10 ** 6; 322 | uint256 constant BILLION = 10 ** 9; 323 | // uint256 constant WAD = 10 ** 18; 324 | // uint256 constant RAY = 10 ** 27; 325 | uint256 constant RAD = 10 ** 45; 326 | 327 | event Debug(uint256 index, uint256 val); 328 | event Debug(uint256 index, address addr); 329 | event Debug(uint256 index, bytes32 what); 330 | 331 | // not provided in DSMath 332 | function rpow( 333 | uint256 x, uint256 n, uint256 b 334 | ) internal pure returns (uint256 z) { 335 | assembly { 336 | switch x case 0 {switch n case 0 {z := b} default {z := 0}} 337 | default { 338 | switch mod(n, 2) case 0 { z := b } default { z := x } 339 | let half := div(b, 2) // for rounding. 340 | for { n := div(n, 2) } n { n := div(n,2) } { 341 | let xx := mul(x, x) 342 | if iszero(eq(div(xx, x), x)) { revert(0,0) } 343 | let xxRound := add(xx, half) 344 | if lt(xxRound, xx) { revert(0,0) } 345 | x := div(xxRound, b) 346 | if mod(n,2) { 347 | let zx := mul(z, x) 348 | if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) } 349 | let zxRound := add(zx, half) 350 | if lt(zxRound, zx) { revert(0,0) } 351 | z := div(zxRound, b) 352 | } 353 | } 354 | } 355 | } 356 | } 357 | // 10^-5 (tenth of a basis point) as a RAY 358 | uint256 TOLERANCE = 10 ** 22; 359 | 360 | function yearlyYield(uint256 duty) public pure returns (uint256) { 361 | return rpow(duty, (365 * 24 * 60 *60), RAY); 362 | } 363 | 364 | function expectedRate(uint256 percentValue) public pure returns (uint256) { 365 | return (10000 + percentValue) * (10 ** 23); 366 | } 367 | 368 | function diffCalc( 369 | uint256 expectedRate_, uint256 yearlyYield_ 370 | ) public pure returns (uint256) { 371 | return (expectedRate_ > yearlyYield_) ? 372 | expectedRate_ - yearlyYield_ : yearlyYield_ - expectedRate_; 373 | } 374 | 375 | function setUp() public { 376 | hevm = Hevm(address(CHEAT_CODE)); 377 | rates = new Rates(); 378 | 379 | spell = KOVAN_SPELL != address(0) ? 380 | RwaSpell(KOVAN_SPELL) : new RwaSpell(); 381 | 382 | // 383 | // Test for all system configuration changes 384 | // 385 | afterSpell = SystemValues({ 386 | pot_dsr: 0, // In basis points 387 | vat_Line: 12320 * MILLION / 100, // In whole Dai units 388 | pause_delay: 60, // In seconds 389 | vow_wait: 3600, // In seconds 390 | vow_dump: 2, // In whole Dai units 391 | vow_sump: 50, // In whole Dai units 392 | vow_bump: 10, // In whole Dai units 393 | vow_hump: 500, // In whole Dai units 394 | cat_box: 10 * THOUSAND, // In whole Dai units 395 | osm_mom_authority: address(0), // OsmMom authority 396 | flipper_mom_authority: address(0), // FlipperMom authority 397 | ilk_count: 18 // Num expected in system 398 | }); 399 | 400 | // 401 | // Test for all collateral based changes here 402 | // 403 | afterSpell.collaterals["RWA001-A"] = CollateralValues({ 404 | line: 1000, // In whole Dai units 405 | dust: 0, // In whole Dai units 406 | pct: 200, // In basis points 407 | chop: 1300, // In basis points 408 | dunk: 50 * THOUSAND, // In whole Dai units 409 | mat: 15000, // In basis points 410 | beg: 300, // In basis points 411 | ttl: 6 hours, // In seconds 412 | tau: 6 hours, // In seconds 413 | liquidations: 1 // 1 if enabled 414 | }); 415 | } 416 | 417 | function scheduleWaitAndCastFailDay() public { 418 | spell.schedule(); 419 | 420 | uint256 castTime = block.timestamp + pause.delay(); 421 | uint256 day = (castTime / 1 days + 3) % 7; 422 | if (day < 5) { 423 | castTime += 5 days - day * 86400; 424 | } 425 | 426 | hevm.warp(castTime); 427 | spell.cast(); 428 | } 429 | 430 | function scheduleWaitAndCastFailEarly() public { 431 | spell.schedule(); 432 | 433 | uint256 castTime = block.timestamp + pause.delay() + 24 hours; 434 | uint256 hour = castTime / 1 hours % 24; 435 | if (hour >= 14) { 436 | castTime -= hour * 3600 - 13 hours; 437 | } 438 | 439 | hevm.warp(castTime); 440 | spell.cast(); 441 | } 442 | 443 | function scheduleWaitAndCastFailLate() public { 444 | spell.schedule(); 445 | 446 | uint256 castTime = block.timestamp + pause.delay(); 447 | uint256 hour = castTime / 1 hours % 24; 448 | if (hour < 21) { 449 | castTime += 21 hours - hour * 3600; 450 | } 451 | 452 | hevm.warp(castTime); 453 | spell.cast(); 454 | } 455 | 456 | function vote(address _spell) private { 457 | if (chief.hat() !=_spell) { 458 | hevm.store( 459 | address(gov), 460 | keccak256(abi.encode(address(this), uint256(1))), 461 | bytes32(uint256(999999999999 ether)) 462 | ); 463 | gov.approve(address(chief), uint256(-1)); 464 | chief.lock(sub(gov.balanceOf(address(this)), 1 ether)); 465 | 466 | assertTrue(!DSSpellAbstract(_spell).done()); 467 | 468 | address[] memory yays = new address[](1); 469 | yays[0] = _spell; 470 | 471 | chief.vote(yays); 472 | chief.lift(_spell); 473 | } 474 | assertEq(chief.hat(), _spell); 475 | } 476 | 477 | function scheduleWaitAndCast() public { 478 | spell.schedule(); 479 | 480 | uint256 castTime = block.timestamp + pause.delay(); 481 | 482 | uint256 day = (castTime / 1 days + 3) % 7; 483 | if(day >= 5) { 484 | castTime += 7 days - day * 86400; 485 | } 486 | 487 | uint256 hour = castTime / 1 hours % 24; 488 | if (hour >= 21) { 489 | castTime += 24 hours - hour * 3600 + 14 hours; 490 | } else if (hour < 14) { 491 | castTime += 14 hours - hour * 3600; 492 | } 493 | 494 | hevm.warp(castTime); 495 | spell.cast(); 496 | } 497 | 498 | function stringToBytes32( 499 | string memory source 500 | ) public pure returns (bytes32 result) { 501 | assembly { 502 | result := mload(add(source, 32)) 503 | } 504 | } 505 | 506 | function checkSystemValues(SystemValues storage values) internal { 507 | // dsr 508 | uint256 expectedDSRRate = rates.rates(values.pot_dsr); 509 | // make sure dsr is less than 100% APR 510 | // bc -l <<< 'scale=27; e( l(2.00)/(60 * 60 * 24 * 365) )' 511 | // 1000000021979553151239153027 512 | assertTrue( 513 | pot.dsr() >= RAY && pot.dsr() < 1000000021979553151239153027 514 | ); 515 | assertTrue(diffCalc(expectedRate(values.pot_dsr), yearlyYield(expectedDSRRate)) <= TOLERANCE); 516 | 517 | { 518 | // Line values in RAD 519 | uint256 normalizedLine = values.vat_Line * RAD; 520 | assertEq(vat.Line(), normalizedLine); 521 | assertTrue( 522 | (vat.Line() >= RAD && vat.Line() < 100 * BILLION * RAD) || 523 | vat.Line() == 0 524 | ); 525 | } 526 | 527 | // Pause delay 528 | assertEq(pause.delay(), values.pause_delay); 529 | 530 | // wait 531 | assertEq(vow.wait(), values.vow_wait); 532 | 533 | { 534 | // dump values in WAD 535 | uint256 normalizedDump = values.vow_dump * WAD; 536 | assertEq(vow.dump(), normalizedDump); 537 | assertTrue( 538 | (vow.dump() >= WAD && vow.dump() < 2 * THOUSAND * WAD) || 539 | vow.dump() == 0 540 | ); 541 | } 542 | { 543 | // sump values in RAD 544 | uint256 normalizedSump = values.vow_sump * RAD; 545 | assertEq(vow.sump(), normalizedSump); 546 | assertTrue( 547 | (vow.sump() >= RAD && vow.sump() < 500 * THOUSAND * RAD) || 548 | vow.sump() == 0 549 | ); 550 | } 551 | { 552 | // bump values in RAD 553 | uint normalizedBump = values.vow_bump * RAD; 554 | assertEq(vow.bump(), normalizedBump); 555 | assertTrue( 556 | (vow.bump() >= RAD && vow.bump() < HUNDRED * THOUSAND * RAD) || 557 | vow.bump() == 0 558 | ); 559 | } 560 | { 561 | // hump values in RAD 562 | uint256 normalizedHump = values.vow_hump * RAD; 563 | assertEq(vow.hump(), normalizedHump); 564 | assertTrue( 565 | (vow.hump() >= RAD && vow.hump() < HUNDRED * MILLION * RAD) || 566 | vow.hump() == 0 567 | ); 568 | } 569 | 570 | // box values in RAD 571 | { 572 | uint256 normalizedBox = values.cat_box * RAD; 573 | assertEq(cat.box(), normalizedBox); 574 | } 575 | 576 | // check OsmMom authority 577 | assertEq(osmMom.authority(), values.osm_mom_authority); 578 | 579 | // check FlipperMom authority 580 | assertEq(flipMom.authority(), values.flipper_mom_authority); 581 | 582 | // check number of ilks 583 | assertEq(reg.count(), values.ilk_count); 584 | } 585 | 586 | function checkCollateralValues(SystemValues storage values) internal { 587 | uint256 sumlines; 588 | bytes32[] memory ilks = reg.list(); 589 | for(uint256 i = 0; i < ilks.length; i++) { 590 | bytes32 ilk_ = ilks[i]; 591 | (uint256 duty,) = jug.ilks(ilk_); 592 | 593 | assertEq(duty, rates.rates(values.collaterals[ilk_].pct)); 594 | // make sure duty is less than 1000% APR 595 | // bc -l <<< 'scale=27; e( l(10.00)/(60 * 60 * 24 * 365) )' 596 | // 1000000073014496989316680335 597 | assertTrue(duty >= RAY && duty < 1000000073014496989316680335); // gt 0 and lt 1000% 598 | assertTrue(diffCalc(expectedRate(values.collaterals[ilk_].pct), yearlyYield(rates.rates(values.collaterals[ilk_].pct))) <= TOLERANCE); 599 | assertTrue(values.collaterals[ilk_].pct < THOUSAND * THOUSAND); // check value lt 1000% 600 | { 601 | (,,, uint256 line, uint256 dust) = vat.ilks(ilk); 602 | // Convert whole Dai units to expected RAD 603 | uint256 normalizedTestLine = values.collaterals[ilk_].line * RAD; 604 | sumlines += values.collaterals[ilk_].line; 605 | assertEq(line, normalizedTestLine); 606 | assertTrue((line >= RAD && line < BILLION * RAD) || line == 0); // eq 0 or gt eq 1 RAD and lt 1B 607 | uint256 normalizedTestDust = values.collaterals[ilk_].dust * RAD; 608 | assertEq(dust, normalizedTestDust); 609 | assertTrue((dust >= RAD && dust < 10 * THOUSAND * RAD) || dust == 0); // eq 0 or gt eq 1 and lt 10k 610 | } 611 | { 612 | (, uint256 chop, uint256 dunk) = cat.ilks(ilk); 613 | // Convert BP to system expected value 614 | uint256 normalizedTestChop = (values.collaterals[ilk_].chop * 10**14) + WAD; 615 | assertEq(chop, normalizedTestChop); 616 | // make sure chop is less than 100% 617 | assertTrue(chop >= WAD && chop < 2 * WAD); // penalty gt eq 0% and lt 100% 618 | // Convert whole Dai units to expected RAD 619 | uint256 normalizedTestDunk = values.collaterals[ilk_].dunk * RAD; 620 | assertEq(dunk, normalizedTestDunk); 621 | // put back in after LIQ-1.2 622 | assertTrue(dunk >= RAD && dunk < MILLION * RAD); 623 | } 624 | { 625 | (,uint256 mat) = spot.ilks(ilk); 626 | // Convert BP to system expected value 627 | uint256 normalizedTestMat = (values.collaterals[ilk_].mat * 10**23); 628 | assertEq(mat, normalizedTestMat); 629 | assertTrue(mat >= RAY && mat < 10 * RAY); // cr eq 100% and lt 1000% 630 | } 631 | { 632 | (address flipper,,) = cat.ilks(ilk); 633 | FlipAbstract flip = FlipAbstract(flipper); 634 | // Convert BP to system expected value 635 | uint256 normalizedTestBeg = (values.collaterals[ilk_].beg + 10000) * 10**14; 636 | assertEq(uint256(flip.beg()), normalizedTestBeg); 637 | assertTrue(flip.beg() >= WAD && flip.beg() < 105 * WAD / 100); // gt eq 0% and lt 5% 638 | assertEq(uint256(flip.ttl()), values.collaterals[ilk_].ttl); 639 | assertTrue(flip.ttl() >= 600 && flip.ttl() < 10 hours); // gt eq 10 minutes and lt 10 hours 640 | assertEq(uint256(flip.tau()), values.collaterals[ilk_].tau); 641 | assertTrue(flip.tau() >= 600 && flip.tau() <= 3 days); // gt eq 10 minutes and lt eq 3 days 642 | 643 | assertEq(flip.wards(address(cat)), values.collaterals[ilk_].liquidations); // liquidations == 1 => on 644 | assertEq(flip.wards(address(makerDeployer06)), 0); // Check deployer denied 645 | assertEq(flip.wards(address(pauseProxy)), 1); // Check pause_proxy ward 646 | } 647 | { 648 | GemJoinAbstract join = GemJoinAbstract(reg.join(ilk)); 649 | assertEq(join.wards(address(makerDeployer06)), 0); // Check deployer denied 650 | assertEq(join.wards(address(pauseProxy)), 1); // Check pause_proxy ward 651 | } 652 | } 653 | assertEq(sumlines, values.vat_Line); 654 | } 655 | 656 | // function testFailWrongDay() public { 657 | // vote(address(spell)); 658 | // scheduleWaitAndCastFailDay(); 659 | // } 660 | 661 | // function testFailTooEarly() public { 662 | // vote(address(spell)); 663 | // scheduleWaitAndCastFailEarly(); 664 | // } 665 | 666 | // function testFailTooLate() public { 667 | // vote(address(spell)); 668 | // scheduleWaitAndCastFailLate(); 669 | // } 670 | 671 | function testSpellIsCast() public { 672 | string memory description = new RwaSpell().description(); 673 | assertTrue(bytes(description).length > 0); 674 | // DS-Test can't handle strings directly, so cast to a bytes32. 675 | assertEq(stringToBytes32(spell.description()), 676 | stringToBytes32(description)); 677 | 678 | if(address(spell) != address(KOVAN_SPELL)) { 679 | assertEq(spell.expiration(), (block.timestamp + 30 days)); 680 | } else { 681 | assertEq(spell.expiration(), (SPELL_CREATED + 30 days)); 682 | } 683 | 684 | if (!spell.done()) { 685 | vote(address(spell)); 686 | scheduleWaitAndCast(); 687 | } 688 | 689 | assertTrue(spell.done()); 690 | 691 | // TODO: add these back into the test 692 | // checkSystemValues(afterSpell); 693 | 694 | // checkCollateralValues(afterSpell); 695 | } 696 | 697 | function testChainlogValues() public { 698 | if (!spell.done()) { 699 | vote(address(spell)); 700 | scheduleWaitAndCast(); 701 | } 702 | assertTrue(spell.done()); 703 | 704 | assertEq(chainlog.getAddress("RWA001"), addr.addr("RWA001")); 705 | assertEq(chainlog.getAddress("MCD_JOIN_RWA001_A"), addr.addr("MCD_JOIN_RWA001_A")); 706 | assertEq(chainlog.getAddress("RWA001_A_URN"), addr.addr("RWA001_A_URN")); 707 | assertEq(chainlog.getAddress("RWA001_A_INPUT_CONDUIT"), addr.addr("RWA001_A_INPUT_CONDUIT")); 708 | assertEq(chainlog.getAddress("RWA001_A_OUTPUT_CONDUIT"), addr.addr("RWA001_A_OUTPUT_CONDUIT")); 709 | assertEq(chainlog.getAddress("MIP21_LIQUIDATION_ORACLE"), addr.addr("MIP21_LIQUIDATION_ORACLE")); 710 | } 711 | 712 | function testSpellIsCast_RWA001_INTEGRATION_BUMP() public { 713 | if (!spell.done()) { 714 | vote(address(spell)); 715 | scheduleWaitAndCast(); 716 | assertTrue(spell.done()); 717 | } 718 | 719 | bumpSpell = new BumpSpell(); 720 | vote(address(bumpSpell)); 721 | 722 | bumpSpell.schedule(); 723 | 724 | uint256 castTime = block.timestamp + pause.delay(); 725 | hevm.warp(castTime); 726 | (, address pip, ,) = oracle.ilks("RWA001-A"); 727 | 728 | assertEq(DSValueAbstract(pip).read(), bytes32(1060 * WAD)); 729 | bumpSpell.cast(); 730 | assertEq(DSValueAbstract(pip).read(), bytes32(1070 * WAD)); 731 | } 732 | 733 | function testSpellIsCast_RWA001_INTEGRATION_TELL() public { 734 | if (!spell.done()) { 735 | vote(address(spell)); 736 | scheduleWaitAndCast(); 737 | assertTrue(spell.done()); 738 | } 739 | 740 | tellSpell = new TellSpell(); 741 | vote(address(tellSpell)); 742 | 743 | tellSpell.schedule(); 744 | 745 | uint256 castTime = block.timestamp + pause.delay(); 746 | hevm.warp(castTime); 747 | (, , , uint48 tocPre) = oracle.ilks("RWA001-A"); 748 | assertTrue(tocPre == 0); 749 | assertTrue(oracle.good("RWA001-A")); 750 | tellSpell.cast(); 751 | (, , , uint48 tocPost) = oracle.ilks("RWA001-A"); 752 | assertTrue(tocPost > 0); 753 | assertTrue(oracle.good("RWA001-A")); 754 | hevm.warp(block.timestamp + 600); 755 | assertTrue(!oracle.good("RWA001-A")); 756 | } 757 | 758 | function testSpellIsCast_RWA001_INTEGRATION_TELL_CURE_GOOD() public { 759 | if (!spell.done()) { 760 | vote(address(spell)); 761 | scheduleWaitAndCast(); 762 | assertTrue(spell.done()); 763 | } 764 | 765 | tellSpell = new TellSpell(); 766 | vote(address(tellSpell)); 767 | 768 | tellSpell.schedule(); 769 | 770 | uint256 castTime = block.timestamp + pause.delay(); 771 | hevm.warp(castTime); 772 | tellSpell.cast(); 773 | assertTrue(oracle.good(ilk)); 774 | hevm.warp(block.timestamp + 600); 775 | assertTrue(!oracle.good(ilk)); 776 | 777 | cureSpell = new CureSpell(); 778 | vote(address(cureSpell)); 779 | 780 | cureSpell.schedule(); 781 | castTime = block.timestamp + pause.delay(); 782 | hevm.warp(castTime); 783 | cureSpell.cast(); 784 | assertTrue(oracle.good(ilk)); 785 | (,,, uint48 toc) = oracle.ilks(ilk); 786 | assertEq(uint256(toc), 0); 787 | } 788 | 789 | function testFailSpellIsCast_RWA001_INTEGRATION_CURE() public { 790 | if (!spell.done()) { 791 | vote(address(spell)); 792 | scheduleWaitAndCast(); 793 | assertTrue(spell.done()); 794 | } 795 | 796 | cureSpell = new CureSpell(); 797 | vote(address(cureSpell)); 798 | 799 | cureSpell.schedule(); 800 | uint256 castTime = block.timestamp + pause.delay(); 801 | hevm.warp(castTime); 802 | cureSpell.cast(); 803 | } 804 | 805 | function testSpellIsCast_RWA001_INTEGRATION_TELL_CULL() public { 806 | if (!spell.done()) { 807 | vote(address(spell)); 808 | scheduleWaitAndCast(); 809 | assertTrue(spell.done()); 810 | } 811 | assertTrue(oracle.good("RWA001-A")); 812 | 813 | tellSpell = new TellSpell(); 814 | vote(address(tellSpell)); 815 | 816 | tellSpell.schedule(); 817 | 818 | uint256 castTime = block.timestamp + pause.delay(); 819 | hevm.warp(castTime); 820 | tellSpell.cast(); 821 | assertTrue(oracle.good("RWA001-A")); 822 | hevm.warp(block.timestamp + 600); 823 | assertTrue(!oracle.good("RWA001-A")); 824 | 825 | cullSpell = new CullSpell(); 826 | vote(address(cullSpell)); 827 | 828 | cullSpell.schedule(); 829 | castTime = block.timestamp + pause.delay(); 830 | hevm.warp(castTime); 831 | cullSpell.cast(); 832 | assertTrue(!oracle.good("RWA001-A")); 833 | (, address pip,,) = oracle.ilks("RWA001-A"); 834 | assertEq(DSValueAbstract(pip).read(), bytes32(0)); 835 | } 836 | 837 | function testSpellIsCast_RWA001_OPERATOR_LOCK_DRAW_CONDUITS_WIPE_FREE() public { 838 | 839 | if (!spell.done()) { 840 | vote(address(spell)); 841 | scheduleWaitAndCast(); 842 | assertTrue(spell.done()); 843 | } 844 | 845 | hevm.warp(now + 10 days); // Let rate be > 1 846 | 847 | // put 1 conti of MKR into this contract to push 848 | hevm.store( 849 | address(gov), 850 | keccak256(abi.encode(address(this), uint256(1))), 851 | bytes32(uint256(1)) 852 | ); 853 | hevm.store( 854 | address(rwagem), 855 | keccak256(abi.encode(address(this), uint256(0))), 856 | bytes32(uint256(2 ether)) 857 | ); 858 | hevm.store( 859 | address(rwagem), 860 | keccak256(abi.encode(address(this), uint256(1))), 861 | bytes32(uint256(1 ether)) 862 | ); 863 | // setting address(this) as operator 864 | hevm.store( 865 | address(rwaurn), 866 | keccak256(abi.encode(address(this), uint256(1))), 867 | bytes32(uint256(1)) 868 | ); 869 | 870 | (uint256 preInk, uint256 preArt) = vat.urns(ilk, address(rwaurn)); 871 | 872 | assertEq(rwagem.totalSupply(), 1 * WAD); 873 | assertEq(rwagem.balanceOf(address(this)), 1 * WAD); 874 | assertEq(rwaurn.can(address(this)), 1); 875 | 876 | rwagem.approve(address(rwaurn), 1 * WAD); 877 | rwaurn.lock(1 * WAD); 878 | assertEq(dai.balanceOf(address(rwaconduitout)), 0); 879 | rwaurn.draw(1 * WAD); 880 | 881 | (, uint256 rate,,,) = vat.ilks("RWA001-A"); 882 | 883 | uint256 dustInVat = vat.dai(address(rwaurn)); 884 | 885 | (uint256 ink, uint256 art) = vat.urns(ilk, address(rwaurn)); 886 | assertEq(ink, 1 * WAD + preInk); 887 | uint256 currArt = ((1 * RAD + dustInVat) / rate) + preArt; 888 | assertTrue(art >= currArt - 2 && art <= currArt + 2); // approximation for vat rounding 889 | assertEq(dai.balanceOf(address(rwaconduitout)), 1 * WAD); 890 | 891 | // wards 892 | hevm.store( 893 | address(rwaconduitout), 894 | keccak256(abi.encode(address(this), uint256(0))), 895 | bytes32(uint256(1)) 896 | ); 897 | 898 | // can 899 | hevm.store( 900 | address(rwaconduitout), 901 | keccak256(abi.encode(address(this), uint256(1))), 902 | bytes32(uint256(1)) 903 | ); 904 | 905 | assertEq(dai.balanceOf(address(rwaconduitout)), 1 * WAD); 906 | 907 | rwaconduitout.kiss(address(this)); 908 | rwaconduitout.pick(address(this)); 909 | 910 | rwaconduitout.push(); 911 | 912 | assertEq(dai.balanceOf(address(rwaconduitout)), 0); 913 | assertEq(dai.balanceOf(address(this)), 1 * WAD); 914 | 915 | hevm.warp(now + 10 days); 916 | 917 | (ink, art) = vat.urns(ilk, address(rwaurn)); 918 | assertEq(ink, 1 * WAD + preInk); 919 | currArt = ((1 * RAD + dustInVat) / rate) + preArt; 920 | assertTrue(art >= currArt - 2 && art <= currArt + 2); // approximation for vat rounding 921 | 922 | (ink,) = vat.urns(ilk, address(this)); 923 | assertEq(ink, 0); 924 | 925 | jug.drip("RWA001-A"); 926 | 927 | (, rate,,,) = vat.ilks("RWA001-A"); 928 | 929 | uint256 daiToPay = (art * rate - dustInVat) / RAY + 1; // extra wei rounding 930 | 931 | hevm.store( 932 | address(dai), 933 | keccak256(abi.encode(address(this), uint256(2))), 934 | bytes32(uint256(daiToPay)) 935 | ); // Forcing extra DAI balance to pay accumulated fee 936 | 937 | assertEq(dai.balanceOf(address(rwaconduitin)), 0); 938 | dai.transfer(address(rwaconduitin), daiToPay); 939 | assertEq(dai.balanceOf(address(rwaconduitin)), daiToPay); 940 | rwaconduitin.push(); 941 | 942 | assertEq(dai.balanceOf(address(rwaurn)), daiToPay); 943 | assertEq(dai.balanceOf(address(rwaconduitin)), 0); 944 | 945 | rwaurn.wipe(daiToPay); 946 | rwaurn.free(1 * WAD); 947 | (ink, art) = vat.urns(ilk, address(rwaurn)); 948 | assertEq(ink, preInk); 949 | assertTrue(art < 4); // wad -> rad conversion in wipe leaves some dust 950 | (ink,) = vat.urns(ilk, address(this)); 951 | assertEq(ink, 0); 952 | } 953 | 954 | function testSpellIsCast_RWA001_END() public { 955 | if (!spell.done()) { 956 | vote(address(spell)); 957 | scheduleWaitAndCast(); 958 | assertTrue(spell.done()); 959 | } 960 | 961 | endSpell = new EndSpell(); 962 | vote(address(endSpell)); 963 | 964 | endSpell.schedule(); 965 | 966 | uint256 castTime = block.timestamp + pause.delay(); 967 | hevm.warp(castTime); 968 | endSpell.cast(); 969 | 970 | // TODO: finish 971 | } 972 | } 973 | -------------------------------------------------------------------------------- /src/test/RwaUrn.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // RwaUrn.t.sol -- Tests for the Urn contract 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | import "ds-test/test.sol"; 24 | import "ds-token/token.sol"; 25 | import "ds-math/math.sol"; 26 | 27 | import {Vat} from "dss/vat.sol"; 28 | import {Jug} from 'dss/jug.sol'; 29 | import {Spotter} from "dss/spot.sol"; 30 | 31 | import {DaiJoin} from 'dss/join.sol'; 32 | import {AuthGemJoin} from "dss-gem-joins/join-auth.sol"; 33 | 34 | import {RwaToken} from "../RwaToken.sol"; 35 | import {RwaInputConduit, RwaOutputConduit} from "../RwaConduit.sol"; 36 | import {RwaLiquidationOracle} from "../RwaLiquidationOracle.sol"; 37 | import {RwaUrn} from "../RwaUrn.sol"; 38 | 39 | interface Hevm { 40 | function warp(uint256) external; 41 | function store(address,bytes32,bytes32) external; 42 | } 43 | 44 | contract RwaUltimateRecipient { 45 | DSToken dai; 46 | constructor(DSToken dai_) public { 47 | dai = dai_; 48 | } 49 | function transfer(address who, uint256 wad) public { 50 | dai.transfer(who, wad); 51 | } 52 | } 53 | 54 | contract TryCaller { 55 | function do_call(address addr, bytes calldata data) external returns (bool) { 56 | bytes memory _data = data; 57 | assembly { 58 | let ok := call(gas(), addr, 0, add(_data, 0x20), mload(_data), 0, 0) 59 | let free := mload(0x40) 60 | mstore(free, ok) 61 | mstore(0x40, add(free, 32)) 62 | revert(free, 32) 63 | } 64 | } 65 | 66 | function try_call(address addr, bytes calldata data) external returns (bool ok) { 67 | (, bytes memory returned) = address(this).call(abi.encodeWithSignature("do_call(address,bytes)", addr, data)); 68 | ok = abi.decode(returned, (bool)); 69 | } 70 | } 71 | 72 | contract RwaUser is TryCaller { 73 | RwaUrn urn; 74 | RwaOutputConduit outC; 75 | RwaInputConduit inC; 76 | 77 | constructor(RwaUrn urn_, RwaOutputConduit outC_, RwaInputConduit inC_) 78 | public { 79 | urn = urn_; 80 | outC = outC_; 81 | inC = inC_; 82 | } 83 | 84 | function approve(RwaToken tok, address who, uint256 wad) public { 85 | tok.approve(who, wad); 86 | } 87 | function pick(address who) public { 88 | outC.pick(who); 89 | } 90 | function lock(uint256 wad) public { 91 | urn.lock(wad); 92 | } 93 | function free(uint256 wad) public { 94 | urn.free(wad); 95 | } 96 | function draw(uint256 wad) public { 97 | urn.draw(wad); 98 | } 99 | function wipe(uint256 wad) public { 100 | urn.wipe(wad); 101 | } 102 | function can_pick(address who) public returns (bool ok) { 103 | ok = this.try_call( 104 | address(outC), 105 | abi.encodeWithSignature("pick(address)", who) 106 | ); 107 | } 108 | function can_draw(uint256 wad) public returns (bool ok) { 109 | ok = this.try_call( 110 | address(urn), 111 | abi.encodeWithSignature("draw(uint256)", wad) 112 | ); 113 | } 114 | function can_free(uint256 wad) public returns (bool ok) { 115 | ok = this.try_call( 116 | address(urn), 117 | abi.encodeWithSignature("free(uint256)", wad) 118 | ); 119 | } 120 | } 121 | 122 | contract TryPusher is TryCaller { 123 | function can_push(address wat) public returns (bool ok) { 124 | ok = this.try_call( 125 | address(wat), 126 | abi.encodeWithSignature("push()") 127 | ); 128 | } 129 | } 130 | 131 | contract RwaExampleTest is DSTest, DSMath, TryPusher { 132 | Hevm hevm; 133 | 134 | DSToken gov; 135 | DSToken dai; 136 | RwaToken rwa; 137 | 138 | Vat vat; 139 | Jug jug; 140 | address vow = address(123); 141 | Spotter spotter; 142 | 143 | DaiJoin daiJoin; 144 | AuthGemJoin gemJoin; 145 | 146 | RwaLiquidationOracle oracle; 147 | RwaUrn urn; 148 | 149 | RwaOutputConduit outConduit; 150 | RwaInputConduit inConduit; 151 | 152 | RwaUser usr; 153 | RwaUltimateRecipient rec; 154 | 155 | // debt ceiling of 400 dai 156 | uint256 ceiling = 400 ether; 157 | string doc = "Please sign on the dotted line."; 158 | 159 | string public constant name = "RWA-001"; 160 | string public constant symbol = "RWA001"; 161 | 162 | function rad(uint wad) internal pure returns (uint) { 163 | return wad * 10 ** 27; 164 | } 165 | function setUp() public { 166 | hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); 167 | hevm.warp(604411200); 168 | 169 | // deploy governance token 170 | gov = new DSToken('GOV'); 171 | gov.mint(100 ether); 172 | 173 | // deploy rwa token 174 | rwa = new RwaToken(name, symbol); 175 | 176 | // standard Vat setup 177 | vat = new Vat(); 178 | 179 | jug = new Jug(address(vat)); 180 | jug.file("vow", address(vow)); 181 | vat.rely(address(jug)); 182 | 183 | spotter = new Spotter(address(vat)); 184 | vat.rely(address(spotter)); 185 | 186 | dai = new DSToken("Dai"); 187 | daiJoin = new DaiJoin(address(vat), address(dai)); 188 | vat.rely(address(daiJoin)); 189 | dai.setOwner(address(daiJoin)); 190 | 191 | // the first RWA ilk is Acme Real World Assets Corporation 192 | vat.init("acme"); 193 | vat.file("Line", 100 * rad(ceiling)); 194 | vat.file("acme", "line", rad(ceiling)); 195 | 196 | jug.init("acme"); 197 | // $ bc -l <<< 'scale=27; e( l(1.08)/(60 * 60 * 24 * 365) )' 198 | uint256 EIGHT_PCT = 1000000002440418608258400030; 199 | jug.file("acme", "duty", EIGHT_PCT); 200 | 201 | oracle = new RwaLiquidationOracle(address(vat), vow); 202 | oracle.init( 203 | "acme", 204 | wmul(ceiling, 1.1 ether), 205 | doc, 206 | 2 weeks); 207 | vat.rely(address(oracle)); 208 | (,address pip,,) = oracle.ilks("acme"); 209 | 210 | spotter.file("acme", "mat", RAY); 211 | spotter.file("acme", "pip", pip); 212 | spotter.poke("acme"); 213 | 214 | gemJoin = new AuthGemJoin(address(vat), "acme", address(rwa)); 215 | vat.rely(address(gemJoin)); 216 | 217 | // deploy output dai conduit 218 | outConduit = new RwaOutputConduit(address(gov), address(dai)); 219 | // deploy urn 220 | urn = new RwaUrn(address(vat), address(jug), address(gemJoin), address(daiJoin), address(outConduit)); 221 | gemJoin.rely(address(urn)); 222 | // deploy input dai conduit, pointed permanently at the urn 223 | inConduit = new RwaInputConduit(address(gov), address(dai), address(urn)); 224 | 225 | // deploy user and ultimate dai recipient 226 | usr = new RwaUser(urn, outConduit, inConduit); 227 | rec = new RwaUltimateRecipient(dai); 228 | 229 | // fund user with rwa 230 | rwa.transfer(address(usr), 1 ether); 231 | 232 | // auth user to operate 233 | urn.hope(address(usr)); 234 | outConduit.hope(address(usr)); 235 | outConduit.kiss(address(rec)); 236 | 237 | usr.approve(rwa, address(urn), uint(-1)); 238 | } 239 | 240 | function test_file() public { 241 | urn.file("outputConduit", address(123)); 242 | assertEq(urn.outputConduit(), address(123)); 243 | urn.file("jug", address(456)); 244 | assertEq(address(urn.jug()), address(456)); 245 | } 246 | 247 | function test_unpick_and_pick_new_rec() public { 248 | // lock some acme and draw some dai 249 | usr.lock(1 ether); 250 | usr.draw(400 ether); 251 | 252 | // usr nominates ultimate recipient 253 | usr.pick(address(rec)); 254 | // the dai can be pushed 255 | assertTrue(can_push(address(outConduit))); 256 | 257 | // unpick current rec 258 | usr.pick(address(0)); 259 | 260 | // dai can't move 261 | assertTrue(! can_push(address(outConduit))); 262 | 263 | // deploy and whitelist new rec 264 | RwaUltimateRecipient newrec = new RwaUltimateRecipient(dai); 265 | outConduit.kiss(address(newrec)); 266 | 267 | usr.pick(address(newrec)); 268 | outConduit.push(); 269 | 270 | assertEq(dai.balanceOf(address(newrec)), 400 ether); 271 | } 272 | 273 | function test_cant_pick_unkissed_rec() public { 274 | RwaUltimateRecipient newrec = new RwaUltimateRecipient(dai); 275 | assertTrue(! usr.can_pick(address(newrec))); 276 | } 277 | 278 | function test_lock_and_draw() public { 279 | // check initial balances 280 | assertEq(dai.balanceOf(address(outConduit)), 0); 281 | assertEq(dai.balanceOf(address(rec)), 0); 282 | 283 | hevm.warp(now + 10 days); // Let rate be > 1 284 | 285 | assertEq(vat.dai(address(urn)), 0); 286 | 287 | (uint256 ink, uint256 art) = vat.urns("acme", address(urn)); 288 | assertEq(ink, 0); 289 | assertEq(art, 0); 290 | 291 | usr.lock(1 ether); 292 | usr.draw(399 ether); // with 400 will fail due vat ceiling (rounding) 293 | 294 | assertEq(vat.dai(address(urn)), 463899466724981907732616508); // dust from divup 295 | 296 | (, uint256 rate,,,) = vat.ilks("acme"); 297 | (ink, art) = vat.urns("acme", address(urn)); 298 | assertEq(ink, 1 ether); 299 | assertEq(art, (rad(399 ether) + 463899466724981907732616508) / rate); 300 | 301 | // check the amount went to the output conduit 302 | assertEq(dai.balanceOf(address(outConduit)), 399 ether); 303 | assertEq(dai.balanceOf(address(rec)), 0); 304 | 305 | // usr nominates ultimate recipient 306 | usr.pick(address(rec)); 307 | // push the amount to the receiver 308 | outConduit.push(); 309 | assertEq(dai.balanceOf(address(outConduit)), 0); 310 | assertEq(dai.balanceOf(address(rec)), 399 ether); 311 | } 312 | 313 | function test_draw_exceeds_debt_ceiling() public { 314 | usr.lock(1 ether); 315 | assertTrue(! usr.can_draw(500 ether)); 316 | } 317 | 318 | function test_cant_draw_unless_hoped() public { 319 | usr.lock(1 ether); 320 | 321 | RwaUser rando = new RwaUser(urn, outConduit, inConduit); 322 | assertTrue(! rando.can_draw(100 ether)); 323 | 324 | urn.hope(address(rando)); 325 | assertEq(dai.balanceOf(address(outConduit)), 0); 326 | rando.draw(100 ether); 327 | assertEq(dai.balanceOf(address(outConduit)), 100 ether); 328 | } 329 | 330 | function test_partial_repay() public { 331 | usr.lock(1 ether); 332 | usr.draw(400 ether); 333 | 334 | // usr nominates ultimate recipient 335 | usr.pick(address(rec)); 336 | outConduit.push(); 337 | 338 | hevm.warp(now + 30 days); 339 | 340 | rec.transfer(address(inConduit), 100 ether); 341 | assertEq(dai.balanceOf(address(inConduit)), 100 ether); 342 | 343 | inConduit.push(); 344 | usr.wipe(100 ether); 345 | assertTrue(! usr.can_free(1 ether)); 346 | usr.free(0.1 ether); 347 | 348 | (uint ink, uint art) = vat.urns("acme", address(urn)); 349 | // > 300 because of accumulated interest 350 | assertTrue(art > 300 ether); 351 | assertTrue(art < 301 ether); 352 | assertEq(ink, 0.9 ether); 353 | assertEq(dai.balanceOf(address(inConduit)), 0 ether); 354 | } 355 | 356 | function test_partial_repay_fuzz(uint256 drawAmount, uint256 wipeAmount, uint256 drawTime, uint256 wipeTime) public { 357 | // Convert to reasonable numbers 358 | drawAmount = (drawAmount % 300 ether) + 100 ether; // 100-400 ether 359 | wipeAmount = wipeAmount % drawAmount; // 0-drawAmount ether 360 | drawTime = drawTime % 15 days; // 0-15 days 361 | wipeTime = wipeTime % 15 days; // 0-15 days 362 | 363 | usr.lock(1 ether); 364 | 365 | hevm.warp(now + drawTime); 366 | jug.drip("acme"); 367 | 368 | usr.draw(drawAmount); 369 | 370 | // usr nominates ultimate recipient 371 | usr.pick(address(rec)); 372 | outConduit.push(); 373 | 374 | hevm.warp(now + wipeTime); 375 | jug.drip("acme"); 376 | 377 | rec.transfer(address(inConduit), wipeAmount); 378 | assertEq(dai.balanceOf(address(inConduit)), wipeAmount); 379 | 380 | inConduit.push(); 381 | usr.wipe(wipeAmount); 382 | } 383 | 384 | function test_repay_rounding_fuzz(uint256 drawAmt, uint256 drawTime, uint256 wipeTime) public { 385 | // Convert to reasonable numbers 386 | drawAmt = (drawAmt % 300 ether) + 99.99 ether; // 99.99-399.99 ether 387 | drawTime = drawTime % 15 days; // 0-15 days 388 | wipeTime = wipeTime % 15 days; // 0-15 days 389 | 390 | (uint256 ink, uint256 art) = vat.urns("acme", address(urn)); 391 | assertEq(ink, 0); 392 | assertEq(art, 0); 393 | 394 | usr.lock(1 ether); 395 | 396 | hevm.warp(now + drawTime); 397 | jug.drip("acme"); 398 | 399 | usr.draw(drawAmt); 400 | 401 | uint256 urnVatDust = vat.dai(address(urn)); 402 | 403 | // A draw should leave less than 2 RAY dust 404 | assertTrue(urnVatDust < 2 * RAY); 405 | 406 | (, uint256 rate,,,) = vat.ilks("acme"); 407 | (ink, art) = vat.urns("acme", address(urn)); 408 | assertEq(ink, 1 ether); 409 | assertEq(art, (rad(drawAmt) + urnVatDust) / rate); 410 | 411 | // usr nominates ultimate recipient 412 | usr.pick(address(rec)); 413 | outConduit.push(); 414 | 415 | hevm.warp(now + wipeTime); 416 | jug.drip("acme"); 417 | 418 | (, rate,,,) = vat.ilks("acme"); 419 | 420 | uint256 fullWipeAmt = art * rate / RAY; 421 | if (fullWipeAmt * RAY < art * rate) { 422 | fullWipeAmt += 1; 423 | } 424 | 425 | // Forcing extra DAI balance to pay accumulated fee 426 | hevm.store( 427 | address(dai), 428 | keccak256(abi.encode(address(rec), uint256(3))), 429 | bytes32(uint256(fullWipeAmt)) 430 | ); 431 | hevm.store( 432 | address(dai), 433 | bytes32(uint256(2)), 434 | bytes32(uint256(fullWipeAmt)) 435 | ); 436 | hevm.store( 437 | address(vat), 438 | keccak256(abi.encode(address(daiJoin), uint256(5))), 439 | bytes32(uint256(fullWipeAmt * RAY)) 440 | ); 441 | // 442 | 443 | rec.transfer(address(inConduit), fullWipeAmt); 444 | assertEq(dai.balanceOf(address(inConduit)), fullWipeAmt); 445 | 446 | inConduit.push(); 447 | usr.wipe(fullWipeAmt); 448 | 449 | (, art) = vat.urns("acme", address(urn)); 450 | assertEq(art, 0); 451 | 452 | uint256 newUrnVatDust = vat.dai(address(urn)); 453 | // A wipe should leave less than 1 RAY dust 454 | assertTrue(newUrnVatDust - urnVatDust < RAY); 455 | } 456 | 457 | function test_full_repay() public { 458 | usr.lock(1 ether); 459 | usr.draw(400 ether); 460 | 461 | // usr nominates ultimate recipient 462 | usr.pick(address(rec)); 463 | outConduit.push(); 464 | 465 | rec.transfer(address(inConduit), 400 ether); 466 | 467 | inConduit.push(); 468 | usr.wipe(400 ether); 469 | usr.free(1 ether); 470 | 471 | (uint ink, uint art) = vat.urns("acme", address(urn)); 472 | assertEq(art, 0); 473 | assertEq(ink, 0); 474 | assertEq(rwa.balanceOf(address(usr)), 1 ether); 475 | } 476 | 477 | function test_oracle_cure() public { 478 | usr.lock(1 ether); 479 | assertTrue(usr.can_draw(10 ether)); 480 | 481 | // flash the liquidation beacon 482 | vat.file("acme", "line", rad(0)); 483 | oracle.tell("acme"); 484 | 485 | // not able to borrow 486 | assertTrue(! usr.can_draw(10 ether)); 487 | 488 | hevm.warp(block.timestamp + 1 weeks); 489 | 490 | oracle.cure("acme"); 491 | vat.file("acme", "line", rad(ceiling)); 492 | assertTrue(oracle.good("acme")); 493 | 494 | // able to borrow 495 | assertEq(dai.balanceOf(address(rec)), 0); 496 | usr.draw(100 ether); 497 | // usr nominates ultimate recipient 498 | usr.pick(address(rec)); 499 | outConduit.push(); 500 | assertEq(dai.balanceOf(address(rec)), 100 ether); 501 | } 502 | 503 | function testFail_oracle_cure_unknown_ilk() public { 504 | // unknown ilk ecma 505 | oracle.cure("ecma"); 506 | } 507 | 508 | function testFail_oracle_cure_not_in_remediation() public { 509 | oracle.cure("acme"); 510 | } 511 | 512 | function testFail_oracle_cure_not_in_remediation_anymore() public { 513 | usr.lock(1 ether); 514 | assertTrue(usr.can_draw(10 ether)); 515 | 516 | // flash the liquidation beacon 517 | vat.file("acme", "line", rad(0)); 518 | oracle.tell("acme"); 519 | 520 | // not able to borrow 521 | assertTrue(! usr.can_draw(10 ether)); 522 | 523 | hevm.warp(block.timestamp + 1 weeks); 524 | 525 | oracle.cure("acme"); 526 | vat.file("acme", "line", rad(ceiling)); 527 | assertTrue(oracle.good("acme")); 528 | 529 | // able to borrow 530 | assertEq(dai.balanceOf(address(rec)), 0); 531 | usr.draw(100 ether); 532 | // usr nominates ultimate recipient 533 | usr.pick(address(rec)); 534 | outConduit.push(); 535 | assertEq(dai.balanceOf(address(rec)), 100 ether); 536 | oracle.cure("acme"); 537 | } 538 | 539 | function test_oracle_cull() public { 540 | usr.lock(1 ether); 541 | // not at full utilisation 542 | usr.draw(200 ether); 543 | 544 | // flash the liquidation beacon 545 | vat.file("acme", "line", rad(0)); 546 | oracle.tell("acme"); 547 | 548 | // not able to borrow 549 | assertTrue(! usr.can_draw(10 ether)); 550 | 551 | hevm.warp(block.timestamp + 1 weeks); 552 | // still in remeditation period 553 | assertTrue(oracle.good("acme")); 554 | 555 | hevm.warp(block.timestamp + 2 weeks); 556 | 557 | assertEq(vat.gem("acme", address(oracle)), 0); 558 | // remediation period has elapsed 559 | assertTrue(! oracle.good("acme")); 560 | oracle.cull("acme", address(urn)); 561 | 562 | assertTrue(! usr.can_draw(10 ether)); 563 | 564 | (uint ink, uint art) = vat.urns("acme", address(urn)); 565 | assertEq(ink, 0); 566 | assertEq(art, 0); 567 | 568 | assertEq(vat.sin(vow), rad(200 ether)); 569 | 570 | // after the write-off, the gem goes to the oracle 571 | assertEq(vat.gem("acme", address(oracle)), 1 ether); 572 | 573 | spotter.poke("acme"); 574 | (,,uint256 spot ,,) = vat.ilks("acme"); 575 | assertEq(spot, 0); 576 | } 577 | 578 | function test_oracle_unremedied_loan_is_not_good() public { 579 | usr.lock(1 ether); 580 | usr.draw(200 ether); 581 | 582 | vat.file("acme", "line", 0); 583 | oracle.tell("acme"); 584 | assertTrue(oracle.good("acme")); 585 | 586 | hevm.warp(block.timestamp + 3 weeks); 587 | assertTrue(! oracle.good("acme")); 588 | } 589 | 590 | function test_oracle_cull_two_urns() public { 591 | RwaUrn urn2 = new RwaUrn( 592 | address(vat), 593 | address(jug), 594 | address(gemJoin), 595 | address(daiJoin), 596 | address(outConduit) 597 | ); 598 | gemJoin.rely(address(urn2)); 599 | RwaUser usr2 = new RwaUser(urn2, outConduit, inConduit); 600 | usr.approve(rwa, address(this), uint(-1)); 601 | rwa.transferFrom(address(usr), address(usr2), 0.5 ether); 602 | usr2.approve(rwa, address(urn2), uint(-1)); 603 | urn2.hope(address(usr2)); 604 | usr.lock(0.5 ether); 605 | usr2.lock(0.5 ether); 606 | usr.draw(100 ether); 607 | usr2.draw(100 ether); 608 | 609 | assertTrue(usr.can_draw(1 ether)); 610 | assertTrue(usr2.can_draw(1 ether)); 611 | 612 | vat.file("acme", "line", 0); 613 | oracle.tell("acme"); 614 | 615 | assertTrue(! usr.can_draw(1 ether)); 616 | assertTrue(! usr2.can_draw(1 ether)); 617 | 618 | hevm.warp(block.timestamp + 3 weeks); 619 | 620 | oracle.cull("acme", address(urn)); 621 | assertEq(vat.sin(vow), rad(100 ether)); 622 | oracle.cull("acme", address(urn2)); 623 | assertEq(vat.sin(vow), rad(200 ether)); 624 | } 625 | 626 | function test_oracle_bump() public { 627 | usr.lock(1 ether); 628 | usr.draw(400 ether); 629 | 630 | // usr nominates ultimate recipient 631 | usr.pick(address(rec)); 632 | outConduit.push(); 633 | 634 | // can't borrow more, ceiling exceeded 635 | assertTrue(! usr.can_draw(1 ether)); 636 | 637 | // increase ceiling by 200 dai 638 | vat.file("acme", "line", rad(ceiling + 200 ether)); 639 | 640 | // still can't borrow much more, vault is unsafe 641 | assertTrue(usr.can_draw(1 ether)); 642 | assertTrue(! usr.can_draw(200 ether)); 643 | 644 | // bump the price of acme 645 | oracle.bump("acme", wmul(ceiling + 200 ether, 1.1 ether)); 646 | spotter.poke("acme"); 647 | 648 | usr.draw(200 ether); 649 | // recipient must be picked again for 2nd push 650 | usr.pick(address(rec)); 651 | outConduit.push(); 652 | 653 | assertEq(dai.balanceOf(address(rec)), 600 ether); 654 | } 655 | 656 | function testFail_oracle_bump_unknown_ilk() public { 657 | // unknown ilk ecma 658 | oracle.bump("ecma", wmul(ceiling + 200 ether, 1.1 ether)); 659 | } 660 | 661 | function testFail_oracle_bump_in_remediation() public { 662 | vat.file("acme", "line", 0); 663 | oracle.tell("acme"); 664 | oracle.bump("acme", wmul(ceiling + 200 ether, 1.1 ether)); 665 | } 666 | 667 | function test_quit() public { 668 | usr.lock(1 ether); 669 | usr.draw(400 ether); 670 | 671 | // usr nominates ultimate recipient 672 | usr.pick(address(rec)); 673 | outConduit.push(); 674 | 675 | rec.transfer(address(inConduit), 400 ether); 676 | 677 | inConduit.push(); 678 | vat.cage(); 679 | assertEq(dai.balanceOf(address(urn)), 400 ether); 680 | assertEq(dai.balanceOf(address(outConduit)), 0); 681 | urn.quit(); 682 | assertEq(dai.balanceOf(address(urn)), 0); 683 | assertEq(dai.balanceOf(address(outConduit)), 400 ether); 684 | } 685 | 686 | function testFail_quit() public { 687 | usr.lock(1 ether); 688 | usr.draw(400 ether); 689 | 690 | // usr nominates ultimate recipient 691 | usr.pick(address(rec)); 692 | outConduit.push(); 693 | 694 | rec.transfer(address(inConduit), 400 ether); 695 | 696 | inConduit.push(); 697 | urn.quit(); 698 | } 699 | } 700 | -------------------------------------------------------------------------------- /src/test/addresses_kovan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // addresses_kovan.sol -- List of Kovan addresses for testing 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | contract Addresses { 24 | 25 | mapping (bytes32 => address) public addr; 26 | 27 | constructor() public { 28 | addr["CHANGELOG"] = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F; 29 | addr["MULTICALL"] = 0xC6D81A2e375Eee15a20E6464b51c5FC6Bb949fdA; 30 | addr["FAUCET"] = 0x57aAeAE905376a4B1899bA81364b4cE2519CBfB3; 31 | addr["MCD_DEPLOY"] = 0x13141b8a5E4A82Ebc6b636849dd6A515185d6236; 32 | addr["FLIP_FAB"] = 0x7c890e1e492FDDA9096353D155eE1B26C1656a62; 33 | addr["MCD_GOV"] = 0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD; 34 | addr["GOV_GUARD"] = 0xE50303C6B67a2d869684EFb09a62F6aaDD06387B; 35 | addr["MCD_ADM"] = 0x27E0c9567729Ea6e3241DE74B3dE499b7ddd3fe6; 36 | addr["VOTE_PROXY_FACTORY"] = 0x1400798AA746457E467A1eb9b3F3f72C25314429; 37 | addr["MCD_VAT"] = 0xbA987bDB501d131f766fEe8180Da5d81b34b69d9; 38 | addr["MCD_JUG"] = 0xcbB7718c9F39d05aEEDE1c472ca8Bf804b2f1EaD; 39 | addr["MCD_CAT"] = 0xdDb5F7A3A5558b9a6a1f3382BD75E2268d1c6958; 40 | addr["MCD_VOW"] = 0x0F4Cbe6CBA918b7488C26E29d9ECd7368F38EA3b; 41 | addr["MCD_JOIN_DAI"] = 0x5AA71a3ae1C0bd6ac27A1f28e1415fFFB6F15B8c; 42 | addr["MCD_FLAP"] = 0xc6d3C83A080e2Ef16E4d7d4450A869d0891024F5; 43 | addr["MCD_FLOP"] = 0x52482a3100F79FC568eb2f38C4a45ba457FBf5fA; 44 | addr["MCD_PAUSE"] = 0x8754E6ecb4fe68DaA5132c2886aB39297a5c7189; 45 | addr["MCD_PAUSE_PROXY"] = 0x0e4725db88Bb038bBa4C4723e91Ba183BE11eDf3; 46 | addr["MCD_GOV_ACTIONS"] = 0x0Ca17E81073669741714354f16D800af64e95C75; 47 | addr["MCD_DAI"] = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa; 48 | addr["MCD_SPOT"] = 0x3a042de6413eDB15F2784f2f97cC68C7E9750b2D; 49 | addr["MCD_POT"] = 0xEA190DBDC7adF265260ec4dA6e9675Fd4f5A78bb; 50 | addr["MCD_END"] = 0x24728AcF2E2C403F5d2db4Df6834B8998e56aA5F; 51 | addr["MCD_ESM"] = 0x0C376764F585828ffB52471c1c35f855e312a06c; 52 | addr["PROXY_ACTIONS"] = 0xd1D24637b9109B7f61459176EdcfF9Be56283a7B; 53 | addr["PROXY_ACTIONS_END"] = 0x7c3f28f174F2b0539C202a5307Ff48efa61De982; 54 | addr["PROXY_ACTIONS_DSR"] = 0xc5CC1Dfb64A62B9C7Bb6Cbf53C2A579E2856bf92; 55 | addr["CDP_MANAGER"] = 0x1476483dD8C35F25e568113C5f70249D3976ba21; 56 | addr["DSR_MANAGER"] = 0x7f5d60432DE4840a3E7AE7218f7D6b7A2412683a; 57 | addr["GET_CDPS"] = 0x592301a23d37c591C5856f28726AF820AF8e7014; 58 | addr["ILK_REGISTRY"] = 0xedE45A0522CA19e979e217064629778d6Cc2d9Ea; 59 | addr["OSM_MOM"] = 0x5dA9D1C3d4f1197E5c52Ff963916Fe84D2F5d8f3; 60 | addr["FLIPPER_MOM"] = 0x50dC6120c67E456AdA2059cfADFF0601499cf681; 61 | addr["MCD_IAM_AUTO_LINE"] = 0xe7D7d61c0ed9306B6c93E7C65F6C9DDF38b9320b; 62 | addr["PROXY_FACTORY"] = 0xe11E3b391F7E8bC47247866aF32AF67Dd58Dc800; 63 | addr["PROXY_REGISTRY"] = 0x64A436ae831C1672AE81F674CAb8B6775df3475C; 64 | addr["ETH"] = 0xd0A1E359811322d97991E03f863a0C30C2cF029C; 65 | addr["PIP_ETH"] = 0x75dD74e8afE8110C8320eD397CcCff3B8134d981; 66 | addr["MCD_JOIN_ETH_A"] = 0x775787933e92b709f2a3C70aa87999696e74A9F8; 67 | addr["MCD_FLIP_ETH_A"] = 0x750295A8db0580F32355f97de7918fF538c818F1; 68 | addr["MCD_JOIN_ETH_B"] = 0xd19A770F00F89e6Dd1F12E6D6E6839b95C084D85; 69 | addr["MCD_FLIP_ETH_B"] = 0x360e15d419c14f6060c88Ac0741323C37fBfDa2D; 70 | addr["BAT"] = 0x9f8cFB61D3B2aF62864408DD703F9C3BEB55dff7; 71 | addr["PIP_BAT"] = 0x5C40C9Eb35c76069fA4C3A00EA59fAc6fFA9c113; 72 | addr["MCD_JOIN_BAT_A"] = 0x2a4C485B1B8dFb46acCfbeCaF75b6188A59dBd0a; 73 | addr["MCD_FLIP_BAT_A"] = 0x44Acf0eb2C7b9F0B55723e5289437AefE8ef7a1c; 74 | addr["USDC"] = 0xBD84be3C303f6821ab297b840a99Bd0d4c4da6b5; 75 | addr["PIP_USDC"] = 0x4c51c2584309b7BF328F89609FDd03B3b95fC677; 76 | addr["MCD_JOIN_USDC_A"] = 0x4c514656E7dB7B859E994322D2b511d99105C1Eb; 77 | addr["MCD_FLIP_USDC_A"] = 0x17C144eaC1B3D6777eF2C3fA1F98e3BC3c18DB4F; 78 | addr["MCD_JOIN_USDC_B"] = 0xaca10483e7248453BB6C5afc3e403e8b7EeDF314; 79 | addr["MCD_FLIP_USDC_B"] = 0x6DCd745D91AB422e962d08Ed1a9242adB47D8d0C; 80 | addr["WBTC"] = 0x7419f744bBF35956020C1687fF68911cD777f865; 81 | addr["PIP_WBTC"] = 0x2f38a1bD385A9B395D01f2Cbf767b4527663edDB; 82 | addr["MCD_JOIN_WBTC_A"] = 0xB879c7d51439F8e7AC6b2f82583746A0d336e63F; 83 | addr["MCD_FLIP_WBTC_A"] = 0x80Fb08f2EF268f491D6B58438326a3006C1a0e09; 84 | addr["TUSD"] = 0xD6CE59F06Ff2070Dd5DcAd0866A7D8cd9270041a; 85 | addr["PIP_TUSD"] = 0xE4bAECdba7A8Ff791E14c6BF7e8089Dfdf75C7E7; 86 | addr["MCD_JOIN_TUSD_A"] = 0xe53f6755A031708c87d80f5B1B43c43892551c17; 87 | addr["MCD_FLIP_TUSD_A"] = 0x867711f695e11663eC8adCFAAD2a152eFBA56dfD; 88 | addr["ZRX"] = 0xC2C08A566aD44129E69f8FC98684EAA28B01a6e7; 89 | addr["PIP_ZRX"] = 0x218037a42947E634191A231fcBAEAE8b16a39b3f; 90 | addr["MCD_JOIN_ZRX_A"] = 0x85D38fF6a6FCf98bD034FB5F9D72cF15e38543f2; 91 | addr["MCD_FLIP_ZRX_A"] = 0x798eB3126f1d5cb54743E3e93D3512C58f461084; 92 | addr["KNC"] = 0x9800a0a3c7e9682e1AEb7CAA3200854eFD4E9327; 93 | addr["PIP_KNC"] = 0x10799280EF9d7e2d037614F5165eFF2cB8522651; 94 | addr["MCD_JOIN_KNC_A"] = 0xE42427325A0e4c8e194692FfbcACD92C2C381598; 95 | addr["MCD_FLIP_KNC_A"] = 0xF2c21882Bd14A5F7Cb46291cf3c86E53057FaD06; 96 | addr["MANA"] = 0x221F4D62636b7B51b99e36444ea47Dc7831c2B2f; 97 | addr["PIP_MANA"] = 0xE97D2b077Fe19c80929718d377981d9F754BF36e; 98 | addr["MCD_JOIN_MANA_A"] = 0xdC9Fe394B27525e0D9C827EE356303b49F607aaF; 99 | addr["MCD_FLIP_MANA_A"] = 0xb2B7430D49D2D2e7abb6a6B4699B2659c141A2a6; 100 | addr["USDT"] = 0x9245BD36FA20fcD292F4765c4b5dF83Dc3fD5e86; 101 | addr["PIP_USDT"] = 0x3588A7973D41AaeA7B203549553C991C4311951e; 102 | addr["MCD_JOIN_USDT_A"] = 0x9B011a74a690dFd9a1e4996168d3EcBDE73c2226; 103 | addr["MCD_FLIP_USDT_A"] = 0x113733e00804e61D5fd8b107Ca11b4569B6DA95D; 104 | addr["PAXUSD"] = 0xa6383AF46c36219a472b9549d70E4768dfA8894c; 105 | addr["PIP_PAXUSD"] = 0xD01fefed46eb21cd057bAa14Ff466842C31a0Cd9; 106 | addr["MCD_JOIN_PAXUSD_A"] = 0x3d6a14C9542B429a4e3d255F6687754d4898D897; 107 | addr["MCD_FLIP_PAXUSD_A"] = 0x88001b9C8192cbf43e14323B809Ae6C4e815E12E; 108 | addr["COMP"] = 0x1dDe24ACE93F9F638Bfd6fCE1B38b842703Ea1Aa; 109 | addr["PIP_COMP"] = 0xcc10b1C53f4BFFEE19d0Ad00C40D7E36a454D5c4; 110 | addr["MCD_JOIN_COMP_A"] = 0x16D567c1F6824ffFC460A11d48F61E010ae43766; 111 | addr["MCD_FLIP_COMP_A"] = 0x2917a962BC45ED48497de85821bddD065794DF6C; 112 | addr["LRC"] = 0xF070662e48843934b5415f150a18C250d4D7B8aB; 113 | addr["PIP_LRC"] = 0xcEE47Bb8989f625b5005bC8b9f9A0B0892339721; 114 | addr["MCD_JOIN_LRC_A"] = 0x436286788C5dB198d632F14A20890b0C4D236800; 115 | addr["MCD_FLIP_LRC_A"] = 0xfC9496337538235669F4a19781234122c9455897; 116 | addr["LINK"] = 0xa36085F69e2889c224210F603D836748e7dC0088; 117 | addr["PIP_LINK"] = 0x20D5A457e49D05fac9729983d9701E0C3079Efac; 118 | addr["MCD_JOIN_LINK_A"] = 0xF4Df626aE4fb446e2Dcce461338dEA54d2b9e09b; 119 | addr["MCD_FLIP_LINK_A"] = 0xfbDCDF5Bd98f68cEfc3f37829189b97B602eCFF2; 120 | addr["BAL"] = 0x630D82Cbf82089B09F71f8d3aAaff2EBA6f47B15; 121 | addr["PIP_BAL"] = 0x4fd34872F3AbC07ea6C45c7907f87041C0801DdE; 122 | addr["MCD_JOIN_BAL_A"] = 0x8De5EA9251E0576e3726c8766C56E27fAb2B6597; 123 | addr["MCD_FLIP_BAL_A"] = 0xF6d19CC05482Ef7F73f09c1031BA01567EF6ac0f; 124 | addr["YFI"] = 0x251F1c3077FEd1770cB248fB897100aaE1269FFC; 125 | addr["PIP_YFI"] = 0x9D8255dc4e25bB85e49c65B21D8e749F2293862a; 126 | addr["MCD_JOIN_YFI_A"] = 0x5b683137481F2FE683E2f2385792B1DeB018050F; 127 | addr["MCD_FLIP_YFI_A"] = 0x5eB5D3B028CD255d79019f7C44a502b31bFFde9d; 128 | addr["GUSD"] = 0x31D8EdbF6F33ef858c80d68D06Ec83f33c2aA150; 129 | addr["PIP_GUSD"] = 0xb6630DE6Eda0f3f3d96Db4639914565d6b82CfEF; 130 | addr["MCD_JOIN_GUSD_A"] = 0x0c6B26e6AB583D2e4528034037F74842ea988909; 131 | addr["MCD_FLIP_GUSD_A"] = 0xf6c0e36a76F2B9F7Bd568155F3fDc53ff1be1Aeb; 132 | addr["UNI"] = 0x0C527850e5D6B2B406F1d65895d5b17c5A29Ce51; 133 | addr["PIP_UNI"] = 0xe573a75BF4827658F6D600FD26C205a3fe34ee28; 134 | addr["MCD_JOIN_UNI_A"] = 0xb6E6EE050B4a74C8cc1DfdE62cAC8C6d9D8F4CAa; 135 | addr["MCD_FLIP_UNI_A"] = 0x6EE8a47eA5d7cF0C951eDc57141Eb9593A36e680; 136 | addr["RENBTC"] = 0xe3dD56821f8C422849AF4816fE9B3c53c6a2F0Bd; 137 | addr["PIP_RENBTC"] = 0x2f38a1bD385A9B395D01f2Cbf767b4527663edDB; 138 | addr["MCD_JOIN_RENBTC_A"] = 0x12F1F6c7E5fDF1B671CebFBDE974341847d0Caa4; 139 | addr["MCD_FLIP_RENBTC_A"] = 0x2a2E2436370e98505325111A6b98F63d158Fedc4; 140 | addr["AAVE"] = 0x7B339a530Eed72683F56868deDa87BbC64fD9a12; 141 | addr["PIP_AAVE"] = 0xd2d9B1355Ea96567E7D6C7A6945f5c7ec8150Cc9; 142 | addr["MCD_JOIN_AAVE_A"] = 0x9f1Ed3219035e6bDb19E0D95d316c7c39ad302EC; 143 | addr["MCD_FLIP_AAVE_A"] = 0x3c84d572749096b67e4899A95430201DF79b8403; 144 | addr["PROXY_PAUSE_ACTIONS"] = 0x7c52826c1efEAE3199BDBe68e3916CC3eA222E29; 145 | addr["UNIV2DAIETH"] = 0xB10cf58E08b94480fCb81d341A63295eBb2062C2; 146 | addr["PIP_UNIV2DAIETH"] = 0x1AE7D6891a5fdAafAd2FE6D894bffEa48F8b2454; 147 | addr["MCD_JOIN_UNIV2DAIETH_A"] = 0x03f18d97D25c13FecB15aBee143276D3bD2742De; 148 | addr["MCD_FLIP_UNIV2DAIETH_A"] = 0x0B6C3512C8D4300d566b286FC4a554dAC217AaA6; 149 | addr["PROXY_PAUSE_ACTIONS"] = 0x7c52826c1efEAE3199BDBe68e3916CC3eA222E29; 150 | addr["PROXY_DEPLOYER"] = 0xA9fCcB07DD3f774d5b9d02e99DE1a27f47F91189; 151 | addr["RWA001"] = 0x8F9A8cbBdfb93b72d646c8DEd6B4Fe4D86B315cB; 152 | addr["MCD_JOIN_RWA001_A"] = 0x029A554f252373e146f76Fa1a7455f73aBF4d38e; 153 | addr["RWA001_A_URN"] = 0x3Ba90D86f7E3218C48b7E0FCa959EcF43d9A30F4; 154 | addr["RWA001_A_INPUT_CONDUIT"] = 0xB944B07EC3B680b2cEA753125667F7663d424DC3; 155 | addr["RWA001_A_OUTPUT_CONDUIT"] = 0xc54fEee07421EAB8000AC8c921c0De9DbfbE780B; 156 | addr["MIP21_LIQUIDATION_ORACLE"] = 0x2881c5dF65A8D81e38f7636122aFb456514804CC; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/test/rates.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // 3 | // rates.sol -- List of APY rates for testing 4 | // 5 | // Copyright (C) 2020-2021 Lev Livnev 6 | // Copyright (C) 2021-2022 Dai Foundation 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU Affero General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU Affero General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU Affero General Public License 19 | // along with this program. If not, see . 20 | 21 | pragma solidity 0.6.12; 22 | 23 | contract Rates { 24 | 25 | mapping (uint256 => uint256) public rates; 26 | 27 | constructor() public { 28 | rates[0] = 1000000000000000000000000000; 29 | rates[25] = 1000000000079175551708715274; 30 | rates[50] = 1000000000158153903837946257; 31 | rates[75] = 1000000000236936036262880196; 32 | rates[100] = 1000000000315522921573372069; 33 | rates[125] = 1000000000393915525145987602; 34 | rates[150] = 1000000000472114805215157978; 35 | rates[175] = 1000000000550121712943459312; 36 | rates[200] = 1000000000627937192491029810; 37 | rates[225] = 1000000000705562181084137268; 38 | rates[250] = 1000000000782997609082909351; 39 | rates[275] = 1000000000860244400048238898; 40 | rates[300] = 1000000000937303470807876289; 41 | rates[325] = 1000000001014175731521720677; 42 | rates[350] = 1000000001090862085746321732; 43 | rates[375] = 1000000001167363430498603315; 44 | rates[400] = 1000000001243680656318820312; 45 | rates[425] = 1000000001319814647332759691; 46 | rates[450] = 1000000001395766281313196627; 47 | rates[475] = 1000000001471536429740616381; 48 | rates[500] = 1000000001547125957863212448; 49 | rates[525] = 1000000001622535724756171269; 50 | rates[550] = 1000000001697766583380253701; 51 | rates[575] = 1000000001772819380639683201; 52 | rates[600] = 1000000001847694957439350562; 53 | rates[625] = 1000000001922394148741344865; 54 | rates[650] = 1000000001996917783620820123; 55 | rates[675] = 1000000002071266685321207000; 56 | rates[700] = 1000000002145441671308778766; 57 | rates[725] = 1000000002219443553326580536; 58 | rates[750] = 1000000002293273137447730714; 59 | rates[775] = 1000000002366931224128103346; 60 | rates[800] = 1000000002440418608258400030; 61 | rates[825] = 1000000002513736079215619839; 62 | rates[850] = 1000000002586884420913935572; 63 | rates[875] = 1000000002659864411854984565; 64 | rates[900] = 1000000002732676825177582095; 65 | rates[925] = 1000000002805322428706865331; 66 | rates[950] = 1000000002877801985002875644; 67 | rates[975] = 1000000002950116251408586949; 68 | rates[1000] = 1000000003022265980097387650; 69 | rates[1025] = 1000000003094251918120023627; 70 | rates[1050] = 1000000003166074807451009595; 71 | rates[1075] = 1000000003237735385034516037; 72 | rates[1100] = 1000000003309234382829738808; 73 | rates[1125] = 1000000003380572527855758393; 74 | rates[1150] = 1000000003451750542235895695; 75 | rates[1175] = 1000000003522769143241571114; 76 | rates[1200] = 1000000003593629043335673582; 77 | rates[1225] = 1000000003664330950215446102; 78 | rates[1250] = 1000000003734875566854894261; 79 | rates[1275] = 1000000003805263591546724039; 80 | rates[1300] = 1000000003875495717943815211; 81 | rates[1325] = 1000000003945572635100236468; 82 | rates[1350] = 1000000004015495027511808328; 83 | rates[1375] = 1000000004085263575156219812; 84 | rates[1400] = 1000000004154878953532704765; 85 | rates[1425] = 1000000004224341833701283597; 86 | rates[1450] = 1000000004293652882321576158; 87 | rates[1475] = 1000000004362812761691191350; 88 | rates[1500] = 1000000004431822129783699001; 89 | rates[1525] = 1000000004500681640286189459; 90 | rates[1550] = 1000000004569391942636426248; 91 | rates[1575] = 1000000004637953682059597074; 92 | rates[1600] = 1000000004706367499604668374; 93 | rates[1625] = 1000000004774634032180348552; 94 | rates[1650] = 1000000004842753912590664903; 95 | rates[1675] = 1000000004910727769570159235; 96 | rates[1700] = 1000000004978556227818707070; 97 | rates[1725] = 1000000005046239908035965222; 98 | rates[1750] = 1000000005113779426955452540; 99 | rates[1775] = 1000000005181175397378268462; 100 | rates[1800] = 1000000005248428428206454010; 101 | rates[1825] = 1000000005315539124475999751; 102 | rates[1850] = 1000000005382508087389505206; 103 | rates[1875] = 1000000005449335914348494113; 104 | rates[1900] = 1000000005516023198985389892; 105 | rates[1925] = 1000000005582570531195155575; 106 | rates[1950] = 1000000005648978497166602432; 107 | rates[1975] = 1000000005715247679413371444; 108 | rates[2000] = 1000000005781378656804591712; 109 | rates[2025] = 1000000005847372004595219844; 110 | rates[2050] = 1000000005913228294456064283; 111 | rates[2075] = 1000000005978948094503498507; 112 | rates[2100] = 1000000006044531969328866955; 113 | rates[2125] = 1000000006109980480027587488; 114 | rates[2150] = 1000000006175294184227954125; 115 | rates[2175] = 1000000006240473636119643770; 116 | rates[2200] = 1000000006305519386481930552; 117 | rates[2225] = 1000000006370431982711611382; 118 | rates[2250] = 1000000006435211968850646270; 119 | rates[2275] = 1000000006499859885613516871; 120 | rates[2300] = 1000000006564376270414306730; 121 | rates[2325] = 1000000006628761657393506584; 122 | rates[2350] = 1000000006693016577444548094; 123 | rates[2375] = 1000000006757141558240069277; 124 | rates[2400] = 1000000006821137124257914908; 125 | rates[2425] = 1000000006885003796806875073; 126 | rates[2450] = 1000000006948742094052165050; 127 | rates[2475] = 1000000007012352531040649627; 128 | rates[2500] = 1000000007075835619725814915; 129 | rates[2525] = 1000000007139191868992490695; 130 | rates[2550] = 1000000007202421784681326287; 131 | rates[2575] = 1000000007265525869613022867; 132 | rates[2600] = 1000000007328504623612325153; 133 | rates[2625] = 1000000007391358543531775311; 134 | rates[2650] = 1000000007454088123275231904; 135 | rates[2675] = 1000000007516693853821156670; 136 | rates[2700] = 1000000007579176223245671878; 137 | rates[2725] = 1000000007641535716745390957; 138 | rates[2750] = 1000000007703772816660025079; 139 | rates[2775] = 1000000007765888002494768329; 140 | rates[2800] = 1000000007827881750942464045; 141 | rates[2825] = 1000000007889754535905554913; 142 | rates[2850] = 1000000007951506828517819323; 143 | rates[2875] = 1000000008013139097165896490; 144 | rates[2900] = 1000000008074651807510602798; 145 | rates[2925] = 1000000008136045422508041783; 146 | rates[2950] = 1000000008197320402430510158; 147 | rates[2975] = 1000000008258477204887202245; 148 | rates[3000] = 1000000008319516284844715115; 149 | rates[3025] = 1000000008380438094647356774; 150 | rates[3050] = 1000000008441243084037259619; 151 | rates[3075] = 1000000008501931700174301437; 152 | rates[3100] = 1000000008562504387655836125; 153 | rates[3125] = 1000000008622961588536236324; 154 | rates[3150] = 1000000008683303742346250114; 155 | rates[3175] = 1000000008743531286112173869; 156 | rates[3200] = 1000000008803644654374843395; 157 | rates[3225] = 1000000008863644279208445392; 158 | rates[3250] = 1000000008923530590239151272; 159 | rates[3275] = 1000000008983304014663575373; 160 | rates[3300] = 1000000009042964977267059505; 161 | rates[3325] = 1000000009102513900441785827; 162 | rates[3350] = 1000000009161951204204719966; 163 | rates[3375] = 1000000009221277306215386279; 164 | rates[3400] = 1000000009280492621793477151; 165 | rates[3425] = 1000000009339597563936298181; 166 | rates[3450] = 1000000009398592543336051086; 167 | rates[3475] = 1000000009457477968396956129; 168 | rates[3500] = 1000000009516254245252215861; 169 | rates[3525] = 1000000009574921777780821942; 170 | rates[3550] = 1000000009633480967624206760; 171 | rates[3575] = 1000000009691932214202741592; 172 | rates[3600] = 1000000009750275914732082986; 173 | rates[3625] = 1000000009808512464239369028; 174 | rates[3650] = 1000000009866642255579267166; 175 | rates[3675] = 1000000009924665679449875210; 176 | rates[3700] = 1000000009982583124408477109; 177 | rates[3725] = 1000000010040394976887155106; 178 | rates[3750] = 1000000010098101621208259840; 179 | rates[3775] = 1000000010155703439599739931; 180 | rates[3800] = 1000000010213200812210332586; 181 | rates[3825] = 1000000010270594117124616733; 182 | rates[3850] = 1000000010327883730377930177; 183 | rates[3875] = 1000000010385070025971152244; 184 | rates[3900] = 1000000010442153375885353361; 185 | rates[3925] = 1000000010499134150096313024; 186 | rates[3950] = 1000000010556012716588907553; 187 | rates[3975] = 1000000010612789441371369043; 188 | rates[4000] = 1000000010669464688489416886; 189 | rates[4025] = 1000000010726038820040263233; 190 | rates[4050] = 1000000010782512196186493739; 191 | rates[4075] = 1000000010838885175169824929; 192 | rates[4100] = 1000000010895158113324739488; 193 | rates[4125] = 1000000010951331365092000772; 194 | rates[4150] = 1000000011007405283032047846; 195 | rates[4175] = 1000000011063380217838272275; 196 | rates[4200] = 1000000011119256518350177948; 197 | rates[4225] = 1000000011175034531566425160; 198 | rates[4250] = 1000000011230714602657760176; 199 | rates[4275] = 1000000011286297074979831462; 200 | rates[4300] = 1000000011341782290085893805; 201 | rates[4325] = 1000000011397170587739401474; 202 | rates[4350] = 1000000011452462305926491579; 203 | rates[4375] = 1000000011507657780868358802; 204 | rates[4400] = 1000000011562757347033522598; 205 | rates[4425] = 1000000011617761337149988016; 206 | rates[4450] = 1000000011672670082217301219; 207 | rates[4475] = 1000000011727483911518500818; 208 | rates[4500] = 1000000011782203152631966084; 209 | rates[4525] = 1000000011836828131443163102; 210 | rates[4550] = 1000000011891359172156289942; 211 | rates[4575] = 1000000011945796597305821848; 212 | rates[4600] = 1000000012000140727767957524; 213 | rates[4625] = 1000000012054391882771967477; 214 | rates[4650] = 1000000012108550379911445472; 215 | rates[4675] = 1000000012162616535155464050; 216 | rates[4700] = 1000000012216590662859635112; 217 | rates[4725] = 1000000012270473075777076530; 218 | rates[4750] = 1000000012324264085069285747; 219 | rates[4775] = 1000000012377964000316921287; 220 | rates[4800] = 1000000012431573129530493155; 221 | rates[4825] = 1000000012485091779160962996; 222 | rates[4850] = 1000000012538520254110254976; 223 | rates[4875] = 1000000012591858857741678240; 224 | rates[4900] = 1000000012645107891890261872; 225 | rates[4925] = 1000000012698267656873003228; 226 | rates[4950] = 1000000012751338451499030498; 227 | rates[4975] = 1000000012804320573079680371; 228 | rates[5000] = 1000000012857214317438491659; 229 | rates[5025] = 1000000012910019978921115695; 230 | rates[5050] = 1000000012962737850405144363; 231 | rates[5075] = 1000000013015368223309856554; 232 | rates[5100] = 1000000013067911387605883890; 233 | rates[5125] = 1000000013120367631824796485; 234 | rates[5150] = 1000000013172737243068609553; 235 | rates[5175] = 1000000013225020507019211652; 236 | rates[5200] = 1000000013277217707947715318; 237 | rates[5225] = 1000000013329329128723730871; 238 | rates[5250] = 1000000013381355050824564143; 239 | rates[5275] = 1000000013433295754344338876; 240 | rates[5300] = 1000000013485151518003044532; 241 | rates[5325] = 1000000013536922619155510237; 242 | rates[5350] = 1000000013588609333800305597; 243 | rates[5375] = 1000000013640211936588569081; 244 | rates[5400] = 1000000013691730700832764691; 245 | rates[5425] = 1000000013743165898515367617; 246 | rates[5450] = 1000000013794517800297479554; 247 | rates[5475] = 1000000013845786675527374380; 248 | rates[5500] = 1000000013896972792248974855; 249 | rates[5525] = 1000000013948076417210261020; 250 | rates[5550] = 1000000013999097815871610946; 251 | rates[5575] = 1000000014050037252414074493; 252 | rates[5600] = 1000000014100894989747580713; 253 | rates[5625] = 1000000014151671289519079548; 254 | rates[5650] = 1000000014202366412120618444; 255 | rates[5675] = 1000000014252980616697354502; 256 | rates[5700] = 1000000014303514161155502800; 257 | rates[5725] = 1000000014353967302170221464; 258 | rates[5750] = 1000000014404340295193434124; 259 | rates[5775] = 1000000014454633394461590334; 260 | rates[5800] = 1000000014504846853003364537; 261 | rates[5825] = 1000000014554980922647294184; 262 | rates[5850] = 1000000014605035854029357558; 263 | rates[5875] = 1000000014655011896600491882; 264 | rates[5900] = 1000000014704909298634052283; 265 | rates[5925] = 1000000014754728307233212158; 266 | rates[5950] = 1000000014804469168338305494; 267 | rates[5975] = 1000000014854132126734111701; 268 | rates[6000] = 1000000014903717426057083481; 269 | rates[6025] = 1000000014953225308802518272; 270 | rates[6050] = 1000000015002656016331673799; 271 | rates[6075] = 1000000015052009788878828253; 272 | rates[6100] = 1000000015101286865558285606; 273 | rates[6125] = 1000000015150487484371326590; 274 | rates[6150] = 1000000015199611882213105818; 275 | rates[6175] = 1000000015248660294879495575; 276 | rates[6200] = 1000000015297632957073876761; 277 | rates[6225] = 1000000015346530102413877471; 278 | rates[6250] = 1000000015395351963438059699; 279 | rates[6275] = 1000000015444098771612554646; 280 | rates[6300] = 1000000015492770757337647112; 281 | rates[6325] = 1000000015541368149954309419; 282 | rates[6350] = 1000000015589891177750685357; 283 | rates[6375] = 1000000015638340067968524580; 284 | rates[6400] = 1000000015686715046809567945; 285 | rates[6425] = 1000000015735016339441884188; 286 | rates[6450] = 1000000015783244170006158447; 287 | rates[6475] = 1000000015831398761621933006; 288 | rates[6500] = 1000000015879480336393800741; 289 | rates[6525] = 1000000015927489115417551681; 290 | rates[6550] = 1000000015975425318786273105; 291 | rates[6575] = 1000000016023289165596403599; 292 | rates[6600] = 1000000016071080873953741499; 293 | rates[6625] = 1000000016118800660979408115; 294 | rates[6650] = 1000000016166448742815766155; 295 | rates[6675] = 1000000016214025334632293755; 296 | rates[6700] = 1000000016261530650631414500; 297 | rates[6725] = 1000000016308964904054283846; 298 | rates[6750] = 1000000016356328307186532328; 299 | rates[6775] = 1000000016403621071363965932; 300 | rates[6800] = 1000000016450843406978224029; 301 | rates[6825] = 1000000016497995523482395247; 302 | rates[6850] = 1000000016545077629396591637; 303 | rates[6875] = 1000000016592089932313481533; 304 | rates[6900] = 1000000016639032638903781446; 305 | rates[6925] = 1000000016685905954921707380; 306 | rates[6950] = 1000000016732710085210385903; 307 | rates[6975] = 1000000016779445233707225354; 308 | rates[7000] = 1000000016826111603449247521; 309 | rates[7025] = 1000000016872709396578380147; 310 | rates[7050] = 1000000016919238814346710603; 311 | rates[7075] = 1000000016965700057121701072; 312 | rates[7100] = 1000000017012093324391365593; 313 | rates[7125] = 1000000017058418814769409273; 314 | rates[7150] = 1000000017104676726000330021; 315 | rates[7175] = 1000000017150867254964483131; 316 | rates[7200] = 1000000017196990597683109018; 317 | rates[7225] = 1000000017243046949323324453; 318 | rates[7250] = 1000000017289036504203077600; 319 | rates[7275] = 1000000017334959455796067168; 320 | rates[7300] = 1000000017380815996736626004; 321 | rates[7325] = 1000000017426606318824569415; 322 | rates[7350] = 1000000017472330613030008543; 323 | rates[7375] = 1000000017517989069498129080; 324 | rates[7400] = 1000000017563581877553935633; 325 | rates[7425] = 1000000017609109225706962029; 326 | rates[7450] = 1000000017654571301655947851; 327 | rates[7475] = 1000000017699968292293481503; 328 | rates[7500] = 1000000017745300383710610088; 329 | rates[7525] = 1000000017790567761201416374; 330 | rates[7550] = 1000000017835770609267563142; 331 | rates[7575] = 1000000017880909111622805195; 332 | rates[7600] = 1000000017925983451197469286; 333 | rates[7625] = 1000000017970993810142902264; 334 | rates[7650] = 1000000018015940369835887686; 335 | rates[7675] = 1000000018060823310883031179; 336 | rates[7700] = 1000000018105642813125114801; 337 | rates[7725] = 1000000018150399055641420686; 338 | rates[7750] = 1000000018195092216754024201; 339 | rates[7775] = 1000000018239722474032056911; 340 | rates[7800] = 1000000018284290004295939569; 341 | rates[7825] = 1000000018328794983621585414; 342 | rates[7850] = 1000000018373237587344574003; 343 | rates[7875] = 1000000018417617990064295840; 344 | rates[7900] = 1000000018461936365648068049; 345 | rates[7925] = 1000000018506192887235221305; 346 | rates[7950] = 1000000018550387727241158310; 347 | rates[7975] = 1000000018594521057361384012; 348 | rates[8000] = 1000000018638593048575507813; 349 | rates[8025] = 1000000018682603871151218019; 350 | rates[8050] = 1000000018726553694648228732; 351 | rates[8075] = 1000000018770442687922199432; 352 | rates[8100] = 1000000018814271019128627481; 353 | rates[8125] = 1000000018858038855726713746; 354 | rates[8150] = 1000000018901746364483201594; 355 | rates[8175] = 1000000018945393711476189463; 356 | rates[8200] = 1000000018988981062098917230; 357 | rates[8225] = 1000000019032508581063526585; 358 | rates[8250] = 1000000019075976432404795643; 359 | rates[8275] = 1000000019119384779483847985; 360 | rates[8300] = 1000000019162733784991836346; 361 | rates[8325] = 1000000019206023610953601168; 362 | rates[8350] = 1000000019249254418731304205; 363 | rates[8375] = 1000000019292426369028037391; 364 | rates[8400] = 1000000019335539621891407188; 365 | rates[8425] = 1000000019378594336717094581; 366 | rates[8450] = 1000000019421590672252390959; 367 | rates[8475] = 1000000019464528786599710033; 368 | rates[8500] = 1000000019507408837220076029; 369 | rates[8525] = 1000000019550230980936588320; 370 | rates[8550] = 1000000019592995373937862689; 371 | rates[8575] = 1000000019635702171781449432; 372 | rates[8600] = 1000000019678351529397228463; 373 | rates[8625] = 1000000019720943601090781625; 374 | rates[8650] = 1000000019763478540546742376; 375 | rates[8675] = 1000000019805956500832123050; 376 | rates[8700] = 1000000019848377634399619849; 377 | rates[8725] = 1000000019890742093090895767; 378 | rates[8750] = 1000000019933050028139841613; 379 | rates[8775] = 1000000019975301590175815296; 380 | rates[8800] = 1000000020017496929226859581; 381 | rates[8825] = 1000000020059636194722898437; 382 | rates[8850] = 1000000020101719535498912200; 383 | rates[8875] = 1000000020143747099798091677; 384 | rates[8900] = 1000000020185719035274971385; 385 | rates[8925] = 1000000020227635488998542076; 386 | rates[8950] = 1000000020269496607455342719; 387 | rates[8975] = 1000000020311302536552532106; 388 | rates[9000] = 1000000020353053421620940223; 389 | rates[9025] = 1000000020394749407418099573; 390 | rates[9050] = 1000000020436390638131256590; 391 | rates[9075] = 1000000020477977257380363298; 392 | rates[9100] = 1000000020519509408221049399; 393 | rates[9125] = 1000000020560987233147574896; 394 | rates[9150] = 1000000020602410874095763456; 395 | rates[9175] = 1000000020643780472445916617; 396 | rates[9200] = 1000000020685096169025709028; 397 | rates[9225] = 1000000020726358104113064837; 398 | rates[9250] = 1000000020767566417439015395; 399 | rates[9275] = 1000000020808721248190538424; 400 | rates[9300] = 1000000020849822735013378765; 401 | rates[9325] = 1000000020890871016014850891; 402 | rates[9350] = 1000000020931866228766623286; 403 | rates[9375] = 1000000020972808510307484860; 404 | rates[9400] = 1000000021013697997146093523; 405 | rates[9425] = 1000000021054534825263707061; 406 | rates[9450] = 1000000021095319130116896449; 407 | rates[9475] = 1000000021136051046640241741; 408 | rates[9500] = 1000000021176730709249010667; 409 | rates[9525] = 1000000021217358251841820063; 410 | rates[9550] = 1000000021257933807803280285; 411 | rates[9575] = 1000000021298457510006622716; 412 | rates[9600] = 1000000021338929490816310513; 413 | rates[9625] = 1000000021379349882090632705; 414 | rates[9650] = 1000000021419718815184281790; 415 | rates[9675] = 1000000021460036420950914938; 416 | rates[9700] = 1000000021500302829745698932; 417 | rates[9725] = 1000000021540518171427838973; 418 | rates[9750] = 1000000021580682575363091474; 419 | rates[9775] = 1000000021620796170426260951; 420 | rates[9800] = 1000000021660859085003681151; 421 | rates[9825] = 1000000021700871446995680519; 422 | rates[9850] = 1000000021740833383819032127; 423 | rates[9875] = 1000000021780745022409388199; 424 | rates[9900] = 1000000021820606489223699321; 425 | rates[9925] = 1000000021860417910242618463; 426 | rates[9950] = 1000000021900179410972889943; 427 | rates[9975] = 1000000021939891116449723415; 428 | rates[10000] = 1000000021979553151239153027; 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /test-rwaspell.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | [[ "$ETH_RPC_URL" && ( "$(seth chain)" == "kovan" || "$(seth chain)" == "goerli" ) ]] || { echo "Please set a kovan ETH_RPC_URL"; exit 1; } 5 | 6 | dapp --use solc:0.6.12 build 7 | 8 | # MkrAuthority 9 | # export DAPP_TEST_ADDRESS=0xdB33dFD3D61308C33C63209845DaD3e6bfb2c674 10 | export DAPP_TEST_TIMESTAMP=$(seth block latest timestamp) 11 | export DAPP_TEST_NUMBER=$(seth block latest number) 12 | 13 | if [[ -z "$1" ]]; then 14 | LANG=C.UTF-8 hevm dapp-test --rpc="$ETH_RPC_URL" --json-file=out/dapp.sol.json --dapp-root=. --verbose 1 15 | else 16 | LANG=C.UTF-8 hevm dapp-test --rpc="$ETH_RPC_URL" --json-file=out/dapp.sol.json --dapp-root=. --verbose 3 --match "$1" 17 | fi 18 | --------------------------------------------------------------------------------