## Deploying a Subgraph Using The Graph Protocol
### Setting Up the Subgraph Studio
1. Visit [The Graph website](https://thegraph.com/en/).
2. Click on "Products" and select "Subgraph Studio."
3. Connect your wallet.
4. Complete all account verification and signing processes.
Once you've completed the above steps, click on the "Create a Subgraph" button. Choose a name that suits the subgraph you want to create (in this tutorial, we'll name it `subgraph-tutorial`).
Next, fill in all the details of your subgraph. On the side panel, you'll see several commands that you'll need to follow in your terminal.
### Scaffolding the Subgraph via the Terminal
1. Initialize a folder and navigate into it:
```bash
mkdir subgraph-tutorial
cd subgraph-tutorial
```
2. Install The Graph CLI:
```bash
npm install -g @graphprotocol/graph-cli
```
3. Initialize the subgraph:
```bash
graph init --studio subgraph-tutorial
```
- Select the protocol, slug, network, and contract address to proceed. In this tutorial, we'll use the Ethereum mainnet and the Uniswap token with the contract address `0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984`.
- If the ABI isn't fetched automatically, create a `.json` file, save the ABI, and provide the path to the ABI. Then, pick a block number, set the contract name (it's best to use the actual contract name), and select `true` to index contract events as entities.
4. Authenticate and deploy:
```bash
graph auth --studio <authToken>
```
After authenticating, proceed with the following steps:
1. Navigate into the subgraph directory:
```bash
cd subgraph-tutorial
```
2. Run the following commands to ensure type safety and compile your subgraph whenever you make changes:
```bash
graph codegen
graph build
```
3. Review the schema file, as it defines what you can query from the frontend.
### Deploying the Subgraph to Subgraph Studio and Working with the Studio
To deploy your subgraph to the Subgraph Studio, use the following command:
```bash
graph deploy --studio uni-sg-tut
```
You can then check the Subgraph Studio to see it indexing. Head over to the playground to demo the requests and see how it works.
These requests can then be integrated into your frontend and utilized in a dApp. Your subgraph can be published and made available for other developers to use. Keep in mind that this is a hosted service, and publishing will require some gas fees.
## Building a Dapp and leveraging the Graph in real time
- Network faucet for base - https://docs.base.org/tools/network-faucets
- Network details for base
- Name Value
Network Name Base Sepolia
Description A public testnet for Base.
RPC Endpoint https://sepolia.base.org
Rate limited and not for production systems.
Chain ID 84532
Currency Symbol ETH
Block Explorer https://sepolia-explorer.base.org
Setup in metamask or download coinbbase wallet https://chromewebstore.google.com/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad?hl=en and switch network to base sepolia
### Initializing environments
`npm create vite@latest my-app -- --template react` to initialize a react project
- `cd my-app` to enter the project folder
- `yarn add hardhat` to setup the smart contract development and testing suite
- `npx hardhat init` to initiate hardhat
- Follow the prompt and select Create a Javascript project.
Press “y" to agree to other options and continue.https://hackmd.io/Nk1sAIyrQDybqbVt8kQiPA?both#
- Setup Hardhat config for deploy to testnet
- Enter the smart contract code
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleRecord {
// Define a Record structure
struct Record {
uint256 id;
string data;
address creator;
}
// State variables
uint256 public recordCount = 0;
mapping(uint256 => Record) public records;
// Event to be emitted when a new record is created
event RecordCreated(
uint256 id,
string data,
address indexed creator
);
// Function to create a new record
function createRecord(string memory _data) public {
recordCount++;
records[recordCount] = Record(recordCount, _data, msg.sender);
// Emit the RecordCreated event
emit RecordCreated(recordCount, _data, msg.sender);
}
// Function to retrieve a record by ID
function getRecord(uint256 _id) public view returns (Record memory) {
return records[_id];
}
}
```
- Update Hardhat config to
```
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.23",
networks: {
"base-sepolia": {
chainId: 84532,
url: "https://sepolia.base.org", // Insert Infura Celo Url here
accounts: [`0x${process.env.WALLET_KEY}`],
},
},
};
```
- Install the toolbox and dotenv
```yarn add @nomicfoundation/hardhat-toolbox && yarn add dotenv```
- ```npx hardhat compile``` to compile the smart contract
- Now to deploy the Smart contract , Create this set of files : scripts/deploy.js
- Add This :-1:
```=javascript
require("hardhat");
async function main() {
const SimpleRecord = await ethers.deployContract("SimpleRecord");
await SimpleRecord.waitForDeployment();
console.log("SimpleRecord Contract Deployed at " + SimpleRecord.target);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
```
- Next run the following command to deploy to base sepolia ```npx hardhat run scripts/deploy.js --network base-sepolia```
- Head over to https://sepolia.basescan.org/ to confirm the address has actually been deployed , then verify the contract. Fetch Abi from artifact folder
Now we can head over to the dapp section .
- Open src/app.jsx and update with the following code, Note our dapp would allow us Create record and also get record based on its ID
```=javascript
import "./App.css";
import React from "react";
import { useState } from "react";
import { ethers, BrowserProvider } from "ethers";
// Import the json-file from the ABI
import SimpleRecord from "../artifacts/contracts/SimpleRecord.sol/SimpleRecord.json";
// Store the contract address in a variable
const simpleRecordAddress = "0x98F7eDaB05Cd392232D317e250CE3337981401D9"; // Deployed to testnet
const App = () => {
// Store Record details in a local state
const [record, setRecordValue] = useState();
const [id, setIdValue] = useState();
const [fetchedRecord, setFetchedRecordValue] = useState();
// Request access to User's MetaMask account
const requestAccount = async () => {
await window.ethereum.request({ method: "eth_requestAccounts" });
};
// Function for retrieving record value from smart contract.
const getRecord = async () => {
if (!id) return;
if (typeof window.ethereum !== "undefined") {
const web3Provider = new ethers.BrowserProvider(window.ethereum);
const contract = new ethers.Contract(
simpleRecordAddress,
SimpleRecord.abi,
web3Provider
);
try {
const data = await contract.getRecord(id);
console.log(`Data: ${data}`);
setFetchedRecordValue(data);
} catch (error) {
console.error(error);
}
}
};
// Function for updating message value on smart contract
const createRecord = async () => {
if (!record) return;
if (typeof window.ethereum !== "undefined") {
await requestAccount();
const web3Provider = new ethers.BrowserProvider(window.ethereum);
const signer = await web3Provider.getSigner();
const contract = new ethers.Contract(
simpleRecordAddress,
SimpleRecord.abi,
signer
);
const transaction = await contract.createRecord(record);
await transaction.wait();
setRecordValue("");
alert("created successfully");
}
};
return (
<div className="container">
<button onClick={getRecord}>FetchRecord</button>
<button onClick={createRecord}>Create Record</button>
<input
onChange={(e) => setRecordValue(e.target.value)}
placeholder="Write your Record here..."
/>
<input
onChange={(e) => setIdValue(e.target.value)}
placeholder="Write The id of record you want to fetch here..."
/>
{fetchedRecord && (
<p>{` Id of ${fetchedRecord[0]} with value of ${fetchedRecord[1]} and creator of address ${fetchedRecord[2]}`}</p>
)}
</div>
);
};
export default App;
```
Then Update src/App.css with :-1:
```=css
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
/* styles.css */
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
button {
background-color: #4caf50; /* Green */
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #45a049;
}
input {
padding: 10px;
margin: 10px;
width: 300px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
transition: border-color 0.3s ease;
}
input:focus {
border-color: #4caf50;
outline: none;
}
```
Then to View it , run ```yarn run dev```
Test functions and all
- Now we build our graph to make things work better
- `yarn global add @graphprotocol/graph-cli` to install graph cli
- `graph init ` to initialize our subgraph then fill in all details
- Follow the prompt > ethereum > subgraphstudio > pick nice slug > base-sepolia> input contract address > index entities - true
- Head over to subgraph studio, create and follow the prompts
- Remember the endpoint created for you
Now we install the graphQL apollo client to interact with the frontend
- We use `yarn add @apollo/client graphql` to install it
- Then import it in our app.jsx with
```=javascript
import { ApolloProvider, ApolloClient, InMemoryCache, gql, useQuery } from '@apollo/client';
```
- Create a client that would be used when querying
```=javascript
const client = new ApolloClient({
uri: 'https://api.thegraph.com/subgraphs/name/<GITHUB_USER>/<SUBGRAPH_NAME>',
cache: new InMemoryCache(),
});
```
- Then a query to collect first 3 values from the indexed data
```=javascript
const GET_DATA = gql`
query {
entities {
id
field1
field2
}
}
`;
```
- Remember to Wrap App component with Apollo Provider and pass client as a prop
- ` <ApolloProvider client={client}>`
- Create a data fetcher component that will fetch all data
```
const client = new ApolloClient({
uri: "https://api.studio.thegraph.com/query/87032/simplerecords/version/latest",
cache: new InMemoryCache(),
});
const GET_DATA = gql`
{
recordCreateds(first: 5) {
id
SimpleRecord_id
data
creator
}
}
`;
function DataFetcher() {
const { loading, error, data } = useQuery(GET_DATA);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
{data.recordCreateds.map((entity) => (
<div key={entity.SimpleRecord_id}>
<p>{entity.SimpleRecord_id}</p>
<p>{entity.data}</p>
<p>{entity.creator}</p>
</div>
))}
</div>
);
}
```
- This component can further be utilized in our project
- By importing it. Final code should look like this
```=javascript
import "./App.css";
import React from "react";
import { useState } from "react";
import { ethers, BrowserProvider } from "ethers";
// Import the json-file from the ABI
import SimpleRecord from "../artifacts/contracts/SimpleRecord.sol/SimpleRecord.json";
// Store the contract address in a variable
const simpleRecordAddress = "0x98F7eDaB05Cd392232D317e250CE3337981401D9"; // Deployed to testnet
//GRAPHQL ADDITION
import {
ApolloProvider,
ApolloClient,
InMemoryCache,
gql,
useQuery,
} from "@apollo/client";
const client = new ApolloClient({
uri: "https://api.studio.thegraph.com/query/87032/simplerecords/version/latest",
cache: new InMemoryCache(),
});
const GET_DATA = gql`
{
recordCreateds(first: 5) {
id
SimpleRecord_id
data
creator
}
}
`;
function DataFetcher() {
const { loading, error, data } = useQuery(GET_DATA);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
{data.recordCreateds.map((entity) => (
<div key={entity.SimpleRecord_id}>
<p>{entity.SimpleRecord_id}</p>
<p>{entity.data}</p>
<p>{entity.creator}</p>
</div>
))}
</div>
);
}
const App = () => {
// Store Record details in a local state
const [record, setRecordValue] = useState();
const [id, setIdValue] = useState();
const [fetchedRecord, setFetchedRecordValue] = useState();
// Request access to User's MetaMask account
const requestAccount = async () => {
await window.ethereum.request({ method: "eth_requestAccounts" });
};
// Function for retrieving record value from smart contract.
const getRecord = async () => {
if (!id) return;
if (typeof window.ethereum !== "undefined") {
const web3Provider = new ethers.BrowserProvider(window.ethereum);
const contract = new ethers.Contract(
simpleRecordAddress,
SimpleRecord.abi,
web3Provider
);
try {
const data = await contract.getRecord(id);
console.log(`Data: ${data}`);
setFetchedRecordValue(data);
} catch (error) {
console.error(error);
}
}
};
// Function for updating message value on smart contract
const createRecord = async () => {
if (!record) return;
if (typeof window.ethereum !== "undefined") {
await requestAccount();
const web3Provider = new ethers.BrowserProvider(window.ethereum);
const signer = await web3Provider.getSigner();
const contract = new ethers.Contract(
simpleRecordAddress,
SimpleRecord.abi,
signer
);
const transaction = await contract.createRecord(record);
await transaction.wait();
setRecordValue("");
alert("created successfully");
}
};
return (
<ApolloProvider client={client}>
<div className="container">
<button onClick={getRecord}>FetchRecord</button>
<button onClick={createRecord}>Create Record</button>
<input
onChange={(e) => setRecordValue(e.target.value)}
placeholder="Write your Record here..."
/>
<input
onChange={(e) => setIdValue(e.target.value)}
placeholder="Write The id of record you want to fetch here..."
/>
{fetchedRecord && (
<p>{` Id of ${fetchedRecord[0]} with value of ${fetchedRecord[1]} and creator of address ${fetchedRecord[2]}`}</p>
)}
</div>
<DataFetcher />
</ApolloProvider>
);
};
export default App;
```