> For the complete documentation index, see [llms.txt](https://docs.replycorp.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.replycorp.io/enterprise/fee-router-smart-contract-integration.md).

# Fee Router Smart Contract Integration

On-chain fee distribution via a custom smart contract.

## Introduction

This is an **optional, enterprise-only** feature. Most customers use the standard points marketplace for attribution rewards. The fee router is for enterprise customers who need on-chain fee distribution via a custom smart contract.

If you are not sure whether you need this, you almost certainly do not. Contact <support@replycorp.io> to discuss enterprise options.

***

### Table of Contents

1. [Overview](#overview)
2. [Prerequisites](#prerequisites)
3. [Conversion API Fields](#conversion-api-fields)
4. [Fee Distribution Response](#fee-distribution-response)
5. [Smart Contract Deployment](#smart-contract-deployment)
6. [Real-Time Distribution Flow](#real-time-distribution-flow)
7. [Deferred / Periodic Distribution](#deferred-periodic-distribution)
8. [Independent Fee Routing](#independent-fee-routing)
9. [CEX Integration Example](#cex-integration-example)
10. [Fee Models](#fee-models)

***

### Overview

The fee router contract receives fees and distributes them to attributed influencers on-chain. When a conversion is recorded via the API, ReplyCorp:

1. Calculates attribution (who influenced the sale)
2. Updates your deployed fee router contract with the attribution breakdown
3. Returns the `feeDistribution` data in the API response

You then interact with the contract to approve token spending, start distribution, process batches, and finalize.

***

### Prerequisites

* **Enterprise plan** with fee router enabled (contact <support@replycorp.io>)
* **Funded distribution wallet**: The wallet calling `startDistribution()` must have sufficient ERC-20 token balance. The contract pulls tokens from this wallet. You can fund it in real time by routing conversion proceeds to it, or pre-fund it and keep it topped up.
* **Consistent token denomination**: All financial data (`totalVolume`, `commission`, and optionally `netProfit`) posted to the API must be denominated in the same ERC-20 token used for rewards. If distributing USDC, all amounts must be in USDC units.
* **Smart contract deployment capability**: You must be able to deploy contracts and interact with them programmatically.
* **Contract parameters entered in admin panel**: Chain, token address, revenue share percentage. ReplyCorp fee percentage is set by ReplyCorp when creating your account.

***

### Conversion API Fields

When using the fee router, your conversion API requests must include these additional fields alongside the standard `twitterId` and `eventType`:

| Field         | Type   | Required | Description                                                                                                             |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `totalVolume` | number | Yes      | Gross revenue from the conversion                                                                                       |
| `commission`  | number | Yes      | Amount to distribute to influencers. Under the default COMMISSION model, the ReplyCorp fee (15%) is deducted from this. |
| `netProfit`   | number | No       | Net profit (totalVolume minus costs). Defaults to `totalVolume`. Only used under the legacy NET\_PROFIT model.          |

If any fee router field is provided, at least `totalVolume` and `commission` must be provided together.

Attribution percentages are calculated from `totalVolume` (who influenced the sale). Commission amounts are distributed from `commission` (the portion you allocate to influencers).

If fee router fields are not provided, the API falls back to using `amount` for backward compatibility.

***

### Fee Distribution Response

When a campaign has a fee router contract configured, the conversion API response includes a `feeDistribution` object:

```json
{
  "feeDistribution": {
    "totalCommission": 20.00,
    "replyCorpFee": 3.00,
    "totalToSendToContract": 23.00,
    "attributionHash": "0x1234567890abcdef..."
  }
}
```

| Field                                   | Description                                                                                                                                               |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `feeDistribution.totalCommission`       | Total commission amount to distribute to influencers                                                                                                      |
| `feeDistribution.replyCorpFee`          | Fee amount for ReplyCorp                                                                                                                                  |
| `feeDistribution.totalToSendToContract` | Total amount to send to contract. Under COMMISSION model this equals commission (fee deducted from it). Under NET\_PROFIT model this is commission + fee. |
| `feeDistribution.attributionHash`       | Hash of attribution data for verification (matches contract's attributionHash)                                                                            |

The `feeDistribution` field is only included when:

* The campaign has a fee router contract configured (`feeRouterContractAddress` is set)
* All attributed participants have wallet addresses (no null wallets)
* The conversion includes `totalVolume` and `commission` fields

***

### Smart Contract Deployment

**Time estimate:** 2-4 hours

1. Enter contract parameters in the admin panel (chain, token address, revenue share %)
2. Receive contract code from ReplyCorp
3. Deploy the contract to your specified chain with the parameters you entered
4. Configure your system to interact with the deployed contract address

See FeeRouter Contract Documentation for the full contract reference.

***

### Real-Time Distribution Flow

```javascript
const grossRevenue = 100.00;
const commission = 20.00;

// 1. Record conversion -- ReplyCorp updates contract with attribution BEFORE responding
const conversionResponse = await fetch(
  'https://api.replycorp.io/api/v1/campaigns/{campaignId}/conversions',
  {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      twitterId: '1234567890',
      eventType: 'purchase',
      totalVolume: grossRevenue,
      commission: commission,
      walletAddress: '0x...'
    })
  }
);

const result = await conversionResponse.json();
const conversionId = result.conversion.id;
const attributionHash = result.feeDistribution.attributionHash;
const totalToSend = result.feeDistribution.totalToSendToContract;

// 2. Verify attribution hash in contract
const distributionRoute = await feeRouterContract.getDistributionRoute(conversionId);
if (distributionRoute.attributionHash !== attributionHash) {
  throw new Error('Attribution hash mismatch - do not proceed');
}

// 3. Approve contract to spend tokens (ERC-20)
await tokenContract.approve(feeRouterContractAddress, totalToSend);

// 4. Start distribution (contract pulls tokens via transferFrom)
await feeRouterContract.startDistribution(conversionId, totalToSend, attributionHash);

// 5. Process batches (for conversions with >200 recipients)
const batchCount = await feeRouterContract.getBatchCount(conversionId);
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
  const isCompleted = await feeRouterContract.isBatchCompleted(conversionId, batchIndex);
  if (isCompleted) continue;

  const tx = await feeRouterContract.processBatch(conversionId, batchIndex);
  const receipt = await tx.wait();
  const batchEvent = receipt.events.find(e => e.event === 'BatchDistributed');
  const failedCount = batchEvent?.args.failedCount || 0;
  console.log(`Batch ${batchIndex} completed. Failed transfers: ${failedCount}`);
}

// 6. Finalize distribution
await feeRouterContract.finalizeDistribution(conversionId);
```

The contract validates the hash, pulls tokens on `startDistribution`, transfers the ReplyCorp fee immediately, then distributes commission to referrers in batches. Failed transfers are skipped and accumulated as dust.

***

### Deferred / Periodic Distribution

If you want to batch payments on a schedule (weekly, monthly) instead of distributing in real time:

1. Deploy the fee router contract (same as above)
2. Call the conversion API -- ReplyCorp automatically updates the contract with attribution data
3. Store conversion IDs and `feeDistribution` data from API responses
4. Later, batch-process multiple conversions by calling the contract distribution functions

```javascript
// Store after each conversion
await db.pendingDistributions.create({
  data: {
    conversionId: result.conversion.id,
    totalToSend: result.feeDistribution.totalToSendToContract,
    attributionHash: result.feeDistribution.attributionHash,
    distributionStatus: 'pending'
  }
});

// Weekly batch job
const pending = await db.pendingDistributions.findMany({
  where: { distributionStatus: 'pending', createdAt: { lte: batchCutoffDate } }
});

for (const dist of pending) {
  try {
    await tokenContract.approve(feeRouterContractAddress, dist.totalToSend);
    await feeRouterContract.startDistribution(dist.conversionId, dist.totalToSend, dist.attributionHash);

    const batchCount = await feeRouterContract.getBatchCount(dist.conversionId);
    for (let i = 0; i < batchCount; i++) {
      if (!(await feeRouterContract.isBatchCompleted(dist.conversionId, i))) {
        await feeRouterContract.processBatch(dist.conversionId, i);
      }
    }
    await feeRouterContract.finalizeDistribution(dist.conversionId);

    await db.pendingDistributions.update({
      where: { id: dist.id },
      data: { distributionStatus: 'distributed', distributedAt: new Date() }
    });
  } catch (error) {
    await db.pendingDistributions.update({
      where: { id: dist.id },
      data: { distributionStatus: 'failed', error: error.message }
    });
  }
}
```

The contract stores attribution data permanently -- you can distribute at any time.

***

### Independent Fee Routing

If you use a different payment system or smart contract architecture, you can store the attribution data from the API and distribute fees yourself.

Key data to store from each conversion response:

* `conversion.id` -- unique identifier
* `attribution.attributions[]` -- array of all attributed referrers with percentages, wallet addresses, and amounts
* `conversion.conversionValue` -- the conversion amount

Handle missing wallet addresses (`walletAddress: null`) by escrowing funds, matching wallets on your side, or waiting for webhook updates when participants connect wallets.

```javascript
const payouts = storedConversion.attribution.attributions.map(attr => ({
  walletAddress: attr.walletAddress,
  twitterId: attr.twitterId,
  payoutAmount: totalCommission * (attr.attributionPercentage / 100)
}));

const withWallets = payouts.filter(p => p.walletAddress !== null);
const withoutWallets = payouts.filter(p => p.walletAddress === null);

for (const payout of withWallets) {
  await distributeToWallet(payout.walletAddress, payout.payoutAmount);
}
```

***

### CEX Integration Example

**Scenario:** User buys $100 of tokens on your exchange.

* `totalVolume` = $100.00 (the purchase amount)
* Platform fees (2% transaction fee): $2.00
* `commission` = $0.40 (20% of your $2.00 net profit, allocated for referral rewards)
* ReplyCorp fee (COMMISSION model): $0.06 (15% of commission, deducted from it)

1. Call the conversion API with `totalVolume: 100` and `commission: 0.40`
2. ReplyCorp responds: send $0.40 total to the fee router contract. Referrers receive $0.34, ReplyCorp receives $0.06.
3. Call `startDistribution()`, `processBatch()`, and `finalizeDistribution()` on the contract.

***

### Fee Models

| Model           | Default             | Fee Calculation     | How Fee Is Handled                                                                   |
| --------------- | ------------------- | ------------------- | ------------------------------------------------------------------------------------ |
| **COMMISSION**  | Yes (new campaigns) | 15% of `commission` | Deducted from commission. Customer deposits commission amount; fee is taken from it. |
| **NET\_PROFIT** | No (legacy)         | 5% of `netProfit`   | Additive. Customer deposits commission + fee.                                        |

Fee model and percentage are configurable per campaign via admin panel or Telegram bot.

***

**Last Updated:** April 2026
