> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Rain-Managed Programs

> Rain-Managed programs simplify collateral management by handling smart contract operations for users. Before spending on Rain, customers must deposit collateral into their assigned contracts.

This guide covers how to:

* [Locate a customer’s contract](#locate-a-customer’s-smart-contract)
* [Check their credit limit](#check-a-customer’s-credit-limit)
* [Manage admin access](#manage-admin-access)
* [Process collateral withdrawals](#process-a-collateral-withdrawal)

## Locate a customer’s smart contract

To enable spending on Rain, customers must deposit collateral into their designated smart contracts. You can retrieve the contract details, including supported tokens, by calling the [get issuing user contracts](/reference/contracts/get-smart-contract-information-for-a-user) endpoint.

Provide the relevant information retrieved from this endpoint to your customers so they can transfer collateral to the given address on the specified blockchain.

## Check a customer’s credit limit

Once the smart contract contains collateral, the customer's credit limit will be updated within minutes. You can check their credit balance and spending limits using the [get issuing user balances](/reference/balances/get-a-userss-credit-balances) endpoint.

This endpoint will provide:

* The customer's current credit limit
* Any outstanding charges on their account
* Any balances that are due

## Manage admin access

To approve collateral withdrawals, a wallet address must have admin access to the customer's smart contract. By default, the wallet used during [customer signup](/docs/signing-up-a-customer) is an admin of the contract, however, additional admins can be added.

### Update a user's wallet address

If you use a Rain-Managed program, you can [update a user's EVM wallet address](/reference/users/update-a-user) even when they have existing collateral contracts on EVM chains. The new wallet must already be an on-chain admin on all of the user's EVM collateral contracts.

To update the wallet address:

1. Add the new wallet address as an admin on all EVM collateral contracts for the user
2. Call `PATCH /v1/issuing/users/{userId}` with the new `walletAddress`

If the new wallet is not an admin on all contracts, the API returns a 423 Locked error indicating which chains failed verification.

<Warning>
  Partner-Managed tenants cannot update wallet addresses when contracts have been deployed for their program. The API returns a 423 Locked error in this case.
</Warning>

## \[Sandbox] Mint Test Tokens for Collateral

For testing collateral, you can mint `rUSD`, a token created for testing purposes. This token can be used as collateral to simulate funding scenarios. See [supported networks and minting process](/docs/sandbox-mint-test-tokens-for-collateral) in resources tab.

## Process a collateral withdrawal

Only an admin wallet on the smart contract can withdraw collateral. However, withdrawals can be made to any address.

### Step 1: Request a withdrawal signature

Use the [get user withdrawal signature](/reference/signatures/get-withdrawal-signature-for-a-user) endpoint.

* Get the `token` info from the[ get issuing user contracts](/reference/contracts/get-smart-contract-information-for-a-user) response.
* If the response status is `pending`, wait the specified number of seconds before retrying.

### Step 2: Execute the withdrawal transaction

Use the `signature` to call `withdrawAsset` on the smart contract controller. You'll need the following parameters:

<CodeGroup>
  ```ts withdrawAsset-params.ts theme={null}
  proxyAddress: string // the proxy address of the user's smart contract
  token: string // the token to be withdrawn
  amount: number // amount of token to be withdrawn
  recipientAddress: string // withdrawal address
  expiresAt: number // value returned from signature endpoint, in unix second format
  salt: Buffer // signature salt
  signature: string // signature
  ```
</CodeGroup>

### Step 3: Execute with `ethers.js`

<CodeGroup>
  ```ts executeWithdrawal.ts theme={null}
  const controllerContract = new ethers.Contract(
    controllerAddress,
    controllerContractAbi,
    ethersProvider.getSigner(),
  );

  await controllerContract.withdrawAsset(
    proxyAddress,
    token,
    amount,
    recipientAddress,
    new Date(expiresAt).getTime() / 1000,
    new Uint8Array(salt),
    signature,
  );
  ```
</CodeGroup>

## Code examples

### V1 Contracts

This example uses the Issuing API and V1 contracts to request a signature and execute the transaction:

<CodeGroup>
  ```ts fetchV1Signature.ts theme={null}
  import axios from "axios";
  import { ethers } from "ethers";
  // ABI Interface for V1 Collateral Controller Contract
  // Can be found at https://etherscan.io/address/0xE5D3d7da4b24bc9D2FDA0e206680CD8A00C0FeBD#code
  import { RainCollateralControllerInterface } from "../lib/utils/abis/v1/RainCollateralController";
  import dotenv from "dotenv";
  import { Services } from "../lib/utils/services";
  dotenv.config();

  type FetchV1SignatureOpts = {
    userId: string; // Must be consumer issuing to use user withdraw endpoint
    apiKey: string;
    token: string; // Token to withdraw
    amount: string; // Who much to withdraw
    recipientAddress: string; // Who to give the asset to
    chainId: string; // Which chain the contracts reside
    controllerAddress: string; // controller contract of collateral contract
  };

  const BASE_URL = "https://api-dev.rain.xyz";

  const main = async ({
    userId,
    apiKey,
    token,
    amount,
    recipientAddress,
    chainId,
    controllerAddress,
  }: FetchV1SignatureOpts) => {
    // get chain provider - this can be any provider for the requested chain
    const chain = (await Services.chains()).getById(chainId);
    /**
     * Setup signer to send transaction
     * @dev this should be the admin of the collateral contract
     */
    const signerPk = process.env.COLLATERAL_ADMIN_PK;
    if (!signerPk) {
      throw new Error("No signer key provided");
    }
    const signer = new ethers.Wallet(signerPk).connect(chain.fallbackProvider);
    const adminAddress = await signer.getAddress();

    //build API request
    const baseUrl = `${BASE_URL}/v1/issuing/users/${userId}/signatures/withdrawals`;

    const params = {
      token,
      amount,
      recipientAddress,
      adminAddress,
      chainId,
    };
    // request signature with api key
    const signatureResponse = await axios.get(baseUrl, {
      headers: {
        "Api-Key": apiKey,
      },
      params,
    });

    // setup parameters from response
    const signature = await signatureResponse.data;
    const [
      collateralProxy,
      assetAddress,
      amountInCents,
      recipient,
      expiresAt,
      executorPublisherSalt,
      executorPublisherSig,
    ] = signature.parameters;

    // Get coordinator & withdrawAsset interface
    const coordinatorContract = new ethers.Contract(controllerAddress, RainCollateralControllerInterface).connect(signer);
    const withdrawAsset = coordinatorContract.getFunction("withdrawAsset");

    // build transaction input
    const functionInputs = [
      collateralProxy,
      assetAddress,
      amountInCents,
      recipient,
      expiresAt,
      Buffer.from(executorPublisherSalt, "base64"),
      executorPublisherSig,
    ];

    // send withdrawAsset transaction
    await withdrawAsset(...functionInputs);
  };
  ```
</CodeGroup>

### V2 Contracts - EVM

V2 contracts require an additional `adminSignature` and `adminSalt`, generated by the contract admin: For V2, we renamed the `controllerAddress` contract to `coordinatorAddress` which can be obtained from the [get issuing user contracts](/reference/contracts/get-smart-contract-information-for-a-user) response.

<CodeGroup>
  ```ts fetchV2Signature.ts theme={null}
  import axios from "axios";
  import { ethers, randomBytes, Signer } from "ethers";
  // ABI Interface for V2 Collateral Coordinator Contract
  import { CoordinatorInterface } from "../lib/utils/abis/v2/Coordinator";
  // Can be found at https://snowtrace.io/address/0xE09916E8777cf88E634578C3875CF54d20769fAa/contract/43114/code
  import { CollateralInterface } from "../lib/utils/abis/v2/Collateral";
  // Can be found at https://snowtrace.io/address/0xbc2F19427EE4224a561Bc04bF16C829B967beB11/contract/43114/code
  import dotenv from "dotenv";
  import { Services } from "../lib/utils/services";
  dotenv.config();

  type FetchV2SignatureOpts = {
    userId: string; // Must be consumer issuing to use user withdraw endpoint
    apiKey: string;
    token: string; // Token to withdraw
    amount: string; // Who much to withdraw
    adminAddress: string; // Wallet address that is the collateral contract admin
    recipientAddress: string; // Who to give the asset to
    chainId: string; // Which chain the contracts reside
    coordinatorAddress: string; // coordinator contract of collateral contract
  };

  const BASE_URL = "https://api-dev.rain.xyz";

  const main = async ({
    userId,
    apiKey,
    token,
    amount,
    adminAddress,
    recipientAddress,
    chainId,
    coordinatorAddress,
  }: FetchV2SignatureOpts) => {
    // get chain - this can be any provider for the requested chain
    const chain = (await Services.chains()).getById(chainId);
    /**
     * Setup signer to send transaction
     * @dev this should be the admin of the collateral contract
     */
    const signerPk = process.env.COLLATERAL_ADMIN_PK;
    if (!signerPk) {
      throw new Error("No signer key provided");
    }
    const signer = new ethers.Wallet(signerPk).connect(chain.fallbackProvider);

    //build API request
    const baseUrl = `${BASE_URL}/v1/issuing/users/${userId}/signatures/withdrawals`;
    const params = {
      token,
      amount,
      adminAddress,
      recipientAddress,
      chainId,
    };
    // request signature with api key
    const signatureResponse = await axios.get(baseUrl, {
      headers: {
        "Api-Key": apiKey,
      },
      params,
    });

    // setup parameters from response
    const signature = await signatureResponse.data;
    const [
      collateralProxy,
      assetAddress,
      amountInCents,
      recipient,
      expiresAt,
      executorPublisherSalt,
      executorPublisherSig,
    ] = signature.parameters;

    // Get coordinator & withdrawAsset interface
    const coordinatorContract = new ethers.Contract(coordinatorAddress, CoordinatorInterface).connect(signer);
    const withdrawAsset = coordinatorContract.getFunction("withdrawAsset");
    
    // Get Collateral & nonce interface
    const collateralContract = new ethers.Contract(collateralProxy, CollateralInterface).connect(signer);
    const adminNonceFunction = collateralContract.getFunction("adminNonce");
    const nonce = await adminNonceFunction.staticCallResult();
    
    // Generate admin signature
    const { salt: adminSalt, signature: adminSignature } = await getAdminSignature({
      signer,
      amount: amountInCents,
      chainId: Number(chainId),
      collateralProxyAddress: collateralProxy,
      recipientAddress: recipient,
      tokenAddress: assetAddress,
      nonce: nonce[0],
    });
    const directTransfer = true;

    // build transaction input
    const functionInputs = [
      collateralProxy,
      assetAddress,
      amountInCents,
      recipient,
      expiresAt,
      Buffer.from(executorPublisherSalt, "base64"),
      executorPublisherSig,
      [adminSalt], // user generated
      [adminSignature], // user generated
      directTransfer,
    ];

    // send withdrawAsset transaction
    await withdrawAsset(...functionInputs);
  };
  ```
</CodeGroup>

### Generating Admin Signature (V2)

Use this helper to generate the required parameters for the withdrawal transaction:

<CodeGroup>
  ```ts getAdminSignature.ts theme={null}
  type AdminSignatureOpts = {
    signer: Signer;
    chainId: number;
    collateralProxyAddress: string;
    recipientAddress: string;
    amount: number;
    tokenAddress: string;
    nonce: number;
  };

  /**
   * Gets admin signature needed to resolve on coordinator contract
   * @param opts
   * @returns
   */
  const getAdminSignature = async (opts: AdminSignatureOpts) => {
    const { collateralProxyAddress, signer, chainId, tokenAddress, amount, recipientAddress, nonce } = opts;

    const salt = randomBytes(32);
    const domain = {
      name: "Collateral",
      version: "2",
      chainId: chainId as number,
      verifyingContract: collateralProxyAddress,
      salt,
    };
    const type = {
      Withdraw: [
        { name: "user", type: "address" },
        { name: "asset", type: "address" },
        { name: "amount", type: "uint256" },
        { name: "recipient", type: "address" },
        { name: "nonce", type: "uint256" },
      ],
    };
    const signerAddress = await signer.getAddress();
    const data = {
      user: signerAddress,
      asset: tokenAddress,
      amount,
      recipient: recipientAddress,
      nonce,
    };
    const signature = await signer.signTypedData(domain, type, data);
    return { salt, signature };
  };
  ```
</CodeGroup>

### V2 Contracts - Solana

[Please find an instruction and code example here](https://github.com/SignifyHQ/collateral-contract-integration-examples/tree/main/src/solana/examples/withdrawal)
