From 884ce48822a1eb366292fc58ccaa1de61a24d527 Mon Sep 17 00:00:00 2001 From: mikera Date: Wed, 17 Jul 2024 13:28:00 +0100 Subject: [PATCH] More Torus tests and error checking --- convex-core/src/main/cvx/torus/exchange.cvx | 21 +++++--- .../src/main/java/convex/core/ErrorCodes.java | 5 ++ .../test/java/convex/actors/TorusTest.java | 54 +++++++++++++++++-- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/convex-core/src/main/cvx/torus/exchange.cvx b/convex-core/src/main/cvx/torus/exchange.cvx index c77de002f..f989b7787 100644 --- a/convex-core/src/main/cvx/torus/exchange.cvx +++ b/convex-core/src/main/cvx/torus/exchange.cvx @@ -96,6 +96,7 @@ [amount] (let [amount (int amount) + _ (cond (< amount 0) (fail :ARGUMENT "Cannot buy negative coin quantity")) required-tokens (or (buy-cvx-quote amount) (fail :LIQUIDITY "Pool cannot supply this amount of CVX"))] (asset/accept *caller* @@ -115,7 +116,10 @@ ^{:callable true} [amount] ;; Security: check pool can provide. - (when-not (<= 0 amount *balance*) (return nil)) + (cond + (< amount 0) (return nil) + (>= amount *balance*) (return nil)) + (let [;; Computes pool and fees/ cvx-balance *balance* pool (* (double token-balance) cvx-balance) @@ -128,7 +132,6 @@ amount)) token-balance)))))) - (defn buy-tokens ^{:callable true} @@ -136,6 +139,7 @@ [amount] (let [amount (int amount) + _ (cond (< amount 0) (fail :ARGUMENT "Cannot buy negative token quantity")) required-cvx (or (buy-tokens-quote amount) (fail :LIQUIDITY "Pool cannot supply this amount of tokens"))] (core/accept required-cvx) @@ -157,7 +161,9 @@ [amount] ;; Security: check pool can provide. - (when-not (<= 0 amount token-balance) (return nil)) + (cond + (< amount 0) (return nil) + (>= amount token-balance) (return nil)) (let [;; Computes pool and fees. cvx-balance *balance* @@ -199,7 +205,7 @@ (let [amount (int amount) gained-tokens (or (sell-cvx-quote amount) - (fail "Cannot sell this amount into pool"))] + (fail :ARGUMENT "Cannot sell negative coin amount"))] (core/accept amount) (def token-balance (- token-balance @@ -220,7 +226,8 @@ ;; Security: check amount is positive. ;; - (when (<= amount 0) (return nil)) + (cond (< amount 0) (return nil)) + (let [;; Computes pool and fees. cvx-balance *balance* pool (* (double token-balance) @@ -244,7 +251,7 @@ (let [amount (int amount) gained-cvx (or (sell-tokens-quote amount) - (fail "Cannot sell this amount into pool"))] + (fail :ARGUMENT "Cannot sell this negative token amount"))] (asset/accept *caller* [token amount]) @@ -265,7 +272,7 @@ [amount] ;; Security: check amount is positive. - (when-not (<= 0 amount) (return nil)) + (cond (< amount 0) (return nil)) (let [;; Computes pool and fees. cvx-balance *balance* diff --git a/convex-core/src/main/java/convex/core/ErrorCodes.java b/convex-core/src/main/java/convex/core/ErrorCodes.java index 8be15ce05..b26632582 100644 --- a/convex-core/src/main/java/convex/core/ErrorCodes.java +++ b/convex-core/src/main/java/convex/core/ErrorCodes.java @@ -213,5 +213,10 @@ public class ErrorCodes { */ public static final Keyword SYNTAX = Keyword.create("SYNTAX"); + /** + * Error code indicating insufficient liquidity in state for a Torus or other trade + */ + public static final Keyword LIQUIDITY = Keyword.create("LIQUIDITY"); + } diff --git a/convex-core/src/test/java/convex/actors/TorusTest.java b/convex-core/src/test/java/convex/actors/TorusTest.java index 63db44b5e..2c67af5eb 100644 --- a/convex-core/src/test/java/convex/actors/TorusTest.java +++ b/convex-core/src/test/java/convex/actors/TorusTest.java @@ -1,7 +1,6 @@ package convex.actors; -import static convex.test.Assertions.assertCVMEquals; -import static convex.test.Assertions.assertError; +import static convex.test.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -10,6 +9,7 @@ import org.junit.jupiter.api.Test; +import convex.core.ErrorCodes; import convex.core.data.AVector; import convex.core.data.Address; import convex.core.data.prim.CVMDouble; @@ -56,23 +56,69 @@ public class TorusTest extends ACVMTest { long STK=1000000; Context baseContext=context(); baseContext=exec(baseContext,"(torus/add-liquidity USD "+STK+" "+STK+")"); + long USDBAL = evalL(baseContext,"(fun/balance USD)"); + assertEquals(USDBAL,SUPPLY-STK); + { // Buy 100 USD Context ctx=baseContext; ctx=exec(ctx,"(torus/buy-tokens USD 100)"); assertEquals(101L,RT.ensureLong(ctx.getResult()).longValue()); ;; // price should be 101 - assertEquals(1000000000-STK+100,evalL(ctx,"(fun/balance USD)")); // should have gained 100 USD + assertEquals(USDBAL+100,evalL(ctx,"(fun/balance USD)")); // should have gained 100 USD assertEquals(STK,evalL(ctx,"(fun/balance USDM)")); // market shares unchanged assertTrue(evalB(ctx,"(< 1.0 (torus/price USD))")); // price has increased } + { // Buy whole pool + Context ctx=baseContext; + ctx=step(ctx,"(torus/buy-tokens USD "+STK+")"); + assertEquals(ErrorCodes.LIQUIDITY,ctx.getErrorCode()); + } + { // Buy 0 USD Context ctx=baseContext; ctx=exec(ctx,"(torus/buy-tokens USD 0)"); assertEquals(0L,RT.ensureLong(ctx.getResult()).longValue()); ;; // price should be 0 - assertEquals(1000000000-STK,evalL(ctx,"(fun/balance USD)")); // should have gained 100 USD + assertEquals(USDBAL,evalL(ctx,"(fun/balance USD)")); // no balance change + assertEquals(STK,evalL(ctx,"(fun/balance USDM)")); // market shares unchanged + assertTrue(evalB(ctx,"(= 1.0 (torus/price USD))")); // price should be unchanged + } + + { // Buy -100 USD + Context ctx=baseContext; + ctx=step(ctx,"(torus/buy-tokens USD -100)"); + assertArgumentError(ctx); + } + + { // Sell 100 USD + Context ctx=baseContext; + ctx=exec(ctx,"(torus/sell-tokens USD 100)"); + assertEquals(99L,RT.ensureLong(ctx.getResult()).longValue()); ;; // price should be 99 + assertEquals(USDBAL-100,evalL(ctx,"(fun/balance USD)")); // should have gained 100 USD + assertEquals(STK,evalL(ctx,"(fun/balance USDM)")); // market shares unchanged + assertTrue(evalB(ctx,"(> 1.0 (torus/price USD))")); // price has decreased + } + + { // Sell whole USD holding + Context ctx=baseContext; + ctx=step(ctx,"(torus/sell-tokens USD (fun/balance USD))"); + assertEquals(0,evalL(ctx,"(fun/balance USD)")); // should have no USD left + } + + { // Sell 0 USD + Context ctx=baseContext; + ctx=exec(ctx,"(torus/sell-tokens USD 0)"); + assertEquals(0L,RT.ensureLong(ctx.getResult()).longValue()); ;; // price should be 0 + assertEquals(USDBAL,evalL(ctx,"(fun/balance USD)")); // no balance change assertEquals(STK,evalL(ctx,"(fun/balance USDM)")); // market shares unchanged assertTrue(evalB(ctx,"(= 1.0 (torus/price USD))")); // price should be unchanged } + + { // Sell -100 USD + Context ctx=baseContext; + ctx=step(ctx,"(torus/sell-tokens USD -100)"); + assertArgumentError(ctx); + } + } @Test public void testMissingMarket() {