Skip to content

Smart Contracts

Rain uses ERC-4337 Account Abstraction to provide on-chain smart contract wallets for each user or company. These wallets hold stablecoin collateral that backs the credit line for card spending.

Contract Structure

Each user or company receives a RainContract with three key addresses:

typescript
interface RainContract {
  id: string;
  proxyAddress: string; // User's smart wallet (upgradeable proxy)
  depositAddress: string; // Where collateral is deposited
  controllerAddress: string; // Rain's controller for limits/liquidation
  chainId: number; // Blockchain network
  contractVersion: number; // Contract version
  tokens?: ContractToken[]; // Supported collateral tokens
  onramp?: RainOnramp; // Fiat onramp bank details (if enabled)
  status?: "active" | "inactive" | "pending";
}

interface ContractToken {
  address: string; // Token contract address (e.g., USDC)
  balance: string; // Current collateral balance
  exchangeRate: number; // Token to USD conversion rate
  advanceRate: number; // Collateral ratio (1.0 for stablecoins)
}
ComponentPurposeControlled By
Proxy AddressUser's smart wallet for signing transactionsUser (via Account Abstraction)
Deposit AddressReceives and holds collateralRain system
Controller AddressEnforces credit limits, triggers liquidationRain

Query Contracts

typescript
// User contracts
const { data: contracts } = await rain.contracts.getUserContracts("user-id");

// Company contracts
const { data: contracts } = await rain.contracts.getCompanyContracts("company-id");

// Tenant contracts
const { data: contracts } = await rain.contracts.getTenantContracts("tenant-id");

All contract endpoints return arrays since a user or company can have contracts on multiple chains.

Create a Contract

typescript
// Create contract for a user
const { data: contract } = await rain.contracts.createUserContract("user-id", {
  userAddress: "0x1234...",
  chainId: 1, // Ethereum mainnet
  contractVersion: 2
});

// Create contract for a company
const { data: contract } = await rain.contracts.createCompanyContract("company-id", {
  userAddress: "0x1234...",
  chainId: 137 // Polygon
});
FieldTypeRequiredDescription
userAddressstringYesEVM wallet address of the contract owner
chainIdnumberYesTarget blockchain network ID
contractVersionnumberNoContract version (defaults to latest)

Update a Contract

typescript
await rain.contracts.updateContract("contract-id", {
  status: "active",
  depositAddress: "0xabcd...",
  controllerAddress: "0xefgh..."
});

Supported Chains

ChainChain IDStatus
Ethereum Mainnet1Live
Polygon137Live
Sepolia (testnet)11155111Live
Base8453Coming Soon
Optimism10Coming Soon
Arbitrum42161Coming Soon

Fiat Onramp

When enabled, contracts include virtual bank account details for direct fiat funding:

typescript
interface RainOnramp {
  ach: { accountNumber; routingNumber; beneficiaryName; beneficiaryAddress };
  rtp: { accountNumber; routingNumber; beneficiaryName; beneficiaryAddress };
  wire: { accountNumber; routingNumber; beneficiaryName; beneficiaryAddress };
}

ERC-4337 Transaction Flow

User Intent
    |
[Bundler] packages into UserOperation
    |
[EntryPoint] validates and executes
    |
[Paymaster] sponsors gas (gasless UX)
    |
[User Smart Account] executes action
    |
[Controller] enforces credit limits

Security Model

PropertyDescription
No Default RiskCredit limit always less than or equal to collateral value
On-Chain CollateralVerifiable on any block explorer
Programmable LimitsController enforces spending rules automatically
Gasless UXPaymaster sponsors transaction fees for users

The Controller contract is Rain's key enforcement mechanism:

  1. Credit Limit Enforcement -- sets limits based on collateral x advanceRate
  2. Auto-Liquidation -- triggers after 8-day grace period if the statement is unpaid
  3. Withdrawal Authorization -- controls when and how collateral can be withdrawn
  4. Rate Management -- manages collateral-to-credit ratios per token
Smart Contracts has loaded