This content originally appeared on DEV Community and was authored by Ankit Sharma
A) Environment Setup:
Install Rust, using command:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Install the Soroban CLI using below mentioned command.
cargo install --locked soroban-cli
For more info visit => Soroban docs
Install Node.js
Get the Freighter Wallet extension for you browser. Once enabled, then got to the network section and connect your wallet to the testnet.
Install wasm32-unknown-unknown package using command:
rustup target add wasm32-unknown-unknown
To configure your CLI to interact with Testnet, run the following command:
soroban network add \
--global testnet \
--rpc-url https://soroban-testnet.stellar.org:443 \
--network-passphrase "Test SDF Network ; September 2015"
In order to deploy the smartcontract you will need an account. You can either use the an account from theFreighter Wallet
or can configure an account named alice
in the testnet using the command:
soroban keys generate --global alice --network testnet
You can see the public key of account alice
:soroban keys address alice
B) Backend (Smart-contract) Setup:
Code for your freighter wallet is as follow :
import {
requestAccess,
signTransaction,
setAllowed,
} from "@stellar/freighter-api";
async function checkConnection() {
const isAllowed = await setAllowed();
if (isAllowed) {
return isAllowed;
}
}
const retrievePublicKey = async () => {
let publicKey = "";
let error = "";
try {
publicKey = await requestAccess();
} catch (e) {
error = e;
}
if (error) {
return error;
}
return publicKey;
};
const userSignTransaction = async (xdr, network, signWith) => {
let signedTransaction = "";
let error = "";
try {
signedTransaction = await signTransaction(xdr, {
network,
accountToSign: signWith,
});
} catch (e) {
error = e;
}
if (error) {
return error;
}
return signedTransaction;
};
export { checkConnection, retrievePublicKey, userSignTransaction };
Smart-contract folder Structure:
smart-contract
├── Cargo.lock
├── Cargo.toml
├── README.md
└── contracts
└── Gate-Pass-Dapp
├── Cargo.toml
└── src
└── lib.rs
=> Go inside the /smart-contract
directory and do the below mentioned steps:
Build the contract:
soroban contract build
Alternte command:
cargo build --target wasm32-unknown-unknown --release
Install Optimizer:
cargo install --locked soroban-cli --features opt
Build an Opmize the contract:
soroban contract optimize --wasm target/wasm32-unknown-unknown/release/Gate_Pass_Dapp.wasm
Steps to the Deploy smart-contract on testnet:
Get the hash of the Wasm bytes, like
4bb69f6355400b2954f9537ec55cd24c4a0a3021eae95758a088b383587657cb
using this command:
soroban contract install --source-account bhupendra --wasm target/wasm32-unknown-unknown/release/Gate_Pass_Dapp.wasm --rpc-url https://soroban-testnet.stellar.org:443 --network-passphrase "Test SDF Network ; September 2015"
By using that Wasm hash, deploy the smartcontract on the testnet and get deployed address of the smartcontract using the following command:
soroban contract deploy \
--wasm-hash 4bb69f6355400b2954f9537ec55cd24c4a0a3021eae95758a088b383587657cb \
--source alice \
--network testnet
Deployed address of this smartcontract:
CBPSRM3TVRYA6PT7ESIXC64QZDTKIQNSKBYJ4CED64CN2OITETB67X2P
*NOTE: If you get the XDR Error error: xdr processing error: xdr value invalid, then follow this article.
The code for Header.js file is as follow:-
import React, { useEffect, useState } from "react";
import { checkConnection, retrievePublicKey } from "./Freighter";
import StellarLogo from "../assets/stellarlogo.png"
const Header = ({setPubKey}) => {
const [connect, getConnected] = useState("Connect");
const [publickey, getPublicKey] = useState("");
const [open, setOpen] = useState(false);
const handleOpenMenu = () => setOpen(!open);
useEffect(() => {
if (publickey !== "") {
getConnected("Connected!");
setPubKey(publickey);
}
}, [publickey]);
const connectWallet = async () => {
if (await checkConnection()) {
getPublicKey(await retrievePublicKey());
}
};
return (
<div className="bg-black flex md:flex-row shadow-md justify-between items-center px-10 py-4">
<div
className="text-2xl sm:text-3xl lg:text-3xl font-semibold text-white flex items-center gap-5"
>
{/* <img src={StellarLogo} alt="CratePass X Stellar" className="w-11" /> */}
<span className="text-white">Product Authenticator</span>
</div>
<div
onClick={() => handleOpenMenu()}
className="text-4xl absolute top-4 right-3 md:hidden cursor-pointer text-white"
>
<ion-icon name={open ? "close" : "menu"}></ion-icon>
</div>
<div>
<ul
className={`${
open ? "top-20 left-0" : "top-[-496px]"
} flex flex-col md:flex-row md:justify-around items-center text-nowrap md:pb-0 py-3 absolute md:static bg-black md:bg-transparent gap-5 w-full md:w-auto pl-3 md:border-none border-2 border-white rounded-b-2xl transition-all duration-500 ease-in-out z-10`}
>
<li>
<div className="p-1 bg-white border-2 max-w-max rounded-md">
<span className="p-1 px-2 bg-black text-white h-full rounded-md">
Address
</span>
<span className="px-2 text-black">
{`${publickey.substring(0, 4)} ${
publickey && "..."
} ${publickey.substring(publickey.length - 4)}`}
</span>
</div>
</li>
<li>
<button
className="text-xl w-52 hover:bg-gray-800 bg-white rounded-md p-4 font-bold text-black border-4 border-white"
onClick={connectWallet}
>
{connect}
</button>
</li>
</ul>
</div>
</div>
);
};
export default Header;
The Soroban file code as follow(without function):-
(1)
import {
Contract,
SorobanRpc,
TransactionBuilder,
Networks,
BASE_FEE,
nativeToScVal,
Address,
} from "@stellar/stellar-sdk";
import { userSignTransaction } from "../Freighter";
let rpcUrl = "https://soroban-testnet.stellar.org";
let contractAddress =
"CDHHT6IKGRCFPSYJNKR2JEC3GUHRXGPIDUE2JN4LQLZMTCHYKLB4KZ7D";
const accountToScVal = (account) => new Address(account).toScVal();
const stringToScValString = (value) => {
return nativeToScVal(value);
};
const numberToU64 = (value) => {
return nativeToScVal(value, { type: "u64" });
};
let params = {
fee: BASE_FEE,
networkPassphrase: Networks.TESTNET,
};
async function contractInt(caller, functName, values) {
const provider = new SorobanRpc.Server(rpcUrl, { allowHttp: true });
const sourceAccount = await provider.getAccount(caller);
const contract = new Contract(contractAddress);
let buildTx;
if (values == null) {
buildTx = new TransactionBuilder(sourceAccount, params)
.addOperation(contract.call(functName))
.setTimeout(30)
.build();
} else if (Array.isArray(values)) {
buildTx = new TransactionBuilder(sourceAccount, params)
.addOperation(contract.call(functName, ...values))
.setTimeout(30)
.build();
} else {
buildTx = new TransactionBuilder(sourceAccount, params)
.addOperation(contract.call(functName, values))
.setTimeout(30)
.build();
}
let _buildTx = await provider.prepareTransaction(buildTx);
let prepareTx = _buildTx.toXDR();
let signedTx = await userSignTransaction(prepareTx, "TESTNET", caller);
let tx = TransactionBuilder.fromXDR(signedTx, Networks.TESTNET);
try {
let sendTx = await provider.sendTransaction(tx).catch(function (err) {
console.error("Catch-1", err);
return err;
});
if (sendTx.errorResult) {
throw new Error("Unable to submit transaction");
}
if (sendTx.status === "PENDING") {
let txResponse = await provider.getTransaction(sendTx.hash);
while (txResponse.status === "NOT_FOUND") {
txResponse = await provider.getTransaction(sendTx.hash);
await new Promise((resolve) => setTimeout(resolve, 100));
}
if (txResponse.status === "SUCCESS") {
let result = txResponse.returnValue;
return result;
}
}
} catch (err) {
console.log("Catch-2", err);
return;
}
}
.....Integrate it with upcoming code .
Invoke functions from the smart-contract:
To invoke any of the function from the smartcontract you can use this command fromat.
soroban contract invoke \
--id <DEPLOYED_CONTRACT_ADDRESS> \
--source <YOUR_ACCOUNT_NAME> \
--network testnet \
-- \
<FUNCTION_NAME> --<FUNCTION_PARAMETER> <ARGUMENT>
For example:
To status of all registered passes, invoke view_all_pass_status function.
soroban contract invoke \
--id CBPSRM3TVRYA6PT7ESIXC64QZDTKIQNSKBYJ4CED64CN2OITETB67X2P \
--source alice \
--network testnet \
-- \
view_all_pass_status
To create a new pass, invoke create_pass function:
soroban contract invoke \
--id CBPSRM3TVRYA6PT7ESIXC64QZDTKIQNSKBYJ4CED64CN2OITETB67X2P \
--source alice \
--network testnet \
-- \
create_pass --record_id <YOUR_PUBLIC_ADDRESS> --title "Going Home" --descrip "I am going to my home today."
Soroban file code with function:-
(95)
async function createPass(caller, title, descrip) {
let titleScVal = stringToScValString(title);
let descripScVal = stringToScValString(descrip);
let values = [titleScVal, descripScVal];
try {
const passId = await contractInt(caller, "create_pass", values);
let resolvedPassId = Number(passId?._value?._value);
console.log(resolvedPassId);
return resolvedPassId;
} catch (error) {
console.log("Pass not created. Check if you already have a active pass");
}
}
async function approvePass(caller, pass_id) {
let values = numberToU64(pass_id);
try {
await contractInt(caller, "approve_pass", values);
console.log(`!!Pass ID - ${pass_id}, is now Arrpoved!!`);
} catch (error) {
console.log("Pass can't be approved!!");
}
}
async function expirePass(caller, pass_id) {
let values = numberToU64(pass_id);
try {
await contractInt(caller, "expire_pass", values);
console.log(`!!Pass ID - ${pass_id}, is now expired!!`);
} catch (error) {
console.log("Pass can't be expired!!");
}
}
async function fetchAllPassStatus(caller) {
try {
let result = await contractInt(caller, "view_all_pass_status", null);
let approvedVal = Number(result?._value[0]?._attributes?.val?._value);
let expiredVal = Number(result?._value[1]?._attributes?.val?._value);
let pendingVal = Number(result?._value[2]?._attributes?.val?._value);
let totalVal = Number(result?._value[3]?._attributes?.val?._value);
console.log(approvedVal, expiredVal, pendingVal, totalVal);
let ansArr = [];
ansArr.push(approvedVal);
ansArr.push(expiredVal);
ansArr.push(pendingVal);
ansArr.push(totalVal);
return ansArr;
} catch (error) {
console.log("Unable to fetch All Pass Status!!");
}
}
async function fetchMyPassStatus(caller, pass_id) {
let values = numberToU64(pass_id);
let result1;
let result2;
try {
result1 = await contractInt(caller, "view_my_pass", values);
} catch (error) {
console.log("Unable to fetch Your Pass Status!!");
}
try {
result2 = await contractInt(caller, "view_ac_pass_by_unique_id", values);
} catch (error) {
console.log("Unable to fetch Your Pass Status!!");
}
let createdTimeVal = Number(result1?._value[0]?._attributes?.val?._value);
console.log(createdTimeVal);
let descripVal = result1?._value[1]?._attributes?.val?._value?.toString();
console.log(descripVal);
let inTimeVal = Number(result1?._value[2]?._attributes?.val?._value);
console.log(inTimeVal);
let isExpiredVal = result1?._value[3]?._attributes?.val?._value;
console.log(isExpiredVal);
let titleVal = result1?._value[4]?._attributes?.val?._value?.toString();
console.log(titleVal);
let passIdVal = Number(result1?._value[5]?._attributes?.val?._value);
console.log(passIdVal);
let approvalStatusVal = result2?._value[1]?._attributes?.val?._value;
console.log(approvalStatusVal);
let outTimeVal = Number(result2?._value[2]?._attributes?.val?._value);
console.log(outTimeVal);
let ansArr = [];
ansArr.push(createdTimeVal);
ansArr.push(descripVal);
ansArr.push(inTimeVal);
ansArr.push(isExpiredVal);
ansArr.push(titleVal);
ansArr.push(passIdVal);
ansArr.push(approvalStatusVal);
ansArr.push(outTimeVal);
return ansArr;
}
export {
createPass,
approvePass,
expirePass,
fetchAllPassStatus,
fetchMyPassStatus,
};
The App.js file is as follow;-
import "./App.css";
import Header from "./components/Header";
import Admin from "./components/Admin/Admin";
import RegularUser from "./components/RegularUser/RegularUser";
import { createContext, useState } from "react";
import { ADMIN_KEY } from "./constants/constants";
const pubKeyData = createContext();
const passIdContext = createContext();
function App() {
const [pubkey, _setPubKey] = useState("");
const [passId, _setPassId] = useState();
return (
<div className="App">
<pubKeyData.Provider value={pubkey}>
<Header setPubKey={_setPubKey} />
<passIdContext.Provider value={{passId, _setPassId}}>
<RegularUser />
{pubkey.toString() === ADMIN_KEY && <Admin />}
</passIdContext.Provider>
</pubKeyData.Provider>
</div>
);
}
export default App;
export { pubKeyData, passIdContext };
The more about my code you can reach to my github repo:-
https://github.com/Ankitsharma2023
This content originally appeared on DEV Community and was authored by Ankit Sharma
Ankit Sharma | Sciencx (2024-08-11T12:23:19+00:00) Product authenticator using :Stellar Blockchain Technology. Retrieved from https://www.scien.cx/2024/08/11/product-authenticator-using-stellar-blockchain-technology-3/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.