@@ -137,6 +137,8 @@ function deEscalateAccountMeta(accountMeta: AccountMeta, accountMetas: AccountMe
137
137
}
138
138
139
139
/**
140
+ * @deprecated Deprecated since v0.3.12. Please use {@link addExtraAccountMetasForExecute} instead.
141
+ *
140
142
* Add extra accounts needed for transfer hook to an instruction
141
143
*
142
144
* @param connection Connection to use
@@ -190,14 +192,120 @@ export async function addExtraAccountsToInstruction(
190
192
return new TransactionInstruction ( { keys : accountMetas , programId, data : instruction . data } ) ;
191
193
}
192
194
195
+ /**
196
+ * Construct an `ExecuteInstruction` for a transfer hook program, without the
197
+ * additional accounts
198
+ *
199
+ * @param programId The program ID of the transfer hook program
200
+ * @param source The source account
201
+ * @param mint The mint account
202
+ * @param destination The destination account
203
+ * @param owner Owner of the source account
204
+ * @param validateStatePubkey The validate state pubkey
205
+ * @param amount The amount of tokens to transfer
206
+ * @returns Instruction to add to a transaction
207
+ */
208
+ export function createExecuteInstruction (
209
+ programId : PublicKey ,
210
+ source : PublicKey ,
211
+ mint : PublicKey ,
212
+ destination : PublicKey ,
213
+ owner : PublicKey ,
214
+ validateStatePubkey : PublicKey ,
215
+ amount : bigint
216
+ ) : TransactionInstruction {
217
+ const keys = [ source , mint , destination , owner , validateStatePubkey ] . map ( ( pubkey ) => ( {
218
+ pubkey,
219
+ isSigner : false ,
220
+ isWritable : false ,
221
+ } ) ) ;
222
+
223
+ const data = Buffer . alloc ( 16 ) ;
224
+ data . set ( Buffer . from ( [ 105 , 37 , 101 , 197 , 75 , 251 , 102 , 26 ] ) , 0 ) ; // `ExecuteInstruction` discriminator
225
+ data . writeBigUInt64LE ( BigInt ( amount ) , 8 ) ;
226
+
227
+ return new TransactionInstruction ( { keys, programId, data } ) ;
228
+ }
229
+
230
+ /**
231
+ * Adds all the extra accounts needed for a transfer hook to an instruction.
232
+ *
233
+ * Note this will modify the instruction passed in.
234
+ *
235
+ * @param connection Connection to use
236
+ * @param instruction The instruction to add accounts to
237
+ * @param programId Transfer hook program ID
238
+ * @param source The source account
239
+ * @param mint The mint account
240
+ * @param destination The destination account
241
+ * @param owner Owner of the source account
242
+ * @param amount The amount of tokens to transfer
243
+ * @param commitment Commitment to use
244
+ */
245
+ export async function addExtraAccountMetasForExecute (
246
+ connection : Connection ,
247
+ instruction : TransactionInstruction ,
248
+ programId : PublicKey ,
249
+ source : PublicKey ,
250
+ mint : PublicKey ,
251
+ destination : PublicKey ,
252
+ owner : PublicKey ,
253
+ amount : number | bigint ,
254
+ commitment ?: Commitment
255
+ ) {
256
+ const validateStatePubkey = getExtraAccountMetaAddress ( mint , programId ) ;
257
+ const validateStateAccount = await connection . getAccountInfo ( validateStatePubkey , commitment ) ;
258
+ if ( validateStateAccount == null ) {
259
+ return instruction ;
260
+ }
261
+ const validateStateData = getExtraAccountMetas ( validateStateAccount ) ;
262
+
263
+ // Check to make sure the provided keys are in the instruction
264
+ if ( ! [ source , mint , destination , owner ] . every ( ( key ) => instruction . keys . some ( ( meta ) => meta . pubkey === key ) ) ) {
265
+ throw new Error ( 'Missing required account in instruction' ) ;
266
+ }
267
+
268
+ const executeInstruction = createExecuteInstruction (
269
+ programId ,
270
+ source ,
271
+ mint ,
272
+ destination ,
273
+ owner ,
274
+ validateStatePubkey ,
275
+ BigInt ( amount )
276
+ ) ;
277
+
278
+ for ( const extraAccountMeta of validateStateData ) {
279
+ executeInstruction . keys . push (
280
+ deEscalateAccountMeta (
281
+ await resolveExtraAccountMeta (
282
+ connection ,
283
+ extraAccountMeta ,
284
+ executeInstruction . keys ,
285
+ executeInstruction . data ,
286
+ executeInstruction . programId
287
+ ) ,
288
+ executeInstruction . keys
289
+ )
290
+ ) ;
291
+ }
292
+
293
+ // Add only the extra accounts resolved from the validation state
294
+ instruction . keys . push ( ...executeInstruction . keys . slice ( 5 ) ) ;
295
+
296
+ // Add the transfer hook program ID and the validation state account
297
+ instruction . keys . push ( { pubkey : programId , isSigner : false , isWritable : false } ) ;
298
+ instruction . keys . push ( { pubkey : validateStatePubkey , isSigner : false , isWritable : false } ) ;
299
+ }
300
+
193
301
/**
194
302
* Construct an transferChecked instruction with extra accounts for transfer hook
195
303
*
196
304
* @param connection Connection to use
197
305
* @param source Source account
198
306
* @param mint Mint to update
199
307
* @param destination Destination account
200
- * @param authority The mint's transfer hook authority
308
+ * @param owner Owner of the source account
201
309
* @param amount The amount of tokens to transfer
202
310
* @param decimals Number of decimals in transfer amount
203
311
* @param multiSigners The signer account(s) for a multisig
@@ -211,33 +319,42 @@ export async function createTransferCheckedWithTransferHookInstruction(
211
319
source : PublicKey ,
212
320
mint : PublicKey ,
213
321
destination : PublicKey ,
214
- authority : PublicKey ,
322
+ owner : PublicKey ,
215
323
amount : bigint ,
216
324
decimals : number ,
217
325
multiSigners : ( Signer | PublicKey ) [ ] = [ ] ,
218
326
commitment ?: Commitment ,
219
327
programId = TOKEN_PROGRAM_ID
220
328
) {
221
- const rawInstruction = createTransferCheckedInstruction (
329
+ const instruction = createTransferCheckedInstruction (
222
330
source ,
223
331
mint ,
224
332
destination ,
225
- authority ,
333
+ owner ,
226
334
amount ,
227
335
decimals ,
228
336
multiSigners ,
229
337
programId
230
338
) ;
231
339
232
- const hydratedInstruction = await addExtraAccountsToInstruction (
233
- connection ,
234
- rawInstruction ,
235
- mint ,
236
- commitment ,
237
- programId
238
- ) ;
340
+ const mintInfo = await getMint ( connection , mint , commitment , programId ) ;
341
+ const transferHook = getTransferHook ( mintInfo ) ;
342
+
343
+ if ( transferHook ) {
344
+ await addExtraAccountMetasForExecute (
345
+ connection ,
346
+ instruction ,
347
+ transferHook . programId ,
348
+ source ,
349
+ mint ,
350
+ destination ,
351
+ owner ,
352
+ amount ,
353
+ commitment
354
+ ) ;
355
+ }
239
356
240
- return hydratedInstruction ;
357
+ return instruction ;
241
358
}
242
359
243
360
/**
@@ -247,7 +364,7 @@ export async function createTransferCheckedWithTransferHookInstruction(
247
364
* @param source Source account
248
365
* @param mint Mint to update
249
366
* @param destination Destination account
250
- * @param authority The mint's transfer hook authority
367
+ * @param owner Owner of the source account
251
368
* @param amount The amount of tokens to transfer
252
369
* @param decimals Number of decimals in transfer amount
253
370
* @param fee The calculated fee for the transfer fee extension
@@ -262,33 +379,42 @@ export async function createTransferCheckedWithFeeAndTransferHookInstruction(
262
379
source : PublicKey ,
263
380
mint : PublicKey ,
264
381
destination : PublicKey ,
265
- authority : PublicKey ,
382
+ owner : PublicKey ,
266
383
amount : bigint ,
267
384
decimals : number ,
268
385
fee : bigint ,
269
386
multiSigners : ( Signer | PublicKey ) [ ] = [ ] ,
270
387
commitment ?: Commitment ,
271
388
programId = TOKEN_PROGRAM_ID
272
389
) {
273
- const rawInstruction = createTransferCheckedWithFeeInstruction (
390
+ const instruction = createTransferCheckedWithFeeInstruction (
274
391
source ,
275
392
mint ,
276
393
destination ,
277
- authority ,
394
+ owner ,
278
395
amount ,
279
396
decimals ,
280
397
fee ,
281
398
multiSigners ,
282
399
programId
283
400
) ;
284
401
285
- const hydratedInstruction = await addExtraAccountsToInstruction (
286
- connection ,
287
- rawInstruction ,
288
- mint ,
289
- commitment ,
290
- programId
291
- ) ;
402
+ const mintInfo = await getMint ( connection , mint , commitment , programId ) ;
403
+ const transferHook = getTransferHook ( mintInfo ) ;
404
+
405
+ if ( transferHook ) {
406
+ await addExtraAccountMetasForExecute (
407
+ connection ,
408
+ instruction ,
409
+ transferHook . programId ,
410
+ source ,
411
+ mint ,
412
+ destination ,
413
+ owner ,
414
+ amount ,
415
+ commitment
416
+ ) ;
417
+ }
292
418
293
- return hydratedInstruction ;
419
+ return instruction ;
294
420
}
0 commit comments