okx-growth-competition
OKX Agentic Walletユーザー向けに、独占的な取引コンペティションの全ライフサイクルを管理します。ユーザーは、コンペティションの発見、ルール確認、参加、取引、ランキング確認、報酬請求をスムーズに行えます。Web3取引コンペティションへの参加を簡素化し、報酬獲得の機会を最大化し、取引体験と効率を向上させます。
npx skills add https://github.com/okx/onchainos-skills --skill okx-growth-competitionBefore / After 効果比較
1 组エージェントの支援なしでは、ユーザーは複数のページを手動で確認し、コンペティションのルール、取引資格、リアルタイムランキングをチェックし、報酬請求も手動で行う必要がありました。このプロセスは時間がかかり、重要な情報を見落としやすく、1回のコンペティションあたり約60分を要していました。
エージェントを使用することで、ユーザーは簡単なコマンドでコンペティションの発見、ルール確認、参加、ランキングチェック、報酬請求を完了できます。エージェントが自動的に進捗を追跡し、リマインダーを提供するため、1回のコンペティションあたりの管理時間を約5分に大幅に短縮し、参加効率と体験を著しく向上させます。
OKX Growth Competition — Trading Competition
Agentic Wallet exclusive trading competitions. Full lifecycle: list → detail → join → trade → rank → claim.
CLI reference: references/cli-reference.md
Facts about every Agentic Wallet competition (always true unless backend confirms otherwise)
chainId— single id. A trading chain AND the claim/reward chain (rewards are paid on this chain; its contract address is here).participateChainIds— array of ids returned by bothlistanddetailendpoints (may be empty on activities created before the field was added). Additional trading chains (no claim role).
The full trading-chain set for an activity is the UNION: {chainId} ∪ participateChainIds. Trades on any chain in that union count toward the same competition standing. The claim chain is chainId only.
- The trading-chain set is
{chainId} ∪ participateChainIds(dedup). Render each id as a human-readable name via the canonicalChain id → display nametable. Currently supported competition chains:1 → Ethereum,196 → X Layer,501 → Solana. - Trades on any chain in the union count toward the same competition standing. Trades on chains NOT in the union do not count. Never tell a user "your chain doesn't count" without first checking the union.
myRankInfo.userTotal = 0means the user has not yet hit the qualifying threshold or the backend metric pipeline has not picked up their trades yet — it does NOT mean the user's chain is unsupported.competition_ranktakes a single optionalwallet. Omit it for self-rank — the tool auto-resolves the chain-appropriate address from the active account based oncompetition_detail.chainId(the primary chain used for reward claim and rank indexing). Pass an explicit address ONLY when querying someone else's rank; the address chain (EVM0x...else Solana) must match the activity's primary chain or the tool rejects the call (no silent wrong-chain queries). The chain you query on is just a lens — trades on every other chain in the union still count toward the same ranking.- Claim path uses only
chainId(that's where the reward contract / activity address lives). Everything else — trading eligibility, "which chains count", chain-list display in templates — uses the union of both fields.
When the user asks "Does my chain count for this competition?" or "Which chains can I trade on?", answer from the union of chainId and participateChainIds. When rendering the "Chain" column / line in a template, render that same union (deduplicated) — see the {supportedChains} computation rule in each Step's field-mapping section.
Identity resolution invariant (deterministic — always answerable)
Algorithm (always runs internally; AI cannot bypass):
| Tool / call shape | Identity sent | Why |
|---|---|---|
competition_user_status (always) | accountId (self) | self-only tool; covers every chain in participateChainIds in one call |
competition_rank with no wallet | accountId (self) | default self-rank, no chain picking needed |
competition_rank with wallet=<addr> | walletAddress (cross-user) | the tool validates that addr's chain family (EVM 0x... else Solana) matches the activity's primary chainId; mismatch → call is rejected (no silent wrong-chain query) |
competition_claim (pre-check) | accountId (self) | claim is always self-action; uses accountId for the eligibility check |
How to answer "which identity did you use?":
| Call | What to say (translate to user's language) |
|---|---|
Self-query (no wallet arg) | "I used your accountId — the backend looks up your status across every chain in this competition (participateChainIds) in one call." |
Cross-user query (wallet arg passed) | "I used the wallet address <addr> you specified — it was validated to match the activity's primary chain before the call." |
Forbidden answers (paraphrase patterns observed and are wrong):
- ❌ "The tool sends both
accountIdandwalletAddress" — Wrong. Exactly one, never both. - ❌ "The tool picks EVM or SOL based on chain" — Wrong (post-refactor). That was the old wallet-only model. Self-queries now send
accountId, no chain pick. - ❌ "I'm not sure which one the tool ended up sending" — Wrong. It is deterministic from the call shape; always answerable from the above table.
For multi-activity queries (e.g. competition_user_status with no activity_name), the same accountId is reused for every activity in the iteration — the backend joins by accountId, not address. The answer is uniformly "I used your accountId for all activities".
⚠️ Mandatory reading order
The template structure is fixed; the language follows the user — see the ## Output Language rule above. When the user writes Chinese, translate the template strings to natural Chinese. When the user writes English, use English as written. Placeholders (including chain display names from {supportedChains}) stay as-is.
Quick router (user intent → template section):
- "list competitions / show available competitions" → Step 1 (table, optionally split by Active / Ended)
- "show details / show rules / show prize pool" → Step 2 (Basic info block + 4 reward sections, with
{supportedChains}chain line and required participation / Skill copy) - "register / join" → Step 3 (registration success fixed template + disclaimer)
- "trade for me" → Step 4 (delegate to okx-dex-swap)
- "leaderboard / ranking" → Step 5
- "claim reward" → Step 6 (use
competition_claimMCP, atomic) - "show registered wallet" → Additional Flows / Query Registered Wallet
- "export wallet" → Additional Flows / Wallet Export Guard
If the user's intent does not clearly map to one of the above, ask which they meant before responding — do not invent a freeform format.
Pre-flight
Read
../okx-agentic-wallet/_shared/preflight.md. If missing, read_shared/preflight.md.
Command Index
| # | Command | Auth | Description |
|---|---|---|---|
| 1 | onchainos competition list [--status 0|1|2] [--page-size N] [--page-num N] | None | List Agentic Wallet exclusive competitions (default status=0, active only) |
| 2 | onchainos competition detail --activity-id <id> | None | Get rules, prize pool, chain, timeline |
| 3 | onchainos competition rank --activity-id <id> [--wallet <addr>] --sort-type <type> [--limit N] | None | Leaderboard + user rank. Omit --wallet to auto-resolve from the active account; the command fetches competition_detail.chainId and picks the chain-appropriate address. Pass --wallet ONLY to query someone else's rank — the address chain must match the activity chain or the call is rejected. MCP tool competition_rank mirrors this (single optional wallet). Discover available sort-type values from competition_detail → tabConfigs[].rankFieldConfig[].sortValueMap.descend (do not hardcode). |
| 4 | onchainos competition user-status [--activity-id <id>] | Wallet login | Check participation & reward status using the active user's accountId (omit --activity-id to check all activities). MCP tool competition_user_status takes no wallet args — accountId is loaded from the local wallet store. |
| 5 | onchainos competition join --activity-id <id> --evm-wallet <addr> --sol-wallet <addr> --chain-index <chain_id> | Wallet login | Register for the competition. MCP tool competition_join makes both wallet args optional. |
| 6 | onchainos competition claim --activity-id <id> --evm-wallet <addr> --sol-wallet <addr> | Wallet login | CLI returns unsigned calldata. MCP tool competition_claim is atomic — wallets are optional, signing + broadcast happens inside the tool, returns txHash array. Surfaces needContact: true for top-tier winners (see Step 6 contact-collection sub-flow). |
| 7 | onchainos competition submit-contact --activity-id <id> --contact-type <Telegram|WeChat|Email|Twitter> --contact-value <text> | Wallet login | Record a contact method for top-tier winners (called only after a claim that returned needContact: true). MCP tool competition_submit_contact looks up accountId + joinedAddress internally. |
--status (request filter): 0=active, 1=ended, 2=all
activityStatus (response field): 3=active, 4=ended — these are DIFFERENT values from the request filter
sort-type: dynamic — read from competition_detail → tabConfigs[].rankFieldConfig[].sortValueMap.descend. Currently observed values: 1=PnL% (realized ROI), 7=PnL (realized profit). Future activities may add more — always trust tabConfigs over hardcoding.
Output Rules
Internal-only IDs vs user-facing display. Internal numeric IDs (
activityId,chainIndex,accountId) are returned in tool responses on purpose — they are needed to chain calls between tools (e.g. aftercompetition_join, you may need to callcompetition_detailwith the activity id to fill the success template). Keep them in the data layer; never render them in user-visible messages.
Forbidden user-visible patterns (do NOT produce output like this):
- ❌
Agentic Trading Contest (#107) - ❌
#106 (agenticwallettest1) - ❌ A column titled
ID,Activity ID,#, or any equivalent in the user's language - ❌ A row labeled
Activity ID,ID,#, or any equivalent in the user's language (e.g. a 2-column key/value table where a row maps the id label to the numeric value) — this is the same violation as a column, just rotated - ❌ Any reference like
competition 107,id 107,the activity id is 107, or the same wording in another language - ❌ Numeric id rendered anywhere a user can see it, regardless of label, shape, or language
Correct user-visible pattern:
- ✅
Agentic Trading Contest - ✅ When disambiguating two activities with the same name, append
chainName(e.g.Agentic Trading Contest (Solana)), never the ID.
Behind the scenes (allowed and expected):
- ✅ Reading
activityIdfrom acompetition_user_status/competition_joinresponse and passing it tocompetition_detailto fetch the data needed by a fixed template. - ✅ Any tool-to-tool chaining via numeric ids — as long as the final user-facing message omits them.
When the user asks to act on a specific activity (e.g. "claim Agentic Trading Contest"), the MCP tools competition_claim / competition_join accept activity_name and resolve the id server-side, so you can also use names directly without doing your own lookup.
Time Formatting (MANDATORY)
Preferred — use backend-formatted strings when available:
For the competition_detail response, the backend now returns pre-formatted UTC+8 strings — use them verbatim, do NOT recompute from the raw epoch:
| Field | Format | Example |
|---|---|---|
startTimeFormatted | yyyy-MM-dd HH:mm:ss (UTC+8, no suffix) | "2026-05-07 18:00:00" |
endTimeFormatted | yyyy-MM-dd HH:mm:ss (UTC+8, no suffix) | "2026-05-21 18:00:00" |
When rendering, take the string as-is and append (UTC+8) for the timezone marker (e.g. 2026-05-07 18:00:00 (UTC+8)). This is the only correct path for detail view times — the backend has already done the math; computing from startTime / endTime epoch invites off-by-an-hour errors and AI hallucination.
Raw fields — only use when no *Formatted counterpart is present:
| Field | Format | Example raw | Notes |
|---|---|---|---|
startTime, endTime (list response — no formatted variant returned) | 10-digit Unix seconds | 1778148000 | Multiply by 1000 only if the runtime expects ms |
joinTime, claimTime (user-status response) | 10-digit Unix seconds | 1778148000 | Same |
rankUpdateTime (rank response) | 13-digit Unix milliseconds | 1774359000638 | Divide by 1000 to convert to seconds first |
If you see a 13-digit value where a 10-digit is documented (or vice versa), do NOT silently coerce — flag it as a backend anomaly.
Display format — exact, no improvisation:
- Detail view (with
*Formattedavailable):YYYY-MM-DD HH:mm:ss (UTC+8)— example:2026-05-07 18:00:00 (UTC+8)(use the formatted string + suffix) - List view compact range:
YYYY-MM-DD ~ YYYY-MM-DD(UTC+8 day boundary) — example:2026-05-07 ~ 2026-05-21(compute from rawstartTime/endTime, take the date portion only) joinTime/claimTimein user-facing context:YYYY-MM-DD HH:mm (UTC+8)(compute from raw epoch)rankUpdateTime(last refresh marker):YYYY-MM-DD HH:mm:ss (UTC+8)(compute from raw ms epoch)
Timezone rule: ALL competition times displayed to the user are in UTC+8 (China Standard Time). The competition product is operated in UTC+8; never display raw UTC, never use the user's local timezone.
When you DO need to convert from raw epoch (no formatted field available):
- Identify the field's documented unit (seconds or milliseconds — see table above).
- Convert to a Date object using the correct unit.
- Format as a UTC+8 wall-clock string per the display format above.
- Always append the
(UTC+8)suffix.
...
ユーザーレビュー (0)
レビューを書く
レビューなし
統計データ
ユーザー評価
この Skill を評価