Oisy wallet icrc2_approve failing because of something related to icrc21_canister_call_consent_message

I am getting the following error when trying to make a ICRC2_approve call from the frontent using oisy wallet

here is my frontend call:

    const bigIntAmount = BigInt(Number(amount) * DECIMALS);
    const result = await signedICPLedgerActor.icrc2_approve({
      fee: [],
      memo: [],
      from_subaccount: [],
      created_at_time: [],
      amount: bigIntAmount + BigInt(ICP_LEDGER_FEE),
      expected_allowance: [],
      expires_at: [],
      spender: {
        owner: Principal.fromText(import.meta.env.CANISTER_ID),
        subaccount: [],
      },
    });

I get the following error message:

An error occurred while retrieving the consent message / Call

D failed: Canister: <CANISTER_ID> lMethod:“”=

icrc21_canister_call_consent_message (update) “Request ID”:

Any idea what might be going on here?

All other wallets work just Oisy fails

  1. Is it a formatting issue, or is the error message you shared incomplete? What’s the full stack trace of the error?

  2. You are experiencing the issue while running OISY locally or are you referring to mainnet?

It is happening when trying to login to oisy

Thats all i can see before it disappears

Can you provide the full log, please? The message displayed in the toast can be scrolled.

Also, what’s the target canister ID?

Here is the full message

The target canister is just a very simple rust canister that is collecting funds before redistributing them on a monthly timer

But this point is just trying to make an ICRC2_transfer_from call which is where i think this might be failing

    pub async fn make_deposit(principal: Principal, amount: u64) -> Result<(), String> {
        let transfer_from_args = TransferFromArgs {
            from: IcpAccount {
                owner: principal,
                subaccount: None,
            },
            memo: None,
            amount: Nat::from(amount),
            spender_subaccount: None,
            fee: Some(Nat::from(ICP_LEDGER_FEE)),
            to: IcpAccount {
                owner: ic_cdk::api::canister_self(),
                subaccount: None,
            },
            created_at_time: None,
        };
        

        let allowance_args = AllowanceArgs {
            account: IcpAccount {
                owner: principal,
                subaccount: None,
            },
            spender: IcpAccount {
                owner: ic_cdk::api::canister_self(),
                subaccount: None,
            },
        };

        let (allowance,) = ApiClients::icp_ledger()
            .icrc_2_allowance(allowance_args)
            .await
            .map_err(|e| e.1.to_string())?;

            
        let (transfer_result,) = ApiClients::icp_ledger()
            .icrc_2_transfer_from(transfer_from_args)
            .await
            .map_err(|e| {
                e.1.to_string()
                    + &format!(" allowance: {:?}", allowance.allowance)
                    + &format!(" amount: {:?}", amount)
            })?;

        match transfer_result {
            Result3::Ok(_) => {
                DEPOSITS.with(|deposits| {
                    let current_amount = deposits.borrow().get(&principal).unwrap_or(0);
                    deposits
                        .borrow_mut()
                        .insert(principal, current_amount + amount);
                });
            }
            Result3::Err(err) => {
                return Err(format!("Error making deposit: {:?}", err)
                    + &format!(" allowance: {:?}", allowance.allowance)
                    + &format!(" amount: {:?}", amount));
            }
        }

        Ok(())
    }

The approve call is going through successfully. I have also just removed the allowance call and it is still coming up with the same error message so it must be the transfer from call that is failing

It would be helpful if you could share the canister ID. Based on your message, I assume the targeted canister is not a ledger (or a fork) and does not implement ICRC-21. Is that correct?

To use the signer standards, the targeted canister must implement ICRC-21. If it doesn’t, OISY may still emulate a potential consent message—but only if the canister is an ICRC ledger that exposes metadata (see this function). All other cases are rejected for security reasons.

Does your case fall into this scenario?

Yes that sounds like our case. Have you got some example implementation in rust of this icrc21 standard

I solved the issue. It’s really frustrating that i have to implement this seemingly useless and unnecessary dead code on to my canister as a developer working on the internet computer just so I can use Oisy wallet when no other wallet requires me to do this.

Anyway here is the code i used to solve this issue.

#[update]
pub fn icrc21_canister_call_consent_message(consent_msg_request: ConsentMessageRequest) -> Result<ConsentInfo, ErrorInfo> {
    let consent_message = match consent_msg_request.method.as_str() {
        "your_method_name" | "your_method_name_async"=> {
            let Ok(your_method_name_args) = decode_one::<THE_ARGUMENTS_FOR_YOUR_METHOD>(&consent_msg_request.arg) else {
                Err(ErrorInfo {
                    description: "Failed to decode AddDepositArgs".to_string(),
                })?
            };
            
            
            ConsentMessage::GenericDisplayMessage(format!(
                "# SOME_MESSAGE",
                your_method_name_args
            ))
        }
        _ => ConsentMessage::GenericDisplayMessage(format!("Approve Ropecoin to execute {}", consent_msg_request.method)),
    };

    let metadata = ConsentMessageMetadata {
        language: "en".to_string(),
        utc_offset_minutes: None,
    };

    Ok(ConsentInfo { metadata, consent_message })
}

It is not, IMO. You’ve implemented the ICRC-21 standard, which is a prerequisite for supporting call canister requests to a third party. Which means you’ve made your canister compatible with any signer, not just OISY - i.e. standards-compliant and future-proof.

Well, this is just my personal opinion again, but, if it’s really the case, I would argue the opposite and instead ask why other wallets don’t follow the spec and have the same level of security as OISY in that regard but, I might be misunderstanding something.

Great to hear :+1:

Hi Jake
Please take my apologies for struggles you had. We are currently working on a documentation on the icrc-21 integration, but did so far focus on the connection between a client and the wallet, but not the implementation details for a backend canister to support icrc-21.
We will include some guidance on this in the document and also our online documentation. Your input really helps a lot, so thank you for this.
And if you have something we might help, don’t hesitate to contact us. We owe you one!
Best
Stefan

I wouldn’t have problem with it if there was clear documentation on it. Anyway I guess there is now thanks for your help

I’m with you on this, it also annoys me that the ICRC specifications are available in split accross repos and not available as a proper documentation website.

Note that I couldn’t answer your question about an example for ICRC-21 while you were looking to implement it because I was attending ETHCluj and wasn’t always online. Sorry about that.