Glamora — Decentralized Fashion Social Platform

Repo: Terese678/glamora · 7 contracts · ~2,500+ lines
Audited: February 21, 2026

Overview

Glamora is a decentralized fashion social platform where creators share content (fashion shows, lookbooks, tutorials, behind-the-scenes, reviews) and earn cryptocurrency tips from fans. Features include dual-token payments (sBTC + USDCx stablecoin), three subscription tiers, NFT collections with marketplace, social following, creator vaults for gas-optimized batch withdrawals, and bridge deposit tracking for cross-chain USDCx from Ethereum.

The architecture is well-separated: main.clar handles business logic, storage-v2.clar is the data layer with authorized-contract access control, bridge-adapter.clar manages vaults and payment intelligence, and glamora-nft.clar implements SIP-009 NFTs with collection management.

Findings Summary

SeverityCountDescription
CRITICAL2Public users can't tip, duplicate error code
HIGH3Follow broken for public users, subscription overwrite, locked fees
MEDIUM4Rounding loss, dead code, no NFT transfer on sale, vault dead-end
LOW3Empty strings, cancel-after-expiry, hardcoded FX rate

Critical Findings

C-01 Public Users Cannot Tip — record-tip Requires Creator Profile

The record-tip function in storage unconditionally unwraps the tipper's creator-profiles entry to update their total-tips-sent:

(tipper-profile (unwrap! (map-get? creator-profiles tipper) ERR-USER-NOT-FOUND))

Public users only exist in public-user-profiles. Any tip from a public user will revert with ERR-USER-NOT-FOUND. This breaks the core value proposition — "public users can tip creators they love."

Fix: Check both profile maps. If tipper is a public user, update public-user-profiles instead, using the total-tips-sent field that already exists there.

C-02 Duplicate Error Code u318 — Silent Collision

Two different error constants share the same value:

(define-constant ERR-INVALID-AMOUNT (err u318))
(define-constant ERR-SUBSCRIPTION-CHECK-FAILED (err u318))

When subscribe-to-creator calls has-active-subscription and it fails, the error is indistinguishable from an invalid tip amount. This makes debugging impossible and could mask subscription validation bypasses.

Fix: Change ERR-SUBSCRIPTION-CHECK-FAILED to (err u319) (skipping the already-used u318).

High Findings

H-01 Follow/Unfollow Only Works Between Creators

Both create-follow and remove-follow in storage unwrap creator-profiles for both the follower and the followed user:

(follower-profile (unwrap! (map-get? creator-profiles follower) ERR-USER-NOT-FOUND))
(following-profile (unwrap! (map-get? creator-profiles following) ERR-USER-NOT-FOUND))

Public users cannot follow anyone, and nobody can follow a public user. The main contract's has-profile check accepts public users, but the storage layer rejects them. The feature silently fails for the majority user type.

H-02 Single Subscription Per User — Silent Overwrite

The user-subscriptions map uses the subscriber's principal as the sole key. When a user subscribes to Creator B while already subscribed to Creator A, the has-active-subscription check passes (because it only checks the specific creator), and map-set silently overwrites the Creator A subscription. No refund, no notification — the old subscription data is permanently lost.

Fix: Use a composite key {subscriber: principal, creator: principal} to allow multiple concurrent subscriptions.

H-03 No Platform Fee Withdrawal — Revenue Permanently Locked

Platform fees (5% of every tip and subscription) accumulate in CONTRACT-ADDRESS for both sBTC and USDCx tokens. There is no admin function to withdraw these fees. The platform-fees-earned variable tracks the amount, but the actual tokens are permanently locked in the contract principal with no exit path.

Fix: Add an admin-only withdraw-platform-fees function that uses as-contract to transfer accumulated fees to the admin.

Medium Findings

M-01 Integer Division Rounding Loss in Fee Calculation

calculate-platform-fee uses (/ (* amount 5) 100). Clarity integer division truncates. For a tip of 999,999 sats: fee = 49,999 instead of the exact 49,999.95. Over thousands of tips, these fractions compound to measurable loss for the platform.

M-02 Storage Contract Reference Mismatch

The constant STORAGE-CONTRACT points to .storage-v3, but the data variable storage-contract points to a hardcoded testnet principal. All actual calls use the constant .storage-v3 directly. The data variable and its getter get-storage-contract are dead code that returns a misleading value.

M-03 NFT Marketplace — Sale Doesn't Transfer NFT

The storage contract's complete-nft-sale records the sale history and marks the listing inactive, but there's no corresponding NFT transfer (nft-transfer?) from seller to buyer in the main contract's marketplace flow. The buyer pays but never receives the NFT.

M-04 Creator Vault Withdrawal Dead-End

complete-vault-withdrawal in bridge-adapter requires is-authorized (only callable from main.clar). But main.clar has no function that calls complete-vault-withdrawal. Creators can set thresholds and deposit, but the actual withdrawal is unreachable — a dead-end in the call chain.

Low Findings

L-01 No Input Validation on Empty Strings

Profile creation in main.clar accepts empty strings for username, display name, and bio. An empty username "" could be registered and reserved in the usernames map, creating a phantom entry that blocks no real username but wastes storage.

L-02 Subscription Cancel After Expiry Breaks Stats

The cancel function doesn't verify the subscription hasn't already expired. Cancelling an expired subscription decrements the creator's total-subscribers and tier counter, pushing them below their true values. Over time, these counters can drift to inaccurate (or underflowing) values.

L-03 Hardcoded Nigerian Naira Exchange Rate

USD-TO-NIGERIAN-NAIRA-RATE is hardcoded at 1,500 NGN/USD. The real rate fluctuates significantly (~1,500-1,700+ in 2024-2026). The earnings stability report will show increasingly inaccurate Naira values with no update mechanism.

Architecture Assessment

FeatureStatus
Multi-contract architecture✅ Clean main → storage → adapter separation
Dual-token payment (sBTC + USDCx)✅ Well-implemented with token selection
Creator profiles & content publishing✅ Working with IPFS integration
Tipping with platform fee split⚠️ Broken for public users (C-01)
Social following⚠️ Creator-only at storage layer (H-01)
Subscription tiers (Basic/Premium/VIP)⚠️ Single-sub per user limitation (H-02)
NFT minting & collections (SIP-009)✅ Working with collection limits
NFT marketplace❌ No NFT transfer on sale (M-03)
Creator vault (gas savings)⚠️ No withdrawal trigger (M-04)
Bridge deposit verification✅ Structure present (not live)
Payment intents✅ Deferred execution pattern
Access control✅ Authorized-contract pattern
Event logging✅ Comprehensive print statements
Platform fee extraction❌ No withdrawal function (H-03)

Verdict: Glamora is the most ambitious and well-documented Clarity project in this audit series. The multi-contract architecture with clean separation of concerns (main → storage, main → bridge-adapter, main → nft) demonstrates strong software engineering. The dual-token system, creator vaults for gas optimization, and payment intent pattern for deferred execution show sophisticated design thinking.

However, the public-user story is fundamentally broken at the storage layer — the contract only truly works for creator-to-creator interactions, contradicting the platform's own documentation. The NFT marketplace collects payment without transferring tokens, and platform revenue has no withdrawal path. With targeted fixes to the storage functions (checking both profile maps), composite subscription keys, and marketplace NFT transfers, this could be a genuinely functional decentralized social platform.