> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celo.org/llms.txt
> Use this file to discover all available pages before exploring further.

# MiniPay Code Library

Snippets of code that can be used to implement flows inside MiniPay

<Warning>
  Make sure you are using Typescript v5 or above and Viem v2 or above.
</Warning>

## Get the connected user's address without any Library

```js theme={null}
// 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
```

To use the code snippets below, install the following packages:

<CodeGroup>
  ```bash yarn theme={null}
  yarn add @celo/abis @celo/identity viem@2
  ```

  ```bash npm theme={null}
  npm install @celo/abis @celo/identity viem@2
  ```
</CodeGroup>

## Check USDm Balance of an address

```js theme={null}
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

```js theme={null}
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)

```js theme={null}
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)

```js theme={null}
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)

```js theme={null}
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)

```js theme={null}
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

```js theme={null}
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:

```bash theme={null}
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).

```js theme={null}
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

```js theme={null}
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:

```js theme={null}
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:

```js theme={null}
const attestations = await federatedAttestationsContract.lookupAttestations(
  obfuscatedIdentifier,
  [issuerAddress] // Trusted issuers
);

console.log(attestations.accounts);
```

## Request an ERC20 token transfer

<Warning>
  USDT and USDC on Celo use **6 decimals**, not 18. Pass `tokenDecimals` as `6` when
  transferring either token. Using `18` will send 1,000,000,000,000× more than intended.
  USDm uses 18 decimals.
</Warning>

```js theme={null}
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 token addresses and their decimals:
    // USDm: '0x765DE816845861e75A25fCA122bb6898B8B1282a'  (18 decimals)
    // USDC: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C'  (6 decimals)
    // USDT: '0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e'  (6 decimals)
    data: encodeFunctionData({
      abi: stableTokenABI, // Token ABI from @celo/abis
      functionName: "transfer",
      args: [
        receiverAddress,
        // USDm uses 18 decimals; USDC and USDT use 6 decimals
        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.
  }
}
```
