• Duel sudah dibuka di Robinhood Chain
  • Luncurkan duel dua token, tiga pool, satu transaksi
  • Ronde ditutup dalam --:--:--
  • Ronde empat jam pembelian kembali pemenang dibatasi harganya
  • Likuiditas terkunci selamanya, tanpa token tim

Membangun di Duel

Pool Duel adalah pool Uniswap V4 standar dengan sebuah hook, dan setiap angka yang ditampilkan aplikasi berasal dari view dan event publik. Bot, terminal, dasbor, dan agregator dapat membangun di atasnya tanpa perlu meminta izin.

Membaca duel

DuelLens mengumpulkan semua hal tentang sebuah duel dalam satu panggilan:

function getPair(uint256 pairId) external view returns (PairView memory);
function getPairs(uint256 offset, uint256 limit) external view returns (PairView[] memory);

Sebuah PairView memuat id duel, kreator, dan URI metadatanya, kedua sisi (token, nama, simbol, suplai, harga dan tick pool, ETH per token, ETH nyata di pool), tiga kunci pool, proporsi saat ini (gaugeBps, bagian sisi A dalam basis poin), rata-rata ronde sejauh ini (roundGaugeBps), pot, biaya kreator, volume dan pembelian kembali sepanjang masa, pajak anti-snipe saat ini, nomor, awal, dan akhir ronde serta apakah ada transaksi di ronde itu (roundTraded), waktu dan tick peluncuran (launchTime, startTick), apakah duel itu duel pendukung, kategorinya (category, sesuai penomoran aplikasi, 0 untuk tanpa kategori), ketentuan biayanya (fees), dan porsi dari bagiannya yang kini diambil protokol (protocolRateBps, dari 10.000): protokol mendapat fees.protocolShareBps × protocolRateBps / 10.000 dari biaya swap, dan pot mendapat sisa dari bagian itu. Di kontrak dan ABI-nya, sisi B adalah D: d, tokenD, keyD.

Hook memiliki view yang lebih kecil: pairCount(), getPair(pairId), poolKeys(pairId), gaugeBps(pairId), snipeTaxBps(pairId), protocolRateBps(), protocolFees(), dan firstRoundEnd(launchTime).

Bertransaksi

Router Uniswap V4 mana pun dapat bertransaksi di pool sebuah duel. Susun kunci pool dari poolKeys(pairId):

Poolcurrency0currency1feetickSpacing
ETH / AETH (address(0))token A0200
ETH / BETH (address(0))token B0200
A / Balamat token yang lebih kecilyang lebih besar060

Hook memungut biayanya melalui return delta, jadi ambil kuotasi dengan V4 Quoter: jawabannya mencakup setiap biaya dan pajak. Peralihan melalui ETH adalah quoteExactInput-nya di sepanjang jalur token → ETH → token lainnya.

Swap yang biayanya dipungut hook sebelum berjalan (pembelian atau peralihan dengan input pasti, penjualan dengan output pasti) harus terisi penuh: swap yang akan terhenti lebih awal karena batas harganya, atau karena pool ETH kekurangan ETH, di-revert dengan PartialFill. PoolManager membungkus error dari hook: PartialFill dan ArbitrageStarved tiba di dalam WrappedError(address target, bytes4 selector, bytes reason, bytes details), dan di dalam UnexpectedRevertBytes(bytes revertData) ketika V4 Quoter yang melaporkannya. Dekode reason untuk membaca error dari hook.

Setiap transaksi diakhiri penyelarasan ulang oleh hook, yang butuh gas sendiri: swap yang menyisakan terlalu sedikit gas untuknya di-revert dengan ArbitrageStarved alih-alih melewatinya. Kirim swap dengan gas dari estimasi, jangan pernah kurang, dan beri kelonggaran: swap pertama setelah penutupan sebuah ronde juga menyelesaikan ronde itu (pembelian kembalinya, lalu penyelarasan ulang, sekitar 170.000 gas lebih banyak), yang tidak dihitung oleh estimasi yang dibuat sebelum penutupan. Aplikasi menambahkan 500.000 gas ke setiap estimasi.

DuelRouter menyediakan satu panggilan untuk swap di satu pool:

function swapExactIn(
    PoolKey calldata key,
    bool zeroForOne,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient,
    uint256 deadline
) external payable returns (uint256 amountOut);

dan satu untuk peralihan melalui ETH, di mana sisi yang Anda tinggalkan dijual di pool ETH-nya dan seluruh ETH yang dihasilkan dibelanjakan di pool sisi lainnya:

function swapThroughEth(
    PoolKey calldata keyIn,
    PoolKey calldata keyOut,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient,
    uint256 deadline
) external returns (uint256 amountOut);

Penjualan ke pool ETH Duel yang berhenti di harga peluncuran pool karena ETH-nya habis, dikirim lagi untuk sisanya dalam panggilan yang sama, setelah penyelarasan ulang hook membeli kembali dari pool apa yang diterima pool di antara kedua sisi: hingga SALE_PASSES() (8) swap, selama harga pool masih di bawah batas swap. Setiap swap membayar biaya hook atas ETH-nya sendiri, dan minAmountOut menghitung semuanya. V4 Quoter Uniswap memberi kuotasi untuk satu swap: melampaui apa yang dieksekusinya, ia menjawab NotEnoughLiquidity. Aplikasi menghitung kuotasi penjualan seperti itu dengan menjalankan router sebagai trader (sebuah eth_call dengan state override, DuelSimulator).

Apa yang tidak dapat lagi dibeli kembali oleh penyelarasan ulang diteruskan oleh sell melalui pool di antara kedua sisi ke pool ETH sisi lainnya, tempat penyelarasan ulang memindahkan ETH yang membayarnya:

function sell(
    PoolKey calldata keyIn,
    PoolKey calldata keyCross,
    PoolKey calldata keyOther,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient,
    uint256 deadline
) external returns (uint256 amountOut);

sell menjual ke keyIn seperti swapExactIn, lalu sisanya melalui keyCross, yang menerima semuanya (biaya peralihannya dibakar, seperti pada setiap peralihan), dan ke keyOther, juga tahap demi tahap. minAmountOut menghitung ETH dari kedua pool. Apa yang juga tidak dapat dibeli kembali oleh keyOther dibayarkan kepada recipient dalam token pool itu, dan tidak dihitung dalam output minimum. Aplikasi menjual melalui sell ketika V4 Quoter menolak sebuah penjualan dan DuelSimulator.simulateSale menunjukkan bahwa sell menjual semuanya untuk ETH.

Router V4 lain melakukan satu swap. Hook tidak menolak penjualan dengan input pasti yang melampaui apa yang sanggup dibayar ETH di pool (biayanya dipungut setelah swap, atas ETH yang dibayarkan): penjualan itu terisi sebagian, sampai harga peluncuran pool, dan sisanya tetap pada penjual. Atur output minimum sesuai yang bisa terisi, lalu jual sisanya dalam transaksi berikutnya, ke apa yang sudah dibeli kembali oleh penyelarasan ulang.

Kirim ETH dengan swapExactIn untuk membeli. Untuk menjual atau beralih, berikan persetujuan token kepada router, atau biarkan sebuah permit (EIP-2612) menyetujuinya dalam transaksi yang sama: swapExactInWithPermit, swapThroughEthWithPermit, dan sellWithPermit menerima argumen yang sama dan permit pemanggil untuk tepat amountIn, yang ditandatangani untuk router:

struct Permit {
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
}

Untuk setiap token Duel, pemilik dompet menandatangani permit dalam domain EIP-712 bernama Duel, versi 1, yang terikat pada alamat token tersebut (eip712Domain() token itu, ERC-5267, menyatakannya). Router memakai permit lebih dulu dan mengabaikan kegagalannya: permit yang sudah dikirim siapa pun telah menetapkan allowance, dan permit yang gagal tidak mengubah apa pun; swap lalu memakai apa yang ditanggung allowance yang sudah ada dan di-revert jika tidak cukup. Setelah swap yang hanya terisi sebagian, bagian allowance yang belum dipakai tetap berlaku: tenggat permit membatasi kapan tanda tangan dapat dipakai, tetapi tidak mengakhiri allowance yang sudah diberikan. Router hanya menarik token dari pemanggil. ETH dari sebuah peralihan tetap berada di PoolManager untuk dibelanjakan pool kedua; pool Duel membelanjakan semuanya, atau swap di-revert oleh hook (PartialFill), dan di pool lain mana pun yang tidak dapat dibelanjakan dikembalikan kepada pemanggil.

Meluncurkan duel

DuelFactory.createPair(CreateParams) meluncurkan sebuah duel; kirim biaya peluncuran dan pembelian peluncuran bersamanya, kelebihannya dikembalikan. CreateParams terdiri dari nameA, symbolA, nameD, symbolD (sisi B), metadataURI, buyA, buyD (ETH yang dibelanjakan untuk tiap sisi saat peluncuran, bebas pajak anti-snipe), supporter, category (sesuai penomoran kategori di aplikasi, 0 untuk tanpa kategori), dan config, yaitu configHash() milik factory seperti yang Anda baca: peluncuran di-revert dengan ConfigChanged jika ketentuannya berubah sejak saat itu. Sebuah nama memakai 1 sampai 128 byte, ticker 1 sampai 40, dan URI metadata paling banyak 4.096 (InvalidMetadata); pembelian peluncuran di atas 5% dari suplai sebuah sisi di-revert (CreatorBuyTooLarge).

Factory dimulai dengan peluncuran dijeda. Selama paused() bernilai true, hanya owner() yang dapat meluncurkan duel; panggilan pihak lain dibatalkan dengan CreationPaused. Pemilik membuka peluncuran dengan setPaused(false). Duel yang sudah ada tetap diperdagangkan.

Event

KontrakEventKapan
DuelFactoryPairCreated(pairId, creator, tokenA, tokenD)Sebuah duel diluncurkan
DuelHookPairRegistered(pairId, tokenA, tokenD, creator, category)Pool-pool-nya didaftarkan ke hook, dalam transaksi yang sama; category adalah temanya, sesuai penomoran kategori di aplikasi (0 untuk tanpa kategori)
DuelHookFeeTaken(pairId, fee, snipeTax)Transaksi dengan ETH membayar biayanya
DuelHookSwitchFeeBurned(pairId, token, amount)Sebuah peralihan membakar biayanya
DuelHookArbitrageCaptured(pairId, profit)Penyelarasan ulang mengisi pot
DuelHookRoundSettled(pairId, roundIndex, averageTick, winner, ethSpent, tokensBurned)Sebuah ronde ditutup
DuelHookCreatorFeesClaimed(pairId, to, amount)Seorang kreator dibayar
DuelFactoryCreatorTransferStarted(pairId, creator, newCreator)Seorang kreator menawarkan perannya (ke alamat nol: tawaran ditarik)
DuelHookCreatorChanged(pairId, from, to)Peran kreator sebuah duel berpindah tangan
DuelHookCreatorFeesGivenUp(pairId, creator)Kreator mengalihkan bagiannya dari biaya mendatang ke pot secara permanen
DuelHookProtocolFeesClaimed(to, amount)Treasury dibayar
DuelHookProtocolRateSet(rateBps)Bagian protokol dari biaya swap semua duel langsung berubah: protokol mengambil rateBps darinya (10.000 untuk seluruhnya), sisanya masuk ke pot
DuelFactoryConfigProposed(config, eta), ConfigApplied(config), ConfigCancelled()Ketentuan peluncuran diusulkan untuk 48 jam ke depan, diterapkan, ditarik
DuelFactoryPausedSet(paused)Peluncuran dijeda atau dilanjutkan
PoolManagerSwap(id, sender, amount0, amount1, sqrtPriceX96, liquidity, tick, fee)Setiap transaksi, termasuk penyelarasan ulang oleh hook sendiri (sender-nya adalah hook)

Daftar transaksi membaca swap dari ketiga pool; grafik harga dan proporsi hanya membaca kedua pool ETH. Daftar pemegang memakai event Transfer token. Subgraph mengindeks swap, saldo, pemegang, dan ronde yang sudah diselesaikan agar aplikasi dapat memuat riwayat tanpa memutar ulang chain sejak peluncuran.

Menyelesaikan dan mengklaim

  • settle(pairId) menyelesaikan ronde yang sudah berakhir. Siapa pun dapat memanggilnya.
  • claimCreatorFees(pairId) hanya membayar kreator duel, yaitu dompet yang meluncurkannya atau alamat yang menerima perannya; claimProtocolFees() membayar treasury yang dikonfigurasi saat klaim. Siapa pun dapat memicu kedua klaim. Treasury dapat berubah setelah masa tunggu 48 jam milik factory; pemanggil tidak dapat memilih penerima lain.
  • transferCreator(pairId, newCreator) di factory menawarkan peran kreator sebuah duel, yang diambil oleh acceptCreator(pairId) dari newCreator; pendingCreator(pairId) membaca tawarannya. Hanya kreator yang dapat menawarkannya, dan hanya alamat yang ditawari yang dapat mengambilnya.
  • giveUpCreatorFees(pairId) pada hook mengirim bagian kreator dari biaya mendatang ke pot ronde secara permanen. Hanya kreator saat ini yang dapat memanggilnya. Biaya yang sudah terkumpul tetap dapat diklaim oleh kreator, dan peran kreator masih dapat dialihkan.