Snippets of code that can be used to implement flows inside MiniPayDocumentation Index
Fetch the complete documentation index at: https://docs.celo.org/llms.txt
Use this file to discover all available pages before exploring further.
Make sure you are using Typescript v5 or above and Viem v2 or above.
Get the connected user’s address without any Library
// The code must run in a browser environment and not in node environment
if (window && window.ethereum) {
// User has a injected wallet
if (window.ethereum.isMiniPay) {
// User is using Minipay
// Requesting account addresses
let accounts = await window.ethereum.request({
method: "eth_requestAccounts",
params: [],
});
// Injected wallets inject all available addresses,
// to comply with API Minipay injects one address but in the form of array
console.log(accounts[0]);
}
// User is not using MiniPay
}
// User does not have a injected wallet
yarn add @celo/abis @celo/identity viem@2
Check USDm Balance of an address
import { getContract, formatEther, createPublicClient, http } from "viem";
import { celo } from "viem/chains";
import { stableTokenABI } from "@celo/abis";
// USDm address on Celo mainnet
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
async function checkUSDmBalance(publicClient, address) {
const StableTokenContract = getContract({
abi: stableTokenABI,
address: STABLE_TOKEN_ADDRESS,
client: publicClient,
});
const balanceInBigNumber = await StableTokenContract.read.balanceOf([
address,
]);
const balanceInWei = balanceInBigNumber.toString();
const balanceInEthers = formatEther(balanceInWei);
return balanceInEthers;
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
}); // Mainnet
const balance = await checkUSDmBalance(publicClient, address); // In Ether unit
Check If a transaction succeeded
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
async function checkIfTransactionSucceeded(publicClient, transactionHash) {
const receipt = await publicClient.getTransactionReceipt({
hash: transactionHash,
});
return receipt.status === "success";
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
}); // Mainnet
const transactionStatus = await checkIfTransactionSucceeded(
publicClient,
transactionHash
);
Estimate Gas for a transaction (in Celo)
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
async function estimateGas(publicClient, transaction, feeCurrency = "") {
return await publicClient.estimateGas({
...transaction,
feeCurrency: feeCurrency ? feeCurrency : "",
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const gasLimit = await estimateGas(publicClient, {
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
});
Estimate Gas for a transaction (in USDm)
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
async function estimateGas(publicClient, transaction, feeCurrency = "") {
return await publicClient.estimateGas({
...transaction,
feeCurrency: feeCurrency ? feeCurrency : "",
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
const gasLimit = await estimateGas(
publicClient,
{
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
},
STABLE_TOKEN_ADDRESS
);
Estimate Gas Price for a transaction (in Celo)
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
async function estimateGasPrice(publicClient, feeCurrency = "") {
return await publicClient.request({
method: "eth_gasPrice",
params: feeCurrency ? [feeCurrency] : [],
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const gasPrice = await estimateGasPrice(publicClient);
Estimate Gas Price for a transaction (in USDm)
import { createPublicClient, http } from "viem";
import { celo } from "viem/chains";
async function estimateGasPrice(publicClient, feeCurrency = "") {
return await publicClient.request({
method: "eth_gasPrice",
params: feeCurrency ? [feeCurrency] : [],
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
const gasPrice = await estimateGasPrice(publicClient, STABLE_TOKEN_ADDRESS);
Calculate USDm to be spent for transaction fees
import { createPublicClient, http, formatEther, fromHex } from "viem";
import { celo } from "viem/chains";
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
// `estimateGas` implemented above
const gasLimit = await estimateGas(
publicClient,
{
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
},
STABLE_TOKEN_ADDRESS
);
// `estimateGasPrice` implemented above
const gasPrice = await estimateGasPrice(publicClient, STABLE_TOKEN_ADDRESS);
// Convert hex gas price to BigInt and calculate fees
const gasPriceBigInt = fromHex(gasPrice, "bigint");
const transactionFeesInUSDm = formatEther(gasLimit * gasPriceBigInt);
Resolve Minipay phone numbers to Addresses
Install the@celo/identity package:
npm install @celo/identity
Step 1: Set up your issuer
The issuer is the account registering attestations. When a user requests attestation registration, verify they own the identifier (e.g., SMS verification for phone numbers).import { createWalletClient, http } from "viem";
import { celoSepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// The issuer is the account that is registering the attestation
const ISSUER_PRIVATE_KEY = "YOUR_ISSUER_PRIVATE_KEY";
// Create Celo Sepolia viem client with the issuer private key
const viemClient = createWalletClient({
account: privateKeyToAccount(ISSUER_PRIVATE_KEY),
transport: http(),
chain: celoSepolia,
});
// Information provided by user, issuer should confirm they own the identifier
const userPlaintextIdentifier = "+12345678910";
const userAccountAddress = "0x000000000000000000000000000000000000user";
// Time at which issuer verified the user owns their identifier
const attestationVerifiedTime = Date.now();
Step 2: Check and top up ODIS quota
import { OdisUtils } from "@celo/identity";
import { AuthSigner } from "@celo/identity/lib/odis/query";
import { OdisContextName } from "@celo/identity/lib/odis/query";
// authSigner provides information needed to authenticate with ODIS
const authSigner: AuthSigner = {
authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY,
sign191: ({ message, account }) => viemClient.signMessage({ message, account }),
};
// serviceContext provides the ODIS endpoint and public key
const serviceContext = OdisUtils.Query.getServiceContext(
OdisContextName.CELO_SEPOLIA
);
// Check existing quota on issuer account
const issuerAddress = viemClient.account.address;
const { remainingQuota } = await OdisUtils.Quota.getPnpQuotaStatus(
issuerAddress,
authSigner,
serviceContext
);
// If needed, approve and send payment to OdisPayments to get quota for ODIS
// Note: This example uses viem. For contract interactions, use getContract from viem
if (remainingQuota < 1) {
// Use viem's getContract to interact with stable token and ODIS payments contracts
// Implementation depends on your specific contract setup
}
Step 3: Derive the obfuscated identifier
Get the obfuscated identifier from the plaintext identifier by querying ODIS:const { obfuscatedIdentifier } = await OdisUtils.Identifier.getObfuscatedIdentifier(
userPlaintextIdentifier,
OdisUtils.Identifier.IdentifierPrefix.PHONE_NUMBER,
issuerAddress,
authSigner,
serviceContext
);
Step 4: Look up account addresses
Query the FederatedAttestations contract to look up account addresses owned by an identifier:const attestations = await federatedAttestationsContract.lookupAttestations(
obfuscatedIdentifier,
[issuerAddress] // Trusted issuers
);
console.log(attestations.accounts);
Request an ERC20 token transfer
import { createWalletClient, createPublicClient, custom, http, encodeFunctionData, parseUnits } from "viem";
import { celo, celoSepolia } from "viem/chains";
import { stableTokenABI } from "@celo/abis";
const walletClient = createWalletClient({
chain: celoSepolia, // For testnet
// chain: celo, // For mainnet
transport: custom(window.ethereum!),
});
const publicClient = createPublicClient({
chain: celoSepolia, // For testnet
// chain: celo, // For mainnet
transport: http(),
});
async function requestTransfer(tokenAddress, transferValue, tokenDecimals, receiverAddress) {
const hash = await walletClient.sendTransaction({
to: tokenAddress,
// Mainnet addresses:
// USDm: '0x765DE816845861e75A25fCA122bb6898B8B1282a'
// USDC: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C'
// USDT: '0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e'
data: encodeFunctionData({
abi: stableTokenABI, // Token ABI from @celo/abis
functionName: "transfer",
args: [
receiverAddress,
// Different tokens can have different decimals, USDm (18), USDC (6)
parseUnits(`${Number(transferValue)}`, tokenDecimals),
],
}),
});
const transaction = await publicClient.waitForTransactionReceipt({
hash, // Transaction hash that can be used to search transaction on the explorer.
});
if (transaction.status === "success") {
// Do something after transaction is successful.
} else {
// Do something after transaction has failed.
}
}