Reddit Injection Fund

A stock-paired, community-executed liquidity protocol powered by $REDIF.

Technical paper v1.0 · Robinhood Chain

Reddit Injection Fund converts a defined portion of trading activity into permanent, publicly inspectable liquidity. REDIF trades against a tokenized Reddit stock asset. The launch fee policy assigns 0.30% to the launch protocol, 0.85% to the injection fund, and 0.85% to the project treasury on every buy and sell.

The injection path is deliberately public. Any wallet can call claimFeesAndInject(), while the contracts control the accounting, pool, price limits, and final destination. The caller pays network gas and receives no authority over the funds.


1. Abstract

REDIF joins five onchain components: the project token, a fee receiver, a permanent liquidity vault, a signer-backed reference oracle, and a graduation-token locker. Together they create two independent but related loops.

The liquidity loop claims actual project-fee receipts, divides them into fixed accounting buckets, and lets any wallet attempt a guarded liquidity injection. Successful batches increase one full-range position in the REDIF stock-paired pool. The vault exposes no path for removing that liquidity.

The graduation-lock loop holds every REDIF token acquired through the full migration purchase. Its standard unlock is 30 days. If REDIF surpasses a $10 million market capitalization during that period, the immutable locker owner calls eternallyLock() before the deadline and changes the locker to an irreversible permanent state.

The design does not depend on a private keeper pressing a button. Public callers can relay oracle reports, trigger fee collection, request liquidity injection, and inspect emitted receipts. Public execution does not mean public control: destinations and safety limits remain fixed by contract.

2. Motivation

Most creator fees leave the market as revenue. REDIF assigns the project share a second job. Half of every project-fee receipt becomes a liquidity budget that can return to the same market through a bounded, observable process.

The protocol targets three problems:

  • Idle fee value: claimed trading fees should become productive liquidity rather than remain unaccounted in a wallet.
  • Operator dependence: the mechanism should continue working even when the project operator does not submit the transaction.
  • Unverifiable promises: fee splits, balances, signer approvals, injections, and permanent locks should be provable from contract state and events.

The result is a stock-paired injection protocol where accounting is deterministic, execution is permissionless, and permanent positions cannot be privately withdrawn.

3. Assets and terminology

TermMeaning
REDIFThe Reddit Injection Fund project token.
stockTokenThe tokenized Reddit stock asset paired with REDIF.
Project feeThe 1.70% remainder after the launch protocol receives 0.30% of each 2% trading fee.
Injection budgetHalf of claimed project fees, equal to 0.85% of trading volume before execution costs.
Treasury accruedThe other half of claimed project fees, also equal to 0.85% before execution costs.
Graduation tokensREDIF acquired through the full migration purchase and deposited into the custom locker.
Reference reportA time-limited pool-tick report approved by at least six of ten oracle signers for liquidity execution.

The paired stock token and REDIF serve different roles. The stock token is the quote-side asset used by the pool and fee accounting. REDIF is the project token, the asset acquired during the full migration purchase, and the asset held by the graduation locker.

4. System architecture

The contracts separate accounting, price validation, liquidity ownership, and graduation custody. No one contract receives unnecessary authority from another.

text
BUY / SELL
    |
    v
Stock-paired REDIF pool
    |
    | 2.00% trading fee
    v
Launch protocol fee accounting
    |
    | 0.30% protocol share
    | 1.70% project share
    v
LiquidityFeeReceiver
    |-------------------------------|
    |                               |
    v                               v
0.85% liquidityBudget          0.85% treasuryAccrued
    |                               |
    | public trigger                | public trigger,
    v                               | fixed recipient
SignedReferenceOracle               v
    |                          Project treasury
    v
LockedLiquidityVault
    |
    v
Permanent full-range liquidity

The graduation purchase follows a separate custody path:

text
Full migration purchase
    |
    | acquired asset: REDIF
    v
GraduationLocker
    |-------------------------------|
    |                               |
    v                               v
30 days elapsed                Owner calls eternallyLock()
    |                               |
    v                               v
standard unlock                state = EternallyLocked
                                    |
                                    v
                              no withdrawal path

5. Fee mathematics

5.1 Trading-fee allocation

Every buy and sell applies the same 2% fee. The launch protocol keeps 0.30 percentage points. The remaining 1.70 percentage points are credited to the REDIF fee recipient.

DestinationShare of tradeShare of project receipts
Launch protocol0.30%Not received by REDIF
Injection fund0.85%50%
Project treasury0.85%50%
Total2.00%100% of project receipts accounted

For a trade notional V, before swap loss, gas, and rounding:

text
protocolFee      = V * 0.0030
projectReceipt   = V * 0.0170
injectionBudget  = projectReceipt / 2 = V * 0.0085
treasuryAccrued  = projectReceipt / 2 = V * 0.0085

The receiver splits actual token receipts rather than estimated volume. If the fee escrow reports received, contract balances must increase by exactly received or the claim reverts.

5.2 Cumulative rounding

Splitting each tiny claim independently could make repeated one-unit claims favor one accounting bucket. REDIF calculates the treasury portion from cumulative receipts instead.

solidity
uint256 treasuryShare =
    (totalClaimed + received) / 2
    - totalClaimed / 2;

totalClaimed += received;
treasuryAccrued += treasuryShare;
liquidityBudget += received - treasuryShare;

This preserves the intended 50/50 project split across any sequence of claim sizes.

6. LiquidityFeeReceiver

The receiver is the protocol accountant and public entry point. It is configured as the launch fee recipient and stores the following independent totals:

  • totalClaimed: all verified project-fee receipts claimed from escrow.
  • liquidityBudget: stock-side assets reserved exclusively for injections.
  • treasuryAccrued: the independently withdrawable project allocation.
  • totalTreasuryPaid: the amount already delivered to the fixed treasury.
  • totalSentToVault: injection budgets transferred into the vault.

6.1 Public execution

Anyone may call claimFeesAndInject(). The function attempts to sweep available launch fees, claims actual escrow receipts, records the split, verifies that the pool is ready, and attempts one bounded batch.

solidity
function claimFeesAndInject()
    external
    nonReentrant
    returns (uint256 claimed, uint128 added)
{
    trySweepProjectFees();
    claimed = _claim();

    if (liquidityBudget < minBatch) return (claimed, 0);
    if (!poolIsReady()) return (claimed, 0);

    uint256 batch = liquidityBudget < maxBatch
        ? liquidityBudget
        : maxBatch;

    try this.executeInjection(batch) returns (uint128 result) {
        added = result;
        emit InjectionCompleted(msg.sender, batch, result);
    } catch (bytes memory reason) {
        emit InjectionDeferred(reason);
    }
}

An unavailable sweep does not erase already claimable fees. A rejected injection does not merge the two accounting buckets or send the reserved liquidity budget elsewhere. The failure is emitted and the budget remains available for a later valid attempt.

6.2 Treasury payment

payTreasury() is also publicly callable, but it always pays the immutable treasury address. The caller cannot substitute itself as recipient and cannot touch liquidityBudget.

solidity
function payTreasury() external nonReentrant returns (uint256 amount) {
    amount = treasuryAccrued;
    treasuryAccrued = 0;
    totalTreasuryPaid += amount;
    if (amount != 0) stockToken.safeTransfer(treasury, amount);
    emit TreasuryPaid(amount);
}

Direct stock-token transfers to the receiver can be recognized through syncDonations(). Synced donations become 100% injection funding and are not split again.

7. LockedLiquidityVault

The vault owns the additional Uniswap v4 liquidity position. It receives a fixed pool key, receiver, oracle, quote asset, and execution limits during construction. Those values cannot be changed later.

The vault follows six invariants:

  1. Only the receiver can call inject().
  2. The pool identity and token ordering are fixed.
  3. A fresh oracle report is required before execution.
  4. The live tick must remain inside the permitted deviation before and after the swap.
  5. The quote-side swap is limited by maxSwapQuote and maxSwapLossBps.
  6. There is no owner withdrawal, proxy upgrade, rescue, arbitrary call, or negative-liquidity function.

7.1 Guarded injection

The vault reuses residual REDIF already held from earlier batches. It swaps only the quote-side imbalance needed to prepare both assets, caps that swap, then increases the same full-range position.

solidity
function inject(uint256 amount)
    external
    nonReentrant
    returns (uint128 added)
{
    if (msg.sender != receiver) revert Unauthorized();
    pullExactStockToken(amount);

    (int24 referenceTick, uint256 observedAt) = oracle.read(poolId());
    requireFresh(observedAt, maxOracleAge);
    requireWithinDeviation(referenceTick, maxTickDeviation);

    swapQuoteImbalance(maxSwapQuote, maxSwapLossBps);
    added = increaseFullRangePosition();

    requireWithinDeviation(referenceTick, maxTickDeviation);
    emit LiquidityInjected(amount, added, stockRemaining, redifRemaining);
}

Any imbalance left after adding liquidity remains inside the vault and is reused during a later injection. totalLiquidityAdded records liquidity units, while token balances show the residual assets still under permanent custody.

8. SignedReferenceOracle

The oracle converts independent observations into a single contract-readable reference. Ten signer addresses are fixed at deployment. An accepted report requires at least six valid signatures from that set.

The signer wallets are reporters, not custodians. They do not hold injection funds, treasury funds, vault positions, or contract ownership. Reporter systems observe the agreed data source, calculate the same report, and sign it offchain. Any relayer can pay gas to submit the collected signatures.

8.1 Report format

solidity
struct ReferenceReport {
    bytes32 poolId;
    int24 referenceTick;
    uint64 observedAt;
    uint64 validUntil;
    uint64 sequence;
}

The pool tick protects liquidity execution. It is covered by the EIP-712 digest along with the exact pool, observation time, expiry, and sequence. The graduation locker is independent from this oracle.

8.2 Acceptance rules

solidity
function submit(
    ReferenceReport calldata report,
    bytes[] calldata signatures
) external {
    if (report.poolId == bytes32(0)) revert InvalidReport();
    if (report.observedAt > block.timestamp) revert FutureReport();
    if (report.validUntil < block.timestamp) revert ExpiredReport();
    if (report.sequence <= latestSequence[report.poolId]) revert Replay();
    if (signatures.length < threshold) revert InsufficientSignatures();

    address previous;
    bytes32 digest = hashTypedReport(report);
    for (uint256 i; i < signatures.length; ++i) {
        address signer = ECDSA.recover(digest, signatures[i]);
        if (!isSigner[signer]) revert UnknownSigner();
        if (signer <= previous) revert DuplicateOrUnsortedSigner();
        previous = signer;
    }

    store(report);
    emit ReportAccepted(report.poolId, report.sequence);
}

Strict address ordering prevents one signer from being counted twice. Increasing sequences prevent replay. Expiry and observation checks prevent an old report from remaining valid indefinitely.

8.3 Reference policy

The signers do not copy the pool's current spot price. A spot-only source could be manipulated immediately before an injection. Reporter systems use a reviewed observation window, a confirmation buffer, canonical token ordering, and the correct token decimals.

Every signer signs the same normalized integer values. Reports that differ by pool, tick, timestamp, expiry, or sequence produce different hashes and cannot be combined into a quorum.

9. GraduationLocker

The graduation locker handles the REDIF purchased through the full migration process. It does not hold the stock-paired injection budget and does not own the liquidity vault.

The locker is deployed before launch with the immutable receiver and owner. After bindLaunch() creates the permanent vault, the owner calls parameterless bindToken(). The locker reads REDIF directly from that receiver and requires the receiver's vault to exist, so the caller cannot supply or substitute another token address. This binding succeeds once and cannot be changed.

The immutable owner then approves one REDIF amount and calls startLock(amount). The locker pulls that exact amount, rejects fee-on-transfer behavior, records it, and sets unlockAt to 30 days after the deposit transaction. No second deposit can restart or extend the timer.

solidity
function eternallyLock() external onlyOwner {
    if (state != State.Locked) revert InvalidState();
    if (block.timestamp >= unlockAt) revert LockExpired();

    state = State.EternallyLocked;
    emit EternallyLocked(REDIF.balanceOf(address(this)));
}

function withdraw() external onlyOwner nonReentrant {
    if (state != State.Locked) revert InvalidState();
    if (block.timestamp < unlockAt) revert LockActive();

    state = State.Withdrawn;
    REDIF.safeTransfer(owner, REDIF.balanceOf(address(this)));
}

Only the immutable owner can start the timer, activate the eternal state, or withdraw after expiry. There is no ownership transfer, rescue, upgrade, pause, extension, or early-withdrawal function. eternallyLock() must be called before the deadline. Once the state changes, no later owner action or market-cap decrease can restore the withdrawal path.

10. Transaction lifecycle

One public injection attempt follows a deterministic sequence:

  1. Sweep: the receiver asks the active launch contract to move pending project fees into escrow.
  2. Claim: the receiver calls the escrow and measures the exact balance increase.
  3. Split: cumulative accounting updates liquidityBudget and treasuryAccrued.
  4. Gate: execution stops cleanly when the pool is unavailable or the budget is below minBatch.
  5. Read: the vault loads the latest quorum-approved oracle state.
  6. Validate: report freshness, tick deviation, swap input, and minimum output are checked.
  7. Balance: a bounded swap prepares stock token and REDIF for the position.
  8. Inject: the vault increases its fixed full-range Uniswap v4 liquidity.
  9. Verify: balances and events provide the public receipt used by the website.
text
caller
  -> claimFeesAndInject()
      -> sweepFees()
      -> escrow.claimToken()
      -> _claim()
      -> executeInjection(batch)
          -> vault.inject(batch)
              -> oracle.read(poolId)
              -> manager.swap()
              -> manager.modifyLiquidity()
              -> LiquidityInjected(...)
      -> InjectionCompleted(...)

11. State and events

The website reads contract state rather than estimating revenue from trading volume.

State or eventWhat it proves
totalClaimedTotal verified project-fee receipts.
liquidityBudgetClaimed stock token reserved for future injections.
treasuryAccruedProject allocation available only to the fixed treasury.
totalSentToVaultQuote-side budget delivered to the vault.
totalLiquidityAddedCumulative Uniswap v4 liquidity units added.
FeesClaimedReceipt amount and exact split recorded in one transaction.
InjectionCompletedPublic caller, batch size, and liquidity added.
InjectionDeferredFailed guarded attempt whose budget remains reserved.
ReportAcceptedOracle pool, observation, sequence, and quorum-approved state.
EternallyLockedThe owner transaction and token balance that made the graduation lock irreversible.

The homepage analytics expose three readable outcomes: total RDDT-side assets injected, their cumulative USD value at execution time, and the number of unique wallets that have triggered a completed injection.

12. Roles and authority

ActorCan doCannot do
Any walletRelay a signed report, trigger a claim, request an injection, pay the fixed treasury.Redirect funds, change limits, choose a pool, or withdraw liquidity.
Oracle signerSign a deterministic reference report.Submit alone, move funds, or modify contract storage without quorum.
RelayerBundle signatures and broadcast a report.Forge signer approval or bypass report validation.
BootstrapperBind the verified launch once and create its vault.Rebind the token or control the system after binding.
Treasury beneficiaryReceive only treasuryAccrued.Withdraw liquidityBudget, vault balances, or the liquidity position.
Locker ownerStart the single deposit, call eternallyLock() before expiry, or withdraw after day 30.Withdraw early, reverse an eternal lock, replace the owner, or restart the timer.

13. Failure behavior

ConditionResult
Nothing claimableClaim returns zero; no accounting changes.
Budget below minBatchFunds remain reserved until more fees arrive.
Budget above maxBatchOne bounded batch is injected; the remainder stays reserved.
Pool not readyClaim can complete; injection waits.
Oracle report stale or missingVault rejects execution; funds remain under contract accounting.
Pool tick outside deviationSwap and liquidity addition revert atomically.
Swap output below minimumInjection reverts without accepting the bad execution.
Unauthorized vault callerinject() reverts.
Fewer than six oracle signaturesReport submission reverts.
Duplicate or replayed signaturesReport submission reverts.
Non-owner locker callThe transaction reverts without changing locker state.
eternallyLock() at or after expiryThe transaction reverts; the expired timed path remains withdrawable by the owner.
Permanent lock already activeGraduation-token withdrawal remains unavailable.

14. Security properties

14.1 Funds separation

Treasury payment reads only treasuryAccrued. Injection reads only liquidityBudget. The receiver never treats its entire token balance as a developer withdrawal allowance.

14.2 Atomic execution

The vault's swap, settlement, price checks, and liquidity modification occur inside one Uniswap v4 unlock callback. A failure rolls back the vault operation. The receiver catches the failure, emits its reason, and retains the injection budget for a later attempt.

14.3 Immutable destinations

The treasury, pool key, quote asset, oracle, receiver, batch limits, and price limits are constructor configuration. Public callers supply timing and gas, not destinations or economic parameters.

14.4 Permanent custody

The liquidity vault exposes positive-liquidity operations only. The graduation locker exposes a withdrawal only from the timed Locked state after expiry. Its EternallyLocked state has no outgoing REDIF path. These are separate custody systems with separate state.

15. Protocol parameters

ParameterREDIF value or rule
Trading fee2.00% on buys and sells
Launch protocol share0.30% of trade notional
Injection allocation0.85% of trade notional
Treasury allocation0.85% of trade notional
Oracle signer set10 fixed addresses
Oracle threshold6 signatures
Graduation unlock30 days
Eternal-lock policyOwner calls eternallyLock() before day 30 if REDIF crosses $10,000,000 market capitalization
Injection callerAny wallet
Treasury recipientOne immutable address
Liquidity destinationOne fixed stock-paired pool and vault position

Raw batch sizes, report-validity windows, oracle age, tick deviation, maximum quote swap, and maximum swap-loss values are published with the final constructor arguments and verified bytecode.

16. Deployment and verification

The launch sequence is ordered so each immutable reference can be verified before the next contract depends on it.

  1. Fix the ten oracle signer addresses, 6-of-10 threshold, report policy, and execution limits.
  2. Deploy and verify the signed reference oracle.
  3. Deploy and verify the fee receiver with its treasury, quote asset, protocol addresses, and oracle.
  4. Launch REDIF with the receiver as creator-fee recipient and the tokenized Reddit stock asset as its pair.
  5. Bind the verified launch once and create the fixed liquidity vault.
  6. Deploy and verify RedifGraduationLocker against the fixed receiver and immutable owner; this may happen before launch.
  7. After receiver binding, call parameterless bindToken() so the locker records the receiver's exact REDIF token once.
  8. Execute the full migration purchase, approve the locker, and call startLock(amount) once for every acquired REDIF token.
  9. Publish the token, receiver, oracle, vault, locker, pool, treasury, launch, and lock-start transactions.
  10. Connect the dashboard to verified addresses and begin relaying signed reports.

Verification is bytecode-specific. The published source, compiler version, optimizer settings, constructor arguments, and linked libraries must reproduce the deployed runtime bytecode.

17. Public interfaces

The primary external functions are intentionally small:

solidity
// Fee receiver
claimFees() external returns (uint256 received);
claimFeesAndInject() external returns (uint256 claimed, uint128 added);
payTreasury() external returns (uint256 amount);
syncDonations() external returns (uint256 amount);

// Oracle
submit(ReferenceReport report, bytes[] signatures) external;
read(bytes32 poolId) external view returns (ReferenceReport memory);

// Vault
inject(uint256 amount) external returns (uint128 liquidityAdded);
poolId() external view returns (bytes32);

// Graduation locker
startLock(uint256 amount) external;
eternallyLock() external;
withdraw() external returns (uint256 amount);

Users do not need the REDIF website to reach these functions. Once verified, the same calls can be inspected and submitted through Robinhood Chain Blockscout.

18. Boundaries and independence

Additional liquidity can reduce price impact for a given trade. It cannot create demand, guarantee profit, establish a price floor, or guarantee that a holder can exit at a chosen price.

The oracle reduces dependence on a single reporter; it does not eliminate source-data, signer-operation, or relayer risks. A 6-of-10 quorum remains secure only while fewer than six signer systems are compromised and the reference policy is followed consistently.

Reddit Injection Fund is an independent internet experiment. Its name and community-inspired interface do not imply affiliation with, sponsorship by, or endorsement from Reddit, Robinhood, Uniswap, or the launch protocol.

19. Standard

The REDIF standard is simple:

  • No hidden recipient.
  • No hand-waved fee math.
  • No private execution dependency.
  • No removable liquidity position.
  • No reversible eternal-lock decision.
  • No permanent lock that can later be reversed.

Collect. Split. Verify. Inject. Repeat.