Skip to content

Commit d826055

Browse files
committed
fix: transactions working for asset hub with legacy fee estimation
1 parent a7a85f8 commit d826055

File tree

6 files changed

+318
-123
lines changed

6 files changed

+318
-123
lines changed

quick-starts/react-quick-start/src/components/ContractData.tsx

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,106 +3,134 @@ import { useReadContracts } from "wagmi";
33
import { Mint } from "./Mint";
44

55
export function ContractData(params: {
6-
contractAddress: `0x${string}`,
7-
userAddresses?: readonly `0x${string}`[],
6+
contractAddress: `0x${string}`;
7+
userAddresses?: readonly `0x${string}`[];
88
}) {
99
const userAddressesSet = new Set(params.userAddresses);
1010

1111
const myTokenContract = {
1212
address: params.contractAddress,
13-
abi: myTokenModuleMyTokenAbi
13+
abi: myTokenModuleMyTokenAbi,
1414
} as const;
1515

1616
const contractData = useReadContracts({
17-
contracts: [{
18-
...myTokenContract,
19-
functionName: "minter"
20-
}, {
21-
...myTokenContract,
22-
functionName: "totalSupply"
23-
}, {
24-
...myTokenContract,
25-
functionName: "symbol"
26-
}, {
27-
...myTokenContract,
28-
functionName: "decimals"
29-
}, ...(params.userAddresses ?? []).map(addr => ({
30-
...myTokenContract,
31-
abi: myTokenModuleMyTokenAbi,
32-
functionName: "balanceOf",
33-
args: [addr]
34-
}))
35-
]
17+
contracts: [
18+
{
19+
...myTokenContract,
20+
functionName: "minter",
21+
},
22+
{
23+
...myTokenContract,
24+
functionName: "totalSupply",
25+
},
26+
{
27+
...myTokenContract,
28+
functionName: "symbol",
29+
},
30+
{
31+
...myTokenContract,
32+
functionName: "decimals",
33+
},
34+
...(params.userAddresses ?? []).map((addr) => ({
35+
...myTokenContract,
36+
abi: myTokenModuleMyTokenAbi,
37+
functionName: "balanceOf",
38+
args: [addr],
39+
})),
40+
],
3641
});
3742

3843
let error: string | null = null;
3944

4045
if (contractData.error !== null) {
4146
error = contractData.error.toString();
4247
} else {
43-
error = contractData.data?.find(el => el.error !== undefined)?.toString() || null;
48+
error =
49+
contractData.data?.find((el) => el.error !== undefined)?.toString() ||
50+
null;
4451
}
4552

4653
if (error !== null) {
4754
return (
4855
<p>
49-
Loading contract data for <span className="font-bold">{params.contractAddress}</span> failed!<br />
56+
Loading contract data for{" "}
57+
<span className="font-bold">{params.contractAddress}</span> failed!
58+
<br />
5059
<code style={{ whiteSpace: "pre-wrap" }}>{error}</code>
5160
</p>
5261
);
5362
}
5463

55-
if (contractData.isLoading || contractData?.data === undefined || contractData.data.some(el => el === undefined)) {
64+
if (
65+
contractData.isLoading ||
66+
contractData?.data === undefined ||
67+
contractData.data.some((el) => el === undefined)
68+
) {
5669
return (
5770
<p>
58-
Loading contract data for <span className="font-bold">{params.contractAddress}</span>...
71+
Loading contract data for{" "}
72+
<span className="font-bold">{params.contractAddress}</span>...
5973
</p>
6074
);
6175
}
6276

6377
const owner = contractData.data[0].result as `0x${string}`;
64-
const isOwner = owner && (userAddressesSet.has(owner));
78+
const isOwner = owner && userAddressesSet.has(owner);
6579
const totalSupply = contractData.data[1].result as bigint;
6680
const tokenName = contractData.data[2].result as string;
6781
const decimals = contractData.data[3].result as number;
68-
const balances = contractData.data.slice(4).map(el => el.result as bigint);
82+
const balances = contractData.data.slice(4).map((el) => el.result as bigint);
6983

70-
const formatMoney = (amount: bigint): string => (
71-
String(Number(amount / 10n ** (BigInt(decimals) - 3n)) / 1000)
72-
+ " "
73-
+ tokenName
74-
);
84+
const formatMoney = (amount: bigint): string =>
85+
String(Number(amount / 10n ** (BigInt(decimals) - 3n)) / 1000) +
86+
" " +
87+
tokenName;
7588

7689
return (
7790
<>
7891
<p>
79-
Smart contract address: <span className="font-bold">{params.contractAddress}</span>
92+
Smart contract address:{" "}
93+
<span className="font-bold">{params.contractAddress}</span>
8094
</p>
8195
<p>
8296
Smart contract owner: <span className="font-bold">{owner}</span>
83-
{isOwner && (<> (that's you!!)</>)}
97+
{isOwner && <> (that's you!!)</>}
8498
</p>
8599
<p>
86-
Total supply: <span className="font-bold">{formatMoney(totalSupply)}</span>
100+
Total supply:{" "}
101+
<span className="font-bold">{formatMoney(totalSupply)}</span>
87102
</p>
88103

89-
{(params.userAddresses && params.userAddresses.length > 0) && (
104+
{params.userAddresses && params.userAddresses.length > 0 && (
90105
<div className="border rounded-md my-5 p-2 w-full align-top">
91106
<h3 className="font-bold text-lg">Balances</h3>
92107
<div className="w-full grid grid-cols-2">
93-
{balances.map((val, index) => [
94-
<div key={index.toString() + "_addr"} className="text-left">{params.userAddresses![index]}</div>,
95-
<div key={index.toString() + "_value"} className="text-right">{formatMoney(val)}</div>
96-
]).flat()}
108+
{balances
109+
.map((val, index) => [
110+
<div key={index.toString() + "_addr"} className="text-left">
111+
{params.userAddresses![index]}
112+
</div>,
113+
<div key={index.toString() + "_value"} className="text-right">
114+
{formatMoney(val)}
115+
</div>,
116+
])
117+
.flat()}
97118
</div>
98119
</div>
99120
)}
100121

101-
<Mint contractAddress={params.contractAddress} ownerAddress={owner} decimals={decimals} symbol={tokenName} />
122+
<Mint
123+
contractAddress={params.contractAddress}
124+
ownerAddress={owner}
125+
isOwner={Boolean(isOwner)}
126+
decimals={decimals}
127+
symbol={tokenName}
128+
/>
102129

103130
{!isOwner && (
104-
<p style={{ color: 'orange', fontSize: '14px', marginTop: '8px' }}>
105-
Note: You are not the contract owner. Minting may fail unless you have the MINTER_ROLE.
131+
<p style={{ color: "orange", fontSize: "14px", marginTop: "8px" }}>
132+
Note: You are not the contract owner. Minting may fail unless you have
133+
the MINTER_ROLE.
106134
</p>
107135
)}
108136
</>
Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import { myTokenModuleMyTokenAbi } from "../generated";
2-
import { useWriteContract, useAccount } from "wagmi";
2+
import {
3+
useWriteContract,
4+
useAccount,
5+
usePublicClient,
6+
useChainId,
7+
} from "wagmi";
38

49
import { useState, useEffect } from "react";
510

611
export function Mint(params: {
7-
contractAddress: `0x${string}`,
8-
ownerAddress: `0x${string}`,
9-
decimals: number,
10-
symbol: string,
12+
contractAddress: `0x${string}`;
13+
ownerAddress: `0x${string}`;
14+
isOwner: boolean;
15+
decimals: number;
16+
symbol: string;
1117
}) {
1218
const { address: userAddress } = useAccount();
19+
const publicClient = usePublicClient();
20+
const chainId = useChainId();
1321
const [amount, setAmount] = useState(0);
14-
const [address, setAddress] = useState<`0x${string}`>(userAddress || "0x932217f9faf715808c1f76eA9EeAb7026806C963");
22+
const [address, setAddress] = useState<`0x${string}`>(
23+
userAddress || "0x932217f9faf715808c1f76eA9EeAb7026806C963"
24+
);
1525

1626
useEffect(() => {
1727
if (userAddress) {
@@ -23,9 +33,13 @@ export function Mint(params: {
2333

2434
return (
2535
<div className="border rounded-md my-5 mx-2 p-2 w-fit inline-block">
26-
<h3 className="px-2 block mb-2 font-bold text-lg">Mint {params.symbol}s</h3>
36+
<h3 className="px-2 block mb-2 font-bold text-lg">
37+
Mint {params.symbol}s
38+
</h3>
2739
<div className="text-right my-2">
28-
<label htmlFor="address" className="px-2 block mb-2 inline-block">Address</label>
40+
<label htmlFor="address" className="px-2 block mb-2 inline-block">
41+
Address
42+
</label>
2943
<input
3044
id="address"
3145
value={address}
@@ -35,10 +49,13 @@ export function Mint(params: {
3549
className="
3650
border rounded-md padding-1 pl-2 h-10 w-400
3751
focus:ring-2 focus:ring-inset focus:ring-indigo-600
38-
" />
52+
"
53+
/>
3954
</div>
4055
<div className="text-right my-2">
41-
<label htmlFor="amount" className="px-2 block mb-2 inline-block">Amount</label>
56+
<label htmlFor="amount" className="px-2 block mb-2 inline-block">
57+
Amount
58+
</label>
4259
<input
4360
id="amount"
4461
type="number"
@@ -48,35 +65,98 @@ export function Mint(params: {
4865
className="
4966
border rounded-md padding-1 pl-2 h-10 w-400
5067
focus:ring-2 focus:ring-inset focus:ring-indigo-600
51-
" />
68+
"
69+
/>
5270
</div>
5371

54-
<button onClick={() => writeContract({
55-
address: params.contractAddress,
56-
abi: myTokenModuleMyTokenAbi,
57-
functionName: "mint",
58-
args: [address, BigInt(amount) * (10n ** BigInt(params.decimals))]
59-
})} disabled={status === "pending" || amount <= 0} className="
72+
<button
73+
onClick={async () => {
74+
if (!userAddress) return;
75+
try {
76+
const value = BigInt(amount) * 10n ** BigInt(params.decimals);
77+
// Precompute fee and limits to avoid wallet estimation issues
78+
const [gasPrice, nonce, gas] = await Promise.all([
79+
publicClient?.getGasPrice().catch(() => undefined),
80+
publicClient
81+
?.getTransactionCount({ address: userAddress })
82+
.catch(() => undefined),
83+
publicClient
84+
?.estimateGas({
85+
account: userAddress,
86+
to: params.contractAddress,
87+
data: await (async () => {
88+
// Encode calldata for the mint(address,uint256)
89+
const selector = "0x40c10f19";
90+
const pad = (s: string) =>
91+
s.replace(/^0x/, "").padStart(64, "0");
92+
const calldata =
93+
selector + pad(address) + pad(value.toString(16));
94+
return calldata as `0x${string}`;
95+
})(),
96+
})
97+
.catch(() => undefined),
98+
]);
99+
100+
writeContract({
101+
chainId,
102+
address: params.contractAddress,
103+
abi: myTokenModuleMyTokenAbi,
104+
functionName: "mint",
105+
args: [address, value],
106+
// Hint wagmi/viem for legacy by setting gasPrice
107+
...(gasPrice ? { gasPrice, type: "legacy" as const } : {}),
108+
...(gas ? { gas } : {}),
109+
...(nonce !== undefined ? { nonce } : {}),
110+
account: userAddress,
111+
});
112+
} catch (e) {
113+
console.error(e);
114+
}
115+
}}
116+
disabled={status === "pending" || amount <= 0 || !params.isOwner}
117+
className="
60118
my-0 mx-3 h-10 py-0
61119
focus:ring-2 focus:ring-inset focus:ring-indigo-600
62-
">Mint {
63-
status === "pending" ? "⏳"
64-
: status === "success" ? "✅"
65-
: status === "error" ? "❌" : ""
66-
}
120+
">
121+
Mint{" "}
122+
{status === "pending"
123+
? "⏳"
124+
: status === "success"
125+
? "✅"
126+
: status === "error"
127+
? "❌"
128+
: ""}
67129
</button>
68130

69131
{status === "error" && error && (
70-
<div style={{ color: 'red', fontSize: '14px', marginTop: '8px', padding: '8px' }}>
132+
<div
133+
style={{
134+
color: "red",
135+
fontSize: "14px",
136+
marginTop: "8px",
137+
padding: "8px",
138+
}}>
71139
Error: {error.message}
72140
</div>
73141
)}
74142

75143
{status === "success" && data && (
76-
<div style={{ color: 'green', fontSize: '14px', marginTop: '8px', padding: '8px' }}>
144+
<div
145+
style={{
146+
color: "green",
147+
fontSize: "14px",
148+
marginTop: "8px",
149+
padding: "8px",
150+
}}>
77151
Transaction successful! Hash: {data}
78152
</div>
79153
)}
154+
155+
{!params.isOwner && (
156+
<div style={{ color: "orange", fontSize: "12px", marginTop: "6px" }}>
157+
You are not the owner/minter. Mint may revert on-chain.
158+
</div>
159+
)}
80160
</div>
81161
);
82162
}

quick-starts/react-quick-start/src/components/getBalance.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function Balance() {
77
const chainId = useChainId();
88

99
const { data, isLoading, error } = useBalance({ address });
10-
console.log("user data: ", data);
10+
// console.log("user data: ", data);
1111

1212
// Get decimals from the current chain's native currency
1313
const getNetworkDecimals = (chainId: number) => {
@@ -23,7 +23,7 @@ export function Balance() {
2323
}
2424
};
2525

26-
const networkDecimals = getNetworkDecimals(chainId);
26+
const networkDecimals = data?.decimals ?? getNetworkDecimals(chainId);
2727

2828
return (
2929
<div>

0 commit comments

Comments
 (0)