Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Salmandabbakuti authored Oct 1, 2024
0 parents commit c4ccec5
Show file tree
Hide file tree
Showing 695 changed files with 136,276 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
12 changes: 12 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/v2-core"]
path = lib/v2-core
url = https://github.com/Uniswap/v2-core
[submodule "lib/v2-periphery"]
path = lib/v2-periphery
url = https://github.com/Uniswap/v2-periphery
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# System Smart Contracts for Reactive Network

## Development & Deployment Instructions

### Environment Setup

To set up `foundry` environment, run:

```
curl -L https://foundry.paradigm.xyz | bash
source ~/.bashrc
foundryup
```

Install dependencies:

```
forge install
```

### Development & Testing

To compile artifacts:

```
forge compile
```

### Additional Documentation & Demos

Refer to `TECH.md` for additional information on implementing reactive contracts and callbacks.

The `src/demos` directory contains several elaborate demos, accompanied by `README.md` files for each one.

### Environment variable configuration for running demos

The following environment variables are used in the instructions for running the demos, and should be configured beforehand.

#### `SEPOLIA_RPC`

The Sepolia Testnet RPC address; `https://rpc2.sepolia.org` unless you want to use your own.

#### `SEPOLIA_PRIVATE_KEY`

The private key to your Sepolia wallet.

#### `REACTIVE_RPC`

For the Reactive Testnet RPC address, refer to the [docs](https://dev.reactive.network/kopli-testnet#kopli-testnet-information).

#### `REACTIVE_PRIVATE_KEY`

The private key to your Reactive wallet.

#### `DEPLOYER_ADDR`

The address of your Reactive wallet.

#### `SYSTEM_CONTRACT_ADDR`

For the system contract address on the Reactive testnet, refer to the [docs](https://dev.reactive.network/kopli-testnet#kopli-testnet-information).

#### `CALLBACK_PROXY_ADDR`

For the callback sender address, refer to the [docs](https://dev.reactive.network/kopli-testnet#kopli-testnet-information).
113 changes: 113 additions & 0 deletions TECH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Basics Of Reactive Smart Contracts

Reactive smart contracts run on a standard EVM and can be written in any EVM-compatible language, although the Application Binary Interfaces (ABIs) are particularly customized for Solidity. Their unique capabilities stem from reactive nodes and a specialized pre-deployed system contract.

## Special Considerations

Reactive contracts are deployed simultaneously to the main reactive network and the private ReactVM subnet. The copy deployed to the main network is accessible by Externally Owned Accounts (EOAs) and can interact with the system contract to manage subscriptions. The copy deployed to ReactVM processes incoming events from origin chain contracts but can't be interacted with by the EOA's copy.

The two contract copies of the contract **DO NOT** share state and can't interact directly. Since both copies use the same bytecode, it's recommended to identify the deployment target in the constructor and guard your methods accordingly. You can determine whether the contract is being deployed to ReactVM by interacting with the system contract. Since it is not present in ReactVMs, your calls will revert. Refer to the reactive demos for examples.

Reactive contracts running in the ReactVM subnet have limited capabilities for interaction with anything outside their VM. They can only:

* Passively receive log records passed to them by the reactive network.
* Initiate calls to destination chain contracts.

## Subscription Basics

Reactive contract's static subscriptions are configured by calling the `subscribe()` method of the Reactive Network's system contract upon deployment. This must happen in the `constructor()`, and the reactive contract must adeptly handle reverts. The latter requirement arises because reactive contracts are deployed both to the Reactive Network and to their deployer's private ReactVM, where the system contract is not present. The following code will accomplish this:

```solidity
bool private vm;
constructor() {
SubscriptionService service = SubscriptionService(service_address);
bytes memory payload = abi.encodeWithSignature(
"subscribe(uint256,address,uint256,uint256,uint256,uint256)",
CHAIN_ID,
CONTRACT_ADDRESS,
TOPIC_0,
REACTIVE_IGNORE,
REACTIVE_IGNORE,
REACTIVE_IGNORE
);
(bool subscription_result,) = address(service).call(payload);
if (!subscription_result) {
vm = true;
}
}
```

Reactive contracts can change their subscriptions dynamically by using callbacks to Reactive Network instances (as opposed to ReactVM) of themselves, which can, in turn, call the system contract to effect the appropriate changes to subscriptions.

The subscription system allows the Reactive Network (the event provider) to associate any number of `uint256` fields with a given event. Subscribers can then request events that match any subset of these fields exactly. During the testnet stage, the Reactive Network provides the originating contract's chain ID, address, and all four topics as filtering criteria. These criteria may be expanded or changed in the future.

`REACTIVE_IGNORE` is a random value (`0xa65f96fc951c35ead38878e0f0b7a3c744a6f5ccc1476b313353ce31712313ad`) set aside to indicate that you're not interested in the given topic. `0` is used for the same purpose where chain ID and contract address are concerned.

To explain the capabilities by example, **YOU CAN**:

* Subscribe to all log records emitted by a specific contract, e.g., to subscribe to all events from `0x7E0987E5b3a30e3f2828572Bb659A548460a3003`, call `subscribe(CHAIN_ID, 0x7E0987E5b3a30e3f2828572Bb659A548460a3003, REACTIVE_IGNORE, REACTIVE_IGNORE, REACTIVE_IGNORE, REACTIVE_IGNORE)` in the constructor.

* Subscribe to all log records with a specific topic 0, e.g., to subscribe to all Uniswap V2 `Sync` events, call `subscribe(CHAIN_ID, 0, 0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1, REACTIVE_INGORE, REACTIVE_INGORE, REACTIVE_INGORE)` in the constructor.

* Subscribe to log records emitted by a specific contract with a specific topic 0.

* Specify multiple independent subscriptions by calling the `subscribe()` method multiple times in the constructor. Your reactive contract will receive events matching any of its subscriptions.

On the other hand, **YOU CAN'T**:

* Match event parameters using less than, greater than, range, or bitwise operations. Only strict equality is supported.

* Use disjunction or sets of criteria in a single subscription. You can, however, call the `subscribe()` method multiple times to achieve similar results, but this approach is somewhat vulnerable to combinatorial explosion.

## Processing Events

To process incoming events, a reactive smart contract must implement the `IReactive` interface. This involves implementing a single method with the following signature:

```solidity
function react(
uint256 chain_id,
address _contract,
uint256 topic_0,
uint256 topic_1,
uint256 topic_2,
uint256 topic_3,
bytes calldata data,
uint256 block_number,
uint256 op_code
) external;
```

The Reactive Network will feed events matching the reactive contract's subscriptions by initiating calls to this method.

Reactive smart contracts can use all the EVM capabilities normally. The only limitation is that reactive contracts are executed in the context of a private ReactVM associated with a specific deployer address, so they can't interact with contracts deployed by anyone else.

## Calls to Destination Chain Contracts

The key capability of reactive smart contracts is the ability to create new transactions in L1 networks. This is achieved by emitting log records of a predetermined format:

```solidity
event Callback(
uint256 indexed chain_id,
address indexed _contract,
uint64 indexed gas_limit,
bytes payload
);
```

Upon observing such a record in the traces, the Reactive Network will submit a new transaction with the desired payload to the L1 network indicated by the chain ID (as long as it's on the supported list). Note that for authorization purposes, the first 160 bits of the call arguments will be replaced with the calling reactive contract's RVM ID, which is equal to the reactive contract's deployer address.

For example, the Uniswap Stop Order Demo uses this capability to initiate token sales through its destination chain contract:

```solidity
bytes memory payload = abi.encodeWithSignature(
"stop(address,address,address,bool,uint256,uint256)",
0,
pair,
client,
token0,
coefficient,
threshold
);
emit Callback(chain_id, stop_order, CALLBACK_GAS_LIMIT, payload);
```
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/forge-std/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/Vm.sol linguist-generated
134 changes: 134 additions & 0 deletions lib/forge-std/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: CI

on:
workflow_dispatch:
pull_request:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly

- name: Print forge version
run: forge --version

# Backwards compatibility checks:
# - the oldest and newest version of each supported minor version
# - versions with specific issues
- name: Check compatibility with latest
if: always()
run: |
output=$(forge build --skip test)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
- name: Check compatibility with 0.8.0
if: always()
run: |
output=$(forge build --skip test --use solc:0.8.0)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
- name: Check compatibility with 0.7.6
if: always()
run: |
output=$(forge build --skip test --use solc:0.7.6)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
- name: Check compatibility with 0.7.0
if: always()
run: |
output=$(forge build --skip test --use solc:0.7.0)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
- name: Check compatibility with 0.6.12
if: always()
run: |
output=$(forge build --skip test --use solc:0.6.12)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
- name: Check compatibility with 0.6.2
if: always()
run: |
output=$(forge build --skip test --use solc:0.6.2)
if echo "$output" | grep -q "Warning"; then
echo "$output"
exit 1
fi
# via-ir compilation time checks.
- name: Measure compilation time of Test with 0.8.17 --via-ir
if: always()
run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir

- name: Measure compilation time of TestBase with 0.8.17 --via-ir
if: always()
run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir

- name: Measure compilation time of Script with 0.8.17 --via-ir
if: always()
run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir

- name: Measure compilation time of ScriptBase with 0.8.17 --via-ir
if: always()
run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly

- name: Print forge version
run: forge --version

- name: Run tests
run: forge test -vvv

fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly

- name: Print forge version
run: forge --version

- name: Check formatting
run: forge fmt --check
29 changes: 29 additions & 0 deletions lib/forge-std/.github/workflows/sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Sync Release Branch

on:
release:
types:
- created

jobs:
sync-release-branch:
runs-on: ubuntu-latest
if: startsWith(github.event.release.tag_name, 'v1')
steps:
- name: Check out the repo
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: v1

- name: Configure Git
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- name: Sync Release Branch
run: |
git fetch --tags
git checkout v1
git reset --hard ${GITHUB_REF}
git push --force
4 changes: 4 additions & 0 deletions lib/forge-std/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cache/
out/
.vscode
.idea
Loading

0 comments on commit c4ccec5

Please sign in to comment.