What the platform earns, and referral rewards
Reading the addon's own side of the book — the three admin-profit entry types it writes, why a customer WIN is recorded as a platform loss, the two fee legs, and the affiliate condition settlement fires.
Forex Investment is a book where a customer's win is your loss. The overview says the platform's own side of every settled investment is recorded; this page says where that record is, what each entry means, and the three ways of reading it that produce a number that does not exist.
Everything below lands in one table, admin_profit, read at
Admin → Finance → Revenue Analytics (/admin/finance/profit), gated on
access.admin.profit.
The three entry types
| Type | Written when | Sign | Booked against |
|---|---|---|---|
FOREX_DEPOSIT |
A customer funds a forex account, in the same transaction as the debit | Positive — a fee you collected | The wallet debit's transaction id |
FOREX_WITHDRAW |
An admin approves a withdrawal. Never on rejection | Positive | The pending transaction's id |
FOREX_INVESTMENT |
Settlement, in the same transaction as the payout | Either — see below | {investmentId}_payout on a win, {investmentId}_house on a loss |
Filter the Type column on /admin/finance/profit to any of the three. The
row itself carries type, amount, currency, chain, description,
createdAt and a link to the transaction that produced it.
A WIN is recorded as a loss, on purpose
Settlement calls one helper, recordInvestmentOutcome, and it inverts the sign
deliberately:
| Result | What the platform did | What is recorded |
|---|---|---|
WIN |
Paid the customer a profit it has to fund | A negative FOREX_INVESTMENT row for the ROI amount, plus a best-effort debit of the Super Admin treasury |
LOSS |
Kept the slice of principal the customer forfeited | A positive FOREX_INVESTMENT row for the same magnitude, credited to the Super Admin treasury |
DRAW |
Nothing moved | No row at all |
The amount on the row is the ROI magnitude, not the principal. A 1,000
principal on a plan paying 15% that settles WIN writes -150, not -1,150
and not -1,000.
The wallet movement behind a WIN and the one behind a LOSS cannot share a
reference. transaction.referenceId is unique platform-wide, so reusing
one investment id for both legs would collide — which is why the helper appends
_payout on the payout leg and _house on the retained-principal leg before
handing the id down.
The wallet layer then adds one more suffix of its own: the treasury debit lands
under {investmentId}_payout_loss, the treasury credit under
{investmentId}_house_fee. The idempotency keys behind them are
platform_loss_FOREX_INVESTMENT_{investmentId}_payout and
platform_fee_FOREX_INVESTMENT_{investmentId}_house, which is what makes a
re-run of the cron pay nothing twice. The admin_profit row itself links to
the movement by transactionId, so you only need these references when that
link is NULL and you are searching the transaction table by hand.
Two things that follow from the payout leg being best-effort:
- The negative row is always written, even when the treasury cannot cover the payout, so your profit report nets out correctly regardless. The wallet debit is capped at the Super Admin wallet's actual balance.
- A short treasury writes a row with no transaction behind it.
transactionIdisNULLon that row, the backend log carries Treasury short on FOREX_INVESTMENT payout or Recorded FOREX_INVESTMENT loss ... with NO treasury debit underPLATFORM_FEE, and nothing on the admin screen flags it. The row is the only evidence the payout happened.
The wallet type on the house side of a settlement is fixed at FIAT,
regardless of the plan's own wallet type. On a platform whose plans are
denominated SPOT USDT, the Super Admin's FIAT USDT wallet is the one
credited on a customer loss and debited on a customer win — not the SPOT wallet
their fees land in.
The admin_profit row is correct either way; only the wallet the money passes
through is affected. Fund the FIAT wallet in each plan currency you run, or the
payout leg will be recorded without a matching treasury debit indefinitely.
The currency stamped on a FOREX_INVESTMENT row is the forex account's bound
currency, falling back to the plan's currency, falling back to USD when the
account has never been funded. On a single-denomination install those are all
the same value; on a mixed one they are not.
Reading the figures without inventing a number
admin_profit.currency is a per-row label, and nothing above the row groups on
it consistently:
- Top fee sources — grouped by
typeonly. The number besideFOREX INVESTMENTis a bareSUM(amount)across every currency on the platform, rendered without a symbol. On a mixed-currency book it is meaningless. - Top currencies — grouped by currency, across every type. Valid as a currency total, useless as a forex figure.
- Today / This week / This month — grouped by currency. Valid.
The only honest per-product read is the table itself: filter Type to one of
the three forex values, then read one currency at a time. If you are querying
directly, GROUP BY type, currency is the minimum.
amount is DECIMAL(36,18), so it is exact — unlike forex_investment.amount,
which is a DOUBLE. Do not reconcile one against the other without rounding
deliberately.
The two fee legs
Neither fee is configured anywhere in this addon. Both come from core's currency tables.
| Leg | Where the fee comes from | When it is collected |
|---|---|---|
| Deposit | FIAT: zero, always — the fiat currency table has no fee column. SPOT: the percentage on the exchange_currency row, plus the network's fixed withdrawal fee when a chain is named and your provider is Binance or KuCoin |
Immediately, in the same transaction as the wallet debit |
| Withdrawal | The same calculation, quoted and debited at submission | On approval only. Rejecting returns amount plus fee to the forex account and collects nothing |
A deposit reversal returns the amount and the fee to the customer's wallet.
It does not write a compensating admin_profit row, so a reversed deposit
leaves its original FOREX_DEPOSIT fee row standing. Account for that when
reconciling a month with reversals in it.
The full behaviour of both queues is in Deposits and withdrawals.
The affiliate reward
One condition in this addon pays a referrer, and it fires from the settlement cron.
| Condition name | FOREX_INVESTMENT |
| Fires | Once per investment, at settlement, before the completion email |
| Amount passed to the engine | The principal — not the profit, not the payout |
| Currency passed | The plan's currency |
| Deduplication id | FOREX_INVESTMENT:forex_investment:{investmentId} |
| Requires | The mlm extension installed and enabled. Without it the call returns immediately |
Because the principal is what is passed, a PERCENTAGE reward pays a
percentage of the amount invested, and the condition's minimum amount is
compared against the principal in the plan's own currency with no conversion. A
minimum of 100 against a BTC-denominated plan can never be met.
Set it at Admin → Affiliate → Program → Conditions
(/admin/affiliate/condition), gated on view.affiliate.condition and
edit.affiliate.condition. The editable fields are title, description,
reward, rewardType (FIXED or PERCENTAGE), rewardWalletType,
rewardCurrency, rewardChain, minAmount, image and status. A save is
refused if a PERCENTAGE reward exceeds the Maximum Commission Rate
ceiling on the affiliate settings screen — a FIXED reward is an absolute
amount rather than a percentage, so that ceiling never applies to it, at any
size — and turning a condition on is refused when it competes with one already
active unless you acknowledge the overlap.
Most affiliate conditions are seeded off. This one is not. A fresh install
seeds FOREX_INVESTMENT active, with rewardType FIXED, reward 100,
minAmount 100, rewardWalletType SPOT and rewardCurrency USDT.
That means the first referred customer whose 100-unit investment settles earns their referrer a flat 100 USDT reward — larger than the investment on most plans. Open the Conditions screen and set it deliberately before you open the product, or turn it off.
Existing installs are untouched: the seeder skips by name, so whatever you already chose stands.
The reward creates a reward record for the referrer, writes them a Referral Reward Earned notification, and notifies admins holding View MLM Rewards. It does not credit a wallet directly — payout follows the affiliate addon's own rules. See Commission conditions.
Reward processing sits in its own try/catch in the cron's post-processing,
after the settlement transaction has committed. It cannot block or reverse a
settlement, and a failure produces no error anywhere an operator looks — only a
line under FOREX_INVESTMENT_POST_PROCESS in the backend log, or under MLM
when the condition itself is missing or inactive.
A referrer reporting a missing reward is a log question, never a screen question.
Two condition names that do nothing
The affiliate engine still accepts FOREX_PROFIT and FOREX_TRADE — they are
listed among the condition names it recognises, and the affiliate screens
group them under the forex product — but nothing in this addon fires
either. FOREX_PROFIT is seeded disabled; FOREX_TRADE is not seeded at all.
Enabling FOREX_PROFIT changes nothing and pays nobody. Its title, Forex
Profit Commission, describes a payout on investment profit that this addon has
never had a code path for. If you want a percentage of profit paid out, the
only working lever is FOREX_INVESTMENT with a PERCENTAGE reward — which
pays on the principal, not the profit.
The FX_TRADE_COMMISSION and FX_TRADE_VOLUME conditions belong to
Forex & Multi-Asset Trading and are
unrelated to anything on this page.
A monthly reconciliation that works
-
Open Revenue Analytics (
/admin/finance/profit) and filter the Type column toFOREX_INVESTMENT. Read one currency at a time — the summary cards above the table will not do this for you. -
Net the signs. Negative rows are payouts you funded; positive rows are forfeited principal you kept. The sum per currency is the addon's real contribution, and it can legitimately be negative.
-
Add the two fee types.
FOREX_DEPOSITandFOREX_WITHDRAWare always positive, and always in the currency the customer moved. -
Check the log for
PLATFORM_FEEover the same window. Rows recorded against an empty treasury, and fees dropped because no Super Admin user exists, are logged there and nowhere else. -
Cross-check against the forex dashboard's per-currency profit rows. They are computed from
forex_investment.profitand answer a different question — the customers' side. The two move in opposite directions by design.
If the two disagree by the size of one settlement, the usual cause is a
settlement whose plan was soft-deleted: the investment is skipped and left
ACTIVE, so it appears in neither book. See
Troubleshooting.