Write Contract
Writing to a contract is a common operation in a dApp. This guide will show you how to write to a contract using MIDL Protocol. Writing a contract in MIDL requires the BTC transaction to cover the fees and the EVM transaction to interact with the contract. You can read more about it here.
The example below is available in the examples.
1. Setup the project
Follow the Connect Wallet guide to setup the project.
2. Install dependencies
Follow the Installation guide to install the required dependencies.
3. Configure the app
Modify the App.tsx
file to include the WagmiProvider
and WagmiMidlProvider
:
tsx
import { MidlProvider } from "@midl-xyz/midl-js-react";
import { QueryClientProvider } from "@tanstack/react-query";
import { WagmiMidlProvider } from "@midl-xyz/midl-js-executor-react";
import { WagmiProvider } from "wagmi";
import { queryClient } from "./query-client";
import { midlConfig } from "./midlConfig";
import { wagmiConfig } from "./wagmiConfig";
function App() {
return (
<WagmiProvider config={wagmiConfig}>
<MidlProvider config={midlConfig}>
<QueryClientProvider client={queryClient}>
<WagmiMidlProvider />
<YourApp />
</QueryClientProvider>
</MidlProvider>
</WagmiProvider>
);
}
ts
import { midlRegtest } from "@midl-xyz/midl-js-executor";
import type { Chain } from "viem";
import { createConfig, http } from "wagmi";
export const wagmiConfig = createConfig({
chains: [
{
...midlRegtest,
rpcUrls: {
default: {
http: [midlRegtest.rpcUrls.default.http[0]],
},
},
} as Chain,
],
transports: {
[midlRegtest.id]: http(midlRegtest.rpcUrls.default.http[0]),
},
});
4. Add WriteContract component
tsx
import {
useAddTxIntention,
useFinalizeTxIntentions,
} from "@midl-xyz/midl-js-executor-react";
import {
useBroadcastTransaction,
useWaitForTransaction,
} from "@midl-xyz/midl-js-react";
import { encodeFunctionData } from "viem";
import { useReadContract, useWalletClient } from "wagmi";
import { SimpleStorage } from "./SimpleStorage";
export function WriteContract() {
const { addTxIntention, txIntentions } = useAddTxIntention();
const { finalizeBTCTransaction, btcTransaction, signIntentionAsync } =
useFinalizeTxIntentions();
const { broadcastTransaction } = useBroadcastTransaction();
const { data: walletClient } = useWalletClient();
const { waitForTransaction } = useWaitForTransaction({
mutation: {
onSuccess: () => {
refetch();
},
},
});
const { data: message, refetch } = useReadContract({
abi: SimpleStorage.abi,
functionName: "getMessage",
address: SimpleStorage.address as `0x${string}`,
});
const onAddIntention = () => {
addTxIntention({
reset: true,
intention: {
evmTransaction: {
to: SimpleStorage.address as `0x${string}`,
data: encodeFunctionData({
abi: SimpleStorage.abi,
functionName: "setMessage",
args: [`Updated message at ${new Date().toISOString()}`],
}),
},
},
});
};
const onFinalizeBTCTransaction = () => {
finalizeBTCTransaction({
feeRateMultiplier: 4,
});
};
const onSignIntentions = async () => {
if (!btcTransaction) {
alert("Please finalize BTC transaction first");
return;
}
for (const intention of txIntentions) {
await signIntentionAsync({
intention,
txId: btcTransaction.tx.id,
});
}
};
const onBroadcast = async () => {
if (!btcTransaction) {
alert("Please finalize BTC transaction first");
return;
}
broadcastTransaction({ tx: btcTransaction.tx.hex });
console.log(`BTC Transaction sent: ${btcTransaction.tx.id}`);
for (const intention of txIntentions) {
const tx = await walletClient?.sendRawTransaction({
serializedTransaction: intention.signedEvmTransaction as `0x${string}`,
});
console.log(`Transaction sent: ${tx}`);
}
waitForTransaction({ txId: btcTransaction.tx.id });
};
return (
<div>
<h2>Current message:</h2>
<div>{message as string}</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
maxWidth: "300px",
margin: "3rem auto",
}}
>
<div>
<h3>1. Add Transaction intention</h3>
<button
type="button"
onClick={onAddIntention}
disabled={txIntentions.length > 0}
>
Add Intention
</button>
</div>
<div>
<h3> 2. Calculate transaction costs and form BTC Tx</h3>
<button type="button" onClick={onFinalizeBTCTransaction}>
Finalize BTC Transaction
</button>
</div>
<div>
<h3>3. Sign transaction intentions</h3>
<button type="button" onClick={onSignIntentions}>
Sign Intentions
</button>
</div>
<div>
<h3>4. Publish transactions </h3>
<button type="button" onClick={onBroadcast}>
Broadcast transactions
</button>
</div>
</div>
<h4>Tx Intentions</h4>
<pre
style={{
wordBreak: "break-all",
whiteSpace: "pre-wrap",
textAlign: "left",
}}
>
{JSON.stringify(txIntentions, null, 2)}
</pre>
</div>
);
}
tsx
export const SimpleStorage = {
address: "0x015bceEFA137a662aFC0347Cb6fc204192960094",
abi: [
{
inputs: [
{
internalType: "string",
name: "initialMessage",
type: "string",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "string",
name: "newMessage",
type: "string",
},
],
name: "MessageUpdated",
type: "event",
},
{
inputs: [],
name: "getMessage",
outputs: [
{
internalType: "string",
name: "",
type: "string",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "string",
name: "newMessage",
type: "string",
},
],
name: "setMessage",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
],
};
5. Put it all together
Add the WriteContract
component to your app:
tsx
import { useAccounts } from "@midl-xyz/midl-js-react";
import { ConnectWallet } from "./ConnectWallet";
import { ConnectedAccounts } from "./ConnectedAccounts";
import { WriteContract } from "./WriteContract";
export function YourApp() {
const { isConnected } = useAccounts();
return (
<div>
{!isConnected && <ConnectWallet />}
{isConnected && (
<>
<ConnectedAccounts />
<WriteContract />
</>
)}
</div>
);
}