Skip to main content

Implement the creation of Orders

Overview

This article will guide you through creating the BasicOrderFactory contract. BasicOrderFactory, when triggered by OrderFactory, deploys Orders (instances of BasicOrder) and registers them in the registry.

This factory pattern supports deterministic address computation, front-running protection, and salt-based deployment security. Note that you can extend some parts to implement the creation of Orders with price prediction.

Directory

Store BasicOrderFactory in the /src directory, alongside with other contracts.

Full code

You can find the full code on GitHub: /src/BasicOrderFactory.sol

1. Create the contract

To start implementing the deployment of Orders, create a file BasicOrderFactory.sol:

/src/BasicOrderFactory.sol
contract BasicOrderFactory is ReentrancyGuard {
// The registry for order tracking
Registry public immutable REGISTRY;

// Track used deployment salts
mapping(bytes32 salt => bool used) public usedSalts;

event BasicOrderCreated(address indexed creator, address orderAddress);

constructor(address registry) {
REGISTRY = Registry(registry);
}
}

2. Implement the Order creation logic

In the same contract, implement the core function for deploying new Orders:

/src/BasicOrderFactory.sol
function createBasicOrder(
Types.BasicOrderData calldata orderData,
Types.CommonExecutionData calldata executionData,
CommonTypes.Coin[] calldata maxKeychainFees,
address scheduler,
bytes32 salt
) external nonReentrant returns (address orderAddress) {
// Front-running protection using tx.origin
address origin = tx.origin;
bytes32 guardedSalt = keccak256(
abi.encodePacked(uint256(uint160(origin)), salt)
);

if (usedSalts[guardedSalt]) {
revert SaltAlreadyUsed();
}

// Create the deployment bytecode
bytes memory bytecode = abi.encodePacked(
type(BasicOrder).creationCode,
abi.encode(
orderData,
executionData,
maxKeychainFees,
scheduler,
address(REGISTRY)
)
);

// Deploy with the CREATE3 opcode
orderAddress = Create3.create3(guardedSalt, bytecode);

// Verify the deployment
address expectedAddress = Create3.addressOf(guardedSalt);
if (orderAddress == address(0) ||
orderAddress != expectedAddress) {
revert OrderDeploymentFailed(guardedSalt);
}

// Register and track the Order
REGISTRY.register(orderAddress);
usedSalts[guardedSalt] = true;

emit BasicOrderCreated(msg.sender, orderAddress);
}

3. Add address computation

Allow users to preview the Order addresses:

/src/BasicOrderFactory.sol
function computeOrderAddress(
address origin,
bytes32 salt
) external view returns (address) {
bytes32 guardedSalt = keccak256(
abi.encodePacked(uint256(uint160(origin)), salt)
);
return Create3.addressOf(guardedSalt);
}

5. Implement tests

Finally, implement tests:

contract BasicOrderFactoryTest is Test {
function test_CreateOrder() public {
// Test basic creation
bytes32 salt = bytes32("test");
address expected = factory.computeOrderAddress(
address(this),
salt
);

address actual = factory.createBasicOrder(
orderData,
executionData,
fees,
scheduler,
salt
);

assertEq(actual, expected);
assertTrue(Registry(registry).isRegistered(actual));
}

function test_PreventSaltReuse() public {
bytes32 salt = bytes32("test");
factory.createBasicOrder(...);

vm.expectRevert(SaltAlreadyUsed.selector);
factory.createBasicOrder(...);
}
}

Extension points

To implement the creation Orders with price prediction, you need to extend the factory pattern with the following advanced features:

  • Complex validation
    function _validateAdvancedOrder(
    Types.AdvancedOrderData memory orderData
    ) internal pure returns (bool);
  • Prediction setup
    function _setupPrediction(
    address order,
    Types.PredictionData memory predData
    ) internal returns (uint64 futureId);
tip

When you were implementing the Order creation logic, you enabled deployment with the CREATE3 opcode. It ensures that Order addresses are known in advance, which becomes crucial for Orders with price prediction since they may need to reference each other.

Next steps

After creating the BasicOrderFactory contract, you can deploy an Order.