mirror of
https://github.com/luxfi/audits.git
synced 2026-11-03 06:36:02 +00:00
1050 lines
33 KiB
TeX
1050 lines
33 KiB
TeX
% Options for packages loaded elsewhere
|
|
\PassOptionsToPackage{unicode}{hyperref}
|
|
\PassOptionsToPackage{hyphens}{url}
|
|
\documentclass[
|
|
]{article}
|
|
\usepackage{xcolor}
|
|
\usepackage{amsmath,amssymb}
|
|
\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
|
|
\usepackage{iftex}
|
|
\ifPDFTeX
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage{textcomp} % provide euro and other symbols
|
|
\else % if luatex or xetex
|
|
\usepackage{unicode-math} % this also loads fontspec
|
|
\defaultfontfeatures{Scale=MatchLowercase}
|
|
\defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
|
|
\fi
|
|
\usepackage{lmodern}
|
|
\ifPDFTeX\else
|
|
% xetex/luatex font selection
|
|
\fi
|
|
% Use upquote if available, for straight quotes in verbatim environments
|
|
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
|
|
\IfFileExists{microtype.sty}{% use microtype if available
|
|
\usepackage[]{microtype}
|
|
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
|
|
}{}
|
|
\makeatletter
|
|
\@ifundefined{KOMAClassName}{% if non-KOMA class
|
|
\IfFileExists{parskip.sty}{%
|
|
\usepackage{parskip}
|
|
}{% else
|
|
\setlength{\parindent}{0pt}
|
|
\setlength{\parskip}{6pt plus 2pt minus 1pt}}
|
|
}{% if KOMA class
|
|
\KOMAoptions{parskip=half}}
|
|
\makeatother
|
|
\usepackage{color}
|
|
\usepackage{fancyvrb}
|
|
\newcommand{\VerbBar}{|}
|
|
\newcommand{\VERB}{\Verb[commandchars=\\\{\}]}
|
|
\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
|
|
% Add ',fontsize=\small' for more characters per line
|
|
\newenvironment{Shaded}{}{}
|
|
\newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
|
|
\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
|
\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}}
|
|
\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
|
\newcommand{\BuiltInTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}}
|
|
\newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
|
\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}}
|
|
\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
|
\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}}
|
|
\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
|
|
\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}}
|
|
\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
|
\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}}
|
|
\newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}}
|
|
\newcommand{\ExtensionTok}[1]{#1}
|
|
\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}}
|
|
\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}}
|
|
\newcommand{\ImportTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}}
|
|
\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
|
\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}}
|
|
\newcommand{\NormalTok}[1]{#1}
|
|
\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
|
|
\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}}
|
|
\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}}
|
|
\newcommand{\RegionMarkerTok}[1]{#1}
|
|
\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
|
\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}}
|
|
\newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
|
\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}}
|
|
\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}}
|
|
\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}}
|
|
\usepackage{longtable,booktabs,array}
|
|
\newcounter{none} % for unnumbered tables
|
|
\usepackage{calc} % for calculating minipage widths
|
|
% Correct order of tables after \paragraph or \subparagraph
|
|
\usepackage{etoolbox}
|
|
\makeatletter
|
|
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
|
|
\makeatother
|
|
% Allow footnotes in longtable head/foot
|
|
\IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}}
|
|
\makesavenoteenv{longtable}
|
|
\setlength{\emergencystretch}{3em} % prevent overfull lines
|
|
\providecommand{\tightlist}{%
|
|
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
|
\usepackage{bookmark}
|
|
\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
|
|
\urlstyle{same}
|
|
\hypersetup{
|
|
hidelinks,
|
|
pdfcreator={LaTeX via pandoc}}
|
|
|
|
\author{}
|
|
\date{}
|
|
|
|
\begin{document}
|
|
|
|
\section{Cryptographic Operations Audit
|
|
Report}\label{cryptographic-operations-audit-report}
|
|
|
|
\textbf{Repository:} \texttt{/Users/z/work/lux/standard/contracts/}
|
|
\textbf{Date:} 2026-01-30 \textbf{Auditor:} Automated Security Analysis
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{Executive Summary}\label{executive-summary}
|
|
|
|
This audit examines all cryptographic operations across the Lux Standard
|
|
contracts. The codebase implements a diverse set of cryptographic
|
|
schemes including ECDSA, FROST Schnorr signatures, Lamport signatures,
|
|
EIP-712 typed data, and Merkle proofs. While most implementations follow
|
|
best practices, several findings require attention.
|
|
|
|
\textbf{Severity Summary:}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
CRITICAL: 2
|
|
\item
|
|
HIGH: 4
|
|
\item
|
|
MEDIUM: 6
|
|
\item
|
|
LOW: 8
|
|
\item
|
|
INFORMATIONAL: 5
|
|
\end{itemize}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{1. Signature Verification
|
|
(ecrecover)}\label{1-signature-verification-ecrecover}
|
|
|
|
\subsubsection{1.1 Files Using
|
|
ecrecover}\label{11-files-using-ecrecover}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}lll@{}}
|
|
\toprule\noalign{}
|
|
File & Line & Context \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{bridge/Teleport.sol} & 273 & MPC oracle signature
|
|
verification \\
|
|
\texttt{bridge/Bridge.sol} & 327 & MPC oracle signature verification \\
|
|
\texttt{governance/DAO.sol} & 323 & Vote by signature \\
|
|
\texttt{safe/FROST.sol} & 15-26 & Schnorr signature via ecrecover
|
|
trick \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{1.2 Findings}\label{12-findings}
|
|
|
|
\paragraph{CRITICAL: Missing Signature Malleability Protection in Bridge
|
|
Contracts}\label{critical-missing-signature-malleability-protection-in-bridge-contracts}
|
|
|
|
\textbf{Files:} \texttt{bridge/Teleport.sol}, \texttt{bridge/Bridge.sol}
|
|
|
|
\textbf{Issue:} Both contracts use raw \texttt{ecrecover} without
|
|
checking the \texttt{s} value is in the lower half of the curve order.
|
|
ECDSA signatures are malleable: for any valid signature
|
|
\texttt{(r,\ s,\ v)}, the signature
|
|
\texttt{(r,\ n\ -\ s,\ v\textquotesingle{})} is also valid for the same
|
|
message.
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// bridge/Teleport.sol:271{-}273}
|
|
\NormalTok{function recoverSigner(bytes32 message, bytes memory sig) internal pure returns (address) \{}
|
|
\NormalTok{ (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);}
|
|
\NormalTok{ return ecrecover(message, v, r, s); // No malleability check}
|
|
\NormalTok{\}}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Impact:} An attacker could submit a malleable signature variant
|
|
to bypass replay protection if the signature bytes themselves are used
|
|
as the replay key (which they are in
|
|
\texttt{transactionMap{[}signedTXInfo{]}}).
|
|
|
|
\textbf{Recommendation:} Use OpenZeppelin\textquotesingle s
|
|
\texttt{ECDSA.recover()} which enforces
|
|
\texttt{s\ \textless{}=\ secp256k1n/2}, or add explicit check:
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{require(uint256(s) \textless{}= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\paragraph{HIGH: No Zero Address Check After
|
|
ecrecover}\label{high-no-zero-address-check-after-ecrecover}
|
|
|
|
\textbf{Files:} \texttt{bridge/Teleport.sol}, \texttt{bridge/Bridge.sol}
|
|
|
|
\textbf{Issue:} \texttt{ecrecover} returns \texttt{address(0)} on
|
|
invalid signatures rather than reverting. The bridge contracts do check
|
|
the recovered address against \texttt{MPCOracleAddrMap}, but this relies
|
|
on \texttt{address(0)} not being in the oracle map. An explicit check
|
|
should be added.
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// bridge/Bridge.sol:445{-}451}
|
|
\NormalTok{address signer = recoverSigner(}
|
|
\NormalTok{ prefixed(keccak256(abi.encodePacked(message))),}
|
|
\NormalTok{ signedTXInfo\_}
|
|
\NormalTok{);}
|
|
\NormalTok{// Missing: require(signer != address(0), "Invalid signature");}
|
|
\NormalTok{require(MPCOracleAddrMap[signer].exists, "Unauthorized Signature");}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Recommendation:} Add explicit zero address check immediately
|
|
after recovery.
|
|
|
|
\paragraph{LOW: DAO.sol Uses Raw ecrecover
|
|
(Acceptable)}\label{low-daosol-uses-raw-ecrecover-acceptable}
|
|
|
|
\textbf{File:} \texttt{governance/DAO.sol:323}
|
|
|
|
The DAO contract uses \texttt{ecrecover} directly but the context is
|
|
acceptable because:
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\tightlist
|
|
\item
|
|
The signature is used to identify the voter, not for replay protection
|
|
\item
|
|
Invalid signatures (returning \texttt{address(0)}) are caught by
|
|
subsequent \texttt{require(signer\ !=\ address(0))}
|
|
\end{enumerate}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{2. Hash Functions
|
|
(keccak256)}\label{2-hash-functions-keccak256}
|
|
|
|
\subsubsection{2.1 Hash Collision Risks}\label{21-hash-collision-risks}
|
|
|
|
\paragraph{MEDIUM: abi.encodePacked with Multiple Dynamic
|
|
Types}\label{medium-abiencodepacked-with-multiple-dynamic-types}
|
|
|
|
\textbf{Multiple Files}
|
|
|
|
Using \texttt{abi.encodePacked} with multiple dynamic-length arguments
|
|
can lead to hash collisions:
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// bridge/Teleport.sol:183}
|
|
\NormalTok{return string(abi.encodePacked(amt, toTargetAddrStr, txid, tokenAddrStrHash, chainIdStr, vault));}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Example Collision:}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{encodePacked("a",\ "bc")} = \texttt{encodePacked("ab",\ "c")}
|
|
= \texttt{"abc"}
|
|
\end{itemize}
|
|
|
|
\textbf{Affected Locations:}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{bridge/Teleport.sol:183} - Message construction
|
|
\item
|
|
\texttt{bridge/Bridge.sol:276-284} - Message construction
|
|
\item
|
|
\texttt{privacy/BulletproofVerifier.sol:166-167} - Challenge
|
|
computation
|
|
\item
|
|
\texttt{account/Account.sol:206} - Session key message hash
|
|
\end{itemize}
|
|
|
|
\textbf{Recommendation:} Use \texttt{abi.encode} instead of
|
|
\texttt{abi.encodePacked} for all security-critical hashing, or ensure
|
|
at least one fixed-length element separates dynamic elements.
|
|
|
|
\paragraph{LOW: Proper Usage Patterns
|
|
Observed}\label{low-proper-usage-patterns-observed}
|
|
|
|
Several contracts correctly use \texttt{abi.encode} for EIP-712
|
|
structured data:
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{governance/DAO.sol:305-320} - Domain separator and struct hash
|
|
\item
|
|
\texttt{governance/Council.sol:224-243} - Transaction hashing
|
|
\item
|
|
\texttt{tokens/LRC20/extensions/LRC20Permit.sol:60-61} - Permit hash
|
|
\item
|
|
\texttt{router/IntentRouter.sol:444-459} - Order hash
|
|
\end{itemize}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{3. Nonce Management}\label{3-nonce-management}
|
|
|
|
\subsubsection{3.1 Files with Nonce
|
|
Management}\label{31-files-with-nonce-management}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}lll@{}}
|
|
\toprule\noalign{}
|
|
File & Nonce Type & Implementation \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{account/Account.sol} & Session nonce & Incremented after use \\
|
|
\texttt{tokens/LRC20/extensions/LRC20Permit.sol} & EIP-2612 nonce &
|
|
OpenZeppelin Nonces \\
|
|
\texttt{fhe/governance/ConfidentialLRC20Votes.sol} & Delegation nonce &
|
|
Custom mapping \\
|
|
\texttt{router/IntentRouter.sol} & Order nonce & Invalidation mapping \\
|
|
\texttt{safe/SafeThresholdLamportModule.sol} & Lamport nonce &
|
|
Incremented after use \\
|
|
\texttt{bridge/teleport/TeleportBridge.sol} & Burn nonce & Sequential
|
|
counter \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{3.2 Findings}\label{32-findings}
|
|
|
|
\paragraph{HIGH: Nonce Not Validated Before Use in
|
|
Account.sol}\label{high-nonce-not-validated-before-use-in-accountsol}
|
|
|
|
\textbf{File:} \texttt{account/Account.sol:206}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{function executeWithSession(...) external returns (bytes memory) \{}
|
|
\NormalTok{ bytes32 hash = keccak256(abi.encodePacked(target, value, data, nonce));}
|
|
\NormalTok{ // ... signature verification ...}
|
|
\NormalTok{ nonce++; // Nonce incremented AFTER use}
|
|
\NormalTok{\}}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} The nonce is included in the hash but
|
|
there\textquotesingle s no validation that the signature was created
|
|
with the current nonce. An attacker could potentially replay old
|
|
signatures if the same \texttt{(target,\ value,\ data)} tuple recurs.
|
|
|
|
\textbf{Recommendation:} Include explicit nonce validation or use a
|
|
nonce mapping per signer.
|
|
|
|
\paragraph{MEDIUM: Potential Nonce Desync in
|
|
ConfidentialLRC20Votes}\label{medium-potential-nonce-desync-in-confidentiallrc20votes}
|
|
|
|
\textbf{File:}
|
|
\texttt{fhe/governance/ConfidentialLRC20Votes.sol:137-139}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{if (nonce != nonces[delegator]++) \{}
|
|
\NormalTok{ revert SignatureNonceInvalid();}
|
|
\NormalTok{\}}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} The nonce is incremented even when the signature
|
|
verification fails (due to the \texttt{++} operator in the condition).
|
|
If \texttt{SignatureChecker.isValidSignatureNow} fails first (line 134),
|
|
the nonce is not affected, but the order of checks could lead to
|
|
inconsistent state if reordered.
|
|
|
|
\textbf{Recommendation:} Separate nonce validation from nonce increment
|
|
for clarity.
|
|
|
|
\paragraph{LOW: Good Practice - TeleportBridge Nonce
|
|
Management}\label{low-good-practice---teleportbridge-nonce-management}
|
|
|
|
\textbf{File:} \texttt{bridge/teleport/TeleportBridge.sol:156}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{uint256 currentNonce = burnNonce++;}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
Good pattern: sequential nonce incremented atomically, included in
|
|
canonical burn ID computation.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{4. Randomness Sources}\label{4-randomness-sources}
|
|
|
|
\subsubsection{4.1 Analysis of block.timestamp/block.number
|
|
Usage}\label{41-analysis-of-blocktimestampblocknumber-usage}
|
|
|
|
\paragraph{MEDIUM: block.timestamp Used in Pool ID
|
|
Generation}\label{medium-blocktimestamp-used-in-pool-id-generation}
|
|
|
|
\textbf{File:} \texttt{mocks/MockZChainAMM.sol:36}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{poolId = keccak256(abi.encodePacked(assetA, assetB, block.timestamp));}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} Using \texttt{block.timestamp} for generating
|
|
identifiers allows miners/validators to influence the outcome. This is a
|
|
mock contract, but the pattern should not be used in production.
|
|
|
|
\paragraph{MEDIUM: Potential Time-Based Manipulation in Oracle
|
|
Staleness}\label{medium-potential-time-based-manipulation-in-oracle-staleness}
|
|
|
|
\textbf{File:} \texttt{precompile/interfaces/IOracle.sol:299}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{require(block.timestamp {-} p.timestamp \textless{}= maxAge, "Oracle: stale price");}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} Price staleness checks rely on \texttt{block.timestamp}
|
|
which can be manipulated by validators within bounds (\textasciitilde15
|
|
seconds on Ethereum). For high-value transactions, this tolerance may be
|
|
exploitable.
|
|
|
|
\textbf{Recommendation:} Consider using multiple oracle sources or
|
|
implementing TWAP for price-sensitive operations.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{5. EIP-712 Typed Data
|
|
Signing}\label{5-eip-712-typed-data-signing}
|
|
|
|
\subsubsection{5.1 Implementation
|
|
Review}\label{51-implementation-review}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}lll@{}}
|
|
\toprule\noalign{}
|
|
Contract & Domain Separator & Findings \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{governance/DAO.sol} & Dynamic construction & Computed per-call
|
|
(gas inefficient but correct) \\
|
|
\texttt{governance/Council.sol} & DOMAIN\_SEPARATOR\_TYPEHASH &
|
|
Correctly typed \\
|
|
\texttt{tokens/LRC20/extensions/LRC20Permit.sol} & OpenZeppelin EIP712 &
|
|
Best practice implementation \\
|
|
\texttt{router/IntentRouter.sol} & OpenZeppelin EIP712 & Best practice
|
|
implementation \\
|
|
\texttt{bridge/teleport/TeleportBridge.sol} & OpenZeppelin EIP712 & Best
|
|
practice implementation \\
|
|
\texttt{fhe/access/PermissionedV2.sol} & Modified EIP712 & Intentionally
|
|
omits verifyingContract \\
|
|
\texttt{account/Account.sol} & Defined but unused & DOMAIN\_TYPEHASH
|
|
defined but EIP-712 not fully implemented \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{5.2 Findings}\label{52-findings}
|
|
|
|
\paragraph{HIGH: DAO.sol Domain Separator Lacks
|
|
Version}\label{high-daosol-domain-separator-lacks-version}
|
|
|
|
\textbf{File:} \texttt{governance/DAO.sol:305-311}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{bytes32 domainSeparator = keccak256(}
|
|
\NormalTok{ abi.encode(}
|
|
\NormalTok{ keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"),}
|
|
\NormalTok{ keccak256(bytes("LuxDAO")),}
|
|
\NormalTok{ block.chainid,}
|
|
\NormalTok{ address(this)}
|
|
\NormalTok{ )}
|
|
\NormalTok{);}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} The domain separator is missing the \texttt{version}
|
|
field that is standard in EIP-712. While functional, this deviates from
|
|
the standard and may cause compatibility issues with signing libraries.
|
|
|
|
\textbf{Recommendation:} Add version field:
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\paragraph{LOW: Domain Separator Not Cached in
|
|
DAO.sol}\label{low-domain-separator-not-cached-in-daosol}
|
|
|
|
\textbf{File:} \texttt{governance/DAO.sol}
|
|
|
|
The domain separator is recomputed on every \texttt{castVoteBySig} call.
|
|
While correct (handles chain forks), it\textquotesingle s gas
|
|
inefficient. Consider caching with fork detection like
|
|
OpenZeppelin\textquotesingle s implementation.
|
|
|
|
\paragraph{INFORMATIONAL: PermissionedV2 Intentionally Omits
|
|
verifyingContract}\label{informational-permissionedv2-intentionally-omits-verifyingcontract}
|
|
|
|
\textbf{File:} \texttt{fhe/access/PermissionedV2.sol}
|
|
|
|
This is intentional design to allow single signatures to work across
|
|
multiple contracts. The access control is handled via the
|
|
\texttt{contracts} and \texttt{projects} arrays in the permission
|
|
struct. This is documented and acceptable.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{6. Permit Signatures
|
|
(ERC-2612)}\label{6-permit-signatures-erc-2612}
|
|
|
|
\subsubsection{6.1 Implementations}\label{61-implementations}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}ll@{}}
|
|
\toprule\noalign{}
|
|
Contract & Base Implementation \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{tokens/LRC20/extensions/LRC20Permit.sol} & Custom (follows
|
|
standard) \\
|
|
\texttt{governance/Stake.sol} & OpenZeppelin ERC20Permit \\
|
|
\texttt{liquid/LiquidLUX.sol} & OpenZeppelin ERC20Permit \\
|
|
\texttt{tokens/LRC4626/LRC4626.sol} & OpenZeppelin ERC20Permit \\
|
|
\texttt{tokens/LRC20/LRC20.sol} & OpenZeppelin ERC20Permit \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{6.2 Findings}\label{62-findings}
|
|
|
|
\paragraph{LOW: LRC20Permit Uses ECDSA.recover
|
|
Correctly}\label{low-lrc20permit-uses-ecdsarecover-correctly}
|
|
|
|
\textbf{File:} \texttt{tokens/LRC20/extensions/LRC20Permit.sol:65}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{address signer = ECDSA.recover(hash, v, r, s);}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
Good: Uses OpenZeppelin\textquotesingle s ECDSA library which includes
|
|
malleability protection.
|
|
|
|
\paragraph{INFORMATIONAL: All Permit Implementations Follow
|
|
ERC-2612}\label{informational-all-permit-implementations-follow-erc-2612}
|
|
|
|
All permit implementations correctly:
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
Use sequential nonces via \texttt{\_useNonce()}
|
|
\item
|
|
Include deadline validation
|
|
\item
|
|
Use EIP-712 typed data hashing
|
|
\item
|
|
Validate signer matches owner
|
|
\end{itemize}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{7. Merkle Proof
|
|
Verification}\label{7-merkle-proof-verification}
|
|
|
|
\subsubsection{7.1 Implementations
|
|
Found}\label{71-implementations-found}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}ll@{}}
|
|
\toprule\noalign{}
|
|
Contract & Purpose \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{privacy/ZNote.sol} & Note commitment tree \\
|
|
\texttt{privacy/ShieldedTreasury.sol} & Commitment tree \\
|
|
\texttt{privacy/Poseidon2Commitments.sol} & ZK commitment tree \\
|
|
\texttt{privacy/PrivateBridge.sol} & Cross-chain commitment \\
|
|
\texttt{precompile/interfaces/IZK.sol} & Precompile interface \\
|
|
\texttt{precompile/interfaces/IHash.sol} & Hash precompile interface \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{7.2 Findings}\label{72-findings}
|
|
|
|
\paragraph{MEDIUM: Second Preimage Attack Risk in Custom Merkle
|
|
Implementations}\label{medium-second-preimage-attack-risk-in-custom-merkle-implementations}
|
|
|
|
\textbf{Files:} \texttt{privacy/ZNote.sol},
|
|
\texttt{privacy/ShieldedTreasury.sol}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// privacy/ZNote.sol:382{-}385}
|
|
\NormalTok{if (pathIndices[i] == 0) \{}
|
|
\NormalTok{ currentHash = keccak256(abi.encodePacked(currentHash, proof[i]));}
|
|
\NormalTok{\} else \{}
|
|
\NormalTok{ currentHash = keccak256(abi.encodePacked(proof[i], currentHash));}
|
|
\NormalTok{\}}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} Without domain separation between leaf and internal
|
|
nodes, second preimage attacks may be possible. An attacker could
|
|
potentially construct a proof where a leaf hash equals an internal node
|
|
hash.
|
|
|
|
\textbf{Recommendation:} Use different hash domains for leaves vs
|
|
internal nodes:
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// For leaves:}
|
|
\NormalTok{keccak256(abi.encodePacked(bytes1(0x00), leafData))}
|
|
\NormalTok{// For internal nodes:}
|
|
\NormalTok{keccak256(abi.encodePacked(bytes1(0x01), left, right))}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\paragraph{INFORMATIONAL: OpenZeppelin MerkleProof Not
|
|
Used}\label{informational-openzeppelin-merkleproof-not-used}
|
|
|
|
The contracts implement custom Merkle verification rather than using
|
|
OpenZeppelin\textquotesingle s audited \texttt{MerkleProof} library.
|
|
Custom implementations are acceptable for specific use cases (e.g.,
|
|
incremental Merkle trees) but increase audit surface.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{8. Commitment Schemes}\label{8-commitment-schemes}
|
|
|
|
\subsubsection{8.1 Implementations}\label{81-implementations}
|
|
|
|
{\def\LTcaptype{none} % do not increment counter
|
|
\begin{longtable}[]{@{}ll@{}}
|
|
\toprule\noalign{}
|
|
Contract & Scheme Type \\
|
|
\midrule\noalign{}
|
|
\endhead
|
|
\bottomrule\noalign{}
|
|
\endlastfoot
|
|
\texttt{safe/SafeLSSSigner.sol} & LSS commitment \\
|
|
\texttt{privacy/Poseidon2Commitments.sol} & Pedersen-style \\
|
|
\texttt{privacy/PrivateTeleport.sol} & Note commitments \\
|
|
\texttt{precompile/interfaces/IZK.sol} & KZG, FRI commitments \\
|
|
\end{longtable}
|
|
}
|
|
|
|
\subsubsection{8.2 Findings}\label{82-findings}
|
|
|
|
\paragraph{LOW: Commitment Binding in
|
|
SafeLSSSigner}\label{low-commitment-binding-in-safelsssigner}
|
|
|
|
\textbf{File:} \texttt{safe/SafeLSSSigner.sol:134}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{commitment: keccak256(abi.encodePacked(\_threshold, \_totalSigners, \_publicKey))}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
The commitment binds threshold parameters to public key. This is a good
|
|
pattern for preventing parameter manipulation.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{9. FROST Schnorr
|
|
Signatures}\label{9-frost-schnorr-signatures}
|
|
|
|
\subsubsection{9.1 Implementation
|
|
Analysis}\label{91-implementation-analysis}
|
|
|
|
\textbf{File:} \texttt{safe/FROST.sol}
|
|
|
|
The FROST library implements RFC 9591 FROST(secp256k1, SHA-256) Schnorr
|
|
signatures using the \texttt{ecrecover} precompile trick for mul-mul-add
|
|
operations.
|
|
|
|
\subsubsection{9.2 Findings}\label{92-findings}
|
|
|
|
\paragraph{INFORMATIONAL: Signature Malleability
|
|
Handled}\label{informational-signature-malleability-handled}
|
|
|
|
\textbf{File:} \texttt{safe/FROST.sol:294-311}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// TODO(nlordell): I don\textquotesingle{}t think this is required for Schnorr}
|
|
\NormalTok{// signatures, but do it anyway just in case.}
|
|
\NormalTok{\{}
|
|
\NormalTok{ bool pOk = isValidPublicKey(px, py);}
|
|
\NormalTok{ bool rOk = \_isOnCurve(rx, ry);}
|
|
\NormalTok{ bool zOk = \_isScalar(z);}
|
|
\NormalTok{ // ...}
|
|
\NormalTok{\}}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
Good: The implementation validates that \texttt{z} is a valid scalar (in
|
|
range \texttt{(0,\ N)}), preventing trivial malleability via \texttt{z}
|
|
manipulation.
|
|
|
|
\paragraph{LOW: Public Key Restriction
|
|
Documented}\label{low-public-key-restriction-documented}
|
|
|
|
\textbf{File:} `safe/FROST.sol:228-230**
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{/// @dev Note that public key\textquotesingle{}s x{-}coordinate \textasciigrave{}px\textasciigrave{} must be smaller than the}
|
|
\NormalTok{/// curve order for the math trick with \textasciigrave{}ecrecover\textasciigrave{} to work.}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
The restriction that \texttt{px\ \textless{}\ N} is documented and
|
|
enforced in \texttt{isValidPublicKey()}. This is a known limitation of
|
|
the ecrecover trick approach.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{10. Lamport Signatures}\label{10-lamport-signatures}
|
|
|
|
\subsubsection{10.1 Implementation
|
|
Analysis}\label{101-implementation-analysis}
|
|
|
|
\textbf{File:} \texttt{safe/SafeThresholdLamportModule.sol}
|
|
|
|
Implements one-time Lamport signatures for post-quantum security with
|
|
M-Chain MPC threshold control (LP-134; the legacy ``T-Chain MPC'' name
|
|
referred to today's M-Chain bridge-custody chain).
|
|
|
|
\subsubsection{10.2 Findings}\label{102-findings}
|
|
|
|
\paragraph{INFORMATIONAL: Proper One-Time Key
|
|
Rotation}\label{informational-proper-one-time-key-rotation}
|
|
|
|
\textbf{File:} \texttt{safe/SafeThresholdLamportModule.sol:197-202}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{bytes32 oldPkh = pkh;}
|
|
\NormalTok{pkh = nextPKH;}
|
|
\NormalTok{lamportNonce++;}
|
|
|
|
\NormalTok{emit LamportKeyRotated(oldPkh, nextPKH);}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
Good: Key rotation is enforced as part of the execution flow, ensuring
|
|
one-time property of Lamport signatures.
|
|
|
|
\paragraph{LOW: Domain Separation Properly
|
|
Implemented}\label{low-domain-separation-properly-implemented}
|
|
|
|
\textbf{File:} `safe/SafeThresholdLamportModule.sol:180-185**
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{uint256 m = uint256(keccak256(abi.encodePacked(}
|
|
\NormalTok{ safeTxHash,}
|
|
\NormalTok{ nextPKH,}
|
|
\NormalTok{ address(this), // Prevent cross{-}contract replay}
|
|
\NormalTok{ block.chainid // Prevent cross{-}chain replay}
|
|
\NormalTok{)));}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
Good: Domain separation includes contract address and chain ID to
|
|
prevent replay attacks across chains and contracts.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{11. Replay Attack Vectors}\label{11-replay-attack-vectors}
|
|
|
|
\subsubsection{11.1 Analysis}\label{111-analysis}
|
|
|
|
\paragraph{CRITICAL: Signature-Based Replay Protection in Legacy
|
|
Bridges}\label{critical-signature-based-replay-protection-in-legacy-bridges}
|
|
|
|
\textbf{Files:} \texttt{bridge/Teleport.sol}, \texttt{bridge/Bridge.sol}
|
|
|
|
\begin{Shaded}
|
|
\begin{Highlighting}[]
|
|
\NormalTok{// bridge/Bridge.sol:441{-}443}
|
|
\NormalTok{require(}
|
|
\NormalTok{ !transactionMap[signedTXInfo\_].exists,}
|
|
\NormalTok{ "Duplicated Transaction Hash"}
|
|
\NormalTok{);}
|
|
\end{Highlighting}
|
|
\end{Shaded}
|
|
|
|
\textbf{Issue:} Replay protection uses the raw signature bytes as the
|
|
key. Due to ECDSA signature malleability (see Section 1.2), an attacker
|
|
can derive an alternative valid signature for the same message and
|
|
bypass replay protection.
|
|
|
|
\textbf{Attack Scenario:}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\tightlist
|
|
\item
|
|
User submits valid bridge mint with signature \texttt{(r,\ s,\ v)}
|
|
\item
|
|
Attacker computes malleable signature
|
|
\texttt{(r,\ n-s,\ v\textquotesingle{})} for same message
|
|
\item
|
|
Attacker replays with malleable signature, passing replay check
|
|
\item
|
|
Tokens minted twice
|
|
\end{enumerate}
|
|
|
|
\textbf{Recommendation:}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\tightlist
|
|
\item
|
|
Use message hash (not signature) as replay key, OR
|
|
\item
|
|
Enforce \texttt{s} is in lower half of curve order before storing
|
|
\end{enumerate}
|
|
|
|
\paragraph{LOW: Proper Replay Protection
|
|
Examples}\label{low-proper-replay-protection-examples}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{bridge/teleport/TeleportBridge.sol}: Uses \texttt{claimId}
|
|
computed from claim data, not signature
|
|
\item
|
|
\texttt{router/IntentRouter.sol}: Uses
|
|
\texttt{filledAmounts{[}orderHash{]}} where orderHash is from order
|
|
data
|
|
\item
|
|
\texttt{governance/DAO.sol}: Uses \texttt{receipt.hasVoted} mapping
|
|
keyed by voter address
|
|
\end{itemize}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{12. Front-Running
|
|
Considerations}\label{12-front-running-considerations}
|
|
|
|
\subsubsection{12.1 Analysis}\label{121-analysis}
|
|
|
|
\paragraph{MEDIUM: Bridge Mints Susceptible to
|
|
Front-Running}\label{medium-bridge-mints-susceptible-to-front-running}
|
|
|
|
\textbf{Files:} \texttt{bridge/Teleport.sol}, \texttt{bridge/Bridge.sol}
|
|
|
|
Once an MPC oracle signature is observed in the mempool, anyone can
|
|
submit the mint transaction. The recipient is fixed in the signed
|
|
message, but gas price wars could occur.
|
|
|
|
\textbf{Mitigation:} The contracts use \texttt{nonReentrant} which
|
|
prevents reentrancy but not front-running. Consider implementing
|
|
commit-reveal or private mempool submission for high-value transfers.
|
|
|
|
\paragraph{LOW: Intent Router Has MEV Protection
|
|
Documentation}\label{low-intent-router-has-mev-protection-documentation}
|
|
|
|
\textbf{File:} \texttt{router/IntentRouter.sol}
|
|
|
|
The contract explicitly mentions "MEV protection via private mempools"
|
|
in its documentation, indicating awareness of front-running concerns.
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{13. Recommendations
|
|
Summary}\label{13-recommendations-summary}
|
|
|
|
\subsubsection{Critical Priority}\label{critical-priority}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\tightlist
|
|
\item
|
|
\textbf{Fix signature malleability in bridge contracts} - Add
|
|
\texttt{s} value validation or use OpenZeppelin ECDSA
|
|
\item
|
|
\textbf{Change replay protection key from signature to message hash}
|
|
in legacy bridges
|
|
\end{enumerate}
|
|
|
|
\subsubsection{High Priority}\label{high-priority}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\setcounter{enumi}{2}
|
|
\tightlist
|
|
\item
|
|
\textbf{Add explicit \texttt{address(0)} check after ecrecover} in all
|
|
signature verification
|
|
\item
|
|
\textbf{Add version field to DAO.sol EIP-712 domain separator}
|
|
\item
|
|
\textbf{Fix nonce validation in Account.sol} session key execution
|
|
\item
|
|
\textbf{Review ConfidentialLRC20Votes nonce increment ordering}
|
|
\end{enumerate}
|
|
|
|
\subsubsection{Medium Priority}\label{medium-priority}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\setcounter{enumi}{6}
|
|
\tightlist
|
|
\item
|
|
\textbf{Replace \texttt{abi.encodePacked} with \texttt{abi.encode}}
|
|
for security-critical hashing
|
|
\item
|
|
\textbf{Add leaf/node domain separation} in custom Merkle
|
|
implementations
|
|
\item
|
|
\textbf{Review oracle staleness timing} for manipulation resistance
|
|
\item
|
|
\textbf{Avoid block.timestamp in identifier generation} (even in
|
|
mocks, as bad patterns propagate)
|
|
\end{enumerate}
|
|
|
|
\subsubsection{Low Priority}\label{low-priority}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\setcounter{enumi}{10}
|
|
\tightlist
|
|
\item
|
|
Consider caching domain separator in DAO.sol
|
|
\item
|
|
Document public key restrictions for FROST signatures in user-facing
|
|
docs
|
|
\item
|
|
Add comments explaining nonce patterns in complex flows
|
|
\end{enumerate}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{14. Positive Findings}\label{14-positive-findings}
|
|
|
|
The following patterns demonstrate good cryptographic practices:
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\tightlist
|
|
\item
|
|
\textbf{OpenZeppelin Usage}: Most newer contracts use audited
|
|
OpenZeppelin libraries (ECDSA, EIP712, Nonces)
|
|
\item
|
|
\textbf{EIP-712 Adoption}: Widespread use of typed data signing for
|
|
user experience and security
|
|
\item
|
|
\textbf{Domain Separation}: Most signature schemes include chain ID
|
|
and contract address
|
|
\item
|
|
\textbf{Sequential Nonces}: Proper sequential nonce patterns in permit
|
|
and order systems
|
|
\item
|
|
\textbf{One-Time Key Rotation}: Lamport implementation correctly
|
|
enforces key rotation
|
|
\item
|
|
\textbf{Snapshot Voting}: DAO uses checkpointed voting to prevent
|
|
flash loan attacks
|
|
\end{enumerate}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\subsection{15. Files Reviewed}\label{15-files-reviewed}
|
|
|
|
\subsubsection{Primary Cryptographic
|
|
Contracts}\label{primary-cryptographic-contracts}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{bridge/Teleport.sol}
|
|
\item
|
|
\texttt{bridge/Bridge.sol}
|
|
\item
|
|
\texttt{bridge/teleport/TeleportBridge.sol}
|
|
\item
|
|
\texttt{governance/DAO.sol}
|
|
\item
|
|
\texttt{governance/Council.sol}
|
|
\item
|
|
\texttt{account/Account.sol}
|
|
\item
|
|
\texttt{safe/FROST.sol}
|
|
\item
|
|
\texttt{safe/SafeThresholdLamportModule.sol}
|
|
\item
|
|
\texttt{tokens/LRC20/extensions/LRC20Permit.sol}
|
|
\item
|
|
\texttt{router/IntentRouter.sol}
|
|
\item
|
|
\texttt{fhe/governance/ConfidentialLRC20Votes.sol}
|
|
\item
|
|
\texttt{fhe/access/PermissionedV2.sol}
|
|
\end{itemize}
|
|
|
|
\subsubsection{Supporting Files}\label{supporting-files}
|
|
|
|
\begin{itemize}
|
|
\tightlist
|
|
\item
|
|
\texttt{privacy/ZNote.sol}
|
|
\item
|
|
\texttt{privacy/ShieldedTreasury.sol}
|
|
\item
|
|
\texttt{privacy/Poseidon2Commitments.sol}
|
|
\item
|
|
\texttt{precompile/interfaces/IZK.sol}
|
|
\item
|
|
\texttt{precompile/interfaces/IHash.sol}
|
|
\item
|
|
Various yield strategy and bridge token contracts
|
|
\end{itemize}
|
|
|
|
\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}
|
|
|
|
\textbf{End of Report}
|
|
|
|
\end{document}
|