Photo by Michal Matlon on Unsplash
Until there is a library (e.g. cmc-js 👀) that facilitates the conversion of ICP to cycles in JavaScript, the following tutorial may help you implement such a transformation.
Introduction
Long story short, to convert ICP to cycles you need two things:
- the exchange rate to transform the values which is returned by the cycles-minting canister (CMC) through the function
get_icp_xdr_conversion_rate
- a function that, well, does the conversion
Dependencies
To query the CMC in JavaScript easily you need agent-js (and its peer dependencies). In addition, because following solution runs in a NodeJS context and not in a browser, node-fetch is required as well to provide previous library a way to perform XMLHttpRequest
.
npm i node-fetch @dfinity/agent @dfinity/candid @dfinity/principal
Candid
The description of the interface of the cycles-minting canister has to be used to initialize the communication channel.
The CMC candid file can be downloaded on the Internet Computer repo. Its binding files ( .idl.js
, .d.ts
etc. ) can be generated with the didc command line tool or Canlista (a GUI version of the tool) but, if you want to spare yourself such operations, you can download all these files directly from the backend repo of my project Papyrs.
In following code snippets I use these particular bindings.
XDR conversion rate
On mainnet, the canister ID of the CMC is rkp4c-7iaaa-aaaaa-aaaca-cai
. This ID should be used to instantiate the actor in order to query get_icp_xdr_conversion_rate
which returns xdr_permyriad_per_icp
, the actual conversion rate in XDR we are looking for.
1 XDR being equal to 1 Trillion cycles, I convert the result to trillion ratio before returning it.
import pkgAgent from "@dfinity/agent";
import fetch from "node-fetch";
import { idlFactory } from "./cmc/cmc.utils.did.mjs";
const { HttpAgent, Actor } = pkgAgent;
const icpXdrConversionRate = async () => {
const agent = new HttpAgent({ fetch, host: "https://ic0.app" });
const actor = Actor.createActor(idlFactory, {
agent,
canisterId: "rkp4c-7iaaa-aaaaa-aaaca-cai"
});
const {
data: { xdr_permyriad_per_icp }
} = await actor.get_icp_xdr_conversion_rate();
const CYCLES_PER_XDR = BigInt(1_000_000_000_000);
// Return conversion rate in trillion ratio
return (xdr_permyriad_per_icp * CYCLES_PER_XDR) / BigInt(10_000);
};
Conversion ICP to cycles
We aim to convert readable numbers. That is why, we first need a small utility that maps number
to BigInt
(because the conversion function will process such types).
const E8S_PER_ICP = BigInt(100000000);
const toBigint = (amount) => {
const [integral, fractional] = `${amount}`.split(".");
if ((fractional ?? "0").length > 8) {
throw new Error("More than 8 decimals not supported.");
}
return BigInt(integral ?? 0) * E8S_PER_ICP + BigInt((fractional ?? "0").padEnd(8, "0"));
};
A few samples of above function result (number -> bigint):
1
->100000000n
1.2
->120000000n
34.456
->3445600000n
Finally, to effectively convert the ICP to cycles, we can implement a cross-multiplication with the conversion rate.
const icpToCycles = async ({ icp, conversionRate }) => {
const e8ToCycleRatio = conversionRate / E8S_PER_ICP;
return toBigint(icp) * e8ToCycleRatio;
};
Demo
Put together in a small demo, e.g. above functions can be chained to log the result of the conversion to the console.
const convertICPToCycles = async (icp) => {
const conversionRate = await icpXdrConversionRate();
const cycles = await icpToCycles({ icp, conversionRate });
const ONE_TRILLION = BigInt(1000000) * BigInt(1000000);
console.log(`${icp} ICP equals ${Number(cycles) / Number(ONE_TRILLION)} (${cycles}) cycles`);
};
await convertICPToCycles(123.56);
If run in a command line, the sample should output such a result:
❯ node index.mjs
123.56 ICP equals 652.0879 (652087900000000) cycles
Conclusion
I hope this small tutorial will be useful. If you have any idea of improvements, let me know!
To infinity and beyond
David