Background
We’re developing a system where we need to generate a new subaccount for every new payment created. The idea is to use the orderId to generate the subaccount, ensuring that each order gets a unique payment address. This approach should make it universal for any wallet to pay.
Current Implementation
Here’s the relevant part of our Motoko code:
private func generateSubaccount(orderId : Nat32) : Blob {
let orderIdBytes = nat32ToBytes(orderId);
let paddingSize : Nat = 32 - orderIdBytes.size();
let padding = Array.tabulate<Nat8>(paddingSize, func(_ : Nat) : Nat8 { 0 });
let subaccountBytes = Array.append<Nat8>(padding, orderIdBytes);
Blob.fromArray(subaccountBytes);
};
private func nat32ToBytes(n : Nat32) : [Nat8] {
[
Nat8.fromNat(Nat32.toNat((n >> 24) & 0xFF)),
Nat8.fromNat(Nat32.toNat((n >> 16) & 0xFF)),
Nat8.fromNat(Nat32.toNat((n >> 8 & 0xFF)),
Nat8.fromNat(Nat32.toNat(n & 0xFF)),
];
};
public shared (msg) func createOrder(amount : Nat) : async Result.Result<Order, Text> {
// ... (other code)
let orderId = await generateRandomOrderId();
let subaccount = generateSubaccount(orderId);
let newOrder : Order = {
id = orderId;
amount = amount;
paymentAddress = subaccount;
status = #Pending;
timestamp = Time.now();
};
// ... (rest of the function)
};
And here’s how we’re handling it on the frontend:
Javascript:
try {
const actor: any = await window.ic.plug.createActor({
canisterId: "CanisterId",
interfaceFactory: idlFactoryB,
});
const orderResult = await actor.createOrder(BigInt(1001));
console.log("Order Result:", orderResult);
const order = orderResult.ok;
const paymentPrincipal = Principal.fromUint8Array(order.paymentAddress);
console.log("Payment Address (Text):", paymentPrincipal.toText());
console.log("Payment Address (Hex):", paymentPrincipal.toHex());
console.log("orderResult", orderResult);
} catch (error) {
console.log("error", error);
}
The Problem
We’re facing an error where we’re getting a long address format that isn’t supported. Specifically, when we convert the payment address to text on the frontend, we’re getting an address like this:
mtt3j-jyaaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-anknp-5im
This format seems to be incorrect for a subaccount or payment address.
Questions
-
Is our approach to generating subaccounts from orderId correct? If not, what would be a better way?
-
How can we ensure that the generated subaccount is in a format that’s compatible with IC ledger accounts?
-
Are we missing any steps in converting the subaccount to a valid payment address?
-
Is there a standard way to generate unique subaccounts for payments in Motoko that we should be using instead?
-
Why are we getting this long principal-like address when converting the subaccount to text, and how can we get the correct format?
-
Should we be using Principal.fromUint8Array on the frontend, or is there a more appropriate way to handle the subaccount blob?
## Additional Context
-
We’re using a Nat32 for the orderId.
-
The generateSubaccount function is creating a 32-byte Blob, which we thought would be the correct format for a subaccount.
-
We’re not sure if we need to do additional processing to turn this subaccount into a valid payment address.
-
On the frontend, we’re using the Principal class to convert the subaccount blob to text, which might not be the correct approach.
Any insights, code examples, or explanations would be greatly appreciated.
Thank you,
Blockbolt