Prerequisites
You need Web3 libraries configured with Para authentication.- Ethers.js
- Viem
- Wagmi
Read Contract Data
import { ethers } from "ethers";
const CONTRACT_ABI = [
"function balanceOf(address owner) view returns (uint256)",
"function totalSupply() view returns (uint256)",
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)"
];
async function readContractData(
provider: ethers.Provider,
contractAddress: string,
userAddress: string
) {
const contract = new ethers.Contract(
contractAddress,
CONTRACT_ABI,
provider
);
const [balance, totalSupply, name, symbol, decimals] = await Promise.all([
contract.balanceOf(userAddress),
contract.totalSupply(),
contract.name(),
contract.symbol(),
contract.decimals()
]);
return {
balance: ethers.formatUnits(balance, decimals),
totalSupply: ethers.formatUnits(totalSupply, decimals),
name,
symbol,
decimals
};
}
Write Contract Data
const STAKING_ABI = [
"function stake(uint256 amount) payable",
"function unstake(uint256 amount)",
"function getStakedBalance(address user) view returns (uint256)",
"event Staked(address indexed user, uint256 amount)",
"event Unstaked(address indexed user, uint256 amount)"
];
async function stakeTokens(
signer: any,
contractAddress: string,
amount: string,
decimals: number
) {
const contract = new ethers.Contract(contractAddress, STAKING_ABI, signer);
const parsedAmount = ethers.parseUnits(amount, decimals);
const tx = await contract.stake(parsedAmount);
await tx.wait();
const newBalance = await contract.getStakedBalance(await signer.getAddress());
return {
hash: tx.hash,
stakedAmount: ethers.formatUnits(newBalance, decimals)
};
}
Read Contract Data
Read operations don’t need mutation helpers — use the ViempublicClient directly:import { createPublicClient, http, formatUnits } from "viem";
import { sepolia } from "viem/chains";
const CONTRACT_ABI = [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "owner", type: "address" }],
outputs: [{ type: "uint256" }],
},
{
name: "totalSupply",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "uint256" }],
},
{
name: "name",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "string" }],
},
{
name: "symbol",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "string" }],
},
{
name: "decimals",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "uint8" }],
},
] as const;
const publicClient = createPublicClient({
chain: sepolia,
transport: http(),
});
async function readContractData(
contractAddress: `0x${string}`,
userAddress: `0x${string}`
) {
const results = await publicClient.multicall({
contracts: [
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "balanceOf",
args: [userAddress],
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "totalSupply",
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "name",
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "symbol",
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "decimals",
},
],
});
const [balance, totalSupply, name, symbol, decimals] = results.map(
(r) => r.result
);
return {
balance: formatUnits(balance, decimals),
totalSupply: formatUnits(totalSupply, decimals),
name,
symbol,
decimals,
};
}
Write Contract Data
import { useParaViemClient, useParaViemWriteContract } from "@getpara/react-sdk";
import { parseUnits, http } from "viem";
import { sepolia } from "viem/chains";
const STAKING_ABI = [
{
name: "stake",
type: "function",
stateMutability: "payable",
inputs: [{ name: "amount", type: "uint256" }],
outputs: [],
},
] as const;
function StakeTokens({
contractAddress,
decimals = 18,
}: {
contractAddress: `0x${string}`;
decimals?: number;
}) {
const amount = "100";
const { viemClient } = useParaViemClient({
walletClientConfig: { chain: sepolia, transport: http() },
});
const {
writeContractAsync,
isPending,
data: hash,
} = useParaViemWriteContract(viemClient);
return (
<div>
<button
onClick={() =>
writeContract({
address: contractAddress,
abi: STAKING_ABI,
functionName: "stake",
args: [parseUnits(amount, decimals)],
})
}
disabled={isPending}
>
{isPending ? "Staking..." : `Stake ${amount} Tokens`}
</button>
{hash && <p>Transaction: {hash}</p>}
</div>
);
}
Read Contract Data
import { useReadContracts } from "@getpara/react-sdk/wagmi";
import { formatUnits } from "viem";
const CONTRACT_ABI = [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "owner", type: "address" }],
outputs: [{ type: "uint256" }]
},
{
name: "totalSupply",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "uint256" }]
},
{
name: "name",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "string" }]
},
{
name: "symbol",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "string" }]
},
{
name: "decimals",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [{ type: "uint8" }]
}
] as const;
function ContractData({
contractAddress,
userAddress
}: {
contractAddress: `0x${string}`;
userAddress: `0x${string}`;
}) {
const { data, isPending } = useReadContracts({
contracts: [
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "balanceOf",
args: [userAddress]
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "totalSupply"
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "name"
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "symbol"
},
{
address: contractAddress,
abi: CONTRACT_ABI,
functionName: "decimals"
}
]
});
if (isPending) return <div>Loading...</div>;
const [balance, totalSupply, name, symbol, decimals] =
data?.map(d => d.result) || [];
return (
<div>
<p>Token: {name} ({symbol})</p>
<p>Balance: {formatUnits(balance || 0n, decimals || 18)}</p>
<p>Total Supply: {formatUnits(totalSupply || 0n, decimals || 18)}</p>
</div>
);
}
Write Contract Data
import { useWriteContract, useWaitForTransactionReceipt, useReadContract } from "@getpara/react-sdk/wagmi";
import { parseUnits, formatUnits } from "viem";
const STAKING_ABI = [
{
name: "stake",
type: "function",
stateMutability: "payable",
inputs: [{ name: "amount", type: "uint256" }],
outputs: []
},
{
name: "getStakedBalance",
type: "function",
stateMutability: "view",
inputs: [{ name: "user", type: "address" }],
outputs: [{ type: "uint256" }]
}
] as const;
function StakeTokens({
contractAddress,
userAddress,
decimals = 18
}: {
contractAddress: `0x${string}`;
userAddress: `0x${string}`;
decimals?: number;
}) {
const amount = "100";
const { data: hash, isPending, writeContract } = useWriteContract();
const { isPending: isConfirming } = useWaitForTransactionReceipt({ hash });
const { data: stakedBalance } = useReadContract({
address: contractAddress,
abi: STAKING_ABI,
functionName: "getStakedBalance",
args: [userAddress]
});
return (
<div>
<p>Staked: {formatUnits(stakedBalance || 0n, decimals)}</p>
<button
onClick={() => writeContract({
address: contractAddress,
abi: STAKING_ABI,
functionName: "stake",
args: [parseUnits(amount, decimals)]
})}
disabled={isPending || isConfirming}
>
{isPending ? "Staking..." : `Stake ${amount} Tokens`}
</button>
{hash && <p>Transaction: {hash}</p>}
</div>
);
}