Skip to main content
1

Configure your Authentication

You’ll need to add the JWKS Endpoint from your auth provider to authenticate gasless transactions.
With this JWKS Endpoint setup, you can now run secure code in your frontend using a rotating JWT token.

Getting your JWKS Endpoint

For these common auth providers, you can get your JWKS Endpoint like this:
  1. Go to your API Keys
  2. Copy your JWKS URL and paste it into the JWKS Endpoint field in your Chipi Dashboard.
2

Get your Public Key

Now go to your API Keys and click on your project.Which will give you access to your Public Key (pk_prod_xxxx). Keep this handy as you’ll need it to initialize the SDK.
You won’t need your Secret Key (sk_prod_xxxx) for now.
3

Frontend Code to create a wallet

Since this wallet is self-custodial, there are many steps that need to be done in the frontend.Here is the code to create a wallet for your user.
Chipi supports two wallet types:
  • CHIPI (default): OpenZeppelin account with SNIP-9 session keys support
  • READY: Argent X Account v0.4.0
// External dependencies (you'll need to install these)
import CryptoJS from "crypto-js";
import {
  Account,
  CairoCustomEnum,
  CairoOption,
  CairoOptionVariant,
  CallData,
  ec,
  hash,
  num,
  RpcProvider,
  stark,
} from "starknet";

// Wallet type configuration
type WalletType = "CHIPI" | "READY";

// Class hashes for each wallet type
const WALLET_CLASS_HASHES = {
  CHIPI: "0x53f4f8791ed5bed0fddaa553d180c664e32cfaf8316bb232ae77bb08f459f2a",
  READY: "0x036078334509b514626504edc9fb252328d1a240e4e948bef8d0c08dff45927f",
};

// RPC endpoints per wallet type
const WALLET_RPC_ENDPOINTS = {
  CHIPI: "https://starknet-mainnet.public.blastapi.io/rpc/v0_7",
  READY: "https://cloud.argent-api.com/v1/starknet/mainnet/rpc/v0.7",
};

// Deployment data type
interface DeploymentData {
  class_hash: string;
  salt: string;
  unique: string;
  calldata: string[];
}

// Encryption utility
const encryptPrivateKey = (
  privateKey: string,
  password: string,
): string => {
  if (!privateKey || !password) {
    throw new Error("Private key and password are required");
  }
  return CryptoJS.AES.encrypt(privateKey, password).toString();
};

// Types (included inline)
interface WalletData {
  publicKey: string;
  encryptedPrivateKey: string;
}

interface CreateWalletParams {
  encryptKey: string;
  externalUserId: string;
  apiPublicKey: string;
  bearerToken: string;
  chain: string; // Currently only "STARKNET" is supported
  walletType?: WalletType; // Optional, defaults to "CHIPI"
}

// Backend URL constant
const BACKEND_URL = "https://api.chipipay.com/v1";

// Build constructor calldata based on wallet type
const buildConstructorCallData = (
  walletType: WalletType,
  starkKeyPubAX: string
): string[] => {
  if (walletType === "READY") {
    // Argent X Account: owner (CairoCustomEnum) + guardian (CairoOption None)
    const axSigner = new CairoCustomEnum({
      Starknet: { pubkey: starkKeyPubAX },
    });
    const axGuardian = new CairoOption<unknown>(CairoOptionVariant.None);
    return CallData.compile({
      owner: axSigner,
      guardian: axGuardian,
    });
  }

  // ChipiWallet (default): Simple OpenZeppelin account with just public_key
  return CallData.compile({
    public_key: starkKeyPubAX,
  });
};

// Main function
export const createWallet = async (
  params: CreateWalletParams
): Promise<CreateWalletResponse> => {
  try {
    const {
      encryptKey,
      apiPublicKey,
      bearerToken,
      walletType = "CHIPI" // Default to CHIPI wallet
    } = params;

    // Select RPC endpoint based on wallet type
    const rpcUrl = WALLET_RPC_ENDPOINTS[walletType];
    const provider = new RpcProvider({ nodeUrl: rpcUrl });

    // Generating the private key with Stark Curve
    const privateKeyAX = stark.randomAddress();
    const starkKeyPubAX = ec.starkCurve.getStarkKey(privateKeyAX);

    // Select class hash based on wallet type
    const accountClassHash = WALLET_CLASS_HASHES[walletType];

    // Build constructor calldata based on wallet type
    const constructorCallData = buildConstructorCallData(walletType, starkKeyPubAX);

    // Calculate future address of the account
    const publicKey = hash.calculateContractAddressFromHash(
      starkKeyPubAX,
      accountClassHash,
      constructorCallData,
      0
    );

    // Initiating Account
    const account = new Account(provider, publicKey, privateKeyAX);

    // Backend Call API to create the wallet
    const typeDataResponse = await fetch(`${BACKEND_URL}/chipi-wallets/prepare-creation`, {
      method: "POST",
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${bearerToken}`,
        'x-api-key': apiPublicKey,
      },
      body: JSON.stringify({
        publicKey,
        walletType,
      }),
    });
    const { typeData, accountClassHash: accountClassHashResponse } = await typeDataResponse.json();

    // Sign the message
    const userSignature = await account.signMessage(typeData);

    const deploymentData: DeploymentData = {
      class_hash: accountClassHashResponse,
      salt: starkKeyPubAX,
      unique: `${num.toHex(0)}`,
      calldata: constructorCallData.map((value: any) => num.toHex(value)),
    };

    const encryptedPrivateKey = encryptPrivateKey(privateKeyAX, encryptKey);

    // Call the API to save the wallet in dashboard
    const executeTransactionResponse = await fetch(`${BACKEND_URL}/chipi-wallets`, {
      method: "POST",
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${bearerToken}`,
        'x-api-key': apiPublicKey,
      },
      body: JSON.stringify({
        apiPublicKey,
        publicKey,
        walletType,
        userSignature: {
          r: (userSignature as any).r.toString(),
          s: (userSignature as any).s.toString(),
          recovery: (userSignature as any).recovery
        },
        typeData,
        encryptedPrivateKey,
        deploymentData: {
          ...deploymentData,
          salt: `${deploymentData.salt}`,
          calldata: deploymentData.calldata.map((data: any) => `${data}`),
        }
      }),
    });
    const executeTransaction = await executeTransactionResponse.json();
    console.log("Execute transaction: ", executeTransaction);

    if (executeTransaction.success) {
      return {
        success: true,
        txHash: executeTransaction.txHash,
        wallet: {
          publicKey: executeTransaction.walletPublicKey,
          encryptedPrivateKey: encryptedPrivateKey,
        } as WalletData,
      };
    } else {
      return {
        success: false,
        txHash: "",
        wallet: {
          publicKey: "",
          encryptedPrivateKey: "",
        } as WalletData,
      };
    }
  } catch (error: unknown) {
    console.error("Error detallado:", error);

    if (error instanceof Error && error.message.includes("SSL")) {
      throw new Error(
        "SSL connection error. Try using NODE_TLS_REJECT_UNAUTHORIZED=0 or verify the RPC URL"
      );
    }

    throw new Error(
      `Error creating wallet: ${
        error instanceof Error ? error.message : "Unknown error"
      }`
    );
  }
};
4

Create a Wallet

Now you can create a wallet for your user.
For the bearerToken you need to pass a valid JWT token from your auth provider.

Create a ChipiWallet (Default)

ChipiWallet is an OpenZeppelin account with SNIP-9 session keys support, perfect for gasless transactions and delegated access.
// Example usage with ChipiWallet (default):
const params: CreateWalletParams = {
  encryptKey: "user-secure-pin", // Encryption key for the private key
  externalUserId: "user-123", // Your user's unique identifier
  apiPublicKey: "pk_prod_***", // Your Chipi Pay public key
  bearerToken: "your-bearer-token", // Valid JWT token from your Auth Provider
  chain: "STARKNET", // Currently only STARKNET is supported
  // walletType: "CHIPI", // Optional - CHIPI is the default
};

createWallet(params)
  .then(response => console.log("Wallet created:", response))
  .catch(error => console.error("Error:", error));

Create a Ready Wallet

If you need Argent X compatibility:
// Example usage with Ready wallet:
const params: CreateWalletParams = {
  encryptKey: "user-secure-pin",
  externalUserId: "user-123",
  apiPublicKey: "pk_prod_***",
  bearerToken: "your-bearer-token",
  chain: "STARKNET", // Currently only STARKNET is supported
  walletType: "READY", // Explicitly use Argent X Account
};

createWallet(params)
  .then(response => console.log("Wallet created:", response))
  .catch(error => console.error("Error:", error));
Backward Compatibility: If you don’t specify walletType, the SDK will create a ChipiWallet by default. Existing integrations will continue to work without changes.