ERC-20 Content Token Protocol

— ON-CHAIN ACCOUNT VALUE PROTOCOL —

A slot is not just a login key. A slot is the account itself — and every content token consumed within it permanently proves its value on-chain, transparently. 슬롯은 단순한 로그인 키가 아닙니다. 슬롯은 계정 그 자체이며, 그 안의 컨텐츠 토큰 소비 기록은 계정의 가치를 온체인에서 투명하게 증명합니다.

💡 The Account Value Paradox 💡 계정 가치의 역설

When buying or selling a game account on a marketplace, the buyer has no way to know the account's true value.
Level, gear, ranking — none of it can be verified without logging in.

But what if every dollar ever spent on that account was permanently recorded on the blockchain?
Trust comes not from the seller's word, but from numbers on the chain.
게임 계정을 마켓플레이스에서 사고팔 때, 구매자는 계정의 진짜 가치를 알 방법이 없습니다.
레벨, 장비, 랭킹 — 이 모든 것은 로그인 없이는 검증 불가합니다.

그런데 만약, 그 계정에 얼마나 많은 돈이 투자되었는지가 블록체인에 영구 기록된다면?
신뢰는 판매자의 말이 아닌, 체인 위의 숫자에서 나옵니다.

DeOAuth's Content Token system works just like in-app purchases on the Google Play Store. Every time a user buys an item in-game, that spending record (used_balances) is accumulated on-chain — permanently, immutably, and publicly queryable by anyone. DeOAuth의 컨텐츠 토큰 시스템은 구글 플레이스토어의 인앱 결제와 동일한 개념입니다. 사용자가 게임에서 아이템을 구매할 때마다 그 소비 기록(used_balances)이 온체인에 누적됩니다. 이 누적값은 영구적이고 불변이며, 외부에서 누구나 조회할 수 있습니다.

🌐
Platform (WebXcom)
Token issuer. 15% fee on purchase. Operates smart contracts. 컨텐츠 토큰 발행자. 구매 시 15% 수수료 수취. 스마트 컨트랙트 운영.
🏢
Provider (Dev) Provider (개발사)
Game/service operator. 85% revenue on purchase. Calls increaseUsedBal to record spending on-chain. 게임/서비스 운영자. 구매 시 85% 수익 수취. increaseUsedBal 호출로 소비 기록 온체인 기록.
👤
User (Slot) User (슬롯)
Slot = game account. Holds content tokens. used_balances = investment history = account value proof. 슬롯 = 게임 계정. 컨텐츠 토큰 보유. used_balances = 투자 내역 = 계정 가치 증명.

STEP 1 Provider Registration — init_permission_provider Provider 등록 — init_permission_provider

To participate in the Content Token system, you must first register as a Provider. Set your cuid (Content Unique ID) and revenue share ratio (release_per) during registration. 컨텐츠 토큰 시스템에 참여하려면 먼저 Provider로 등록해야 합니다. 등록 시 cuid(Content Unique ID)와 수익 분배 비율(release_per)을 설정합니다.

ℹ️ What is cuid? ℹ️ cuid란?

A cuid is a content identifier issued by the Provider. When a single Provider operates multiple games, each game uses its own cuid. The slot's used_balances is tracked per cuid. cuid는 Provider가 발급하는 컨텐츠 식별자입니다. 하나의 Provider가 여러 게임을 운영할 때 각 게임마다 별도 cuid를 사용합니다. 슬롯의 used_balances는 cuid 단위로 추적됩니다.

POST /v1/development-api/init_permission_provider
ParameterTypeRequiredDescription
client_id string Required Provider client IDProvider의 클라이언트 ID
cuid string Required Content unique identifier (per game)컨텐츠 고유 식별자 (게임별로 구분)
release_per number Required Settlement ratio based on used_balances (0–100)used_balances 기준 정산 비율 (0–100)
owner_addr string Required Provider wallet address to receive revenue수익을 수취할 Provider 지갑 주소
// Provider registration example (JavaScript) | Provider 등록 예시
const response = await fetch('/v1/development-api/init_permission_provider', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: 'your-client-id',
    cuid: 'game-rpg-2026',          // game identifier | 게임 식별자
    release_per: 80,                 // settle 80% of used_balances | used_balances의 80%를 정산
    owner_addr: '0xProviderWallet...'
  })
});

const data = await response.json();
// data.tx_hash — registration transaction hash | 등록 트랜잭션 해시

STEP 2 Buy Content Token — buy_content_token 컨텐츠 토큰 구매 — buy_content_token

When a user pays XOTN, the smart contract instantly distributes revenue to Platform and Provider, then issues content tokens to the slot. 사용자가 XOTN을 지불하면, 스마트 컨트랙트가 즉시 Platform과 Provider에게 수익을 분배하고, 슬롯에 컨텐츠 토큰을 발급합니다.

XOTN Revenue Distribution XOTN 분배 구조

Platform 15%
Provider 85%
POST /v1/development-api/user_buy_content_token
ParameterTypeRequiredDescription
client_id string Required Provider client IDProvider 클라이언트 ID
user_dbkey string Required User DB key (slot identifier)사용자 DB 키 (슬롯 식별자)
amount string Required XOTN amount to pay (wei or ether string)구매할 XOTN 금액 (단위: wei 또는 ether string)
cuid string Required Content unique identifier컨텐츠 고유 식별자
// Buy content token — user pays 0.01 XOTN | 컨텐츠 토큰 구매 — 사용자가 0.01 XOTN 지불
const response = await fetch('/v1/development-api/user_buy_content_token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: 'your-client-id',
    user_dbkey: 'user-uuid-12345',
    amount: '10000000000000000', // 0.01 XOTN (wei)
    cuid: 'game-rpg-2026'
  })
});

const data = await response.json();
// data.tx_hash       — purchase transaction hash | 구매 트랜잭션 해시
// data.token_amount  — content tokens issued | 지급된 컨텐츠 토큰 수량
curl -X POST /v1/development-api/user_buy_content_token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "your-client-id",
    "user_dbkey": "user-uuid-12345",
    "amount": "10000000000000000",
    "cuid": "game-rpg-2026"
  }'

Response

{
  "success": true,
  "tx_hash": "0x1a2b3c4d5e6f...",
  "token_amount": "100",
  "xotn_paid": "10000000000000000",
  "platform_fee": "1500000000000000",
  "provider_received": "8500000000000000"
}
sequenceDiagram participant User as 👤 User participant App as 💻 Your App participant API as 🔐 B-Server API participant Chain as ⛓️ XOTN Network User->>App: Item purchase request (pay XOTN) App->>API: POST /user_buy_content_token API->>Chain: buy_content_token(slot, amount) payable Chain->>Chain: Distribute 15% to Platform Chain->>Chain: Distribute 85% to Provider Chain->>Chain: Issue content tokens to slot Chain-->>API: TX hash API-->>App: success + tx_hash + token_amount App-->>User: Token balance updated
sequenceDiagram participant User as 👤 User participant App as 💻 Your App participant API as 🔐 B-Server API participant Chain as ⛓️ XOTN Network User->>App: 아이템 구매 요청 (XOTN 지불) App->>API: POST /user_buy_content_token API->>Chain: buy_content_token(slot, amount) payable Chain->>Chain: Platform 15% 분배 Chain->>Chain: Provider 85% 분배 Chain->>Chain: 슬롯에 컨텐츠 토큰 발급 Chain-->>API: TX hash API-->>App: success + tx_hash + token_amount App-->>User: 토큰 잔액 업데이트

STEP 3 Record Spending — increaseUsedBal 소비 기록 — increaseUsedBal

This is the key.

Every time a user purchases an item in-game, call increaseUsedBal.
Each call permanently accumulates in the slot's used_balances.

used_balances is an on-chain receipt that proves "how much was invested in this account."
Anyone, anytime, without logging in.
이것이 핵심입니다.

사용자가 게임 내에서 아이템을 구매할 때마다 increaseUsedBal을 호출하세요.
이 호출 하나하나가 슬롯의 used_balances에 영구 누적됩니다.

used_balances는 "이 계정에 얼마나 투자했는가"를 증명하는 온체인 영수증입니다.
로그인 없이도, 누구나, 언제든지 조회할 수 있습니다.

Called from the Provider's server when a user consumes in-game items. Deducts from the content token balance while permanently accumulating the same amount in used_balances. 사용자가 게임 내 아이템을 소비할 때 Provider 서버에서 호출합니다. 컨텐츠 토큰 잔액에서 차감되며, 동시에 used_balances에 같은 금액이 영구 누적됩니다.

Slot State After increaseUsedBal increaseUsedBal 호출 후 슬롯 상태 변화

balance (current)balance (현재 잔액)
900
tokens (100 deducted)토큰 (100 차감)
used_balances (cumulative)used_balances (누적 소비)
+100
permanent (immutable)영구 누적 (불변)
POST /v1/development-api/increase_used_content_token
ParameterTypeRequiredDescription
client_id string Required Provider client IDProvider 클라이언트 ID
user_dbkey string Required User DB key (slot identifier)사용자 DB 키 (슬롯 식별자)
amount string Required Content token amount to consume소비할 컨텐츠 토큰 수량
cuid string Required Content unique identifier컨텐츠 고유 식별자
encrypted_message string Required AES-256-CTR encrypted payloadAES-256-CTR 암호화된 페이로드

⚠️ Must be called from Provider server ⚠️ 반드시 Provider 서버에서 호출

increaseUsedBal must never be called directly from the client (browser/app). It must be called from the Provider's backend server with an AES-256-CTR encrypted payload. If the encryption key is exposed, spending records can be arbitrarily manipulated. increaseUsedBal은 클라이언트(브라우저/앱)에서 직접 호출해서는 안 됩니다. Provider의 백엔드 서버에서 AES-256-CTR 암호화 페이로드와 함께 호출해야 합니다. 암호화 키가 노출되면 임의 소비 기록 조작이 가능합니다.

// Record spending — user consumes 100 tokens on item (called from Provider server)
// 소비 기록 — 사용자가 아이템 100토큰 소비 (Provider 서버에서 호출)
import crypto from 'crypto';

function encryptPayload(payload, key, iv) {
  // AES-256-CTR encryption | AES-256-CTR 암호화
  const cipher = crypto.createCipheriv('aes-256-ctr',
    Buffer.from(key, 'hex'),
    Buffer.from(iv, 'hex')
  );
  let encrypted = cipher.update(JSON.stringify(payload), 'utf8', 'hex');
  encrypted += cipher.final('hex');
  return encrypted;
}

const payload = {
  client_id: 'your-client-id',
  user_dbkey: 'user-uuid-12345',
  amount: '100',      // token amount to consume | 소비할 토큰 수량
  cuid: 'game-rpg-2026'
};

const response = await fetch('/v1/development-api/increase_used_content_token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    encrypted_message: encryptPayload(payload,
      process.env.DEAUTH_KEY,
      process.env.DEAUTH_IV
    )
  })
});

// After this call: slot's used_balances += 100 (permanently recorded on-chain)
// 이 호출 후 슬롯의 used_balances += 100 (온체인에 영구 기록)

STEP 4 Account Value Query — balanceAdex 계정 가치 조회 — balanceAdex

When trading accounts on a marketplace, buyers can verify a slot's investment history through balanceAdex — no login required. 마켓플레이스에서 계정을 거래할 때, 구매자는 balanceAdex를 통해 로그인 없이도 계정의 투자 내역을 검증할 수 있습니다.

balanceAdex(slot)(balance, used_balances)

balance: Currently held content tokens (unspent or refunded)
used_balances: Total tokens ever consumed in this account (immutable, cumulative)

Higher used_balances → more items purchased → higher account value.
balance: 현재 보유 중인 컨텐츠 토큰 (환불 또는 미소비분)
used_balances: 지금까지 이 계정에서 소비된 총 토큰 (불변, 누적)

used_balances가 높을수록 → 더 많은 아이템을 구매했을 가능성 → 계정 가치 높음.
GET /v1/development-api/balance_adex/{slot_address}
// Public query — no auth required, anyone can call | 공개 조회 — 인증 불필요, 누구나 호출 가능
const slotAddress = '0xUserSlotAddress...';
const response = await fetch(
  `/v1/development-api/balance_adex/${slotAddress}`
);

const data = await response.json();
// data.balance       — current content token balance | 현재 컨텐츠 토큰 잔액
// data.used_balances — total spent (= account investment history) | 총 소비 누적값 (= 계정 투자 내역)

console.log(`Balance: ${data.balance} tokens`);       // 잔액
console.log(`Total invested: ${data.used_balances} tokens spent`); // 총 투자

Response

{
  "success": true,
  "slot": "0xUserSlotAddress...",
  "balance": "900",
  "used_balances": "3200",
  "cuid": "game-rpg-2026"
}

ℹ️ Marketplace Integration Example ℹ️ 마켓플레이스 활용 예시

Account trading platforms can use balanceAdex to automatically estimate account value. 계정 거래 플랫폼에서 balanceAdex를 활용해 계정 가치를 자동 산정할 수 있습니다.

// Marketplace — auto-estimate account price | 마켓플레이스 — 계정 가격 자동 산정
async function estimateAccountValue(slotAddress, pricePerToken = 0.001) {
  const { used_balances } = await fetchBalanceAdex(slotAddress);
  
  // Minimum guaranteed value based on spending history | 소비 기록 기반 최소 보증 가치 산정
  const minGuaranteedValue = Number(used_balances) * pricePerToken;
  
  return {
    used_balances,
    min_value_xotn: minGuaranteedValue,
    verified: true,       // on-chain verified | 온체인 검증 완료
    requires_login: false // no login needed | 로그인 불필요
  };
}

STEP 5 Provider Settlement — releaseUsedBal Provider 정산 — releaseUsedBal

Providers can receive additional settlement through releaseUsedBal, based on the slot's used_balances. This is a separate settlement mechanism from the 85% revenue share at purchase time. Provider는 releaseUsedBal을 통해 슬롯의 used_balances를 기반으로 추가 정산을 받을 수 있습니다. 이는 구매 시점의 수익 분배(85%)와는 별도의 정산 메커니즘입니다.

⚠️ Activation Requirement ⚠️ 활성화 조건

releaseUsedBal only works when the Platform sets _cuid_releaseTokenEnabled[cuid] = true for that cuid. Disabled by default. Contact the WebXcom admin team to request activation. releaseUsedBal은 Platform이 해당 cuid에 대해 _cuid_releaseTokenEnabled[cuid] = true로 설정한 경우에만 동작합니다. 기본값은 비활성화입니다. 활성화 요청은 WebXcom 관리팀에 문의하세요.

POST /v1/development-api/release_used_balance
ParameterTypeRequiredDescription
client_id string Required Provider client IDProvider 클라이언트 ID
cuid string Required Content unique identifier컨텐츠 고유 식별자
slot string Required Target slot address for settlement정산 대상 슬롯 주소
// Provider settlement call | Provider 정산 호출
const response = await fetch('/v1/development-api/release_used_balance', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_id: 'your-client-id',
    cuid: 'game-rpg-2026',
    slot: '0xUserSlotAddress...'
  })
});

const data = await response.json();
// data.released_amount — settled token amount | 정산된 토큰 수량
// data.tx_hash         — settlement transaction hash | 정산 트랜잭션 해시

🔄 Full Lifecycle 🔄 전체 라이프사이클

The complete flow from content token issuance to account value verification. 컨텐츠 토큰의 발급부터 계정 가치 검증까지 전체 흐름입니다.

sequenceDiagram participant Dev as 🏢 Provider participant API as 🔐 B-Server participant Chain as ⛓️ XOTN Network participant User as 👤 User participant Market as 🛒 Marketplace Dev->>API: init_permission_provider(cuid, release_per) API->>Chain: Register Provider on-chain Chain-->>Dev: Registration complete User->>API: user_buy_content_token(amount) API->>Chain: buy_content_token(slot, amount) payable Chain->>Chain: Platform 15% / Provider 85% distribution Chain-->>User: Content tokens issued User->>Dev: In-game item purchase Dev->>API: increase_used_content_token(amount, encrypted) API->>Chain: increaseUsedBal(slot, cuid, amount) Chain->>Chain: balance -= amount, used_balances += amount Note over Chain: used_balances = permanent record Market->>API: balance_adex(slot) — account value query API->>Chain: balanceAdex(slot) Chain-->>Market: (balance, used_balances) Note over Market: Verify account investment without login
sequenceDiagram participant Dev as 🏢 Provider participant API as 🔐 B-Server participant Chain as ⛓️ XOTN Network participant User as 👤 User participant Market as 🛒 Marketplace Dev->>API: init_permission_provider(cuid, release_per) API->>Chain: 온체인 Provider 등록 Chain-->>Dev: 등록 완료 User->>API: user_buy_content_token(amount) API->>Chain: buy_content_token(slot, amount) payable Chain->>Chain: Platform 15% / Provider 85% 분배 Chain-->>User: 컨텐츠 토큰 발급 User->>Dev: 게임 내 아이템 구매 Dev->>API: increase_used_content_token(amount, encrypted) API->>Chain: increaseUsedBal(slot, cuid, amount) Chain->>Chain: balance -= amount, used_balances += amount Note over Chain: used_balances = 영구 기록 Market->>API: balance_adex(slot) — 계정 가치 조회 API->>Chain: balanceAdex(slot) Chain-->>Market: (balance, used_balances) Note over Market: 로그인 없이 계정 투자 내역 검증

🔒 AES-256-CTR Encryption Reference 🔒 AES-256-CTR 암호화 레퍼런스

Sensitive API calls like increaseUsedBal require AES-256-CTR encryption. Use the DEAUTH_KEY (32 bytes) and DEAUTH_IV (16 bytes) issued at Provider registration. increaseUsedBal 등 민감한 API 호출에는 AES-256-CTR 암호화가 필요합니다. Provider 등록 시 발급되는 DEAUTH_KEY(32바이트)와 DEAUTH_IV(16바이트)를 사용합니다.

⚠️ Key Security Rules ⚠️ 키 보안 원칙

Store encryption keys and IVs in environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault). Never hardcode them in client code or commit them to version control. 암호화 키와 IV는 반드시 환경변수 또는 시크릿 매니저(AWS Secrets Manager, HashiCorp Vault)에 저장하세요. 클라이언트 코드에 하드코딩하거나 버전 관리에 커밋하지 마세요.

// Node.js — AES-256-CTR Encryption | AES-256-CTR 암호화
import crypto from 'crypto';

/**
 * Encrypts a JSON payload with AES-256-CTR.
 * AES-256-CTR로 JSON 페이로드를 암호화합니다.
 * @param {object} payload  - object to encrypt | 암호화할 객체
 * @param {string} key      - 32-byte hex key (DEAUTH_KEY) | 32바이트 hex 키
 * @param {string} iv       - 16-byte hex IV (DEAUTH_IV) | 16바이트 hex IV
 * @returns {string} hex-encoded encrypted string | hex 인코딩된 암호화 문자열
 */
function encryptPayload(payload, key, iv) {
  const cipher = crypto.createCipheriv('aes-256-ctr',
    Buffer.from(key, 'hex'),
    Buffer.from(iv, 'hex')
  );
  let encrypted = cipher.update(JSON.stringify(payload), 'utf8', 'hex');
  encrypted += cipher.final('hex');
  return encrypted;
}

// Usage example | 사용 예시
const encrypted = encryptPayload(
  { client_id: 'xxx', user_dbkey: 'yyy', amount: '100', cuid: 'game-rpg-2026' },
  process.env.DEAUTH_KEY,
  process.env.DEAUTH_IV
);
# Python — AES-256-CTR Encryption | AES-256-CTR 암호화
# pip install pycryptodome
import json
from Crypto.Cipher import AES
import binascii

def encrypt_payload(payload: dict, key: str, iv: str) -> str:
    """
    Encrypts a dict payload with AES-256-CTR.
    AES-256-CTR로 dict 페이로드를 암호화합니다.
    key: 32-byte hex string | 32바이트 hex 문자열
    iv:  16-byte hex string | 16바이트 hex 문자열
    """
    key_bytes = bytes.fromhex(key)
    iv_bytes  = bytes.fromhex(iv)
    
    cipher = AES.new(key_bytes, AES.MODE_CTR,
                     nonce=iv_bytes[:8],         # CTR nonce (8 bytes)
                     initial_value=iv_bytes[8:])  # counter initial value | 카운터 초기값
    
    plaintext = json.dumps(payload).encode('utf-8')
    encrypted = cipher.encrypt(plaintext)
    return binascii.hexlify(encrypted).decode('ascii')

# Usage example | 사용 예시
encrypted = encrypt_payload(
    {'client_id': 'xxx', 'user_dbkey': 'yyy', 'amount': '100', 'cuid': 'game-rpg-2026'},
    os.environ['DEAUTH_KEY'],
    os.environ['DEAUTH_IV']
)
// Verify encryption implementation against server result | 암호화 구현 검증 — 서버의 암호화 결과와 비교
async function verifyEncryption(testString) {
  // 1. Encrypt on server | 서버에서 암호화
  const serverRes = await fetch('/v1/development-api/test_encryption', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: 'your-client-id',
      test_string: testString
    })
  });
  const { encrypted_string: serverEncrypted } = await serverRes.json();

  // 2. Encrypt locally | 로컬에서 암호화
  const localEncrypted = encryptPayload(
    testString,
    process.env.DEAUTH_KEY,
    process.env.DEAUTH_IV
  );

  // 3. Compare | 비교
  const matches = localEncrypted === serverEncrypted;
  console.log('Verify:', matches ? '✅ Match' : '❌ Mismatch'); // 암호화 검증
  return matches;
}

await verifyEncryption('Hello DeOAuth');