├── .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 |
--------------------------------------------------------------------------------