<style>
@import url(https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap);@import url('https://fonts.googleapis.com/css2?family=Source Code Pro:wght@400;500;600;700&display=swap');ul{opacity:1;}table{border:none !important;}a,code,pre,tt{overflow-wrap:break-word;word-wrap:break-word}.chroma .lntable,.chroma .lntd{margin:0;border:0;padding:0}pre,table{width:100%}.findings-count td,.findings-count th,.rating-critical,.rating-high,.rating-informational,.rating-low,.rating-medium,.status-closed,.status-open,.status-resolved{text-align:center}.rating-critical,.rating-high,.rating-informational,.rating-low,.rating-medium,.status-closed,.status-open,.status-resolved,a,h1,h2,h3{font-weight:500}:root{--color-link:#f66263;--color-heading:#f66263;--color-codeblock:#f5f2f0;--color-codeblock-border:#f5f2f0;--color-critical:#cc7ab9;--color-high:#ff6263;--color-medium:#f68463;--color-low:#f6b263;--color-informational:#6ba2f6;--color-open:#f6d263;--color-closed:#b6b6b6;--color-resolved:#9fbf9f;--size-200:0.694rem;--size-300:0.833rem;--size-400:1rem;--size-500:1.2rem;--size-600:1.44rem;--size-700:1.728rem;--size-800:2.074rem;--size-900:2.488rem}.report-container{color:#222;font-family:Montserrat,sans-serif;line-height:1.6;margin:1rem;font-size:var(--size-400)}a{color:var(--color-link)}@media screen and (min-width:1600px){.report-main{display:grid;grid-gap:1em}.toc{grid-column:1;max-width:30em}.frontpage-logo,.frontpage-subtitle,.frontpage-title,.report{grid-column:2}}@media screen and (max-width:1599px){ .report-main{ margin: 0 auto;max-width:70em} }h1,h2,h3,h4,h5,h6{margin-bottom:0}.toc>ul>li>a,dt,h4,h5,h6,table th{font-weight:600}h1,h2{color:var(--color-heading)}h3,h4,h5,h6{color:#444 !important}h1{font-size:var(--size-800)}h2{font-size:var(--size-700)}h3{font-size:var(--size-600) } .audit-text-formatting .report-main h3{font-size:var(--size-600) }h4{font-size:var(--size-500)}h1 strong,h2 strong,h3 strong,h4 strong{font-weight:400;font-size:.7em;font-family:"Source Code Pro",monospace}h2 strong::after{content:"\a";white-space:pre}code,pre,tt{font-family:"Source Code Pro",monospace,sans-serif;background-color:var(--color-codeblock);white-space:pre-wrap}code,tt{padding:1px 3px;border-radius:2px}pre{box-sizing:border-box;padding:10px;overflow:auto;word-break:break-all}pre code,tt{font-size:inherit;background:0 0;border:none;padding:0}.findings code,h2 code{background-color:inherit;border-width:0}@media screen and (min-width:600px){dl{display:grid;grid-gap:0.5em}dt{grid-column:1}dd{grid-column:2}}@media screen and (max-width:599px){dl{display:block}}table{background-color:inherit;max-width:100%;min-width:100%;border:none;font-size:.9em}table thead th{border-bottom:2px solid #222}table td,table th{text-align:left;border:none}table,td,th{border-collapse:collapse}.findings-count thead tr th:first-of-type{border-style:none}.findings-count tbody td:first-child{text-align:right;width:6em;padding-right:.75em;border-right:2px solid #222;font-weight:600}.findings-count td:nth-child(2),.rating-critical{background-color:var(--color-critical)}.findings-count td:nth-child(3),.rating-high{background-color:var(--color-high)}.findings-count td:nth-child(4),.rating-medium{background-color:var(--color-medium)}.findings-count td:nth-child(5),.rating-low{background-color:var(--color-low)}.findings-count td:nth-child(6),.rating-informational{background-color:var(--color-informational)}.status-open{background-color:var(--color-open)}.status-closed{background-color:var(--color-closed)}.status-resolved{background-color:var(--color-resolved)}.findings td:first-of-type{white-space:nowrap;word-break:keep-all;font-family:"Source Code Pro",monospace;vertical-align:top}.findings td:nth-of-type(2){vertical-align:bottom}@media screen{.audit-header,.report{max-width:65rem}h1{margin-top:3em}h2{margin-top:2em}table th{padding:6px}table td{padding:8px 6px}.findings td:nth-of-type(2){min-width:2em}}.metadata td:last-of-type{background-color:#eee}.metadata td:first-of-type,.metadata td:nth-of-type(2){width:8em}.toc ul{list-style:none;margin-left:0;padding-left:0}.toc li ul{margin-left:3em}.toc{counter-reset:tocSectionCounter}.toc>ul>li::before{content:counter(tocSectionCounter) ". ";font-weight:600;padding-right:4px}.toc>ul>li{counter-increment:tocSectionCounter}.report{counter-reset:sectionCounter}.report h1::before{content:counter(sectionCounter) ". ";font-weight:400;padding-right:6px}.report h1{counter-increment:sectionCounter}hr{display:none}@media print{.landing-header{display:none}.toc li a,h1,h2{color:#222}dd,dt{margin:.2em 0;break-inside:avoid}dl,pre{page-break-inside:auto;break-inside:auto}dd,dt,pre{padding:0}.toc li a,.toc li a::after,pre{background-color:#fff}td{min-width:2em}pre,td{word-break:break-word}h1,h2{margin-top:0}#document-control,.break-before,h1,hr{page-break-before:always}h1{font-size:18pt}h2{font-size:16pt}h3{font-size:14pt}.frontpage-subtitle,h4{font-size:12pt}.toc,p,ul,ol,dl{font-size:11pt}summary{list-style:none;font-size:1.1em;margin-bottom:1em}.toc{background:0 0;max-width:100%;counter-reset:page;line-height:1.6}.toc li a::after{content:target-counter(attr(href),page);float:right;position:absolute;right:0;padding-left:3px}.toc li ul{margin-left:1.5em}.toc li{overflow-x:hidden;max-width:98.5%;text-align:left}.toc li ul li::after{content:".............................................." ".............................................." ".............................................." "........";float:left;width:0;letter-spacing:6px}footer,header{display:none}dd,li,p,p *{text-align:justify}dl{width:100%;display:flex;flex-wrap:wrap}dt{flex:1;min-width:30%}dd{flex:1;min-width:65%}.frontpage-logo{margin-top:75mm;width:65mm;padding-bottom:1cm}.report-container{height:auto}pre{display:inline;font-size:.9em}pre:first-child,pre:last-child{padding:0;margin:0;background-color:#fff}pre *{white-space:pre-wrap;background-color:var(--color-codeblock);padding:1px;word-wrap:normal}.findings td{max-width:20em;overflow-wrap:break-word;text-wrap:balance}.frontpage-title{font-size:20pt;font-weight:700}.frontpage-subtitle{page-break-after:always;}.metadata td{padding:.3em}td{padding:4px}}.page-header{margin-top:-1cm;opacity:.8;position:running(pageHeaderRunning)}.page-header svg{width:34mm}@media screen{.page-header,.frontpage-subtitle,.frontpage-title,.frontpage-logo{display:none}.frontpage-subtitle,.frontpage-title{text-align:center}.frontpage-logo{width:10em;padding-bottom:.5em;margin-left:auto;margin-right:auto}.frontpage-title{font-size:var(--size-900);font-weight:700}.frontpage-subtitle{font-size:var(--size-500)}}@page{size:A4;margin:2cm 1.6cm;bleed:6mm}@page{@bottom-center{content:"𝗣𝗨𝗕𝗟𝗜𝗖 \A hello@iosiro.com";white-space:pre;color:#b7b7b7;font-size:9pt;font-family:Montserrat,sans-serif}@bottom-right-corner{content:counter(page);font-size:9pt}@top-center{content:element(pageHeaderRunning)}}@page:first{text-align:center;@top-center{content:none}@bottom-right-corner{content:none}}.chroma .lnlinks,a{text-decoration:none}.chroma .lntd,.codequality td{vertical-align:top;font-size:.9em}.chroma .ge,.chroma .sd{font-style:italic}.chroma .gh,.chroma .gp,.chroma .gs,.chroma .gu,.chroma .nc,.chroma .nd,.chroma .ni,.chroma .nl,.chroma .nn,.chroma .nt,.chroma .se,summary{font-weight:700}.bg,.chroma{background-color:var(--color-codeblock)}.chroma .lnlinks{outline:0;color:inherit}.chroma .lntable{border-spacing:0}.chroma .hl{background-color:#d8d8d8}.chroma .ln,.chroma .lnt{white-space:pre;-webkit-user-select:none;user-select:none;margin-right:.4em;padding:0 .4em;color:#7f7f7f}.chroma .line{display:flex}.chroma .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kr,.chroma .ow{color:#007020;font-weight:700}.chroma .cp,.chroma .cpf,.chroma .kp,.chroma .nb,.chroma .ne{color:#007020}.chroma .kt{color:#902000}.chroma .dl,.chroma .na,.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma .sb,.chroma .sc,.chroma .sd,.chroma .se,.chroma .sh{color:#4070a0}.chroma .nc,.chroma .nn{color:#0e84b5}.chroma .no{color:#60add5}.chroma .nd{color:#555}.chroma .ni{color:#d55537}.chroma .nf{color:#06287e}.chroma .nl{color:#002070}.chroma .nt{color:#062873}.chroma .nv{color:#bb60d5}.chroma .si{color:#70a0d0}.chroma .gp,.chroma .sx{color:#c65d09}.chroma .sr{color:#235388}.chroma .ss{color:#517918}.chroma .il,.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma .mi,.chroma .mo{color:#40a070}.chroma .o{color:#666}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm{color:#60a0b0;font-style:italic}.chroma .cs{color:#60a0b0;background-color:#fff0f0}.chroma .gd{color:#a00000}.chroma .gr{color:red}.chroma .gh{color:navy}.chroma .gi{color:#00a000}.chroma .go{color:#888}.chroma .gu{color:purple}.chroma .gt{color:#04d}.chroma .gl{text-decoration:underline}.chroma .w{color:#bbb}.container{max-width:100%}
</style>
<div class="report-container">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" style="display:none;">
<defs>
<symbol id="iosiro-logo" viewBox="0 0 1233 312" preserveAspectRatio="xMidYMid meet">
<g fill="#061f45">
<path d="M171.5 300.6 c-3.9 -1.7 -9.5 -4.6 -12.5 -6.5 -5.1 -3.2 -21 -15 -21 -15.6 0 -0.2 6.1 -3.5 13.5 -7.4 7.4 -3.8 17.2 -9.6 21.8 -12.7 11.1 -7.7 24.4 -21.3 29 -29.5 8.2 -14.7 9.4 -21.4 11.2 -62 1.5 -36.7 3 -46.9 8.5 -62.1 10.1 -27.6 28.2 -43.3 68 -58.8 20.3 -8 31.8 -14.8 42.4 -25.2 l8.9 -8.8 -0.7 5.7 c-1.1 9.2 -4.7 15.7 -13.4 24.2 -9.3 9.1 -20.2 16 -49.9 31.5 -22.9 12 -27 14.8 -33.3 22.7 -8.4 10.6 -10.6 21.1 -12.5 59.2 -1.6 32.3 -2.4 39.8 -5.6 52.7 -7.4 29.9 -24.7 52.9 -51.2 68.2 l-7.7 4.4 6.4 3.6 6.4 3.5 7.9 -4.1 c4.3 -2.2 11.7 -6.7 16.5 -9.9 9.8 -6.6 30.5 -26 40.1 -37.5 8.9 -10.6 21.7 -30.5 28 -43.3 l5.2 -10.7 9.5 -4.5 c5.2 -2.5 9.6 -4.4 9.9 -4.2 1.6 1.7 -13.3 32.8 -22.4 46.7 -22.3 34 -54.5 62.6 -91.3 81.2 l-4.7 2.4 -7 -3.2z"></path>
<path d="M582.1 272 c-19.3 -4.1 -31.7 -11.4 -45.4 -26.8 -14.2 -16 -19.7 -31.4 -19.7 -55.5 0.1 -22.2 4.9 -36.2 17.4 -50.9 9.3 -10.9 16.1 -16.5 25.8 -21.4 14.5 -7.3 21.7 -8.9 39.8 -8.9 18.3 0 23.5 1.2 38.5 8.5 20.6 10 36.7 29.7 43.1 53 2.6 9.2 2.5 31.8 0 41.4 -7.3 27.2 -27.1 48.7 -53 57.3 -10.6 3.4 -11.8 3.6 -26.6 3.9 -8.5 0.2 -17.5 -0.1 -19.9 -0.6z m31 -31.4 c13.1 -2.8 21.4 -9.3 27.7 -21.3 5.1 -10 6.6 -16.5 6.5 -28.8 0 -22.7 -9.7 -39.6 -26.9 -46.9 -5.9 -2.5 -8.5 -3 -17.7 -3.4 -6.4 -0.3 -12.7 0.1 -15.5 0.8 -25.7 6.4 -40 33.3 -33.7 63.5 2.9 13.8 10.9 25.6 21.7 31.9 8.6 5 25.2 6.9 37.9 4.2z"></path>
<path d="M754 271.9 c-11.5 -3.2 -17.4 -6.4 -24.9 -13.9 -6.6 -6.6 -12.6 -14.9 -14.6 -20.1 -0.5 -1.4 2.3 -3.1 14.1 -8.8 8.1 -3.9 14.9 -7 15 -6.8 0.2 0.2 2.1 3 4.3 6.4 4.7 7.3 9 10.7 15.4 12.4 5.8 1.4 14.3 0.7 18.7 -1.6 6.2 -3.2 8.8 -14 5.1 -21.4 -3.4 -6.6 -9.5 -10.9 -28.6 -20.2 -21.5 -10.4 -27.5 -15.1 -32.7 -25.7 -3.1 -6.3 -3.3 -7.2 -3.3 -17.7 0.1 -9.1 0.5 -12 2.4 -16.8 5.3 -13.4 16.5 -23.3 31.5 -27.9 5.6 -1.7 8.6 -2 16.7 -1.6 8.9 0.4 10.7 0.9 17.4 4.2 8.9 4.4 17.2 12.1 20.9 19.4 1.4 2.8 2.6 5.6 2.6 6.1 0 0.9 -27.9 15.6 -28.4 14.9 -0.2 -0.2 -1.4 -2 -2.8 -4.1 -5.3 -7.9 -13.3 -11.2 -20.2 -8.3 -4 1.7 -7.6 6.8 -7.6 10.9 0 6.9 5.4 11.8 20.3 18.4 18 7.9 32.9 16.8 39.6 23.4 1.8 1.9 4.7 6.3 6.4 9.9 2.7 5.6 3.2 7.9 3.5 16.2 0.5 11.8 -1.7 20.3 -7.6 29.3 -7.6 11.7 -18.3 19.3 -32.2 23 -6.2 1.7 -25.4 1.9 -31 0.4z"></path>
<path d="M1118 271 c-17.1 -4.3 -27.7 -10.6 -40.6 -24.3 -5.2 -5.4 -8.2 -9.8 -12.2 -17.7 -6.9 -13.9 -8.4 -19.7 -8.9 -36 -0.6 -16.4 0.9 -24.3 6.6 -36.6 5.5 -11.7 11.7 -20 20.5 -27.7 9.2 -8.1 17.3 -12.6 29.6 -16.8 8 -2.7 11.4 -3.3 21.9 -3.7 17.7 -0.7 28.4 1.5 43.8 9 9.7 4.7 14.7 8.7 24 19.1 14.8 16.5 19.6 29.6 19.7 54.2 0 12.8 -0.3 16.2 -2.3 23.5 -3.4 12.4 -9.3 23 -18 32.2 -12.8 13.7 -23.3 19.9 -41 24.4 -12.1 3.1 -31.5 3.3 -43.1 0.4z m32.9 -30.1 c8.9 -1.6 14.3 -4.2 21.1 -10.5 10.4 -9.7 15.3 -22.3 15.3 -39.4 0 -22.3 -9.5 -39.2 -26.6 -47.1 -6.5 -3 -8.3 -3.3 -18.3 -3.7 -17 -0.7 -27.7 3.1 -37.1 13.1 -8.5 8.9 -13.3 22.2 -13.3 36.8 -0.1 36.5 23.6 56.8 58.9 50.8z"></path>
<path d="M116.7 259.8 c-21.5 -21.4 -40.1 -47.4 -49.3 -69 -1.9 -4.5 -3.3 -8.2 -3.2 -8.3 0.2 -0.2 6.4 1.9 13.8 4.6 39.7 14.5 52.8 20.7 56.3 26.6 2.3 4 2.4 9.6 0.2 14.8 l-1.8 4 -1.2 -4.8 c-1.6 -6.3 -7.9 -12.8 -16.1 -16.7 -5.8 -2.8 -22.3 -7.4 -23.2 -6.5 -0.8 0.7 20.8 30.1 29.1 39.6 l4.9 5.6 7.6 -4.1 c16.5 -8.9 28.7 -25.3 31.8 -42.6 3.5 -20.6 -2.9 -41.1 -18.1 -56.9 -12.9 -13.5 -24.5 -20.2 -49.7 -28.9 -17.7 -6.1 -23.4 -8.5 -31.3 -13.1 -20.4 -11.6 -34.4 -28.2 -39 -46.2 -2 -7.5 -2 -20.9 -0.2 -28.9 l1.3 -5.5 0.8 6.5 c2.4 21.7 11.4 39 26.7 51.6 9.4 7.7 18.7 12.1 44.4 20.9 58.6 20.2 75.6 34.1 85.1 69.2 3.7 13.8 4 33.3 0.6 44.1 -6.8 21.5 -25.1 39.9 -49.2 49.4 -11.5 4.6 -9.9 5 -20.3 -5.4z"></path>
<path d="M441 190.5 l0 -78.5 17.5 0 17.5 0 0 78.5 0 78.5 -17.5 0 -17.5 0 0 -78.5z"></path>
<path d="M858 190.5 l0 -78.5 18 0 18 0 0 78.5 0 78.5 -18 0 -18 0 0 -78.5z"></path>
<path d="M942 190.5 l0 -78.5 18 0 18 0 0 7.1 0 7.2 4.5 -4.2 c6.7 -6.2 15.9 -11.7 21.6 -13.1 8.5 -2.1 21 -0.8 29.9 2.9 4.1 1.7 8.1 3.5 8.8 4 1.1 0.6 -0.4 4.3 -6.3 16.2 -4.3 8.5 -7.9 15.6 -8.1 15.8 -0.2 0.2 -3.2 -0.9 -6.6 -2.5 -5.3 -2.4 -7.6 -2.9 -14.3 -2.9 -7 0 -8.7 0.4 -13.4 3 -6.6 3.7 -10.4 8.2 -13.3 16 -2.3 5.9 -2.3 6.9 -2.6 56.8 l-0.3 50.7 -17.9 0 -18 0 0 -78.5z"></path>
<path d="M145 191.5 c-7.1 -7.2 -17 -14 -29.8 -20.4 -10.1 -5.1 -33.5 -13.6 -44.2 -16.1 -13.5 -3.2 -32.8 -13.7 -42 -23 -6.9 -6.8 -13.5 -17.3 -16.6 -26.6 -2.3 -6.6 -2.7 -9.7 -2.8 -18.9 -0.1 -11.4 1.1 -23.2 2.2 -22 0.4 0.3 1.4 4.6 2.3 9.4 3.6 18.6 10.6 32 23.8 45.2 11.8 11.7 18.1 15.2 39.9 21.9 25.8 8 39.3 14.5 53.6 26.1 10 8 20.9 24.4 19.4 28.9 -0.2 0.6 -2.8 -1.5 -5.8 -4.5z"></path>
<path d="M240 182.6 c0 -2.9 4.2 -12.6 7.5 -17.1 7.7 -10.9 17.3 -18.4 40 -31.2 18.3 -10.3 23.6 -14.3 37.8 -28.2 l11.7 -11.6 0 3.5 c0 10 -5.7 21.6 -15.1 31.1 -7.3 7.4 -13.7 11.2 -32.1 19.3 -21.6 9.6 -32.7 16.6 -43 27.4 -3.8 3.8 -6.8 6.9 -6.8 6.8z"></path>
<path d="M244 145.8 c0 -3.2 4.3 -15.5 7.8 -22.2 4.1 -7.9 14.2 -18.7 22.2 -23.8 3 -2 11.6 -6.7 19 -10.7 16.7 -8.8 31.3 -19.6 44.9 -33.2 l10.4 -10.3 -0.6 4.2 c-3.3 20.1 -19.8 37.3 -50.7 52.7 -6.3 3.2 -14 7.5 -17.2 9.6 -5.9 4 -25.8 23.1 -32.2 30.8 -2.1 2.6 -3.6 3.8 -3.6 2.9z"></path>
<path d="M182.1 139.6 c-6.5 -8.4 -21.1 -21.9 -26.3 -24.3 -4.1 -2 -5 -3 -8.2 -9.6 -5 -10.4 -15.6 -21.6 -24.4 -25.9 -6.4 -3.1 -7.3 -3.3 -18.3 -3.3 -13.8 0 -15.9 1 -15.9 7.8 0 3.6 -0.2 3.8 -1.7 2.6 -3 -2.5 -7.3 -10.2 -7.3 -13.2 0 -9.4 12.7 -18.9 29.4 -21.8 5.5 -1 8.2 -2.1 12.1 -5.1 7.3 -5.6 13.7 -7.2 26.5 -6.6 13.4 0.7 20.2 3 46.3 15.4 22.9 11 24.1 11.5 28.5 12.3 l3.2 0.6 -3.6 5 c-9.8 13.7 -18.1 38.4 -19.6 58.5 -0.5 5.9 -2.8 -13.8 -2.8 -23.7 0 -9.8 1.9 -21.5 4.4 -27.5 0.9 -2.1 1.6 -4 1.6 -4.3 0 -0.2 -4.2 -2.4 -9.2 -4.8 -5.1 -2.4 -13.3 -7 -18.3 -10.1 -15.3 -9.7 -33.9 -13.8 -42 -9.4 l-3 1.6 5 0.7 c6.9 0.9 18.2 4.3 19.1 5.7 0.5 0.8 0.1 0.9 -1.1 0.4 -1.1 -0.4 -10.7 -1 -21.4 -1.3 -21 -0.6 -29.5 0.3 -32.5 3.6 -1.6 1.7 -1.3 1.8 5.3 2.5 13.2 1.2 28 7.2 39.8 16 11.7 8.9 21.8 21.8 30.1 38.7 4.1 8.5 9.7 23.8 8.9 24.5 -0.2 0.3 -2.3 -2 -4.6 -5z"></path>
<path d="M57.1 65.6 l-6.4 -6.4 2.9 -12.2 2.9 -12.3 11 -3.9 c56.8 -20.4 121.7 -24.1 184 -10.6 12 2.6 32.2 8.6 36.2 10.7 0.7 0.4 -3.1 2.5 -8.8 5 l-10 4.4 -7.4 -2.2 c-10.1 -2.9 -23.7 -5.6 -38.6 -7.7 -16.6 -2.4 -59.3 -2.4 -79.4 0 -26.3 3 -71.7 12.6 -73.4 15.5 -0.4 0.6 -1.8 6.8 -3.1 13.6 -1.3 6.9 -2.6 12.5 -3 12.5 -0.3 0 -3.4 -2.9 -6.9 -6.4z"></path>
<path d="M449.3 66.9 c-12.3 -6.1 -17 -20.9 -10.4 -32.4 5.2 -9 12.9 -12.5 24.6 -11 6.5 0.8 11.1 4.1 14.9 10.9 11 19 -9.7 42.1 -29.1 32.5z"></path>
<path d="M867.5 67.2 c-5 -2.3 -10.3 -7.6 -12.1 -11.9 -1.8 -4.3 -1.8 -15.2 0.1 -19.6 2.2 -5.4 9.8 -11.5 15.2 -12.3 6.4 -0.9 13.9 0.2 17.9 2.7 7.6 4.7 12.7 16.5 10.5 24.5 -1.5 5.4 -6.1 11.7 -11.2 15.1 -5.4 3.6 -14.5 4.3 -20.4 1.5z"></path>
</g>
</symbol>
</defs>
<use href="#iosiro-logo"></use>
</svg>
<div class="page-header" id="page-header">
<svg version="1.0">
<use href="#iosiro-logo"></use>
</svg>
</div>
<main class="report-main">
<svg version="1.0" class="frontpage-logo">
<use href="#iosiro-logo"></use>
</svg>
<div class="frontpage-title">Zap Octopus Release Smart Contract Audit</div>
<div class="frontpage-subtitle">Kwenta, 6 December 2024</div>
<nav class="toc">
<h1>Contents</h1>
<ul>
<li>
<a href="#introduction">Introduction</a></li>
<li>
<a href="#disclaimer">Disclaimer</a></li>
<li>
<a href="#methodology">Methodology</a></li>
<li>
<a href="#audit-findings">Audit findings</a><ul>
<li>
<a href="#io-kwt-zpo-001-incorrect-asset-amount-used-to-repay-when-unwinding">IO-KWT-ZPO-001 Incorrect asset amount used to repay when unwinding</a></li>
<li>
<a href="#io-kwt-zpo-002-perp-accounts-could-be-modified-by-unauthorized-addresses">IO-KWT-ZPO-002 Perp accounts could be modified by unauthorized addresses</a></li>
<li>
<a href="#io-kwt-zpo-006-insufficient-access-control-on-executeoperation">IO-KWT-ZPO-006 Insufficient access control on executeOperation()</a></li>
<li>
<a href="#io-kwt-zpo-003-missing-path-parameter-when-decoding">IO-KWT-ZPO-003 Missing path parameter when decoding</a></li>
<li>
<a href="#io-kwt-zpo-004-incorrect-decimals-used-for-usdc-flashloan-and-swaps">IO-KWT-ZPO-004 Incorrect decimals used for USDC flashloan and swaps</a></li>
<li>
<a href="#io-kwt-zpo-005-aave-flashloan-griefing-attack">IO-KWT-ZPO-005 Aave flashloan griefing attack</a></li>
<li>
<a href="#io-kwt-zpo-007-non-usdc-collateral-transfer-will-fail-in-executeoperation">IO-KWT-ZPO-007 Non-USDC collateral transfer will fail in executeOperation()</a></li>
<li>
<a href="#io-kwt-zpo-008-incorrect-function-used-to-pay-debt">IO-KWT-ZPO-008 Incorrect function used to pay debt</a></li>
<li>
<a href="#io-kwt-zpo-009-safeerc20-not-used-for-token-transfer">IO-KWT-ZPO-009 SafeERC20 not used for token transfer</a></li>
<li>
<a href="#io-kwt-zpo-010-unspent-token-allowance-not-reset">IO-KWT-ZPO-010 Unspent token allowance not reset</a></li>
<li>
<a href="#io-kwt-zpo-011-dust-amount-of-usdx-could-be-trapped-on-zap-contract">IO-KWT-ZPO-011 Dust amount of USDX could be trapped on Zap contract</a></li>
<li>
<a href="#io-kwt-zpo-012-unexpected-revert-during-unwind">IO-KWT-ZPO-012 Unexpected revert during unwind()</a></li>
<li>
<a href="#io-kwt-zpo-019--push-during-unwind-may-revert-with-zero-value">IO-KWT-ZPO-019 _push() during unwind may revert with zero value</a></li>
<li>
<a href="#io-kwt-zpo-020-swapfrom-does-not-refund-excess-input-asset">IO-KWT-ZPO-020 swapFrom() does not refund excess input asset</a></li>
<li>
<a href="#io-kwt-zpo-022-flush-plumber-could-be-set-to-address0">IO-KWT-ZPO-022 Flush plumber could be set to address(0)</a></li>
<li>
<a href="#io-kwt-zpo-013-burn-function-could-be-used-as-a-usdx-sweep-function">IO-KWT-ZPO-013 burn() function could be used as a USDX sweep function</a></li>
<li>
<a href="#io-kwt-zpo-014--tolerance-should-be-renamed-to--minamountout">IO-KWT-ZPO-014 _tolerance should be renamed to _minAmountOut</a></li>
<li>
<a href="#io-kwt-zpo-015-assign-constant-variables-at-declaration">IO-KWT-ZPO-015 Assign constant variables at declaration</a></li>
<li>
<a href="#io-kwt-zpo-016-fetch-synth-address-in-external-function">IO-KWT-ZPO-016 Fetch synth address in external function</a></li>
<li>
<a href="#io-kwt-zpo-017-no-checks-for-zero-address-and-zero-amounts-in-token-transfers">IO-KWT-ZPO-017 No checks for zero address and zero amounts in token transfers</a></li>
<li>
<a href="#io-kwt-zpo-018-error-catching-only-works-for-string-reverts">IO-KWT-ZPO-018 Error catching only works for string reverts</a></li>
<li>
<a href="#io-kwt-zpo-021-unsafe-assumption-with-external-contract-integration">IO-KWT-ZPO-021 Unsafe assumption with external contract integration</a></li>
</ul>
</li>
<li>
<a href="#code-quality-improvement-suggestions">Code quality improvement suggestions</a></li>
<li>
<a href="#specification">Specification</a><ul>
<li>
<a href="#overview-1">Overview</a></li>
<li>
<a href="#buying-and-selling-synthetix-v3-synths">Buying and selling Synthetix v3 synths</a></li>
<li>
<a href="#perp-debt-and-collateral">Perp debt and collateral</a></li>
<li>
<a href="#odos-swaps-to-usdc">Odos swaps to USDC</a></li>
</ul>
</li>
<li>
<a href="#test-coverage-report">Test coverage report</a></li>
</ul>
</nav>
<article class="report">
<h1 id="introduction">Introduction</h1>
<p>iosiro was commissioned by Kwenta to perform a smart contract audit of the Octopus release of Zap. Two auditors conducted the audit from 24 September 2024 until 21 November 2024, using 18 audit days.</p>
<h4 id="overview">Overview</h4>
<p>The audit uncovered several functional issues that would have prevented the system from working as intended. Further issues with missing access controls exposed Zap users to having their perp accounts modified by unauthorized addresses. Numerous code quality and best practice suggestions were provided as detailed in this report. All identified issues were resolved upon completion of the audit.</p>
<p>Users should note that the Zap contract does not enforce or validate any slippage checks directly, rather it relies on its external integration partners to enforce the checks correctly based on the values passed by the caller.</p>
<table class="findings-count">
<thead>
<tr>
<th> </th>
<th>Critical</th>
<th>High</th>
<th>Medium</th>
<th>Low</th>
<th>Informational</th>
</tr>
</thead>
<tbody>
<tr>
<td>Open</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>Resolved</td>
<td>0</td>
<td>3</td>
<td>6</td>
<td>6</td>
<td>6</td>
</tr>
<tr>
<td>Closed</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
<h4 id="scope">Scope</h4>
<p>The assessment focused on the source files listed below, with all other files considered out of scope. Any out-of-scope code interacting with the assessed code was presumed to operate correctly without introducing functional or security vulnerabilities.</p>
<ul>
<li><strong>Project name:</strong> zap</li>
<li><strong>Initial audit commit:</strong> <a href="https://github.com/JaredBorders/zap/commit/237c1839f6e0183e8307389893514809859450dd">237c183</a></li>
<li><strong>Final review commit:</strong> <a href="https://github.com/JaredBorders/zap/commit/6b2ca51112c28f612000c030532a0ad0cdcc55bb">6b2ca51</a></li>
<li><strong>Files:</strong> Zap.sol, Flush.sol, Errors.sol, Reentrancy.sol, SafeTransferERC20.sol</li>
</ul>
<p>A specification is available in the <a href="#specification">Specification section</a> of this report.</p>
<h1 id="disclaimer">Disclaimer</h1>
<p>This report aims to provide an overview of the assessed smart contracts' risk exposure and a guide to improving their security posture by addressing identified issues. The audit, limited to specific source code at the time of review, sought to:</p>
<ul>
<li>Identify potential security flaws.</li>
<li>Verify that the smart contracts' functionality aligns with their documentation.</li>
</ul>
<p>Off-chain components, such as backend web application code, keeper functionality, and deployment scripts, were explicitly excluded from the scope of this audit.</p>
<p>Given the unregulated nature and ease of cryptocurrency transfers, operations involving these assets face a high risk from cyber attacks. Maintaining the highest security level is crucial, necessitating a proactive and adaptive approach that accounts for the experimental and rapidly evolving nature of blockchain technology. To encourage secure code development, developers should:</p>
<ul>
<li>Integrate security throughout the development lifecycle.</li>
<li>Employ defensive programming to mitigate the risks posed by unexpected events.</li>
<li>Adhere to current best practices wherever possible.</li>
</ul>
<h1 id="methodology">Methodology</h1>
<p>The audit was conducted using the techniques described below.</p>
<dl>
<dt>Code review</dt>
<dd>The source code was manually inspected to identify potential security flaws. Code review is a useful approach for detecting security flaws, discrepancies between the specification and implementation, design improvements, and high-risk areas of the system.</dd>
<dt>Dynamic analysis</dt>
<dd>The contracts were compiled, deployed, and tested in a test environment, both manually and through the test suite provided. Dynamic analysis was used to identify additional edge cases, confirm that the code was functional, and validate the reported issues.</dd>
<dt>Automated analysis</dt>
<dd>Automated tooling was used to detect the presence of various types of security vulnerabilities. Static analysis results were reviewed manually, and any false positives were removed. Any true positive results are included in this report.</dd>
</dl>
<h1 id="audit-findings">Audit findings</h1>
<p>The table below provides an overview of the audit's findings. Detailed write-ups are provided below.</p>
<table class="findings">
<thead>
<tr>
<th>ID</th>
<th>Issue</th>
<th>Risk</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="#IO-KWT-ZPO-001">IO-KWT-ZPO-001</a></td>
<td>Incorrect unwound amount returned on unwinding with a swap</td>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-002">IO-KWT-ZPO-002</a></td>
<td>Perp accounts could be modified by unauthorized addresses</td>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-006">IO-KWT-ZPO-006</a></td>
<td>Insufficient access control in <code>executeOperation()</code></td>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-003">IO-KWT-ZPO-003</a></td>
<td>Missing path parameter when decoding</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-004">IO-KWT-ZPO-004</a></td>
<td>Incorrect decimals used for USDC flashloan and swaps</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-005">IO-KWT-ZPO-005</a></td>
<td>Aave flashloan griefing attack</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-007">IO-KWT-ZPO-007</a></td>
<td>Non USDC collateral transfer will fail</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-008">IO-KWT-ZPO-008</a></td>
<td>Incorrect function used to pay debt</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-009">IO-KWT-ZPO-009</a></td>
<td>Use <code>SafeERC20</code> functions for token transfer</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-010">IO-KWT-ZPO-010</a></td>
<td>Unspent token allowance not reset</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-011">IO-KWT-ZPO-011</a></td>
<td>Dust amount of USDX could be trapped on Zap contract</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-012">IO-KWT-ZPO-012</a></td>
<td>Unexpected revert during <code>unwind()</code></td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-019">IO-KWT-ZPO-019</a></td>
<td><code>_push()</code> during unwind may revert with zero value</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-020">IO-KWT-ZPO-020</a></td>
<td><code>swapFrom()</code> does not refund excess input asset</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-022">IO-KWT-ZPO-022</a></td>
<td>Flush plumber could be set to <code>address(0)</code></td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-013">IO-KWT-ZPO-013</a></td>
<td><code>burn()</code> function could be used as a USDX sweep function</td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-014">IO-KWT-ZPO-014</a></td>
<td><code>_tolerance</code> should be renamed to <code>_minAmountOut</code></td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-015">IO-KWT-ZPO-015</a></td>
<td>Assign constant variables at declaration</td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-016">IO-KWT-ZPO-016</a></td>
<td>Fetch synth address in external function</td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-017">IO-KWT-ZPO-017</a></td>
<td>No checks for zero address and amounts in token transfers</td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-018">IO-KWT-ZPO-018</a></td>
<td>Error catching only works for string reverts</td>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-KWT-ZPO-021">IO-KWT-ZPO-021</a></td>
<td>Unsafe assumption with external contract integration</td>
<td class="rating-informational">Informational</td>
<td class="status-closed">Closed</td>
</tr>
</tbody>
</table>
<p>Each issue identified during the audit has been assigned a risk rating, which is determined based on the criteria outlined below.</p>
<dl>
<dt>Critical risk</dt>
<dd>The issue could result in the theft of funds from the contract or its users.</dd>
<dt>High risk</dt>
<dd>The issue could result in the loss of funds for the contract owner or its users.</dd>
<dt>Medium risk</dt>
<dd>The issue resulted in the code being dysfunctional or the specification being implemented incorrectly.</dd>
<dt>Low risk</dt>
<dd>A design or best practice issue that could affect the ordinary functioning of the contract.</dd>
<dt>Informational</dt>
<dd>An improvement related to best practice or a suboptimal design pattern.</dd>
</dl>
<p>In addition to a risk rating, each issue is assigned a status:</p>
<dl>
<dt>Open</dt>
<dd>The issue remained present in the code as of the final commit reviewed and may still pose a risk.</dd>
<dt>Resolved</dt>
<dd>The issue was identified during the audit and has since been satisfactorily addressed, removing the risk it posed.</dd>
<dt>Closed</dt>
<dd>The issue was identified during the audit and acknowledged by the developers as an acceptable risk without actioning any change.</dd>
</dl>
<a name="IO-KWT-ZPO-001"></a><h2 id="io-kwt-zpo-001-incorrect-asset-amount-used-to-repay-when-unwinding" class="break-before"><strong>IO-KWT-ZPO-001</strong> Incorrect asset amount used to repay when unwinding</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L349">Zap.sol#L349</a></td>
</tr>
</tbody>
</table>
<p>When unwinding non-USDC collateral, the unwound collateral is swapped to USDC using Uniswap to repay the flashloan, and the remaining collateral is repaid to the owner. The amount owed to the account owner was calculated as <code>unwound collateral - (fl amount + fl premium)</code>. As the unwound collateral was in asset units and the flashloan amount plus premium in USD, the calculation produced incorrect results.</p>
<p>This issue in combination with <a href="#IO-KWT-ZPO-007">IO-KWT-ZPO-007</a> caused the unwinding of all accounts with non-USDC collateral to fail.</p>
<h3 id="recommendation">Recommendation</h3>
<p>When calculating the amount to repay to the caller, care should be taken to ensure the correct units are used. Assuming IO-KWT-ZPO-007 is also resolved, the amount of collateral owed should equal collateral unwound minus collateral swapped to repay the loan.</p>
<h3 id="client-response">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/fbc5a14e6bdf2dcfa5dd6a34d1f4c619047f3296">fbc5a14</a>.</p>
<a name="IO-KWT-ZPO-002"></a><h2 id="io-kwt-zpo-002-perp-accounts-could-be-modified-by-unauthorized-addresses" class="break-before"><strong>IO-KWT-ZPO-002</strong> Perp accounts could be modified by unauthorized addresses</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L349">Zap.sol#L349</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L502">Zap.sol#L502</a></td>
</tr>
</tbody>
</table>
<p>If a perp account had granted the <code>PERPS_MODIFY_COLLATERAL</code> permission to the Zap contract, anyone could call the <code>unwind()</code> or <code>withdraw()</code> functions to withdraw available collateral from the account and send it to a <code>_receiver</code> address of their choice.</p>
<h3 id="recommendation-1">Recommendation</h3>
<p>The <code>unwind()</code> and <code>withdraw()</code> functions should revert if the caller of the function does not have <code>PERPS_MODIFY_COLLATERAL</code> permission. The permission could be checked using the <code>IPerpsMarket(PERPS_MARKET).isAuthorized()</code> function. An additional protection could be to omit the <code>_receiver</code> address and return funds to the owner of the perp account instead.</p>
<h3 id="client-response-1">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/7e4e0e5ee3f211d653d76f385714fd832a577809">7e4e0e5</a>.</p>
<a name="IO-KWT-ZPO-006"></a><h2 id="io-kwt-zpo-006-insufficient-access-control-on-executeoperation" class="break-before"><strong>IO-KWT-ZPO-006</strong> Insufficient access control on <code>executeOperation()</code></h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L381">Zap.sol#L381</a></td>
</tr>
</tbody>
</table>
<p>The <code>executeOperation()</code> function could be called by any address. This could lead to the draining of a perp account that granted Zap the <code>PERPS_MODIFY_COLLATERAL</code> permission.</p>
<h3 id="recommendation-2">Recommendation</h3>
<p><code>executeOperation()</code> should revert if the caller is not the AAVE pool address stored on the Zap contract.</p>
<h3 id="client-response-2">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/0d0779249fc77e636da56dce98fd10d14b7464d0">0d07792</a>.</p>
<a name="IO-KWT-ZPO-003"></a><h2 id="io-kwt-zpo-003-missing-path-parameter-when-decoding" class="break-before"><strong>IO-KWT-ZPO-003</strong> Missing path parameter when decoding</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/f04250f496e4dad07753ea3041fc2d2660316cb9/src/Zap.sol#L423">Zap.sol#L423</a></td>
</tr>
</tbody>
</table>
<p>The <code>abi.decode(...)</code> used to decode the <code>params</code> data passed to <code>executeOperation()</code> did not account for the <code>path</code> parameter. This caused the EVM to revert unexpectedly when attempting to complete an <code>unwind()</code> process.</p>
<h3 id="recommendation-3">Recommendation</h3>
<p>The decode block should be updated to also consider the <code>path</code> parameter encoded into the data.</p>
<p>Readability could be improved by adding comment placeholders of the parameter name and type for unused variables.</p>
<h3 id="client-response-3">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/66714f7b116272e7efd8c7b15058311270e45f71">66714f7</a>.</p>
<a name="IO-KWT-ZPO-004"></a><h2 id="io-kwt-zpo-004-incorrect-decimals-used-for-usdc-flashloan-and-swaps" class="break-before"><strong>IO-KWT-ZPO-004</strong> Incorrect decimals used for USDC flashloan and swaps</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L369">Zap.sol#L369</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L435">Zap.sol#L435</a></td>
</tr>
</tbody>
</table>
<p>The flashloan amount requested from Aave is based on the USD D18 denominated value returned from the <code>IPerpsMarket(PERPS_MARKET).debt(accountId)</code> function. However, the Aave USDC pool uses USDC token amounts denominated in D6. The execution would likely revert since the flashloan amount requested would be larger than the pool balance.</p>
<p>Furthermore, in <code>_unwind()</code> the <code>_flashloan</code> value is not scaled up from D6 to D18 when used to pay the perp account’s debt.</p>
<h3 id="recommendation-4">Recommendation</h3>
<p>The <code>amount</code> used to request a flashloan from Aave should be scaled from D18 to D6 and inversely the <code>_flashloan</code> amount should be scaled from D6 to D18 before being passed to <code>_zapIn()</code>.</p>
<p>Readability could be improved by renaming the <code>_amount</code> parameter in <code>swapFor()</code> and the <code>_tolerance</code> parameter in <code>swapWith()</code> to specify that they are denominated as D6.</p>
<h3 id="client-response-4">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/912e1a7e71ceef4895f24b5eeff5c8600e37527f">912e1a7</a>.</p>
<a name="IO-KWT-ZPO-005"></a><h2 id="io-kwt-zpo-005-aave-flashloan-griefing-attack" class="break-before"><strong>IO-KWT-ZPO-005</strong> Aave flashloan griefing attack</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L381">Zap.sol#L381</a></td>
</tr>
</tbody>
</table>
<p>Any contract could initiate an Aave flashloan and specify the Zap contract as the receiver address. The Aave pool contract would callback into the Zap contract's <code>executeOperation()</code> function and attempt to unwind the targeted perp account. If the account has available collateral and it grants the Zap contract the <code>PERPS_MODIFY_COLLATERAL</code> permission, the collateral could be taken.</p>
<h3 id="recommendation-5">Recommendation</h3>
<p>A multi-leveled reentrancy guard could be used to protect the callback function from such an attack. The guard levels could be <code>enum(unset, level1, level2)</code>. When initiating a flashloan by calling <code>unwind()</code>, the guard should confirm that it is <code>unset</code> and set it to <code>level1</code> before initiating the flashloan. The <code>executeOperation()</code> callback should confirm the guard is currently at <code>level1</code> and set it to <code>level2</code> before continuing, so that no further reentrant calls can be made during execution. Upon completion, the callback should reset the guard to the <code>unset</code> level. If at any point the guard is in an unexpected state, execution should revert.</p>
<h3 id="client-response-5">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/0d0779249fc77e636da56dce98fd10d14b7464d0">0d07792</a>.</p>
<p>It was further recommended that the implementation shift all updates to <code>stage</code> into the modifier itself and remove it from functions protected by the modifier. However, after internal deliberation, the team opted to leave the implementation as-is.</p>
<a name="IO-KWT-ZPO-007"></a><h2 id="io-kwt-zpo-007-non-usdc-collateral-transfer-will-fail-in-executeoperation" class="break-before"><strong>IO-KWT-ZPO-007</strong> Non-USDC collateral transfer will fail in executeOperation()</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L351">Zap.sol#L351</a></td>
</tr>
</tbody>
</table>
<p>At the end of <code>executeOperation()</code>, a transfer of the specified collateral is attempted even though all the collateral was swapped to USDC. This transfer would fail due to insufficient funds and would prevent the unwinding of positions with non-USDC collateral through Zap.</p>
<h3 id="recommendation-6">Recommendation</h3>
<p>If it is intended that accounts fully unwind to USDC, the transfer function should only attempt to repay the caller in USDC. Note that in such an implementation accounts will experience a higher loss of funds due to Uniswap fees and slippage.</p>
<p>If only the collateral amount required to pay the loan should be swapped, the Uniswap <code>exactOutputSingle()</code> swap function should be used instead of exact input.</p>
<h3 id="client-response-6">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/fbc5a14e6bdf2dcfa5dd6a34d1f4c619047f3296">fbc5a14</a>.</p>
<a name="IO-KWT-ZPO-008"></a><h2 id="io-kwt-zpo-008-incorrect-function-used-to-pay-debt" class="break-before"><strong>IO-KWT-ZPO-008</strong> Incorrect function used to pay debt</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L387">Zap.sol#L387</a></td>
</tr>
</tbody>
</table>
<p>The <code>ICore(CORE).burnUsd()</code> function was used to pay the debt of a perp account. However, this implementation would not work since the <code>burnUsd()</code> function is intended to reduce the debt of a delegator account on Synthetix core and is in no way associated with the debt owed by a perp account.</p>
<h3 id="recommendation-7">Recommendation</h3>
<p><code>IPerpsMarket(PERPS_MARKET).payDebt()</code> should be used to pay off the perp account’s debt.</p>
<h3 id="client-response-7">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/fbc5a14e6bdf2dcfa5dd6a34d1f4c619047f3296">fbc5a14</a>.</p>
<a name="IO-KWT-ZPO-009"></a><h2 id="io-kwt-zpo-009-safeerc20-not-used-for-token-transfer" class="break-before"><strong>IO-KWT-ZPO-009</strong> <code>SafeERC20</code> not used for token transfer</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L478-L504">Zap.sol#L478-L504</a></td>
</tr>
</tbody>
</table>
<p>The token transfers in <code>Zap.sol</code> did not use the <code>SafeERC20</code> library. This could allow transfer errors to fail silently.</p>
<h3 id="recommendation-8">Recommendation</h3>
<p>The <code>safeERC20</code> versions of the token transfer functions should be used instead of <code>transfer()</code> and <code>transferFrom()</code> in the <code>_pull()</code> and <code>_push()</code> functions to ensure that unsuccessful token transfers revert.</p>
<h3 id="client-response-8">Client response</h3>
<p>Initial fix in <a href="https://github.com/JaredBorders/zap/commit/0045ad86cba692ad6ccc1c9e8e42e6dc88f2c060">0045ad8</a>.</p>
<p>The <code>bool success</code> value returned from <code>transfer()</code> and <code>transferFrom()</code> was required to be true. This introduced a possible DoS condition for tokens that do not return a boolean value such as USDT.</p>
<p>The contract was updated to use the <code>safeERC20</code> library in <a href="https://github.com/JaredBorders/zap/commit/c904af59a4c8d463dc47fde4b95aafa95ac46f26">c904af5</a>.</p>
<a name="IO-KWT-ZPO-010"></a><h2 id="io-kwt-zpo-010-unspent-token-allowance-not-reset" class="break-before"><strong>IO-KWT-ZPO-010</strong> Unspent token allowance not reset</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/9e27e3f0c2c0ea9972c15f355f3de5f109785e08/src/Zap.sol#L629">Zap.sol#L629</a>, <a href="https://github.com/JaredBorders/zap/blob/9e27e3f0c2c0ea9972c15f355f3de5f109785e08/src/Zap.sol#L556">Zap.sol#L556</a></td>
</tr>
</tbody>
</table>
<p>The <code>IUniswap(UNISWAP).exactOutputSingle(params)</code> call may not swap all of the input tokens that were approved. While all calls to <code>_swapFor()</code> correctly ensured that no unspent token amount was left on Zap, the unspent allowance between Zap and Uniswap was not reset back to 0.</p>
<p>Similarly, the internal function <code>_burn()</code> called <code>IPerpsMarket(PERPS_MARKET).payDebt()</code>, which may not spend all of the approved USDX. When called through the external <code>burn()</code>, the unspent USDX was returned but the unspent allowance between the perp market and Zap was not reset to 0.</p>
<h3 id="recommendation-9">Recommendation</h3>
<p>In both instances, the allowance should be reset to 0.</p>
<h3 id="client-response-9">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/40344866e1af3401ce3d7594ec4cd61d32bacb44">4034486</a>.</p>
<a name="IO-KWT-ZPO-011"></a><h2 id="io-kwt-zpo-011-dust-amount-of-usdx-could-be-trapped-on-zap-contract" class="break-before"><strong>IO-KWT-ZPO-011</strong> Dust amount of USDX could be trapped on Zap contract</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/9e27e3f0c2c0ea9972c15f355f3de5f109785e08/src/Zap.sol#L482">Zap.sol#L482</a></td>
</tr>
</tbody>
</table>
<p>During <code>unwind()</code>, the amount of USDC received from the AAVE flashloan was greater than <code>IPerpsMarket(PERPS_MARKET).debt(accountId)</code> by a dust amount due to rounding errors. <code>payDebt()</code> will only spend an exact amount of USDX, so the remaining dust amount of USDX would be stuck on Zap since <code>_burn()</code> did not refund it.</p>
<h3 id="recommendation-10">Recommendation</h3>
<p>The dust amount of USDC should also be sent to the caller upon completion of <code>unwind()</code>.</p>
<h3 id="client-response-10">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/bb3cb1584db5a2649b57ef5395ad06afd11f7c0c">bb3cb15</a>.
Instead of returning this residual amount to the sender, a <code>Flush</code> contract was introduced. It allows Zap to capture the dust as a minimal form of compensation for the convenience provided and enables the recovery of tokens that may have been inadvertently locked in the Zap contract.</p>
<a name="IO-KWT-ZPO-012"></a><h2 id="io-kwt-zpo-012-unexpected-revert-during-unwind" class="break-before"><strong>IO-KWT-ZPO-012</strong> Unexpected revert during <code>unwind()</code></h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L443">Zap.sol#L443</a></td>
</tr>
</tbody>
</table>
<p>When determining the amount of collateral to withdraw from the perp account, <code>IPerpsMarket(PERPS_MARKET).getCollateralAmount()</code> was used. This function returns the total deposited amount of the specified collateral for that account. The implementation attempted to withdraw that full amount which would revert if the account had any open positions.</p>
<h3 id="recommendation-11">Recommendation</h3>
<p><code>IPerpsMarket(PERPS_MARKET).getWithdrawableMargin()</code> should be used and its result should be converted from USD to the collateral asset amount using the <code>ISpotMarket(SPOT_MARKET).quoteBuyExactIn()</code> function. The minimum between the quoted amount and the deposited amount of that collateral should be withdrawn from the account.</p>
<h3 id="client-response-11">Client response</h3>
<p><code>unwind()</code> was updated to require the caller to specify the amount of collateral to withdraw in <a href="https://github.com/JaredBorders/zap/commit/d5ce24560b66c99a818621c2b3fc346587876096">d5ce245</a>.</p>
<a name="IO-KWT-ZPO-019"></a><h2 id="io-kwt-zpo-019--push-during-unwind-may-revert-with-zero-value" class="break-before"><strong>IO-KWT-ZPO-019</strong> <code>_push()</code> during unwind may revert with zero value</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L435">Zap.sol#L435</a></td>
</tr>
</tbody>
</table>
<p>During the unwind process it is possible for the caller of the function to pass values that swap all of the collateral to USDC. In such a case, <code>_unwind()</code> would return a value of 0 which would cause an unexpected revert when <code>_push()</code> is used to refund collateral to the recipient.</p>
<h3 id="recommendation-12">Recommendation</h3>
<p>The <code>executeOperation()</code> function should only call <code>_push()</code> if <code>unwound > 0</code>;.</p>
<h3 id="client-response-12">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/2744f3bb44760d93fd8adcfc5dd04ef7b4e633ad">2744f3b</a>.</p>
<a name="IO-KWT-ZPO-020"></a><h2 id="io-kwt-zpo-020-swapfrom-does-not-refund-excess-input-asset" class="break-before"><strong>IO-KWT-ZPO-020</strong> <code>swapFrom()</code> does not refund excess input asset</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L645">Zap.sol#L645</a></td>
</tr>
</tbody>
</table>
<p>When calling <code>swapFrom()</code> with an <code>amountIn</code> that is specified as an amount larger than the swap amount required by Odos, <code>swapFrom()</code> does not return the unspent amount to the user.</p>
<h3 id="recommendation-13">Recommendation</h3>
<p>Unspent input asset that was pulled by Zap should be returned to the caller.</p>
<h3 id="client-response-13">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/91a18f1ba5cbfff2b5646d0466ad39f30a5b2689">91a18f1</a>.</p>
<a name="IO-KWT-ZPO-022"></a><h2 id="io-kwt-zpo-022-flush-plumber-could-be-set-to-address0" class="break-before"><strong>IO-KWT-ZPO-022</strong> Flush plumber could be set to <code>address(0)</code></h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/utils/Flush.sol#L37">Flush.sol#L37</a></td>
</tr>
</tbody>
</table>
<p>The were no checks when designating a new plumber, making it possible to set the plumber address to <code>address(0)</code>.</p>
<h3 id="recommendation-14">Recommendation</h3>
<p>It should not be possible to designate <code>address(0)</code> for the flush plumber. A two-step nomination and acceptance process should be used to change the flush plumber, so as to prevent tokens becoming stuck on the Zap contract.</p>
<h3 id="client-response-14">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/91a18f1ba5cbfff2b5646d0466ad39f30a5b2689">91a18f1</a>.</p>
<a name="IO-KWT-ZPO-013"></a><h2 id="io-kwt-zpo-013-burn-function-could-be-used-as-a-usdx-sweep-function" class="break-before"><strong>IO-KWT-ZPO-013</strong> <code>burn()</code> function could be used as a USDX sweep function</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/9e27e3f0c2c0ea9972c15f355f3de5f109785e08/src/Zap.sol#L548">Zap.sol#L548</a></td>
</tr>
</tbody>
</table>
<p>If any USDX is on the Zap contract, which could be the case with the accumulation of dust from <code>unwind()</code>, then a user could call <code>burn()</code> with an amount of 1 wei. This would transfer the Zap balance of USDX to the caller while burning 1 wei of USDX debt.</p>
<h3 id="recommendation-15">Recommendation</h3>
<p>The USDX balance of Zap should be checked before any funds are pulled from the caller. This initial balance should be subtracted from the final Zap USDX balance and only the difference should be paid back to the caller.</p>
<h3 id="client-response-15">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/a7e11d436b1bdfdbc5f1c352708d699d8750a488">a7e11d4</a>.</p>
<a name="IO-KWT-ZPO-014"></a><h2 id="io-kwt-zpo-014--tolerance-should-be-renamed-to--minamountout" class="break-before"><strong>IO-KWT-ZPO-014</strong> <code>_tolerance</code> should be renamed to <code>_minAmountOut</code></h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L87">Zap.sol#L87</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L119">Zap.sol#L119</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L160">Zap.sol#L160</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L207">Zap.sol#L207</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L256">Zap.sol#L256</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L301">Zap.sol#L301</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L428">Zap.sol#L428</a>, <a href="https://github.com/JaredBorders/zap/blob/8998193a525758be3e3669f3a1732b99e355e4cb/src/Zap.sol#L608">Zap.sol#L608</a></td>
</tr>
</tbody>
</table>
<p>The tolerance value is used as a slippage control in the Zap contract. This can be misleading since a “tolerance” is typically provided as a percentage but the spot market functions listed below expect the value as a <code>minAmountOut</code> and not a percentage:</p>
<ul>
<li><code>wrap()</code></li>
<li><code>unwrap()</code></li>
<li><code>buy()</code></li>
<li><code>sell()</code></li>
</ul>
<p>Calls to functions with a <code>_tolerance</code> passed as a percentage would likely succeed as the percentage value would be smaller than the expected <code>minAmountOut</code> for most actions.</p>
<h3 id="recommendation-16">Recommendation</h3>
<p><code>_tolerance</code> should be renamed to <code>_minAmountOut</code> for all relevant functions and renamed to <code>_maxAmountIn</code> in the <code>swapFor()</code> function.</p>
<h3 id="client-response-16">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/d761662fc88e8b0d370a7790c767a475cc5bb8bd">d761662</a>.</p>
<a name="IO-KWT-ZPO-015"></a><h2 id="io-kwt-zpo-015-assign-constant-variables-at-declaration" class="break-before"><strong>IO-KWT-ZPO-015</strong> Assign constant variables at declaration</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L42-L73">Zap.sol#L42-L73</a></td>
</tr>
</tbody>
</table>
<p>Some variables were defined as immutables and instantiated in the constructor, but with fixed values rather than parameters. These could be made constants and assigned at declaration instead.</p>
<h3 id="recommendation-17">Recommendation</h3>
<p>The following parameters should be made constants and assigned at declaration instead of in the constructor:
-<code>MODIFY_PERMISSION</code>
-<code>BURN_PERMISSION</code>
-<code>USDX_ID</code>
-<code>REFERRAL_CODE</code>
-<code>FEE_TIER</code></p>
<h3 id="client-response-17">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/969e9c052947c3d9f5ba5701c699d7e0ccf2a3bd">969e9c0</a>.</p>
<a name="IO-KWT-ZPO-016"></a><h2 id="io-kwt-zpo-016-fetch-synth-address-in-external-function" class="break-before"><strong>IO-KWT-ZPO-016</strong> Fetch synth address in external function</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L240">Zap.sol#L240</a></td>
</tr>
</tbody>
</table>
<p>The synth address could be retrieved in the external <code>buy()</code> function instead of the internal <code>_buy()</code> function as this is the only place it is required. This would simplify the internal function and bring its style in line with the <code>_sell()</code> function.</p>
<h3 id="client-response-18">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/274af46097001a6c1aa3feccd3686e663dbda9bd">274af46</a>.</p>
<a name="IO-KWT-ZPO-017"></a><h2 id="io-kwt-zpo-017-no-checks-for-zero-address-and-zero-amounts-in-token-transfers" class="break-before"><strong>IO-KWT-ZPO-017</strong> No checks for zero address and zero amounts in token transfers</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L478-L504">Zap.sol#L478-L504</a></td>
</tr>
</tbody>
</table>
<p>There were no checks for zero addresses and zero amounts in the <code>_pull()</code> and <code>_push()</code> functions. This could result in unintentional loss of funds should the underlying token implementation not check for these edge cases.</p>
<h3 id="recommendation-18">Recommendation</h3>
<p>The <code>_push()</code> and <code>_pull()</code> functions should revert if an amount of 0 is passed in or <code>address(0)</code> is passed to <code>_push()</code>.</p>
<h3 id="client-response-19">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/c3ecffabc2aded6e264372fddb78d60ea8306d47">c3ecffa</a>.</p>
<a name="IO-KWT-ZPO-018"></a><h2 id="io-kwt-zpo-018-error-catching-only-works-for-string-reverts" class="break-before"><strong>IO-KWT-ZPO-018</strong> Error catching only works for string reverts</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol">Zap.sol</a></td>
</tr>
</tbody>
</table>
<p>Multiple try/catch blocks were used when calling into Synthetix functions. The catch block used is <code>catch Error(string memory reason)</code> which would only catch <code>revert(string reason)</code> and <code>require(condition, "reason")</code> reverts. As all Synthetix code uses custom errors instead of strings, none of the reverts thrown by the protocol or its markets would be caught.</p>
<h3 id="recommendation-19">Recommendation</h3>
<p>Currently, there is no way to catch and decode arbitrary custom errors in a generic <code>catch</code> block. As such, these try/catch blocks can be removed.</p>
<h3 id="client-response-20">Client response</h3>
<p>Fixed in <a href="https://github.com/JaredBorders/zap/commit/0e559913094af938a20714b6a806ff4305ecebf5">0e55991</a>.</p>
<a name="IO-KWT-ZPO-021"></a><h2 id="io-kwt-zpo-021-unsafe-assumption-with-external-contract-integration" class="break-before"><strong>IO-KWT-ZPO-021</strong> Unsafe assumption with external contract integration</h2>
<table class="metadata">
<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-closed">Closed</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol">29bf803</a></td>
</tr>
</tbody>
</table>
<p>Zap does not enforce any slippage checks itself and relies on the checks of its integration partners. Should Synthetix or Odos not return tokens to Zap or return an incorrect amount, Zap may still assume the transaction is successful. Under normal use, this is not an issue, but in the case of catastrophic failure by the integrators, Zap users may be at risk of losing their funds.</p>
<h3 id="recommendation-20">Recommendation</h3>
<p>Zap should perform slippage checks on the amount of tokens returned from external integrations like Odos and Synthetix.</p>
<h3 id="client-response-21">Client response</h3>
<p>The team decided that the slippage check for integrators was not necessary and opted to not implement this recommendation.</p>
<h1 id="code-quality-improvement-suggestions">Code quality improvement suggestions</h1>
<p>Code improvement suggestions without security implications are listed below.</p>
<table class="codequality">
<thead>
<tr>
<th>#</th>
<th>Location</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><a href="https://github.com/JaredBorders/zap/blob/5941b2c67881373a971c5fa5142984bf131481c0/src/Zap.sol#L287">Zap.sol#L287</a></td>
<td><code>requestFlashLoan()</code> should be given a more descriptive name, for example <code>payDebtAndWithdrawCollateral()</code>.</td>
</tr>
<tr>
<td>2</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L166">Zap.sol#L166</a>, <a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L209">Zap.sol#L209</a></td>
<td>A synth is not only a representation of wrapped collateral. A synth is the Synthetix token representation of an asset with an accepted on-chain price oracle.</td>
</tr>
<tr>
<td>3</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L343">Zap.sol#L343</a></td>
<td>This line could be removed as a caller does not need to grant USDC approval to unwind.</td>
</tr>
<tr>
<td>4</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L346">Zap.sol#L346</a></td>
<td>This is the Synthetix spot market ID or synthId for the collateral, not the Synthetix market ID.</td>
</tr>
<tr>
<td>5</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L352">Zap.sol#L352</a></td>
<td><code>swapAmountIn</code> is no longer a slippage value. It is the amount intended to be swapped by Odos.</td>
</tr>
<tr>
<td>6</td>
<td><a href="https://github.com/JaredBorders/zap/blob/29bf803413be6fe7f02803e08e882cb062d56172/src/Zap.sol#L638">Zap.sol#L638</a></td>
<td>"is the expected" should be “is expected”.</td>
</tr>
<tr>
<td>7</td>
<td><a href="https://github.com/JaredBorders/zap/blob/87684d63e78c16042bda3099d4d305c93533e8c3/src/Zap.sol#L639">Zap.sol#L639</a></td>
<td>This should say: “@notice swap the input amount of tokens for USDC using Odos”.</td>
</tr>
</tbody>
</table>
<h3 id="client-response-22">Client response</h3>
<ol>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/fbc5a14e6bdf2dcfa5dd6a34d1f4c619047f3296">fbc5a14</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/242eb4ce1ac2e7a6e24a9d13938b63aeb99949d6">242eb4c</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/242eb4ce1ac2e7a6e24a9d13938b63aeb99949d6">242eb4c</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/242eb4ce1ac2e7a6e24a9d13938b63aeb99949d6">242eb4c</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/242eb4ce1ac2e7a6e24a9d13938b63aeb99949d6">242eb4c</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/c5970cff232055935508a32930457f9cd1dee901">c5970cf</a>.</li>
<li>Fixed in <a href="https://github.com/JaredBorders/zap/commit/91a18f1ba5cbfff2b5646d0466ad39f30a5b2689">91a18f1</a>.</li>
</ol>
<h1 id="specification">Specification</h1>
<p>The following section outlines the system's intended functionality at a high level, based on its implementation in the codebase. Any perceived points of conflict should be highlighted with the auditing team to determine the source of the discrepancy.</p>
<h2 id="overview-1">Overview</h2>
<p>Kwenta Zap was originally implemented to enable users to atomically facilitate exchanges between USDC and USDX. On top of this existing functionality, this implementation introduced functionality to perform the following actions through Zap:</p>
<ul>
<li>Buy / Sell Synthetix V3 synths</li>
<li>Pay debt with USDX for a perp account</li>
<li>Unwind perp accounts that have debt and non-USDX collateral</li>
<li>Withdraw collateral from a perp account</li>
<li>Perform Odos swaps to USDC</li>
</ul>
<p>All of these actions simplify front-end interaction from Kwenta as users only need to grant approvals and permission to the Zap contract and it orchestrates the listed actions on the user's behalf. This simplification can reduce user error as users are less likely to permit the wrong addresses. It is also easier to monitor permissions as users would only need to keep track of the Zap address if they use Kwenta.</p>
<h2 id="buying-and-selling-synthetix-v3-synths">Buying and selling Synthetix v3 synths</h2>
<p>Zap now allows users to buy and sell Synthetix V3 synths using USDX without needing to interact with the spot market directly. This can make Kwenta's frontend implementation simpler as users are only required to approve the Zap contract address for any action they may perform through Kwenta.</p>
<h2 id="perp-debt-and-collateral">Perp debt and collateral</h2>
<p>Through Zap, Synthetix Perp v3 accounts can pay any outstanding debt if they have USDX by approving the Zap contract and calling the relevant function.</p>
<p>If the user does not have USDX and their account has debt, they can use the unwind process which flash loans USDC from Aave, zaps it into USDX, and pays the debt. Once paid, the account's collateral can be withdrawn and some of it swapped to USDC to repay the flash loan. To perform this action, the perp account owner would need to grant Zap the modify collateral permission on perps.</p>
<p>Users can also withdraw collateral from the perp account using Zap if their account has no debt and they have permitted Zap to modify collateral.</p>
<h2 id="odos-swaps-to-usdc">Odos swaps to USDC</h2>
<p>The integration with Odos to enable the sale of assets to USDC enabled the unwinding of collateral and repayment of Aave flash loans. This functionality has been extended further to allow users to perform asset swaps into USDC through Zap, including multi-hop paths to minimize slippage losses. Callers would need to generate a swap path using Odos' API before being able to use the smart contract functionality.</p>
<h1 id="test-coverage-report">Test coverage report</h1>
<p>The coverage report of the provided tests on the final day of the audit is given below.</p>
<table>
<thead>
<tr>
<th>File</th>
<th>% Lines</th>
<th>% Statements</th>
<th>% Branches</th>
<th>% Funcs</th>
</tr>
</thead>
<tbody>
<tr>
<td>src/Zap.sol</td>
<td>99.06% (105/106)</td>
<td>99.21% (125/126)</td>
<td>22.22% (4/18)</td>
<td>100.00% (27/27)</td>
</tr>
<tr>
<td>src/utils/Flush.sol</td>
<td>100.00% (13/13)</td>
<td>100.00% (15/15)</td>
<td>11.11% (1/9)</td>
<td>100.00% (4/4)</td>
</tr>
<tr>
<td>src/utils/Reentrancy.sol</td>
<td>100.00% (2/2)</td>
<td>100.00% (2/2)</td>
<td>0.00% (0/2)</td>
<td>100.00% (2/2)</td>
</tr>
<tr>
<td>src/utils/SafeTransferERC20.sol</td>
<td>35.71% (5/14)</td>
<td>38.46% (5/13)</td>
<td>0.00% (0/2)</td>
<td>100.00% (3/3)</td>
</tr>
</tbody>
</table>
</article>
</main>
</div>