Nexus Mutual RWI Vault Smart Contract Audit

<script src="https://cdn.jsdelivr.net/npm/[email protected]/tex-mml-chtml.js" integrity="sha256-vRVWcOQgIgZwqXAy0HojsQITgghn7o9e0qe7KDF/JO8=" crossorigin="anonymous"></script>
<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');.codequality td{min-width:6em;} 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:10pt}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:9pt}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 [email protected]";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">Nexus Mutual RWI Vault Smart Contract Audit</div>
<div class="frontpage-subtitle">Nexus Mutual, 18 March 2026</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-nxm-rwi-018-removing-member-with-pending-redemption">IO-NXM-RWI-018 Removing member with pending redemption</a></li>
<li>
<a href="#io-nxm-rwi-001-no-period-validation-for-locked-deposits">IO-NXM-RWI-001 No period validation for locked deposits</a></li>
<li>
<a href="#io-nxm-rwi-002-can-lock-shares-when-paused">IO-NXM-RWI-002 Can lock shares when paused</a></li>
<li>
<a href="#io-nxm-rwi-003-extended-lock-period-can-exceed-maximum">IO-NXM-RWI-003 Extended lock period can exceed maximum</a></li>
<li>
<a href="#io-nxm-rwi-004-stuck-funds">IO-NXM-RWI-004 Stuck funds</a></li>
<li>
<a href="#io-nxm-rwi-016-loss-of-assets-due-to-rounding">IO-NXM-RWI-016 Loss of assets due to rounding</a></li>
<li>
<a href="#io-nxm-rwi-019-removing-member-with-pending-deposit">IO-NXM-RWI-019 Removing member with pending deposit</a></li>
<li>
<a href="#io-nxm-rwi-009-erc-7540-return-values">IO-NXM-RWI-009 ERC-7540 return values</a></li>
<li>
<a href="#io-nxm-rwi-011-check-effects-interactions-pattern">IO-NXM-RWI-011 Check-Effects-Interactions pattern</a></li>
<li>
<a href="#io-nxm-rwi-017-counter-not-incremented-when-redeeming-max-assets">IO-NXM-RWI-017 Counter not incremented when redeeming max assets</a></li>
<li>
<a href="#io-nxm-rwi-020-removed-members-cannot-withdraw-locked-shares">IO-NXM-RWI-020 Removed members cannot withdraw locked shares</a></li>
<li>
<a href="#io-nxm-rwi-024-no-bounds-validation-on-proposed-rate-change">IO-NXM-RWI-024 No bounds validation on proposed rate change</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="#vault">Vault</a></li>
<li>
<a href="#locking-shares">Locking shares</a></li>
<li>
<a href="#erc-7540-compliance">ERC-7540 compliance</a></li>
<li>
<a href="#governance">Governance</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 Nexus Mutual to perform a smart contract audit of their RWI Vault, an ERC-7540-inspired smart contract that enables select members to back and earn yield from off-chain insurance opportunities. One auditor conducted the initial audit between 29 September and 26 November 2025, using four audit days. Two follow-up reviews were conducted, each using one audit day: the first on 3 February 2026 covering rate calculation and naming changes, and the second on 18 March 2026 covering changes to the emergency pause mechanism and lock extension logic.</p>
<h4 id="overview">Overview</h4>
<p>The initial audit identified one high-risk, one medium-risk and five low-risk issues. Several informational issues and code quality improvement suggestions were also found. All findings from the initial audit were resolved or closed at its conclusion.</p>
<p>The high-risk finding was a potential redemption fullfillment denial of service in the event of a member being removed after they had requested a redemption. The medium-risk finding pertained to an issue in the share-locking mechanism, which allowed members to bypass the minimum and maximum lock periods defined in the contract.</p>
<p>The low-risk issues included the possibility of funds becoming stuck, shares being locked even when the share locking mechanism is paused, and the extended lock period exceeding the maximum lock period when editing an existing lock.</p>
<p>The follow-up reviews identified two additional low-risk issues: removed members being unable to withdraw locked shares, and missing bounds validation on proposed rate changes. The rate bounds issue was resolved during the review.</p>
<p>During the audits, the vault and registry contracts were renamed from <code>RWAVault</code> and <code>Registry</code> to <code>RWIVault</code> and <code>RWIRegistry</code> respectively. References to the original names in this report should be read as pertaining to their renamed counterparts.</p>
<table>
<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>1</td>
<td>1</td>
<td>5</td>
<td>2</td>
</tr>
<tr>
<td>Closed</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>2</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> RWI Vault</li>
<li><strong>Initial audit commit:</strong>  <a href="https://github.com/NexusMutual/rwi-vault/tree/ce47d1e0e7e59ebf3104fc76606a771497e2390e">ce47d1e</a></li>
<li><strong>Final review commit:</strong>  <a href="https://github.com/NexusMutual/rwi-vault/commit/326d4ab0b75acfbc0500a1bd021beee17955fa2a">326d4ab</a></li>
<li><strong>Files:</strong> ERC20.sol, ERC7540.sol, Locks.sol, RWIRegistry.sol (renamed from Registry.sol), RegistryAware.sol, RWIVault.sol (renamed from RWAVault.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 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>
<thead>
<tr>
<th>ID</th>
<th>Issue</th>
<th>Risk</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="#IO-NXM-RWI-018">IO-NXM-RWI-018</a></td>
<td>Removing member with pending redemption</td>
<td>High</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-001">IO-NXM-RWI-001</a></td>
<td>No period validation for locked deposits</td>
<td>Medium</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-002">IO-NXM-RWI-002</a></td>
<td>Can lock shares when paused</td>
<td>Low</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-003">IO-NXM-RWI-003</a></td>
<td>Extended lock period can exceed maximum</td>
<td>Low</td>
<td>Closed</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-004">IO-NXM-RWI-004</a></td>
<td>Stuck funds</td>
<td>Low</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-016">IO-NXM-RWI-016</a></td>
<td>Loss of assets due to rounding</td>
<td>Low</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-019">IO-NXM-RWI-019</a></td>
<td>Removing member with pending deposit</td>
<td>Low</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-020">IO-NXM-RWI-020</a></td>
<td>Removed members cannot withdraw locked shares</td>
<td>Low</td>
<td>Closed</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-024">IO-NXM-RWI-024</a></td>
<td>No bounds validation on proposed rate change</td>
<td>Low</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-009">IO-NXM-RWI-009</a></td>
<td>ERC-7540 return values</td>
<td>Informational</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-011">IO-NXM-RWI-011</a></td>
<td>Check-Effects-Interactions pattern</td>
<td>Informational</td>
<td>Resolved</td>
</tr>
<tr>
<td><a href="#IO-NXM-RWI-017">IO-NXM-RWI-017</a></td>
<td>Counter not incremented when redeeming max assets</td>
<td>Informational</td>
<td>Closed</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-NXM-RWI-018"></a><h2 id="io-nxm-rwi-018-removing-member-with-pending-redemption" class="break-before"><strong>IO-NXM-RWI-018</strong> Removing member with pending redemption</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-high">High</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L232">RWAVault.<wbr/>sol<wbr/>#L232</a></td>
</tr>
</tbody>
</table>
<p>If a member with a pending redemption request opts to leave or is removed by the operator, it will be impossible to process subsequent redemptions, as attempts to fulfill the previous member’s request will attempt to transfer assets to <code>address(0)</code> and thus revert.</p>
<p>It is also not possible to cancel the previous member’s request, as the function will attempt to return the shares to <code>address(0)</code> and revert.</p>
<p>The following proof of concept demonstrates this issue.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'can process redeem requests even if user is removed from registry'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">_requestRedeem</span><span class="p">(</span><span class="nx">user</span><span class="p">,</span> <span class="nx">userShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">registry</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">membershipManager</span><span class="p">).</span><span class="nx">removeMember</span><span class="p">(</span><span class="kr">await</span> <span class="nx">registry</span><span class="p">.</span><span class="nx">getMemberId</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">cancelRedeemRequest</span><span class="p">(</span><span class="mi">1</span><span class="p">)).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nx">revertedWithCustomError</span><span class="p">(</span><span class="nx">rwaVault</span><span class="p">,</span> <span class="s1">'ERC20InvalidReceiver'</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">userShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">fulfillRedeems</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">userShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre><h3 id="recommendation">Recommendation</h3>
<p><code>if (registry.<wbr/>get<wbr/>Member<wbr/>Address(redeem<wbr/>Request.<wbr/>member<wbr/>Id) == address(0)) continue;</code> should be added to the loop in <code>RWAVault::ful<wbr/>Fill<wbr/>Redeems()</code>.</p>
<h3 id="client-response">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/ca33cb630624744d201432b43af2dc29cbed61dc">ca33cb6</a>.</p>
<a name="IO-NXM-RWI-001"></a><h2 id="io-nxm-rwi-001-no-period-validation-for-locked-deposits" class="break-before"><strong>IO-NXM-RWI-001</strong> No period validation for locked deposits</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-medium">Medium</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L48">Locks.<wbr/>sol<wbr/>#L48</a></td>
</tr>
</tbody>
</table>
<p>The <code>Locks::lock<wbr/>Shares<wbr/>On<wbr/>Deposit(.<wbr/>.<wbr/>.<wbr/>)</code> function does not validate the lock period. Furthermore, the period is entirely under the caller’s control and not constrained by the <code>RWAVault</code> contract. This implementation allows members to lock their deposits for a period of arbitrary length, from <code>1 second</code> to far in excess of <code>MAX_<wbr/>LOCK_<wbr/>PERIOD</code>. The following proof of concept demonstrates this.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'locked deposit request allows users to lock shares without validating the lock period.'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">accounts</span><span class="o">:</span> <span class="p">{</span><span class="nx">members</span><span class="p">,</span> <span class="nx">vaultManager</span><span class="p">},</span> <span class="nx">contracts</span><span class="o">:</span> <span class="p">{</span><span class="nx">rwaVault</span><span class="p">,</span> <span class="nx">usdcMock</span><span class="p">,</span> <span class="nx">locks</span><span class="p">}</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">networkHelpers</span><span class="p">.</span><span class="nx">loadFixture</span><span class="p">(</span><span class="nx">setupFixture</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">members</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">depositAmount</span> <span class="o">=</span> <span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"1000"</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">expectedShares</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">convertToShares</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">requestDepositAndLock</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">duration</span><span class="p">.</span><span class="nx">seconds</span><span class="p">(</span><span class="mi">30</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">())).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">expectedShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// assets should be transferred to the vault manager directly
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">.</span><span class="nx">address</span><span class="p">)).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">())).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-1">Recommendation</h3>
<p>The <code>Locks::lock<wbr/>Shares<wbr/>On<wbr/>Deposit(.<wbr/>.<wbr/>.<wbr/>)</code> function should ensure that the lock period is greater than the minimum lock period and less than the maximum lock period.</p>
<h3 id="client-response-1">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/39ce5deb3ccea6a4d37d5ac26727f52b8ab6424a">39ce5de</a>.</p>
<a name="IO-NXM-RWI-002"></a><h2 id="io-nxm-rwi-002-can-lock-shares-when-paused" class="break-before"><strong>IO-NXM-RWI-002</strong> Can lock shares when paused</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L48">Locks.<wbr/>sol<wbr/>#L48</a></td>
</tr>
</tbody>
</table>
<p>The function <code>Locks::lock<wbr/>Shares<wbr/>On<wbr/>Deposit(.<wbr/>.<wbr/>.<wbr/>)</code> does not implement the <code>when<wbr/>Not<wbr/>Paused(PAUSE_<wbr/>LOCKS)</code> modifier, unlike <code>Locks::lock<wbr/>Shares(.<wbr/>.<wbr/>.<wbr/>)</code>. This would allow members to lock shares when depositing, even when the feature has been paused. This is shown in the proof of concept below.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">  <span class="nx">it</span><span class="p">(</span><span class="s1">'locked deposit request allows users to lock shares without checking if the locks contract is paused.'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">accounts</span><span class="o">:</span> <span class="p">{</span><span class="nx">members</span><span class="p">,</span> <span class="nx">vaultManager</span><span class="p">,</span> <span class="nx">emergencyAdmin</span><span class="p">},</span> <span class="nx">contracts</span><span class="o">:</span> <span class="p">{</span><span class="nx">rwaVault</span><span class="p">,</span> <span class="nx">usdcMock</span><span class="p">,</span> <span class="nx">locks</span><span class="p">,</span> <span class="nx">registry</span><span class="p">}</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">networkHelpers</span><span class="p">.</span><span class="nx">loadFixture</span><span class="p">(</span><span class="nx">setupFixture</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">members</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">depositAmount</span> <span class="o">=</span> <span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"1000"</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">expectedShares</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">convertToShares</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// pause the locks contract
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">PAUSE_LOCKS</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">expect</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nx">registry</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">emergencyAdmin</span><span class="p">).</span><span class="nx">setPauseConfig</span><span class="p">(</span><span class="nx">PAUSE_LOCKS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="nx">registry</span><span class="p">,</span> <span class="s1">'PauseConfigConfirmed'</span><span class="p">).</span><span class="nx">withArgs</span><span class="p">(</span><span class="nx">PAUSE_LOCKS</span><span class="p">,</span> <span class="nx">emergencyAdmin</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">requestDepositAndLock</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">duration</span><span class="p">.</span><span class="nx">seconds</span><span class="p">(</span><span class="mi">30</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">())).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">expectedShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// assets should be transferred to the vault manager directly
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">.</span><span class="nx">address</span><span class="p">)).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">())).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-2">Recommendation</h3>
<p><code>Locks::lock<wbr/>Shares<wbr/>On<wbr/>Deposit(.<wbr/>.<wbr/>.<wbr/>)</code> should implement the <code>when<wbr/>Not<wbr/>Paused(PAUSE_<wbr/>LOCKS)</code> modifier.</p>
<h3 id="client-response-2">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/39ce5deb3ccea6a4d37d5ac26727f52b8ab6424a">39ce5de</a>.</p>
<a name="IO-NXM-RWI-003"></a><h2 id="io-nxm-rwi-003-extended-lock-period-can-exceed-maximum" class="break-before"><strong>IO-NXM-RWI-003</strong> Extended lock period can exceed maximum</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-closed">Closed</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L58">Locks.<wbr/>sol<wbr/>#L58</a></td>
</tr>
</tbody>
</table>
<p>Through editing a lock, a lock’s period can be made to exceed the <code>MAX_<wbr/>LOCK_<wbr/>PERIOD</code>, as only the extension period is validated, not the new total duration. The following proof of concept shows how the maximum lock period can be exceeded.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'editing a lock extends the total extended duration to greater than max lock period.'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">accounts</span><span class="o">:</span> <span class="p">{</span><span class="nx">members</span><span class="p">},</span> <span class="nx">contracts</span><span class="o">:</span> <span class="p">{</span><span class="nx">rwaVault</span><span class="p">,</span> <span class="nx">usdcMock</span><span class="p">,</span> <span class="nx">locks</span><span class="p">}</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">networkHelpers</span><span class="p">.</span><span class="nx">loadFixture</span><span class="p">(</span><span class="nx">setupFixture</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">members</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">depositAmount</span> <span class="o">=</span> <span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"1000"</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">expectedShares</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">convertToShares</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">requestDepositAndLock</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">duration</span><span class="p">.</span><span class="nx">years</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">())).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">expectedShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">lockBeforeEdit</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">getMemberLock</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="nx">lockBeforeEdit</span><span class="p">.</span><span class="nx">period</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">duration</span><span class="p">.</span><span class="nx">years</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">networkHelpers</span><span class="p">.</span><span class="nx">time</span><span class="p">.</span><span class="nx">increase</span><span class="p">(</span><span class="nx">duration</span><span class="p">.</span><span class="nx">years</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">editLock</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">duration</span><span class="p">.</span><span class="nx">years</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span> <span class="c1">// total extended duration = 3 years
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">lockAfterEdit</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">locks</span><span class="p">.</span><span class="nx">getMemberLock</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="nx">lockAfterEdit</span><span class="p">.</span><span class="nx">period</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">duration</span><span class="p">.</span><span class="nx">years</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="nx">duration</span><span class="p">.</span><span class="nx">seconds</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="c1">// +1 second for next block time increase
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-3">Recommendation</h3>
<p>To prevent this, the updated <code>lock.<wbr/>period</code> should be validated instead of the extension period.</p>
<h3 id="client-response-3">Client response</h3>
<p>The development team clarified that this functionality is by design. The extension starting from the current timestamp must be less than 2 years; however, the total lasting period can be longer than 2 years.</p>
<h4 id="follow-up-review-note">Follow-up review note</h4>
<p>In commit <a href="https://github.com/NexusMutual/rwi-vault/commit/326d4ab">326d4ab</a>, the <code>edit<wbr/>Lock</code> function was changed from resetting the period from the current timestamp (<code>lock.<wbr/>period = passed<wbr/>Time + new<wbr/>Period</code>) to an additive extension (<code>lock.<wbr/>period += period</code>). The validation was also updated to check that the remaining time until expiry (<code>time<wbr/>Until<wbr/>End = lock.<wbr/>start<wbr/>Time + lock.<wbr/>period - block.<wbr/>timestamp</code>) falls within <code>[MIN_<wbr/>LOCK_<wbr/>PERIOD, MAX_<wbr/>LOCK_<wbr/>PERIOD]</code>, rather than validating the raw period input. The cumulative <code>lock.<wbr/>period</code> can still grow beyond <code>MAX_<wbr/>LOCK_<wbr/>PERIOD</code> through repeated near-expiry extensions, consistent with the original design decision. Fork testing confirmed this behaviour on the deployed contracts.</p>
<a name="IO-NXM-RWI-004"></a><h2 id="io-nxm-rwi-004-stuck-funds" class="break-before"><strong>IO-NXM-RWI-004</strong> Stuck funds</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L235">RWAVault.<wbr/>sol<wbr/>#L235</a></td>
</tr>
</tbody>
</table>
<p>The redeem functionality requires the vault manager to transfer USDC to the contract. As the number of assets to be withdrawn increases over time, it will be difficult for the vault manager to fund the contract with the exact amount of USDC needed for redemptions; therefore, they would likely overfund the contract.</p>
<p>The additional funds will become inaccessible unless the vault manager processes additional redemptions.</p>
<h3 id="recommendation-4">Recommendation</h3>
<p>To avoid funds being stuck in the contract temporarily, the <code>fulfil<wbr/>Redeems</code> function should use <code>safe<wbr/>Transfer<wbr/>From</code> to transfer the funds directly from the vault manager to the requestor (the vault manager would need to provide an allowance).</p>
<p>Additionally, a <code>sweep()</code> function, callable only by the vault manager, should be introduced, which transfers the vault’s asset balance to the manager if funds are accidentally transferred directly to the vault.</p>
<h3 id="client-response-4">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/18751010ec9b4bb1d07b1ee1fd58f014409753c8">1875101</a>. The <code>fulfil<wbr/>Redeems</code> function now uses <code>safe<wbr/>Transfer<wbr/>From</code> to transfer assets. The <code>sweep()</code> function will not be implemented because some queued assets are held in the contract until the vault manager approves the deposit. These assets could be lost if the function is implemented.</p>
<a name="IO-NXM-RWI-016"></a><h2 id="io-nxm-rwi-016-loss-of-assets-due-to-rounding" class="break-before"><strong>IO-NXM-RWI-016</strong> Loss of assets due to rounding</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/9351e36fb9840805482396a6a42d8f09db92900e/contracts/RWAVault.sol#L239">RWAVault.<wbr/>sol<wbr/>#L239</a></td>
</tr>
</tbody>
</table>
<p>When partially fulfilling redemptions, <code>fulfilled<wbr/>Shares</code> is rounded down, while the assets remain the same, resulting in members receiving more assets than they should.</p>
<p>For example, if <code>assets<wbr/>Left</code> is 1 wei, <code>convert<wbr/>To<wbr/>Shares(assets<wbr/>Left)</code> will result in zero if the vault has accrued any yield. The member will then receive 1 wei of assets while <code>fufilled<wbr/>Shares</code> remains unchanged.</p>
<p>The following proof of concept demonstrates this.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'redeeming 1 wei of shares does not decrease shares but increases assets due to rounding'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userAssetsBefore</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userSharesBefore</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">_requestRedeem</span><span class="p">(</span><span class="nx">user</span><span class="p">,</span> <span class="nx">userSharesBefore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">[</span><span class="nx">firstRequest</span><span class="p">]</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getRedeemRequests</span><span class="p">([</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Vault manager fulfills the redemption
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"1"</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">fulfillRedeems</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">[</span><span class="nx">updatedFirstRequest</span><span class="p">]</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getRedeemRequests</span><span class="p">([</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userAssetsAfter</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Verify that assets increased but fulfilled shares remained the same
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">expect</span><span class="p">(</span><span class="nx">userAssetsAfter</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">be</span><span class="p">.</span><span class="nx">gt</span><span class="p">(</span><span class="nx">userAssetsBefore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="nx">updatedFirstRequest</span><span class="p">.</span><span class="nx">fulfilledShares</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">firstRequest</span><span class="p">.</span><span class="nx">fulfilledShares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-5">Recommendation</h3>
<p>Assets should be recalculated as <code>assets = convert<wbr/>To<wbr/>Assets(shares)</code> and <code>assets<wbr/>Left</code> should be set to zero when partially fulfilling a redemption. <code>assets<wbr/>Left -= assets</code> should only be done in the case of a non-partial redemption in the loop.</p>
<h3 id="client-response-5">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/033d7fe8cd058dc748ecf7d56f3b3edeb0fcf8b9">033d7fe</a>.</p>
<a name="IO-NXM-RWI-019"></a><h2 id="io-nxm-rwi-019-removing-member-with-pending-deposit" class="break-before"><strong>IO-NXM-RWI-019</strong> Removing member with pending deposit</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L147">RWAVault.<wbr/>sol<wbr/>#L147</a></td>
</tr>
</tbody>
</table>
<p>If a member has a pending deposit request and opts to leave or the operator removes them, it won’t be possible to process the request or refund the user due to attempts to transfer assets or mint shares to <code>address(0)</code>. This will result in funds being stuck in the contract.</p>
<p>The following proof of concept demonstrates this.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'can cancel deposit after user is removed from registry'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">accounts</span><span class="o">:</span> <span class="p">{</span><span class="nx">members</span><span class="p">,</span> <span class="nx">vaultManager</span><span class="p">,</span> <span class="nx">membershipManager</span><span class="p">},</span> <span class="nx">contracts</span><span class="o">:</span> <span class="p">{</span><span class="nx">rwaVault</span><span class="p">,</span> <span class="nx">usdcMock</span><span class="p">,</span> <span class="nx">registry</span><span class="p">}</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">networkHelpers</span><span class="p">.</span><span class="nx">loadFixture</span><span class="p">(</span><span class="nx">setupFixture</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">members</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">depositAmount</span> <span class="o">=</span> <span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"1000"</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">setAssetCap</span><span class="p">(</span><span class="nx">parseUsdc</span><span class="p">(</span><span class="s2">"100"</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">depositAmount</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">requestDeposit</span><span class="p">(</span><span class="nx">depositAmount</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">registry</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">membershipManager</span><span class="p">).</span><span class="nx">removeMember</span><span class="p">(</span><span class="kr">await</span> <span class="nx">registry</span><span class="p">.</span><span class="nx">getMemberId</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">cancelDepositRequest</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-6">Recommendation</h3>
<p>A check should be added to <code>RWAVault::cancel<wbr/>Deposit<wbr/>Request()</code> so that if the <code>member<wbr/>Address == address(0)</code> the request is cancelled, but the remaining assets are transferred to the <code>vault<wbr/>Manager</code>.</p>
<h3 id="client-response-6">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/ca33cb630624744d201432b43af2dc29cbed61dc">ca33cb6</a>.</p>
<a name="IO-NXM-RWI-009"></a><h2 id="io-nxm-rwi-009-erc-7540-return-values" class="break-before"><strong>IO-NXM-RWI-009</strong> ERC-7540 return values</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/ERC7540.sol">ERC7540.<wbr/>sol</a></td>
</tr>
</tbody>
</table>
<p>Some of the ERC7540 functions revert with <code>Not<wbr/>Supported</code> or hardcoded values when in practice, they could return valid results.</p>
<h3 id="recommendation-7">Recommendation</h3>
<p>The following changes are recommended:</p>
<ul>
<li><code>pending<wbr/>Deposit<wbr/>Request</code> can return the amount of assets pending deposit. A special case for partially fulfilled deposits will need to be considered (could return <code>0</code> if the status is not <code>PENDING</code>).</li>
<li><code>pending<wbr/>Redeem<wbr/>Request</code> can return the amount of shares pending redemption.</li>
<li><code>max<wbr/>Deposit</code> can return the maximum amount of assets that can be transferred to the vault, factoring in the <code>asset<wbr/>Cap</code>.</li>
</ul>
<h3 id="client-response-7">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/0b08564dfd884a809995efb912218c63e187b6b2">0b08564</a>. <code>max<wbr/>Deposit</code> will remain uncapped because users can still deposit and send assets to the queue more than what is defined by <code>asset<wbr/>Cap</code>.</p>
<a name="IO-NXM-RWI-011"></a><h2 id="io-nxm-rwi-011-check-effects-interactions-pattern" class="break-before"><strong>IO-NXM-RWI-011</strong> Check-Effects-Interactions pattern</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L152">RWAVault.<wbr/>sol<wbr/>#L152</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L177">RWAVault.<wbr/>sol<wbr/>#L177</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L182">RWAVault.<wbr/>sol<wbr/>#L182</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L235">RWAVault.<wbr/>sol<wbr/>#L235</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L126">RWAVault.<wbr/>sol<wbr/>#L126</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L209">RWAVault.<wbr/>sol<wbr/>#L209</a></td>
</tr>
</tbody>
</table>
<p>Although the USDC token does not provide a manner to perform a reentrancy attack, it is advisable to follow the Check-Effects-Interactions security principle where sensible.</p>
<h3 id="recommendation-8">Recommendation</h3>
<p>The following changes are recommended:</p>
<ul>
<li><code>cancel<wbr/>Deposit<wbr/>Request</code> should perform the <code>safe<wbr/>Transfer</code> after all internal state changes (just before emitting <code>Deposit<wbr/>Request<wbr/>Canceled</code>)</li>
<li><code>_<wbr/>fulfill<wbr/>Deposit</code> should perform all transfers after all internal state changes and interactions with the <code>Locks</code> contract (just before emitting <code>Deposit<wbr/>Fulfilled</code>).</li>
<li><code>fulfill<wbr/>Redeems</code> should perform the <code>safe<wbr/>Transfer</code> after all internal state changes within the loop (before emitting <code>Redeem<wbr/>Fulfilled</code>).</li>
<li><code>_<wbr/>request<wbr/>Deposit</code> should perform the  <code>safe<wbr/>Transfer<wbr/>From</code> before all internal state changes (before <code>deposit<wbr/>Request<wbr/>Next<wbr/>Id++</code>).</li>
<li><code>request<wbr/>Redeem</code> should perform the <code>safe<wbr/>Transfer<wbr/>From</code> before <code>redeem<wbr/>Request<wbr/>Nex<wbr/>Id++</code>.</li>
</ul>
<h3 id="client-response-8">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/7d3d4be64d55b50abbde0d9ca1da6bbf319cf73a">7d3d4be</a>.</p>
<a name="IO-NXM-RWI-017"></a><h2 id="io-nxm-rwi-017-counter-not-incremented-when-redeeming-max-assets" class="break-before"><strong>IO-NXM-RWI-017</strong> Counter not incremented when redeeming max assets</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-informational">Informational</td>
<td class="status-closed">Closed</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/9351e36fb9840805482396a6a42d8f09db92900e/contracts/RWAVault.sol#L237">RWAVault.<wbr/>sol<wbr/>#L237</a></td>
</tr>
</tbody>
</table>
<p>When a member redeems the maximum asset amount specified by the vault manager, the redemption request’s status is updated to <code>FULFILLED</code> but <code>redeem<wbr/>Request<wbr/>Next<wbr/>Fulfill<wbr/>Id</code> is not incremented. This will cause the function to perform unnecessary checks and waste a small amount of gas on the next round of fulfillments, as the first request will already be fulfilled.</p>
<p>The following proof of concept demonstrates this issue:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nx">it</span><span class="p">(</span><span class="s1">'redeemRequestNextFulfillId is not incremented when assets redeemed equal maxTotalAssets'</span><span class="p">,</span> <span class="kr">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userAssetsBefore</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userShares1</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Disable automining
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">"evm_setAutomine"</span><span class="p">,</span> <span class="p">[</span><span class="kc">false</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Request redemption
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">userShares1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tx1</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">user</span><span class="p">).</span><span class="nx">requestRedeem</span><span class="p">(</span><span class="nx">userShares1</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">storageSlot</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span> <span class="c1">// storage slot for `redeemRequestNextFulfillId`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">initialFulfillId</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">getStorage</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">storageSlot</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`initialFulfillIdInt: </span><span class="si">${</span><span class="nx">BigInt</span><span class="p">(</span><span class="nx">initialFulfillId</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// Vault manager fulfills exactly maxTotalAssets
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">maxTotalAssets</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">convertToAssets</span><span class="p">(</span><span class="nx">userShares1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">approve</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">maxTotalAssets</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tx2</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vaultManager</span><span class="p">).</span><span class="nx">fulfillRedeems</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">maxTotalAssets</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Manually mine a block
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">"evm_mine"</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Re-enable automining
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="s2">"evm_setAutomine"</span><span class="p">,</span> <span class="p">[</span><span class="kc">true</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Fetch the block details for both transactions
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">receipt1</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">tx1</span><span class="p">.</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">receipt2</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">tx2</span><span class="p">.</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">receipt1BlockNumber</span> <span class="o">=</span> <span class="nx">receipt1</span> <span class="o">?</span> <span class="nx">receipt1</span><span class="p">.</span><span class="nx">blockNumber</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">receipt2BlockNumber</span> <span class="o">=</span> <span class="nx">receipt2</span> <span class="o">?</span> <span class="nx">receipt2</span><span class="p">.</span><span class="nx">blockNumber</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">receipt1BlockNumber</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">receipt2BlockNumber</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">"Failed to fetch transaction receipts or block numbers."</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">block1</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">getBlock</span><span class="p">(</span><span class="nx">receipt1BlockNumber</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">block2</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">getBlock</span><span class="p">(</span><span class="nx">receipt2BlockNumber</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Compare the timestamps
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Timestamp for tx1: </span><span class="si">${</span><span class="nx">block1</span><span class="o">?</span><span class="p">.</span><span class="nx">timestamp</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`Timestamp for tx2: </span><span class="si">${</span><span class="nx">block2</span><span class="o">?</span><span class="p">.</span><span class="nx">timestamp</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">block1</span><span class="o">?</span><span class="p">.</span><span class="nx">timestamp</span> <span class="o">===</span> <span class="nx">block2</span><span class="o">?</span><span class="p">.</span><span class="nx">timestamp</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Both transactions have the same timestamp."</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Transactions have different timestamps."</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"block1?.timestamp"</span><span class="p">,</span> <span class="nx">block1</span><span class="o">?</span><span class="p">.</span><span class="nx">timestamp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">//verify assets redeemed is equal to max total assets
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="p">[</span><span class="nx">updatedFirstRequest</span><span class="p">]</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getRedeemRequests</span><span class="p">([</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">expect</span><span class="p">(</span><span class="nx">updatedFirstRequest</span><span class="p">.</span><span class="nx">status</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">RequestStatus</span><span class="p">.</span><span class="nx">FULFILLED</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">userAssetsAfter</span> <span class="o">=</span> <span class="nx">BigInt</span><span class="p">(</span><span class="kr">await</span> <span class="nx">usdcMock</span><span class="p">.</span><span class="nx">balanceOf</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">address</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">assetsRedeemed</span> <span class="o">=</span> <span class="nx">userAssetsAfter</span> <span class="o">-</span> <span class="nx">userAssetsBefore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">assert</span><span class="p">(</span><span class="nx">assetsRedeemed</span> <span class="o">===</span> <span class="nx">maxTotalAssets</span><span class="p">,</span> <span class="sb">`Assets redeemed (</span><span class="si">${</span><span class="nx">assetsRedeemed</span><span class="si">}</span><span class="sb">) do not equal maxTotalAssets (</span><span class="si">${</span><span class="nx">maxTotalAssets</span><span class="si">}</span><span class="sb">)`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"assetsRedeemed"</span><span class="p">,</span> <span class="nx">assetsRedeemed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"maxTotalAssets"</span><span class="p">,</span> <span class="nx">assetsRedeemed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Fetch the storage position again
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">finalFulfillId</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">ethers</span><span class="p">.</span><span class="nx">provider</span><span class="p">.</span><span class="nx">getStorage</span><span class="p">(</span><span class="kr">await</span> <span class="nx">rwaVault</span><span class="p">.</span><span class="nx">getAddress</span><span class="p">(),</span> <span class="nx">storageSlot</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`finalFulfillId: </span><span class="si">${</span><span class="nx">BigInt</span><span class="p">(</span><span class="nx">finalFulfillId</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</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="c1">// Verify that redeemRequestNextFulfillId has not been incremented
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">expect</span><span class="p">(</span><span class="nx">initialFulfillId</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">finalFulfillId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span></code></pre><h3 id="recommendation-9">Recommendation</h3>
<p>A possible fix would be to remove the increment from the <code>for</code> statement and increment <code>request<wbr/>Id</code> when setting the status to <code>Request<wbr/>Status.<wbr/>FULFILLED</code></p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="n">requestId</span> <span class="o">=</span> <span class="n">redeemRequestFulfillId</span><span class="p">;</span> <span class="n">requestId</span> <span class="o"><=</span> <span class="n">maxRequestId</span><span class="p">;</span> <span class="p">)</span> <span class="p">{</span>
</span></span></code></pre><h3 id="client-response-9">Client response</h3>
<p>The development team acknowledged this issue but will leave the functionality as-is, due to the low gas impact and low probability of the scenario occurring.</p>
<a name="IO-NXM-RWI-020"></a><h2 id="io-nxm-rwi-020-removed-members-cannot-withdraw-locked-shares" class="break-before"><strong>IO-NXM-RWI-020</strong> Removed members cannot withdraw locked shares</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-closed">Closed</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/4c317b1/contracts/Locks.sol#L81-L93">Locks.<wbr/>sol<wbr/>#L81-L93</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/4c317b1/contracts/RegistryAware.sol#L48-L52">Registry<wbr/>Aware.<wbr/>sol<wbr/>#L48-L52</a></td>
</tr>
</tbody>
</table>
<p>The <code>withdraw<wbr/>Shares</code> function in the <code>Locks</code> contract calls <code>get<wbr/>Active<wbr/>Member<wbr/>Id(msg.<wbr/>sender)</code>, which requires the caller to be a registered member. If a member removes themselves (or is removed by the MembershipOperator) while they have active locks, the member can never call <code>withdraw<wbr/>Shares</code> because their address no longer resolves to a valid member ID. The locked shares remain in the <code>Locks</code> contract.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="kd">function</span> <span class="nf">withdrawShares</span><span class="p">(</span><span class="kt">uint</span> <span class="n">lockId</span><span class="p">)</span> <span class="k">external</span> <span class="n">whenNotPaused</span><span class="p">(</span><span class="n">PAUSE_LOCKS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint</span> <span class="n">memberId</span> <span class="o">=</span> <span class="n">getActiveMemberId</span><span class="p">(</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">);</span>  <span class="c1">// @audit reverts if member was removed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="n">Lock</span> <span class="k">memory</span> <span class="n">lock</span> <span class="o">=</span> <span class="n">memberLocks</span><span class="p">[</span><span class="n">memberId</span><span class="p">][</span><span class="n">lockId</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">require</span><span class="p">(</span><span class="n">lock</span><span class="p">.</span><span class="n">shares</span> <span class="o">></span> <span class="mi">0</span><span class="p">,</span> <span class="n">LockDoesntExist</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span><span class="p">(</span><span class="nb">block</span><span class="p">.</span><span class="nb">timestamp</span> <span class="o">>=</span> <span class="n">lock</span><span class="p">.</span><span class="n">startTime</span> <span class="o">+</span> <span class="n">lock</span><span class="p">.</span><span class="n">period</span><span class="p">,</span> <span class="n">NotExpired</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">delete</span> <span class="n">memberLocks</span><span class="p">[</span><span class="n">memberId</span><span class="p">][</span><span class="n">lockId</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">IERC20</span><span class="p">(</span><span class="n">vault</span><span class="p">).</span><span class="n">safeTransfer</span><span class="p">(</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">,</span> <span class="n">lock</span><span class="p">.</span><span class="n">shares</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">emit</span> <span class="n">SharesWithdrawn</span><span class="p">(</span><span class="n">memberId</span><span class="p">,</span> <span class="n">lockId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre><p>There is no administrative function to withdraw shares on behalf of a removed member, and re-registering the same address as a new member would assign a different member ID, leaving the original locks inaccessible.</p>
<h3 id="recommendation-10">Recommendation</h3>
<p>An administrative function should be added to the <code>Locks</code> contract that allows the Governor or VaultOperator to withdraw expired locks on behalf of a removed member to a specified address.</p>
<h3 id="client-response-10">Client response</h3>
<p>This issue has been closed without action. The Locks contract is deployed behind a proxy and a recovery function can be introduced through a governance approved upgrade, should the need arise.</p>
<a name="IO-NXM-RWI-024"></a><h2 id="io-nxm-rwi-024-no-bounds-validation-on-proposed-rate-change" class="break-before"><strong>IO-NXM-RWI-024</strong> No bounds validation on proposed rate change</h2>
<table class="metadata">

<tbody>
<tr>
<td class="rating-low">Low</td>
<td class="status-resolved">Resolved</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/4c317b1/contracts/RWIVault.sol#L90-L100">RWIVault.<wbr/>sol<wbr/>#L90-L100</a></td>
</tr>
</tbody>
</table>
<p>The <code>propose<wbr/>Base<wbr/>Rate<wbr/>Change</code> function (originally named <code>propose<wbr/>Base<wbr/>Apy<wbr/>Change</code>) accepted any value for <code>proposal<wbr/>Rate</code> without validating it against meaningful bounds. A VaultOperator signer could propose a rate below <code>WAD</code> (1e18), which would cause share values to decrease over time as the compounding rate falls below unity, effectively imposing a negative yield on all depositors. Conversely, an excessively high rate could produce unrealistic APY, inflating share values and distorting the asset-to-share conversion. The only validation present was on <code>proposal<wbr/>Activation<wbr/>Time</code> meeting the 90-day minimum notice period.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1">// Before fix (commit 5099ff7)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nf">proposeBaseApyChange</span><span class="p">(</span><span class="kt">uint</span> <span class="n">proposalRate</span><span class="p">,</span> <span class="kt">uint</span> <span class="n">proposalActivationTime</span><span class="p">)</span> <span class="k">external</span> <span class="n">only</span><span class="p">(</span><span class="n">A_VAULT_OPERATOR</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">require</span><span class="p">(</span><span class="n">proposalActivationTime</span> <span class="o">></span> <span class="nb">block</span><span class="p">.</span><span class="nb">timestamp</span> <span class="o">+</span> <span class="n">MIN_APY_PROPOSAL_TIME</span><span class="p">,</span> <span class="n">ProposalActivationTimeTooSoon</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// @audit no validation on proposalRate
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">apyConfig</span><span class="p">.</span><span class="n">proposedRate</span> <span class="o">=</span> <span class="n">proposalRate</span><span class="p">.</span><span class="n">toUint64</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">apyConfig</span><span class="p">.</span><span class="n">proposedActivationTime</span> <span class="o">=</span> <span class="n">proposalActivationTime</span><span class="p">.</span><span class="n">toUint32</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre><h3 id="recommendation-11">Recommendation</h3>
<p>A validation check should be added to ensure the proposed rate is at least <code>WAD</code> (no negative growth) and that the annualised APY does not exceed a reasonable upper bound.</p>
<h3 id="client-response-11">Client response</h3>
<p>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/667f52c">667f52c</a>. Added <code>require(proposal<wbr/>Rate >= WAD && proposed<wbr/>Apy <= MAX_<wbr/>APY, Invalid<wbr/>Rate())</code> where <code>MAX_<wbr/>APY</code> is set to <code>1.<wbr/>5e18</code> (150%).</p>
<h1 id="code-quality-improvement-suggestions">Code quality improvement suggestions</h1>
<p>Code improvement suggestions without security implications are listed below.</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>Location</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td>IO-NXM-RWI-005</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L63">Locks.<wbr/>sol<wbr/>#L63</a></td>
<td>When specifying a zero amount for <code>top<wbr/>Up<wbr/>Shares</code>, it should not be necessary to call <code>transfer<wbr/>From</code>. Recommend adding the following:<br/> <code>if (top<wbr/>Up<wbr/>Shares > 0) IERC20(vault).<wbr/>safe<wbr/>Transfer<wbr/>From(msg.<wbr/>sender, address(this), top<wbr/>Up<wbr/>Shares);</code></td>
</tr>
<tr>
<td>IO-NXM-RWI-006</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Registry.sol#L207">Registry.<wbr/>sol<wbr/>#L207</a></td>
<td>The Governor can remove itself from the registry. Recommend validating that the <code>index != C_<wbr/>GOVERNOR</code> in <code>remove<wbr/>Contract</code></td>
</tr>
<tr>
<td>IO-NXM-RWI-007</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Registry.sol#L53">Registry.<wbr/>sol<wbr/>#L53</a></td>
<td>In <code>Registry::set<wbr/>Pause<wbr/>Config(.<wbr/>.<wbr/>.<wbr/>)</code>, a <code>Pause<wbr/>Config<wbr/>Confirmed</code> event is emitted. The name of the event does not match the pattern used in the contract and should rather be updated to be <code>Pause<wbr/>Config<wbr/>Set</code>. The main NXM protocol Registry requires two emergency admins – a proposer and a confirmer – to toggle the pause configuration, whereas this version of the Registry requires only a single Emergency Admin.</td>
</tr>
<tr>
<td>IO-NXM-RWI-008</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L22">Locks.<wbr/>sol<wbr/>#L22</a></td>
<td>The <code>constructor</code> for the <code>Locks</code> contract dynamically queries the Registry to determine the <code>vault</code> address. The <code>vault</code> address is stored as an <code>immutable</code> and cannot be changed afterward. This pattern unnecessarily complicates the deployment process because the <code>vault</code> must first be deployed and registered with the Registry before the <code>Locks</code> contract can be deployed and registered. By passing the <code>vault</code> as a constructor argument, the contracts can be deployed first and then registered in tandem.</td>
</tr>
<tr>
<td>IO-NXM-RWI-010</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L289">RWAVault.<wbr/>sol<wbr/>#L289</a></td>
<td>The ERC-7540 specification for <code>total<wbr/>Assets()</code> includes the following: “SHOULD include any compounding that occurs from yield.” To match the specification, the function should instead return the current asset value of the vault’s total supply, e.g., <code>convert<wbr/>To<wbr/>Assets(total<wbr/>Supply())</code></td>
</tr>
<tr>
<td>IO-NXM-RWI-012</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/Locks.sol#L33">Locks.<wbr/>sol<wbr/>#L33</a></td>
<td>When calling <code>Locks:lock<wbr/>Shares()</code>, the function does not return the <code>lock<wbr/>Id</code> for the newly locked shares, and as the <code>member<wbr/>Locks</code> mapping is private, there is no way to query the Id before creation. The <code>Locks:lock<wbr/>Shares()</code> function should return the <code>lock<wbr/>Id</code> for the newly locked shares.</td>
</tr>
<tr>
<td>IO-NXM-RWI-013</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L17">RWAVault.<wbr/>sol<wbr/>#L17</a></td>
<td>The <code>RWAVault</code> contains a typo in the declaration for the variable for <code>BPS</code>, which is set to <code>100_<wbr/>00</code>. The <code>BPS</code> variable should be set to <code>10_<wbr/>000</code> as it is easier to read and the units are grouped correctly.</td>
</tr>
<tr>
<td>IO-NXM-RWI-014</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L24">RWAVault.<wbr/>sol<wbr/>#L24</a></td>
<td>The <code>RWAVault</code> contains a typo in the declaration of the variable to track redeem request IDs, which was named <code>redeem<wbr/>Request<wbr/>Nex<wbr/>Id</code> but should be <code>redeem<wbr/>Request<wbr/>Next<wbr/>Id</code>.</td>
</tr>
<tr>
<td>IO-NXM-RWI-015</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/ce47d1e0e7e59ebf3104fc76606a771497e2390e/contracts/RWAVault.sol#L225">RWAVault.<wbr/>sol<wbr/>#L225</a></td>
<td>In <code>fulfill<wbr/>Redeems</code>, there is no validation that <code>until<wbr/>Request<wbr/>Id</code> is within the bounds of existing requests. This will result in an <code>ERC20Invalid<wbr/>Receiver</code> revert when specifying an <code>until<wbr/>Request<wbr/>Id</code> higher than <code>redeem<wbr/>Request<wbr/>Next<wbr/>Id - 1</code>. Recommend using the minimum between <code>until<wbr/>Request<wbr/>Id</code> and <code>redeem<wbr/>Request<wbr/>Next<wbr/>Id - 1</code> to prevent attempts to process non-existent requests, e.g.: <code>until<wbr/>Request<wbr/>Id = until<wbr/>Request<wbr/>Id < redeem<wbr/>Request<wbr/>Next<wbr/>Id ? until<wbr/>Request<wbr/>Id: redeem<wbr/>Request<wbr/>Next<wbr/>Id - 1;</code></td>
</tr>
<tr>
<td>IO-NXM-RWI-025</td>
<td><a href="https://github.com/NexusMutual/rwi-vault/blob/4c317b1/contracts/RWIVault.sol">RWIVault.<wbr/>sol</a>, <a href="https://github.com/NexusMutual/rwi-vault/blob/4c317b1/contracts/interfaces/IRWIVault.sol">IRWIVault.<wbr/>sol</a></td>
<td>The codebase used “APY” terminology throughout its structs, functions, events, and state variables (<code>Base<wbr/>Apy<wbr/>Config</code>, <code>propose<wbr/>Base<wbr/>Apy<wbr/>Change</code>, <code>execute<wbr/>Base<wbr/>Apy<wbr/>Change</code>, <code>Base<wbr/>Apy<wbr/>Change<wbr/>Proposed</code>, <code>MIN_<wbr/>APY_<wbr/>PROPOSAL_<wbr/>TIME</code>) despite these values representing a per-second compounding rate rather than an annual percentage yield. Only <code>get<wbr/>Base<wbr/>Apy</code> actually returns an annualized value. Recommend renaming all references to use “rate” terminology, retaining <code>get<wbr/>Base<wbr/>Apy</code> as the sole APY-denominated accessor.</td>
</tr>
</tbody>
</table>
<h3 id="client-response-12">Client response</h3>
<dl>
<dt>IO-NXM-RWI-005</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/a6b561c04cc98b82404e8ff53c0a42eb5e6399a6">a6b561c</a>.</dd>
<dt>IO-NXM-RWI-006</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/0c921661685d97088b03972ee408526b88ecd132">0c92166</a>.</dd>
<dt>IO-NXM-RWI-007</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/0c921661685d97088b03972ee408526b88ecd132">0c92166</a>.</dd>
<dt>IO-NXM-RWI-008</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/a6b561c04cc98b82404e8ff53c0a42eb5e6399a6">a6b561c</a>.</dd>
<dt>IO-NXM-RWI-010</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/9c83e591dbf451650158b79a69063f70bcd8fcbf">9c83e59</a>.</dd>
<dt>IO-NXM-RWI-012</dt>
<dd>Acknowledged without any fix. There is a public getter for the member locks; the next ID can be determined from there, but it should not be important in general.</dd>
<dt>IO-NXM-RWI-013</dt>
<dd>Acknowledged without any fix. This is not a typo; it is a subjective preference, as it represents one hundred percent (100_00 => 100.00%).</dd>
<dt>IO-NXM-RWI-014</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/8dee0802d1e70fff377f53823a83a3d88c07f993">8dee080</a>.</dd>
<dt>IO-NXM-RWI-015</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/eee04c16773bfb6442acf978a68f0b6598f414ea">eee04c1</a>.</dd>
<dt>IO-NXM-RWI-025</dt>
<dd>Fixed in <a href="https://github.com/NexusMutual/rwi-vault/commit/667f52c">667f52c</a>.</dd>
</dl>
<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="vault">Vault</h2>
<p>The RWI Vault system is designed to allow selected Nexus Mutual members, mainly institutional investors, to earn returns on their USDC deposits. The system comprises several smart contracts that manage deposits, withdrawals, LP share locking, and the distribution of additional returns. The vault adheres to the ERC-7540 standard for asynchronous deposits and redemptions, with some modifications to enhance the user experience.</p>
<p>The vault only allows deposits from whitelisted members, and depositors receive LP tokens in exchange for their USDC deposits. The vault has a cap on total deposits, which the vault manager can adjust as needed. If the total deposits exceed the cap, deposits are marked as pending for vault manager approval. Pending deposits do not earn interest until approved. The vault manager can approve the deposit in full, partially approve it, or reject it entirely. In the case of partial approval, LP tokens are minted for the approved amount, and the remaining USDC is returned to the depositor.</p>
<p>The vault accrues value at a fixed annual percentage yield (APY), which is calculated on a per-second basis using the formula <code>rate = start<wbr/>Rate * rate<wbr/>Per<wbr/>Second ^ time<wbr/>Passed</code>. The vault operator can propose changes to the rate, which take effect after a 90-day notice period has elapsed. The proposed rate must be at least <code>WAD</code> (1e18) and produce an annualised APY of at most 150%. Anyone can execute a pending rate change once the activation time has passed. Depositors can request withdrawals immediately after depositing, provided their shares are not locked; no default lock-up period is enforced on deposits. Members must opt in to lock their shares. Withdrawals are processed asynchronously, adhering to the ERC-7540 standard.</p>
<h2 id="locking-shares">Locking shares</h2>
<p>The system includes a mechanism for locking shares, which allows depositors to earn additional returns from surplus distributions by insurance partners. Rewards are distributed to depositors who lock their tokens between the effective dates of distribution. The locking mechanism enforces a minimum lock period of 30 days and a maximum lock period of 732 days (approximately two years). Lock periods can be extended additively via <code>edit<wbr/>Lock</code>, but the remaining time after the edit must be at least 30 days and at most 732 days. Members can top up shares to an existing lock when editing. Depositors can also choose to lock shares immediately when requesting a deposit.</p>
<h2 id="erc-7540-compliance">ERC-7540 compliance</h2>
<p>The vault implements the ERC-7540 standard for asynchronous deposits and redemptions, with deviations to improve user experience. Shares are directly transferred to user wallets upon deposit fulfillment, eliminating the need for users to claim them. This deviation is justified as the vault is intended for a limited number of whitelisted members.</p>
<h2 id="governance">Governance</h2>
<p>The registry contract (<code>RWIRegistry</code>, renamed from <code>Registry</code>) manages contracts, roles, and whitelisted members. The membership operator adds members, and both members and the membership operator can update member addresses or withdraw memberships. Members are referenced by their IDs rather than their addresses.</p>
<p>The system defines several roles to manage its operations:</p>
<dl>
<dt><code>C_<wbr/>GOVERNOR</code></dt>
<dd>A multisig role (three-of-five) with the ability to upgrade all proxy contracts, deploy new contracts, manage the contract registry, and set emergency admins.</dd>
<dt><code>A_<wbr/>VAULT_<wbr/>OPERATOR</code></dt>
<dd>Responsible for vault maintenance, including setting the asset cap, proposing rate changes, fulfilling deposit and redeem requests, canceling requests, and distributing rewards via the <code>Locks</code> contract. This will be configured as a three-of-five multisig.</dd>
<dt><code>A_<wbr/>MEMBERSHIP_<wbr/>OPERATOR</code></dt>
<dd>Responsible for adding members. Currently held by the same multisig as the VaultOperator.</dd>
<dt><code>Emergency<wbr/>Admin</code></dt>
<dd>A role with the ability to pause all operations or specific functionality (vault, locks, or global) in the event of an emergency. Pausing requires two distinct emergency admins: one to propose and a different one to confirm.</dd>
</dl>
<h1 id="test-coverage-report">Test coverage report</h1>
<p>The coverage report of the provided tests as of the final review is given below.</p>
<table>
<thead>
<tr>
<th>File Path</th>
<th>Line %</th>
<th>Statement %</th>
</tr>
</thead>
<tbody>
<tr>
<td>contracts/ERC20.sol</td>
<td>97.47</td>
<td>73.91</td>
</tr>
<tr>
<td>contracts/ERC7540.sol</td>
<td>21.74</td>
<td>21.74</td>
</tr>
<tr>
<td>contracts/Locks.sol</td>
<td>100.00</td>
<td>100.00</td>
</tr>
<tr>
<td>contracts/RegistryAware.sol</td>
<td>88.24</td>
<td>86.67</td>
</tr>
<tr>
<td>contracts/RWIVault.sol</td>
<td>96.57</td>
<td>88.54</td>
</tr>
<tr>
<td>contracts/UpgradeableProxy.sol</td>
<td>35.48</td>
<td>52.94</td>
</tr>
<tr>
<td>contracts/RWIRegistry.sol</td>
<td>95.76</td>
<td>95.15</td>
</tr>
</tbody>
</table>
<p>Overall, test coverage was found to be satisfactory. <code>Locks.<wbr/>sol</code> has 100% line and statement coverage, and <code>RWIRegistry.<wbr/>sol</code> improved significantly from the initial audit (38.10% to 95.76% line coverage) following the addition of dedicated unit tests. <code>Upgradeable<wbr/>Proxy.<wbr/>sol</code> has the lowest in-scope coverage at 35.48%, as <code>transfer<wbr/>Proxy<wbr/>Ownership</code>, the delegation logic, and the <code>fallback</code>/<code>receive</code> functions are not exercised by the test suite.</p>

</article>
</main>
</div>

Secure your system.
Request a service
Start Now