Make sure you are using Typescript v5 or above and Viem v2 or above.
Get the connected user’s address without any Library
Copy
// 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
Copy
yarn add @celo/abis @celo/identity viem@1
Check cUSD Balance of an address
Copy
const { getContract, formatEther, createPublicClient, http } = require("viem");
const { celo } = require("viem/chains");
const { stableTokenABI } = require("@celo/abis");
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
async function checkCUSDBalance(publicClient, address) {
let StableTokenContract = getContract({
abi: stableTokenABI,
address: STABLE_TOKEN_ADDRESS,
publicClient,
});
let balanceInBigNumber = await StableTokenContract.read.balanceOf([
address,
]);
let balanceInWei = balanceInBigNumber.toString();
let balanceInEthers = formatEther(balanceInWei);
return balanceInEthers;
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
}); // Mainnet
let balance = await checkCUSDBalance(publicClient, address); // In Ether unit
Check If a transaction succeeded
Copy
const { createPublicClient, http } = require("viem");
const { celo } = require("viem/chains");
async function checkIfTransactionSucceeded(publicClient, transactionHash) {
let receipt = await publicClient.getTransactionReceipt({
hash: transactionHash,
});
return receipt.status === "success";
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
}); // Mainnet
let transactionStatus = await checkIfTransactionSucceeded(
publicClient,
transactionHash
);
Estimate Gas for a transaction (in Celo)
Copy
const { createPublicClient, http } = require("viem");
const { celo } = require("viem/chains");
async function estimateGas(publicClient, transaction, feeCurrency = "") {
return await publicClient.estimateGas({
...transaction,
feeCurrency: feeCurrency ? feeCurrency : "",
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
let gasLimit = await estimateGas(publicClient, {
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
});
Estimate Gas for a transaction (in cUSD)
Copy
const { createPublicClient, http } = require("viem");
const { celo } = require("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";
let gasLimit = await estimateGas(
publicClient,
{
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
},
STABLE_TOKEN_ADDRESS
);
Estimate Gas Price for a transaction (in Celo)
Copy
const { createPublicClient, http } = require("viem");
const { celo } = require("viem/chains");
async function estimateGasPrice(publicClient, feeCurrency = "") {
return await publicClient.request({
method: "eth_gasPrice",
params: feeCurrency ? [feeCurrency] : [],
});
}
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
let gasPrice = await estimateGasPrice(publicClient);
Estimate Gas Price for a transaction (in cUSD)
Copy
const { createPublicClient, http } = require("viem");
const { celo } = require("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";
let gasPrice = await estimateGasPrice(publicClient, STABLE_TOKEN_ADDRESS);
Calculate cUSD to be spent for transaction fees
Copy
const { createPublicClient, http, formatEther } = require("viem");
const { celo } = require("viem/chains");
const publicClient = createPublicClient({
chain: celo,
transport: http(),
});
const STABLE_TOKEN_ADDRESS = "0x765DE816845861e75A25fCA122bb6898B8B1282a";
// `estimateGas` implemented above
let gasLimit = await estimateGas(
publicClient,
{
account: "0x8eb02597d85abc268bc4769e06a0d4cc603ab05f",
to: "0x4f93fa058b03953c851efaa2e4fc5c34afdfab84",
value: "0x1",
data: "0x",
},
STABLE_TOKEN_ADDRESS
);
// `estimateGasPrice` implemented above
let gasPrice = await estimateGasPrice(publicClient, STABLE_TOKEN_ADDRESS);
let transactionFeesInCUSD = formatEther(gasLimit * hexToBigInt(gasPrice));
Resolve Minipay phone numbers to Addresses
Install the@celo/identity
package:
Copy
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).Copy
import { createClient } from "viem";
import { celoAlfajores } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// The issuer is the account that is registering the attestation
let ISSUER_PRIVATE_KEY = "YOUR_ISSUER_PRIVATE_KEY";
// Create Alfajores viem client with the issuer private key
const viemClient = createClient({
account: privateKeyToAccount(ISSUER_PRIVATE_KEY),
transport: http(),
chain: celoAlfajores,
});
// 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
Copy
import { OdisUtils } from "@celo/identity";
import { AuthSigner } 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.ALFAJORES
);
// Check existing quota on issuer account
const { remainingQuota } = await OdisUtils.Quota.getPnpQuotaStatus(
issuerAddress,
authSigner,
serviceContext
);
// If needed, approve and send payment to OdisPayments to get quota for ODIS
if (remainingQuota < 1) {
const stableTokenContract = await kit.contracts.getStableToken();
const odisPaymentsContract = await kit.contracts.getOdisPayments();
const ONE_CENT_CUSD_WEI = 10000000000000000;
await stableTokenContract
.increaseAllowance(odisPaymentsContract.address, ONE_CENT_CUSD_WEI)
.sendAndWaitForReceipt();
const odisPayment = await odisPaymentsContract
.payInCUSD(issuerAddress, ONE_CENT_CUSD_WEI)
.sendAndWaitForReceipt();
}
Step 3: Derive the obfuscated identifier
Get the obfuscated identifier from the plaintext identifier by querying ODIS:Copy
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:Copy
const attestations = await federatedAttestationsContract.lookupAttestations(
obfuscatedIdentifier,
[issuerAddress] // Trusted issuers
);
console.log(attestations.accounts);
Request an ERC20 token transfer
Copy
import { createWalletClient, custom } from 'viem'
// import { celo } from 'viem/chains'
import { celoAlfajores } from 'viem/chains'
const client = createWalletClient({
chain: celoAlfajores,
// chain: celo,
transport: custom(window.ethereum!)
})
const publicClient = createPublicClient({
chain: celoAlfajores,
// chain: celo,
transport: http()
})
async function requestTransfer(tokenAddress, transferValue, tokenDecimals) {
let hash = await client.sendTransaction({
to: tokenAddress,
// to: '0x765DE816845861e75A25fCA122bb6898B8B1282a' // cUSD (Mainnet)
// to: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C' // USDC (Mainnet)
// to: '0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e' // USDT (Mainnet)
data: encodeFunctionData({
abi: stableTokenAbi, // Token ABI can be fetched from Explorer.
functionName: "transfer",
args: [
receiverAddress,
// Different tokens can have different decimals, cUSD (18), USDC (6)
parseUnits(`${Number(transferValue)}`, tokenDecimals),
],
}),
// If the wallet is connected to a different network then you get an error.
chain: celoAlfajores,
// chain: celo,
});
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.
}
}