Omniscia Platypus Finance Audit

Core Manual Review Findings

Core Manual Review Findings

COR-01M: Equilibrium Point Arbitrage

Description:

The system combats arbitrage opportunities by enforcing a dynamic withdrawal and deposit fee that scales with the cash and liability held by the system. However, it manages the case of equilibrium by not applying a fee in either deposits or withdrawals.

Example:

contracts/pool/Core.sol
125/**
126 * @notice Yellow Paper Def. 6.2 (Withdrawal Fee)
127 * @dev When covBefore >= 1, fee is 0
128 * @dev When covBefore < 1, we apply a fee to prevent withdrawal arbitrage
129 * @param k K slippage parameter in WAD
130 * @param n N slippage parameter
131 * @param c1 C1 slippage parameter in WAD
132 * @param xThreshold xThreshold slippage parameter in WAD
133 * @param cash cash position of asset in WAD
134 * @param liability liability position of asset in WAD
135 * @param amount amount to be withdrawn in WAD
136 * @return The final fee to be applied
137 */
138function _withdrawalFee(
139 uint256 k,
140 uint256 n,
141 uint256 c1,
142 uint256 xThreshold,
143 uint256 cash,
144 uint256 liability,
145 uint256 amount
146) internal pure returns (uint256) {
147 uint256 covBefore = cash.wdiv(liability);
148 if (covBefore >= WAD) {
149 return 0;
150 }
151
152 if (liability <= amount) {
153 return 0;
154 }
155
156 uint256 cashAfter;
157 // Cover case where cash <= amount
158 if (cash > amount) {
159 cashAfter = cash - amount;
160 } else {
161 cashAfter = 0;
162 }
163
164 uint256 covAfter = (cashAfter).wdiv(liability - amount);
165 uint256 slippageBefore = _slippageFunc(k, n, c1, xThreshold, covBefore);
166 uint256 slippageAfter = _slippageFunc(k, n, c1, xThreshold, covAfter);
167 uint256 slippageNeutral = _slippageFunc(k, n, c1, xThreshold, WAD); // slippage on cov = 1
168
169 // fee = [(Li - Di) * SlippageAfter] + [g(1) * Di] - [Li * SlippageBefore]
170 return
171 ((liability - amount).wmul(slippageAfter) + slippageNeutral.wmul(amount)) - liability.wmul(slippageBefore);
172}
173
174/**
175 * @notice Yellow Paper Def. 7.2 (Deposit Fee)
176 * @dev When covBefore <= 1, fee is 0
177 * @dev When covBefore > 1, we apply a fee to prevent deposit arbitrage
178 * @param k K slippage parameter in WAD
179 * @param n N slippage parameter
180 * @param c1 C1 slippage parameter in WAD
181 * @param xThreshold xThreshold slippage parameter in WAD
182 * @param cash cash position of asset in WAD
183 * @param liability liability position of asset in WAD
184 * @param amount amount to be deposited in WAD
185 * @return The final fee to be applied
186 */
187function _depositFee(
188 uint256 k,
189 uint256 n,
190 uint256 c1,
191 uint256 xThreshold,
192 uint256 cash,
193 uint256 liability,
194 uint256 amount
195) internal pure returns (uint256) {
196 // cover case where the asset has no liquidity yet
197 if (liability == 0) {
198 return 0;
199 }
200
201 uint256 covBefore = cash.wdiv(liability);
202 if (covBefore <= WAD) {
203 return 0;
204 }
205
206 uint256 covAfter = (cash + amount).wdiv(liability + amount);
207 uint256 slippageBefore = _slippageFunc(k, n, c1, xThreshold, covBefore);
208 uint256 slippageAfter = _slippageFunc(k, n, c1, xThreshold, covAfter);
209
210 // (Li + Di) * g(cov_after) - Li * g(cov_before)
211 return ((liability + amount).wmul(slippageAfter)) - (liability.wmul(slippageBefore));
212}

Recommendation:

We advise this point of the formulas to be evaluated as it may open up multi-step attack vectors where attackers bring the system state from a negative (< 1) cash-to-liability state to equilibrium (= 1) and then to a positive (> 1) cash-to-liability state instead of directly transitioning from a negative to a positive state. To achieve this, we advise the Platypus team to introduce corresponding test suites validating the mathematical model in such a scenario.

Alleviation:

The Platypus team has provided us with two articles analyzing a potential withdrawal arbitrage attack vector and justifying why the withdrawal fee imposed by the system counteracts such attacks. After consideration, we ascertained that the articles provided in correlation with the codebase adequately deal with the exhibit.

COR-02M: Potential Nullification of k

TypeSeverityLocation
Mathematical OperationsMinorCore.sol:L43

Description:

For relatively small values of x the rpow instruction may not yield a sufficiently large value to be properly casted to the WAD precision, especially if the rpow instruction is performed with a large power and a fractional number.

Example:

contracts/pool/Core.sol
23/**
24 * @notice Yellow Paper Def. 2.4 (Price Slippage Curve)
25 * @dev Calculates g(xr,i) or g(xr,j). This function always returns >= 0
26 * @param k K slippage parameter in WAD
27 * @param n N slippage parameter
28 * @param c1 C1 slippage parameter in WAD
29 * @param xThreshold xThreshold slippage parameter in WAD
30 * @param x coverage ratio of asset in WAD
31 * @return The result of price slippage curve
32 */
33function _slippageFunc(
34 uint256 k,
35 uint256 n,
36 uint256 c1,
37 uint256 xThreshold,
38 uint256 x
39) internal pure returns (uint256) {
40 if (x < xThreshold) {
41 return c1 - x;
42 } else {
43 return k.wdiv((((x * RAY) / WAD).rpow(n) * WAD) / RAY); // k / (x ** n)
44 }
45}

Recommendation:

We advise the formulas to be evaluated as to whether such a condition is expected to be encountered under normal operations and a potentially default value to be returned, such as slippage equivalent to 100% / 0% rather than a fatal failure.

Alleviation:

The Platypus team has provided us with sufficient test coverage validating small values of x and thus ensuring that the formula preforms as expected in real-world scenario. In light of this, we consider this exhibit dealt with.