From dd5c2c28776981f3155c30bc88002fd087e98491 Mon Sep 17 00:00:00 2001 From: Margarita Skomorokh Date: Sat, 28 Dec 2024 02:21:45 +0100 Subject: [PATCH] edited the BasicOrder article --- .../agent_factory.md | 4 + .../deploy_script.md | 15 +- .../build-a-basic-warden-agent/deployment.md | 16 +- .../main_contract.md | 161 +++++++++--------- .../build-a-basic-warden-agent/precompiles.md | 4 +- .../build-a-basic-warden-agent/structure.md | 4 +- 6 files changed, 101 insertions(+), 103 deletions(-) diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/agent_factory.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/agent_factory.md index f2025d7e1..c9e8f09ad 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/agent_factory.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/agent_factory.md @@ -10,6 +10,10 @@ The `OrderFactory` contract securely manages the creation and tracking of orders When triggered by a user, `OrderFactory` deploys a new [`BasicOrder`](main_contract) contract and registers it in the [registry](structure#3-implement-the-registry). +:::note Directory +Store `OrderFactory` in the [`/src`](https://github.com/warden-protocol/wardenprotocol/blob/main/solidity/orders/src) directory, alongside with other contracts. +::: + ## Create the `OrderFactory` contract Implement order creation and tracking in a file `OrderFactory.sol`: diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deploy_script.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deploy_script.md index 2bba98c1a..0ca407cc3 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deploy_script.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deploy_script.md @@ -8,14 +8,17 @@ sidebar_position: 7 This tutorial explains how to implement the main deployment script and the script for creating orders. +:::note Directory +Before you proceed, create a [`/script`](https://github.com/warden-protocol/wardenprotocol/blob/main/solidity/orders/mocks) directory for storing your scripts +::: ## 1. Implement the main deployment script The main deployment script handles the following tasks: -- Deploying [`Registry`](structure#3-implement-the-registry) -- Deploying [`OrderFactory`](agent_factory) -- Environment configuration +- Deploys [`Registry`](structure#3-implement-the-registry) +- Deploys [`OrderFactory`](agent_factory) +- Configures the environment To implement this script, use the following code: @@ -49,9 +52,9 @@ contract Deploy is Script { This script for creating orders handles the following tasks: -- Deploying [`BasicOrder`](main_contract) through [`OrderFactory`](agent_factory) -- Setting up [mock precompiles](precompiles) -- Parameter configuration +- Deploys [`BasicOrder`](main_contract) through [`OrderFactory`](agent_factory) +- Sets up [mock precompiles](precompiles) +- Configures parameters To implement this script, use the following code: diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deployment.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deployment.md index 1c4de3604..4618b73bc 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deployment.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/deployment.md @@ -48,19 +48,19 @@ To deploy the Agent, take the following steps: --broadcast \ --sig "run(uint256,uint8,(string,string),(uint256,uint256,address),(uint256,address[],address,uint256),uint64,uint64, uint64,bytes,bytes)" \ 3324181371 \ # the threshold price - 0 \ # LTE condition - '("ETH","USD")' \ # the price pair + 0 \ # the LTE condition + '("ETH","USD")' \ # the currency pair '(100000000000000,11155111,0x467b...)' \ # tx fields # ... other parameters ``` The key parameters to specify include the following: - - `thresholdPrice`: The price threshold to trigger execution - - `priceCondition`: 0 for LTE, 1 for GTE - - `pricePair`: The trading pair – e.g., "ETH/USD" + - `thresholdPrice`: The price threshold to trigger the execution of an order + - `priceCondition`: The price condition: 0 for LTE (`<=`), 1 for GTE (`>=`) + - `pricePair`: The currency pair - `creatorDefinedTxFields`: The chain and transaction details - - `swapData`: Uniswap swap parameters + - `swapData`: Swap parameters for Uniswap - `keyId`: The ID of the Warden key for signing transactions - `spaceNonce`: The nonce for the signing space - `actionTimeoutHeight`: The timeout for execution @@ -113,13 +113,13 @@ After deploying the Basic Agent, you can monitor and manage orders using the com ### Monitor events -- Monitor the `Executed` [event](main_contract#monitoring-and-events): +- Monitor the `Executed()` event emitted by the [`execute()`](main_contract#4-implement-trade-execution) function of the `BasicOrder` contract: ```bash cast logs $ORDER_ADDRESS "Executed()" ``` -- Monitor the `NewTx` [event](main_contract#monitoring-and-events): +- Monitor the `NewTx()` event emitted by the [registry](structure#3-implement-the-registry): ```bash cast logs $REGISTRY_ADDRESS "NewTx(address,bytes32)" diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/main_contract.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/main_contract.md index 174218c31..597ea2dfa 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/main_contract.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/main_contract.md @@ -10,19 +10,23 @@ The `BasicOrder` contract implements **orders** that monitor prices and automati To create an order, a user triggers the [`OrderFactory`](agent_factory) contract, and it deploys an instance of the `BasicOrder` contract. Then the trading Agent executes and manages orders, as shown in [Create the trading Agent structure](structure). -Create the `BasicOrder` contract +This article will guide you through creating the `BasicOrder` contract. + +:::note Directory +Store `BasicOrder` in the [`/src`](https://github.com/warden-protocol/wardenprotocol/blob/main/solidity/orders/src) directory, alongside with other contracts. +::: :::note GitHub You can find the full code on GitHub: [`/src/BasicOrder.sol`](https://github.com/warden-protocol/wardenprotocol/blob/main/solidity/orders/src/BasicOrder.sol) ::: -## 1. Create the core components +## 1. Create core components -First, let's define our state variables and imports: +First, define the state variables and imports: ```solidity title="/src/BasicOrder.sol" contract BasicOrder is IExecution, ReentrancyGuard { - // NEW: Constant for Uniswap interface + // A constant for the Uniswap interface string public constant SWAP_EXACT_ETH_FOR_TOKENS = "swapExactETHForTokens(uint256,address[],address,uint256)"; @@ -31,7 +35,7 @@ contract BasicOrder is IExecution, ReentrancyGuard { ISlinky private immutable SLINKY_PRECOMPILE; Registry private immutable REGISTRY; - // NEW: Enhanced state tracking + // State tracking Caller[] private _callers; bool private _executed; address private _keyAddress; @@ -39,7 +43,13 @@ contract BasicOrder is IExecution, ReentrancyGuard { } ``` -## 2. Create a constructor with validations +## 2. Create a constructor + +Now create a `constructor` with validations. As shown in the code below, your constructor should handle the following tasks: + +- Validate all inputs (the scheduler, the registry, price conditions) +- Set up connections with the price feed and the signing service +- Initialize order parameters ```solidity title="/src/BasicOrder.sol" constructor( @@ -48,27 +58,31 @@ constructor( address scheduler, address registry ) { - // NEW: Enhanced input validation + // Input validation if (scheduler == address(0)) revert InvalidScheduler(); if (registry == address(0)) revert InvalidRegistry(); if (_orderData.swapData.amountIn == 0) revert InvalidSwapDataAmountIn(); if (_orderData.swapData.to == address(0)) revert InvalidSwapDataTo(); - // Initialize services and state + // Initialize services and the state WARDEN_PRECOMPILE = IWarden(IWARDEN_PRECOMPILE_ADDRESS); SLINKY_PRECOMPILE = ISlinky(ISLINKY_PRECOMPILE_ADDRESS); _callers.push(Caller.Scheduler); } ``` -## 3. Create the price monitoring logic +## 3. Implement price monitoring + +In the `canExecute()` function, implement the logic for monitoring prices. This function should handle these tasks: + +- Retrieve a price from the [Slinky mock precompile](precompiles#11-create-a-slinky-precompile) +- Check if the price meets a given condition: `>=` or `<=` than the threshold + (see the `PriceCondtion` enum in [`Types.sol`](structure#1-define-data-structures)) ```solidity title="/src/BasicOrder.sol" function canExecute() public view returns (bool value) { - // NEW: Enhanced price condition checking GetPriceResponse memory priceResponse = SLINKY_PRECOMPILE.getPrice(orderData.pricePair.base, orderData.pricePair.quote); - Types.PriceCondition condition = orderData.priceCondition; if (condition == Types.PriceCondition.GTE) { value = priceResponse.price.price >= orderData.thresholdPrice; @@ -80,7 +94,17 @@ function canExecute() public view returns (bool value) { } ``` -## 4. Implement the trade execution +## 4. Implement trade execution + +In the `execute()` function, implement the logic for executing trades. This function should do the following: + +- Verify the caller and conditions +- Pack swap data for Uniswap +- Create and encode a transaction +- Request a signature through Warden +- Emit the `Executed()` event +- Register the transaction in the [registry](structure#3-implement-the-registry) +- Return the execution status ```solidity title="/src/BasicOrder.sol" function execute( @@ -90,17 +114,17 @@ function execute( uint256 maxPriorityFeePerGas, uint256 maxFeePerGas ) external nonReentrant returns (bool, bytes32) { - // NEW: Security checks + // Security checks if (msg.sender != _scheduler) revert Unauthorized(); if (isExecuted()) revert ExecutedError(); if (!canExecute()) revert ConditionNotMet(); - // Build and encode transaction + // Build and encode a transaction bytes memory unsignedTx = _buildTransaction( nonce, gas, maxPriorityFeePerGas, maxFeePerGas ); - // Request signature and register transaction + // Request a signature and register the transaction _executed = _requestSignature(unsignedTx); if (_executed) { emit Executed(); @@ -111,87 +135,54 @@ function execute( } ``` -## Flow - -1.Construction: - -- Validates all inputs (scheduler, registry, price conditions) -- Sets up price feed and signing service connections -- Initializes order parameters - -2.Price monitoring (`canExecute`): - -- Check if price meets conditions. - -3.Trade execution (`execute`): - -a. Verify caller and conditions -b. Pack swap data for Uniswap -c. Create and encode transaction -d. Request signature through Warden -e. Register transaction in Registry -f. Return execution status - -## Key security features - -1.**ReentrancyGuard protection** - -- Prevents reentrancy attacks during execution -- Guards critical state changes - -2.**Input validation** +## 5. Test the contract +To test price conditions, use the following code: + ```solidity -// Example of comprehensive validation -if (_orderData.thresholdPrice == 0) revert InvalidThresholdPrice(); -if (_orderData.creatorDefinedTxFields.to == address(0)) revert InvalidTxTo(); +function test_priceConditions() public { + // Test the GTE condition + vm.mockCall( + address(SLINKY_PRECOMPILE), + abi.encodeWithSelector(ISlinky.getPrice.selector), + abi.encode(price) + ); + assertTrue(order.canExecute()); +} ``` -3.**Transaction safety** - -- EIP-1559 transaction support -- Secure RLP encoding -- Transaction hash verification - -## Monitoring and events +To test the execution flow, use this: ```solidity -event Executed(); -event PriceConditionMet(uint256 currentPrice, uint256 thresholdPrice); -event TransactionRegistered(bytes32 indexed txHash); +function test_execution() public { + vm.startPrank(scheduler); + (bool success, bytes32 txHash) = order.execute( + 1, // nonce + 200000, // gas + 0, // unused + 2 gwei, // maxPriorityFeePerGas + 100 gwei // maxFeePerGas + ); + assertTrue(success); +} ``` -## Testing considerations - -1. **Test price conditions** - - ```solidity - function test_priceConditions() public { - // Test GTE condition - vm.mockCall( - address(SLINKY_PRECOMPILE), - abi.encodeWithSelector(ISlinky.getPrice.selector), - abi.encode(price) - ); - assertTrue(order.canExecute()); - } - ``` +## Key security features -2. **Test the execution flow** - +Key security features of the `BasicOrder` contract include the following: +- **ReentrancyGuard protection** + - Preventing reentrancy attacks during execution + - Guarding critical state changes +- **Input validation** ```solidity - function test_execution() public { - vm.startPrank(scheduler); - (bool success, bytes32 txHash) = order.execute( - 1, // nonce - 200000, // gas - 0, // unused - 2 gwei, // maxPriorityFeePerGas - 100 gwei // maxFeePerGas - ); - assertTrue(success); - } + // An example of comprehensive validation + if (_orderData.thresholdPrice == 0) revert InvalidThresholdPrice(); + if (_orderData.creatorDefinedTxFields.to == address(0)) revert InvalidTxTo(); ``` +- **Transaction safety** + - The support of EIP-1559 transactions + - Secure RLP encoding + - Transaction hash verification ## Next steps diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/precompiles.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/precompiles.md index c0e1683db..2816a6bb9 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/precompiles.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/precompiles.md @@ -8,9 +8,9 @@ sidebar_position: 4 **Mock precompiles** are essential for end-to-end testing of the Basic Agent. This article explains how to build and test two mock precompiles: **Slinky** and **Warden**. -:::note Structure +:::note Directories - Before you proceed, create a [`/mocks`](https://github.com/warden-protocol/wardenprotocol/blob/main/solidity/orders/mocks) directory for storing mock precompiles. -- You can test the precompiles in [/orders/test](https://github.com/warden-protocol/wardenprotocol/tree/main/solidity/orders/test). +- You can test the precompiles in [`/orders/test`](https://github.com/warden-protocol/wardenprotocol/tree/main/solidity/orders/test). ::: ## 1. Create mock precompiles diff --git a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/structure.md b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/structure.md index b7da4e744..503027878 100644 --- a/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/structure.md +++ b/docs/developer-docs/docs/build-an-app/build-a-basic-warden-agent/structure.md @@ -34,8 +34,8 @@ library Types { // A price condition for flexible trading enum PriceCondition { - LTE, // Less than or equal to threshold - GTE // Greater than or equal to threshold + LTE, // Less than or equal to the threshold + GTE // Greater than or equal to the threshold } // The main order configuration