# Error exchanging ICP for cycles

**URL:** <https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645>\
**Category:** Developers\
**Created:** [November 17, 2022, 12:38pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645 "2022-11-17T12:38:11Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Safik](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/safik/32/953_2.png) [@Safik](https://forum.dfinity.org/u/Safik)\
**Post date:** [November 17, 2022, 12:38pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/1 "2022-11-17T12:38:11Z")

</div>

Hi!  
After all, not all issues have been resolved by me.  
Error exchanging ICP for cycles. The transaction is stuck. I must say right away I tried to solve the problem myself for a long time.Below in details.  
Coinage code:

```auto
    public shared({caller}) func minting_cycles_this_canister(
            icp_amount: Nat) : async (TransferResult, NotifyTopUpResult){
                return await mctc(caller, icp_amount);
    };
    public shared({caller}) func mctc(
            user_caller: Principal,
            icp_amount: Nat) : async (TransferResult, NotifyTopUpResult){
        var amount = Nat64.fromNat(icp_amount);
        assert(amount > (Const.transfer_icp_fee + 100_000));
        var transfer_res: TransferResult = #Err(#TxCreatedInFuture);
        var transform_notify_res: NotifyTopUpResult = #Err(#InvalidTransaction(""));
        let time = { timestamp_nanos = Nat64.fromNat(Int.abs(Time.now())) };
        let amount_res = { e8s = amount - (Const.transfer_icp_fee + 100_000)};
        let coinage: Principal = Principal.fromText(Const.canister_nns_cycles_minting);
        let subaccount: SubAccount = Tools.principalToAccount(caller, null);
        let to_cycles = Tools.accountIdentifier(coinage, subaccount);
        try{
             transfer_res := await public_ledger.transfer({
                to = to_cycles;
                fee = { e8s = Const.transfer_icp_fee; };
                memo = 0;
                from_subaccount = null;
                // from_subaccount = ?subaccount;
                created_at_time = ?time;
                amount = amount_res;
            });
            switch(transfer_res){
                case(#Err(e)){
                    return (transfer_res, transform_notify_res);
                };
                case(#Ok(height)){

                    transform_notify_res := await public_ccn.notify_top_up({
                                block_index = height;
                                canister_id = caller;
                            });
                    return (transfer_res, transform_notify_res);
                };
            };
        }
        catch(e){ 
            return (transfer_res, transform_notify_res);
        };
    };

```

Error:

```auto
(
  variant { Ok = 4_878_415 : nat64 },
  variant {
    Err = variant {
      InvalidTransaction = "Destination account in the block (c46e6da5d5aab2239fe94051c759104885d80243c1f685f61e4089bf48b94a2b) different than in the notification (b00c86416e82dd18993ed926eaeee00a94de9aceb02d37ca9c4823076a7aef83)"
    }
  },
)

```

That is, the transaction with ICP passes, but they get stuck and do not return the exchanged cycles.

All trial transactions are stuck on [ICSCAN](https://icscan.io/account/c46e6da5d5aab2239fe94051c759104885d80243c1f685f61e4089bf48b94a2b)

-Can you tell me what’s wrong again?

-Maybe it is necessary to create a canister using an actor (rkp4c-7iaaa-aaaaa-aaaca-com) .notify\_create\_canister and only then will the coinage cycles work for the created canister?

---

<div class="post-metadata">

**Author:** ![roman-kashitsyn](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/roman-kashitsyn/32/3802_2.png) [@roman-kashitsyn](https://forum.dfinity.org/u/roman-kashitsyn)\
**Post date:** [November 17, 2022, 4:30pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/2 "2022-11-17T16:30:28Z")

</div>

> let subaccount: SubAccount = Tools.principalToAccount(caller, null);

Can I look at the `principalToAccount` function?  
[That’s how](https://github.com/dfinity/sdk/blob/3d91c1be7f1a508f9506e10f666584634a2f85a3/src/dfx/src/lib/nns_types/account_identifier.rs#L177-L185) DFX computes the subaccount for a principal:

```auto
    fn from(principal_id: &Principal) -> Self {
        let mut subaccount = [0; std::mem::size_of::<Subaccount>()];
        let principal_id = principal_id.as_slice();
        subaccount[0] = principal_id.len().try_into().unwrap();
        subaccount[1..1 + principal_id.len()].copy_from_slice(principal_id);
        Subaccount(subaccount)
    }

```

---

<div class="post-metadata">

**Author:** ![bogwar](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/bogwar/32/2161_2.png) [@bogwar](https://forum.dfinity.org/u/bogwar)\
**Post date:** [November 17, 2022, 4:41pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/3 "2022-11-17T16:41:34Z")

</div>

I think there are two issues with the code.

First, the subaccount the ICP transfer goes to needs to be an encoding of the canister id where the cycles will go to. The encoding is what Roman shared above. I think `principalToAccount` returns the default account of the principal.

The second issue is that transfers that are intended for topping up canisters need to have a specific memo field, namely `0x50555054` (there’s also a specific memo if you want to use the converted cycles to create a canister). You can see the definitions of the memos used [here](https://sourcegraph.com/github.com/dfinity/ic@05aec87/-/blob/rs/nns/cmc/src/lib.rs?L104).

---

<div class="post-metadata">

**Author:** ![Safik](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/safik/32/953_2.png) [@Safik](https://forum.dfinity.org/u/Safik)\
**Post date:** [November 17, 2022, 5:09pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/4 "2022-11-17T17:09:56Z")

</div>

Yes, of course. I immediately say there are two implementations of them, they work the same way. I had to experiment because of errors

variant 1

```auto
 public func principalToAccount(p : Principal, sa : ?[Nat8]) : [Nat8] {
        return principalBlobToAccount(Principal.toBlob(p), sa);
    };
    public func principalBlobToAccount(b : Blob, sa : ?[Nat8]) : [Nat8] { //Blob & [Nat8]
        return generate(Blob.toArray(b), sa);
    };
    private func generate(data : [Nat8], sa : ?[Nat8]) : [Nat8] {
        var _sa : [Nat8] = sa_zero;
        if (Option.isSome(sa)) {
            _sa := Option.get(sa, _sa);
            while (_sa.size() < 32){
                _sa := Array.append([0:Nat8], _sa);
            };
        };
        var hash : [Nat8] = SHA224.sha224(Array.append(Array.append(ads, data), _sa));
        var crc : [Nat8] = CRC32.crc32(hash);
        return Array.append(crc, hash);                     
    };

```

variant 2  
(where: subaccount → defaultSubaccount())

```auto
 public func defaultSubaccount() : Subaccount {
        Array.freeze<Nat8>(Array.init(32, 0 : Nat8)); 
    };

    public func accountIdentifier(principal: Principal, subaccount: Subaccount) : AccountIdentifier {
        let hash = SHA224.Digest();
        hash.write([0x0A]);
        hash.write(Blob.toArray(Text.encodeUtf8("account-id")));
        hash.write(Blob.toArray(Principal.toBlob(principal)));
        hash.write(subaccount);
        let hashSum = hash.sum();
        let crc32Bytes = beBytes(CRC32.ofArray(hashSum));
        let arr: [Nat8] = Array.append(crc32Bytes, hashSum);
        return arr ;
    };

```

---

<div class="post-metadata">

**Author:** ![Safik](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/safik/32/953_2.png) [@Safik](https://forum.dfinity.org/u/Safik)\
**Post date:** [November 17, 2022, 5:13pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/5 "2022-11-17T17:13:03Z")

</div>

> [@bogwar](#):
>
> The second issue is that transfers that are intended for topping up canisters need to have a specific memo field, namely `0x50555054` (there’s also a specific memo if you want to use the converted cycles to create a canister)

Ок, thanks you. I think this is the key to the solution.

> [@bogwar](#):
>
> I think `principalToAccount` returns the default account of the principal.

Yes it is

---

<div class="post-metadata">

**Author:** ![bogwar](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/bogwar/32/2161_2.png) [@bogwar](https://forum.dfinity.org/u/bogwar)\
**Post date:** [November 17, 2022, 5:57pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/6 "2022-11-17T17:57:08Z")

</div>

The error you are hitting is not because you don’t use the correct memo, but because the transfer does not go to the correct subaccount. Basically, you need a function which takes a canister\_id and returns a subaccount; this is the function that Roman in his answer.  
The two functions you provided take a canister\_id and a subaccount and return an account (which is not what you want).

---

<div class="post-metadata">

**Author:** ![Safik](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/safik/32/953_2.png) [@Safik](https://forum.dfinity.org/u/Safik)\
**Post date:** [November 17, 2022, 6:44pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/7 "2022-11-17T18:44:57Z")

</div>

This is excellent. Your help has yielded results! I’ll give you the code, maybe it will help someone else. Function for subaccount:

```auto
 public func principalToSubAccount(id: Principal) : [Nat8] {
        let p = Blob.toArray(Principal.toBlob(id));
        Array.tabulate(32, func(i : Nat) : Nat8 {
            if (i >= p.size() + 1) 0
            else if (i == 0) (Nat8.fromNat(p.size()))
            else (p[i - 1])
        })
    };

```

but then there was a mistake

```auto
variant { Ok = 4_880_838 : nat64 },
  variant {
    Err = variant {
      InvalidTransaction = "Intent in the block (0 == unrecognized) different than in the notification (1347768404 == TopUp)"
    }
  },

```

And a clear indication  
let coinage\_cycles\_memo = 0x50555054 : Nat64;

Fixed the problem

---

<div class="post-metadata">

**Author:** ![ragpatel8742](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/ragpatel8742/32/3828_2.png) [@ragpatel8742](https://forum.dfinity.org/u/ragpatel8742)\
**Post date:** [September 18, 2024, 6:20am UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/8 "2024-09-18T06:20:58Z")

</div>

@bogwar We passed the correct memo with the wrong endianness i.e. passed “\54\50\55\50” to use instead of “\50\55\50\54” as shown in the attached code fragment. We received the error message as `#InvalidTransaction("Destination account in the block (3d9f6eb216b2dc34d458346a4fac426fe7547c714133aa7b076febedf51c3c8e) different than in the notification (6cbd57ae110d3ee0505ae32b516d4e1514ee6e244444f37e4c8575a63db233c6)")`

We lost a significant amount of ICPs in this exercise. Is there a way to recover our lost ICPs?

 ![image](https://us1.discourse-cdn.com/flex023/uploads/dfn/original/3X/5/d/5dd91c0d8a594b51c4691ca8f30285a6f2fb3602.png)

---

<div class="post-metadata">

**Author:** ![bogwar](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/bogwar/32/2161_2.png) [@bogwar](https://forum.dfinity.org/u/bogwar)\
**Post date:** [September 18, 2024, 7:19am UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/9 "2024-09-18T07:19:20Z")

</div>

the CMC checks that the transaction has a specific format, in particular it checks that the memo field is what it expects, before proceeding with the conversion. I think that at the moment it doesn’t do refunds if this check fails so as it stands there’s not much you can do.

Let me bring this up to the team in charge of the CMC.

One possibility would be to add an endpoint for reclaiming transfers for which the conversion fails (for whatever reason). It may be tricky to implement though.

---

<div class="post-metadata">

**Author:** ![ragpatel8742](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/ragpatel8742/32/3828_2.png) [@ragpatel8742](https://forum.dfinity.org/u/ragpatel8742)\
**Post date:** [September 18, 2024, 7:23am UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/10 "2024-09-18T07:23:44Z")

</div>

@bogwar Also wanted you to review the code of icrc1\_transfer as shown in the snapshot above. I wanted to make sure we’re passing the memo & others correctly format-wise, value-wise, etc

---

<div class="post-metadata">

**Author:** ![bogwar](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/bogwar/32/2161_2.png) [@bogwar](https://forum.dfinity.org/u/bogwar)\
**Post date:** [September 18, 2024, 4:11pm UTC](https://forum.dfinity.org/t/error-exchanging-icp-for-cycles/16645/11 "2024-09-18T16:11:43Z")

</div>

As far as i know CMC doesn’t know (yet) about ICRC-1 transfers but only about the legacy ICP transfers. So you should use those to transfer the ICPs for cycle minting; that API is described here: [https://internetcomputer.org/docs/current/references/ledger#\_transferring\_tokens](https://internetcomputer.org/docs/current/references/ledger#_transferring_tokens).

Also, we’re discussing how to proceed with malformed transactions to the CMC. Hopefully I’ll be able to provide an update on this sometime soon.
