Skip to main content
1

Configure your Authentication

You’ll need to configure your auth provider’s JWKS endpoint in the Chipi Dashboard. The dashboard quickstart detects your auth provider and walks you through the setup.
With this JWKS Endpoint setup, you can now run secure code in your frontend using a rotating JWT token.

Common auth providers

The dashboard auto-configures JWKS when you provide your Clerk Secret Key.
2

Get your API Keys

Go to the Chipi Dashboard and open your project. The quickstart shows your .env snippet with both keys ready to copy.You’ll need:
  • Public Key (pk_prod_xxxx) — used client-side
  • Secret Key (sk_prod_xxxx) — used by the server-side ChipiProvider
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.