├── .gitattributes
├── .github
└── workflows
│ └── tests.yaml
├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── shell.nix
└── src
├── DssProxyActions.sol
└── DssProxyActions.t.sol
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sol linguist-language=Solidity
2 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | jobs:
4 | tests:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Checkout repository and submodules
8 | uses: actions/checkout@v2
9 | with:
10 | submodules: recursive
11 |
12 | - name: Install nix 2.3.6
13 | uses: cachix/install-nix-action@v13
14 | with:
15 | install_url: https://releases.nixos.org/nix/nix-2.3.6/install
16 | nix_path: nixpkgs=channel:nixos-unstable
17 |
18 | - name: Use maker cachix
19 | uses: cachix/cachix-action@v10
20 | with:
21 | name: maker
22 |
23 | - name: Run tests
24 | run: nix-shell --pure --run 'dapp test'
25 |
--------------------------------------------------------------------------------
/.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/dss-cdp-manager"]
5 | path = lib/dss-cdp-manager
6 | url = https://github.com/makerdao/dss-cdp-manager
7 | branch = v1.2
8 | [submodule "lib/proxy-registry"]
9 | path = lib/proxy-registry
10 | url = https://github.com/makerdao/proxy-registry
11 | [submodule "lib/ds-weth"]
12 | path = lib/ds-weth
13 | url = https://github.com/dapphub/ds-weth
14 | [submodule "lib/dss-gem-joins"]
15 | path = lib/dss-gem-joins
16 | url = https://github.com/makerdao/dss-gem-joins
17 | branch = v1.2
18 |
--------------------------------------------------------------------------------
/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 | build :; dapp --use solc:0.5.12 build
2 | clean :; dapp clean
3 | test :; dapp --use solc:0.5.12 test -v ${TEST_FLAGS}
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dss-proxy-actions
2 | 
3 |
4 | Proxy functions to be used via ds-proxy. These functions are based on `dss-cdp-manager` as CDP registry.
5 |
6 | https://github.com/makerdao/dss-proxy-actions
7 |
8 | ## DssProxyActions
9 |
10 | `open(address manager, bytes32 ilk, address usr)`: creates an `UrnHandler` (`cdp`) for the address `usr` (for a specific `ilk`) and allows to manage it via the internal registry of the `manager`.
11 |
12 | `give(address manager, uint cdp, address usr)`: transfers the ownership of `cdp` to `usr` address in the `manager` registry.
13 |
14 | `giveToProxy(address proxyRegistry, address manager, uint cdp, address usr)`: transfers the ownership of `cdp` to the proxy of `usr` address (via `proxyRegistry`) in the `manager` registry.
15 |
16 | `cdpAllow(address manager, uint cdp, address usr, uint ok)`: allows/denies `usr` address to manage the `cdp`.
17 |
18 | `urnAllow(address manager, address usr, uint ok)`: allows/denies `usr` address to manage the `msg.sender` address as `dst` for `quit`.
19 |
20 | `flux(address manager, uint cdp, address dst, uint wad)`: moves `wad` amount of collateral from `cdp` address to `dst` address.
21 |
22 | `move(address manager, uint cdp, address dst, uint rad)`: moves `rad` amount of DAI from `cdp` address to `dst` address.
23 |
24 | `frob(address manager, uint cdp, int dink, int dart)`: executes `frob` to `cdp` address assigning the collateral freed and/or DAI drawn to the same address.
25 |
26 | `quit(address manager, uint cdp, address dst)`: moves `cdp` collateral balance and debt to `dst` address.
27 |
28 | `enter(address manager, address src, uint cdp)`: moves `src` collateral balance and debt to `cdp`.
29 |
30 | `shift(address manager, uint cdpSrc, uint cdpDst)`: moves `cdpSrc` collateral balance and debt to `cdpDst`.
31 |
32 | `lockETH(address manager, address ethJoin, uint cdp)`: deposits `msg.value` amount of ETH in `ethJoin` adapter and executes `frob` to `cdp` increasing the locked value.
33 |
34 | `safeLockETH(address manager, address ethJoin, uint cdp, address owner)`: same than `lockETH` but requiring `owner == cdp owner`.
35 |
36 | `lockGem(address manager, address gemJoin, uint cdp, uint wad, bool transferFrom)`: deposits `wad` amount of collateral in `gemJoin` adapter and executes `frob` to `cdp` increasing the locked value. Gets funds from `msg.sender` if `transferFrom == true`.
37 |
38 | `safeLockGem(address manager, address gemJoin, uint cdp, uint wad, bool transferFrom, address owner)`: same than `lockGem` but requiring `owner == cdp owner`.
39 |
40 | `freeETH(address manager, address ethJoin, uint cdp, uint wad)`: executes `frob` to `cdp` decreasing locked collateral and withdraws `wad` amount of ETH from `ethJoin` adapter.
41 |
42 | `freeGem(address manager, address gemJoin, uint cdp, uint wad)`: executes `frob` to `cdp` decreasing locked collateral and withdraws `wad` amount of collateral from `gemJoin` adapter.
43 |
44 | `draw(address manager, address jug, address daiJoin, uint cdp, uint wad)`: updates collateral fee rate, executes `frob` to `cdp` increasing debt and exits `wad` amount of DAI token (minting it) from `daiJoin` adapter.
45 |
46 | `wipe(address manager, address daiJoin, uint cdp, uint wad)`: joins `wad` amount of DAI token to `daiJoin` adapter (burning it) and executes `frob` to `cdp` for decreasing debt.
47 |
48 | `safeWipe(address manager, address daiJoin, uint cdp, uint wad, address owner)`: same than `wipe` but requiring `owner == cdp owner`.
49 |
50 | `wipeAll(address manager, address daiJoin, uint cdp)`: joins all the necessary amount of DAI token to `daiJoin` adapter (burning it) and executes `frob` to `cdp` setting the debt to zero.
51 |
52 | `safeWipeAll(address manager, address daiJoin, uint cdp, address owner)`: same than `wipeAll` but requiring `owner == cdp owner`.
53 |
54 | `lockETHAndDraw(address manager, address jug, address ethJoin, address daiJoin, uint cdp, uint wadD)`: combines `lockETH` and `draw`.
55 |
56 | `openLockETHAndDraw(address manager, address jug, address ethJoin, address daiJoin, bytes32 ilk, uint wadD)`: combines `open`, `lockETH` and `draw`.
57 |
58 | `lockGemAndDraw(address manager, address jug, address gemJoin, address daiJoin, uint cdp, uint wadC, uint wadD, bool transferFrom)`: combines `lockGem` and `draw`.
59 |
60 | `openLockGemAndDraw(address manager, address jug, address gemJoin, address daiJoin, bytes32 ilk, uint wadC, uint wadD, bool transferFrom)`: combines `open`, `lockGem` and `draw`.
61 |
62 | `wipeAndFreeETH(address manager, address ethJoin, address daiJoin, uint cdp, uint wadC, uint wadD)`: combines `wipe` and `freeETH`.
63 |
64 | `wipeAllAndFreeETH(address manager, address ethJoin, address daiJoin, uint cdp, uint wadC)`: combines `wipeAll` and `freeETH`.
65 |
66 | `wipeAndFreeGem(address manager, address gemJoin, address daiJoin, uint cdp, uint wadC, uint wadD)`: combines `wipe` and `freeGem`.
67 |
68 | `wipeAllAndFreeGem(address manager, address gemJoin, address daiJoin, uint cdp, uint wadC)`: combines `wipeAll` and `freeGem`.
69 |
70 | `openLockGNTAndDraw(address manager, address jug, address gntJoin, address daiJoin, bytes32 ilk, uint wadC, uint wadD)`: like `openLockGemAndDraw` but specially for GNT token.
71 |
72 | ## DssProxyActionsFlip
73 |
74 | `exitETH(address manager, address ethJoin, uint cdp, uint wad)`: exits `wad` amount of ETH from `ethJoin` adapter (received in the `cdp` urn after the liquidation auction is over).
75 |
76 | `exitGem(address manager, address gemJoin, uint cdp, uint wad)`: exits `wad` amount of collateral from `gemJoin` adapter (received in the `cdp` urn after the liquidation auction is over).
77 |
78 | ## DssProxyActionsEnd
79 |
80 | `freeETH(address manager, address ethJoin, address end, uint cdp)`: after system is caged, recovers remaining ETH from `cdp` (pays remaining debt if exists).
81 |
82 | `freeGem(address manager, address gemJoin, address end, uint cdp)`: after system is caged, recovers remaining token from `cdp` (pays remaining debt if exists).
83 |
84 | `pack(address daiJoin, address end, uint wad)`: after system is caged, packs `wad` amount of DAI to be ready for cashing.
85 |
86 | `cashETH(address ethJoin, address end, bytes32 ilk, uint wad)`: after system is caged, cashes `wad` amount of previously packed DAI and returns the equivalent in ETH.
87 |
88 | `cashGem(address gemJoin, address end, bytes32 ilk, uint wad)`: after system is caged, cashes `wad` amount of previously packed DAI and returns the equivalent in token.
89 |
90 | ## DssProxyActionsDsr
91 |
92 | `join(address daiJoin, address pot, uint wad)`: joins `wad` amount of DAI token to `daiJoin` adapter (burning it) and moves balance to `pot` for DAI Saving Rates.
93 |
94 | `exit(address daiJoin, address pot, uint wad)`: retrieves `wad` amount of DAI from `pot` and exits DAI token from `daiJoin` adapter (minting it).
95 |
96 | `exitAll(address daiJoin, address pot)`: same than `exit` but all the available amount.
97 |
--------------------------------------------------------------------------------
/shell.nix:
--------------------------------------------------------------------------------
1 | { dappPkgs ? (
2 | import (fetchTarball "https://github.com/makerdao/makerpkgs/tarball/master") {}
3 | ).dappPkgsVersions.hevm-0_43_1
4 | }: with dappPkgs;
5 |
6 | mkShell {
7 | DAPP_SOLC = solc-static-versions.solc_0_5_12 + "/bin/solc-0.5.12";
8 | # SOLC_FLAGS = "--optimize --optimize-runs=200";
9 | buildInputs = [
10 | dapp
11 | ];
12 | }
13 |
--------------------------------------------------------------------------------
/src/DssProxyActions.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: AGPL-3.0-or-later
2 |
3 | /// DssProxyActions.sol
4 |
5 | // Copyright (C) 2018-2020 Maker Ecosystem Growth Holdings, INC.
6 |
7 | // This program is free software: you can redistribute it and/or modify
8 | // it under the terms of the GNU Affero General Public License as published by
9 | // the Free Software Foundation, either version 3 of the License, or
10 | // (at your option) any later version.
11 | //
12 | // This program is distributed in the hope that it will be useful,
13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | // GNU Affero General Public License for more details.
16 | //
17 | // You should have received a copy of the GNU Affero General Public License
18 | // along with this program. If not, see .
19 |
20 | pragma solidity >=0.5.12;
21 |
22 | interface GemLike {
23 | function approve(address, uint) external;
24 | function transfer(address, uint) external;
25 | function transferFrom(address, address, uint) external;
26 | function deposit() external payable;
27 | function withdraw(uint) external;
28 | }
29 |
30 | interface ManagerLike {
31 | function cdpCan(address, uint, address) external view returns (uint);
32 | function ilks(uint) external view returns (bytes32);
33 | function owns(uint) external view returns (address);
34 | function urns(uint) external view returns (address);
35 | function vat() external view returns (address);
36 | function open(bytes32, address) external returns (uint);
37 | function give(uint, address) external;
38 | function cdpAllow(uint, address, uint) external;
39 | function urnAllow(address, uint) external;
40 | function frob(uint, int, int) external;
41 | function flux(uint, address, uint) external;
42 | function move(uint, address, uint) external;
43 | function exit(address, uint, address, uint) external;
44 | function quit(uint, address) external;
45 | function enter(address, uint) external;
46 | function shift(uint, uint) external;
47 | }
48 |
49 | interface VatLike {
50 | function can(address, address) external view returns (uint);
51 | function ilks(bytes32) external view returns (uint, uint, uint, uint, uint);
52 | function dai(address) external view returns (uint);
53 | function urns(bytes32, address) external view returns (uint, uint);
54 | function frob(bytes32, address, address, address, int, int) external;
55 | function hope(address) external;
56 | function move(address, address, uint) external;
57 | }
58 |
59 | interface GemJoinLike {
60 | function dec() external returns (uint);
61 | function gem() external returns (GemLike);
62 | function join(address, uint) external payable;
63 | function exit(address, uint) external;
64 | }
65 |
66 | interface GNTJoinLike {
67 | function bags(address) external view returns (address);
68 | function make(address) external returns (address);
69 | }
70 |
71 | interface DaiJoinLike {
72 | function vat() external returns (VatLike);
73 | function dai() external returns (GemLike);
74 | function join(address, uint) external payable;
75 | function exit(address, uint) external;
76 | }
77 |
78 | interface HopeLike {
79 | function hope(address) external;
80 | function nope(address) external;
81 | }
82 |
83 | interface EndLike {
84 | function fix(bytes32) external view returns (uint);
85 | function cash(bytes32, uint) external;
86 | function free(bytes32) external;
87 | function pack(uint) external;
88 | function skim(bytes32, address) external;
89 | }
90 |
91 | interface JugLike {
92 | function drip(bytes32) external returns (uint);
93 | }
94 |
95 | interface PotLike {
96 | function pie(address) external view returns (uint);
97 | function drip() external returns (uint);
98 | function join(uint) external;
99 | function exit(uint) external;
100 | }
101 |
102 | interface ProxyRegistryLike {
103 | function proxies(address) external view returns (address);
104 | function build(address) external returns (address);
105 | }
106 |
107 | interface ProxyLike {
108 | function owner() external view returns (address);
109 | }
110 |
111 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
112 | // WARNING: These functions meant to be used as a a library for a DSProxy. Some are unsafe if you call them directly.
113 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
114 |
115 | contract Common {
116 | uint256 constant RAY = 10 ** 27;
117 |
118 | // Internal functions
119 |
120 | function mul(uint x, uint y) internal pure returns (uint z) {
121 | require(y == 0 || (z = x * y) / y == x, "mul-overflow");
122 | }
123 |
124 | // Public functions
125 |
126 | function daiJoin_join(address apt, address urn, uint wad) public {
127 | // Gets DAI from the user's wallet
128 | DaiJoinLike(apt).dai().transferFrom(msg.sender, address(this), wad);
129 | // Approves adapter to take the DAI amount
130 | DaiJoinLike(apt).dai().approve(apt, wad);
131 | // Joins DAI into the vat
132 | DaiJoinLike(apt).join(urn, wad);
133 | }
134 | }
135 |
136 | contract DssProxyActions is Common {
137 | // Internal functions
138 |
139 | function sub(uint x, uint y) internal pure returns (uint z) {
140 | require((z = x - y) <= x, "sub-overflow");
141 | }
142 |
143 | function toInt(uint x) internal pure returns (int y) {
144 | y = int(x);
145 | require(y >= 0, "int-overflow");
146 | }
147 |
148 | function toRad(uint wad) internal pure returns (uint rad) {
149 | rad = mul(wad, 10 ** 27);
150 | }
151 |
152 | function convertTo18(address gemJoin, uint256 amt) internal returns (uint256 wad) {
153 | // For those collaterals that have less than 18 decimals precision we need to do the conversion before passing to frob function
154 | // Adapters will automatically handle the difference of precision
155 | wad = mul(
156 | amt,
157 | 10 ** (18 - GemJoinLike(gemJoin).dec())
158 | );
159 | }
160 |
161 | function _getDrawDart(
162 | address vat,
163 | address jug,
164 | address urn,
165 | bytes32 ilk,
166 | uint wad
167 | ) internal returns (int dart) {
168 | // Updates stability fee rate
169 | uint rate = JugLike(jug).drip(ilk);
170 |
171 | // Gets DAI balance of the urn in the vat
172 | uint dai = VatLike(vat).dai(urn);
173 |
174 | // If there was already enough DAI in the vat balance, just exits it without adding more debt
175 | if (dai < mul(wad, RAY)) {
176 | // Calculates the needed dart so together with the existing dai in the vat is enough to exit wad amount of DAI tokens
177 | dart = toInt(sub(mul(wad, RAY), dai) / rate);
178 | // This is neeeded due lack of precision. It might need to sum an extra dart wei (for the given DAI wad amount)
179 | dart = mul(uint(dart), rate) < mul(wad, RAY) ? dart + 1 : dart;
180 | }
181 | }
182 |
183 | function _getWipeDart(
184 | address vat,
185 | uint dai,
186 | address urn,
187 | bytes32 ilk
188 | ) internal view returns (int dart) {
189 | // Gets actual rate from the vat
190 | (, uint rate,,,) = VatLike(vat).ilks(ilk);
191 | // Gets actual art value of the urn
192 | (, uint art) = VatLike(vat).urns(ilk, urn);
193 |
194 | // Uses the whole dai balance in the vat to reduce the debt
195 | dart = toInt(dai / rate);
196 | // Checks the calculated dart is not higher than urn.art (total debt), otherwise uses its value
197 | dart = uint(dart) <= art ? - dart : - toInt(art);
198 | }
199 |
200 | function _getWipeAllWad(
201 | address vat,
202 | address usr,
203 | address urn,
204 | bytes32 ilk
205 | ) internal view returns (uint wad) {
206 | // Gets actual rate from the vat
207 | (, uint rate,,,) = VatLike(vat).ilks(ilk);
208 | // Gets actual art value of the urn
209 | (, uint art) = VatLike(vat).urns(ilk, urn);
210 | // Gets actual dai amount in the urn
211 | uint dai = VatLike(vat).dai(usr);
212 |
213 | uint rad = sub(mul(art, rate), dai);
214 | wad = rad / RAY;
215 |
216 | // If the rad precision has some dust, it will need to request for 1 extra wad wei
217 | wad = mul(wad, RAY) < rad ? wad + 1 : wad;
218 | }
219 |
220 | // Public functions
221 |
222 | function transfer(address gem, address dst, uint amt) public {
223 | GemLike(gem).transfer(dst, amt);
224 | }
225 |
226 | function ethJoin_join(address apt, address urn) public payable {
227 | // Wraps ETH in WETH
228 | GemJoinLike(apt).gem().deposit.value(msg.value)();
229 | // Approves adapter to take the WETH amount
230 | GemJoinLike(apt).gem().approve(address(apt), msg.value);
231 | // Joins WETH collateral into the vat
232 | GemJoinLike(apt).join(urn, msg.value);
233 | }
234 |
235 | function gemJoin_join(address apt, address urn, uint amt, bool transferFrom) public {
236 | // Only executes for tokens that have approval/transferFrom implementation
237 | if (transferFrom) {
238 | // Gets token from the user's wallet
239 | GemJoinLike(apt).gem().transferFrom(msg.sender, address(this), amt);
240 | // Approves adapter to take the token amount
241 | GemJoinLike(apt).gem().approve(apt, amt);
242 | }
243 | // Joins token collateral into the vat
244 | GemJoinLike(apt).join(urn, amt);
245 | }
246 |
247 | function hope(
248 | address obj,
249 | address usr
250 | ) public {
251 | HopeLike(obj).hope(usr);
252 | }
253 |
254 | function nope(
255 | address obj,
256 | address usr
257 | ) public {
258 | HopeLike(obj).nope(usr);
259 | }
260 |
261 | function open(
262 | address manager,
263 | bytes32 ilk,
264 | address usr
265 | ) public returns (uint cdp) {
266 | cdp = ManagerLike(manager).open(ilk, usr);
267 | }
268 |
269 | function give(
270 | address manager,
271 | uint cdp,
272 | address usr
273 | ) public {
274 | ManagerLike(manager).give(cdp, usr);
275 | }
276 |
277 | function giveToProxy(
278 | address proxyRegistry,
279 | address manager,
280 | uint cdp,
281 | address dst
282 | ) public {
283 | // Gets actual proxy address
284 | address proxy = ProxyRegistryLike(proxyRegistry).proxies(dst);
285 | // Checks if the proxy address already existed and dst address is still the owner
286 | if (proxy == address(0) || ProxyLike(proxy).owner() != dst) {
287 | uint csize;
288 | assembly {
289 | csize := extcodesize(dst)
290 | }
291 | // We want to avoid creating a proxy for a contract address that might not be able to handle proxies, then losing the CDP
292 | require(csize == 0, "Dst-is-a-contract");
293 | // Creates the proxy for the dst address
294 | proxy = ProxyRegistryLike(proxyRegistry).build(dst);
295 | }
296 | // Transfers CDP to the dst proxy
297 | give(manager, cdp, proxy);
298 | }
299 |
300 | function cdpAllow(
301 | address manager,
302 | uint cdp,
303 | address usr,
304 | uint ok
305 | ) public {
306 | ManagerLike(manager).cdpAllow(cdp, usr, ok);
307 | }
308 |
309 | function urnAllow(
310 | address manager,
311 | address usr,
312 | uint ok
313 | ) public {
314 | ManagerLike(manager).urnAllow(usr, ok);
315 | }
316 |
317 | function flux(
318 | address manager,
319 | uint cdp,
320 | address dst,
321 | uint wad
322 | ) public {
323 | ManagerLike(manager).flux(cdp, dst, wad);
324 | }
325 |
326 | function move(
327 | address manager,
328 | uint cdp,
329 | address dst,
330 | uint rad
331 | ) public {
332 | ManagerLike(manager).move(cdp, dst, rad);
333 | }
334 |
335 | function frob(
336 | address manager,
337 | uint cdp,
338 | int dink,
339 | int dart
340 | ) public {
341 | ManagerLike(manager).frob(cdp, dink, dart);
342 | }
343 |
344 | function quit(
345 | address manager,
346 | uint cdp,
347 | address dst
348 | ) public {
349 | ManagerLike(manager).quit(cdp, dst);
350 | }
351 |
352 | function enter(
353 | address manager,
354 | address src,
355 | uint cdp
356 | ) public {
357 | ManagerLike(manager).enter(src, cdp);
358 | }
359 |
360 | function shift(
361 | address manager,
362 | uint cdpSrc,
363 | uint cdpOrg
364 | ) public {
365 | ManagerLike(manager).shift(cdpSrc, cdpOrg);
366 | }
367 |
368 | function makeGemBag(
369 | address gemJoin
370 | ) public returns (address bag) {
371 | bag = GNTJoinLike(gemJoin).make(address(this));
372 | }
373 |
374 | function lockETH(
375 | address manager,
376 | address ethJoin,
377 | uint cdp
378 | ) public payable {
379 | // Receives ETH amount, converts it to WETH and joins it into the vat
380 | ethJoin_join(ethJoin, address(this));
381 | // Locks WETH amount into the CDP
382 | VatLike(ManagerLike(manager).vat()).frob(
383 | ManagerLike(manager).ilks(cdp),
384 | ManagerLike(manager).urns(cdp),
385 | address(this),
386 | address(this),
387 | toInt(msg.value),
388 | 0
389 | );
390 | }
391 |
392 | function safeLockETH(
393 | address manager,
394 | address ethJoin,
395 | uint cdp,
396 | address owner
397 | ) public payable {
398 | require(ManagerLike(manager).owns(cdp) == owner, "owner-missmatch");
399 | lockETH(manager, ethJoin, cdp);
400 | }
401 |
402 | function lockGem(
403 | address manager,
404 | address gemJoin,
405 | uint cdp,
406 | uint amt,
407 | bool transferFrom
408 | ) public {
409 | // Takes token amount from user's wallet and joins into the vat
410 | gemJoin_join(gemJoin, address(this), amt, transferFrom);
411 | // Locks token amount into the CDP
412 | VatLike(ManagerLike(manager).vat()).frob(
413 | ManagerLike(manager).ilks(cdp),
414 | ManagerLike(manager).urns(cdp),
415 | address(this),
416 | address(this),
417 | toInt(convertTo18(gemJoin, amt)),
418 | 0
419 | );
420 | }
421 |
422 | function safeLockGem(
423 | address manager,
424 | address gemJoin,
425 | uint cdp,
426 | uint amt,
427 | bool transferFrom,
428 | address owner
429 | ) public {
430 | require(ManagerLike(manager).owns(cdp) == owner, "owner-missmatch");
431 | lockGem(manager, gemJoin, cdp, amt, transferFrom);
432 | }
433 |
434 | function freeETH(
435 | address manager,
436 | address ethJoin,
437 | uint cdp,
438 | uint wad
439 | ) public {
440 | // Unlocks WETH amount from the CDP
441 | frob(manager, cdp, -toInt(wad), 0);
442 | // Moves the amount from the CDP urn to proxy's address
443 | flux(manager, cdp, address(this), wad);
444 | // Exits WETH amount to proxy address as a token
445 | GemJoinLike(ethJoin).exit(address(this), wad);
446 | // Converts WETH to ETH
447 | GemJoinLike(ethJoin).gem().withdraw(wad);
448 | // Sends ETH back to the user's wallet
449 | msg.sender.transfer(wad);
450 | }
451 |
452 | function freeGem(
453 | address manager,
454 | address gemJoin,
455 | uint cdp,
456 | uint amt
457 | ) public {
458 | uint wad = convertTo18(gemJoin, amt);
459 | // Unlocks token amount from the CDP
460 | frob(manager, cdp, -toInt(wad), 0);
461 | // Moves the amount from the CDP urn to proxy's address
462 | flux(manager, cdp, address(this), wad);
463 | // Exits token amount to the user's wallet as a token
464 | GemJoinLike(gemJoin).exit(msg.sender, amt);
465 | }
466 |
467 | function exitETH(
468 | address manager,
469 | address ethJoin,
470 | uint cdp,
471 | uint wad
472 | ) public {
473 | // Moves the amount from the CDP urn to proxy's address
474 | flux(manager, cdp, address(this), wad);
475 |
476 | // Exits WETH amount to proxy address as a token
477 | GemJoinLike(ethJoin).exit(address(this), wad);
478 | // Converts WETH to ETH
479 | GemJoinLike(ethJoin).gem().withdraw(wad);
480 | // Sends ETH back to the user's wallet
481 | msg.sender.transfer(wad);
482 | }
483 |
484 | function exitGem(
485 | address manager,
486 | address gemJoin,
487 | uint cdp,
488 | uint amt
489 | ) public {
490 | // Moves the amount from the CDP urn to proxy's address
491 | flux(manager, cdp, address(this), convertTo18(gemJoin, amt));
492 |
493 | // Exits token amount to the user's wallet as a token
494 | GemJoinLike(gemJoin).exit(msg.sender, amt);
495 | }
496 |
497 | function draw(
498 | address manager,
499 | address jug,
500 | address daiJoin,
501 | uint cdp,
502 | uint wad
503 | ) public {
504 | address urn = ManagerLike(manager).urns(cdp);
505 | address vat = ManagerLike(manager).vat();
506 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
507 | // Generates debt in the CDP
508 | frob(manager, cdp, 0, _getDrawDart(vat, jug, urn, ilk, wad));
509 | // Moves the DAI amount (balance in the vat in rad) to proxy's address
510 | move(manager, cdp, address(this), toRad(wad));
511 | // Allows adapter to access to proxy's DAI balance in the vat
512 | if (VatLike(vat).can(address(this), address(daiJoin)) == 0) {
513 | VatLike(vat).hope(daiJoin);
514 | }
515 | // Exits DAI to the user's wallet as a token
516 | DaiJoinLike(daiJoin).exit(msg.sender, wad);
517 | }
518 |
519 | function wipe(
520 | address manager,
521 | address daiJoin,
522 | uint cdp,
523 | uint wad
524 | ) public {
525 | address vat = ManagerLike(manager).vat();
526 | address urn = ManagerLike(manager).urns(cdp);
527 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
528 |
529 | address own = ManagerLike(manager).owns(cdp);
530 | if (own == address(this) || ManagerLike(manager).cdpCan(own, cdp, address(this)) == 1) {
531 | // Joins DAI amount into the vat
532 | daiJoin_join(daiJoin, urn, wad);
533 | // Paybacks debt to the CDP
534 | frob(manager, cdp, 0, _getWipeDart(vat, VatLike(vat).dai(urn), urn, ilk));
535 | } else {
536 | // Joins DAI amount into the vat
537 | daiJoin_join(daiJoin, address(this), wad);
538 | // Paybacks debt to the CDP
539 | VatLike(vat).frob(
540 | ilk,
541 | urn,
542 | address(this),
543 | address(this),
544 | 0,
545 | _getWipeDart(vat, wad * RAY, urn, ilk)
546 | );
547 | }
548 | }
549 |
550 | function safeWipe(
551 | address manager,
552 | address daiJoin,
553 | uint cdp,
554 | uint wad,
555 | address owner
556 | ) public {
557 | require(ManagerLike(manager).owns(cdp) == owner, "owner-missmatch");
558 | wipe(manager, daiJoin, cdp, wad);
559 | }
560 |
561 | function wipeAll(
562 | address manager,
563 | address daiJoin,
564 | uint cdp
565 | ) public {
566 | address vat = ManagerLike(manager).vat();
567 | address urn = ManagerLike(manager).urns(cdp);
568 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
569 | (, uint art) = VatLike(vat).urns(ilk, urn);
570 |
571 | address own = ManagerLike(manager).owns(cdp);
572 | if (own == address(this) || ManagerLike(manager).cdpCan(own, cdp, address(this)) == 1) {
573 | // Joins DAI amount into the vat
574 | daiJoin_join(daiJoin, urn, _getWipeAllWad(vat, urn, urn, ilk));
575 | // Paybacks debt to the CDP
576 | frob(manager, cdp, 0, -int(art));
577 | } else {
578 | // Joins DAI amount into the vat
579 | daiJoin_join(daiJoin, address(this), _getWipeAllWad(vat, address(this), urn, ilk));
580 | // Paybacks debt to the CDP
581 | VatLike(vat).frob(
582 | ilk,
583 | urn,
584 | address(this),
585 | address(this),
586 | 0,
587 | -int(art)
588 | );
589 | }
590 | }
591 |
592 | function safeWipeAll(
593 | address manager,
594 | address daiJoin,
595 | uint cdp,
596 | address owner
597 | ) public {
598 | require(ManagerLike(manager).owns(cdp) == owner, "owner-missmatch");
599 | wipeAll(manager, daiJoin, cdp);
600 | }
601 |
602 | function lockETHAndDraw(
603 | address manager,
604 | address jug,
605 | address ethJoin,
606 | address daiJoin,
607 | uint cdp,
608 | uint wadD
609 | ) public payable {
610 | address urn = ManagerLike(manager).urns(cdp);
611 | address vat = ManagerLike(manager).vat();
612 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
613 | // Receives ETH amount, converts it to WETH and joins it into the vat
614 | ethJoin_join(ethJoin, urn);
615 | // Locks WETH amount into the CDP and generates debt
616 | frob(manager, cdp, toInt(msg.value), _getDrawDart(vat, jug, urn, ilk, wadD));
617 | // Moves the DAI amount (balance in the vat in rad) to proxy's address
618 | move(manager, cdp, address(this), toRad(wadD));
619 | // Allows adapter to access to proxy's DAI balance in the vat
620 | if (VatLike(vat).can(address(this), address(daiJoin)) == 0) {
621 | VatLike(vat).hope(daiJoin);
622 | }
623 | // Exits DAI to the user's wallet as a token
624 | DaiJoinLike(daiJoin).exit(msg.sender, wadD);
625 | }
626 |
627 | function openLockETHAndDraw(
628 | address manager,
629 | address jug,
630 | address ethJoin,
631 | address daiJoin,
632 | bytes32 ilk,
633 | uint wadD
634 | ) public payable returns (uint cdp) {
635 | cdp = open(manager, ilk, address(this));
636 | lockETHAndDraw(manager, jug, ethJoin, daiJoin, cdp, wadD);
637 | }
638 |
639 | function lockGemAndDraw(
640 | address manager,
641 | address jug,
642 | address gemJoin,
643 | address daiJoin,
644 | uint cdp,
645 | uint amtC,
646 | uint wadD,
647 | bool transferFrom
648 | ) public {
649 | address urn = ManagerLike(manager).urns(cdp);
650 | address vat = ManagerLike(manager).vat();
651 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
652 | // Takes token amount from user's wallet and joins into the vat
653 | gemJoin_join(gemJoin, urn, amtC, transferFrom);
654 | // Locks token amount into the CDP and generates debt
655 | frob(manager, cdp, toInt(convertTo18(gemJoin, amtC)), _getDrawDart(vat, jug, urn, ilk, wadD));
656 | // Moves the DAI amount (balance in the vat in rad) to proxy's address
657 | move(manager, cdp, address(this), toRad(wadD));
658 | // Allows adapter to access to proxy's DAI balance in the vat
659 | if (VatLike(vat).can(address(this), address(daiJoin)) == 0) {
660 | VatLike(vat).hope(daiJoin);
661 | }
662 | // Exits DAI to the user's wallet as a token
663 | DaiJoinLike(daiJoin).exit(msg.sender, wadD);
664 | }
665 |
666 | function openLockGemAndDraw(
667 | address manager,
668 | address jug,
669 | address gemJoin,
670 | address daiJoin,
671 | bytes32 ilk,
672 | uint amtC,
673 | uint wadD,
674 | bool transferFrom
675 | ) public returns (uint cdp) {
676 | cdp = open(manager, ilk, address(this));
677 | lockGemAndDraw(manager, jug, gemJoin, daiJoin, cdp, amtC, wadD, transferFrom);
678 | }
679 |
680 | function openLockGNTAndDraw(
681 | address manager,
682 | address jug,
683 | address gntJoin,
684 | address daiJoin,
685 | bytes32 ilk,
686 | uint amtC,
687 | uint wadD
688 | ) public returns (address bag, uint cdp) {
689 | // Creates bag (if doesn't exist) to hold GNT
690 | bag = GNTJoinLike(gntJoin).bags(address(this));
691 | if (bag == address(0)) {
692 | bag = makeGemBag(gntJoin);
693 | }
694 | // Transfer funds to the funds which previously were sent to the proxy
695 | GemLike(GemJoinLike(gntJoin).gem()).transfer(bag, amtC);
696 | cdp = openLockGemAndDraw(manager, jug, gntJoin, daiJoin, ilk, amtC, wadD, false);
697 | }
698 |
699 | function wipeAndFreeETH(
700 | address manager,
701 | address ethJoin,
702 | address daiJoin,
703 | uint cdp,
704 | uint wadC,
705 | uint wadD
706 | ) public {
707 | address urn = ManagerLike(manager).urns(cdp);
708 | // Joins DAI amount into the vat
709 | daiJoin_join(daiJoin, urn, wadD);
710 | // Paybacks debt to the CDP and unlocks WETH amount from it
711 | frob(
712 | manager,
713 | cdp,
714 | -toInt(wadC),
715 | _getWipeDart(ManagerLike(manager).vat(), VatLike(ManagerLike(manager).vat()).dai(urn), urn, ManagerLike(manager).ilks(cdp))
716 | );
717 | // Moves the amount from the CDP urn to proxy's address
718 | flux(manager, cdp, address(this), wadC);
719 | // Exits WETH amount to proxy address as a token
720 | GemJoinLike(ethJoin).exit(address(this), wadC);
721 | // Converts WETH to ETH
722 | GemJoinLike(ethJoin).gem().withdraw(wadC);
723 | // Sends ETH back to the user's wallet
724 | msg.sender.transfer(wadC);
725 | }
726 |
727 | function wipeAllAndFreeETH(
728 | address manager,
729 | address ethJoin,
730 | address daiJoin,
731 | uint cdp,
732 | uint wadC
733 | ) public {
734 | address vat = ManagerLike(manager).vat();
735 | address urn = ManagerLike(manager).urns(cdp);
736 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
737 | (, uint art) = VatLike(vat).urns(ilk, urn);
738 |
739 | // Joins DAI amount into the vat
740 | daiJoin_join(daiJoin, urn, _getWipeAllWad(vat, urn, urn, ilk));
741 | // Paybacks debt to the CDP and unlocks WETH amount from it
742 | frob(
743 | manager,
744 | cdp,
745 | -toInt(wadC),
746 | -int(art)
747 | );
748 | // Moves the amount from the CDP urn to proxy's address
749 | flux(manager, cdp, address(this), wadC);
750 | // Exits WETH amount to proxy address as a token
751 | GemJoinLike(ethJoin).exit(address(this), wadC);
752 | // Converts WETH to ETH
753 | GemJoinLike(ethJoin).gem().withdraw(wadC);
754 | // Sends ETH back to the user's wallet
755 | msg.sender.transfer(wadC);
756 | }
757 |
758 | function wipeAndFreeGem(
759 | address manager,
760 | address gemJoin,
761 | address daiJoin,
762 | uint cdp,
763 | uint amtC,
764 | uint wadD
765 | ) public {
766 | address urn = ManagerLike(manager).urns(cdp);
767 | // Joins DAI amount into the vat
768 | daiJoin_join(daiJoin, urn, wadD);
769 | uint wadC = convertTo18(gemJoin, amtC);
770 | // Paybacks debt to the CDP and unlocks token amount from it
771 | frob(
772 | manager,
773 | cdp,
774 | -toInt(wadC),
775 | _getWipeDart(ManagerLike(manager).vat(), VatLike(ManagerLike(manager).vat()).dai(urn), urn, ManagerLike(manager).ilks(cdp))
776 | );
777 | // Moves the amount from the CDP urn to proxy's address
778 | flux(manager, cdp, address(this), wadC);
779 | // Exits token amount to the user's wallet as a token
780 | GemJoinLike(gemJoin).exit(msg.sender, amtC);
781 | }
782 |
783 | function wipeAllAndFreeGem(
784 | address manager,
785 | address gemJoin,
786 | address daiJoin,
787 | uint cdp,
788 | uint amtC
789 | ) public {
790 | address vat = ManagerLike(manager).vat();
791 | address urn = ManagerLike(manager).urns(cdp);
792 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
793 | (, uint art) = VatLike(vat).urns(ilk, urn);
794 |
795 | // Joins DAI amount into the vat
796 | daiJoin_join(daiJoin, urn, _getWipeAllWad(vat, urn, urn, ilk));
797 | uint wadC = convertTo18(gemJoin, amtC);
798 | // Paybacks debt to the CDP and unlocks token amount from it
799 | frob(
800 | manager,
801 | cdp,
802 | -toInt(wadC),
803 | -int(art)
804 | );
805 | // Moves the amount from the CDP urn to proxy's address
806 | flux(manager, cdp, address(this), wadC);
807 | // Exits token amount to the user's wallet as a token
808 | GemJoinLike(gemJoin).exit(msg.sender, amtC);
809 | }
810 | }
811 |
812 | contract DssProxyActionsEnd is Common {
813 | // Internal functions
814 |
815 | function _free(
816 | address manager,
817 | address end,
818 | uint cdp
819 | ) internal returns (uint ink) {
820 | bytes32 ilk = ManagerLike(manager).ilks(cdp);
821 | address urn = ManagerLike(manager).urns(cdp);
822 | VatLike vat = VatLike(ManagerLike(manager).vat());
823 | uint art;
824 | (ink, art) = vat.urns(ilk, urn);
825 |
826 | // If CDP still has debt, it needs to be paid
827 | if (art > 0) {
828 | EndLike(end).skim(ilk, urn);
829 | (ink,) = vat.urns(ilk, urn);
830 | }
831 | // Approves the manager to transfer the position to proxy's address in the vat
832 | if (vat.can(address(this), address(manager)) == 0) {
833 | vat.hope(manager);
834 | }
835 | // Transfers position from CDP to the proxy address
836 | ManagerLike(manager).quit(cdp, address(this));
837 | // Frees the position and recovers the collateral in the vat registry
838 | EndLike(end).free(ilk);
839 | }
840 |
841 | // Public functions
842 | function freeETH(
843 | address manager,
844 | address ethJoin,
845 | address end,
846 | uint cdp
847 | ) public {
848 | uint wad = _free(manager, end, cdp);
849 | // Exits WETH amount to proxy address as a token
850 | GemJoinLike(ethJoin).exit(address(this), wad);
851 | // Converts WETH to ETH
852 | GemJoinLike(ethJoin).gem().withdraw(wad);
853 | // Sends ETH back to the user's wallet
854 | msg.sender.transfer(wad);
855 | }
856 |
857 | function freeGem(
858 | address manager,
859 | address gemJoin,
860 | address end,
861 | uint cdp
862 | ) public {
863 | uint amt = _free(manager, end, cdp) / 10 ** (18 - GemJoinLike(gemJoin).dec());
864 | // Exits token amount to the user's wallet as a token
865 | GemJoinLike(gemJoin).exit(msg.sender, amt);
866 | }
867 |
868 | function pack(
869 | address daiJoin,
870 | address end,
871 | uint wad
872 | ) public {
873 | daiJoin_join(daiJoin, address(this), wad);
874 | VatLike vat = DaiJoinLike(daiJoin).vat();
875 | // Approves the end to take out DAI from the proxy's balance in the vat
876 | if (vat.can(address(this), address(end)) == 0) {
877 | vat.hope(end);
878 | }
879 | EndLike(end).pack(wad);
880 | }
881 |
882 | function cashETH(
883 | address ethJoin,
884 | address end,
885 | bytes32 ilk,
886 | uint wad
887 | ) public {
888 | EndLike(end).cash(ilk, wad);
889 | uint wadC = mul(wad, EndLike(end).fix(ilk)) / RAY;
890 | // Exits WETH amount to proxy address as a token
891 | GemJoinLike(ethJoin).exit(address(this), wadC);
892 | // Converts WETH to ETH
893 | GemJoinLike(ethJoin).gem().withdraw(wadC);
894 | // Sends ETH back to the user's wallet
895 | msg.sender.transfer(wadC);
896 | }
897 |
898 | function cashGem(
899 | address gemJoin,
900 | address end,
901 | bytes32 ilk,
902 | uint wad
903 | ) public {
904 | EndLike(end).cash(ilk, wad);
905 | // Exits token amount to the user's wallet as a token
906 | uint amt = mul(wad, EndLike(end).fix(ilk)) / RAY / 10 ** (18 - GemJoinLike(gemJoin).dec());
907 | GemJoinLike(gemJoin).exit(msg.sender, amt);
908 | }
909 | }
910 |
911 | contract DssProxyActionsDsr is Common {
912 | function join(
913 | address daiJoin,
914 | address pot,
915 | uint wad
916 | ) public {
917 | VatLike vat = DaiJoinLike(daiJoin).vat();
918 | // Executes drip to get the chi rate updated to rho == now, otherwise join will fail
919 | uint chi = PotLike(pot).drip();
920 | // Joins wad amount to the vat balance
921 | daiJoin_join(daiJoin, address(this), wad);
922 | // Approves the pot to take out DAI from the proxy's balance in the vat
923 | if (vat.can(address(this), address(pot)) == 0) {
924 | vat.hope(pot);
925 | }
926 | // Joins the pie value (equivalent to the DAI wad amount) in the pot
927 | PotLike(pot).join(mul(wad, RAY) / chi);
928 | }
929 |
930 | function exit(
931 | address daiJoin,
932 | address pot,
933 | uint wad
934 | ) public {
935 | VatLike vat = DaiJoinLike(daiJoin).vat();
936 | // Executes drip to count the savings accumulated until this moment
937 | uint chi = PotLike(pot).drip();
938 | // Calculates the pie value in the pot equivalent to the DAI wad amount
939 | uint pie = mul(wad, RAY) / chi;
940 | // Exits DAI from the pot
941 | PotLike(pot).exit(pie);
942 | // Checks the actual balance of DAI in the vat after the pot exit
943 | uint bal = DaiJoinLike(daiJoin).vat().dai(address(this));
944 | // Allows adapter to access to proxy's DAI balance in the vat
945 | if (vat.can(address(this), address(daiJoin)) == 0) {
946 | vat.hope(daiJoin);
947 | }
948 | // It is necessary to check if due rounding the exact wad amount can be exited by the adapter.
949 | // Otherwise it will do the maximum DAI balance in the vat
950 | DaiJoinLike(daiJoin).exit(
951 | msg.sender,
952 | bal >= mul(wad, RAY) ? wad : bal / RAY
953 | );
954 | }
955 |
956 | function exitAll(
957 | address daiJoin,
958 | address pot
959 | ) public {
960 | VatLike vat = DaiJoinLike(daiJoin).vat();
961 | // Executes drip to count the savings accumulated until this moment
962 | uint chi = PotLike(pot).drip();
963 | // Gets the total pie belonging to the proxy address
964 | uint pie = PotLike(pot).pie(address(this));
965 | // Exits DAI from the pot
966 | PotLike(pot).exit(pie);
967 | // Allows adapter to access to proxy's DAI balance in the vat
968 | if (vat.can(address(this), address(daiJoin)) == 0) {
969 | vat.hope(daiJoin);
970 | }
971 | // Exits the DAI amount corresponding to the value of pie
972 | DaiJoinLike(daiJoin).exit(msg.sender, mul(chi, pie) / RAY);
973 | }
974 | }
975 |
--------------------------------------------------------------------------------
/src/DssProxyActions.t.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.12;
2 |
3 | import "ds-test/test.sol";
4 |
5 | import "./DssProxyActions.sol";
6 |
7 | import {DssDeployTestBase, GemJoin, Flipper} from "dss-deploy/DssDeploy.t.base.sol";
8 | import {DGD} from "dss-gem-joins/tokens/DGD.sol";
9 | import {GNT} from "dss-gem-joins/tokens/GNT.sol";
10 | import {GemJoin3} from "dss-gem-joins/join-3.sol";
11 | import {GemJoin4} from "dss-gem-joins/join-4.sol";
12 | import {DSValue} from "ds-value/value.sol";
13 | import {DssCdpManager} from "dss-cdp-manager/DssCdpManager.sol";
14 | import {GetCdps} from "dss-cdp-manager/GetCdps.sol";
15 | import {ProxyRegistry, DSProxyFactory, DSProxy} from "proxy-registry/ProxyRegistry.sol";
16 | import {WETH9_} from "ds-weth/weth9.sol";
17 |
18 | contract ProxyCalls {
19 | DSProxy proxy;
20 | address dssProxyActions;
21 | address dssProxyActionsEnd;
22 | address dssProxyActionsDsr;
23 |
24 | function transfer(address, address, uint256) public {
25 | proxy.execute(dssProxyActions, msg.data);
26 | }
27 |
28 | function open(address, bytes32, address) public returns (uint cdp) {
29 | bytes memory response = proxy.execute(dssProxyActions, msg.data);
30 | assembly {
31 | cdp := mload(add(response, 0x20))
32 | }
33 | }
34 |
35 | function give(address, uint, address) public {
36 | proxy.execute(dssProxyActions, msg.data);
37 | }
38 |
39 | function giveToProxy(address, address, uint, address) public {
40 | proxy.execute(dssProxyActions, msg.data);
41 | }
42 |
43 | function cdpAllow(address, uint, address, uint) public {
44 | proxy.execute(dssProxyActions, msg.data);
45 | }
46 |
47 | function urnAllow(address, address, uint) public {
48 | proxy.execute(dssProxyActions, msg.data);
49 | }
50 |
51 | function hope(address, address) public {
52 | proxy.execute(dssProxyActions, msg.data);
53 | }
54 |
55 | function nope(address, address) public {
56 | proxy.execute(dssProxyActions, msg.data);
57 | }
58 |
59 | function flux(address, uint, address, uint) public {
60 | proxy.execute(dssProxyActions, msg.data);
61 | }
62 |
63 | function move(address, uint, address, uint) public {
64 | proxy.execute(dssProxyActions, msg.data);
65 | }
66 |
67 | function frob(address, uint, int, int) public {
68 | proxy.execute(dssProxyActions, msg.data);
69 | }
70 |
71 | function frob(address, uint, address, int, int) public {
72 | proxy.execute(dssProxyActions, msg.data);
73 | }
74 |
75 | function quit(address, uint, address) public {
76 | proxy.execute(dssProxyActions, msg.data);
77 | }
78 |
79 | function enter(address, address, uint) public {
80 | proxy.execute(dssProxyActions, msg.data);
81 | }
82 |
83 | function shift(address, uint, uint) public {
84 | proxy.execute(dssProxyActions, msg.data);
85 | }
86 |
87 | function lockETH(address, address, uint) public payable {
88 | (bool success,) = address(proxy).call.value(msg.value)(abi.encodeWithSignature("execute(address,bytes)", dssProxyActions, msg.data));
89 | require(success, "");
90 | }
91 |
92 | function safeLockETH(address, address, uint, address) public payable {
93 | (bool success,) = address(proxy).call.value(msg.value)(abi.encodeWithSignature("execute(address,bytes)", dssProxyActions, msg.data));
94 | require(success, "");
95 | }
96 |
97 | function lockGem(address, address, uint, uint, bool) public {
98 | proxy.execute(dssProxyActions, msg.data);
99 | }
100 |
101 | function safeLockGem(address, address, uint, uint, bool, address) public {
102 | proxy.execute(dssProxyActions, msg.data);
103 | }
104 |
105 | function makeGemBag(address) public returns (address bag) {
106 | address payable target = address(proxy);
107 | bytes memory data = abi.encodeWithSignature("execute(address,bytes)", dssProxyActions, msg.data);
108 | assembly {
109 | let succeeded := call(sub(gas(), 5000), target, callvalue(), add(data, 0x20), mload(data), 0, 0)
110 | let size := returndatasize()
111 | let response := mload(0x40)
112 | mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
113 | mstore(response, size)
114 | returndatacopy(add(response, 0x20), 0, size)
115 |
116 | bag := mload(add(response, 0x60))
117 |
118 | switch iszero(succeeded)
119 | case 1 {
120 | // throw if delegatecall failed
121 | revert(add(response, 0x20), size)
122 | }
123 | }
124 | }
125 |
126 | function freeETH(address, address, uint, uint) public {
127 | proxy.execute(dssProxyActions, msg.data);
128 | }
129 |
130 | function freeGem(address, address, uint, uint) public {
131 | proxy.execute(dssProxyActions, msg.data);
132 | }
133 |
134 | function exitETH(address, address, uint, uint) public {
135 | proxy.execute(dssProxyActions, msg.data);
136 | }
137 |
138 | function exitGem(address, address, uint, uint) public {
139 | proxy.execute(dssProxyActions, msg.data);
140 | }
141 |
142 | function draw(address, address, address, uint, uint) public {
143 | proxy.execute(dssProxyActions, msg.data);
144 | }
145 |
146 | function wipe(address, address, uint, uint) public {
147 | proxy.execute(dssProxyActions, msg.data);
148 | }
149 |
150 | function wipeAll(address, address, uint) public {
151 | proxy.execute(dssProxyActions, msg.data);
152 | }
153 |
154 | function safeWipe(address, address, uint, uint, address) public {
155 | proxy.execute(dssProxyActions, msg.data);
156 | }
157 |
158 | function safeWipeAll(address, address, uint, address) public {
159 | proxy.execute(dssProxyActions, msg.data);
160 | }
161 |
162 | function lockETHAndDraw(address, address, address, address, uint, uint) public payable {
163 | (bool success,) = address(proxy).call.value(msg.value)(abi.encodeWithSignature("execute(address,bytes)", dssProxyActions, msg.data));
164 | require(success, "");
165 | }
166 |
167 | function openLockETHAndDraw(address, address, address, address, bytes32, uint) public payable returns (uint cdp) {
168 | address payable target = address(proxy);
169 | bytes memory data = abi.encodeWithSignature("execute(address,bytes)", dssProxyActions, msg.data);
170 | assembly {
171 | let succeeded := call(sub(gas(), 5000), target, callvalue(), add(data, 0x20), mload(data), 0, 0)
172 | let size := returndatasize()
173 | let response := mload(0x40)
174 | mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
175 | mstore(response, size)
176 | returndatacopy(add(response, 0x20), 0, size)
177 |
178 | cdp := mload(add(response, 0x60))
179 |
180 | switch iszero(succeeded)
181 | case 1 {
182 | // throw if delegatecall failed
183 | revert(add(response, 0x20), size)
184 | }
185 | }
186 | }
187 |
188 | function lockGemAndDraw(address, address, address, address, uint, uint, uint, bool) public {
189 | proxy.execute(dssProxyActions, msg.data);
190 | }
191 |
192 | function openLockGemAndDraw(address, address, address, address, bytes32, uint, uint, bool) public returns (uint cdp) {
193 | bytes memory response = proxy.execute(dssProxyActions, msg.data);
194 | assembly {
195 | cdp := mload(add(response, 0x20))
196 | }
197 | }
198 |
199 | function openLockGNTAndDraw(address, address, address, address, bytes32, uint, uint) public returns (address bag, uint cdp) {
200 | bytes memory response = proxy.execute(dssProxyActions, msg.data);
201 | assembly {
202 | bag := mload(add(response, 0x20))
203 | cdp := mload(add(response, 0x40))
204 | }
205 | }
206 |
207 | function wipeAndFreeETH(address, address, address, uint, uint, uint) public {
208 | proxy.execute(dssProxyActions, msg.data);
209 | }
210 |
211 | function wipeAllAndFreeETH(address, address, address, uint, uint) public {
212 | proxy.execute(dssProxyActions, msg.data);
213 | }
214 |
215 | function wipeAndFreeGem(address, address, address, uint, uint, uint) public {
216 | proxy.execute(dssProxyActions, msg.data);
217 | }
218 |
219 | function wipeAllAndFreeGem(address, address, address, uint, uint) public {
220 | proxy.execute(dssProxyActions, msg.data);
221 | }
222 |
223 | function end_freeETH(address a, address b, address c, uint d) public {
224 | proxy.execute(dssProxyActionsEnd, abi.encodeWithSignature("freeETH(address,address,address,uint256)", a, b, c, d));
225 | }
226 |
227 | function end_freeGem(address a, address b, address c, uint d) public {
228 | proxy.execute(dssProxyActionsEnd, abi.encodeWithSignature("freeGem(address,address,address,uint256)", a, b, c, d));
229 | }
230 |
231 | function end_pack(address a, address b, uint c) public {
232 | proxy.execute(dssProxyActionsEnd, abi.encodeWithSignature("pack(address,address,uint256)", a, b, c));
233 | }
234 |
235 | function end_cashETH(address a, address b, bytes32 c, uint d) public {
236 | proxy.execute(dssProxyActionsEnd, abi.encodeWithSignature("cashETH(address,address,bytes32,uint256)", a, b, c, d));
237 | }
238 |
239 | function end_cashGem(address a, address b, bytes32 c, uint d) public {
240 | proxy.execute(dssProxyActionsEnd, abi.encodeWithSignature("cashGem(address,address,bytes32,uint256)", a, b, c, d));
241 | }
242 |
243 | function dsr_join(address a, address b, uint c) public {
244 | proxy.execute(dssProxyActionsDsr, abi.encodeWithSignature("join(address,address,uint256)", a, b, c));
245 | }
246 |
247 | function dsr_exit(address a, address b, uint c) public {
248 | proxy.execute(dssProxyActionsDsr, abi.encodeWithSignature("exit(address,address,uint256)", a, b, c));
249 | }
250 |
251 | function dsr_exitAll(address a, address b) public {
252 | proxy.execute(dssProxyActionsDsr, abi.encodeWithSignature("exitAll(address,address)", a, b));
253 | }
254 | }
255 |
256 | contract FakeUser {
257 | function doGive(
258 | DssCdpManager manager,
259 | uint cdp,
260 | address dst
261 | ) public {
262 | manager.give(cdp, dst);
263 | }
264 | }
265 |
266 | contract DssProxyActionsTest is DssDeployTestBase, ProxyCalls {
267 | DssCdpManager manager;
268 |
269 | GemJoin3 dgdJoin;
270 | DGD dgd;
271 | DSValue pipDGD;
272 | Flipper dgdFlip;
273 | GemJoin4 gntJoin;
274 | GNT gnt;
275 | DSValue pipGNT;
276 | ProxyRegistry registry;
277 | WETH9_ realWeth;
278 |
279 | function setUp() public {
280 | super.setUp();
281 | deployKeepAuth();
282 |
283 | // Create a real WETH token and replace it with a new adapter in the vat
284 | realWeth = new WETH9_();
285 | this.deny(address(vat), address(ethJoin));
286 | ethJoin = new GemJoin(address(vat), "ETH", address(realWeth));
287 | this.rely(address(vat), address(ethJoin));
288 |
289 | // Add a token collateral
290 | dgd = new DGD(1000 * 10 ** 9);
291 | dgdJoin = new GemJoin3(address(vat), "DGD", address(dgd), 9);
292 | pipDGD = new DSValue();
293 | dssDeploy.deployCollateral("DGD", address(dgdJoin), address(pipDGD));
294 | (dgdFlip, ) = dssDeploy.ilks("DGD");
295 | pipDGD.poke(bytes32(uint(50 ether))); // Price 50 DAI = 1 DGD (in precision 18)
296 | this.file(address(spotter), "DGD", "mat", uint(1500000000 ether)); // Liquidation ratio 150%
297 | this.file(address(vat), bytes32("DGD"), bytes32("line"), uint(10000 * 10 ** 45));
298 | spotter.poke("DGD");
299 | (,,uint spot,,) = vat.ilks("DGD");
300 | assertEq(spot, 50 * RAY * RAY / 1500000000 ether);
301 |
302 | gnt = new GNT(1000000 ether);
303 | gntJoin = new GemJoin4(address(vat), "GNT", address(gnt));
304 | pipGNT = new DSValue();
305 | dssDeploy.deployCollateral("GNT", address(gntJoin), address(pipGNT));
306 | pipGNT.poke(bytes32(uint(100 ether))); // Price 100 DAI = 1 GNT
307 | this.file(address(spotter), "GNT", "mat", uint(1500000000 ether)); // Liquidation ratio 150%
308 | this.file(address(vat), bytes32("GNT"), bytes32("line"), uint(10000 * 10 ** 45));
309 | spotter.poke("GNT");
310 | (,, spot,,) = vat.ilks("GNT");
311 | assertEq(spot, 100 * RAY * RAY / 1500000000 ether);
312 |
313 | manager = new DssCdpManager(address(vat));
314 | DSProxyFactory factory = new DSProxyFactory();
315 | registry = new ProxyRegistry(address(factory));
316 | dssProxyActions = address(new DssProxyActions());
317 | dssProxyActionsEnd = address(new DssProxyActionsEnd());
318 | dssProxyActionsDsr = address(new DssProxyActionsDsr());
319 | proxy = DSProxy(registry.build());
320 | }
321 |
322 | function ink(bytes32 ilk, address urn) public view returns (uint inkV) {
323 | (inkV,) = vat.urns(ilk, urn);
324 | }
325 |
326 | function art(bytes32 ilk, address urn) public view returns (uint artV) {
327 | (,artV) = vat.urns(ilk, urn);
328 | }
329 |
330 | function testTransfer() public {
331 | col.mint(10);
332 | col.transfer(address(proxy), 10);
333 | assertEq(col.balanceOf(address(proxy)), 10);
334 | assertEq(col.balanceOf(address(123)), 0);
335 | this.transfer(address(col), address(123), 4);
336 | assertEq(col.balanceOf(address(proxy)), 6);
337 | assertEq(col.balanceOf(address(123)), 4);
338 | }
339 |
340 | function testCreateCDP() public {
341 | uint cdp = this.open(address(manager), "ETH", address(proxy));
342 | assertEq(cdp, 1);
343 | assertEq(manager.owns(cdp), address(proxy));
344 | }
345 |
346 | function testGiveCDP() public {
347 | uint cdp = this.open(address(manager), "ETH", address(proxy));
348 | this.give(address(manager), cdp, address(123));
349 | assertEq(manager.owns(cdp), address(123));
350 | }
351 |
352 | function testGiveCDPToProxy() public {
353 | uint cdp = this.open(address(manager), "ETH", address(proxy));
354 | address userProxy = registry.build(address(123));
355 | this.giveToProxy(address(registry), address(manager), cdp, address(123));
356 | assertEq(manager.owns(cdp), userProxy);
357 | }
358 |
359 | function testGiveCDPToNewProxy() public {
360 | uint cdp = this.open(address(manager), "ETH", address(proxy));
361 | assertEq(address(registry.proxies(address(123))), address(0));
362 | this.giveToProxy(address(registry), address(manager), cdp, address(123));
363 | DSProxy userProxy = registry.proxies(address(123));
364 | assertTrue(address(userProxy) != address(0));
365 | assertEq(userProxy.owner(), address(123));
366 | assertEq(manager.owns(cdp), address(userProxy));
367 | }
368 |
369 | function testFailGiveCDPToNewContractProxy() public {
370 | uint cdp = this.open(address(manager), "ETH", address(proxy));
371 | FakeUser user = new FakeUser();
372 | assertEq(address(registry.proxies(address(user))), address(0));
373 | this.giveToProxy(address(registry), address(manager), cdp, address(user)); // Fails as user is a contract and not a regular address
374 | }
375 |
376 | function testGiveCDPAllowedUser() public {
377 | uint cdp = this.open(address(manager), "ETH", address(proxy));
378 | FakeUser user = new FakeUser();
379 | this.cdpAllow(address(manager), cdp, address(user), 1);
380 | user.doGive(manager, cdp, address(123));
381 | assertEq(manager.owns(cdp), address(123));
382 | }
383 |
384 | function testAllowUrn() public {
385 | assertEq(manager.urnCan(address(proxy), address(123)), 0);
386 | this.urnAllow(address(manager), address(123), 1);
387 | assertEq(manager.urnCan(address(proxy), address(123)), 1);
388 | this.urnAllow(address(manager), address(123), 0);
389 | assertEq(manager.urnCan(address(proxy), address(123)), 0);
390 | }
391 |
392 | function testFlux() public {
393 | uint cdp = this.open(address(manager), "ETH", address(proxy));
394 |
395 | assertEq(dai.balanceOf(address(this)), 0);
396 | realWeth.deposit.value(1 ether)();
397 | realWeth.approve(address(ethJoin), uint(-1));
398 | ethJoin.join(manager.urns(cdp), 1 ether);
399 | assertEq(vat.gem("ETH", address(this)), 0);
400 | assertEq(vat.gem("ETH", manager.urns(cdp)), 1 ether);
401 |
402 | this.flux(address(manager), cdp, address(this), 0.75 ether);
403 |
404 | assertEq(vat.gem("ETH", address(this)), 0.75 ether);
405 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0.25 ether);
406 | }
407 |
408 | function testFrob() public {
409 | uint cdp = this.open(address(manager), "ETH", address(proxy));
410 |
411 | assertEq(dai.balanceOf(address(this)), 0);
412 | realWeth.deposit.value(1 ether)();
413 | realWeth.approve(address(ethJoin), uint(-1));
414 | ethJoin.join(manager.urns(cdp), 1 ether);
415 |
416 | this.frob(address(manager), cdp, 0.5 ether, 60 ether);
417 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0.5 ether);
418 | assertEq(vat.dai(manager.urns(cdp)), mul(RAY, 60 ether));
419 | assertEq(vat.dai(address(this)), 0);
420 |
421 | this.move(address(manager), cdp, address(this), mul(RAY, 60 ether));
422 | assertEq(vat.dai(manager.urns(cdp)), 0);
423 | assertEq(vat.dai(address(this)), mul(RAY, 60 ether));
424 |
425 | vat.hope(address(daiJoin));
426 | daiJoin.exit(address(this), 60 ether);
427 | assertEq(dai.balanceOf(address(this)), 60 ether);
428 | }
429 |
430 | function testLockETH() public {
431 | uint initialBalance = address(this).balance;
432 | uint cdp = this.open(address(manager), "ETH", address(proxy));
433 | assertEq(ink("ETH", manager.urns(cdp)), 0);
434 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
435 | assertEq(ink("ETH", manager.urns(cdp)), 2 ether);
436 | assertEq(address(this).balance, initialBalance - 2 ether);
437 | }
438 |
439 | function testSafeLockETH() public {
440 | uint initialBalance = address(this).balance;
441 | uint cdp = this.open(address(manager), "ETH", address(proxy));
442 | assertEq(ink("ETH", manager.urns(cdp)), 0);
443 | this.safeLockETH.value(2 ether)(address(manager), address(ethJoin), cdp, address(proxy));
444 | assertEq(ink("ETH", manager.urns(cdp)), 2 ether);
445 | assertEq(address(this).balance, initialBalance - 2 ether);
446 | }
447 |
448 | function testLockETHOtherCDPOwner() public {
449 | uint initialBalance = address(this).balance;
450 | uint cdp = this.open(address(manager), "ETH", address(proxy));
451 | this.give(address(manager), cdp, address(123));
452 | assertEq(ink("ETH", manager.urns(cdp)), 0);
453 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
454 | assertEq(ink("ETH", manager.urns(cdp)), 2 ether);
455 | assertEq(address(this).balance, initialBalance - 2 ether);
456 | }
457 |
458 | function testFailSafeLockETHOtherCDPOwner() public {
459 | uint cdp = this.open(address(manager), "ETH", address(proxy));
460 | this.give(address(manager), cdp, address(123));
461 | this.safeLockETH.value(2 ether)(address(manager), address(ethJoin), cdp, address(321));
462 | }
463 |
464 | function testLockGem() public {
465 | col.mint(5 ether);
466 | uint cdp = this.open(address(manager), "COL", address(proxy));
467 | col.approve(address(proxy), 2 ether);
468 | assertEq(ink("COL", manager.urns(cdp)), 0);
469 | this.lockGem(address(manager), address(colJoin), cdp, 2 ether, true);
470 | assertEq(ink("COL", manager.urns(cdp)), 2 ether);
471 | assertEq(col.balanceOf(address(this)), 3 ether);
472 | }
473 |
474 | function testSafeLockGem() public {
475 | col.mint(5 ether);
476 | uint cdp = this.open(address(manager), "COL", address(proxy));
477 | col.approve(address(proxy), 2 ether);
478 | assertEq(ink("COL", manager.urns(cdp)), 0);
479 | this.safeLockGem(address(manager), address(colJoin), cdp, 2 ether, true, address(proxy));
480 | assertEq(ink("COL", manager.urns(cdp)), 2 ether);
481 | assertEq(col.balanceOf(address(this)), 3 ether);
482 | }
483 |
484 | function testLockGemDGD() public {
485 | uint cdp = this.open(address(manager), "DGD", address(proxy));
486 | dgd.approve(address(proxy), 2 * 10 ** 9);
487 | assertEq(ink("DGD", manager.urns(cdp)), 0);
488 | uint prevBalance = dgd.balanceOf(address(this));
489 | this.lockGem(address(manager), address(dgdJoin), cdp, 2 * 10 ** 9, true);
490 | assertEq(ink("DGD", manager.urns(cdp)), 2 ether);
491 | assertEq(dgd.balanceOf(address(this)), prevBalance - 2 * 10 ** 9);
492 | }
493 |
494 | function testLockGemGNT() public {
495 | uint cdp = this.open(address(manager), "GNT", address(proxy));
496 | assertEq(ink("GNT", manager.urns(cdp)), 0);
497 | uint prevBalance = gnt.balanceOf(address(this));
498 | address bag = this.makeGemBag(address(gntJoin));
499 | assertEq(gnt.balanceOf(bag), 0);
500 | gnt.transfer(bag, 2 ether);
501 | assertEq(gnt.balanceOf(address(this)), prevBalance - 2 ether);
502 | assertEq(gnt.balanceOf(bag), 2 ether);
503 | this.lockGem(address(manager), address(gntJoin), cdp, 2 ether, false);
504 | assertEq(ink("GNT", manager.urns(cdp)), 2 ether);
505 | assertEq(gnt.balanceOf(bag), 0);
506 | }
507 |
508 | function testLockGemOtherCDPOwner() public {
509 | col.mint(5 ether);
510 | uint cdp = this.open(address(manager), "COL", address(proxy));
511 | this.give(address(manager), cdp, address(123));
512 | col.approve(address(proxy), 2 ether);
513 | assertEq(ink("COL", manager.urns(cdp)), 0);
514 | this.lockGem(address(manager), address(colJoin), cdp, 2 ether, true);
515 | assertEq(ink("COL", manager.urns(cdp)), 2 ether);
516 | assertEq(col.balanceOf(address(this)), 3 ether);
517 | }
518 |
519 | function testFailSafeLockGemOtherCDPOwner() public {
520 | col.mint(5 ether);
521 | uint cdp = this.open(address(manager), "COL", address(proxy));
522 | this.give(address(manager), cdp, address(123));
523 | col.approve(address(proxy), 2 ether);
524 | this.safeLockGem(address(manager), address(colJoin), cdp, 2 ether, true, address(321));
525 | }
526 |
527 | function testFreeETH() public {
528 | uint initialBalance = address(this).balance;
529 | uint cdp = this.open(address(manager), "ETH", address(proxy));
530 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
531 | this.freeETH(address(manager), address(ethJoin), cdp, 1 ether);
532 | assertEq(ink("ETH", manager.urns(cdp)), 1 ether);
533 | assertEq(address(this).balance, initialBalance - 1 ether);
534 | }
535 |
536 | function testFreeGem() public {
537 | col.mint(5 ether);
538 | uint cdp = this.open(address(manager), "COL", address(proxy));
539 | col.approve(address(proxy), 2 ether);
540 | this.lockGem(address(manager), address(colJoin), cdp, 2 ether, true);
541 | this.freeGem(address(manager), address(colJoin), cdp, 1 ether);
542 | assertEq(ink("COL", manager.urns(cdp)), 1 ether);
543 | assertEq(col.balanceOf(address(this)), 4 ether);
544 | }
545 |
546 | function testFreeGemDGD() public {
547 | uint cdp = this.open(address(manager), "DGD", address(proxy));
548 | dgd.approve(address(proxy), 2 * 10 ** 9);
549 | assertEq(ink("DGD", manager.urns(cdp)), 0);
550 | uint prevBalance = dgd.balanceOf(address(this));
551 | this.lockGem(address(manager), address(dgdJoin), cdp, 2 * 10 ** 9, true);
552 | this.freeGem(address(manager), address(dgdJoin), cdp, 1 * 10 ** 9);
553 | assertEq(ink("DGD", manager.urns(cdp)), 1 ether);
554 | assertEq(dgd.balanceOf(address(this)), prevBalance - 1 * 10 ** 9);
555 | }
556 |
557 | function testFreeGemGNT() public {
558 | uint cdp = this.open(address(manager), "GNT", address(proxy));
559 | assertEq(ink("GNT", manager.urns(cdp)), 0);
560 | uint prevBalance = gnt.balanceOf(address(this));
561 | address bag = this.makeGemBag(address(gntJoin));
562 | gnt.transfer(bag, 2 ether);
563 | this.lockGem(address(manager), address(gntJoin), cdp, 2 ether, false);
564 | this.freeGem(address(manager), address(gntJoin), cdp, 1 ether);
565 | assertEq(ink("GNT", manager.urns(cdp)), 1 ether);
566 | assertEq(gnt.balanceOf(address(this)), prevBalance - 1 ether);
567 | }
568 |
569 | function testDraw() public {
570 | uint cdp = this.open(address(manager), "ETH", address(proxy));
571 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
572 | assertEq(dai.balanceOf(address(this)), 0);
573 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
574 | assertEq(dai.balanceOf(address(this)), 300 ether);
575 | assertEq(art("ETH", manager.urns(cdp)), 300 ether);
576 | }
577 |
578 | function testDrawAfterDrip() public {
579 | this.file(address(jug), bytes32("ETH"), bytes32("duty"), uint(1.05 * 10 ** 27));
580 | hevm.warp(now + 1);
581 | jug.drip("ETH"); // This is actually not necessary as `draw` will also call drip
582 | uint cdp = this.open(address(manager), "ETH", address(proxy));
583 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
584 | assertEq(dai.balanceOf(address(this)), 0);
585 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
586 | assertEq(dai.balanceOf(address(this)), 300 ether);
587 | assertEq(art("ETH", manager.urns(cdp)), mul(300 ether, RAY) / (1.05 * 10 ** 27) + 1); // Extra wei due rounding
588 | }
589 |
590 | function testWipe() public {
591 | uint cdp = this.open(address(manager), "ETH", address(proxy));
592 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
593 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
594 | dai.approve(address(proxy), 100 ether);
595 | this.wipe(address(manager), address(daiJoin), cdp, 100 ether);
596 | assertEq(dai.balanceOf(address(this)), 200 ether);
597 | assertEq(art("ETH", manager.urns(cdp)), 200 ether);
598 | }
599 |
600 | function testWipeAll() public {
601 | uint cdp = this.open(address(manager), "ETH", address(proxy));
602 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
603 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
604 | dai.approve(address(proxy), 300 ether);
605 | this.wipeAll(address(manager), address(daiJoin), cdp);
606 | assertEq(dai.balanceOf(address(this)), 0);
607 | assertEq(art("ETH", manager.urns(cdp)), 0);
608 | }
609 |
610 | function testSafeWipe() public {
611 | uint cdp = this.open(address(manager), "ETH", address(proxy));
612 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
613 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
614 | dai.approve(address(proxy), 100 ether);
615 | this.safeWipe(address(manager), address(daiJoin), cdp, 100 ether, address(proxy));
616 | assertEq(dai.balanceOf(address(this)), 200 ether);
617 | assertEq(art("ETH", manager.urns(cdp)), 200 ether);
618 | }
619 |
620 | function testSafeWipeAll() public {
621 | uint cdp = this.open(address(manager), "ETH", address(proxy));
622 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
623 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
624 | dai.approve(address(proxy), 300 ether);
625 | this.safeWipeAll(address(manager), address(daiJoin), cdp, address(proxy));
626 | assertEq(dai.balanceOf(address(this)), 0);
627 | assertEq(art("ETH", manager.urns(cdp)), 0);
628 | }
629 |
630 | function testWipeOtherCDPOwner() public {
631 | uint cdp = this.open(address(manager), "ETH", address(proxy));
632 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
633 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
634 | dai.approve(address(proxy), 100 ether);
635 | this.give(address(manager), cdp, address(123));
636 | this.wipe(address(manager), address(daiJoin), cdp, 100 ether);
637 | assertEq(dai.balanceOf(address(this)), 200 ether);
638 | assertEq(art("ETH", manager.urns(cdp)), 200 ether);
639 | }
640 |
641 | function testFailSafeWipeOtherCDPOwner() public {
642 | uint cdp = this.open(address(manager), "ETH", address(proxy));
643 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
644 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
645 | dai.approve(address(proxy), 100 ether);
646 | this.give(address(manager), cdp, address(123));
647 | this.safeWipe(address(manager), address(daiJoin), cdp, 100 ether, address(321));
648 | }
649 |
650 | function testFailSafeWipeAllOtherCDPOwner() public {
651 | uint cdp = this.open(address(manager), "ETH", address(proxy));
652 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
653 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
654 | dai.approve(address(proxy), 300 ether);
655 | this.give(address(manager), cdp, address(123));
656 | this.safeWipeAll(address(manager), address(daiJoin), cdp, address(321));
657 | }
658 |
659 | function testWipeAfterDrip() public {
660 | this.file(address(jug), bytes32("ETH"), bytes32("duty"), uint(1.05 * 10 ** 27));
661 | hevm.warp(now + 1);
662 | jug.drip("ETH");
663 | uint cdp = this.open(address(manager), "ETH", address(proxy));
664 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
665 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
666 | dai.approve(address(proxy), 100 ether);
667 | this.wipe(address(manager), address(daiJoin), cdp, 100 ether);
668 | assertEq(dai.balanceOf(address(this)), 200 ether);
669 | assertEq(art("ETH", manager.urns(cdp)), mul(200 ether, RAY) / (1.05 * 10 ** 27) + 1);
670 | }
671 |
672 | function testWipeAllAfterDrip() public {
673 | this.file(address(jug), bytes32("ETH"), bytes32("duty"), uint(1.05 * 10 ** 27));
674 | hevm.warp(now + 1);
675 | jug.drip("ETH");
676 | uint cdp = this.open(address(manager), "ETH", address(proxy));
677 | this.lockETH.value(2 ether)(address(manager), address(ethJoin), cdp);
678 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
679 | dai.approve(address(proxy), 300 ether);
680 | this.wipe(address(manager), address(daiJoin), cdp, 300 ether);
681 | assertEq(art("ETH", manager.urns(cdp)), 0);
682 | }
683 |
684 | function testWipeAllAfterDrip2() public {
685 | this.file(address(jug), bytes32("ETH"), bytes32("duty"), uint(1.05 * 10 ** 27));
686 | hevm.warp(now + 1);
687 | jug.drip("ETH"); // This is actually not necessary as `draw` will also call drip
688 | uint cdp = this.open(address(manager), "ETH", address(proxy));
689 | uint times = 30;
690 | this.lockETH.value(2 ether * times)(address(manager), address(ethJoin), cdp);
691 | for (uint i = 0; i < times; i++) {
692 | this.draw(address(manager), address(jug), address(daiJoin), cdp, 300 ether);
693 | }
694 | dai.approve(address(proxy), 300 ether * times);
695 | this.wipe(address(manager), address(daiJoin), cdp, 300 ether * times);
696 | assertEq(art("ETH", manager.urns(cdp)), 0);
697 | }
698 |
699 | function testLockETHAndDraw() public {
700 | uint cdp = this.open(address(manager), "ETH", address(proxy));
701 | uint initialBalance = address(this).balance;
702 | assertEq(ink("ETH", manager.urns(cdp)), 0);
703 | assertEq(dai.balanceOf(address(this)), 0);
704 | this.lockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 300 ether);
705 | assertEq(ink("ETH", manager.urns(cdp)), 2 ether);
706 | assertEq(dai.balanceOf(address(this)), 300 ether);
707 | assertEq(address(this).balance, initialBalance - 2 ether);
708 | }
709 |
710 | function testOpenLockETHAndDraw() public {
711 | uint initialBalance = address(this).balance;
712 | assertEq(dai.balanceOf(address(this)), 0);
713 | uint cdp = this.openLockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), "ETH", 300 ether);
714 | assertEq(ink("ETH", manager.urns(cdp)), 2 ether);
715 | assertEq(dai.balanceOf(address(this)), 300 ether);
716 | assertEq(address(this).balance, initialBalance - 2 ether);
717 | }
718 |
719 | function testLockGemAndDraw() public {
720 | col.mint(5 ether);
721 | uint cdp = this.open(address(manager), "COL", address(proxy));
722 | col.approve(address(proxy), 2 ether);
723 | assertEq(ink("COL", manager.urns(cdp)), 0);
724 | assertEq(dai.balanceOf(address(this)), 0);
725 | this.lockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), cdp, 2 ether, 10 ether, true);
726 | assertEq(ink("COL", manager.urns(cdp)), 2 ether);
727 | assertEq(dai.balanceOf(address(this)), 10 ether);
728 | assertEq(col.balanceOf(address(this)), 3 ether);
729 | }
730 |
731 | function testLockGemDGDAndDraw() public {
732 | uint cdp = this.open(address(manager), "DGD", address(proxy));
733 | dgd.approve(address(proxy), 3 * 10 ** 9);
734 | assertEq(ink("DGD", manager.urns(cdp)), 0);
735 | uint prevBalance = dgd.balanceOf(address(this));
736 | this.lockGemAndDraw(address(manager), address(jug), address(dgdJoin), address(daiJoin), cdp, 3 * 10 ** 9, 50 ether, true);
737 | assertEq(ink("DGD", manager.urns(cdp)), 3 ether);
738 | assertEq(dai.balanceOf(address(this)), 50 ether);
739 | assertEq(dgd.balanceOf(address(this)), prevBalance - 3 * 10 ** 9);
740 | }
741 |
742 | function testLockGemGNTAndDraw() public {
743 | uint cdp = this.open(address(manager), "GNT", address(proxy));
744 | assertEq(ink("GNT", manager.urns(cdp)), 0);
745 | uint prevBalance = gnt.balanceOf(address(this));
746 | address bag = this.makeGemBag(address(gntJoin));
747 | gnt.transfer(bag, 3 ether);
748 | this.lockGemAndDraw(address(manager), address(jug), address(gntJoin), address(daiJoin), cdp, 3 ether, 50 ether, false);
749 | assertEq(ink("GNT", manager.urns(cdp)), 3 ether);
750 | assertEq(dai.balanceOf(address(this)), 50 ether);
751 | assertEq(gnt.balanceOf(address(this)), prevBalance - 3 ether);
752 | }
753 |
754 | function testOpenLockGemAndDraw() public {
755 | col.mint(5 ether);
756 | col.approve(address(proxy), 2 ether);
757 | assertEq(dai.balanceOf(address(this)), 0);
758 | uint cdp = this.openLockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), "COL", 2 ether, 10 ether, true);
759 | assertEq(ink("COL", manager.urns(cdp)), 2 ether);
760 | assertEq(dai.balanceOf(address(this)), 10 ether);
761 | assertEq(col.balanceOf(address(this)), 3 ether);
762 | }
763 |
764 | function testOpenLockGemGNTAndDraw() public {
765 | assertEq(dai.balanceOf(address(this)), 0);
766 | address bag = this.makeGemBag(address(gntJoin));
767 | assertEq(address(bag), gntJoin.bags(address(proxy)));
768 | gnt.transfer(bag, 2 ether);
769 | uint cdp = this.openLockGemAndDraw(address(manager), address(jug), address(gntJoin), address(daiJoin), "GNT", 2 ether, 10 ether, false);
770 | assertEq(ink("GNT", manager.urns(cdp)), 2 ether);
771 | assertEq(dai.balanceOf(address(this)), 10 ether);
772 | }
773 |
774 | function testOpenLockGemGNTAndDrawSafe() public {
775 | assertEq(dai.balanceOf(address(this)), 0);
776 | gnt.transfer(address(proxy), 2 ether);
777 | (address bag, uint cdp) = this.openLockGNTAndDraw(address(manager), address(jug), address(gntJoin), address(daiJoin), "GNT", 2 ether, 10 ether);
778 | assertEq(address(bag), gntJoin.bags(address(proxy)));
779 | assertEq(ink("GNT", manager.urns(cdp)), 2 ether);
780 | assertEq(dai.balanceOf(address(this)), 10 ether);
781 | }
782 |
783 | function testOpenLockGemGNTAndDrawSafeTwice() public {
784 | assertEq(dai.balanceOf(address(this)), 0);
785 | gnt.transfer(address(proxy), 4 ether);
786 | (address bag, uint cdp) = this.openLockGNTAndDraw(address(manager), address(jug), address(gntJoin), address(daiJoin), "GNT", 2 ether, 10 ether);
787 | (address bag2, uint cdp2) = this.openLockGNTAndDraw(address(manager), address(jug), address(gntJoin), address(daiJoin), "GNT", 2 ether, 10 ether);
788 | assertEq(address(bag), gntJoin.bags(address(proxy)));
789 | assertEq(address(bag), address(bag2));
790 | assertEq(ink("GNT", manager.urns(cdp)), 2 ether);
791 | assertEq(ink("GNT", manager.urns(cdp2)), 2 ether);
792 | assertEq(dai.balanceOf(address(this)), 20 ether);
793 | }
794 |
795 | function testWipeAndFreeETH() public {
796 | uint cdp = this.open(address(manager), "ETH", address(proxy));
797 | uint initialBalance = address(this).balance;
798 | this.lockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 300 ether);
799 | dai.approve(address(proxy), 250 ether);
800 | this.wipeAndFreeETH(address(manager), address(ethJoin), address(daiJoin), cdp, 1.5 ether, 250 ether);
801 | assertEq(ink("ETH", manager.urns(cdp)), 0.5 ether);
802 | assertEq(art("ETH", manager.urns(cdp)), 50 ether);
803 | assertEq(dai.balanceOf(address(this)), 50 ether);
804 | assertEq(address(this).balance, initialBalance - 0.5 ether);
805 | }
806 |
807 | function testWipeAllAndFreeETH() public {
808 | uint cdp = this.open(address(manager), "ETH", address(proxy));
809 | uint initialBalance = address(this).balance;
810 | this.lockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 300 ether);
811 | dai.approve(address(proxy), 300 ether);
812 | this.wipeAllAndFreeETH(address(manager), address(ethJoin), address(daiJoin), cdp, 1.5 ether);
813 | assertEq(ink("ETH", manager.urns(cdp)), 0.5 ether);
814 | assertEq(art("ETH", manager.urns(cdp)), 0);
815 | assertEq(dai.balanceOf(address(this)), 0);
816 | assertEq(address(this).balance, initialBalance - 0.5 ether);
817 | }
818 |
819 | function testWipeAndFreeGem() public {
820 | col.mint(5 ether);
821 | uint cdp = this.open(address(manager), "COL", address(proxy));
822 | col.approve(address(proxy), 2 ether);
823 | this.lockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), cdp, 2 ether, 10 ether, true);
824 | dai.approve(address(proxy), 8 ether);
825 | this.wipeAndFreeGem(address(manager), address(colJoin), address(daiJoin), cdp, 1.5 ether, 8 ether);
826 | assertEq(ink("COL", manager.urns(cdp)), 0.5 ether);
827 | assertEq(art("COL", manager.urns(cdp)), 2 ether);
828 | assertEq(dai.balanceOf(address(this)), 2 ether);
829 | assertEq(col.balanceOf(address(this)), 4.5 ether);
830 | }
831 |
832 | function testWipeAllAndFreeGem() public {
833 | col.mint(5 ether);
834 | uint cdp = this.open(address(manager), "COL", address(proxy));
835 | col.approve(address(proxy), 2 ether);
836 | this.lockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), cdp, 2 ether, 10 ether, true);
837 | dai.approve(address(proxy), 10 ether);
838 | this.wipeAllAndFreeGem(address(manager), address(colJoin), address(daiJoin), cdp, 1.5 ether);
839 | assertEq(ink("COL", manager.urns(cdp)), 0.5 ether);
840 | assertEq(art("COL", manager.urns(cdp)), 0);
841 | assertEq(dai.balanceOf(address(this)), 0);
842 | assertEq(col.balanceOf(address(this)), 4.5 ether);
843 | }
844 |
845 | function testWipeAndFreeGemDGDAndDraw() public {
846 | uint cdp = this.open(address(manager), "DGD", address(proxy));
847 | dgd.approve(address(proxy), 3 * 10 ** 9);
848 | assertEq(ink("DGD", manager.urns(cdp)), 0);
849 | uint prevBalance = dgd.balanceOf(address(this));
850 | this.lockGemAndDraw(address(manager), address(jug), address(dgdJoin), address(daiJoin), cdp, 3 * 10 ** 9, 50 ether, true);
851 | dai.approve(address(proxy), 25 ether);
852 | this.wipeAndFreeGem(address(manager), address(dgdJoin), address(daiJoin), cdp, 1 * 10 ** 9, 25 ether);
853 | assertEq(ink("DGD", manager.urns(cdp)), 2 ether);
854 | assertEq(dai.balanceOf(address(this)), 25 ether);
855 | assertEq(dgd.balanceOf(address(this)), prevBalance - 2 * 10 ** 9);
856 | }
857 |
858 | function testPreventHigherDaiOnWipe() public {
859 | uint cdp = this.open(address(manager), "ETH", address(proxy));
860 | this.lockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 300 ether);
861 |
862 | realWeth.deposit.value(2 ether)();
863 | realWeth.approve(address(ethJoin), 2 ether);
864 | ethJoin.join(address(this), 2 ether);
865 | vat.frob("ETH", address(this), address(this), address(this), 1 ether, 150 ether);
866 | vat.move(address(this), manager.urns(cdp), 150 ether);
867 |
868 | dai.approve(address(proxy), 300 ether);
869 | this.wipe(address(manager), address(daiJoin), cdp, 300 ether);
870 | }
871 |
872 | function testHopeNope() public {
873 | assertEq(vat.can(address(proxy), address(123)), 0);
874 | this.hope(address(vat), address(123));
875 | assertEq(vat.can(address(proxy), address(123)), 1);
876 | this.nope(address(vat), address(123));
877 | assertEq(vat.can(address(proxy), address(123)), 0);
878 | }
879 |
880 | function testQuit() public {
881 | uint cdp = this.open(address(manager), "ETH", address(proxy));
882 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 50 ether);
883 |
884 | assertEq(ink("ETH", manager.urns(cdp)), 1 ether);
885 | assertEq(art("ETH", manager.urns(cdp)), 50 ether);
886 | assertEq(ink("ETH", address(proxy)), 0);
887 | assertEq(art("ETH", address(proxy)), 0);
888 |
889 | this.hope(address(vat), address(manager));
890 | this.quit(address(manager), cdp, address(proxy));
891 |
892 | assertEq(ink("ETH", manager.urns(cdp)), 0);
893 | assertEq(art("ETH", manager.urns(cdp)), 0);
894 | assertEq(ink("ETH", address(proxy)), 1 ether);
895 | assertEq(art("ETH", address(proxy)), 50 ether);
896 | }
897 |
898 | function testEnter() public {
899 | realWeth.deposit.value(1 ether)();
900 | realWeth.approve(address(ethJoin), 1 ether);
901 | ethJoin.join(address(this), 1 ether);
902 | vat.frob("ETH", address(this), address(this), address(this), 1 ether, 50 ether);
903 | uint cdp = this.open(address(manager), "ETH", address(proxy));
904 |
905 | assertEq(ink("ETH", manager.urns(cdp)), 0);
906 | assertEq(art("ETH", manager.urns(cdp)), 0);
907 | assertEq(ink("ETH", address(this)), 1 ether);
908 | assertEq(art("ETH", address(this)), 50 ether);
909 |
910 | vat.hope(address(manager));
911 | manager.urnAllow(address(proxy), 1);
912 | this.enter(address(manager), address(this), cdp);
913 |
914 | assertEq(ink("ETH", manager.urns(cdp)), 1 ether);
915 | assertEq(art("ETH", manager.urns(cdp)), 50 ether);
916 | assertEq(ink("ETH", address(this)), 0);
917 | assertEq(art("ETH", address(this)), 0);
918 | }
919 |
920 | function testShift() public {
921 | uint cdpSrc = this.open(address(manager), "ETH", address(proxy));
922 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdpSrc, 50 ether);
923 |
924 | uint cdpDst = this.open(address(manager), "ETH", address(proxy));
925 |
926 | assertEq(ink("ETH", manager.urns(cdpSrc)), 1 ether);
927 | assertEq(art("ETH", manager.urns(cdpSrc)), 50 ether);
928 | assertEq(ink("ETH", manager.urns(cdpDst)), 0);
929 | assertEq(art("ETH", manager.urns(cdpDst)), 0);
930 |
931 | this.shift(address(manager), cdpSrc, cdpDst);
932 |
933 | assertEq(ink("ETH", manager.urns(cdpSrc)), 0);
934 | assertEq(art("ETH", manager.urns(cdpSrc)), 0);
935 | assertEq(ink("ETH", manager.urns(cdpDst)), 1 ether);
936 | assertEq(art("ETH", manager.urns(cdpDst)), 50 ether);
937 | }
938 |
939 | function _flipETH() internal returns (uint cdp) {
940 | this.file(address(cat), "ETH", "dunk", rad(200 ether)); // 200 units of DAI per batch
941 | this.file(address(cat), "box", rad(1000 ether)); // 1000 units of DAI max
942 | this.file(address(cat), "ETH", "chop", WAD);
943 |
944 | cdp = this.open(address(manager), "ETH", address(proxy));
945 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 200 ether); // Maximun DAI generated
946 | pipETH.poke(bytes32(uint(300 * 10 ** 18 - 1))); // Force liquidation
947 | spotter.poke("ETH");
948 | uint batchId = cat.bite("ETH", manager.urns(cdp));
949 |
950 | realWeth.deposit.value(10 ether)();
951 | realWeth.transfer(address(user1), 10 ether);
952 | user1.doWethJoin(address(realWeth), address(ethJoin), address(user1), 10 ether);
953 | user1.doFrob(address(vat), "ETH", address(user1), address(user1), address(user1), 10 ether, 1000 ether);
954 |
955 | realWeth.deposit.value(10 ether)();
956 | realWeth.transfer(address(user2), 10 ether);
957 | user2.doWethJoin(address(realWeth), address(ethJoin), address(user2), 10 ether);
958 | user2.doFrob(address(vat), "ETH", address(user2), address(user2), address(user2), 10 ether, 1000 ether);
959 |
960 | user1.doHope(address(vat), address(ethFlip));
961 | user2.doHope(address(vat), address(ethFlip));
962 |
963 | user1.doTend(address(ethFlip), batchId, 1 ether, rad(200 ether));
964 |
965 | user2.doDent(address(ethFlip), batchId, 0.7 ether, rad(200 ether));
966 | }
967 |
968 | function testExitETHAfterFlip() public {
969 | uint cdp = _flipETH();
970 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0.3 ether);
971 | uint prevBalance = address(this).balance;
972 | this.exitETH(address(manager), address(ethJoin), cdp, 0.3 ether);
973 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0);
974 | assertEq(address(this).balance, prevBalance + 0.3 ether);
975 | }
976 |
977 | function testExitGemAfterFlip() public {
978 | this.file(address(cat), "COL", "dunk", rad(40 ether)); // 100 units of DAI per batch
979 | this.file(address(cat), "box", rad(1000 ether)); // 1000 units of DAI max
980 | this.file(address(cat), "COL", "chop", WAD);
981 |
982 | col.mint(1 ether);
983 | uint cdp = this.open(address(manager), "COL", address(proxy));
984 | col.approve(address(proxy), 1 ether);
985 | this.lockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), cdp, 1 ether, 40 ether, true);
986 |
987 | pipCOL.poke(bytes32(uint(40 * 10 ** 18))); // Force liquidation
988 | spotter.poke("COL");
989 | uint batchId = cat.bite("COL", manager.urns(cdp));
990 |
991 | realWeth.deposit.value(10 ether)();
992 | realWeth.transfer(address(user1), 10 ether);
993 | user1.doWethJoin(address(realWeth), address(ethJoin), address(user1), 10 ether);
994 | user1.doFrob(address(vat), "ETH", address(user1), address(user1), address(user1), 10 ether, 1000 ether);
995 |
996 | realWeth.deposit.value(10 ether)();
997 | realWeth.transfer(address(user2), 10 ether);
998 | user2.doWethJoin(address(realWeth), address(ethJoin), address(user2), 10 ether);
999 | user2.doFrob(address(vat), "ETH", address(user2), address(user2), address(user2), 10 ether, 1000 ether);
1000 |
1001 | user1.doHope(address(vat), address(colFlip));
1002 | user2.doHope(address(vat), address(colFlip));
1003 |
1004 | user1.doTend(address(colFlip), batchId, 1 ether, rad(40 ether));
1005 |
1006 | user2.doDent(address(colFlip), batchId, 0.7 ether, rad(40 ether));
1007 | assertEq(vat.gem("COL", manager.urns(cdp)), 0.3 ether);
1008 | assertEq(col.balanceOf(address(this)), 0);
1009 | this.exitGem(address(manager), address(colJoin), cdp, 0.3 ether);
1010 | assertEq(vat.gem("COL", manager.urns(cdp)), 0);
1011 | assertEq(col.balanceOf(address(this)), 0.3 ether);
1012 | }
1013 |
1014 | function testExitDGDAfterFlip() public {
1015 | this.file(address(cat), "DGD", "dunk", rad(30 ether)); // 30 units of DAI per batch
1016 | this.file(address(cat), "box", rad(1000 ether)); // 1000 units of DAI max
1017 | this.file(address(cat), "DGD", "chop", WAD);
1018 |
1019 | uint cdp = this.open(address(manager), "DGD", address(proxy));
1020 | dgd.approve(address(proxy), 1 * 10 ** 9);
1021 | this.lockGemAndDraw(address(manager), address(jug), address(dgdJoin), address(daiJoin), cdp, 1 * 10 ** 9, 30 ether, true);
1022 |
1023 | pipDGD.poke(bytes32(uint(40 * 10 ** 18))); // Force liquidation
1024 | spotter.poke("DGD");
1025 | uint batchId = cat.bite("DGD", manager.urns(cdp));
1026 |
1027 | realWeth.deposit.value(10 ether)();
1028 | realWeth.transfer(address(user1), 10 ether);
1029 | user1.doWethJoin(address(realWeth), address(ethJoin), address(user1), 10 ether);
1030 | user1.doFrob(address(vat), "ETH", address(user1), address(user1), address(user1), 10 ether, 1000 ether);
1031 |
1032 | realWeth.deposit.value(10 ether)();
1033 | realWeth.transfer(address(user2), 10 ether);
1034 | user2.doWethJoin(address(realWeth), address(ethJoin), address(user2), 10 ether);
1035 | user2.doFrob(address(vat), "ETH", address(user2), address(user2), address(user2), 10 ether, 1000 ether);
1036 |
1037 | user1.doHope(address(vat), address(dgdFlip));
1038 | user2.doHope(address(vat), address(dgdFlip));
1039 |
1040 | user1.doTend(address(dgdFlip), batchId, 1 ether, rad(30 ether));
1041 |
1042 | user2.doDent(address(dgdFlip), batchId, 0.7 ether, rad(30 ether));
1043 | assertEq(vat.gem("DGD", manager.urns(cdp)), 0.3 ether);
1044 | uint prevBalance = dgd.balanceOf(address(this));
1045 | this.exitGem(address(manager), address(dgdJoin), cdp, 0.3 * 10 ** 9);
1046 | assertEq(vat.gem("DGD", manager.urns(cdp)), 0);
1047 | assertEq(dgd.balanceOf(address(this)), prevBalance + 0.3 * 10 ** 9);
1048 | }
1049 |
1050 | function testLockBackAfterFlip() public {
1051 | uint cdp = _flipETH();
1052 | (uint inkV,) = vat.urns("ETH", manager.urns(cdp));
1053 | assertEq(inkV, 0);
1054 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0.3 ether);
1055 | this.frob(address(manager), cdp, 0.3 ether, 0);
1056 | (inkV,) = vat.urns("ETH", manager.urns(cdp));
1057 | assertEq(inkV, 0.3 ether);
1058 | assertEq(vat.gem("ETH", manager.urns(cdp)), 0);
1059 | }
1060 |
1061 | function testEnd() public {
1062 | uint cdp = this.openLockETHAndDraw.value(2 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), "ETH", 300 ether);
1063 | col.mint(1 ether);
1064 | col.approve(address(proxy), 1 ether);
1065 | uint cdp2 = this.openLockGemAndDraw(address(manager), address(jug), address(colJoin), address(daiJoin), "COL", 1 ether, 5 ether, true);
1066 | dgd.approve(address(proxy), 1 * 10 ** 9);
1067 | uint cdp3 = this.openLockGemAndDraw(address(manager), address(jug), address(dgdJoin), address(daiJoin), "DGD", 1 * 10 ** 9, 5 ether, true);
1068 |
1069 | this.cage(address(end));
1070 | end.cage("ETH");
1071 | end.cage("COL");
1072 | end.cage("DGD");
1073 |
1074 | (uint inkV, uint artV) = vat.urns("ETH", manager.urns(cdp));
1075 | assertEq(inkV, 2 ether);
1076 | assertEq(artV, 300 ether);
1077 |
1078 | (inkV, artV) = vat.urns("COL", manager.urns(cdp2));
1079 | assertEq(inkV, 1 ether);
1080 | assertEq(artV, 5 ether);
1081 |
1082 | (inkV, artV) = vat.urns("DGD", manager.urns(cdp3));
1083 | assertEq(inkV, 1 ether);
1084 | assertEq(artV, 5 ether);
1085 |
1086 | uint prevBalanceETH = address(this).balance;
1087 | this.end_freeETH(address(manager), address(ethJoin), address(end), cdp);
1088 | (inkV, artV) = vat.urns("ETH", manager.urns(cdp));
1089 | assertEq(inkV, 0);
1090 | assertEq(artV, 0);
1091 | uint remainInkVal = 2 ether - 300 * end.tag("ETH") / 10 ** 9; // 2 ETH (deposited) - 300 DAI debt * ETH cage price
1092 | assertEq(address(this).balance, prevBalanceETH + remainInkVal);
1093 |
1094 | uint prevBalanceCol = col.balanceOf(address(this));
1095 | this.end_freeGem(address(manager), address(colJoin), address(end), cdp2);
1096 | (inkV, artV) = vat.urns("COL", manager.urns(cdp2));
1097 | assertEq(inkV, 0);
1098 | assertEq(artV, 0);
1099 | remainInkVal = 1 ether - 5 * end.tag("COL") / 10 ** 9; // 1 COL (deposited) - 5 DAI debt * COL cage price
1100 | assertEq(col.balanceOf(address(this)), prevBalanceCol + remainInkVal);
1101 |
1102 | uint prevBalanceDGD = dgd.balanceOf(address(this));
1103 | this.end_freeGem(address(manager), address(dgdJoin), address(end), cdp3);
1104 | (inkV, artV) = vat.urns("DGD", manager.urns(cdp3));
1105 | assertEq(inkV, 0);
1106 | assertEq(artV, 0);
1107 | remainInkVal = (1 ether - 5 * end.tag("DGD") / 10 ** 9) / 10 ** 9; // 1 DGD (deposited) - 5 DAI debt * DGD cage price
1108 | assertEq(dgd.balanceOf(address(this)), prevBalanceDGD + remainInkVal);
1109 |
1110 | end.thaw();
1111 |
1112 | end.flow("ETH");
1113 | end.flow("COL");
1114 | end.flow("DGD");
1115 |
1116 | dai.approve(address(proxy), 310 ether);
1117 | this.end_pack(address(daiJoin), address(end), 310 ether);
1118 |
1119 | this.end_cashETH(address(ethJoin), address(end), "ETH", 310 ether);
1120 | this.end_cashGem(address(colJoin), address(end), "COL", 310 ether);
1121 | this.end_cashGem(address(dgdJoin), address(end), "DGD", 310 ether);
1122 |
1123 | assertEq(address(this).balance, prevBalanceETH + 2 ether - 1); // (-1 rounding)
1124 | assertEq(col.balanceOf(address(this)), prevBalanceCol + 1 ether - 1); // (-1 rounding)
1125 | assertEq(dgd.balanceOf(address(this)), prevBalanceDGD + 1 * 10 ** 9 - 1); // (-1 rounding)
1126 | }
1127 |
1128 | function testDSRSimpleCase() public {
1129 | this.file(address(pot), "dsr", uint(1.05 * 10 ** 27)); // 5% per second
1130 | uint initialTime = 0; // Initial time set to 0 to avoid any intial rounding
1131 | hevm.warp(initialTime);
1132 | uint cdp = this.open(address(manager), "ETH", address(proxy));
1133 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 50 ether);
1134 | dai.approve(address(proxy), 50 ether);
1135 | assertEq(dai.balanceOf(address(this)), 50 ether);
1136 | assertEq(pot.pie(address(this)), 0 ether);
1137 | this.nope(address(vat), address(daiJoin)); // Remove vat permission for daiJoin to test it is correctly re-activate in exit
1138 | this.dsr_join(address(daiJoin), address(pot), 50 ether);
1139 | assertEq(dai.balanceOf(address(this)), 0 ether);
1140 | assertEq(pot.pie(address(proxy)) * pot.chi(), 50 ether * RAY);
1141 | hevm.warp(initialTime + 1); // Moved 1 second
1142 | pot.drip();
1143 | assertEq(pot.pie(address(proxy)) * pot.chi(), 52.5 ether * RAY); // Now the equivalent DAI amount is 2.5 DAI extra
1144 | this.dsr_exit(address(daiJoin), address(pot), 52.5 ether);
1145 | assertEq(dai.balanceOf(address(this)), 52.5 ether);
1146 | assertEq(pot.pie(address(proxy)), 0);
1147 | }
1148 |
1149 | function testDSRRounding() public {
1150 | this.file(address(pot), "dsr", uint(1.05 * 10 ** 27));
1151 | uint initialTime = 1; // Initial time set to 1 this way some the pie will not be the same than the initial DAI wad amount
1152 | hevm.warp(initialTime);
1153 | uint cdp = this.open(address(manager), "ETH", address(proxy));
1154 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 50 ether);
1155 | dai.approve(address(proxy), 50 ether);
1156 | assertEq(dai.balanceOf(address(this)), 50 ether);
1157 | assertEq(pot.pie(address(this)), 0 ether);
1158 | this.nope(address(vat), address(daiJoin)); // Remove vat permission for daiJoin to test it is correctly re-activate in exit
1159 | this.dsr_join(address(daiJoin), address(pot), 50 ether);
1160 | assertEq(dai.balanceOf(address(this)), 0 ether);
1161 | // Due rounding the DAI equivalent is not the same than initial wad amount
1162 | assertEq(pot.pie(address(proxy)) * pot.chi(), 49999999999999999999350000000000000000000000000);
1163 | hevm.warp(initialTime + 1);
1164 | pot.drip(); // Just necessary to check in this test the updated value of chi
1165 | assertEq(pot.pie(address(proxy)) * pot.chi(), 52499999999999999999317500000000000000000000000);
1166 | this.dsr_exit(address(daiJoin), address(pot), 52.5 ether);
1167 | assertEq(dai.balanceOf(address(this)), 52499999999999999999);
1168 | assertEq(pot.pie(address(proxy)), 0);
1169 | }
1170 |
1171 | function testDSRRounding2() public {
1172 | this.file(address(pot), "dsr", uint(1.03434234324 * 10 ** 27));
1173 | uint initialTime = 1;
1174 | hevm.warp(initialTime);
1175 | uint cdp = this.open(address(manager), "ETH", address(proxy));
1176 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 50 ether);
1177 | dai.approve(address(proxy), 50 ether);
1178 | assertEq(dai.balanceOf(address(this)), 50 ether);
1179 | assertEq(pot.pie(address(this)), 0 ether);
1180 | this.nope(address(vat), address(daiJoin)); // Remove vat permission for daiJoin to test it is correctly re-activate in exit
1181 | this.dsr_join(address(daiJoin), address(pot), 50 ether);
1182 | assertEq(pot.pie(address(proxy)) * pot.chi(), 49999999999999999999993075745400000000000000000);
1183 | assertEq(vat.dai(address(proxy)), mul(50 ether, RAY) - 49999999999999999999993075745400000000000000000);
1184 | this.dsr_exit(address(daiJoin), address(pot), 50 ether);
1185 | // In this case we get the full 50 DAI back as we also use (for the exit) the dust that remained in the proxy DAI balance in the vat
1186 | // The proxy function tries to return the wad amount if there is enough balance to do it
1187 | assertEq(dai.balanceOf(address(this)), 50 ether);
1188 | }
1189 |
1190 | function testDSRExitAll() public {
1191 | this.file(address(pot), "dsr", uint(1.03434234324 * 10 ** 27));
1192 | uint initialTime = 1;
1193 | hevm.warp(initialTime);
1194 | uint cdp = this.open(address(manager), "ETH", address(proxy));
1195 | this.lockETHAndDraw.value(1 ether)(address(manager), address(jug), address(ethJoin), address(daiJoin), cdp, 50 ether);
1196 | this.nope(address(vat), address(daiJoin)); // Remove vat permission for daiJoin to test it is correctly re-activate in exitAll
1197 | dai.approve(address(proxy), 50 ether);
1198 | this.dsr_join(address(daiJoin), address(pot), 50 ether);
1199 | this.dsr_exitAll(address(daiJoin), address(pot));
1200 | // In this case we get 49.999 DAI back as the returned amount is based purely in the pie amount
1201 | assertEq(dai.balanceOf(address(this)), 49999999999999999999);
1202 | }
1203 |
1204 | function () external payable {}
1205 | }
1206 |
--------------------------------------------------------------------------------