Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b8df141
fix: Update comments for clarity and remove unnecessary setup functio…
Le-Caignec May 19, 2025
ef3951d
format readme
Le-Caignec May 19, 2025
a5361c3
feat: Upgrade contracts to use OpenZeppelin's upgradeable libraries a…
Le-Caignec May 19, 2025
5bc265b
feat: Add OpenZeppelin upgradeable libraries and update deployment sc…
Le-Caignec May 19, 2025
7f46039
fix: Remove outdated OpenZeppelin contracts remapping and delete unus…
Le-Caignec May 19, 2025
fed0076
refactor: Simplify UUPS proxy deployment calls and improve code forma…
Le-Caignec May 19, 2025
29cc9f1
fix: Ensure optimizer settings are correctly defined in foundry.toml
Le-Caignec May 19, 2025
69215f9
refactor: Update configuration scripts for RLCAdapter and RLCOFT, rem…
Le-Caignec May 19, 2025
ea1d723
refactor: Rename SENDER_ADDRESS to OWNER_ADDRESS for clarity in envir…
Le-Caignec May 19, 2025
373c7fa
refactor: Update initialize function parameters to use _owner for cla…
Le-Caignec May 19, 2025
133e830
docs: Add instructions for importing a wallet in the Foundry keystore…
Le-Caignec May 20, 2025
c025eac
fix: Add missing initialization calls for OFTAdapter in RLCAdapter an…
Le-Caignec May 20, 2025
07fbd3d
fix: Remove unnecessary --ffi flag from deploy scripts and ensure ffi…
Le-Caignec May 20, 2025
2f99a4f
fix: Correct contract names in UUPS proxy deployment for RLCAdapter a…
Le-Caignec May 20, 2025
57ee01f
fix: Add missing initializer call to unsafeAllow options in RLCAdapte…
Le-Caignec May 20, 2025
454cb54
fix: Update import paths for OpenZeppelin contracts and adjust proxy …
Le-Caignec May 20, 2025
bbcea83
fix: Update OWNER_ADDRESS variable in environment template and deploy…
Le-Caignec May 21, 2025
0f7cbc6
fix: Update owner retrieval method in RLCAdapter and RLCOFT contracts…
Le-Caignec May 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RLC_OFT_TOKEN_NAME="RLC OFT"
RLC_TOKEN_SYMBOL="RLC"

# Transaction Settings
SENDER_ADDRESS=0x<delegate-wallet-address>
OWNER_ADDRESS=0x<owner-wallet-address>

# API Keys
ETHERSCAN_API_KEY=<your-etherscan-api-key>
Expand Down
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
[submodule "lib/rlc-faucet-contract"]
path = lib/rlc-faucet-contract
url = https://github.com/iExecBlockchainComputing/rlc-faucet-contract
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "lib/openzeppelin-foundry-upgrades"]
path = lib/openzeppelin-foundry-upgrades
url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ deploy-oft:

configure-adapter:
@echo "Configuring RLCAdapter on SEPOLIA..."
forge script script/ConfigureRLCAdapter.s.sol:Configure \
forge script script/RLCAdapter.s.sol:Configure \
--rpc-url $(SEPOLIA_RPC_URL) \
--account $(ACCOUNT) \
--broadcast \
-vvv
configure-oft:
@echo "Configuring RLCOFT on Arbitrum SEPOLIA..."
forge script script/ConfigureRLCOFT.s.sol:Configure \
forge script script/RLCOFT.s.sol:Configure \
--rpc-url $(ARBITRUM_SEPOLIA_RPC_URL) \
--account $(ACCOUNT) \
--broadcast \
Expand Down Expand Up @@ -64,7 +64,7 @@ verify-adapter:
forge verify-contract \
--chain-id 11155111 \
--watch \
--constructor-args $(shell cast abi-encode "constructor(address,address,address)" $(RLC_SEPOLIA_ADDRESS) $(LAYER_ZERO_SEPOLIA_ENDPOINT_ADDRESS) $(SENDER_ADDRESS)) \
--constructor-args $(shell cast abi-encode "constructor(address,address,address)" $(RLC_SEPOLIA_ADDRESS) $(LAYER_ZERO_SEPOLIA_ENDPOINT_ADDRESS) $(OWNER_ADDRESS)) \
--etherscan-api-key $(ETHERSCAN_API_KEY) \
$(RLC_SEPOLIA_ADAPTER_ADDRESS) \
src/RLCAdapter.sol:RLCAdapter
Expand All @@ -74,7 +74,7 @@ verify-oft:
forge verify-contract \
--chain-id 421614 \
--watch \
--constructor-args $(shell cast abi-encode "constructor(string,string,address,address)" $(RLC_OFT_TOKEN_NAME) $(RLC_TOKEN_SYMBOL) $(LAYER_ZERO_ARBITRUM_SEPOLIA_ENDPOINT_ADDRESS) $(SENDER_ADDRESS)) \
--constructor-args $(shell cast abi-encode "constructor(string,string,address,address)" $(RLC_OFT_TOKEN_NAME) $(RLC_TOKEN_SYMBOL) $(LAYER_ZERO_ARBITRUM_SEPOLIA_ENDPOINT_ADDRESS) $(OWNER_ADDRESS)) \
--etherscan-api-key $(ARBISCAN_API_KEY) \
$(RLC_ARBITRUM_SEPOLIA_OFT_ADDRESS) \
src/RLCOFT.sol:RLCOFT
Expand Down
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,32 @@ The system consists of two main components:
## Installation

1. Clone the repository

```bash
git clone https://github.com/iExecBlockchainComputing/rlc-multichain.git
cd rlc-multichain
```

2. Install dependencies

```bash
forge install
```

3. Create a `.env` file

```sh
cp .env.template .env # and edit .env content
```

**Note:** To run scripts, you must save a wallet in the Foundry keystore. Use the following command to import a wallet with a raw private key:

```bash
cast wallet import --private-key <RAW_PRIVATE_KEY> <ACCOUNT_NAME>
```

Alternatively, you can use a mnemonic by specifying the `--mnemonic-path` option. Remember the `<ACCOUNT_NAME>` you choose, and set it in your `.env` file under the `ACCOUNT` field.

## Contract Overview

Instead of duplicating code that may become outdated, here are links to the key contracts in the repository:
Expand All @@ -47,21 +58,25 @@ Instead of duplicating code that may become outdated, here are links to the key
The deployment process involves four steps:

1. Deploy the RLCAdapter on Ethereum Sepolia:

```bash
make deploy-adapter
```

2. Deploy the RLCOFT on Arbitrum Sepolia:

```bash
make deploy-oft
```

3. Configure the RLCAdapter to trust the RLCOFT contract:

```bash
make configure-adapter
```

4. Configure the RLCOFT to trust the RLCAdapter contract:

```bash
make configure-oft
```
Expand All @@ -79,6 +94,7 @@ make send-tokens-to-arbitrum-sepolia
```

This will:

1. Approve the RLCAdapter to spend your RLC tokens
2. Initiate the cross-chain transfer through LayerZero
3. Lock tokens in the adapter and mint equivalent tokens on Arbitrum
Expand All @@ -90,6 +106,7 @@ make send-tokens-to-sepolia
```

This will:

1. Burn RLCOFT tokens on Arbitrum
2. Send a cross-chain message to the adapter
3. Release the original RLC tokens on Ethereum
Expand Down Expand Up @@ -118,6 +135,7 @@ This will:
## Gas Costs and Fees

LayerZero transactions require fees to cover:

1. Gas on the source chain
2. Gas on the destination chain (prepaid)
3. LayerZero relayer fees
Expand All @@ -126,9 +144,8 @@ The scripts automatically calculate these fees and include them in the transacti

## Troubleshooting


## References

- [LayerZero Documentation](https://layerzero.gitbook.io/docs/)
- [OFT Contracts](https://github.com/LayerZero-Labs/solidity-examples/tree/main/contracts/token/oft)
- [iExec Platform Documentation](https://docs.iex.ec/)
- [iExec Platform Documentation](https://docs.iex.ec/)
15 changes: 14 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./" }]
optimizer = true
optimizer_runs = 200
via_ir = true

## Needed by openzepplin upgrade plugin
ffi = true
ast = true
build_info = true
extra_output = ["storageLayout"]

remappings = [
'@layerzerolabs/oft-evm-upgradeable/=lib/devtools/packages/oft-evm-upgradeable/',
'@layerzerolabs/oft-evm/=lib/devtools/packages/oft-evm/',
'@layerzerolabs/oapp-evm-upgradeable/=lib/devtools/packages/oapp-evm-upgradeable/',
'@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/',
'@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/protocol',
'@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/',
'@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
'@openzeppelin-foundry/contracts/=lib/openzeppelin-foundry-upgrades/src/',
]


# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
1 change: 1 addition & 0 deletions lib/openzeppelin-foundry-upgrades
25 changes: 0 additions & 25 deletions script/ConfigureRLCAdapter.s.sol

This file was deleted.

25 changes: 0 additions & 25 deletions script/ConfigureRLCOFT.s.sol

This file was deleted.

38 changes: 32 additions & 6 deletions script/RLCAdapter.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,50 @@
pragma solidity ^0.8.22;

import {Script, console} from "forge-std/Script.sol";
import {Upgrades, Options} from "@openzeppelin-foundry/contracts/Upgrades.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {RLCAdapter} from "../src/RLCAdapter.sol";
import {EnvUtils} from "./UpdateEnvUtils.sol";

contract Deploy is Script {
function setUp() public {}

function run() external {
vm.startBroadcast();

address rlcToken = vm.envAddress("RLC_SEPOLIA_ADDRESS"); // RLC token address on sepolia testnet
address lzEndpoint = vm.envAddress("LAYER_ZERO_SEPOLIA_ENDPOINT_ADDRESS"); // LayerZero sepolia endpoint
address delegate = vm.envAddress("SENDER_ADDRESS"); // Your actual wallet address
address ownerAddress = vm.envAddress("OWNER_ADDRESS"); // Your actual wallet address

// Deploy the RLCAdapter contract
RLCAdapter rlcAdapterImplementation = new RLCAdapter(rlcToken, lzEndpoint);
console.log("RLCAdapter implementation deployed at:", address(rlcAdapterImplementation));

RLCAdapter rlcAdapter = new RLCAdapter(rlcToken, lzEndpoint, delegate);
console.log("RLCAdapter deployed at:", address(rlcAdapter));
// Deploy the proxy contract
ERC1967Proxy rlcAdapterProxy = new ERC1967Proxy(
address(rlcAdapterImplementation),
abi.encodeWithSelector(rlcAdapterImplementation.initialize.selector, ownerAddress)
);
console.log("RLCAdapter proxy deployed at:", address(rlcAdapterProxy));

vm.stopBroadcast();

EnvUtils.updateEnvVariable("RLC_SEPOLIA_ADAPTER_ADDRESS", address(rlcAdapter));
EnvUtils.updateEnvVariable("RLC_SEPOLIA_ADAPTER_ADDRESS", address(rlcAdapterProxy));
}
}

contract Configure is Script {
function run() external {
vm.startBroadcast();

// RLCAdapter on Ethereum Sepolia
address adapterAddress = vm.envAddress("RLC_SEPOLIA_ADAPTER_ADDRESS"); // Read this variable from .env file
RLCAdapter adapter = RLCAdapter(adapterAddress);

// RLCOFT on Arbitrum Sepolia
address oftAddress = vm.envAddress("RLC_ARBITRUM_SEPOLIA_OFT_ADDRESS");
uint16 arbitrumSepoliaChainId = uint16(vm.envUint("LAYER_ZERO_ARBITRUM_SEPOLIA_CHAIN_ID")); //TODO: remove or make it chain agnostic
// Set trusted remote
adapter.setPeer(arbitrumSepoliaChainId, bytes32(uint256(uint160(oftAddress))));

vm.stopBroadcast();
}
}
38 changes: 32 additions & 6 deletions script/RLCOFT.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,51 @@
pragma solidity ^0.8.22;

import {Script, console} from "forge-std/Script.sol";
import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {RLCOFT} from "../src/RLCOFT.sol";
import {EnvUtils} from "./UpdateEnvUtils.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract Deploy is Script {
function setUp() public {}

function run() external {
vm.startBroadcast();

string memory name = vm.envString("RLC_OFT_TOKEN_NAME");
string memory symbol = vm.envString("RLC_TOKEN_SYMBOL");
address lzEndpoint = vm.envAddress("LAYER_ZERO_ARBITRUM_SEPOLIA_ENDPOINT_ADDRESS");
address delegate = vm.envAddress("SENDER_ADDRESS");
address owner = vm.envAddress("OWNER_ADDRESS");

RLCOFT rlcOFTImplementation = new RLCOFT(lzEndpoint);
console.log("RLCOFT implementation deployed at:", address(rlcOFTImplementation));

RLCOFT rlcOFT = new RLCOFT(name, symbol, lzEndpoint, delegate);
console.log("rlcOFT deployed at:", address(rlcOFT));
// Deploy the proxy contract
ERC1967Proxy rlcOFTProxy = new ERC1967Proxy(
address(rlcOFTImplementation),
abi.encodeWithSelector(rlcOFTImplementation.initialize.selector, name, symbol, owner)
);
console.log("RLCOFT proxy deployed at:", address(rlcOFTProxy));

vm.stopBroadcast();

EnvUtils.updateEnvVariable("RLC_ARBITRUM_SEPOLIA_OFT_ADDRESS", address(rlcOFT));
EnvUtils.updateEnvVariable("RLC_ARBITRUM_SEPOLIA_OFT_ADDRESS", address(rlcOFTProxy));
}
}

contract Configure is Script {
function run() external {
vm.startBroadcast();

// RLCOFT on Arbitrum Sepolia
address oftAddress = vm.envAddress("RLC_ARBITRUM_SEPOLIA_OFT_ADDRESS");
RLCOFT oft = RLCOFT(oftAddress);

// RLCAdapter on Ethereum Sepolia
address adapterAddress = vm.envAddress("RLC_SEPOLIA_ADAPTER_ADDRESS"); // Read this variable from .env file
uint16 ethereumSepoliaChainId = uint16(vm.envUint("LAYER_ZERO_SEPOLIA_CHAIN_ID")); // LayerZero chain ID for Ethereum Sepolia - TODO: remove or make it chain agnostic

// Set trusted remote
oft.setPeer(ethereumSepoliaChainId, bytes32(uint256(uint160(adapterAddress))));

vm.stopBroadcast();
}
}
2 changes: 1 addition & 1 deletion script/SendArbitrumToEthereum.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract SendTokensToSepolia is Script {

// Transfer parameters
uint16 destinationChainId = uint16(vm.envUint("LAYER_ZERO_SEPOLIA_CHAIN_ID")); // LayerZero chain ID for Ethereum Sepolia
address recipientAddress = vm.envAddress("SENDER_ADDRESS"); // Recipient on Ethereum Sepolia
address recipientAddress = vm.envAddress("OWNER_ADDRESS"); // Recipient on Ethereum Sepolia
console.log("Recipient: %s", recipientAddress);

uint256 amount = 5 * 10 ** 18; // RLC tokens (adjust the amount as needed)
Expand Down
2 changes: 1 addition & 1 deletion script/SendEthereumToArbitrum.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ contract SendTokensToArbitrumSepolia is Script {

// Transfer parameters
uint16 destinationChainId = uint16(vm.envUint("LAYER_ZERO_ARBITRUM_SEPOLIA_CHAIN_ID")); // LayerZero chain ID for Arbitrum Sepolia
address recipientAddress = vm.envAddress("SENDER_ADDRESS"); // Recipient on Arbitrum (your address)
address recipientAddress = vm.envAddress("OWNER_ADDRESS"); // Recipient on Arbitrum (your address)
uint256 amount = 5 * 10 ** 9; // RLC tokens (adjust the amount as needed)

// First, approve the adapter to spend your tokens
Expand Down
Loading