Docs

How streams work, the math, what the owner can and cannot do, and the risks.

What Accrue is

Accrue streams tokens per second on Robinhood Chain. A sender locks an amount of a Stock Token, USDG or WETH for a recipient. It accrues linearly from a start time to an end time, and the recipient withdraws what has accrued whenever they like. It suits payroll paid in stocks, grants and vesting, and allowances. Every stream lives in one contract, AccrueStreams, under its own id.

Creating a stream

The sender approves the token once and calls create. The amount moves into the contract in the same transaction. Tokens that arrive short (a cut on transfer) are refused.

create(
  token,       // an allowed Stock Token, USDG or WETH
  recipient,   // who accrues
  amount,      // total, in the token's units (18 decimals, USDG 6)
  start,       // unix seconds, 0 = now
  end,         // unix seconds, after start and after now
  cliff,       // 0 = none, else a time in (start, end]
  cancelable   // false = the sender can never stop it
) returns (uint256 id)

In the app this is one form: asset, recipient, amount, length (1 month to 4 years), cliff, cancelable or fixed, and an optional start date. Approve and create go out in one wallet window where the wallet supports batching.

Accrual and rounding

The contract computes accrual from the block time, per second. Division rounds down, so a recipient can never take more than the linear share, and at the end time the full amount is available. The sum of all withdrawals of a stream equals its amount exactly.

accrued(t) = 0                                   if t <= start or t < cliff
accrued(t) = amount                              if t >= end or canceled
accrued(t) = amount * (t - start) / (end - start)  otherwise, rounded down
withdrawable(t) = accrued(t) - withdrawn

The counter on the site recomputes the same formula every frame from your clock, with 9 decimals, and values it at the live Chainlink price. The contract itself moves in whole seconds.

Cliff

A cliff is a time before which nothing can be withdrawn. When it passes, everything accrued since the start unlocks at once, and the stream continues per second. A cliff equal to the end makes the stream all or nothing. Cancelling during the cliff returns the full amount to the sender.

Withdrawing and transferring

Only the recipient can withdraw: withdraw(id, amount) for part, withdrawMax(id) for everything accrued. There are no claim windows and nothing expires: tokens that have accrued stay in the contract until withdrawn.

The recipient can hand the stream to a new address with transfer(id, to). The new address receives everything not yet withdrawn, including what has already accrued. The sender cannot redirect a stream.

Cancelling

A stream created as cancelable can be stopped by its sender before the end with cancel(id). In that one transaction the accrued part not yet withdrawn goes to the recipient and the unaccrued part goes back to the sender. A stream created as fixed can never be cancelled. A completed stream cannot be cancelled either.

Topping up

The sender can add to a running stream with topUp(id, amount). The amount is added and the end moves out by the time the extra amount takes at the current rate, so the per-second rate stays the same. The new end is rounded down, so the rate never drops and what has accrued never decreases.

Agents and operators

A stream can pay an AI agent: the recipient is the agent's wallet, it accrues every second while the agent works, and a cancelable stream stops with one transaction from the sender.

A recipient can name one operator per stream with setOperator, and remove it by setting the zero address. The operator, often an agent, can call withdrawFor, which sends everything accrued so far to the recipient. It cannot withdraw to itself, transfer, top up or cancel. Transferring the stream to a new recipient clears the operator.

The agent API (x402 style)

Accrue serves a pay-per-call API at 0.001 USDG a call. An agent deposits USDG credits into the AgentCredits contract from its own wallet (the app's Agent API tab) and withdraws the rest any time.

For every request it signs accrue:<address in lowercase>:<METHOD>:<path>:<timestamp> with that wallet and sends x-accrue-agent, x-accrue-ts and x-accrue-sig. The timestamp may be seconds or milliseconds and must be within 60 seconds of the server; each signature works once. Without a valid signature or credit the answer is HTTP 402 Payment Required with the price and how to pay. Served calls are charged in batches about every 10 minutes; the contract never takes more than the credit.

GET /v1/streams/{id}paidToken, sender, recipient, operator, accrued and withdrawable now with USD value, rate per second, cliff and end.
GET /v1/address/{address}paidEvery stream the address receives, sends or operates, and the USD total withdrawable now.
GET /v1freeThe index.
import { privateKeyToAccount } from "viem/accounts";

const agent = privateKeyToAccount(process.env.AGENT_KEY); // the wallet holding your credits

async function accrue(path) {
  const ts = Date.now(); // seconds or milliseconds both work
  const msg = `accrue:${agent.address.toLowerCase()}:GET:${path}:${ts}`;
  const sig = await agent.signMessage({ message: msg });
  const r = await fetch("https://appaccrue.xyz" + path, {
    headers: { "x-accrue-agent": agent.address, "x-accrue-ts": String(ts), "x-accrue-sig": sig },
  });
  if (r.status === 402) throw new Error((await r.json()).error); // add credits
  return r.json();
}

// an agent paid by the second: what it has earned and can withdraw now
const { withdrawableUsd, incoming } = await accrue("/v1/address/" + agent.address);

Fees and the owner

The protocol fee is 0%. The owner can set a fee of at most 0.5% (50 basis points), charged on top of the amount when a stream is created or topped up, and sent to the treasury in the same transaction. A fee change never touches existing escrow.

The owner can also allow or remove tokens for new streams; streams already running are not affected. The owner has no function that moves escrowed tokens, changes a stream or pauses withdrawals.

Risks

  • Unaudited. The contract is new and tested with unit tests and a fork of mainnet, not audited.
  • Market price. A stream is a number of tokens. The dollar value of a Stock Token stream moves with the stock, both ways.
  • Token controls. Stock Tokens, USDG and WETH have their own issuers and rules. A paused, frozen or restricted token can block a withdrawal or a cancel.
  • Cancelable streams. The unaccrued part of a cancelable stream can be taken back by the sender at any moment.
  • Keys. A stream belongs to the recipient address. If its key is lost, so is the stream.
  • Prices on the site. Dollar values use Chainlink feeds (24/5 for equities) and DexScreener for the 24h change and WETH. They are for display only; the contract uses no prices.

Contracts

Robinhood Chain mainnet, chain id 4663. Unaudited.

AccrueStreams0xd59f…4BD6Every stream, by id
AgentCredits0x758b…96f8Prepaid USDG for the agent API
API meter0x1292…d402Charges served calls, within each credit
Owner0x4175…bAE0Allows tokens, sets the fee (max 0.5%) and the API price (max 0.01 USDG)

Allowed tokens

Ready to stream? Open the app.