Skip to content

Commit 32866fb

Browse files
authored
docs(fdc): add foundry support for missing hardhat guides (#804)
2 parents 22febce + 8a44fc8 commit 32866fb

File tree

5 files changed

+1498
-1
lines changed

5 files changed

+1498
-1
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
---
2+
title: Cross-Chain FDC
3+
slug: cross-chain-fdc
4+
authors: [anthonyamadia]
5+
description: Use the FDC data on a non-Flare, like the XRPL EVM Sidechain.
6+
tags: [fdc, foundry]
7+
keywords:
8+
[ethereum, flare-data-connector, evm, cross-chain, web2json, relay, xrpl]
9+
sidebar_position: 11
10+
---
11+
12+
import AvailableTestnet from "../_available_testnet.mdx";
13+
14+
This guide demonstrates a powerful cross-chain workflow using the Flare Data Connector (FDC).
15+
We will fetch data from a public Web2 API using the [Web2Json](/fdc/attestation-types/web-2-json) attestation type on a Flare network (Coston2) and then use the resulting proof to trigger a state change on a different EVM-compatible blockchain, the XRPL EVM Sidechain Testnet.
16+
17+
The key to this cross-chain interaction is the `Relay` contract.
18+
For a non-Flare chain to verify FDC proofs, the Merkle root of each finalized voting round on Flare must be transmitted to the target chain.
19+
This is handled by the `Relay` contract, which must be deployed on the target chain.
20+
For the XRPL EVM Sidechain, Flare provides a relayer service where a backend script listens for `Relay` transactions on Flare and copies them to the `Relay` contract on the target chain.
21+
This allows a verifier contract on the target chain to confirm the validity of FDC proofs without trusting a centralized intermediary.
22+
23+
Before running any code, check if the `Relay` contract is already being relayed, or submit a request to the Flare team.
24+
25+
<AvailableTestnet />
26+
27+
For this example, we will:
28+
29+
1. Deploy a custom verification infrastructure to the XRPL EVM Sidechain Testnet.
30+
2. Request data about a Star Wars character from the `swapi.info` API on the Coston2 Testnet.
31+
3. Submit this request to the FDC on Coston2.
32+
4. Retrieve the finalized proof and use it to call a consumer contract on the XRPL EVM Sidechain, which verifies the proof and stores the character's data on-chain.
33+
34+
:::info
35+
The code used in this guide is available in the [Flare Foundry starter repository](https://github.com/flare-foundation/flare-foundry-starter).
36+
:::
37+
38+
### 1. Deploy Infrastructure on Target Chain (XRPL EVM Sidechain)
39+
40+
This workflow requires a one-time deployment of a persistent infrastructure on the target chain. These core contracts manage contract addresses and proof verification.
41+
42+
- **`AddressUpdater`**: A governance-controlled contract that maintains a registry of key contract addresses, such as the `Relay`.
43+
- **`FdcVerification`**: A custom verification contract that retrieves the trusted Merkle root from the `Relay` to verify FDC proofs on the target chain. The protocol ID (e.g., 200 for Coston2) configured in this contract must match the FDC instance on the source Flare network.
44+
45+
The `DeployInfrastructure` script handles this setup. On a non-Flare chain, the `Relay` address is a known, pre-deployed address that must be provided in your `.env` file.
46+
47+
```solidity title="script/CrossChainFdc.s.sol"
48+
// Deploys the persistent CORE INFRASTRUCTURE contracts to the target non-Flare chain.
49+
contract DeployInfrastructure is Script {
50+
function run() external {
51+
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
52+
address governance = vm.addr(deployerPrivateKey);
53+
54+
// On a non-Flare chain, the Relay address is a known, pre-deployed address.
55+
// It must be provided in the .env file.
56+
address relayAddress = vm.envAddress("RELAY_ADDRESS");
57+
console.log("Using Relay address from .env file:", relayAddress);
58+
require(relayAddress != address(0), "Error: RELAY_ADDRESS not set in .env or invalid.");
59+
60+
vm.startBroadcast(deployerPrivateKey);
61+
62+
AddressUpdater addressUpdater = new AddressUpdater(governance);
63+
// The protocol ID must match the one used by the FDC instance on the Flare network (e.g., 200 for Coston2).
64+
FdcVerification fdcVerification = new FdcVerification(address(addressUpdater), 200);
65+
66+
string[] memory names = new string[](2);
67+
address[] memory addresses = new address[](2);
68+
69+
names[0] = "Relay";
70+
addresses[0] = relayAddress;
71+
72+
names[1] = "AddressUpdater";
73+
addresses[1] = address(addressUpdater);
74+
75+
addressUpdater.addOrUpdateContractNamesAndAddresses(names, addresses);
76+
77+
IIAddressUpdatable[] memory contractsToUpdate = new IIAddressUpdatable[](1);
78+
contractsToUpdate[0] = fdcVerification;
79+
addressUpdater.updateContractAddresses(contractsToUpdate);
80+
81+
vm.stopBroadcast();
82+
83+
vm.createDir(dirPath, true);
84+
vm.writeFile(string.concat(dirPath, "addressUpdater.txt"), vm.toString(address(addressUpdater)));
85+
vm.writeFile(string.concat(dirPath, "fdcVerification.txt"), vm.toString(address(fdcVerification)));
86+
87+
console.log("\n--- Infrastructure Deployment Complete on Chain ID:", block.chainid, "---");
88+
console.log("AddressUpdater: ", address(addressUpdater));
89+
console.log("FdcVerification: ", address(fdcVerification));
90+
}
91+
}
92+
```
93+
94+
Run the script to deploy the core contracts to the XRPL EVM Sidechain. This only needs to be done once.
95+
96+
```bash
97+
forge script script/CrossChainFdc.s.sol:DeployInfrastructure --rpc-url $XRPLEVM_RPC_URL_TESTNET --broadcast
98+
```
99+
100+
### 2. Prepare and Submit Request on Source Chain (Coston2)
101+
102+
This step, executed by the `PrepareAndSubmitRequest` script, must be run on a Flare network (Coston2).
103+
104+
It prepares the `Web2Json` request off-chain by calling the verifier API and then immediately submits the resulting `abiEncodedRequest` to the FDC on-chain.
105+
106+
The script saves the `abiEncodedRequest` and the resulting `votingRoundId` to files, which will be needed for the final step on the target chain.
107+
108+
```solidity title="script/CrossChainFdc.s.sol"
109+
// Prepares and submits the FDC request on the Flare Network.
110+
contract PrepareAndSubmitRequest is Script {
111+
using Surl for *;
112+
string public constant SOURCE_NAME = "PublicWeb2";
113+
114+
function run() external {
115+
console.log("--- Preparing and Submitting FDC request on Chain ID:", block.chainid, "---");
116+
vm.createDir(dirPath, true);
117+
118+
string memory attestationType = FdcBase.toUtf8HexString(attestationTypeName);
119+
string memory sourceId = FdcBase.toUtf8HexString(SOURCE_NAME);
120+
121+
string memory apiUrl = "https://swapi.info/api/people/3"; // C-3PO
122+
string memory postProcessJq = '{name: .name, height: .height, mass: .mass, numberOfMovies: .films | length, apiUid: (.url | split(\\"/\\") | .[-1] | tonumber)}';
123+
string memory abiSignature = '{\\"components\\":[{\\"internalType\\":\\"string\\",\\"name\\":\\"name\\",\\"type\\":\\"string\\"},{\\"internalType\\":\\"uint256\\",\\"name\\":\\"height\\",\\"type\\":\\"uint256\\"},{\\"internalType\\":\\"uint256\\",\\"name\\":\\"mass\\",\\"type\\":\\"uint256\\"},{\\"internalType\\":\\"uint256\\",\\"name\\":\\"numberOfMovies\\",\\"type\\":\\"uint256\\"},{\\"internalType\\":\\"uint256\\",\\"name\\":\\"apiUid\\",\\"type\\":\\"uint256\\"}],\\"name\\":\\"dto\\",\\"type\\":\\"tuple\\"}';
124+
string memory requestBody = string.concat('{"url":"',apiUrl,'","httpMethod":"GET","headers":"{}","queryParams":"{}","body":"{}","postProcessJq":"',postProcessJq,'","abiSignature":"',abiSignature,'"}');
125+
126+
// Prepare request off-chain
127+
(string[] memory headers, string memory body) = FdcBase.prepareAttestationRequest(attestationType, sourceId, requestBody);
128+
string memory baseUrl = vm.envString("WEB2JSON_VERIFIER_URL_TESTNET");
129+
string memory url = string.concat(baseUrl, attestationTypeName, "/prepareRequest");
130+
(, bytes memory data) = url.post(headers, body);
131+
FdcBase.AttestationResponse memory response = FdcBase.parseAttestationRequest(data);
132+
133+
// Submit request on-chain
134+
uint256 timestamp = FdcBase.submitAttestationRequest(response.abiEncodedRequest);
135+
uint256 votingRoundId = FdcBase.calculateRoundId(timestamp);
136+
137+
// Write data to files for the next step
138+
FdcBase.writeToFile(dirPath, "abiEncodedRequest.txt", StringsBase.toHexString(response.abiEncodedRequest), true);
139+
FdcBase.writeToFile(dirPath, "votingRoundId.txt", Strings.toString(votingRoundId), true);
140+
console.log("Successfully prepared and submitted request. Voting Round ID:", votingRoundId);
141+
}
142+
}
143+
```
144+
145+
Run the script on **Coston2**:
146+
147+
```bash
148+
forge script script/CrossChainFdc.s.sol:PrepareAndSubmitRequest --rpc-url $COSTON2_RPC_URL --broadcast --ffi
149+
```
150+
151+
### 3. Deliver Proof to Consumer on Target Chain (XRPL EVM Sidechain)
152+
153+
The final script, `DeliverProofToContract`, is executed on the target chain (XRPL EVM Sidechain).
154+
155+
It handles deploying the consumer contract, retrieving the proof, and delivering it for verification.
156+
157+
1. **Deploy Consumer Contract**: It first deploys the `StarWarsCharacterListV3` contract, passing it the address of the `FdcVerification` contract deployed in Step 1.
158+
2. **Retrieve Proof**: It reads the `abiEncodedRequest` and `votingRoundId` from the files created in Step 2. After waiting for the voting round on Flare to finalize (max. 180 seconds), it polls the Data Availability layer to get the proof.
159+
3. **Interact with Consumer**: It calls `addCharacter` on the deployed consumer contract, passing the `finalProof`. A `value` of 1 wei is sent to cover the fee required by the `Relay` contract to fetch the Merkle root from the source chain.
160+
161+
```solidity title="script/CrossChainFdc.s.sol"
162+
// Deploys the consumer, waits, retrieves proof, and delivers it on the target chain.
163+
contract DeliverProofToContract is Script {
164+
function run() external {
165+
console.log("--- Delivering Proof on Chain ID:", block.chainid, "---");
166+
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
167+
168+
// --- Deploy Consumer Contract ---
169+
string memory fdcVerificationPath = string.concat(dirPath, "fdcVerification.txt");
170+
require(vm.exists(fdcVerificationPath), "Infrastructure not deployed. Run DeployInfrastructure first.");
171+
address fdcVerificationAddress = vm.parseAddress(vm.readFile(fdcVerificationPath));
172+
173+
vm.startBroadcast(deployerPrivateKey);
174+
StarWarsCharacterListV3 characterList = new StarWarsCharacterListV3(fdcVerificationAddress);
175+
vm.stopBroadcast();
176+
console.log("StarWarsCharacterListV3 consumer deployed to:", address(characterList));
177+
178+
// --- Retrieve Proof and Interact ---
179+
string memory requestHex = vm.readFile(string.concat(dirPath, "abiEncodedRequest.txt"));
180+
uint256 votingRoundId = FdcBase.stringToUint(vm.readFile(string.concat(dirPath, "votingRoundId.txt")));
181+
182+
FdcVerification fdcVerification = FdcVerification(fdcVerificationAddress);
183+
uint8 protocolId = fdcVerification.fdcProtocolId();
184+
185+
bytes memory proofData = FdcBase.retrieveProof(protocolId, requestHex, votingRoundId);
186+
187+
FdcBase.ParsableProof memory parsedProof = abi.decode(proofData, (FdcBase.ParsableProof));
188+
IWeb2Json.Response memory proofResponse = abi.decode(parsedProof.responseHex, (IWeb2Json.Response));
189+
IWeb2Json.Proof memory finalProof = IWeb2Json.Proof(parsedProof.proofs, proofResponse);
190+
191+
console.log("\nDelivering proof to consumer contract...");
192+
vm.startBroadcast(deployerPrivateKey);
193+
characterList.addCharacter{value: 1}(finalProof);
194+
vm.stopBroadcast();
195+
console.log("Proof successfully delivered!");
196+
197+
StarWarsCharacter[] memory characters = characterList.getAllCharacters();
198+
require(characters.length > 0, "Verification failed: No character was added.");
199+
console.log("\n--- Character Added Verification ---");
200+
console.log("Name:", characters[0].name);
201+
console.log("BMI:", characters[0].bmi);
202+
}
203+
}
204+
```
205+
206+
Run the final script on the **XRPL EVM Sidechain**:
207+
208+
```bash
209+
forge script script/CrossChainFdc.s.sol:DeliverProofToContract --rpc-url $XRPLEVM_RPC_URL_TESTNET --broadcast --ffi
210+
```

0 commit comments

Comments
 (0)