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

# Automate USDC collection with a Treasury Wallet

> Use Sequence Embedded Wallets and Smart Sessions to automate pull-and-push USDC flows from client wallets to a central treasury, with no per-transfer approvals.

This guide shows how to build an automated stablecoin collection system where your platform pulls USDC from client wallets on a schedule and settles to a downstream destination, without requiring manual approval from clients on every transfer.

A common real-world pattern: a payment processor needs to collect USDC from their fintech clients each day and forward it to a card network for settlement. The flow is:

```
Client wallet (one-time Smart Session grant)
    → Platform checks balance via Indexer
        → Platform pulls USDC on schedule
            → Treasury wallet aggregates funds
                → Treasury pushes to settlement destination
```

No per-transfer approval is needed from clients after the initial session grant.

## How it works

There are two roles:

* **Client wallet:** A Sequence Embedded Wallet held by each client. During onboarding, the client grants a one-time Explicit Smart Session that authorizes your platform to pull USDC up to a defined daily cap, locked to your treasury address only.
* **Treasury wallet:** An operator-controlled Sequence wallet. It receives pulled USDC from client wallets and forwards it to the settlement destination (e.g., a card network settlement account or stablecoin bridge).

## Prerequisites

* A project access key from the [Polygon project dashboard](https://sequence.build)
* Polygon mainnet configured (chain ID: 137)
* USDC on Polygon: `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359`
* An operator-controlled treasury wallet

## Install

```bash theme={null}
# Frontend (client session grant)
pnpm install @0xsequence/connect wagmi viem

# Backend (monitoring)
pnpm install @0xsequence/indexer
```

***

## Step 1: Client grants a Smart Session

Each client connects their Sequence Embedded Wallet during onboarding and grants one Explicit Smart Session. This session authorizes your backend to pull USDC up to a daily cap, locked to your treasury address. It cannot be used to send funds anywhere else.

Configure this in your frontend provider:

```tsx theme={null}
import {
  SequenceConnect,
  createConfig,
  createContractPermission,
  createExplicitSession,
} from "@0xsequence/connect";
import { parseUnits } from "viem";

const USDC_POLYGON = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
const TREASURY_ADDRESS = "0xYourTreasuryAddress";
const DAILY_CAP_USDC = parseUnits("100000", 6); // 100,000 USDC

const config = createConfig({
  projectAccessKey: process.env.NEXT_PUBLIC_PROJECT_ACCESS_KEY!,
  signIn: { projectName: "Your Platform" },
  walletUrl: "https://wallet.polygon.technology",
  dappOrigin: window.location.origin,
  appName: "Your Platform",
  chainIds: [137],
  defaultChainId: 137,
  explicitSession: createExplicitSession({
    chainId: 137,
    nativeTokenSpending: { valueLimit: 0n },
    expiresIn: { days: 30 },
    permissions: [
      createContractPermission({
        address: USDC_POLYGON,
        functionSignature: "function transfer(address to, uint256 amount)",
        rules: [
          {
            param: "to",
            type: "address",
            condition: "EQUAL",
            value: TREASURY_ADDRESS, // locked — cannot send to any other address
          },
          {
            param: "amount",
            type: "uint256",
            condition: "LESS_THAN_OR_EQUAL",
            value: DAILY_CAP_USDC,
            cumulative: true, // enforced across all pulls in the session period
          },
        ],
      }),
    ],
  }),
});

export const App = () => (
  <SequenceConnect config={config}>
    <YourApp />
  </SequenceConnect>
);
```

The client reviews the permission scope and signs once. Your backend can pull up to the cap at any time within the 30-day session without prompting them again.

<Note>
  `cumulative: true` enforces the cap across all transfers during the session period, not per-transfer. A client with a 100,000 USDC cap cannot be exceeded in total until the session expires and they re-authorize.
</Note>

***

## Step 2: Check client USDC balance

Before each pull, verify the client has sufficient USDC using the Sequence Indexer API. This avoids failed transactions and lets you pull only what's available.

```typescript theme={null}
import { SequenceIndexer } from "@0xsequence/indexer";

const indexer = new SequenceIndexer(
  "https://polygon-indexer.sequence.app",
  process.env.PROJECT_ACCESS_KEY!
);

const USDC_POLYGON = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";

async function getClientUsdcBalance(clientAddress: string): Promise<bigint> {
  const result = await indexer.getTokenBalances({
    accountAddress: clientAddress,
    contractAddress: USDC_POLYGON,
    includeMetadata: false,
  });

  const usdcBalance = result.balances.find(
    (b) => b.contractAddress.toLowerCase() === USDC_POLYGON.toLowerCase()
  );

  return BigInt(usdcBalance?.balance ?? "0");
}
```

***

## Step 3: Pull USDC from client wallets

Your backend runs a scheduled job that calls `transfer` on each client's USDC balance via their active Smart Session. The session rules (address lock and cumulative cap) are enforced onchain by the client's smart account.

```typescript theme={null}
import { createWalletClient, http, parseAbi } from "viem";
import { polygon } from "viem/chains";

const USDC_POLYGON = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
const TREASURY_ADDRESS = "0xYourTreasuryAddress";

const USDC_ABI = parseAbi([
  "function transfer(address to, uint256 amount) returns (bool)",
]);

async function pullFromClient(
  sessionWalletClient: ReturnType<typeof createWalletClient>,
  clientAddress: `0x${string}`,
  amountUsdc: bigint
): Promise<`0x${string}`> {
  const hash = await sessionWalletClient.writeContract({
    address: USDC_POLYGON,
    abi: USDC_ABI,
    functionName: "transfer",
    args: [TREASURY_ADDRESS, amountUsdc],
    account: clientAddress,
    chain: polygon,
  });

  console.log("Pull submitted. tx:", hash);
  return hash;
}
```

***

## Step 4: Push from treasury to settlement

Once USDC has accumulated in the treasury, forward it to the settlement destination using your operator-controlled treasury wallet. If your treasury is itself a Sequence wallet, you can use the same SDK to manage outbound settlement.

```typescript theme={null}
async function pushToSettlement(
  treasuryWalletClient: ReturnType<typeof createWalletClient>,
  treasuryAddress: `0x${string}`,
  settlementAddress: `0x${string}`,
  amountUsdc: bigint
): Promise<`0x${string}`> {
  const hash = await treasuryWalletClient.writeContract({
    address: USDC_POLYGON,
    abi: USDC_ABI,
    functionName: "transfer",
    args: [settlementAddress, amountUsdc],
    account: treasuryAddress,
    chain: polygon,
  });

  console.log("Settlement push submitted. tx:", hash);
  return hash;
}
```

***

## Step 5: Monitor with Indexer

After each run, check the live treasury balance and query transaction history to verify pulls settled correctly.

```typescript theme={null}
async function getTreasuryBalance(): Promise<bigint> {
  const result = await indexer.getTokenBalances({
    accountAddress: TREASURY_ADDRESS,
    contractAddress: USDC_POLYGON,
    includeMetadata: false,
  });

  const usdcBalance = result.balances.find(
    (b) => b.contractAddress.toLowerCase() === USDC_POLYGON.toLowerCase()
  );

  return BigInt(usdcBalance?.balance ?? "0");
}

async function getTreasuryHistory() {
  const result = await indexer.getTransactionHistory({
    filter: {
      accountAddress: TREASURY_ADDRESS,
      contractAddresses: [USDC_POLYGON],
    },
  });

  return result.transactions;
}
```

***

## Putting it together

A complete daily settlement run: check balances, pull from each client, push to settlement, and log the result:

```typescript theme={null}
async function dailySettlementRun(
  clients: Array<{
    sessionWalletClient: ReturnType<typeof createWalletClient>;
    address: `0x${string}`;
    requestedAmount: bigint;
  }>,
  treasuryWalletClient: ReturnType<typeof createWalletClient>,
  treasuryAddress: `0x${string}`,
  settlementAddress: `0x${string}`
) {
  let totalCollected = 0n;

  // 1. Check balance and pull from each client
  for (const client of clients) {
    const available = await getClientUsdcBalance(client.address);
    const pullAmount =
      available < client.requestedAmount ? available : client.requestedAmount;

    if (pullAmount === 0n) {
      console.log(`Skipping ${client.address} — zero balance`);
      continue;
    }

    await pullFromClient(client.sessionWalletClient, client.address, pullAmount);
    totalCollected += pullAmount;
  }

  // 2. Push accumulated USDC to settlement
  if (totalCollected > 0n) {
    await pushToSettlement(
      treasuryWalletClient,
      treasuryAddress,
      settlementAddress,
      totalCollected
    );
  }

  // 3. Log final treasury balance
  const finalBalance = await getTreasuryBalance();
  console.log(
    `Run complete. Collected: ${totalCollected} USDC. Treasury balance: ${finalBalance} USDC`
  );
}
```

***

## Security considerations

* **Session locking:** The `to` address rule locks transfers to your treasury address only. A client's session cannot be used to send funds anywhere else, even if your backend is compromised.
* **Cumulative cap:** `cumulative: true` prevents the backend from pulling more than the authorized total across the session period.
* **Onchain enforcement:** Session rules are enforced at the smart contract level. They cannot be bypassed by your backend or any third party.
* **Key custody:** Client wallets are non-custodial. Private keys are secured in AWS Nitro Enclaves via threshold cryptography. Neither your platform nor Sequence holds user keys.

## Next steps

<CardGroup cols={2}>
  <Card title="Smart Sessions" icon="bolt" href="/wallets/smart-sessions">
    Understand implicit vs. explicit sessions and when to use each.
  </Card>

  <Card title="Wallet Quickstart" icon="rocket" href="/wallets/quickstart">
    Set up a Sequence Embedded Wallet from scratch.
  </Card>

  <Card title="Allow Your Users to Earn" icon="chart-line" href="/wallets/morpho-vault-deposit">
    Route client USDC into DeFi yield vaults using Trails.
  </Card>

  <Card title="Wallet Operations" icon="arrow-right-arrow-left" href="/wallets/wallet-operations">
    Full reference for sending transactions and reading balances.
  </Card>
</CardGroup>
