🛠️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-projectIf 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-contractsInstall OpenZeppelin library.
forge install OpenZeppelin/openzeppelin-contracts --no-commitForge 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/contractsWriting 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
Examplecontract imports the necessary modules from the Sight Oracle package.The contract uses the
RequestBuilderlibrary to construct requests and theResponseResolverlibrary 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
randfunction is called to generate a random encrypted value and store it in the Sight Network.The
sendfunction 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
ResponseResolverand stores the encrypted target value in the_targetvariable.
Modifiers:
onlyOracle: Ensures that only the Sight Oracle can call thecallbackfunction.
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=0xC5ac65f17Ce781E9F325634b6218Dc75a5CF9abFAdd 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
Scriptcontract in the Foundry framework is a foundational contract that provides various utilities and functionalities required when deploying smart contracts.DeployExampleis a deployment script contract, which inherits from theScriptcontract 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.newis 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
.envfile 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:DeployExampleis the path to the script you want to run.The
--rpc-urloption specifies the RPC endpoint, which can be a URL or an existing alias from the[rpc_endpoints]table.The
--broadcastparameter will broadcast the transactions to the network.The
-vvvvflag 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_KEYReplace
<YOUR_CONTACT_ADDRESS>with theExamplecontract address you just deployed.The
cast sendis used to sign and send a transaction;makeRequest()is the function being called. The--private-keyoption 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