diff --git a/docs/docs/debugging.md b/docs/docs/debugging.md index 9e231cf..08ae987 100644 --- a/docs/docs/debugging.md +++ b/docs/docs/debugging.md @@ -1,6 +1,6 @@ --- title: Debugging -sidebar_position: 14 +sidebar_position: 15 description: The page dedicated to debugging of most frequent errors and issues on Reactive Network. slug: /debugging hide_title: true diff --git a/docs/docs/testing.md b/docs/docs/testing.md index 6092379..4cc9ae8 100644 --- a/docs/docs/testing.md +++ b/docs/docs/testing.md @@ -1,7 +1,7 @@ --- title: Testing with Foundry -sidebar_position: 15 -description: Test Reactive Contracts locally with Foundry using reactive-test-lib. Simulate event-driven, cross-chain smart contract execution with forge test — no testnet required. +sidebar_position: 14 +description: Test Reactive Contracts locally with Foundry using reactive-test-lib. Simulate event-driven, cross-chain smart contract execution with forge test without using a testnet. slug: /testing hide_title: true --- @@ -10,16 +10,14 @@ hide_title: true ## Overview -Test Reactive Contracts locally with Foundry. No testnet, no waiting. +[Reactive Test Lib](https://github.com/Reactive-Network/reactive-test-lib) allows to simulate the full Reactive Network's lifecycle from triggering events to callback execution. You can test Reactive contracts locally with Foundry. The library replaces the system contract, ReactVM, and callback proxies with mock implementations that run entirely within `forge test`. -[Reactive Test Lib](https://github.com/Reactive-Network/reactive-test-lib) swaps out the System Contract, ReactVM, and Callback Proxy for mock versions that run entirely in `forge test`. You get the full reactive flow (event trigger through callback execution) on a single EVM instance. +Supported features: -What works: - -- Event subscriptions (wildcards included) +- Event subscriptions (including wildcards) - Full `react()` pipeline - Cross-chain and same-chain callbacks -- Same-chain callbacks via `SERVICE_ADDR` +- Same-chain callbacks via `SERVICE_ADDR` (`0x0000000000000000000000000000000000fffFfF`) - Cron-based triggers - Multi-step workflows (bridges, confirmations) - Automatic chain ID resolution @@ -36,9 +34,9 @@ Add to `remappings.txt`: reactive-test-lib/=lib/reactive-test-lib/src/ ``` -## What Gets Mocked +## Mocked Components -The library replaces three main Reactive components with local mocks: +The library replaces three Reactive components with local mocks: | Real Component | Mock | Purpose | |-----------------|----------------------|------------------------------------------------------| @@ -46,7 +44,7 @@ The library replaces three main Reactive components with local mocks: | ReactVM | `ReactiveSimulator` | Event delivery and `react()` invocation | | Callback Proxy | `MockCallbackProxy` | Cross-chain callback execution with RVM ID injection | -Chain IDs are purely logical, nothing actually crosses chains. It all runs on one EVM. +Chain IDs are purely logical. All execution takes place on a single EVM instance. ## Getting Started @@ -61,21 +59,21 @@ import {CallbackResult} from "reactive-test-lib/interfaces/IReactiveInterfaces.s contract MyReactiveTest is ReactiveTest { function setUp() public override { super.setUp(); - // your contracts go here + // deploy contracts here } } ``` -`super.setUp()` handles the plumbing: +`super.setUp()` performs the following setup: -1. Deploys `MockSystemContract` and etches its code to `SERVICE_ADDR` (`0x...fffFfF`) +1. Deploys `MockSystemContract` and writes its code to `SERVICE_ADDR` (`0x0000000000000000000000000000000000fffFfF`) 2. Deploys `MockCallbackProxy` for cross-chain callback execution 3. Sets `rvmId` to `address(this)` -4. Sets `reactiveChainId` to `REACTIVE_CHAIN_ID` (`0x512512`) +4. Sets `reactiveChainId` to `REACTIVE_CHAIN_ID` (`0x512512`) -Any contract that calls `subscribe()` on `SERVICE_ADDR` in its constructor (including anything extending `AbstractReactive`) will register subscriptions automatically. +Any contract that calls `subscribe()` on `SERVICE_ADDR` in its constructor (including contracts extending `AbstractReactive`) will register subscriptions automatically. -### Deploying Your Contracts +### Deploying Contracts Set up your Origin, Reactive, and Callback contracts in `setUp()`. Pass `address(proxy)` to anything that extends `AbstractCallback`: @@ -88,18 +86,18 @@ cb = new BasicDemoL1Callback(address(proxy)); // Reactive contract — constructor calls subscribe() on MockSystemContract rc = new BasicDemoReactiveContract( - address(sys), // system contract - SEPOLIA_CHAIN_ID, // origin chain - SEPOLIA_CHAIN_ID, // destination chain - address(origin), // contract to watch - uint256(keccak256("Received(address,address,uint256)")), // topic_0 - address(cb) // callback target +address(sys), // system contract +SEPOLIA_CHAIN_ID, // origin chain +SEPOLIA_CHAIN_ID, // destination chain +address(origin), // contract to watch +uint256(keccak256("Received(address,address,uint256)")), // topic_0 +address(cb) // callback target ); ``` ### Running a Reactive Cycle -`triggerAndReact()` does the whole thing in one call: emit → match → react → execute callbacks. +`triggerAndReact()` executes a full cycle in a single call: emit event, match subscription, invoke react(), and execute callbacks. ```solidity function testCallbackFires() public { @@ -128,7 +126,7 @@ CallbackResult[] memory results = triggerAndReactWithValue( ## Basic Demo -The [Basic Demo](https://github.com/Reactive-Network/reactive-smart-contract-demos/tree/main/src/demos/basic) is the simplest Reactive Contract pattern. An origin contract emits an event when receiving ETH. The Reactive contract subscribes to that event and, if the value exceeds a threshold, emits a Callback to the destination contract. +The [Basic Demo](https://github.com/Reactive-Network/reactive-smart-contract-demos/tree/main/src/demos/basic) shows the simplest Reactive contract pattern. An origin contract emits an event when receiving ETH. The Reactive contract subscribes to that event and, if the value exceeds a threshold, emits a callback to the destination contract. ```solidity contract BasicDemoTest is ReactiveTest { @@ -182,9 +180,9 @@ contract BasicDemoTest is ReactiveTest { ## Uniswap Stop Orders -The [Uniswap V2 Stop Order](https://github.com/Reactive-Network/reactive-smart-contract-demos/tree/main/src/demos/uniswap-v2-stop-order) demo watches a pair's reserves and triggers a swap when price crosses a threshold. The reactive contract subscribes to `Sync` events. +The [Uniswap V2 Stop Order Demo](https://github.com/Reactive-Network/reactive-smart-contract-demos/tree/main/src/demos/uniswap-v2-stop-order) monitors a pair's reserves and triggers a swap when the price crosses a threshold. The Reactive contract subscribes to `Sync` events. -To test it, deploy a mock pair that emits the event: +Testing requires a mock pair contract that emits the event: ```solidity contract StopOrderTest is ReactiveTest { @@ -226,24 +224,24 @@ contract StopOrderTest is ReactiveTest { ``` :::info[vmOnly Modifier] -If your Reactive contract uses `vmOnly` on `react()`, call `enableVmMode(address(rc))` after deploying it. This flips the internal `vm` flag to `true`. Skip this and `react()` reverts with `"VM only"`. +If the Reactive contract uses `vmOnly` on `react()`, call `enableVmMode(address(rc))` after deployment. This sets the internal `vm` flag to `true`. Without this call, `react()` reverts with `"VM only"`. ::: ## Self-Callbacks -Some Reactive Contracts emit callbacks targeting themselves on Reactive Network, not an external chain. The [REACT Bridge](https://github.com/Reactive-Network/react-bridge) does this where `ReactiveBridge` emits: +Some Reactive contracts emit callbacks targeting themselves on Reactive Network rather than an external chain. The REACT Bridge follows this pattern where `ReactiveBridge` emits: ```solidity emit Callback(reactive_chain_id, address(this), GAS_LIMIT, payload); ``` -In production, `SERVICE_ADDR` delivers these. The bridge authorizes it in its constructor: +In production, `SERVICE_ADDR` delivers these callbacks. The bridge authorizes it in its constructor: ```solidity constructor(...) AbstractCallback(address(SERVICE_ADDR)) { ... } ``` -The simulator handles this for you. When a `Callback` event's `chain_id` matches `reactiveChainId`, it delivers via `vm.prank(SERVICE_ADDR)` instead of the proxy. `authorizedSenderOnly` passes correctly. +The simulator handles this automatically. When a `Callback` event's `chain_id` matches `reactiveChainId`, the callback is delivered via `vm.prank(SERVICE_ADDR)` instead of the proxy. The `authorizedSenderOnly` modifier passes correctly. ```solidity contract SelfCallbackTest is ReactiveTest { @@ -281,7 +279,7 @@ contract SelfCallbackTest is ReactiveTest { ## Multi-Step Protocols -Complex protocols like the [REACT Bridge](https://github.com/Reactive-Network/react-bridge) chain multiple Reactive cycles off a single user action: +Complex protocols such as [REACT Bridge](https://github.com/Reactive-Network/react-bridge) chain multiple Reactive cycles from a single user action: ``` 1. ReactiveBridge.bridge() → emits SendMessage @@ -292,7 +290,7 @@ Complex protocols like the [REACT Bridge](https://github.com/Reactive-Network/re 6. Bridge emits DeliveryConfirmation → react() → ... ``` -`triggerAndReact()` only runs one cycle. For chains like this, use `triggerFullCycle()`: +`triggerAndReact()` executes only one cycle. For multi-step chains, use `triggerFullCycle()`: ```solidity CallbackResult[] memory results = triggerFullCycleWithValue( @@ -304,20 +302,20 @@ CallbackResult[] memory results = triggerFullCycleWithValue( ); ``` -Under the hood, the simulator loops: +The simulator loops through the following steps: -1. Executes the call, captures events -2. Matches events → calls `react()` → collects callback specs -3. Executes each callback, recording any new events -4. Tags new events with the callback's chain_id (events from a Sepolia callback become Sepolia events) -5. Feeds those events back into step 2 -6. Stops when there are no more callbacks or `maxIterations` is hit +1. Executes the call and captures events +2. Matches events, calls `react()`, and collects callback specs +3. Executes each callback and records any new events +4. Tags new events with the callback's `chain_id` (events from a Sepolia callback become Sepolia events) +5. Feeds new events back into step 2 +6. Stops when no more callbacks are produced or `maxIterations` is reached -Every `CallbackResult` from every iteration comes back in one array. +All `CallbackResult` entries from every iteration are returned in a single array. ## Chain Registry -When contracts span multiple logical chains, you need to pass the right `originChainId` for each trigger. The chain registry handles this — map addresses to chain IDs once, then forget about it: +When contracts span multiple logical chains, each trigger requires the correct `originChainId`. The chain registry maps addresses to chain IDs, removing the need to pass chain IDs manually: ```solidity function setUp() public override { @@ -332,7 +330,7 @@ function setUp() public override { } ``` -Now you can drop the `originChainId` argument: +With registrations in place, the `originChainId` argument can be omitted: ```solidity // These resolve the chain ID from the registry automatically @@ -348,13 +346,11 @@ CallbackResult[] memory results = triggerFullCycle( ); ``` -In full-cycle mode, events from callbacks are auto-tagged with destination chain IDs, so the registry mostly matters for the initial trigger. +In full-cycle mode, events from callbacks are automatically tagged with destination chain IDs. The registry is primarily relevant for the initial trigger. ## Cron Contracts -Reactive Contracts can subscribe to system cron events for periodic execution. The simulator provides `triggerCron()` to deliver synthetic cron events: - -Reactive Contracts can subscribe to system cron events. The simulator gives you `triggerCron()` for this: +Reactive contracts can subscribe to system cron events for periodic execution. The simulator provides `triggerCron()` to deliver synthetic cron events: ```solidity import {CronType} from "reactive-test-lib/interfaces/IReactiveInterfaces.sol"; @@ -404,7 +400,7 @@ assertCallbackSuccess(results, 0); assertCallbackFailure(results, 1); ``` -Each `CallbackResult` gives you: +Each `CallbackResult` contains the following fields: | Field | Description | |--------------|------------------------------------------------| @@ -415,11 +411,11 @@ Each `CallbackResult` gives you: | `success` | Whether the call succeeded | | `returnData` | Return or revert data | -## How the Mock Environment Works +## Mock Environment Details ### Subscription Matching -`MockSystemContract` supports the same wildcard rules as the real system contract: +`MockSystemContract` supports the same wildcard rules as the production system contract: | Field | Wildcard | Meaning | |--------------|-------------------|--------------| @@ -429,7 +425,7 @@ Each `CallbackResult` gives you: ### RVM ID Injection -The real network overwrites the first 160 bits of the first callback argument with the deployer's address. Both `MockCallbackProxy` (cross-chain) and the simulator's direct delivery (same-chain) replicate this, so `rvmIdOnly` works correctly in tests. +The production network overwrites the first 160 bits of the first callback argument with the deployer's address. Both `MockCallbackProxy` (cross-chain) and the simulator's direct delivery (same-chain) replicate this behavior. The `rvmIdOnly` modifier works correctly in tests. To simulate a different deployer: @@ -439,22 +435,22 @@ rvmId = makeAddr("customDeployer"); ### Callback Routing -Based on the `Callback` event's `chain_id`: +Routing is determined by the `Callback` event's `chain_id`: -- **Cross-chain** (`chain_id != reactiveChainId`) → goes through `MockCallbackProxy` -- **Same-chain** (`chain_id == reactiveChainId`) → delivered via `vm.prank(SERVICE_ADDR)` +- **Cross-chain** (`chain_id != reactiveChainId`) — delivered through `MockCallbackProxy` +- **Same-chain** (`chain_id == reactiveChainId`) — delivered via `vm.prank(SERVICE_ADDR)` ### vmOnly and rnOnly -Since `MockSystemContract` is deployed to `SERVICE_ADDR`, `detectVm()` sets `vm = false` (it sees code at that address). So: +`MockSystemContract` is deployed to `SERVICE_ADDR`, so `detectVm()` sets `vm = false` (it detects code at that address). As a result: -- `rnOnly` functions work in constructors — `subscribe()` calls go through +- `rnOnly` functions work in constructors, `subscribe()` calls execute normally - `vmOnly` functions need `enableVmMode(address(rc))` after deployment -## Good to Know +## Additional Notes -- **Single EVM**: Everything runs in one place. Chain IDs are just numbers. -- **No reactive-lib dependency**: The test lib reimplements ABI-compatible interfaces. Your contracts keep importing `reactive-lib` as usual. +- **Single EVM**: All execution takes place on a single EVM instance. Chain IDs are logical identifiers only. +- **No reactive-lib dependency**: The test lib reimplements ABI-compatible interfaces. Contracts continue importing `reactive-lib` as usual. - **Requirements**: Solidity ≥ 0.8.20, Foundry with `vm.recordLogs()`, `reactive-lib` v0.2.0+. [Reactive Test Lib on GitHub →](https://github.com/Reactive-Network/reactive-test-lib) diff --git a/docs/education/introduction/reactive-contracts.md b/docs/education/introduction/reactive-contracts.md index bd54249..7aef6b7 100644 --- a/docs/education/introduction/reactive-contracts.md +++ b/docs/education/introduction/reactive-contracts.md @@ -1,5 +1,5 @@ --- -title: "Reactive Contracts: What They Are and Why We Need Them" +title: "What Are Reactive Contracts and Why Do They Matter" sidebar_position: 1 description: Reactive Contracts are smart contracts that autonomously monitor and respond to events across EVM-compatible blockchains. Explains what they are, how they differ from traditional smart contracts, and why cross-chain automation matters. slug: reactive-contracts diff --git a/docs/education/module-1/react-vm.md b/docs/education/module-1/react-vm.md index 4aa2029..1fe4e70 100644 --- a/docs/education/module-1/react-vm.md +++ b/docs/education/module-1/react-vm.md @@ -1,5 +1,5 @@ --- -title: "Lesson 3: ReactVM and Reactive Network As a Dual-State Environment" +title: "Lesson 3: ReactVM and the Dual-State Environment" sidebar_position: 3 description: Learn how Reactive contracts operate across two separate states — one on Reactive Network and one in a private ReactVM instance. Covers execution context detection, managing dual variable sets, and how transactions flow in each environment. slug: react-vm diff --git a/docs/education/module-2/basic-reactive-functions.md b/docs/education/module-2/basic-reactive-functions.md index d531855..c43f159 100644 --- a/docs/education/module-2/basic-reactive-functions.md +++ b/docs/education/module-2/basic-reactive-functions.md @@ -1,29 +1,31 @@ --- -title: "Lesson 7: Implementing Basic Reactive Functions" +title: "Lesson 7: Basic Reactive Functions" sidebar_position: 2 -description: Learn to implement Reactive Contracts for Uniswap V2, automate stop orders, and understand their execution based on Sync events. +description: Walkthrough of a Reactive Contract that implements a Uniswap V2 stop order. Covers the contract structure, event subscriptions, the react() function, threshold logic, and the full execution flow from initialization to trade completion. slug: basic-reactive-functions --- -# Lesson 7: Implementing Basic Reactive Functions +# Lesson 7: Basic Reactive Functions ## Overview -In this lesson, we’ll go through the Reactive Contract (RC) specifically designed for the Uniswap V2 platform, aimed at executing stop orders based on predefined conditions. By the end of this lesson, you’ll know: +This lesson walks through a complete Reactive сontract, one that monitors a Uniswap V2 pool and executes a stop order when the price drops below a threshold. It's a practical example that ties together the concepts from the previous lessons: subscriptions, event processing, the dual-state environment, and callbacks. -* That RCs are pretty similar to Ethereum smart contracts and thus easy to understand. -* What each part of the stop-order reactive contract means. -* How this reactive contract is executed and what it does. +If you've worked with Solidity before, most of this will look familiar. Reactive contracts follow the same patterns as standard Ethereum smart contracts, with the addition of the `react()` function and the subscription/callback mechanics covered earlier in the course. -## Contract +By the end of this lesson, you'll understand: + +- How a real Reactive contract is structured from imports to execution logic +- What each part of the stop-order contract does +- How the contract moves from initialization to monitoring to trade execution -The [UniswapDemoStopOrderReactive](https://github.com/Reactive-Network/reactive-smart-contract-demos/blob/main/src/demos/uniswap-v2-stop-order/UniswapDemoStopOrderReactive.sol) contract is set up to monitor liquidity pool events on Uniswap V2, namely tracking the `Sync` events to determine when the conditions for a stop order are met. When these conditions are triggered, it executes a callback transaction on the Ethereum blockchain to perform the stop order. +## Contract -## Key Components +The [UniswapDemoStopOrderReactive](https://github.com/Reactive-Network/reactive-smart-contract-demos/blob/main/src/demos/uniswap-v2-stop-order/UniswapDemoStopOrderReactive.sol) contract monitors `Sync` events from a Uniswap V2 pair. When the reserve ratio drops below a predefined threshold, it sends a callback to execute a stop order on the destination chain. -### Event Declarations +### Imports and Events -Event Declarations: Events like `Subscribed`, `VM`, `AboveThreshold`, `CallbackSent`, and `Done` are used for logging and tracking the contract's operations on the blockchain. +The contract imports the `IReactive` interface and `AbstractReactive` base contract, then defines a `Reserves` struct to decode Uniswap's `Sync` event data. The events are used for logging at each stage of the contract's lifecycle: ```solidity // SPDX-License-Identifier: GPL-2.0-or-later @@ -57,9 +59,9 @@ contract UniswapDemoStopOrderReactive is IReactive, AbstractReactive { event Done(); ``` -### Contract Variables +`Subscribed` logs when the contract registers for events. `AboveThreshold` logs when the reserve ratio is checked but doesn't trigger. `CallbackSent` and `Done` mark the two key state transitions: when the stop order is sent and when it's confirmed. -`UNISWAP_V2_SYNC_TOPIC_0` and `STOP_ORDER_STOP_TOPIC_0` are constants representing the topics for Uniswap's `Sync` events and the contract's `Stop` events, respectively. `CALLBACK_GAS_LIMIT` is the gas limit set for the callback transaction. Variables like `triggered`, `done`, `pair`, `stop_order`, `client`, `token0`, `coefficient`, and `threshold` store the state and configuration of the stop order. +### Constants and State Variables ```solidity uint256 private constant SEPOLIA_CHAIN_ID = 11155111; @@ -68,7 +70,7 @@ contract UniswapDemoStopOrderReactive is IReactive, AbstractReactive { uint64 private constant CALLBACK_GAS_LIMIT = 1000000; // State specific to ReactVM instance of the contract. - + bool private triggered; bool private done; address private pair; @@ -79,13 +81,15 @@ contract UniswapDemoStopOrderReactive is IReactive, AbstractReactive { uint256 private threshold; ``` +The constants define the chain ID, the topic hashes for the two events the contract cares about (`Sync` from Uniswap and `Stop` from the stop-order contract), and the gas limit for callbacks. + +The state variables track the contract's progress: `triggered` prevents duplicate callbacks once the threshold is hit, `done` signals that the stop order has been confirmed. The remaining variables (`pair`, `stop_order`, `client`, `token0`, `coefficient`, and `threshold`) configure which pool to watch, which contract to call, and what price condition to act on. + ## Contract Logic ### Constructor -The constructor initializes the contract by storing references to the Uniswap V2 pair (`_pair`), the stop-order contract (`_stop_order`), and the client (`_client`). It also records a boolean flag (`_token0`), which indicates whether this contract is managing `token0` or `token1`, and sets the `coefficient` and `threshold` parameters that handle its behavior. - -After these values are stored, the contract subscribes to the Uniswap V2 pair and stop-order contract events, but only if it is not operating in a reactVM instance. Subscribing to these events ensures the contract will be notified of any relevant updates, specifically `UNISWAP_V2_SYNC_TOPIC_0` from the Uniswap pair and `STOP_ORDER_STOP_TOPIC_0` from the stop-order contract. +The constructor stores the configuration and sets up event subscriptions on Reactive Network: ```solidity constructor( @@ -126,16 +130,13 @@ After these values are stored, the contract subscribes to the Uniswap V2 pair an } ``` -### react() Function - -The `react()` function processes incoming blockchain events and determines if actions need to be triggered based on the event type: +The `if (!vm)` block only runs on Reactive Network (as covered in [Lesson 3](../module-1/react-vm.md)). It subscribes to two event types: `Sync` events from the Uniswap pair to track reserve changes, and `Stop` events from the stop-order contract to know when execution is confirmed. The ReactVM instance skips this block and uses the same variables for event processing. -**Stop-Order Events**: If the event originates from the stop-order contract, the function verifies that the event matches the expected topics and addresses (`pair` and `client`). Once confirmed and if the stop order has already been triggered (`triggered = true`), the contract marks the operation as completed (`done = true`) and emits the `Done` event. +### react() Function -**Uniswap Pair Sync Events**: For events originating from the Uniswap pair contract (specifically `Sync` events), the function decodes the reserves data to check if the conditions for triggering the stop-order are met. This check is performed using the `below_threshold` function, which calculates whether the reserve ratio falls below the defined threshold. If the condition is satisfied, the contract emits a `CallbackSent` event, prepares the callback payload, sets `triggered = true`, and emits a `Callback` event to execute the stop order. +This is where the contract's logic lives. It handles two types of incoming events: ```solidity - // Methods specific to ReactVM instance of the contract. function react(LogRecord calldata log) external vmOnly { assert(!done); @@ -169,11 +170,13 @@ The `react()` function processes incoming blockchain events and determines if ac } ``` -### below_threshold() Function +**When a `Sync` event arrives** from the Uniswap pair, the function decodes the reserve data and checks if the price has dropped below the threshold. If it has and the stop order hasn't been triggered yet, it encodes a callback payload targeting the stop-order contract and emits a `Callback` event. Reactive Network picks that up and submits the transaction on the destination chain. + +**When a `Stop` event arrives** from the stop-order contract, the function verifies that it matches the expected pair and client, then marks the operation as complete. After `done` is set to `true`, the contract won't process any more events. -The `below_threshold()` function checks whether the current reserves in the Uniswap pool satisfy the conditions for executing a stop order. It compares the reserve ratio to a predefined threshold based on the selected token (either `token0` or `token1`). +### Threshold Check -If `token0` is selected, the function checks if the ratio of `reserve1` to `reserve0`, multiplied by a coefficient, is less than or equal to the threshold. If `token0` is not selected, the function checks if the ratio of `reserve0` to `reserve1`, multiplied by the coefficient, is less than or equal to the threshold. +The `below_threshold()` function determines whether the current reserve ratio warrants triggering the stop order: ```solidity function below_threshold(Reserves memory sync) internal view returns (bool) { @@ -185,25 +188,24 @@ If `token0` is selected, the function checks if the ratio of `reserve1` to `rese } ``` -## Execution Flow - -**Initialization**: Upon deployment, the contract subscribes to the necessary events from the Uniswap V2 pair and the stop order callback contract. +The `token0` flag determines which direction to calculate the ratio. If you're watching token 0, it checks whether the price of token 0 (expressed as the ratio of `reserve1` to `reserve0`, scaled by the coefficient) has fallen to or below the threshold. The reverse applies when watching token 1. The `coefficient` is a scaling factor that lets you set precise price targets without running into integer division issues. -**Event Monitoring**: The contract listens for Sync events from the Uniswap pair to monitor the pool's reserve changes and`Stop` events from the stop-order contract to track the execution of orders. +## Execution Flow -**Stop Order Activation**: When the `Sync` event indicates that the pool's price hits the threshold, the contract initiates the stop order through the callback function, executing a trade on Uniswap V2. +The full lifecycle of this contract follows four stages: -**Completion**: After the stop order is executed, the contract captures the Stop event from the stop-order contract, marking the process as complete. +**Initialization.** On deployment, the Reactive Network instance subscribes to `Sync` events from the Uniswap pair and `Stop` events from the stop-order contract. +**Monitoring.** Every time the Uniswap pair's reserves change, a `Sync` event is emitted. The ReactVM instance receives it through `react()` and checks the reserve ratio against the threshold. -## Conclusion +**Triggering.** When the ratio drops below the threshold, the contract encodes a callback payload and emits a `Callback` event. Reactive Network submits the corresponding transaction to the stop-order contract on the destination chain, executing the trade. -In this article, we’ve examined the implementation of a Reactive Contract (RC) for managing stop orders on the Uniswap V2 platform. Key takeaways include: +**Completion.** The stop-order contract emits a `Stop` event after execution. The Reactive contract receives it, verifies the details, and marks the operation as done. No further events are processed. -- **Similarity to Ethereum Smart Contracts:** RCs are conceptually similar to Ethereum smart contracts, making them accessible for those familiar with Ethereum's architecture. +## About This Course -- **Contract Components:** We reviewed the key elements of the stop-order reactive contract, including event declarations, contract variables, and the logic behind the `react()` and `below_threshold()` functions. +This course is designed to give you both the theory and the hands-on experience to start building with Reactive contracts. It includes detailed lectures, code examples on GitHub, and video workshops covering everything from basic concepts to real-world deployments. -- **Execution Flow:** The contract’s lifecycle involves subscribing to relevant events, monitoring Uniswap V2 pool reserves, triggering stop orders when conditions are met, and capturing completion events to finalize the process. +Whether you want to understand how Reactive contracts work under the hood or jump straight into building, the course adapts to either path. Explore the [use cases](../use-cases/index.md) if you want to see what's possible, or start from Module 1 to build up from the fundamentals. -For a deeper look into practical applications, explore the [Uniswap Stop Order](../use-cases/use-case-3) use case and consider experimenting with these concepts in your own projects. Join our [Telegram](https://t.me/reactivedevs) group to engage with the community. \ No newline at end of file +Join the [Telegram](https://t.me/reactivedevs) community if you have questions or want to connect with other developers working with Reactive contracts. \ No newline at end of file diff --git a/docs/education/module-2/how-uniswap-works.md b/docs/education/module-2/how-uniswap-works.md index 61357c3..9891832 100644 --- a/docs/education/module-2/how-uniswap-works.md +++ b/docs/education/module-2/how-uniswap-works.md @@ -1,40 +1,42 @@ --- -title: "Lesson 6: How Uniswap Works / Understanding Uniswap V2 Pools and Smart Contracts" +title: "Lesson 6: How Uniswap V2 Works" sidebar_position: 1 description: Discover how Uniswap V2 pools and smart contracts work, including the constant product formula and key events like Swap and Sync. Learn about token swaps, liquidity provisioning, and see a smart contract example. slug: how-uniswap-works --- -# Lesson 6: How Uniswap Works / Understanding Uniswap V2 Pools and Smart Contracts +# Lesson 6: How Uniswap V2 Works ## Overview -Uniswap V2, a decentralized finance protocol, operates on the Ethereum blockchain, facilitating automated trading of decentralized tokens. At its core are liquidity pools and smart contracts that enable seamless token swaps. Understanding Uniswap-like DEXes is crucial for understanding DeFi, smart contract applications, and Reactive use cases. By the end of this lesson, you'll be equipped with knowledge on: +Before building a Reactive contract that interacts with a DeFi protocol, you need to understand how that protocol works. This lesson covers Uniswap V2, one of the most widely used decentralized exchanges, and the mechanics that make it relevant for Reactive contracts. -* The structure and function of Uniswap V2 pools, including how they facilitate token swaps and liquidity provisioning. -* The constant product formula (x * y = k) that governs the pricing mechanism within Uniswap V2. -* The execution and significance of Swap and Sync events in maintaining pool dynamics and providing transparency. -* A practical understanding through a code example that demonstrates the swap function within Uniswap V2's smart contracts. +By the end of this lesson, you'll understand: + +- How Uniswap V2 liquidity pools work and how they enable token swaps without traditional market makers +- The constant product formula that governs pricing +- How the `swap()` function executes trades on-chain +- What the `Swap` and `Sync` events contain and why they matter for Reactive contracts ## Uniswap V2 Pools -Liquidity pools in Uniswap V2 are essentially reserves of two tokens, forming a trading pair. These pools are the backbone of the Uniswap ecosystem, allowing users to trade tokens without the need for traditional market makers. +A Uniswap V2 liquidity pool is a pair of two tokens held in reserve. Anyone can trade one token for the other by interacting with the pool's smart contract. No order book, no counterparty, no intermediary. These pools are the foundation of the Uniswap ecosystem and a good example of how decentralized exchanges work in general. -In Uniswap V2, each trade or liquidity provision is executed through transactions on the Ethereum blockchain. These transactions are public and can be [viewed on Etherscan](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503) or similar block explorers. +Every trade and liquidity provision is an on-chain transaction, publicly visible on block explorers like [Etherscan](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503). The smart contracts managing these pools enforce the trading rules and ensure every swap follows the protocol's pricing algorithm, known as the Constant Product Market Maker model. -Smart contracts in Uniswap V2 manage the liquidity pools, dictate the rules for token swapping, and ensure that trades are executed according to the protocol's algorithm, often referred to as the Constant Product Market Maker model. +### Constant Product Formula -### The Constant Product Formula +Uniswap V2 pricing is governed by a simple formula: **x * y = k**, where `x` and `y` are the quantities of the two tokens in the pool and `k` is a constant. When someone buys token A from the pool, the amount of token A decreases and the amount of token B increases, shifting the price. The formula ensures that the pool's total liquidity is preserved regardless of how the ratio changes. -The Uniswap V2 smart contract uses this formula: x * y = k, where x and y represent the quantity of the two tokens in the liquidity pool, and k is a constant. This formula maintains the pool's total liquidity while allowing the token prices to fluctuate based on trading activity. +### Swap() Function -Code Example: Here's a simplified snippet of what a Uniswap V2 swap() function might look like (see the explanation below the code): +Here's a simplified version of Uniswap V2's `swap()` function: ```solidity function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external { -require(amount0Out > 0 || amount1Out > 0, "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT"); -(uint112 reserve0, uint112 reserve1,) = getReserves(); // fetches reserves of the pool -require(amount0Out < reserve0 && amount1Out < reserve1, "UniswapV2: INSUFFICIENT_LIQUIDITY"); + require(amount0Out > 0 || amount1Out > 0, "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT"); + (uint112 reserve0, uint112 reserve1,) = getReserves(); + require(amount0Out < reserve0 && amount1Out < reserve1, "UniswapV2: INSUFFICIENT_LIQUIDITY"); uint balance0; uint balance1; @@ -47,7 +49,6 @@ require(amount0Out < reserve0 && amount1Out < reserve1, "UniswapV2: INSUFFICIENT uint balanceAdjusted1 = balance1 * 1000 - amount1In * 3; require(balanceAdjusted0 * balanceAdjusted1 >= uint(reserve0) * uint(reserve1) * (1000**2), "UniswapV2: K"); - // Emit the Swap event emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); } @@ -62,85 +63,61 @@ require(amount0Out < reserve0 && amount1Out < reserve1, "UniswapV2: INSUFFICIENT } ``` -In this function: - -* `amount0Out` and `amount1Out` are the amounts of each token that the caller wants to receive from the pool. - -* The function first checks that the output amounts are positive and that the swap doesn't deplete the pool's reserves. +Here's what's happening step by step: -* It then calculates the input amounts (`amount0In` and `amount1In`) as the difference between the initial reserves and -the new balances after the swap. +1. The caller specifies how many of each token they want out (`amount0Out`, `amount1Out`). The function checks that at least one is positive and that the pool has enough reserves. -* The contract ensures that the trade maintains the constant product invariant (k) after accounting for a 0.3% fee -(`balanceAdjusted0` and `balanceAdjusted1` calculations). +2. It calculates how many tokens came in (`amount0In`, `amount1In`) based on the difference between the original reserves and the new balances. -* The `_update` function is called to update the pool's reserves with the new balances. +3. The constant product invariant is enforced after accounting for Uniswap's 0.3% fee (the `balanceAdjusted` calculations). If the invariant doesn't hold, the transaction reverts. -* Tokens are transferred to the recipient's address `to`. +4. The `Swap` event is emitted, logging the sender, input amounts, output amounts, and recipient. -* If there is callback data (`data`), it calls the `uniswapV2Call` function on the recipient address, which can be used -for more complex interactions like flash swaps. +5. `_update` records the new reserve balances. Tokens are transferred to the recipient address. -* The `Swap` event is emitted right after calculating the input and output amounts and before updating the reserves. The `Swap` event logs the sender, the amounts of tokens coming in and going out of the pool, and the recipient of the tokens. +6. If callback data is provided, the function calls `uniswapV2Call` on the recipient. This is how flash swaps work. -This logic encapsulates the essence of a swap transaction in Uniswap V2, balancing the pool's reserves to maintain the constant product while facilitating token exchanges. - -We will be mostly interested in `Swap` events to monitor the blockchain activity and run Reactive Contracts based on it. Since the code of the pool smart contract does not change, most of the information that is different for every transaction is being logged in the event. So let’s talk a bit more about the two types of events we’ll be most interested in: `Swap` and `Sync`. +The key takeaway for Reactive contracts: most of the transaction-specific information lives in the events, not in contract storage. Since the pool's code doesn't change between swaps, the events are where you find what actually happened in each trade. ## Events in Uniswap V2 -### Swap +Two events are particularly important for Reactive contracts: `Swap` and `Sync`. -The `Swap` event is emitted every time a trade occurs in a Uniswap V2 pool. It provides vital information about the transaction, such as the number of tokens involved in the swap and the addresses of the trader and recipient. +### Swap -Event structure example: +The `Swap` event is emitted every time a trade occurs. It logs everything you need to know about the transaction: ```solidity event Swap( -address indexed sender, -uint amount0In, -uint amount1In, -uint amount0Out, -uint amount1Out, -address indexed to + address indexed sender, + uint amount0In, + uint amount1In, + uint amount0Out, + uint amount1Out, + address indexed to ); ``` -In this event: +`sender` is the address that initiated the swap. `amount0In` and `amount1In` are the tokens sent to the pool. `amount0Out` and `amount1Out` are the tokens sent from the pool. `to` is the address receiving the output tokens. -* `sender` is the address that initiated the swap. -* `amount0In` and `amount1In` are the amounts of the respective tokens that were sent to the pool. -* `amount0Out` and `amount1Out` are the amounts of the respective tokens that were sent from the pool. -* `to` is the address that receives the output tokens. - -You can see this event in [the list of the events](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503#eventlog) in this transaction on Etherscan. +You can see a real example of this event in [the event logs of this transaction](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503#eventlog) on Etherscan. ### Sync -The `Sync` event is emitted whenever the reserves of a Uniswap V2 pool are updated. This event occurs after a swap when liquidity is added or removed, or when there's a direct token transfer into or out of the pool. The `Sync` event helps keep track of the pool's reserves current state. - -Event Structure Example: +The `Sync` event is emitted whenever the pool's reserves are updated: after a swap, when liquidity is added or removed, or when tokens are transferred directly into or out of the pool: ```solidity event Sync(uint112 reserve0, uint112 reserve1); ``` -In this event: - -* `reserve0` and `reserve1` represent the updated reserves of the pool's two tokens. - -The `Sync` event is critical for maintaining up-to-date information on the pool's liquidity, which in turn affects trading price and slippage. You can see this event in [the list of the events](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503#eventlog) in this transaction on Etherscan. - -## Conclusion - -In this article, we’ve explored the fundamentals of Uniswap V2, a cornerstone of DeFi that facilitates automated trading through liquidity pools and smart contracts. Key takeaways include: +`reserve0` and `reserve1` are the updated token balances in the pool. This event is what keeps external observers (including Reactive contracts) informed about the pool's current state, which directly affects pricing and slippage. -- **Uniswap V2 Pools:** These pools, consisting of two tokens, enable seamless trading and liquidity provisioning without traditional market makers. Each transaction is governed by the Constant Product Market Maker model, which maintains the balance of liquidity in the pool. +Both events are visible in [the same Etherscan transaction logs](https://etherscan.io/tx/0x7b969e8a74ae9891e322311ca5fe6e5d7bcb53ac3412b4189d84683961043503#eventlog). In the next lesson, you'll see how a Reactive contract subscribes to these events and acts on them. -- **Constant Product Formula:** The formula (x * y = k) ensures that the product of the quantities of the two tokens remains constant, allowing for dynamic pricing based on trading activity. +## About This Course -- **Swap and Sync Events:** The `Swap` event provides detailed information about trades, including token amounts and addresses, while the `Sync` event keeps track of reserve updates. These events are crucial for monitoring and integrating Uniswap activity with Reactive Contracts. +This course is designed to give you both the theory and the hands-on experience to start building with Reactive contracts. It includes detailed lectures, code examples on GitHub, and video workshops covering everything from basic concepts to real-world deployments. -- **Code Mechanics:** The provided code example illustrates the core functionality of the `swap` function in Uniswap V2, demonstrating how the contract maintains liquidity and ensures accurate token swaps. +Whether you want to understand how Reactive contracts work under the hood or jump straight into building, the course adapts to either path. Explore the [use cases](../use-cases/index.md) if you want to see what's possible, or start from Module 1 to build up from the fundamentals. -For practical applications and further insights into integrating Uniswap V2 with your projects, explore our [use cases](../use-cases/index.md) and join our [Telegram](https://t.me/reactivedevs) group to engage with the community. \ No newline at end of file +Join the [Telegram](https://t.me/reactivedevs) community if you have questions or want to connect with other developers working with Reactive contracts. \ No newline at end of file diff --git a/docs/education/module-2/index.md b/docs/education/module-2/index.md index 48c3640..a78f73b 100644 --- a/docs/education/module-2/index.md +++ b/docs/education/module-2/index.md @@ -1,7 +1,7 @@ --- title: "Module 2: Intermediate - Building Blocks for Reactivity" sidebar_position: 1 -description: Learn the basics of DeFi with Uniswap V2 and Reactive Contracts. Discover how liquidity pools work and see RCs in action as they autonomously execute trades. +description: Practical introduction to DeFi and Reactive Contracts. Covers how Uniswap V2 liquidity pools and smart contracts work, then shows how Reactive Contracts can autonomously execute trades based on real-time on-chain conditions. slug: /education/module-2 --- @@ -9,12 +9,14 @@ slug: /education/module-2 # Overview -Welcome to Module 2: Intermediate - Building Blocks for Reactivity! In this module, we're diving into decentralized finance (DeFi), with a focus on understanding and applying Reactive Contracts (RCs). +Module 1 covered the core concepts: how Reactive contracts work, how they process events, and how subscriptions and callbacks fit together. This module puts that foundation to use with a real DeFi protocol. -[Lesson 6: Understanding Uniswap V2 Pools and Smart Contracts](./how-uniswap-works.md) +You'll start by learning how Uniswap V2 works under the hood: its liquidity pools, smart contracts, and trading mechanics. Then you'll see how a Reactive contract can plug into that system and autonomously execute trades when specific conditions are met. This is where the theory from Module 1 starts turning into practical applications. -Gain an understanding of Uniswap V2, a key decentralized finance protocol. Learn how liquidity pools function and explore the smart contracts that drive Uniswap V2, enabling efficient and decentralized trading. +[Lesson 6: How Uniswap V2 Works](./how-uniswap-works.md) -[Lesson 7: Implementing Basic Reactive Functions](./basic-reactive-functions.md) +How Uniswap V2's liquidity pools and smart contracts enable decentralized trading. Covers the constant product formula, how swaps work, and what events Uniswap emits, which is what a Reactive contract will subscribe to in the next lesson. -Explore how Reactive Contracts (RCs) operate within the DeFi space. Understand how RCs can autonomously execute trades and respond to specific conditions, improving the capabilities of decentralized applications. \ No newline at end of file +[Lesson 7: Basic Reactive Functions](./basic-reactive-functions.md) + +How Reactive contracts operate in a DeFi context. Walks through how a Reactive contract can monitor Uniswap events and autonomously execute trades when on-chain conditions match predefined criteria. \ No newline at end of file