From 00721074483b1328f4a9a5081bcda03a099b5053 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 8 Jul 2024 21:49:49 +0530 Subject: [PATCH 1/8] stop limit --- strategies/stop-limit.rain | 266 +++++++++++++++++++++++++++++++++++++ test/StopLimitTest.t.sol | 141 ++++++++++++++++++++ 2 files changed, 407 insertions(+) create mode 100644 strategies/stop-limit.rain create mode 100644 test/StopLimitTest.t.sol diff --git a/strategies/stop-limit.rain b/strategies/stop-limit.rain new file mode 100644 index 0000000..5117821 --- /dev/null +++ b/strategies/stop-limit.rain @@ -0,0 +1,266 @@ +# Limit Order Strategy +# 5 buy and sell targets +# Each target you set buy price and amount and sell price and amount +# Target Network: Base +# Quote (Input / Incoming): USDC or WLTH +# Base (Output / Outgoing): WLTH or USDC +# Token contract: https://basescan.org/address/0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D +# Token github: NA +# Liquidity protocol: Uniswap V3 +# Liquidity pool address: https://www.dextools.io/app/en/base/pair-explorer/0x1536ee1506e24e5a36be99c73136cd82907a902e?t=1717921711270 +# Liquidity pool fee: 0.3% + +networks: + base-community: + rpc: https://rpc.ankr.com/base/8e4cac5728d95471ae55724953c7ae6f19a227ac05146276f09717692b732dae + chain-id: 8453 + network-id: 8453 + currency: ETH + +subgraphs: + base-community: https://api.thegraph.com/subgraphs/name/h20liquidity/base-0x2aee87 + +orderbooks: + base-community: + address: 0x2AeE87D75CD000583DAEC7A28db103B1c0c18b76 + network: base-community + subgraph: base-community + +deployers: + base-community: + address: 0x56394785a22b3BE25470a0e03eD9E0a939C47b9b + network: base-community + +tokens: + base-wlth: + network: base-community + address: 0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D + base-usdc: + network: base-community + address: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 + +orders: + # vault-id generated with `openssl rand -hex 32` + base-wlth-sell: + orderbook: base-community + inputs: + - token: base-usdc + vault-id: 0xe8c8cd1fae13ab9fa00c611a0d57cbd9298e939e34f1a38361fd37f917d4b065 + outputs: + - token: base-wlth + vault-id: 0xe8c8cd1fae13ab9fa00c611a0d57cbd9298e939e34f1a38361fd37f917d4b065 + base-wlth-buy: + orderbook: base-community + inputs: + - token: base-wlth + vault-id: 0x4767d92a5f01500424d2a2dd88964314f8a98a6b66bcf1db362b0ad9006c93e8 + outputs: + - token: base-usdc + vault-id: 0x4767d92a5f01500424d2a2dd88964314f8a98a6b66bcf1db362b0ad9006c93e8 + +scenarios: + stop-limit-order: + network: base-community + deployer: base-community + orderbook: base-community + bindings: + # Ask for now, registry in future. + uniswap-words: 0xD6B34F97d4A8Cb38D0544dB241CB3f335866f490 + orderbook-subparser: 0x8D96ea3EF24D7123882c51CE4325b89bc0d63f9e + + # Uniswap V3 factory addresses and init code + uniswap-v3-factory: '[uniswap-v3-factory]' + uniswap-v3-init-code: '[uniswap-v3-init-code]' + + # Uniswap V3 pool fee + pool-fee: '[uniswap-v3-fee-medium]' + scenarios: + buy: + bindings: + # Input and Output token from perspective of the order. + input-token-address: 0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D + output-token-address: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 + + # Stop ratio, once market price reaches below the stop price order will be active. + stop-ratio: 45 + + # io-ratio and amount for first limit order. + limit-ratio-1: 35 + ouptut-amount-1: 1 + + # Binding to get current orcale price + get-oracle-price: '''get-oracle-price-univ3' + + scenarios: + prod: + bindings: + plottables: '''plottables-prod' + get-trade-count: '''get-trade-count-prod' + ensure-stop-loss: '''ensure-stop-loss-prod' + metric: + runs: 1 + bindings: + plottables: '''plottables-metric' + get-trade-count: '''get-trade-count-prod' + ensure-stop-loss: '''ensure-stop-loss-metric' + sell: + bindings: + # Input and Output token from perspective of the order. + input-token-address: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 + output-token-address: 0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D + + # Stop ratio, once market price reaches below the stop price order will be active. + stop-ratio: 0.05 + + # io-ratio and amount for first order. + limit-ratio-1: 0.02 + ouptut-amount-1: 50 + + # Binding to get current orcale price + get-oracle-price: '''get-oracle-price-univ3' + + scenarios: + prod: + bindings: + plottables: '''plottables-prod' + get-trade-count: '''get-trade-count-prod' + ensure-stop-loss: '''ensure-stop-loss-prod' + metric: + runs: 1 + bindings: + plottables: '''plottables-metric' + get-trade-count: '''get-trade-count-prod' + ensure-stop-loss: '''ensure-stop-loss-metric' + +charts: + Buy WLTH, sell USDC single limit order: + scenario: stop-limit-order.buy.metric + metrics: + - label: Stop Price + value: 0.3.2 + unit-suffix: " WLTH/USDC" + - label: Price + value: 0.3.3 + unit-suffix: " WLTH" + description: 'Unit price, WLTH per USDC (stack item 0.3.2)' + - label: Effective Price (as visible on dex tools) + value: 0.3.4 + unit-suffix: " USDC" + description: 'Effective price in USDC denomination' + - label: Units + value: 0.3.5 + unit-suffix: " USDC" + description: 'Units purchased (stack item 0.3.3)' + - label: Total Price + value: 0.3.6 + unit-suffix: " WLTH" + description: 'Amount of WLTH recevied.' + Buy USDC, sell WLTH single limit order: + scenario: stop-limit-order.sell.metric + metrics: + - label: Stop Price + value: 0.3.2 + unit-suffix: " USDC/WLTH" + - label: Price + value: 0.3.3 + unit-suffix: " USDC" + description: 'Unit price, USDC per WLTH (stack item 0.3.2)' + - label: Effective Price (as visible on dex tools) + value: 0.3.3 + unit-suffix: " USDC" + description: 'Effective price in USDC denomination' + - label: Units + value: 0.3.4 + unit-suffix: " WLTH" + description: 'Units sold (stack item 0.3.3)' + - label: Total Price + value: 0.3.6 + unit-suffix: " USDC" + description: 'Amount of USDC recevied.' + +deployments: + base-wlth-buy: + scenario: stop-limit-order.buy.prod + order: base-wlth-buy + base-wlth-sell: + scenario: stop-limit-order.sell.prod + order: base-wlth-sell +--- +#uniswap-words !The subparser for the Uniswap words +#orderbook-subparser !The subparser for the Orderbook words +#uniswap-v3-factory !Uniswap v3 factory address. +#uniswap-v3-init-code !Uniswap v3 init code hash. +#pool-fee !Uniswap v3 pool fees. + +#input-token-address !Input token from the perspective of the order. +#output-token-address !Output token from the perspective of the order. + +#stop-ratio !Ratio below which trade is active +#limit-ratio-1 !IO ratio for first limit order. +#ouptut-amount-1 !Output amount for first limit order. + +#get-oracle-price !Binding to get currect price from orcale. +#ensure-stop-loss !Binding to ensure stop loss. + +#count-key "count-key" +#plottables !Binding to plot values. +#get-trade-count !Binding to get trade count. +#test-trade-count !Test trade count for plotting. + +#plottables-prod + _ _: ; + +#plottables-metric + amount io-ratio: , + _: stop-ratio, + _: limit-ratio-1, + effective-limit-ratio-1: inv(limit-ratio-1), + _: ouptut-amount-1, + input-amount-1: mul(limit-ratio-1 ouptut-amount-1); + +#get-trade-count-prod + trade-count-key: hash(order-hash() count-key), + trade-count: get(trade-count-key); + +#get-ratio-amount + trade-count: call<'get-trade-count>(), + ouput-amount: conditions( + equal-to(trade-count 0) ouptut-amount-1 + "Max order count" + ), + io-ratio: conditions( + equal-to(trade-count 0) limit-ratio-1 + "Max order count" + ); + +#get-oracle-price-univ3 + _: uniswap-v3-quote-exact-input( + output-token-address input-token-address + 1 + uniswap-v3-factory uniswap-v3-init-code + pool-fee + ); + +#ensure-stop-loss-prod + current-orcale-price: , + :ensure( + less-than(current-orcale-price stop-ratio) + "Stop price." + ); + +#ensure-stop-loss-metric + _: ; + +#calculate-io + using-words-from uniswap-words orderbook-subparser + + orcale-price: call<'get-oracle-price>(), + :ensure<'ensure-stop-loss>(orcale-price), + final-amount + final-ratio: call<'get-ratio-amount>(), + :call<'plottables>(final-amount final-ratio); + +#handle-io + trade-count-key: hash(order-hash() count-key), + trade-count: get(trade-count-key), + :set(trade-count-key add(trade-count 1)); diff --git a/test/StopLimitTest.t.sol b/test/StopLimitTest.t.sol new file mode 100644 index 0000000..e734cea --- /dev/null +++ b/test/StopLimitTest.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; +import {console2, Test} from "forge-std/Test.sol"; + +import { + IOrderBookV3, + IO, + OrderV2, + OrderConfigV2, + TakeOrderConfigV2, + TakeOrdersConfigV2 +} from "rain.orderbook.interface/interface/IOrderBookV3.sol"; +import {IOrderBookV3ArbOrderTaker} from "rain.orderbook.interface/interface/IOrderBookV3ArbOrderTaker.sol"; +import {IParserV1} from "rain.interpreter.interface/interface/IParserV1.sol"; +import {IExpressionDeployerV3} from "rain.interpreter.interface/interface/IExpressionDeployerV3.sol"; +import { EvaluableConfigV3, SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {IInterpreterV2,SourceIndexV2} from "rain.interpreter.interface/interface/IInterpreterV2.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol"; +import {StrategyTests, IRouteProcessor, LibStrategyDeployment, LibComposeOrders} from "h20.test-std/StrategyTests.sol"; +import {LibEncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; +import {StateNamespace, LibNamespace, FullyQualifiedNamespace} from "rain.interpreter.interface/lib/ns/LibNamespace.sol"; +import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol"; +import {SafeERC20, IERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; +import "h20.test-std/lib/LibProcessStream.sol"; + +uint256 constant VAULT_ID = uint256(keccak256("vault")); + + +/// @dev https://basescan.org/address/0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D +IERC20 constant BASE_WLTH= IERC20(0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D); + +/// @dev https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 +IERC20 constant BASE_USDC = IERC20(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913); + +function baseWlthIo() pure returns (IO memory) { + return IO(address(BASE_WLTH), 18, VAULT_ID); +} + +function baseUsdcIo() pure returns (IO memory) { + return IO(address(BASE_USDC), 6, VAULT_ID); +} + +contract WlthLimitOrderSingleTest is StrategyTests { + + using SafeERC20 for IERC20; + using Strings for address; + + uint256 constant FORK_BLOCK_NUMBER = 16833054; + + function selectFork() internal { + uint256 fork = vm.createFork(vm.envString("RPC_URL_BASE")); + vm.selectFork(fork); + vm.rollFork(FORK_BLOCK_NUMBER); + } + + function getNamespace() public view returns (FullyQualifiedNamespace) { + return LibNamespace.qualifyNamespace(StateNamespace.wrap(0), address(this)); + } + + function setUp() public { + selectFork(); + + PARSER = IParserV1(0xF836f2746B407136a5bCB515495949B1edB75184); + STORE = IInterpreterStoreV2(0x6E4b01603edBDa617002A077420E98C86595748E); + INTERPRETER = IInterpreterV2(0x379b966DC6B117dD47b5Fc5308534256a4Ab1BCC); + EXPRESSION_DEPLOYER = IExpressionDeployerV3(0x56394785a22b3BE25470a0e03eD9E0a939C47b9b); + ORDERBOOK = IOrderBookV3(0x2AeE87D75CD000583DAEC7A28db103B1c0c18b76); + ARB_INSTANCE = IOrderBookV3ArbOrderTaker(0x199b22ce0c9fD88476cCaA2d2aB253Af38BAE3Ae); + ROUTE_PROCESSOR = IRouteProcessor(address(0x83eC81Ae54dD8dca17C3Dd4703141599090751D1)); + EXTERNAL_EOA = address(0x654FEf5Fb8A1C91ad47Ba192F7AA81dd3C821427); + APPROVED_EOA = address(0x669845c29D9B1A64FFF66a55aA13EB4adB889a88); + ORDER_OWNER = address(0x19f95a84aa1C48A2c6a7B2d5de164331c86D030C); + + EXTERNAL_EOA = address(0x654FEf5Fb8A1C91ad47Ba192F7AA81dd3C821427); + APPROVED_EOA = address(0x669845c29D9B1A64FFF66a55aA13EB4adB889a88); + ORDER_OWNER = address(0x19f95a84aa1C48A2c6a7B2d5de164331c86D030C); + } + + function testStopLimitSell() public { + IO[] memory inputVaults = new IO[](1); + inputVaults[0] = baseUsdcIo(); + + IO[] memory outputVaults = new IO[](1); + outputVaults[0] = baseWlthIo(); + + LibStrategyDeployment.StrategyDeployment memory strategy = LibStrategyDeployment.StrategyDeployment( + getEncodedBuyWlthRoute(address(ARB_INSTANCE)), + getEncodedSellWlthRoute(address(ARB_INSTANCE)), + 0, + 0, + 1e6, + 10000e18, + 0, + 0, + "strategies/stop-limit.rain", + "stop-limit-order.buy.prod", + "./lib/h20.test-std/lib/rain.orderbook", + "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", + inputVaults, + outputVaults + ); + + OrderV2 memory order = addOrderDepositOutputTokens(strategy); + + // { + // vm.recordLogs(); + // // `arb()` called + // takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + + // Vm.Log[] memory entries = vm.getRecordedLogs(); + // (uint256 strategyAmount, uint256 strategyRatio) = getCalculationContext(entries); + + // assertEq(strategyRatio, 0.02e18); + // assertEq(strategyAmount, 50e18); + // } + // { + // vm.expectRevert("Max order count"); + // takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + // } + + } + + + + function getEncodedBuyWlthRoute(address toAddress) internal pure returns (bytes memory) { + bytes memory ROUTE_PRELUDE = + hex"02833589fCD6eDb6E08f4c7C32D4f71b54bdA0291301ffff011536EE1506e24e5A36Be99C73136cD82907A902E01"; + + return abi.encode(bytes.concat(ROUTE_PRELUDE, abi.encodePacked(address(toAddress)))); + } + + function getEncodedSellWlthRoute(address toAddress) internal pure returns (bytes memory) { + bytes memory ROUTE_PRELUDE = + hex"0299b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D01ffff011536EE1506e24e5A36Be99C73136cD82907A902E00"; + + return abi.encode(bytes.concat(ROUTE_PRELUDE, abi.encodePacked(address(toAddress)))); + } + +} + + From 3c9a5f0a3d14d2d87a35065ee65c3f9d1574f08a Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 8 Jul 2024 22:02:54 +0530 Subject: [PATCH 2/8] update stop-limit --- strategies/stop-limit.rain | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/strategies/stop-limit.rain b/strategies/stop-limit.rain index 5117821..c2db92b 100644 --- a/strategies/stop-limit.rain +++ b/strategies/stop-limit.rain @@ -1,14 +1,8 @@ -# Limit Order Strategy -# 5 buy and sell targets -# Each target you set buy price and amount and sell price and amount -# Target Network: Base -# Quote (Input / Incoming): USDC or WLTH -# Base (Output / Outgoing): WLTH or USDC -# Token contract: https://basescan.org/address/0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D -# Token github: NA -# Liquidity protocol: Uniswap V3 -# Liquidity pool address: https://www.dextools.io/app/en/base/pair-explorer/0x1536ee1506e24e5a36be99c73136cd82907a902e?t=1717921711270 -# Liquidity pool fee: 0.3% +# Stop Limit Orders. +# +# The stop price dictates the price whether the order is triggered, then the limit price dictates the price at which the order is filled. +# Once the market price reaches below the stop price, the order becomes a limit order and then the limit price dictates the price at which the order is filled. +# https://www.investopedia.com/terms/s/stop-limitorder.asp networks: base-community: @@ -69,7 +63,7 @@ scenarios: orderbook-subparser: 0x8D96ea3EF24D7123882c51CE4325b89bc0d63f9e # Uniswap V3 factory addresses and init code - uniswap-v3-factory: '[uniswap-v3-factory]' + uniswap-v3-factory: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD uniswap-v3-init-code: '[uniswap-v3-init-code]' # Uniswap V3 pool fee @@ -255,7 +249,7 @@ deployments: using-words-from uniswap-words orderbook-subparser orcale-price: call<'get-oracle-price>(), - :ensure<'ensure-stop-loss>(orcale-price), + :call<'ensure-stop-loss>(orcale-price), final-amount final-ratio: call<'get-ratio-amount>(), :call<'plottables>(final-amount final-ratio); From b66d2158ac0cfd24249574d092eea02155e0a19d Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 8 Jul 2024 23:17:02 +0530 Subject: [PATCH 3/8] update --- strategies/stop-limit.rain | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/strategies/stop-limit.rain b/strategies/stop-limit.rain index c2db92b..b69ca45 100644 --- a/strategies/stop-limit.rain +++ b/strategies/stop-limit.rain @@ -131,44 +131,44 @@ charts: scenario: stop-limit-order.buy.metric metrics: - label: Stop Price - value: 0.3.2 + value: 0.5.2 unit-suffix: " WLTH/USDC" - label: Price - value: 0.3.3 + value: 0.5.3 unit-suffix: " WLTH" - description: 'Unit price, WLTH per USDC (stack item 0.3.2)' + description: 'Unit price, WLTH per USDC (stack item 0.5.3)' - label: Effective Price (as visible on dex tools) - value: 0.3.4 + value: 0.5.4 unit-suffix: " USDC" description: 'Effective price in USDC denomination' - label: Units - value: 0.3.5 + value: 0.5.5 unit-suffix: " USDC" - description: 'Units purchased (stack item 0.3.3)' + description: 'Units purchased (stack item 0.5.5)' - label: Total Price - value: 0.3.6 + value: 0.5.6 unit-suffix: " WLTH" description: 'Amount of WLTH recevied.' Buy USDC, sell WLTH single limit order: scenario: stop-limit-order.sell.metric metrics: - label: Stop Price - value: 0.3.2 + value: 0.5.2 unit-suffix: " USDC/WLTH" - label: Price - value: 0.3.3 + value: 0.5.3 unit-suffix: " USDC" - description: 'Unit price, USDC per WLTH (stack item 0.3.2)' + description: 'Unit price, USDC per WLTH (stack item 0.5.3)' - label: Effective Price (as visible on dex tools) - value: 0.3.3 + value: 0.5.3 unit-suffix: " USDC" description: 'Effective price in USDC denomination' - label: Units - value: 0.3.4 + value: 0.5.4 unit-suffix: " WLTH" - description: 'Units sold (stack item 0.3.3)' + description: 'Units sold (stack item 0.5.4)' - label: Total Price - value: 0.3.6 + value: 0.5.6 unit-suffix: " USDC" description: 'Amount of USDC recevied.' From 3ba37dd33a3c642d70800a931fc450d8ff47fc25 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 8 Jul 2024 23:31:39 +0530 Subject: [PATCH 4/8] test --- test/StopLimitTest.t.sol | 109 +++++++++++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 15 deletions(-) diff --git a/test/StopLimitTest.t.sol b/test/StopLimitTest.t.sol index e734cea..c433a57 100644 --- a/test/StopLimitTest.t.sol +++ b/test/StopLimitTest.t.sol @@ -88,12 +88,12 @@ contract WlthLimitOrderSingleTest is StrategyTests { getEncodedSellWlthRoute(address(ARB_INSTANCE)), 0, 0, - 1e6, + 10000e6, 10000e18, 0, 0, "strategies/stop-limit.rain", - "stop-limit-order.buy.prod", + "stop-limit-order.sell.prod", "./lib/h20.test-std/lib/rain.orderbook", "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", inputVaults, @@ -102,24 +102,103 @@ contract WlthLimitOrderSingleTest is StrategyTests { OrderV2 memory order = addOrderDepositOutputTokens(strategy); - // { - // vm.recordLogs(); - // // `arb()` called - // takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + { + vm.recordLogs(); + // `arb()` called + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); - // Vm.Log[] memory entries = vm.getRecordedLogs(); - // (uint256 strategyAmount, uint256 strategyRatio) = getCalculationContext(entries); + Vm.Log[] memory entries = vm.getRecordedLogs(); + (uint256 strategyAmount, uint256 strategyRatio) = getCalculationContext(entries); - // assertEq(strategyRatio, 0.02e18); - // assertEq(strategyAmount, 50e18); - // } - // { - // vm.expectRevert("Max order count"); - // takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); - // } + assertEq(strategyRatio, 0.02e18); + assertEq(strategyAmount, 50e18); + } + { + vm.expectRevert("Max order count"); + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } } + function testStopLimitBuy() public { + IO[] memory inputVaults = new IO[](1); + inputVaults[0] = baseWlthIo(); + + IO[] memory outputVaults = new IO[](1); + outputVaults[0] = baseUsdcIo(); + + LibStrategyDeployment.StrategyDeployment memory strategy = LibStrategyDeployment.StrategyDeployment( + getEncodedSellWlthRoute(address(ARB_INSTANCE)), + getEncodedBuyWlthRoute(address(ARB_INSTANCE)), + 0, + 0, + 100000e18, + 10000e6, + 0, + 0, + "strategies/stop-limit.rain", + "stop-limit-order.buy.prod", + "./lib/h20.test-std/lib/rain.orderbook", + "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", + inputVaults, + outputVaults + ); + + OrderV2 memory order = addOrderDepositOutputTokens(strategy); + + { + vm.recordLogs(); + // `arb()` called + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + (uint256 strategyAmount, uint256 strategyRatio) = getCalculationContext(entries); + + assertEq(strategyRatio, 35e18); + assertEq(strategyAmount, 1e18); + } + { + vm.expectRevert("Max order count"); + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } + } + + function testEnsureStopLimit() public { + + IO[] memory inputVaults = new IO[](1); + inputVaults[0] = baseUsdcIo(); + + IO[] memory outputVaults = new IO[](1); + outputVaults[0] = baseWlthIo(); + + LibStrategyDeployment.StrategyDeployment memory strategy = LibStrategyDeployment.StrategyDeployment( + getEncodedBuyWlthRoute(address(ARB_INSTANCE)), + getEncodedSellWlthRoute(address(ARB_INSTANCE)), + 0, + 0, + 10000e6, + 10000e18, + 0, + 0, + "strategies/stop-limit.rain", + "stop-limit-order.sell.prod", + "./lib/h20.test-std/lib/rain.orderbook", + "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", + inputVaults, + outputVaults + ); + + OrderV2 memory order = addOrderDepositOutputTokens(strategy); + + moveExternalPrice( + strategy.inputVaults[strategy.inputTokenIndex].token, + strategy.outputVaults[strategy.outputTokenIndex].token, + 100000e6, + strategy.makerRoute + ); + vm.expectRevert("Stop price."); + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } function getEncodedBuyWlthRoute(address toAddress) internal pure returns (bytes memory) { From 84bf2780b782ab7fa6cdf7dbe6931295617a4044 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Tue, 9 Jul 2024 14:07:56 +0530 Subject: [PATCH 5/8] update --- strategies/{stop-limit.rain => wlth/wlth-stop-limit.rain} | 6 ++++-- test/{StopLimitTest.t.sol => wlth/WlthStopLimitTest.t.sol} | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) rename strategies/{stop-limit.rain => wlth/wlth-stop-limit.rain} (97%) rename test/{StopLimitTest.t.sol => wlth/WlthStopLimitTest.t.sol} (98%) diff --git a/strategies/stop-limit.rain b/strategies/wlth/wlth-stop-limit.rain similarity index 97% rename from strategies/stop-limit.rain rename to strategies/wlth/wlth-stop-limit.rain index b69ca45..ca21b2d 100644 --- a/strategies/stop-limit.rain +++ b/strategies/wlth/wlth-stop-limit.rain @@ -1,7 +1,9 @@ # Stop Limit Orders. # # The stop price dictates the price whether the order is triggered, then the limit price dictates the price at which the order is filled. -# Once the market price reaches below the stop price, the order becomes a limit order and then the limit price dictates the price at which the order is filled. +# Once the market price reaches below the stop price, the order becomes a limit order and then the limit price dictates the price at +# which the order is filled. +# The only difference between a stop-limit and limit order is that the order won't even be visible until the stop price is reached. # https://www.investopedia.com/terms/s/stop-limitorder.asp networks: @@ -79,7 +81,7 @@ scenarios: stop-ratio: 45 # io-ratio and amount for first limit order. - limit-ratio-1: 35 + limit-ratio- 1: 35 ouptut-amount-1: 1 # Binding to get current orcale price diff --git a/test/StopLimitTest.t.sol b/test/wlth/WlthStopLimitTest.t.sol similarity index 98% rename from test/StopLimitTest.t.sol rename to test/wlth/WlthStopLimitTest.t.sol index c433a57..5865e94 100644 --- a/test/StopLimitTest.t.sol +++ b/test/wlth/WlthStopLimitTest.t.sol @@ -92,7 +92,7 @@ contract WlthLimitOrderSingleTest is StrategyTests { 10000e18, 0, 0, - "strategies/stop-limit.rain", + "strategies/wlth/wlth-stop-limit.rain", "stop-limit-order.sell.prod", "./lib/h20.test-std/lib/rain.orderbook", "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", @@ -136,7 +136,7 @@ contract WlthLimitOrderSingleTest is StrategyTests { 10000e6, 0, 0, - "strategies/stop-limit.rain", + "strategies/wlth/wlth-stop-limit.rain", "stop-limit-order.buy.prod", "./lib/h20.test-std/lib/rain.orderbook", "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", @@ -180,7 +180,7 @@ contract WlthLimitOrderSingleTest is StrategyTests { 10000e18, 0, 0, - "strategies/stop-limit.rain", + "strategies/wlth/wlth-stop-limit.rain", "stop-limit-order.sell.prod", "./lib/h20.test-std/lib/rain.orderbook", "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", From adddf3451b2ff9fb2c2a488859e488090a91f727 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Tue, 9 Jul 2024 16:25:28 +0530 Subject: [PATCH 6/8] update stop-limit --- strategies/wlth/wlth-stop-limit.rain | 17 +++-- test/wlth/WlthStopLimitTest.t.sol | 96 +++++++++++++--------------- 2 files changed, 55 insertions(+), 58 deletions(-) diff --git a/strategies/wlth/wlth-stop-limit.rain b/strategies/wlth/wlth-stop-limit.rain index ca21b2d..3d875d0 100644 --- a/strategies/wlth/wlth-stop-limit.rain +++ b/strategies/wlth/wlth-stop-limit.rain @@ -81,7 +81,7 @@ scenarios: stop-ratio: 45 # io-ratio and amount for first limit order. - limit-ratio- 1: 35 + limit-ratio-1: 46 ouptut-amount-1: 1 # Binding to get current orcale price @@ -92,7 +92,7 @@ scenarios: bindings: plottables: '''plottables-prod' get-trade-count: '''get-trade-count-prod' - ensure-stop-loss: '''ensure-stop-loss-prod' + ensure-stop-loss: '''ensure-stop-loss-increase' metric: runs: 1 bindings: @@ -106,7 +106,7 @@ scenarios: output-token-address: 0x99b2B1A2aDB02B38222ADcD057783D7e5D1FCC7D # Stop ratio, once market price reaches below the stop price order will be active. - stop-ratio: 0.05 + stop-ratio: 0.022 # io-ratio and amount for first order. limit-ratio-1: 0.02 @@ -120,7 +120,7 @@ scenarios: bindings: plottables: '''plottables-prod' get-trade-count: '''get-trade-count-prod' - ensure-stop-loss: '''ensure-stop-loss-prod' + ensure-stop-loss: '''ensure-stop-loss-decrease' metric: runs: 1 bindings: @@ -237,13 +237,20 @@ deployments: pool-fee ); -#ensure-stop-loss-prod +#ensure-stop-loss-decrease current-orcale-price: , :ensure( less-than(current-orcale-price stop-ratio) "Stop price." ); +#ensure-stop-loss-increase + current-orcale-price: , + :ensure( + greater-than(current-orcale-price stop-ratio) + "Stop price." + ); + #ensure-stop-loss-metric _: ; diff --git a/test/wlth/WlthStopLimitTest.t.sol b/test/wlth/WlthStopLimitTest.t.sol index 5865e94..be3cbba 100644 --- a/test/wlth/WlthStopLimitTest.t.sol +++ b/test/wlth/WlthStopLimitTest.t.sol @@ -45,7 +45,7 @@ contract WlthLimitOrderSingleTest is StrategyTests { using SafeERC20 for IERC20; using Strings for address; - uint256 constant FORK_BLOCK_NUMBER = 16833054; + uint256 constant FORK_BLOCK_NUMBER = 16863755; function selectFork() internal { uint256 fork = vm.createFork(vm.envString("RPC_URL_BASE")); @@ -74,9 +74,10 @@ contract WlthLimitOrderSingleTest is StrategyTests { EXTERNAL_EOA = address(0x654FEf5Fb8A1C91ad47Ba192F7AA81dd3C821427); APPROVED_EOA = address(0x669845c29D9B1A64FFF66a55aA13EB4adB889a88); ORDER_OWNER = address(0x19f95a84aa1C48A2c6a7B2d5de164331c86D030C); - } + } + + function testEnsureStopLimitSell() public { - function testStopLimitSell() public { IO[] memory inputVaults = new IO[](1); inputVaults[0] = baseUsdcIo(); @@ -100,9 +101,23 @@ contract WlthLimitOrderSingleTest is StrategyTests { outputVaults ); - OrderV2 memory order = addOrderDepositOutputTokens(strategy); + OrderV2 memory order = addOrderDepositOutputTokens(strategy); + + // Current Price is not below the market price. + { + vm.expectRevert("Stop price."); + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } + // When current price goes below the market price the order suceeds. { + moveExternalPrice( + strategy.outputVaults[strategy.outputTokenIndex].token, + strategy.inputVaults[strategy.inputTokenIndex].token, + 200000e18, + strategy.takerRoute + ); + vm.recordLogs(); // `arb()` called takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); @@ -112,15 +127,15 @@ contract WlthLimitOrderSingleTest is StrategyTests { assertEq(strategyRatio, 0.02e18); assertEq(strategyAmount, 50e18); - } - { + vm.expectRevert("Max order count"); takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); - } - + + } } - function testStopLimitBuy() public { + function testEnsureStopLimitBuy() public { + IO[] memory inputVaults = new IO[](1); inputVaults[0] = baseWlthIo(); @@ -132,7 +147,7 @@ contract WlthLimitOrderSingleTest is StrategyTests { getEncodedBuyWlthRoute(address(ARB_INSTANCE)), 0, 0, - 100000e18, + 200000e18, 10000e6, 0, 0, @@ -144,7 +159,21 @@ contract WlthLimitOrderSingleTest is StrategyTests { outputVaults ); - OrderV2 memory order = addOrderDepositOutputTokens(strategy); + OrderV2 memory order = addOrderDepositOutputTokens(strategy); + + // Current Price is not above the market price. + { + vm.expectRevert("Stop price."); + takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } + { + moveExternalPrice( + strategy.inputVaults[strategy.inputTokenIndex].token, + strategy.outputVaults[strategy.outputTokenIndex].token, + strategy.makerAmount, + strategy.makerRoute + ); + } { vm.recordLogs(); @@ -154,53 +183,14 @@ contract WlthLimitOrderSingleTest is StrategyTests { Vm.Log[] memory entries = vm.getRecordedLogs(); (uint256 strategyAmount, uint256 strategyRatio) = getCalculationContext(entries); - assertEq(strategyRatio, 35e18); + assertEq(strategyRatio, 46e18); assertEq(strategyAmount, 1e18); - } - { + vm.expectRevert("Max order count"); takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); - } - } - - function testEnsureStopLimit() public { - - IO[] memory inputVaults = new IO[](1); - inputVaults[0] = baseUsdcIo(); - - IO[] memory outputVaults = new IO[](1); - outputVaults[0] = baseWlthIo(); - - LibStrategyDeployment.StrategyDeployment memory strategy = LibStrategyDeployment.StrategyDeployment( - getEncodedBuyWlthRoute(address(ARB_INSTANCE)), - getEncodedSellWlthRoute(address(ARB_INSTANCE)), - 0, - 0, - 10000e6, - 10000e18, - 0, - 0, - "strategies/wlth/wlth-stop-limit.rain", - "stop-limit-order.sell.prod", - "./lib/h20.test-std/lib/rain.orderbook", - "./lib/h20.test-std/lib/rain.orderbook/Cargo.toml", - inputVaults, - outputVaults - ); - - OrderV2 memory order = addOrderDepositOutputTokens(strategy); - - moveExternalPrice( - strategy.inputVaults[strategy.inputTokenIndex].token, - strategy.outputVaults[strategy.outputTokenIndex].token, - 100000e6, - strategy.makerRoute - ); - vm.expectRevert("Stop price."); - takeArbOrder(order, strategy.takerRoute, strategy.inputTokenIndex, strategy.outputTokenIndex); + } } - function getEncodedBuyWlthRoute(address toAddress) internal pure returns (bytes memory) { bytes memory ROUTE_PRELUDE = hex"02833589fCD6eDb6E08f4c7C32D4f71b54bdA0291301ffff011536EE1506e24e5A36Be99C73136cD82907A902E01"; From 6dcb3a4b46bc1f6cf713091f2083a97a56659657 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Mon, 15 Jul 2024 16:38:01 +0530 Subject: [PATCH 7/8] update stop-limit --- strategies/wlth/wlth-stop-limit.rain | 42 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/strategies/wlth/wlth-stop-limit.rain b/strategies/wlth/wlth-stop-limit.rain index 3d875d0..7ac0eb5 100644 --- a/strategies/wlth/wlth-stop-limit.rain +++ b/strategies/wlth/wlth-stop-limit.rain @@ -8,7 +8,7 @@ networks: base-community: - rpc: https://rpc.ankr.com/base/8e4cac5728d95471ae55724953c7ae6f19a227ac05146276f09717692b732dae + rpc: https://mainnet.base.org chain-id: 8453 network-id: 8453 currency: ETH @@ -133,44 +133,44 @@ charts: scenario: stop-limit-order.buy.metric metrics: - label: Stop Price - value: 0.5.2 + value: 0.6.2 unit-suffix: " WLTH/USDC" - label: Price - value: 0.5.3 + value: 0.6.3 unit-suffix: " WLTH" - description: 'Unit price, WLTH per USDC (stack item 0.5.3)' + description: 'Unit price, WLTH per USDC (stack item 0.6.3)' - label: Effective Price (as visible on dex tools) - value: 0.5.4 + value: 0.6.4 unit-suffix: " USDC" description: 'Effective price in USDC denomination' - label: Units - value: 0.5.5 + value: 0.6.5 unit-suffix: " USDC" - description: 'Units purchased (stack item 0.5.5)' + description: 'Units purchased (stack item 0.6.5)' - label: Total Price - value: 0.5.6 + value: 0.6.6 unit-suffix: " WLTH" description: 'Amount of WLTH recevied.' Buy USDC, sell WLTH single limit order: scenario: stop-limit-order.sell.metric metrics: - label: Stop Price - value: 0.5.2 + value: 0.6.2 unit-suffix: " USDC/WLTH" - label: Price - value: 0.5.3 + value: 0.6.3 unit-suffix: " USDC" - description: 'Unit price, USDC per WLTH (stack item 0.5.3)' + description: 'Unit price, USDC per WLTH (stack item 0.6.3)' - label: Effective Price (as visible on dex tools) - value: 0.5.3 + value: 0.6.3 unit-suffix: " USDC" description: 'Effective price in USDC denomination' - label: Units - value: 0.5.4 + value: 0.6.4 unit-suffix: " WLTH" - description: 'Units sold (stack item 0.5.4)' + description: 'Units sold (stack item 0.6.4)' - label: Total Price - value: 0.5.6 + value: 0.6.6 unit-suffix: " USDC" description: 'Amount of USDC recevied.' @@ -219,13 +219,13 @@ deployments: trade-count: get(trade-count-key); #get-ratio-amount - trade-count: call<'get-trade-count>(), - ouput-amount: conditions( - equal-to(trade-count 0) ouptut-amount-1 + count: , + output-amount: conditions( + equal-to(count 0) ouptut-amount-1 "Max order count" ), io-ratio: conditions( - equal-to(trade-count 0) limit-ratio-1 + equal-to(count 0) limit-ratio-1 "Max order count" ); @@ -259,8 +259,8 @@ deployments: orcale-price: call<'get-oracle-price>(), :call<'ensure-stop-loss>(orcale-price), - final-amount - final-ratio: call<'get-ratio-amount>(), + trade-count: call<'get-trade-count>(), + final-amount final-ratio: call<'get-ratio-amount>(trade-count), :call<'plottables>(final-amount final-ratio); #handle-io From 9950e328f7386be305b5eecb117044e5bef97306 Mon Sep 17 00:00:00 2001 From: TobyMeller <120049117+TobyMeller@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:34:53 +0100 Subject: [PATCH 8/8] typos --- strategies/wlth/wlth-stop-limit.rain | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/strategies/wlth/wlth-stop-limit.rain b/strategies/wlth/wlth-stop-limit.rain index 7ac0eb5..f13e4ad 100644 --- a/strategies/wlth/wlth-stop-limit.rain +++ b/strategies/wlth/wlth-stop-limit.rain @@ -82,7 +82,7 @@ scenarios: # io-ratio and amount for first limit order. limit-ratio-1: 46 - ouptut-amount-1: 1 + output-amount-1: 1 # Binding to get current orcale price get-oracle-price: '''get-oracle-price-univ3' @@ -110,7 +110,7 @@ scenarios: # io-ratio and amount for first order. limit-ratio-1: 0.02 - ouptut-amount-1: 50 + output-amount-1: 50 # Binding to get current orcale price get-oracle-price: '''get-oracle-price-univ3' @@ -193,7 +193,7 @@ deployments: #stop-ratio !Ratio below which trade is active #limit-ratio-1 !IO ratio for first limit order. -#ouptut-amount-1 !Output amount for first limit order. +#output-amount-1 !Output amount for first limit order. #get-oracle-price !Binding to get currect price from orcale. #ensure-stop-loss !Binding to ensure stop loss. @@ -211,8 +211,8 @@ deployments: _: stop-ratio, _: limit-ratio-1, effective-limit-ratio-1: inv(limit-ratio-1), - _: ouptut-amount-1, - input-amount-1: mul(limit-ratio-1 ouptut-amount-1); + _: output-amount-1, + input-amount-1: mul(limit-ratio-1 output-amount-1); #get-trade-count-prod trade-count-key: hash(order-hash() count-key), @@ -221,7 +221,7 @@ deployments: #get-ratio-amount count: , output-amount: conditions( - equal-to(count 0) ouptut-amount-1 + equal-to(count 0) output-amount-1 "Max order count" ), io-ratio: conditions(