App call problem, TypeScript

Hi!
In TealScript I can write methods like this:

/**
   * Buy 1 piece of the asset
   * @param payment Payment in /uAlgos
   */
  buyAsset(payment: PayTxn): void {
    /// Ensure asset selling period hasn't ended yet
    assert(globals.latestTimestamp < this.sellPeriodEnd.value);

    /// Verify payment transaction
    verifyPayTxn(payment, {
      sender: this.txn.sender,
      receiver: globals.creatorAddress,
      amount: { greaterThanEqualTo: this.assetPrice.value, lessThanEqualTo: this.assetPrice.value },
      rekeyTo: globals.zeroAddress,
      closeRemainderTo: globals.zeroAddress,
    });

    /// Verify asset amount
    assert(this.assetAmount.value > 0);

    /// @todo: check asset amount in  buyer account

    /// Send asset to payer
    sendAssetTransfer({
      assetReceiver: this.txn.sender,
      xferAsset: this.asset.value,
      assetAmount: 1,
    });

    // Decrease asset amount
    this.assetAmount.value = this.assetAmount.value - 1;
  }

My question is, how can this be called from TypeScript Jest framework? From the compiled TEAL code I see that TEAL waits for a txn grup, and checks whether the last txn in the group is a payment txn. So a transaction group is needed, where the first tx is the app call, the second is the payment txn, but I still don’t know how to make it in TypeScript?

If you have abi parameter PayTxn, it means it is one index before the app call, not after the appcall

check out the tests for example here: BiatecCLAMM/__test__/BiatecClammPool.test.ts at main · scholtz/BiatecCLAMM · GitHub

or how to compose the txs here: BiatecCLAMM/src/biatecClamm/txs/clammSwapTxs.ts at ace7f6a7d9b769b751530a810e96dd98f28ff86e · scholtz/BiatecCLAMM · GitHub

Also note that the generated clients are great help nowadays… if you bootstraped your project with algokit template, you can generate them using npm run generate-client … or npm run build and also npm run test builds the teal and clients for you and runs the tests

Hi, Ludo @scholtz , thanks for the prompt answer.

Part of the smart contract BizKor.algo.ts is:

  /**
   * Buy 1 piece of the asset
   * @param payment Payment in /uAlgos
   */
  buyAsset(payment: PayTxn): void {
    /// Ensure asset selling period hasn't ended yet
    assert(globals.latestTimestamp < this.sellPeriodEnd.value);

    /// Verify payment transaction
    verifyPayTxn(payment, {
      sender: this.txn.sender,
      receiver: globals.creatorAddress,
      amount: { greaterThanEqualTo: this.assetPrice.value, lessThanEqualTo: this.assetPrice.value },
      rekeyTo: globals.zeroAddress,
      closeRemainderTo: globals.zeroAddress,
    });

    /// Verify asset amount
    assert(this.assetAmount.value > 0);

    /// @todo: check asset amount in  buyer account

    /// Send asset to payer
    sendAssetTransfer({
      assetReceiver: this.txn.sender,
      xferAsset: this.asset.value,
      assetAmount: 1,
    });

    // Decrease asset amount
    this.assetAmount.value = this.assetAmount.value - 1;
  }

It is tested by this code:

    const { algod, testAccount } = fixture.context;
    const params = await algod.getTransactionParams().do();
    await appClient.appClient.fundAppAccount(algokit.microAlgos(400_000));

    // Opt in to asset
    const globalState = await appClient.getGlobalState();
    const asset = globalState.asset!.asNumber();
    console.log('Try to opt in to asset: ', asset);
    const txn1 = algosdk.makeAssetTransferTxnWithSuggestedParamsFromObject({
      from: sender1.addr,
      to: sender1.addr,
      amount: 0,
      assetIndex: asset,
      suggestedParams: params,
    });
    const stxn1 = txn1.signTxn(sender1.sk);
    const txn2 = await algod.sendRawTransaction(stxn1).do();
    await algosdk.waitForConfirmation(algod, txn2.txId, 4);

    // Make a payment tx, to buy asset
    const tx1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
      from: sender1.addr,
      to: testAccount.addr,
      amount: 1_000_000,
      suggestedParams: params,
    });

    // Buy asset
    const compose = appClient.compose().buyAsset(
      {
        payment: tx1,
      },
      {
        sender: signer1,
        sendParams: {
          fee: algokit.microAlgos(2000),
        },
      }
    );

    const atc = await compose.atc();
    const txs = atc.buildGroup().map((tx) => tx.txn);
    const signed = await signer1.signer(
      txs,
      Array.from(Array(txs.length), (_, i) => i)
    );
    const { txId } = await algod.sendRawTransaction(signed).do();
    console.log('txId:', txId);
  });

The test starts to runs now, so the original problem is solved.