Contract reference
Addresses (Quai mainnet, Cyprus-1)
| Contract | Address | Notes |
|---|---|---|
SubscriptionFactory | 0x007420f8fDfBa7048bB04c8B87DE12Df49176665 | deployed 2026-11-03, block 10957967, 11,043-byte runtime |
SubscriptionPlan "Hartii Membership" | 0x0033c5A37dcB57ca4e614DF4c2E66c95DAA77c88 | 500 QUAI / 30 days, created 2026-11-03, block 10957970 |
Both were confirmed live with quai_getCode and read back after deployment. solc 0.8.20, optimizer
200 runs, viaIR. No proxy, no upgrade, no rescue function.
SubscriptionFactory
Write
function createPlan(string name, uint256 price, uint256 period, address payout)
external payable returns (address plan); // msg.value == creationFee
function withdrawPending() external; // pull anything booked to the callerRead
| Function | Launch value | Owner can change? | Hard cap |
|---|---|---|---|
platformFeeBps() | 300 (3%) | yes, new plans only | MAX_PLATFORM_FEE_BPS = 500 |
creationFee() | 20 QUAI | yes | MAX_CREATION_FEE = 100 QUAI |
maxRefundWei() | 10 QUAI | yes | MAX_REFUND_WEI = 50 QUAI |
maxRefundGasPrice() | 100,000 gwei | yes | MAX_REFUND_GAS_PRICE = 1,000,000 gwei |
refundOverheadGas() | 45,000 | yes | MAX_REFUND_OVERHEAD_GAS = 100,000 |
paused() | false | yes | — |
treasury(), owner(), pendingOwner() | — | two-step ownership | — |
MIN_PERIOD / MAX_PERIOD | 1 day / 366 days | no | — |
MAX_NAME_BYTES | 64 | no | — |
allPlans(i), allPlansLength(), isPlan(a), plansOf(creator), pending(a) | — | — | — |
refundOverheadGas is the gas a plan cannot measure from inside the call (the 21,000 intrinsic cost,
the access list, calldata, the event and the final refund transfer). It is calibrated on Orchard and
tunable so it can follow Quai's gas accounting without a redeploy.
Events
PlanCreated(plan, creator, name, price, period, payout, platformFeeBps), PaymentBooked(to, amount),
PendingWithdrawn(to, amount), PlatformFeeUpdated, CreationFeeUpdated, MaxRefundUpdated,
MaxRefundGasPriceUpdated, RefundOverheadGasUpdated, TreasuryUpdated, PausedSet,
OwnershipTransferStarted, OwnershipTransferred.
SubscriptionPlan
Write
function subscribe(uint32 periods) external payable; // msg.value == price * periods, 1 <= periods <= 12
function withdrawPending() external; // works while paused
function setPrice(uint256 newPrice) external; // creator only; future payments only
function setPayout(address newPayout) external; // creator onlyRead
factory(), creator(), name(), price(), period(), payout(), platformFeeBps(),
paidUntil(address) (unix seconds), isActive(address), pending(address), MAX_PERIODS (12),
PUSH_GAS (30,000).
Events
Subscribed(subscriber, periods, paidUntil, fee, refund, creatorShare) — fee + refund + creatorShare
always equals the payment. PaymentBooked(to, amount) when an amount is booked instead of sent.
PendingWithdrawn, PriceUpdated(oldPrice, newPrice), PayoutUpdated(oldPayout, newPayout).
Reverts
| Message | Cause |
|---|---|
Exact creation fee required | msg.value differs from creationFee() |
Bad name | empty or longer than 64 bytes |
Zero price | price is 0 |
Period out of range | period below 1 day or above 366 days |
Zero payout / Bad payout / Payout not Cyprus-1 | payout is zero, the plan, the factory, a precompile, another zone or a Qi address |
Exact price required | msg.value differs from price × periods |
Periods out of range | periods is 0 or above 12 |
Paused | the factory is paused (new plans and payments only) |
Nothing pending / Withdraw failed | nothing booked to the caller, or the caller refused the QUAI |
Not creator / Not owner / Not pending owner | access control |
Example: subscribe with quais
import { quais } from 'quais';
const provider = new quais.BrowserProvider(window.pelagus);
const signer = await provider.getSigner();
const plan = new quais.Contract(
quais.getAddress('0x0033c5a37dcb57ca4e614df4c2e66c95daa77c88'),
[
'function price() view returns (uint256)',
'function subscribe(uint32 periods) payable',
'function isActive(address) view returns (bool)',
],
signer,
);
const periods = 1;
const price = await plan.price();
// quais estimates gas and builds the access list per transaction (required on Quai).
const tx = await plan.subscribe(periods, { value: price * BigInt(periods) });
await tx.wait();
console.log(await plan.isActive(await signer.getAddress())); // trueTrust model
- The plan never holds QUAI except amounts booked to
pendingfor a contract recipient or a failed transfer. Value is sent only to normal Cyprus-1 Quai addresses with no code, with a fixed gas budget and without copying return data. - The owner can change the platform fee for future plans only, within the caps above, and can pause new plans and payments. The owner cannot touch paid time, move a plan's funds, or block a withdrawal.
- The creator can change the price for future payments and the payout address. The creator cannot shorten anyone's paid time.