Skip to main content

Perk Effects System

The Perk Effects System allows any perk to carry configurable effects that activate when claimed. Effects can multiply earnings, protect streaks, grant raffle entries, award badges, and assign Discord roles.

Key principle: Effects are brand-deployable. Any brand can create perks with effects through the Partner Portal — no developer assistance needed.

How It Works

1. Brand creates a perk in the Partner Portal PerkWizard
2. Brand enables one or more effects (checkboxes) and configures values
3. Perk is published to the marketplace with effect metadata
4. User claims the perk (burns RSNC, receives NFT)
5. Post-claim hook creates activation records in the database
6. Bot checks activation records during reward calculation
7. Effects are applied: multiplied earnings, protected streaks, etc.

Effect Types

EffectWhat It DoesDurationStacking
Earning MultiplierMultiplies RSNC earned per reward event7 days (configurable)Replace (latest wins)
Event Cap BoostIncreases daily event claim limits for count-based events7 days (configurable)Replace (latest wins)
Streak ShieldPrevents streak reset when a day is missed7-day shelf lifeAccumulate (up to 5 charges)
Raffle EntryGrants entries into a weekly raffle drawingInstantAccumulate (up to 15/week)
Profile BadgePermanent cosmetic badge on user profilePermanentCollect (own multiple)
Discord RoleGrants a Discord role when claimedPermanent or durationCollect

Creating a Perk with Effects

In the Partner Portal

  1. Open the PerkWizard (Create Perk)
  2. Fill in basic info (name, description, price, supply)
  3. Scroll to the Perk Effects section
  4. Check the effects you want this perk to have
  5. Configure the values for each enabled effect
  6. Set the Effect Scope (where effects apply)
  7. Review and publish

Effect Scope

Effects can apply beyond the creating brand's servers:

ScopeBehavior
Own Brand (default)Effects only apply in the creating brand's servers
Selected PartnersEffects apply in the creating brand's servers + named partner brands
NetworkEffects apply across all Resonance servers (admin-only)

Cross-brand consent required: For "Selected Partners" scope, each partner brand must explicitly accept external effects via the Brand Consent settings in their Partner Portal.

Effect Metadata

Effects are stored in the perk's metadata as a JSON array:

{
"effects": [
{
"type": "earning_multiplier",
"value": { "multiplier": 2.75 },
"duration_days": 7,
"stacking": "replace",
"scope": { "type": "own_brand" },
"max_active_per_user": 1
},
{
"type": "streak_shield",
"value": { "charges": 3 },
"duration_days": 7,
"stacking": "accumulate",
"scope": { "type": "own_brand" },
"max_active_per_user": 5
}
]
}

Effect Resolution

When the bot calculates a reward, the PerkEffectEngine resolves active effects through a 7-step pipeline:

  1. Cache check — KV cache lookup (5-min TTL)
  2. Fetch activations — Query perk_effect_activations for user
  3. Scope filter — Does this effect apply in the current brand context?
  4. Consent filter — Does the current brand accept effects from the source brand?
  5. Group by type — Organize effects by effect_type
  6. Apply stacking — Replace (latest wins), accumulate (sum with cap), highest, or collect
  7. Cache result — Store resolved set in KV

Reward Calculation Chain

final_reward = base_reward
x role_multiplier (from Discord role config)
x perk_earning_multiplier (from active Amplifier effect)
+ flat_bonus (from role config)
x streak_multiplier (from streak days)

The combined role_multiplier x perk_earning_multiplier is capped at 10x (security limit).

API Reference

All endpoints require internal authentication (CF-Worker header or X-Internal-Secret).

Resolve Effects

GET /perk-effects/:brandId/:userIdentifier

Returns the resolved effect set for a user in a brand context.

Response:

{
"earning_multiplier": { "multiplier": 2.75 },
"event_cap_multiplier": null,
"streak_shield": { "charges": 3 },
"badges": [{ "badge_id": "signal_carrier", "badge_tier": "standard" }],
"role_grants": []
}

Activate Effects

POST /perk-effects/activate

Called after a perk claim to create activation records.

Body:

{
"user_identifier": "discord:123456789",
"source_brand_id": "0xBrandAddress",
"collection_id": 42,
"effects": [
{
"type": "earning_multiplier",
"value": { "multiplier": 1.5 },
"duration_days": 7,
"scope": { "type": "own_brand" }
}
]
}

Consume Shield Charge

POST /perk-effects/consume-shield

Atomically consumes one streak shield charge.

Body:

{
"user_identifier": "discord:123456789",
"context_brand_id": "0xBrandAddress"
}

Response:

{
"consumed": true,
"chargesRemaining": 2
}

Cleanup Expired Effects

POST /perk-effects/cleanup

Deactivates expired effects. Called by scheduled CRON.

GET /brand-consent/:brandId
POST /brand-consent/:brandId

Body:

{
"source_brand_id": "0xStartaleAddress",
"allowed_effect_types": ["earning_multiplier", "badge"],
"max_multiplier": 3.0
}
DELETE /brand-consent/:brandId/:sourceBrandId

Raffle API

Add Entries

POST /raffle/enter

Body:

{
"brand_id": "0xBrandAddress",
"user_identifier": "discord:123456789",
"collection_id": 42,
"entry_count": 5,
"rsnc_spent": 400
}

Get Draw Pool

GET /raffle/pool/:brandId/:drawWeek

Execute Draw

POST /raffle/draw

Body:

{
"brand_id": "0xBrandAddress",
"draw_week": "2026-W14",
"prize_config": {
"type": "rsnc_pot",
"winner_share": 0.7,
"burn_share": 0.3
}
}

Security

Validation Limits

EffectMax ValueEnforced At
Earning Multiplier5.0x per perkPerkWizard + post-claim hook
Event Cap Multiplier3.0x per perkPerkWizard + post-claim hook
Streak Shield5 charges maxPerkWizard + activation logic
Raffle Entries15 per purchase, 15 per weekPerkWizard + entry logic
Combined Multiplier10x (role + perk)Bot reward calculation

Brand Isolation

  • Effects are scoped by source_brand_id and effect_scope
  • Cross-brand effects require explicit consent via brand_effect_consent
  • Consent can cap multiplier values independently
  • The brandId used for resolution comes from trusted server config, not user input

Deduplication

A unique index prevents duplicate active effects from the same collection:

UNIQUE(user_identifier, collection_id, effect_type) WHERE is_active = true

Re-claiming the same perk replaces (UPSERT) rather than stacking.

Database Tables

perk_effect_activations

Core table tracking active effects per user.

ColumnTypeDescription
source_brand_idTEXTBrand that created the perk
collection_idBIGINTPerk collection ID
user_identifierTEXTdiscord:{userId}
effect_typeTEXTRegistry key
effect_valueJSONBType-specific payload
effect_scopeJSONBWhere the effect applies
expires_atTIMESTAMPTZNULL for permanent effects
charges_remainingINTFor consumable effects
is_activeBOOLEANDeactivated on expiry/consumption

Cross-brand effect acceptance.

ColumnTypeDescription
brand_idTEXTBrand accepting effects
source_brand_idTEXTBrand whose effects are accepted
allowed_effect_typesTEXT[]NULL = all types
max_multiplierNUMERICSafety cap on multiplier values

raffle_entries / raffle_draws

Weekly raffle system tables. See Raffle API for details.