Publish on chain using secretjs
Pre-requisite
At this stage, we assume that you followed the steps at ReactJs.
We will be using Keplr (opens in a new tab) as a wallet to interact with the frontend interface. Make sure that you have it installed and funded with testnet Scrt (opens in a new tab). To connect to the testnet you can use this website (opens in a new tab)
You can access the code of this walkthrough on Gitlab:
Contract deployment
Clone the client contract repo.
This Secret contract (opens in a new tab) serves as a client to our Reclaim contract. It takes Reclaim's contract address and code hash, handles proofs, and verifies them.
git clone https://gitlab.reclaimprotocol.org/starterpacks/reclaim-secret-client
cd reclaim-secret-client
make build
Add your wallet information.
As stated in the README, add your wallet credentials to your node/.env
as specified in node/.env.example
.
MNEMONIC= // Your mnemonic
Deploy and verify a proof.
Give the node/verify.js file a quick read, make sure that you have the correct RPCs and contract addresses.
If you want to deploy to mainnet, search for the Uncomment for mainnet
comment.
By running node/verify.js
, you can upload the contract, instantiate it, and verify a Reclaim proof on it.
Run the script and take a note of your contract address and code hash, we will be using them later.
cd node && npm i
node verify
Here is an example of how your output should look like:
React client development
Bootstrap project and install packages.
npx create-react-app your-app-name
cd your-app-name
npm install @reclaimprotocol/js-sdk secretjs react-qr-code
Setup your React codebase.
We will continue building on the reclaim react client, new lines are highlighted below.
import "./App.css";
import { Reclaim } from "@reclaimprotocol/js-sdk";
import { useState } from "react";
import QRCode from "react-qr-code";
function App() {
const [url, setUrl] = useState("");
const [ready, setReady] = useState(false);
const [proof, setProof] = useState({});
const reclaimClient = new Reclaim.ProofRequest("YOUR_APPLICATION_ID_HERE"); //TODO: replace with your applicationId
async function generateVerificationRequest() {
const providerId = "PROVIDER_ID"; //TODO: replace with your provider ids you had selected while creating the application
reclaimClient.addContext(
`user's address`,
"for acmecorp.com on 1st january"
);
await reclaimClient.buildProofRequest(providerId);
reclaimClient.setSignature(
await reclaimClient.generateSignature(
APP_SECRET //TODO : replace with your APP_SECRET
)
);
const { requestUrl, statusUrl } =
await reclaimClient.createVerificationRequest();
setUrl(requestUrl);
await reclaimClient.startSession({
onSuccessCallback: (proofs) => {
console.log("Verification success", proofs);
setReady(true);
setProof(proofs[0]);
// Your business logic here
},
onFailureCallback: (error) => {
console.error("Verification failed", error);
// Your business logic here to handle the error
},
});
}
return (
<div className="App">
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "50vh",
}}
>
{!url && (
<button onClick={generateVerificationRequest}>
Create Claim QrCode
</button>
)}
{url && <QRCode value={url} />}
</div>
</div>
);
}
export default App;
Create a new folder (secretJs).
Structure the folder as per the following, these are configs to call Reclaim on Secret.
src/
|- secretJs/SecretjsContext.js
|- secretJs/SecretjsFunctions.js
|- App.js
Copy this to SecretjsContext.js.
By default, the values below are for Secret's testnet, uncomment for mainnet.
import { createContext, useState } from "react";
import { SecretNetworkClient } from "secretjs";
const SecretjsContext = createContext(null);
// Uncomment for mainnet metadata
// const SECRET_CHAIN_ID = "secret-4";
// const SECRET_LCD = "https://lcd.mainnet.secretsaturn.net";
const SECRET_CHAIN_ID = "pulsar-3";
const SECRET_LCD = "https://api.pulsar3.scrttestnet.com";
const SecretjsContextProvider = ({ children }) => {
const [secretjs, setSecretjs] = useState(null);
const [secretAddress, setSecretAddress] = useState("");
async function setupKeplr(setSecretjs, setSecretAddress) {
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
while (
!window.keplr ||
!window.getEnigmaUtils ||
!window.getOfflineSignerOnlyAmino
) {
await sleep(50);
}
await window.keplr.enable(SECRET_CHAIN_ID);
window.keplr.defaultOptions = {
sign: {
preferNoSetFee: false,
disableBalanceCheck: true,
},
};
const keplrOfflineSigner =
window.getOfflineSignerOnlyAmino(SECRET_CHAIN_ID);
const accounts = await keplrOfflineSigner.getAccounts();
const secretAddress = accounts[0].address;
const secretjs = new SecretNetworkClient({
url: SECRET_LCD,
chainId: SECRET_CHAIN_ID,
wallet: keplrOfflineSigner,
walletAddress: secretAddress,
encryptionUtils: window.getEnigmaUtils(SECRET_CHAIN_ID),
});
setSecretAddress(secretAddress);
setSecretjs(secretjs);
}
async function connectWallet() {
try {
if (!window.keplr) {
console.log("intall keplr!");
} else {
await setupKeplr(setSecretjs, setSecretAddress);
localStorage.setItem("keplrAutoConnect", "true");
console.log(secretAddress);
}
} catch (error) {
alert(
"An error occurred while connecting to the wallet. Please try again."
);
}
}
function disconnectWallet() {
// reset secretjs and secretAddress
setSecretAddress("");
setSecretjs(null);
// disable auto connect
localStorage.setItem("keplrAutoConnect", "false");
// console.log for success
console.log("Wallet disconnected!");
}
return (
<SecretjsContext.Provider
value={{
secretjs,
setSecretjs,
secretAddress,
setSecretAddress,
connectWallet,
disconnectWallet,
}}
>
{children}
</SecretjsContext.Provider>
);
};
export { SecretjsContext, SecretjsContextProvider };
Copy this to SecretjsFunctions.js.
Replace with your contract address and code hash. You can leave values as are if you did not do the deployment step, make sure to uncomment values for mainnet.
import { useContext } from "react";
import { SecretjsContext } from "./SecretjsContext";
// Uncomment for mainnet metadata
// let contractCodeHash = "db17efceec7a8d2c464af53e142dda38de5ea0665b5c548928d5243b21a624b4";
// let contractAddress = "secret18u22df5dan6cyl6xuyjn9wsa3gauf53567ej90";
let contractCodeHash =
"db17efceec7a8d2c464af53e142dda38de5ea0665b5c548928d5243b21a624b4";
let contractAddress = "secret14k7awjkw8ykllsx8uvq0dfc6h57afrzudunhah";
const SecretjsFunctions = () => {
const { secretjs, secretAddress } = useContext(SecretjsContext);
let verify_proof = async (proof) => {
let tx = await secretjs.tx.compute.executeContract(
{
sender: secretAddress,
contract_address: contractAddress,
msg: {
verify_proof: {
proof: proof,
},
},
code_hash: contractCodeHash,
},
{ gasLimit: 100_000 }
);
console.log(tx);
};
return {
verify_proof,
};
};
export { SecretjsFunctions };
Wrap the App with the specified Secret context (index.js).
import { SecretjsContextProvider } from "./secretJs/SecretjsContext";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<SecretjsContextProvider>
<App />
</SecretjsContextProvider>
);
Create a VerifyProof component.
src/
|- secretJs/SecretjsContext.js
|- secretJs/SecretjsFunctions.js
|- VerifyProof.jsx
|- App.js
Include the VerifyProof() function.
import { useState, useEffect } from "react";
import { Reclaim } from "@reclaimprotocol/js-sdk";
import { SecretjsFunctions } from "./secretJs/SecretjsFunctions";
export default function VerifyProof(props) {
const [proof, setProof] = useState({});
const [verified, setVerified] = useState(false);
const { verify_proof } = SecretjsFunctions();
useEffect(() => {
const newProof = Reclaim.transformForOnchain(props.proof);
setProof(newProof);
}, []);
return (
<div>
<button
className="button"
onClick={async () => {
try {
await verify_proof(proof);
} catch (e) {
console.error(e);
} finally {
setVerified(true);
}
}}
>
Verify Proof
</button>
{verified && <p> Proof verified </p>}
<style jsx="true">{`
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.button {
border: solid 1px #ccc;
margin: 0 0 20px;
border-radius: 3px;
}
`}</style>
</div>
);
}
Create a ConnectButton component.
|- secretJs/SecretjsContext.js
|- secretJs/SecretjsFunctions.js
|- VerifyProof.jsx
|- ConnectButton.jsx
|- App.js
Add a button and placeholder for connecting.
import { useContext } from "react";
import { SecretjsContext } from "./secretJs/SecretjsContext";
export default function ConnectButton() {
const { secretAddress, connectWallet } = useContext(SecretjsContext);
return (
<div>
<div>
<button className="button" onClick={connectWallet}>
Connect Keplr
</button>
</div>
<h2>
{secretAddress
? secretAddress.slice(0, 10) + "...." + secretAddress.slice(41, 45)
: "Please connect wallet"}
</h2>
</div>
);
}
Bringing it all together (App.js).
We will submit the proof on chain once we get the success
callback. New lines are highlighted.
import "./App.css";
import { Reclaim } from "@reclaimprotocol/js-sdk";
import { useState } from "react";
import QRCode from "react-qr-code";
import VerifyProof from "./VerifyProof";
import ConnectButton from "./ConnectButton";
function App() {
const [url, setUrl] = useState("");
const [ready, setReady] = useState(false);
const [proof, setProof] = useState({});
const reclaimClient = new Reclaim.ProofRequest("YOUR_APPLICATION_ID_HERE"); //TODO: replace with your applicationId
async function generateVerificationRequest() {
const providerId = "PROVIDER_ID"; //TODO: replace with your provider ids you had selected while creating the application
reclaimClient.addContext(
`user's address`,
"for acmecorp.com on 1st january"
);
await reclaimClient.buildProofRequest(providerId);
reclaimClient.setSignature(
await reclaimClient.generateSignature(
APP_SECRET //TODO : replace with your APP_SECRET
)
);
const { requestUrl, statusUrl } =
await reclaimClient.createVerificationRequest();
setUrl(requestUrl);
await reclaimClient.startSession({
onSuccessCallback: (proofs) => {
console.log("Verification success", proofs);
setReady(true);
setProof(proofs[0]);
// Your business logic here
},
onFailureCallback: (error) => {
console.error("Verification failed", error);
// Your business logic here to handle the error
},
});
}
return (
<div className="App">
<ConnectButton />
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "50vh",
}}
>
{!url && (
<button onClick={generateVerificationRequest}>
Create Claim QrCode
</button>
)}
{url && <QRCode value={url} />}
</div>
{ready && <VerifyProof proof={proof}></VerifyProof>}
</div>
);
}
export default App;
Submitting the proof
npm run build
npm run start
After requesting a proof from Reclaim and performing the verification on your end, a verify proof button will appear on the screen. Make sure your Keplr is connected, click the button, a wallet pop-up will show prompting you to submit.
Now your proof will get approved on-chain, here is the sample transaction (opens in a new tab) from the screenshot above.