@algorandfoundation/algokit-utils 8.1.0-beta.1 → 8.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,8 +23,10 @@ class TransactionComposer {
23
23
  constructor(params) {
24
24
  /** The ATC used to compose the group */
25
25
  this.atc = new algosdk.AtomicTransactionComposer();
26
- /** Map of txid to ABI method */
27
- this.txnMethodMap = new Map();
26
+ /** Map of transaction index in the atc to a max logical fee.
27
+ * This is set using the value of either maxFee or staticFee.
28
+ */
29
+ this.txnMaxFees = new Map();
28
30
  /** Transactions that have not yet been composed */
29
31
  this.txns = [];
30
32
  /** The default transaction validity window */
@@ -248,11 +250,17 @@ class TransactionComposer {
248
250
  const txnWithSigners = group.map((ts, idx) => {
249
251
  // Remove underlying group ID from the transaction since it will be re-grouped when this TransactionComposer is built
250
252
  ts.txn.group = undefined;
251
- // If this was a method call stash the ABIMethod for later
253
+ // If this was a method call return the ABIMethod for later
252
254
  if (atc['methodCalls'].get(idx)) {
253
- this.txnMethodMap.set(ts.txn.txID(), atc['methodCalls'].get(idx));
255
+ return {
256
+ ...ts,
257
+ context: { abiMethod: atc['methodCalls'].get(idx) },
258
+ };
254
259
  }
255
- return ts;
260
+ return {
261
+ ...ts,
262
+ context: {},
263
+ };
256
264
  });
257
265
  return txnWithSigners;
258
266
  }
@@ -295,7 +303,8 @@ class TransactionComposer {
295
303
  if (params.maxFee !== undefined && txn.fee > params.maxFee.microAlgo) {
296
304
  throw Error(`Transaction fee ${txn.fee} µALGO is greater than maxFee ${params.maxFee}`);
297
305
  }
298
- return txn;
306
+ const logicalMaxFee = params.maxFee !== undefined && params.maxFee.microAlgo > (params.staticFee?.microAlgo ?? 0n) ? params.maxFee : params.staticFee;
307
+ return { txn, context: { maxFee: logicalMaxFee } };
299
308
  }
300
309
  /**
301
310
  * Builds an ABI method call transaction and any other associated transactions represented in the ABI args.
@@ -351,7 +360,38 @@ class TransactionComposer {
351
360
  });
352
361
  }
353
362
  const methodAtc = new algosdk.AtomicTransactionComposer();
354
- transactionsForGroup.reverse().forEach((txn) => methodAtc.addTransaction(txn));
363
+ const maxFees = new Map();
364
+ transactionsForGroup.reverse().forEach(({ context, ...txnWithSigner }) => {
365
+ methodAtc.addTransaction(txnWithSigner);
366
+ const atcIndex = methodAtc.count() - 1;
367
+ if (context.abiMethod) {
368
+ methodAtc['methodCalls'].set(atcIndex, context.abiMethod);
369
+ }
370
+ if (context.maxFee !== undefined) {
371
+ maxFees.set(atcIndex, context.maxFee);
372
+ }
373
+ });
374
+ // If any of the args are method call transactions, add that info to the methodAtc
375
+ methodArgs
376
+ .filter((arg) => {
377
+ if (typeof arg === 'object' && 'context' in arg) {
378
+ const { context, ...txnWithSigner } = arg;
379
+ return isTransactionWithSigner(txnWithSigner);
380
+ }
381
+ return isTransactionWithSigner(arg);
382
+ })
383
+ .reverse()
384
+ .forEach((arg, idx) => {
385
+ if (typeof arg === 'object' && 'context' in arg && arg.context) {
386
+ const atcIndex = methodAtc.count() + idx;
387
+ if (arg.context.abiMethod) {
388
+ methodAtc['methodCalls'].set(atcIndex, arg.context.abiMethod);
389
+ }
390
+ if (arg.context.maxFee !== undefined) {
391
+ maxFees.set(atcIndex, arg.context.maxFee);
392
+ }
393
+ }
394
+ });
355
395
  const appId = Number('appId' in params ? params.appId : 0n);
356
396
  const approvalProgram = 'approvalProgram' in params
357
397
  ? typeof params.approvalProgram === 'string'
@@ -393,19 +433,37 @@ class TransactionComposer {
393
433
  : params.signer
394
434
  : this.getSigner(params.sender)
395
435
  : TransactionComposer.NULL_SIGNER,
396
- methodArgs: methodArgs.reverse(),
436
+ methodArgs: methodArgs
437
+ .map((arg) => {
438
+ if (typeof arg === 'object' && 'context' in arg) {
439
+ const { context, ...txnWithSigner } = arg;
440
+ return txnWithSigner;
441
+ }
442
+ return arg;
443
+ })
444
+ .reverse(),
397
445
  // note, lease, and rekeyTo are set in the common build step
398
446
  note: undefined,
399
447
  lease: undefined,
400
448
  rekeyTo: undefined,
401
449
  };
402
450
  // Build the transaction
403
- this.commonTxnBuildStep((txnParams) => {
451
+ const result = this.commonTxnBuildStep((txnParams) => {
404
452
  methodAtc.addMethodCall(txnParams);
405
453
  return methodAtc.buildGroup()[methodAtc.count() - 1].txn;
406
454
  }, params, txnParams);
407
455
  // Process the ATC to get a set of transactions ready for broader grouping
408
- return this.buildAtc(methodAtc);
456
+ return this.buildAtc(methodAtc).map(({ context: _context, ...txnWithSigner }, idx) => {
457
+ const maxFee = idx === methodAtc.count() - 1 ? result.context.maxFee : maxFees.get(idx);
458
+ const context = {
459
+ ..._context, // Adds method context info
460
+ maxFee,
461
+ };
462
+ return {
463
+ ...txnWithSigner,
464
+ context,
465
+ };
466
+ });
409
467
  }
410
468
  buildPayment(params, suggestedParams) {
411
469
  return this.commonTxnBuildStep(algosdk.makePaymentTxnWithSuggestedParamsFromObject, params, {
@@ -566,7 +624,12 @@ class TransactionComposer {
566
624
  }
567
625
  async buildTxnWithSigner(txn, suggestedParams) {
568
626
  if (txn.type === 'txnWithSigner') {
569
- return [txn];
627
+ return [
628
+ {
629
+ ...txn,
630
+ context: {},
631
+ },
632
+ ];
570
633
  }
571
634
  if (txn.type === 'atc') {
572
635
  return this.buildAtc(txn.atc);
@@ -575,7 +638,7 @@ class TransactionComposer {
575
638
  return await this.buildMethodCall(txn, suggestedParams, true);
576
639
  }
577
640
  const signer = txn.signer ? ('signer' in txn.signer ? txn.signer.signer : txn.signer) : this.getSigner(txn.sender);
578
- return (await this.buildTxn(txn, suggestedParams)).map((txn) => ({ txn, signer }));
641
+ return (await this.buildTxn(txn, suggestedParams)).map(({ txn, context }) => ({ txn, signer, context }));
579
642
  }
580
643
  /**
581
644
  * Compose all of the transactions without signers and return the transaction objects directly along with any ABI method calls.
@@ -589,7 +652,7 @@ class TransactionComposer {
589
652
  const signers = new Map();
590
653
  for (const txn of this.txns) {
591
654
  if (!['txnWithSigner', 'atc', 'methodCall'].includes(txn.type)) {
592
- transactions.push(...(await this.buildTxn(txn, suggestedParams)));
655
+ transactions.push(...(await this.buildTxn(txn, suggestedParams)).map((txn) => txn.txn));
593
656
  }
594
657
  else {
595
658
  const transactionsWithSigner = txn.type === 'txnWithSigner'
@@ -599,19 +662,17 @@ class TransactionComposer {
599
662
  : txn.type === 'methodCall'
600
663
  ? await this.buildMethodCall(txn, suggestedParams, false)
601
664
  : [];
602
- transactions.push(...transactionsWithSigner.map((ts) => ts.txn));
603
665
  transactionsWithSigner.forEach((ts, idx) => {
666
+ transactions.push(ts.txn);
604
667
  if (ts.signer && ts.signer !== TransactionComposer.NULL_SIGNER) {
605
668
  signers.set(idx, ts.signer);
606
669
  }
670
+ if ('context' in ts && ts.context.abiMethod) {
671
+ methodCalls.set(idx, ts.context.abiMethod);
672
+ }
607
673
  });
608
674
  }
609
675
  }
610
- for (let i = 0; i < transactions.length; i++) {
611
- const method = this.txnMethodMap.get(transactions[i].txID());
612
- if (method)
613
- methodCalls.set(i, method);
614
- }
615
676
  return { transactions, methodCalls, signers };
616
677
  }
617
678
  /**
@@ -639,12 +700,15 @@ class TransactionComposer {
639
700
  }
640
701
  // Add all of the transactions to the underlying ATC
641
702
  const methodCalls = new Map();
642
- txnWithSigners.forEach((ts, idx) => {
703
+ txnWithSigners.forEach(({ context, ...ts }, idx) => {
643
704
  this.atc.addTransaction(ts);
644
705
  // Populate consolidated set of all ABI method calls
645
- const method = this.txnMethodMap.get(ts.txn.txID());
646
- if (method)
647
- methodCalls.set(idx, method);
706
+ if (context.abiMethod) {
707
+ methodCalls.set(idx, context.abiMethod);
708
+ }
709
+ if (context.maxFee !== undefined) {
710
+ this.txnMaxFees.set(idx, context.maxFee);
711
+ }
648
712
  });
649
713
  this.atc['methodCalls'] = methodCalls;
650
714
  }
@@ -667,9 +731,10 @@ class TransactionComposer {
667
731
  async send(params) {
668
732
  const group = (await this.build()).transactions;
669
733
  let waitRounds = params?.maxRoundsToWaitForConfirmation;
734
+ const suggestedParams = waitRounds === undefined || params?.coverAppCallInnerTransactionFees ? await this.getSuggestedParams() : undefined;
670
735
  if (waitRounds === undefined) {
671
736
  const lastRound = group.reduce((max, txn) => (txn.txn.lastValid > max ? txn.txn.lastValid : BigInt(max)), 0n);
672
- const { firstValid: firstRound } = await this.getSuggestedParams();
737
+ const { firstValid: firstRound } = suggestedParams;
673
738
  waitRounds = Number(BigInt(lastRound) - BigInt(firstRound)) + 1;
674
739
  }
675
740
  return await sendAtomicTransactionComposer({
@@ -677,6 +742,13 @@ class TransactionComposer {
677
742
  suppressLog: params?.suppressLog,
678
743
  maxRoundsToWaitForConfirmation: waitRounds,
679
744
  populateAppCallResources: params?.populateAppCallResources,
745
+ coverAppCallInnerTransactionFees: params?.coverAppCallInnerTransactionFees,
746
+ additionalAtcContext: params?.coverAppCallInnerTransactionFees
747
+ ? {
748
+ maxFees: this.txnMaxFees,
749
+ suggestedParams: suggestedParams,
750
+ }
751
+ : undefined,
680
752
  }, this.algod);
681
753
  }
682
754
  /**