Overview
Game Store is the execution layer for purchasing games. Responsibilities:- Validate game from Registry
- Process payment
- Mint license via PGC-1
Dependencies
| Component | Purpose |
|---|---|
| PGC-1 | License minting |
| Registry | Source of truth for game data |
| Treasury | Platform fee receiver |
Data Structures
PriceConfig
| Field | Type | Description |
|---|---|---|
| price | uint256 | Base price |
| currency | address | Payment token address |
| discountBps | uint16 | Discount in bps |
State
| Variable | Type | Description |
|---|---|---|
| registry | address | Address of Registry contract |
| governance | address | Governance Role |
| treasury | address | Platform fee receiver |
| prices | mapping(string => PriceConfig) | Game pricing |
| platformFeeBps | uint16 | Platform fee (basis points) |
| publisherBalances | mapping(address => mapping(address => uint256)) | Publisher earnings per token |
Functions
1. setPrice
Sets base price for a game.function setPrice(gameId, price, currency)
Params
| Name | Type | Description |
|---|---|---|
| gameId | string | Game identifier |
| price | uint256 | Base price |
| currency | address | Payment token |
Rules
- Only publisher
- msg.sender MUST match
PGC1(contractAddress).getPublisher() currencyMUST be a valid supported payment token- Game MUST exist in Registry
priceMAY be 0 for free games
2. setDiscount
Sets discount in basis points.function setDiscount(gameId, discountBps)
Params
| Name | Type | Description |
|---|---|---|
| gameId | string | Game identifier |
| discountBps | uint16 | Discount (e.g. 1000 = 10%) |
Rules
- Only publisher
- msg.sender MUST match
PGC1(contractAddress).getPublisher() discountBpsMUST be<=10000
3. getFinalPrice
Returns final price after discount.function getFinalPrice(gameId): uint256
Returns
| Type | Description |
|---|---|
| uint256 | Final price |
Logic
finalPrice = price - (price * discountBps / 10000)4. buyGame
Executes purchase and mints license.function buyGame(gameId)
Flow
- Fetch game from Registry
- Validate
status == approved - Validate
game.contractAddressexists - Get PGC-1 contract
- Get publisher:
publisher = PGC1.getPublisher()
- Load
PriceConfigcurrency = prices[gameId].currency
- Calculate
finalPrice - Validate payment token and amount
- Prevent duplicate purchase by checking
PGC1.canAccessGame(msg.sender) - Calculate:
platformFeepublisherRevenue
- Receive payment
- Process split:
transfer(platformFee → treasury)publisherBalances[publisher][currency] += publisherRevenue
- Call
PGC1.mintLicense(msg.sender, 0) - Revert whole transaction if mint fails
Internal Calls
const game = Registry.getGame(gameId)const finalPrice = getFinalPrice(gameId)PGC1(game.contractAddress).mintLicense(msg.sender, 0)publisherBalances[publisher][currency] += publisherRevenue
Rules
- If
finalPrice == 0, payment transfer MAY be skipped - Game MUST have a valid PriceConfig before purchase (Including Free Game)
- Purchase, accounting update, and minting MUST be executed atomically
- Mint MUST only happen AFTER payment is recorded
- If mint fails → transaction MUST revert
- Double purchase MUST be prevented if the buyer already owns a valid license for the same game.
5. setPlatformFee
Updates platform fee.function setPlatformFee(feeBps)
Params
| Name | Type | Description |
|---|---|---|
| feeBps | uint16 | Fee in basis points |
Rules
- Only governance
feeBpsMUST be<=10000
6. withdraw
Withdraw publisher earnings per token.function withdraw(token)
Params
| Name | Type | Description |
|---|---|---|
| token | address | Token address |
Rules
- Only publisher
- Balance MUST be > 0
- Withdrawal MUST revert if transfer fails
Behavior
amount = publisherBalances[msg.sender][token]publisherBalances[msg.sender][token] = 0transfer(token → msg.sender)
7. setGovernance
Updates governance address.function setGovernance(governance)
Params
| Name | Type | Description |
|---|---|---|
| governance | address | New governance address |
Rules
- Only current governance
- Governance MUST NOT be zero address
- Event SHOULD be emitted
8. setTreasury
Updates treasury receiver address.function setTreasury(treasury)
Params
| Name | Type | Description |
|---|---|---|
| treasury | address | New treasury receiver |
Rules
- Only governance
- Treasury MUST NOT be zero address
- Event SHOULD be emitted
9. getPriceConfig
Returns pricing configuration for a game.function getPriceConfig(gameId)
Params
| Name | Type | Description |
|---|---|---|
| gameId | string | Game identifier |
Returns
| Name | Type | Description |
|---|---|---|
| price | uint256 | Base price |
| currency | address | Payment token address |
| discountBps | uint16 | Discount in bps |
10. getPublisherBalance
Returns withdrawable publisher balance for a token.function getPublisherBalance(publisher, token)
Params
| Name | Type | Description |
|---|---|---|
| publisher | address | Publisher address |
| token | address | Token address |
Returns
| Type | Description |
|---|---|
| uint256 | Withdrawable token balance |
11. getPlatformFee
Returns current platform fee in basis points.function getPlatformFee()
Returns
| Type | Description |
|---|---|
| uint16 | Platform fee in basis points |
12. getTreasury
Returns treasury receiver address.function getTreasury()
Returns
| Type | Description |
|---|---|
| address | Treasury receiver address |
13. getGovernance
Returns governance address.function getGovernance()
Returns
| Type | Description |
|---|---|
| address | Governance address |
14. getRegistry
Returns registry contract address.function getRegistry()
Returns
| Type | Description |
|---|---|
| address | Registry address |
Payment Flow
Hybrid (Push + Escrow) Payments are split during purchase:- Platform fee → sent directly to treasury
- Publisher revenue → stored in contract (escrow)
Flow
- User pays
finalPrice - Contract calculates:
- platformFee
- publisherRevenue
- Execute transfers:
- send platform fee to treasury
publisherBalances[publisher][currency] += publisherRevenue
- Mint license
Withdraw
- Publisher MUST call
withdraw(token)to claim revenue - Treasury does NOT use withdraw, because platform fee is transferred directly during purchase
Revenue Split
| Component | Formula |
|---|---|
| platformFee | finalPrice * platformFeeBps / 10000 |
| publisherRevenue | finalPrice - platformFee |
Access Control
| Function | Access |
|---|---|
| setPrice | Publisher |
| setDiscount | Publisher |
| withdraw | Publisher |
| getFinalPrice | Public |
| buyGame | Public |
| setPlatformFee | Governance |
| setGovernance | Governance |
| setTreasury | Governance |
| getPriceConfig | Public |
| getPublisherBalance | Public |
| getPlatformFee | Public |
| getTreasury | Public |
| getGovernance | Public |
| getRegistry | Public |
Requirements
- Game MUST be
approvedin Registry - Registry MUST return a valid
contractAddress - Registry MUST return game status
- Store MUST be authorized minter in PGC-1
- PGC-1 MUST implement
getPublisher() - PGC-1 MUST implement
canAccessGame(user) - PGC-1 MUST implement
mintLicense(to, expiresAt) - Payment MUST succeed before mint
Notes
- Price is dynamic (not stored in Registry)
- Supports future extensions:
- subscription
- bundle
- regional pricing