Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Salmandabbakuti authored Oct 10, 2024
0 parents commit 3c00c00
Show file tree
Hide file tree
Showing 644 changed files with 124,460 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ORIGIN1_RPC=https://polygon.drpc.org
ORIGIN2_RPC=https://rpc2.sepolia.org
REACTIVE_RPC=
PRIVATE_KEY=
ORIGIN1_ADDR=
ORIGIN2_ADDR=
ORIGIN1_CHAINID=
ORIGIN2_CHAINID=
SYSTEM_CONTRACT_ADDR=
CALLBACK_SENDER_ADDR=
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Compiler files
cache/
out/
artifacts/
.deps/

# 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
221 changes: 221 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# Reactive Bridge

## Overview

This project illustrates a basic use case of the Reactive Network, showcasing a token bridge between two chains. The setup consists of a ERC20 token contract deployed on origin & destination chains, with a Reactive Contract managing the bridge between them. The Reactive Contract listens for events on both chains, emits logs, and triggers callbacks based on predefined conditions. For now, the demo only showcase A->B(Polygon to Sepolia) because, Sepolia is only destination chain supported by Reactive Network as of now. But, the Reactive contract is designed to support two-way token bridging(A->B and B->A).

![Screen1](https://github.com/user-attachments/assets/cc6c018e-d1c1-44a8-8ce6-628b4a0cdaaf)

### Features

- **Two-way Token Bridge:** This project supports two-way(A->B, B->A) token bridging between two chains. The Reactive Contract designed to listen for events on both chains and trigger callbacks to mint tokens on the destination chain. For now, the demo only showcase A->B(Polygon to Sepolia) because, Sepolia is only destination chain supported by Reactive Network as of now.

- **Intuitive UI:** The demo includes a simple UI(Uniswap's Swap like UI) to interact with the contracts. Users can request a bridge transfer from one chain to another by entering the amount of tokens to be bridged.

- **Realtime Updates:** The UI updates token balances in real-time to reflect the status of the bridge transfer. Users can track the progress of the bridge transfer, from the request to the bridging of tokens on the destination chain.

```mermaid
%%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
flowchart LR
subgraph Reactive Network
subgraph ReactVM
RC(ReactiveBridge Contract)
end
end
subgraph Origin1
OCC(CrossToken Contract)
end
subgraph Origin2
DCC(CrossToken Contrat)
end
OCC -.->|BridgeRequest Event| RC
RC -.->|Callback| DCC
```

## Contracts

The demo involves two contracts. Can be found in `src/demos/token-bridge` directory:

1. **Origin & Destination Chain Contract:** `CrossToken.sol` is a Simple ERC20 token contract that can be deployed on both the origin and destination chains(called as origin1, origin2). It emits an event `BridgeRequest` when a user requests a bridge transfer from one chain to another. The event contains the user's address and the amount to be bridged.

2. **Reactive Contract:** `ReactiveBridge.sol` subscribes to events on both origin & destination, emits logs, and triggers callbacks when conditions are met, such as when `BridgeRequest` is emitted on the either of the chains, The callback is sent to the other chain with payload data to mint tokens on the destination chain. This contract is designed to support two-way token bridging(A->B and B->A). Right now, the demo only supports A->B because, Sepolia is only destination chain supported by Reactive Network.

## Getting Started

### Prerequisites

- [Node.js](https://nodejs.org/en/download/)

- [Foundry](https://book.getfoundry.sh/)

To set up foundry environment, run:

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

- Etherem wallet with Some funds on origin and destination chains

To deploy contracts, follow these steps, making sure you substitute the appropriate keys, addresses, and endpoints where necessary. You will need the following environment variables configured appropriately to follow this script:

```.env
ORIGIN1_RPC=
ORIGIN2_RPC=
REACTIVE_RPC=
PRIVATE_KEY=
ORIGIN1_ADDR=
ORIGIN2_ADDR=
ORIGIN1_CHAINID=
ORIGIN2_CHAINID=
SYSTEM_CONTRACT_ADDR=
CALLBACK_SENDER_ADDR=
```

You can use the recommended Sepolia RPC URL: `https://rpc2.sepolia.org`.

Load the environment variables:

```bash
source .env
```

### Compile & Deploy Contracts

This project is scaffolded using Foundry, a smart contract development toolchain. Please refer to the [Foundry documentation](https://book.getfoundry.sh/) for more details.

Directory structure:

```bash
├── README.md
├── .env
├── lib # Foundry, OpenZeppelin, and other dependencies
├── src
│ ├── demos
│ │ └── token-bridge # Token Bridge Demo Contracts
│ │ ├── CrossToken.sol
│ │ └── ReactiveBridge.sol
├── frontend
```

Install dependencies and compile the contracts:

```bash
forge install

forge compile
```

Deploy the `CrossToken` contract with authorized callback sender on both chains(eg. Polygon & Sepolia) and assign the `Deployed to` address from the response to `ORIGIN1_ADDR` and `ORIGIN2_ADDR` respectively.

```bash
forge create --rpc-url $ORIGIN1_RPC --private-key $PRIVATE_KEY src/demos/token-bridge/CrossToken.sol:CrossToken --constructor-args 1000000000000000000000 $CALLBACK_SENDER_ADDR

forge create --rpc-url $ORIGIN2_RPC --private-key $PRIVATE_KEY src/demos/token-bridge/CrossToken.sol:CrossToken --constructor-args 1000000000000000000000 $CALLBACK_SENDER_ADDR
```

### Callback Payment

To ensure a successful callback, the callback contract(both origin1, origin2 contracts) must have an ETH balance. You can find more details [here](https://dev.reactive.network/system-contract#callback-payments). To fund the callback contracts, run the following command:

```bash
cast send $ORIGIN1_ADDR --rpc-url $ORIGIN1_RPC --private-key $PRIVATE_KEY --value 0.1ether

cast send $ORIGIN2_ADDR --rpc-url $ORIGIN2_RPC --private-key $PRIVATE_KEY --value 0.1ether
```

### Deploy Reactive Contract

Deploy the `ReactiveBridge.sol` (reactive contract), configuring it to listen to `BridgeRequest` event on both chains(origin1, origin2) and trigger a callback to the other chain with the payload data.

```bash
forge create --rpc-url $REACTIVE_RPC --private-key $PRIVATE_KEY src/demos/token-bridge/ReactiveBridge.sol:ReactiveBridge --constructor-args $SYSTEM_CONTRACT_ADDR $ORIGIN1_ADDR $ORIGIN2_ADDR $ORIGIN1_CHAINID $ORIGIN2_CHAINID
```

### Test the Bridge

Test the whole setup by sending a bridge request from origin1 to origin2(Should be Sepolia).

```bash
cast send --rpc-url $ORIGIN1_RPC --private-key $PRIVATE_KEY $ORIGIN1_ADDR "bridgeRequest(uint256)" 5000000000000000000
```

> **Note:** The bridge request callback should be on the destination chain(Sepolia) because the Reactive Network only supports Sepolia as the destination chain for now. So, Make sure you are calling `birdgeRequest` function from the other chain. (Not Sepolia). Adjust `ORIGIN1_ADDR` and `ORIGIN2_ADDR` accordingly in the above command.
This should trigger the callback on the destination chain(Sepolia) and mint the `amount` of tokens to the `caller` address on the destination chain.

### Deployed Contracts

- **CrossToken(XT) Contract(Polygon):** [0xd231fe46b4a8500d4add5ad98ec3c4ca56e7dee4](https://polygonscan.com/token/0xd231fe46b4a8500d4add5ad98ec3c4ca56e7dee4)
- **CrossToken(XT) Contract(Sepolia):** [0x3eed33dcf10ea9543380e71b9e245dca16c30605](https://sepolia.etherscan.io/token/0x3eed33dcf10ea9543380e71b9e245dca16c30605)
- **ReactiveBridge Contract:** [0x6d21161d1D17cDCA58707829E4d57D5a4EfE5489](https://kopli.reactscan.net/rvms/0xc7203561EF179333005a9b81215092413aB86aE9?screen=info)
- **RVM:** [0xc7203561EF179333005a9b81215092413aB86aE9](https://kopli.reactscan.net/rvms/0xc7203561EF179333005a9b81215092413aB86aE9)

### Running the Demo

> Copy the `frontend/.env.example` file to `frontend/.env` and update the environment variables accordingly. Update contract addresses in `frontend/src/utils/constants.js` file.
Install dependencies and start the frontend:

```bash
cd frontend

npm install

npm run dev
```

Open your browser and navigate to `http://localhost:3000` to view the demo.

### Demo

https://github.com/user-attachments/assets/803c6520-aedd-4be1-9d18-4057da0a2e1f


### Bridging Workflow

1. **Connect Wallet:** Connect your wallet to the frontend. Make sure you have some funds on Polygon.

2. **Mint Tokens:** Mint CrossToken(XT) on the origin chain(Polygon) by clicking the `Mint` button. This will mint 50 tokens to your address on the origin chain(Polygon) for demonstration purposes.

3. **Bridge Tokens:** Enter the amount of tokens to be bridged and click the `Bridge` button. This will trigger a bridge request from the origin chain(Polygon) burning them mount of tokens on origin(Polygon) and emitting a `BridgeRequest` event with the user's address and the amount to be bridged.

4. Reactive Contract listens for the `BridgeRequest` event on the origin chain(Polygon) and triggers a callback to the destination chain(Sepolia) with the event payload data to mint the amount of tokens to the user's address on the destination chain(Sepolia). The UI shows source and destination transaction links for tracking the progress of the bridge transfer.

5. The frontend updates the token balances in real-time to reflect the status of the bridge transfer.

#### Workflow Example

1. User mints 50 XT(CrossToken) on the origin chain(Polygon).
https://polygonscan.com/tx/0xb34029deb89e48f2447a95a2240434f7055bac38e34a2ddaeafbd51fcca861f4

2. User requests to bridge 10 XT from the origin chain(Polygon) to the destination chain(Sepolia).
https://polygonscan.com/tx/0x23552c280b0b40a5d426d4ca23858faa8d6ec26cb696d4c154e5c0e834ad8b02

3. Reactive Contract listens for the `BridgeRequest` event on the origin chain(Polygon) and triggers a callback to the destination chain(Sepolia) with the event payload data to mint 10 XT to the user's address on the destination chain(Sepolia).

https://kopli.reactscan.net/rvms/0xc7203561ef179333005a9b81215092413ab86ae9

4. Destination contract receives the callback and mints 10 XT to the user's address on the destination chain(Sepolia).
https://sepolia.etherscan.io/tx/0xf767aa66dffbbc6330f640f31d1aa541cecce2c96b0bc8cdcd763469d00796eb

## Built With

- [Reactive Network](https://reactive.network) - Facilitates seamless cross-chain and multi-chain interactions, breaking down barriers for true blockchain interoperability.
- [Foundry](https://book.getfoundry.sh/) - A smart contract development toolchain. It provides a set of tools to help you build, test, and deploy smart contracts.
- [Solidity](https://docs.soliditylang.org/en/v0.8.24/) - Ethereum's smart contract programming language
- [Thirdweb](https://thirdweb.com) - Full-stack, open-source web3 development platform. Frontend, backend, and onchain tools to build complete web3 apps — on every EVM chain.
- [Antd](https://ant.design/) - A design system for enterprise-level products. Create an efficient and enjoyable work experience.
- [React + Vite](https://vitejs.dev/) - Frontend development environment for building fast and modern web apps

## Safety & Security

This is experimental software and subject to change over time.

This is a proof of concept and is not ready for production use. It is not audited and has not been tested for security. Use at your own risk. I do not give any warranties and will not be liable for any loss incurred through any use of this codebase.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
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 frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_THIRDWEB_CLIENT_ID=
Loading

0 comments on commit 3c00c00

Please sign in to comment.