@@ -17,21 +17,33 @@ import { useSyncExternalStore } from "react";
1717import { useAlchemyAccountContext } from "./useAlchemyAccountContext.js" ;
1818import type { PromiseOrValue } from "../../../../aa-sdk/core/dist/types/types.js" ;
1919
20+ /** Used right before we send the transaction out, this is going to be the signer. */
2021export type PreSend = (
21- transaction : VersionedTransaction | Transaction ,
22- next : PreSend
22+ this : void ,
23+ transaction : VersionedTransaction | Transaction
2324) => PromiseOrValue < VersionedTransaction | Transaction > ;
25+ /**
26+ * Used in the sendTransaction, will transform either the instructions (or the transfer -> instructions) into a transaction
27+ */
28+ export type TransformInstruction = (
29+ this : void ,
30+ instructions : TransactionInstruction [ ]
31+ ) => PromiseOrValue < Transaction | VersionedTransaction > ;
32+ export type SolanaTransactionParamOptions = {
33+ preSend ?: PreSend ;
34+ transformInstruction ?: TransformInstruction ;
35+ } ;
2436export type SolanaTransactionParams =
2537 | {
2638 transfer : {
2739 amount : number ;
2840 toAddress : string ;
2941 } ;
30- preSend ?: PreSend ;
42+ transactionComponents ?: SolanaTransactionParamOptions ;
3143 }
3244 | {
3345 instructions : TransactionInstruction [ ] ;
34- preSend ?: PreSend ;
46+ transactionComponents ?: SolanaTransactionParamOptions ;
3547 } ;
3648/**
3749 * We wanted to make sure that this will be using the same useMutation that the
@@ -106,39 +118,88 @@ export function useSolanaTransaction(
106118 ( ) => getSolanaConnection ( config )
107119 ) ;
108120 const mutation = useMutation ( {
109- mutationFn : async ( params : SolanaTransactionParams ) => {
110- if ( ! signer ) throw new Error ( "Not ready" ) ;
111- if ( ! connection ) throw new Error ( "Not ready" ) ;
112- const instructions =
113- "instructions" in params
114- ? params . instructions
115- : [
116- SystemProgram . transfer ( {
117- fromPubkey : new PublicKey ( signer . address ) ,
118- toPubkey : new PublicKey ( params . transfer . toAddress ) ,
119- lamports : params . transfer . amount ,
120- } ) ,
121- ] ;
122- const policyId =
123- "policyId" in opts ? opts . policyId : backupConnection ?. policyId ;
124- let transaction : VersionedTransaction | Transaction = policyId
125- ? await signer . addSponsorship ( instructions , connection , policyId )
126- : await signer . createTransfer ( instructions , connection ) ;
121+ mutationFn : async ( {
122+ transactionComponents : {
123+ preSend,
124+ transformInstruction = mapTransformInstructions . default ,
125+ } = { } ,
126+ ...params
127+ } : SolanaTransactionParams ) => {
128+ const instructions = getInstructions ( ) ;
129+ let transaction : VersionedTransaction | Transaction =
130+ await transformInstruction ( instructions ) ;
127131
128- const iSign : PreSend = async ( t ) => {
129- await signer . addSignature ( t ) ;
130- return t ;
131- } ;
132- const preSend = params . preSend || iSign ;
133- transaction = await preSend ( transaction , iSign ) ;
132+ transaction = ( await preSend ?.( transaction ) ) || transaction ;
134133
135- const hash = await solanaNetwork . broadcast ( connection , transaction ) ;
134+ if ( needsSignerToSign ( ) ) {
135+ await signer ?. addSignature ( transaction ) ;
136+ }
137+
138+ const localConnection = connection || missing ( "connection" ) ;
139+ const hash = await solanaNetwork . broadcast ( localConnection , transaction ) ;
136140 return { hash } ;
141+
142+ function getInstructions ( ) {
143+ if ( "instructions" in params ) {
144+ return params . instructions ;
145+ }
146+ return [
147+ SystemProgram . transfer ( {
148+ fromPubkey : new PublicKey (
149+ signer ?. address || missing ( "signer.address" )
150+ ) ,
151+ toPubkey : new PublicKey ( params . transfer . toAddress ) ,
152+ lamports : params . transfer . amount ,
153+ } ) ,
154+ ] ;
155+ }
156+
157+ function needsSignerToSign ( ) {
158+ if ( "message" in transaction ) {
159+ const message = transaction . message ;
160+ return message . staticAccountKeys . some (
161+ ( key , index ) =>
162+ ( ( ) => {
163+ debugger ;
164+ return false ;
165+ } ) ( ) ||
166+ ( key . toBase58 ( ) === signer ?. address &&
167+ message ?. isAccountSigner ( index ) )
168+ ) ;
169+ }
170+ return transaction . instructions . some ( ( x ) =>
171+ x . keys . some (
172+ ( x ) => x . pubkey . toBase58 ( ) === signer ?. address && x . isSigner
173+ )
174+ ) ;
175+ }
137176 } ,
138177 ...opts . mutation ,
139178 } ) ;
140179 const signer : null | SolanaSigner = opts ?. signer || fallbackSigner ;
141180 const connection = opts ?. connection || backupConnection ?. connection || null ;
181+ const policyId =
182+ "policyId" in opts ? opts . policyId : backupConnection ?. policyId ;
183+ const mapTransformInstructions : Record < string , TransformInstruction > = {
184+ async addSponsorship ( instructions : TransactionInstruction [ ] ) {
185+ return await ( signer || missing ( "signer" ) ) . addSponsorship (
186+ instructions ,
187+ connection || missing ( "connection" ) ,
188+ policyId || missing ( "policyId" )
189+ ) ;
190+ } ,
191+ async createTransfer ( instructions : TransactionInstruction [ ] ) {
192+ return await ( signer || missing ( "signer" ) ) . createTransfer (
193+ instructions ,
194+ connection || missing ( "connection" )
195+ ) ;
196+ } ,
197+ get default ( ) {
198+ return policyId
199+ ? mapTransformInstructions . addSponsorship
200+ : mapTransformInstructions . createTransfer ;
201+ } ,
202+ } ;
142203
143204 return {
144205 connection,
@@ -148,3 +209,7 @@ export function useSolanaTransaction(
148209 sendTransactionAsync : mutation . mutateAsync ,
149210 } ;
150211}
212+
213+ function missing ( message : string ) : never {
214+ throw new Error ( message ) ;
215+ }
0 commit comments