Skip to content

Commit 32fb8b9

Browse files
authored
Merge pull request #16 from macalinao/igm/send-tx-updates
Improve transaction sending
2 parents a2299f5 + 49c1d2c commit 32fb8b9

File tree

10 files changed

+114
-27
lines changed

10 files changed

+114
-27
lines changed

.changeset/clear-grapes-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@macalinao/grill": minor
3+
---
4+
5+
Restructure sendTX types, add error toasts for transaction signing rejected

.changeset/kind-women-boil.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@macalinao/zod-solana": patch
3+
---
4+
5+
Fix TypeScript issues with Zod type

packages/grill/src/contexts/grill-context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { DataLoader } from "@macalinao/dataloader-es";
22
import type { Address, EncodedAccount } from "@solana/kit";
33
import type { GetExplorerLinkArgs } from "gill";
44
import { createContext, useContext } from "react";
5-
import type { SendTXFunction } from "../utils/internal/create-send-tx.js";
5+
import type { SendTXFunction } from "../utils/types.js";
66

77
export type GetExplorerLinkFunction = (args?: GetExplorerLinkArgs) => string;
88

packages/grill/src/hooks/use-send-tx.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { useGrillContext } from "../contexts/grill-context.js";
22

3-
export type {
4-
SendTXFunction,
5-
SendTXOptions,
6-
} from "../utils/internal/create-send-tx.js";
3+
export type { SendTXFunction, SendTXOptions } from "../utils/types.js";
74

85
/**
96
* Hook that provides a function to send transactions using the modern @solana/kit API

packages/grill/src/providers/grill-provider.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ export const GrillProvider: FC<GrillProviderProps> = ({
8080
break;
8181
}
8282

83+
case "error-transaction-send-failed": {
84+
console.error("Error sending transaction", event);
85+
const description = event.errorMessage;
86+
87+
if (existingToastId) {
88+
// Update existing toast to error
89+
toast.error(event.title, {
90+
id: existingToastId,
91+
description,
92+
duration: errorToastDuration,
93+
});
94+
} else {
95+
// Create new error toast if somehow we don't have one
96+
toast.error(event.title, {
97+
description,
98+
duration: errorToastDuration,
99+
});
100+
}
101+
// Clean up toast ID after error
102+
toastIds.current.delete(txId);
103+
break;
104+
}
105+
83106
case "preparing": {
84107
// Clean up any existing toast for this transaction
85108
if (existingToastId) {
@@ -160,6 +183,7 @@ export const GrillProvider: FC<GrillProviderProps> = ({
160183
}
161184

162185
case "error-transaction-failed": {
186+
console.error("Transaction failed", event);
163187
const description = event.errorMessage;
164188
const action = createExplorerAction(event.explorerLink);
165189

packages/grill/src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export type TransactionStatusEvent = {
1515
| {
1616
type: "awaiting-wallet-signature";
1717
}
18+
| {
19+
type: "error-transaction-send-failed";
20+
errorMessage: string;
21+
}
1822
| {
1923
type: "waiting-for-confirmation";
2024
sig: Signature;

packages/grill/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from "./get-signature-from-bytes.js";
33
export * from "./get-solscan-explorer-link.js";
44
export * from "./poll-confirm-transaction.js";
55
export * from "./refetch-accounts.js";
6+
export * from "./types.js";

packages/grill/src/utils/internal/create-send-tx.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import type {
22
Address,
3-
AddressesByLookupTableAddress,
43
Instruction,
54
Signature,
5+
SignatureBytes,
66
TransactionSendingSigner,
7-
TransactionSigner,
87
} from "@solana/kit";
98
import {
109
compressTransactionMessageUsingAddressLookupTables,
@@ -16,17 +15,7 @@ import type { GetExplorerLinkFunction } from "../../contexts/grill-context.js";
1615
import type { TransactionStatusEvent } from "../../types.js";
1716
import { getSignatureFromBytes } from "../get-signature-from-bytes.js";
1817
import { pollConfirmTransaction } from "../poll-confirm-transaction.js";
19-
20-
export interface SendTXOptions {
21-
luts?: AddressesByLookupTableAddress;
22-
signers?: TransactionSigner[];
23-
}
24-
25-
export type SendTXFunction = (
26-
name: string,
27-
ixs: readonly Instruction[],
28-
options?: SendTXOptions,
29-
) => Promise<Signature>;
18+
import type { SendTXFunction, SendTXOptions } from "../types.js";
3019

3120
export interface CreateSendTXParams {
3221
signer: TransactionSendingSigner | null;
@@ -77,13 +66,18 @@ export const createSendTX = ({
7766
instructions: [...ixs],
7867
latestBlockhash,
7968
// the compute budget values are HIGHLY recommend to be set in order to maximize your transaction landing rate
80-
// TODO(igm): make this configurable and/or dynamic based on the instructions
81-
computeUnitLimit: 1_400_000,
82-
computeUnitPrice: 100_000n,
69+
computeUnitLimit:
70+
options.computeUnitLimit === null
71+
? undefined
72+
: (options.computeUnitLimit ?? 1_400_000),
73+
computeUnitPrice:
74+
options.computeUnitPrice === null
75+
? undefined
76+
: (options.computeUnitPrice ?? 100_000n),
8377
});
8478

8579
// Apply address lookup tables if provided to compress the transaction
86-
const addressLookupTables = options.luts ?? {};
80+
const addressLookupTables = options.lookupTables ?? {};
8781
const finalTransactionMessage =
8882
Object.keys(addressLookupTables).length > 0
8983
? compressTransactionMessageUsingAddressLookupTables(
@@ -98,9 +92,22 @@ export const createSendTX = ({
9892
});
9993

10094
// Send transaction using wallet adapter
101-
const sigBytes = await signAndSendTransactionMessageWithSigners(
102-
finalTransactionMessage,
103-
);
95+
let sigBytes: SignatureBytes;
96+
try {
97+
sigBytes = await signAndSendTransactionMessageWithSigners(
98+
finalTransactionMessage,
99+
);
100+
} catch (error: unknown) {
101+
const errorMessage =
102+
error instanceof Error ? error.message : "Failed to send transaction";
103+
onTransactionStatusEvent({
104+
...baseEvent,
105+
type: "error-transaction-send-failed",
106+
errorMessage,
107+
});
108+
throw error;
109+
}
110+
104111
const sig = getSignatureFromBytes(sigBytes);
105112
const sentTxEvent = {
106113
...baseEvent,
@@ -130,7 +137,15 @@ export const createSendTX = ({
130137
.filter((key) => key.writable)
131138
.map((k) => k.pubkey);
132139
if (writableAccounts.length > 0) {
133-
await refetchAccounts(writableAccounts);
140+
const waitForAccountRefetch = options.waitForAccountRefetch ?? true;
141+
if (waitForAccountRefetch) {
142+
await refetchAccounts(writableAccounts);
143+
} else {
144+
// Refetch in background without waiting
145+
refetchAccounts(writableAccounts).catch((error: unknown) => {
146+
console.warn("Failed to refetch accounts in background:", error);
147+
});
148+
}
134149
}
135150

136151
if (result.meta?.logMessages) {

packages/grill/src/utils/types.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type {
2+
AddressesByLookupTableAddress,
3+
Instruction,
4+
Signature,
5+
} from "@solana/kit";
6+
7+
export interface SendTXOptions {
8+
lookupTables?: AddressesByLookupTableAddress;
9+
/**
10+
* Compute unit limit for the transaction.
11+
* Set to null to omit compute unit limit instruction.
12+
* Defaults to 1,400,000 if not specified.
13+
*/
14+
computeUnitLimit?: number | null;
15+
/**
16+
* Compute unit price for the transaction in microlamports.
17+
* Set to null to omit compute unit price instruction.
18+
* Defaults to 100,000 if not specified.
19+
*/
20+
computeUnitPrice?: bigint | null;
21+
/**
22+
* Whether to wait for account refetch after transaction confirmation.
23+
* When true (default), the function will wait for all writable accounts
24+
* to be refetched before resolving. When false, the function will
25+
* resolve immediately after confirmation and accounts will be refetched
26+
* in the background.
27+
* @default true
28+
*/
29+
waitForAccountRefetch?: boolean;
30+
}
31+
32+
export type SendTXFunction = (
33+
name: string,
34+
ixs: readonly Instruction[],
35+
options?: SendTXOptions,
36+
) => Promise<Signature>;

packages/zod-solana/src/address-schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { z } from "zod";
77
* Validates that a string is a valid Solana address and transforms it to an Address type.
88
* Compatible with both Zod v3 and v4.
99
*/
10-
export const addressSchema: z.ZodType<Address> = z
10+
export const addressSchema: z.ZodType<Address, string> = z
1111
.string()
1212
.transform((val, ctx) => {
1313
try {

0 commit comments

Comments
 (0)