Solidity
ReactiveDOT works with Solidity contracts on chains with PolkaVM support, such as Polkadot Asset Hub.
Contract support via PolkaVM on Polkadot is still in development.
If you're new to ReactiveDOT, we strongly recommend starting with the “Getting started guide”. This guide covers essential topics such as connecting to a blockchain, setting up wallets, querying data, and handling errors effectively.
Defining contract
ReactiveDOT can infer types based on ABI and EIP-712 Typed Data definitions (powered by ABIType), giving you full end-to-end type-safety from your contracts to your frontend and incredible developer experience (e.g. autocomplete ABI function names and catch misspellings, strongly-typed ABI function arguments, etc.).
For this to work, define your contract using its ABI, as shown below:
import { contracts } from "@polkadot-api/descriptor";
import { defineContract } from "@reactive-dot/core";
export const myContract = defineContract({
type: "solidity",
abi: [
{
type: "function",
name: "balanceOf",
stateMutability: "view",
inputs: [{ name: "account", type: "address" }],
outputs: [{ type: "uint256" }],
},
{
type: "function",
name: "totalSupply",
stateMutability: "view",
inputs: [],
outputs: [{ name: "supply", type: "uint256" }],
},
],
});
Reading contract data
The useLazyLoadQuery
hook with Query.contract
instruction allows you to read data on a smart contract, from a view
or pure
(read-only) function. They can only read the state of the contract, and cannot make any changes to it.
import { myContract } from "./contracts.ts";
import { useLazyLoadQuery } from "@reactive-dot/react";
function Component() {
const [totalSupply, balance] = useLazyLoadQuery((builder) =>
builder.contract(myContract, CONTRACT_ADDRESS, (builder) =>
builder.func("totalSupply").func("balanceOf", [SOME_ADDRESS]),
),
);
return (
<div>
<p>Total supply: {totalSupply.toLocaleString()}</p>
<p>Balance: {balance.toLocaleString()}</p>
</div>
);
}
Multi query
Similar to chain multi-query, the contracts
& funcs
instructions enable reading multiple pieces of data in parallel.
import { myContract } from "./contracts.ts";
import { useLazyLoadQuery } from "@reactive-dot/react";
function Component() {
const contracts = [CONTRACT_ADDRESS_1, CONTRACT_ADDRESS_2] as const;
const results = useLazyLoadQuery((builder) =>
builder.contracts(myContract, contracts, (builder) =>
builder
.func("totalSupply")
.funcs("balanceOf", [[ACCOUNT_1_ADDRESS], [ACCOUNT_2_ADDRESS]]),
),
);
return (
<div>
{results.map(([totalSupply, balances], index) => (
<div key={index}>
<p>Contract address: {contracts[index]}</p>
<p>Total supply: {totalSupply}</p>
<p>Balances: {balances.join(", ")}</p>
</div>
))}
</div>
);
}
Writing to contract
The useContractMutation
hook allows you to mutate data on a smart contract, from a payable or nonpayable (write) message.
import {
idle,
MutationError,
pending,
defineContract,
} from "@reactive-dot/core";
import { useContractMutation } from "@reactive-dot/react";
const myContract = defineContract({
type: "solidity",
abi: [
{
name: "mint",
type: "function",
stateMutability: "nonpayable",
inputs: [{ internalType: "uint32", name: "tokenId", type: "uint32" }],
outputs: [],
},
],
});
function Component() {
const [status, mint] = useContractMutation((mutate) =>
mutate(myContract, CONTRACT_ADDRESS, "mint", {
args: [1n],
}),
);
return (
<div>
<button onClick={() => mint()}>Mint</button>
{(() => {
switch (status) {
case idle:
return <p>No transaction submitted yet.</p>;
case pending:
return <p>Submitting transaction...</p>;
default:
if (status instanceof MutationError) {
return <p>Error submitting transaction!</p>;
}
return (
<p>
Submitted tx with hash: {status.txHash}, current state:{" "}
{status.type}
</p>
);
}
})()}
</div>
);
}