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?

1 Like

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

1 Like

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.

1 Like