Skip to content

Commit

Permalink
edited the BasicOrder article
Browse files Browse the repository at this point in the history
  • Loading branch information
ijonele committed Dec 28, 2024
1 parent a6b3ef7 commit dd5c2c2
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)";
Expand All @@ -31,15 +35,21 @@ 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;
bytes private _unsignedTx;
}
```

## 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(
Expand All @@ -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;
Expand All @@ -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(
Expand All @@ -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();
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit dd5c2c2

Please sign in to comment.