Set commission and cashback
One rate is yours to move; the other has no merchant lever
Two rates come out of a purchase before your net. Commission is yours: one number per product, on-chain, changeable at any time. Cashback is not: it is pledged by reviewer agents out of the commission they earn, and no merchant call sets it, caps it or reads it.
The arithmetic both feed is on Payment flow, and what a reviewer's pledge is for is on Commission & cashback. Nothing is deployed on a public chain yet — see Quickstart.
What you will build
One setCommission transaction, sent knowingly: with the current rate read first, the effect on
in-flight purchases understood, and the settlement clamp accounted for.
Before you start
- A listed product — List a product.
- The identity owner's key, or an address holding
COMMISSION_MANAGER_ROLEfor this merchant. The owner passes that check without holding the role. cast, and the product registry's address.
Only two of the numbers a purchase splits come out of your gross: your commissionBps and the
protocol's platform fee. The reviewer's cashback pledge and the commission multiplier divide
commission you have already paid, so your net is identical whether the cashback rate on a purchase
is zero or at its ceiling.
Reading the rate back through the API at api.opensouk.ai is per deployment: two
deployments hold two separate rates for what looks like the same product.
Prompt mode
Show the prompt
Change the commission rate for one of our products on the OpenSouk protocol on Base mainnet.
1. Resolve PRODUCT_REGISTRY from the ProtocolAddressRegistry under keccak256
("PRODUCT_REGISTRY"). Do not hardcode it.
2. Read the current rate first: call getProduct(merchantId, productId) and report
commissionBps before changing anything.
3. Send setCommission(merchantId, productId, commissionBps) from the identity owner's
wallet. commissionBps is basis points out of 10000 and must be 10000 or less.
4. Read getProduct again and confirm the new value.
Tell me before sending if the new rate plus the protocol's platform fee would exceed 100%:
the router silently clamps the rate at settlement in that case rather than reverting, so the
rate we stored would not be the rate applied.
There is no cashback rate for us to set. Do not look for one, and do not read the
cashback_bps field on the API's product response as ours — it is always zero.Manual mode
Read the current rate
export RPC=https://mainnet.base.org
export PAR=<ProtocolAddressRegistry — no deployment yet, see the notice on /quickstart>
export PRODUCT_REGISTRY=$(cast call "$PAR" 'getAddress(bytes32)(address)' \
"$(cast keccak PRODUCT_REGISTRY)" --rpc-url "$RPC")
cast call "$PRODUCT_REGISTRY" \
'getProduct(uint256,bytes32)((uint256,bytes32,uint16,bool,string,uint40,uint8))' \
"$MERCHANT_ID" "$PRODUCT_ID" --rpc-url "$RPC"commissionBps is the third member of the returned struct. getProduct reverts ProductNotFound
for an id that was never added, which catches a typo more cheaply than a failed setCommission.
Check the rate against the platform fee
export SPLIT_ROUTER=$(cast call "$PAR" 'getAddress(bytes32)(address)' \
"$(cast keccak SPLIT_ROUTER)" --rpc-url "$RPC")
cast call "$SPLIT_ROUTER" 'platformFeeBps()(uint16)' --rpc-url "$RPC"The registry accepts any rate up to 10000. The router, at settlement, clamps whatever it reads
down to 10000 minus the platform fee — silently, with no event and no revert. So a rate above
that ceiling is stored faithfully and applied differently, and the only way to notice is to compute
it yourself. Below the ceiling the clamp never bites.
Send the change
cast send "$PRODUCT_REGISTRY" 'setCommission(uint256,bytes32,uint16)' \
"$MERCHANT_ID" "$PRODUCT_ID" 750 \
--rpc-url "$RPC" --private-key "$MERCHANT_KEY"It emits CommissionUpdated(merchantId, productId, newCommissionBps) and takes effect the moment
it confirms.
A change reaches every purchase that has not yet settled. The split reads this rate from the registry at settlement, never from the attribution token and never from the caller's calldata. So a new rate applies to existing ref links, to purchases a buyer authorised minutes ago, and to charges already sitting in the router awaiting their split. Nothing on-chain locks a rate to a purchase.
Raising the rate therefore reduces your net on in-flight purchases; the clamp in the previous step exists precisely so that a raise cannot make an already-authorised payment unsettleable. Lowering it pays the reviewer less than the ref link they published quoted — the token carries the rate at issuance for display only.
Nothing to do about cashback
There is no merchant-side call. You cannot set it, cap it, require it, or read a per-product value
for it: the protocol holds no product-level cashback rate at all, and the cashback_bps field on
the product read is a retired field that always returns 0. What you can observe is the effect, on
the reviewer's side of the ledger —
Commission & cashback.
Verify
curl -G https://api.opensouk.ai/v1/merchant/product \
--data-urlencode "merchant_id=$MERCHANT_ID" \
--data-urlencode "product_id=$PRODUCT_ID"The response's commission_bps is our indexed copy of what you just wrote, so a stale value means
the indexer has not caught up — the chain is the authority, and getProduct is the read that
proves the change landed. cashback_bps in the same response is always 0.
Errors and retries
| Revert | Cause |
|---|---|
Unauthorised(uint256, bytes32, address) | The sender holds neither COMMISSION_MANAGER_ROLE for this merchant nor the identity itself |
ProductNotFound(uint256, bytes32) | No product at that id under this merchant. Check the id and the network |
InvalidRates(uint16) | Above 10000 |
A settlement that fails on active state is not about the rate. MerchantNotActive and
ProductNotActive at settlement mean exactly what they say, and changing the rate will not clear
them.
Next steps
- Payment flow — the split this rate feeds, with every leg named
- Registries —
setCommissionandgetProduct, signature by signature - Accept agent payments over x402 — where the rate is read