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 (AltsCodex)
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 — Verify Ownership (Developer AA Wallet) Provider 등록 — 소유권 인증 (개발자 AA 지갑)

To participate in the Content Token system, your application must first be tied to a verified owner. There is no public REST endpoint for provider registration. It is currently done in the Developer Center with Verify Ownership, which proves ownership with your developer AA wallet. 컨텐츠 토큰 시스템에 참여하려면 먼저 앱이 인증된 소유자와 연결되어 있어야 합니다. Provider 등록용 공개 REST 엔드포인트는 없습니다. 현재는 개발자센터의 Verify Ownership(소유권 인증)으로 처리하며, 개발자 AA 지갑으로 소유권을 증명합니다.

StepWhereWhat happens
1 Sign up회원가입 Verify your email and set a 6-digit PIN. Your developer AA wallet is created automatically.이메일 인증 후 6자리 PIN 을 정하면 개발자 AA 지갑이 자동으로 만들어집니다.
2 Register App Register your application and receive its client_id.앱을 등록하고 client_id 를 받습니다.
3 My Applications → Verify Ownership Enter your PIN. The platform checks it against your AA wallet and records the wallet as the app owner (OWNER VERIFIED (AA) badge).PIN 을 입력하면 플랫폼이 AA 지갑으로 검증한 뒤 그 지갑을 앱 소유자로 기록합니다(OWNER VERIFIED (AA) 배지).
4 My Applications → ⛓ Register on-chain Enter your PIN again. The platform registers the provider on the contract on your behalf, and the on-chain owner is your developer AA wallet (ON-CHAIN badge).PIN 을 한 번 더 입력하면 플랫폼이 대신 컨트랙트에 Provider 를 등록합니다. 온체인 owner 는 개발자 AA 지갑입니다(ON-CHAIN 배지).

ℹ️ 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 단위로 추적됩니다.

⚠️ On-chain registration is done by the platform ⚠️ 온체인 등록은 플랫폼이 대리로 합니다

You do not call the contract yourself and you do not need MetaMask. When you press Register on-chain, the platform key sends init_permission_provider(owner, provider, cuid) with owner set to your developer AA wallet address. Only the platform can register a provider, so nobody can claim your app's slot on the contract first. 컨트랙트를 직접 호출하지 않으며 MetaMask 도 필요 없습니다. Register on-chain 을 누르면 플랫폼 키가 owner 를 개발자 AA 지갑 주소로 넣어 init_permission_provider(owner, provider, cuid) 를 보냅니다. Provider 등록은 플랫폼만 할 수 있어 다른 사람이 내 앱 자리를 컨트랙트에 먼저 차지할 수 없습니다.

Order matters. Verify Ownership first, then Register on-chain. Apps registered before developer wallets existed have no owner record, so set up your wallet and run both steps once per existing app. Card sales and payouts for registered apps appear in the Settlements menu. 순서가 중요합니다. 먼저 Verify Ownership, 그다음 Register on-chain 입니다. 개발자 지갑 도입 전에 등록한 앱은 소유자 기록이 없으므로, 지갑을 설정한 뒤 기존 앱마다 두 단계를 한 번씩 실행하세요. 등록된 앱의 카드 매출과 지급 내역은 정산 메뉴에서 볼 수 있습니다.

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 AltsCodex admin team to request activation. releaseUsedBal은 Platform이 해당 cuid에 대해 _cuid_releaseTokenEnabled[cuid] = true로 설정한 경우에만 동작합니다. 기본값은 비활성화입니다. 활성화 요청은 AltsCodex 관리팀에 문의하세요.

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 A as 🛡️ A-Server participant API as 🔐 B-Server participant Chain as ⛓️ XOTN Network participant User as 👤 User participant Market as 🛒 Marketplace Dev->>A: Verify Ownership (PIN) → verify_owner_aa(dev_uuid, client_id, pin_hash) A->>API: Developer AA wallet check API-->>A: OK A-->>Dev: owner_aa_address recorded Dev->>A: Register on-chain (PIN) → register_owner_onchain A->>API: RegisterProviderOwner API->>Chain: init_permission_provider(owner = developer AA) signed by platform key 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 A as 🛡️ A-Server participant API as 🔐 B-Server participant Chain as ⛓️ XOTN Network participant User as 👤 User participant Market as 🛒 Marketplace Dev->>A: 소유권 인증(PIN) → verify_owner_aa(dev_uuid, client_id, pin_hash) A->>API: 개발자 AA 지갑 검증 API-->>A: 통과 A-->>Dev: owner_aa_address 기록 Dev->>A: 온체인 등록(PIN) → register_owner_onchain A->>API: RegisterProviderOwner API->>Chain: init_permission_provider(owner = 개발자 AA) 플랫폼 키 서명 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');