Intermittent "Signature verification failed" errors from AgentJS

We just received this error again a few times throughout today in CI, then reran the test and it passed. It happens when we’re using agent-js to make a call to a query method on our canister. Curiously, it always happens on the same test?

AgentJS version ^1.2.0

The call is made to this query endpoint on a canister that has some relatively simple logic.

/// Returns the local account of the caller.
  public query ({ caller }) func customerLocalAccount() : async Ledger.AccountIdentifier {
    _localAccount(caller);
  };

  private func _localAccount(customerId : Principal) : Ledger.AccountIdentifier = Ledger.accountIdentifierFrom(
    Principal.fromActor(this),
    customerId,
  );

We end up getting an error that looks like this every time:

Invalid certificate: Signature verification failed

      63 | // Deposit ICP from NNS ledger to account
      64 | export async function depositICP(user: User, amount: number) {
    > 65 |   const customerLocalAccount = await user.call.accounts.customerLocalAccount(); // see the endpoint I listed above
         |                                ^
      66 |   await user.call.ledger.transfer({
      67 |     amount: { e8s: BigInt(amount * 1e8 - 10_000) },
      68 |     to: [...customerLocalAccount],

Here is the test

// Some setup
  test("User deposits ICP into their cycleops account", async () => {
    await grantICP(principalToAddressBytes(user.key.getPrincipal()), 10);
    // User transfers funds to their cycleops account
    await depositICP(user, 10);
    expect((await user.call.accounts.customerBalance()).e8s).toEqual(
      BigInt(10 * 1e8 - 10_000)
    );
  });

Where the depositICP function is:

export async function depositICP(user: User, amount: number) {
  const customerLocalAccount = await user.call.accounts.customerLocalAccount();
  await user.call.ledger.transfer({
    amount: { e8s: BigInt(amount * 1e8 - 10_000) },
    to: [...customerLocalAccount],
    from_subaccount: [],
    memo: BigInt(0),
    fee: { e8s: BigInt(10_000) },
    created_at_time: [],
  });
}

and the User type looks like this

export interface User {
  key: Ed25519KeyIdentity;
  call: {
    accounts: ActorSubclass<Accounts>; // the canister we're making the call to
    //...similar pattern for other canisters
  };
}
1 Like