đ ī¸Create Your First dAPP using Foundry
Foundry is a smart contract development toolchain and manages your dependencies, compiles your project, runs tests, deploys, and lets you interact with the chain from the command-line and via Solidity scripts. Foundry also allow you to simulate development on the Sepolia network including Solidity native testing.
Note: that the FHE operations are only behavior simulations and may not accurately reflect actual system performance, gas consumption, etc.
Getting Started
First, create a new project, and then initialize the Foundry project using the forge init
command. The default template comes with one dependency installed: Forge Standard Library. This is the preferred testing library used for Foundry projects. Additionally, the template also comes with an empty starter contract and a simple test, you can easily remove them.
mkdir my-project
cd my-project
forge init
code my-project
If this is your first time using Foundry, refer to the installation instructions for guidance.
The initialized project has the following structure:
âââ lib/
âââ script/
âââ src/
âââ test/
These are the default paths for a Foundry project.
lib/
is where to store dependency libraries or external contracts.script/
is where to store scripts for automating tasks, such as contract deployment scripts or contract interaction scripts.src/
is where Solidity source code is placed, usually containing your contract files.test/
is used to store test files.
Clone the sight-oracle-contracts repository.
git clone https://github.com/sight-ai/sight-oracle-contracts
Install OpenZeppelin library.
forge install OpenZeppelin/openzeppelin-contracts --no-commit
Forge can remap dependencies to make them easier to import. Create a remappings.txt file in the root directory, then copy the text below into remappings.txt.
forge-std/=lib/forge-std/src/
@sight-oracle/contracts=lib/sight-oracle-contracts/contracts
@openzeppelin/contracts=lib/openzeppelin-contracts/contracts
Writing Contract
In the src directory, create a new file named Example.sol
, and copy the example contract code below into src/Example.sol
.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@sight-oracle/contracts/Oracle/Types.sol";
import "@sight-oracle/contracts/Oracle/Oracle.sol";
import "@sight-oracle/contracts/Oracle/RequestBuilder.sol";
import "@sight-oracle/contracts/Oracle/ResponseResolver.sol";
contract Example {
// Use Sight Oracle's RequestBuilder and ResponseResolver to interact with Sight Oracle
using RequestBuilder for Request;
using ResponseResolver for CapsulatedValue;
event OracleCallback(bytes32 indexed reqId);
bytes32 latestReqId;
Oracle public oracle;
CapsulatedValue private _target;
constructor(address oracle_) payable {
oracle = Oracle(payable(oracle_));
}
function makeRequest() public payable {
// Initialize new FHE computation request of a single step.
Request memory r = RequestBuilder.newRequest(
msg.sender,
1,
address(this),
this.callback.selector, // specify the callback for Oracle
""
);
// Generate a random encrypted value and store in Sight Network
r.rand();
// Send the request via Sight FHE Oracle
latestReqId = oracle.send(r);
}
// only Oracle can call this
function callback(
bytes32 reqId,
CapsulatedValue[] memory values
) public onlyOracle {
// Decode value from Oracle callback
CapsulatedValue memory result = values[0];
// Keep this encrypted target value
_target = result;
emit OracleCallback(reqId);
}
function getLatestReqId() public view returns (bytes32) {
return latestReqId;
}
function getTarget() public view returns (CapsulatedValue memory) {
return _target;
}
modifier onlyOracle() {
require(msg.sender == address(oracle), "Only Oracle Can Do This");
_;
}
}
Explanation
Contract Initialization:
The
Example
contract imports the necessary modules from the Sight Oracle package.The contract uses the
RequestBuilder
library to construct requests and theResponseResolver
library to interpret the responses.
Constructor:
The constructor initializes the contract with the address of the Sight Oracle. This address is used to send requests and handle callbacks.
makeRequest Function:
This function initiates a new request to the Sight Oracle.
A new request is created using
RequestBuilder.newRequest
, specifying the sender, the number of steps in the computation, the address to which the callback should be sent, and the callback function.The
rand
function is called to generate a random encrypted value and store it in the Sight Network.The
send
function sends the request to the Sight Oracle.
callback Function:
This function is called by the Sight Oracle once the computation is complete.
It decodes the returned value using the
ResponseResolver
and stores the encrypted target value in the_target
variable.
Modifiers:
onlyOracle
: Ensures that only the Sight Oracle can call thecallback
function.
View Function:
getLatestReqId
: Retrieves the request ID of the most recent request sent to theOracle
.getTarget
: Retrieves the target value of the most recent request.
Deploy Contracts
Environment Setup
We will to deploy the Example
contract and the Sight Oracle contract to the Sepolia testnet. To do this, we need to configure Foundry by setting up the Sepolia RPC.
NoteīŧIf you donât have Sepolia RPC yet, click here to get Sepolia RPC.
PRIVATE_KEY=<YOUR_PRIVATE_KEY>
SEPOLIA_RPC_URL=<SEPOLIA_RPC_URL>
SEPOLIA_ORACLE_CONTRACT_ADDRESS=0xC5ac65f17Ce781E9F325634b6218Dc75a5CF9abF
Add these environment variables to the .env
file. SEPOLIA_ORACLE_CONTRACT_ADDRESS
is the Oracle address deployed on the Sepolia test network.
Write a deployment script.
Next, create a folder and name it script
and create a file in it called DeployExample.s.sol
. This is where we will create the deployment script itself.
Copy the following code snippet into the scripts/DeployExample.sol
file.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {Example} from "../src/Example.sol";
contract DeployExample is Script {
function run() external {
address oracleAddress = vm.envAddress(
"SEPOLIA_ORACLE_CONTRACT_ADDRESS"
);
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
Example example = new Example(oracleAddress);
vm.stopBroadcast();
}
}
Explanation
A
Script
contract in the Foundry framework is a foundational contract that provides various utilities and functionalities required when deploying smart contracts.DeployExample
is a deployment script contract, which inherits from theScript
contract in the Foundry framework.Use
vm.envAddress()
andvm.envUint()
to read the Oracle contract address on the Sepolia test network and the private key used for deployment from environment variables, respectively.vm.startBroadcast()
initiates the recording of all transactions that will be broadcasted to the blockchain, whilevm.stopBroadcast()
concludes the broadcasting of transactions.new
is used to create a new instance of a smart contract. Usingnew Example(oracleAddress)
to deploy a Example contract with the Oracle address argument.
Note: you must be careful when exposing private keys in a
.env
file and loading them into programs. This is only recommended for use with non-privileged deployers or for local / test setups. For production setups please review the various wallet options that Foundry supports.
Run deployment script
# To load the variables in the .env file
source .env
# To deploy oracle contract and example contract
forge script script/DeployExample.s.sol:DeployExample --rpc-url $SEPOLIA_RPC_URL --broadcast -vvvv
Explanation
script/DeployExample.s.sol:DeployExample
is the path to the script you want to run.The
--rpc-url
option specifies the RPC endpoint, which can be a URL or an existing alias from the[rpc_endpoints]
table.The
--broadcast
parameter will broadcast the transactions to the network.The
-vvvv
flag will display debug information, including internal calls for each transaction, gas consumption, RPC request details, etc. This is especially useful for debugging and diagnostics.
This should take a little while, since Forge will also wait for the transaction receipts. You should see something like this after a minute or so:

Congratulations! you have successfully deployed the Sight Oracle and Example contracts to the Sepolia testnet.
Send Request
Now, you have successfully deployed the Example contract. Next, you can call the makeRequest
function from the Example contract just deployed to send a request. Foundry provides a powerful tool specifically for interacting with on-chain contractsâcast
, and we will use this tool to execute the makeRequest
call.
Run this code to send a request to the Example contract.
cast send <YOUR_CONTACT_ADDRESS> "makeRequest()" --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY
Replace
<YOUR_CONTACT_ADDRESS>
with theExample
contract address you just deployed.The
cast send
is used to sign and send a transaction;makeRequest()
is the function being called. The--private-key
option specifies the private key used for signing the transaction.
It may take some time for this transaction to be written to the Sepolia test chain. After about 10 seconds, you should see something like this:

Congratulations, you have successfully sent a Sight Oracle request on the Sepolia test network. Next, you can copy the transactionHash
from the log to view the details of the request on Etherscan, as shown in the image below:

Last updated