@algorandfoundation/algokit-utils 8.1.0-beta.2 → 8.1.0-beta.4

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