Failed to transfer using ledger/icrc

I’m trying to transfer using ledger/icrc but get the following error:

image

I’m trying to transfer from my users caller account on the FPL ledger to my apps backend account with the caller as a sub account.

Here is the transfer code:

async function saveEuro2024Predictions(
dto: Euro2024PredictionDTO,
): Promise {
try {
const identityActor = await ActorFactory.createIdentityActor(
authStore,
process.env.FOOTBALL_GOD_BACKEND_CANISTER_ID ?? “”,
);

  if (dto.alreadyEntered) {
    const result = await identityActor.submitEuro2024Prediction(dto);
    console.log(result);
    if (isError(result)) {
      console.error("Error saving Euro2024 prediction.");
      return;
    }
    return result;
  }


  authStore.subscribe(async (auth) => {
            
    const agent = await createAgent({
      identity: auth.identity!,
      host:  process.env.DFX_NETWORK === "ic"
      ? `https://${ActorFactory.getAgent()}.icp-api.io`
      : `http://localhost:8080/?canisterId=qhbym-qaaaa-aaaaa-aaafq-cai`,
      fetchRootKey: true
    });

    const { transfer } = IcrcLedgerCanister.create({
      agent,
      canisterId: Principal.fromText("avqkn-guaaa-aaaaa-qaaea-cai")
    });
    
    let principal = auth.identity?.getPrincipal();
    if(principal){
      let subaccount: Uint8Array = principalToSubAccount(principal);
      console.log(subaccount)
      let transfer_result = await transfer({
        to: {
          owner: Principal.fromText(
            process.env.FOOTBALL_GOD_BACKEND_CANISTER_ID ?? "",
          ),
          subaccount: [subaccount],
        },
        fee: 100_000n,
        memo: new Uint8Array(Text.encodeValue("0")),
        from_subaccount: undefined,
        created_at_time: BigInt(Date.now()),
        amount: 100_000_000_000n,
      });
      console.log(transfer_result);


      
    }

Thinking the problem might be the undefined from_subaccount but I transferred from my local nns to that sub account using just my apps principal id. So maybe the from_subaccount needs to be a combination of (FPL_SNS_LEDGER_CANISTER, CALLER). If so, how do I create this blob from these 2 principals…

If i get you correctly you want the caller to make the transfer right?

to do that you should be passing the prinicpalId as the owner not the canisterId
something like this

let principal = auth.identity?.getPrincipal();
    if(principal){
      let subaccount: Uint8Array = principalToSubAccount(principal);
      console.log(subaccount)
      let transfer_result = await transfer({
        to: {
          owner: Principal.fromText(principal), //take note of this line
          subaccount: [subaccount],
        },
        fee: 100_000n,
        memo: new Uint8Array(Text.encodeValue("0")),
        from_subaccount: undefined,
        created_at_time: BigInt(Date.now()),
        amount: 100_000_000_000n,
      });
      console.log(transfer_result);

with this, the caller is recognized as the one doing the transfer

Well that’s the ‘to’ field. I want to transfer ‘to’ my app, whose principal is the football_god_backend and I want to mark it as the users by using the caller as the sub account.

Oh sorry about that

maybe you should try using square bracket for the from_subaccount

from_subaccount: []

instead of undefined
since it’s supposed to be an (opt vec nat8), i.e an optional field

Hey, so thanks for this.

I get a new error (progress):

image

image

Before rushing to change the code, did you actually had a look to the all error?

“Failed to transfer” is just the message, the error returned by transfer() provides more information, namely an errorType. Having a further look to it should provide more information about the effective issue.

It should provide you one of those error:

export type TransferError =
  | {
      GenericError: { message: string; error_code: bigint };
    }
  | { TemporarilyUnavailable: null }
  | { BadBurn: { min_burn_amount: Tokens } }
  | { Duplicate: { duplicate_of: BlockIndex } }
  | { BadFee: { expected_fee: Tokens } }
  | { CreatedInFuture: { ledger_time: Timestamp } }
  | { TooOld: null }
  | { InsufficientFunds: { balance: Tokens } };

Ok so here is the full console error:

I do look to log the result and I do catch the error but I don’t know how to extract the TransferError detail :pensive:

That’s the error of your updated code no? Not the original error when you opened this thread no?

I believe the only change I’ve made since opening the thread is updating the from_subaccount: undefined to from_subaccount:

But again, that error is not very descriptive

image

Again, this error contains an errorType. Can you debug it and provide the related error type?

try {
  transfer
} catch err {
  if ("errorType" in err) {
    console.error(err.errorType)

I’ve probably set something up wrong as I can’t do that

image

Well, that was just a snippet to explain, I excepted you to finalize the proper code. You can cast it as any for quick debug or the actual type is IcrcTransferError.

You can also maybe just console.log(err) probably that might do too.

try 
   transfer
catch err
  console.log(err)

Yeah the whole time it’s been wrapped in a try catch with a log for the error but it doesn’t ever get there. Just doing this returns me to my original error:

image

image

As for the casting I can’t seem to find the library with the error types you mention although I have IcrcTransferError but requires

image

Please try either:

catch (err: unknown) {
  console.log(err); // <----- LOG not error

or

catch (err: unknown) {
   if ("errorType" in (err as IcrcTransferError<unknown>) {
      console.log((err as IcrcTransferError<unknown>).errorType);

or just

catch (err: any) {
  console.log(err.errorType);

image

Thank you, so it’s TooOld, will take a look at the dates etc.

From Rosetta:

// The request is too old.
// The ledger only accepts requests created within 24 hours window.
// This is a non-recoverable error.

1 Like

This looks wrong. Date.now gives milliseconds. The IC works with nanoseconds.

const nowInBigIntNanoSeconds = (): bigint => BigInt(Date.now()) * BigInt(1e6);
1 Like