Computing Deposit Address for SNS Token

I’ve been trying to implement some functionality similar to OpenChat for another site I have called FootballGod (not OpenFPL) but it requires FPL to play. I need to show the deposit address for the user to send FPL to from the NNS, but I’m not sure how to compute this address.

OpenChat have 1 canister per user, so the address you send to is the canister id which is also the user’s principal id. But FootballGod is just a really small prediction data set, 30 numbers, an entire canister for a user would be overkill. I was thinking the deposit address would be a combination of:

{ owner: football_god_backend, subaccount: caller_principical_id }

This would create the entry in the OpenFPL SNS Ledger canister using that combination. Then within the FootballGod backend I could get the users balance using the same combination…

However when I send FPL from my local SNS to the principal id within my app and then attempt to get the balance of the account from the above computed FPL SNS ledger entry, it returns 0.

Principal is 29 bytes long. Is it being padded to 32 bytes before calling the ledger?

Anything can be used as sub-account, but to be sure you can sha256 it, so the result is always 32 bytes long.

1 Like

Hi Victor,

Thanks for getting back to me. So here is the process I’ve been following:

The user logs into FootballGod and gets their principal ID as they would in any other app. They then go to the NNS and send some FPL to that principal ID. So locally I use the sns-testing setup and send some FPL to the principal:

It’s then within the app I want to see that balance. So I call this:

public func getUserAccountBalance(defaultAccount : Principal, user : Principal) : async Nat {
  let balance = await ledger.icrc1_balance_of({owner = defaultAccount; subaccount = ?Account.principalToSubaccount(user)});
  return balance;

With the default account being my football_god_backend canister:

fpl_ledger.getUserAccountBalance(Principal.fromActor(Self), caller);

and the caller is converted to a subaccount using the helper function:

public func principalToSubaccount(principal : Principal) : Blob {
let idHash = SHA224.Digest();
let hashSum = idHash.sum();
let crc32Bytes = beBytes(CRC32.ofArray(hashSum));

let buffer = Buffer.fromArray<Nat8>(crc32Bytes);
for (x in hashSum.vals()) {

let blob = Blob.fromArray(Buffer.toArray(buffer));

return blob;


Unforuntately I get a zero balance and not the tokens sent from the local NNS.

Any help appreciated.

If the destination address used by the user at the NNS is the same as the address generated by getUserAccountBalance() - where owner is football_god_backend’s canister id, not the frontend’s canister id -, it should be working.


So I was able to get the account balance from just using the caller and null. Really I was confused about who owned the FPL after transferring to FootballGod, just because I use a FootballGod principal ID the user still has the funds and I can’t then use them for things after sending 100FPL like entering them into the competition directly.

So the modified path I’m taking is to send it to the principal id for FootballGod from the NNS but when a user enters the game I will have to do the follow:

  • From the frontend, have the user approve a payment with @dfinity/ledger, sending the FPL entry fee to the account { owner: football_god_backend_canister_id, subaccount: caller }.
  • After the successful payment, call the backend play() function, checking for the balance for { owner: football_god_backend_canister_id, subaccount: caller } and transferring it to { owner: football_god_backend_canister_id, subaccount: null } (i.e the prize pot).
  • After the successful transfer into the prize pot I can then create the users entry record into the game.

I just seem to be hitting an issue with installing the npm package for ledger, it conflicts with @dfinity/agent:

Think I just need to update @dfinity/agent to 1.3

Does this mean that we can’t send SNS tokens from the NNS to an account? Do you know why?

I think you can if you use the textual encoding: ICRC-1/standards/ICRC-1/ at main · dfinity/ICRC-1 · GitHub

It would look something like:


which would be

owner = “k2t6j-2nvnp-4zjm3-25dtz-6xhaa-c7boj-5gayf-oj3xs-i43lp-teztq-6ae”, subaccount = opt blob “\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\01”

The NNS is not throwing an error when I put this address in, but I haven’t tried submitting a transaction.


The NNS is not throwing an error when I put this address in, but I haven’t tried submitting a transaction.

It works, thanks!