• Duel खुला है Robinhood Chain पर
  • मुक़ाबला लॉन्च करें दो टोकन, तीन पूल, एक ट्रांज़ैक्शन
  • राउंड बंद होने में --:--:--
  • चार घंटे के राउंड विजेता का बायबैक क़ीमत से सीमित है
  • लिक्विडिटी लॉक हमेशा के लिए, कोई टीम टोकन नहीं

Duel पर बनाएँ

Duel के पूल hook वाले मानक Uniswap V4 पूल हैं, और ऐप जो भी संख्या दिखाता है वह सार्वजनिक व्यू और इवेंट से आती है। बॉट, टर्मिनल, डैशबोर्ड और एग्रीगेटर बिना इजाज़त माँगे इस पर बना सकते हैं।

मुक़ाबला पढ़ना

DuelLens एक ही कॉल में किसी मुक़ाबले की हर जानकारी इकट्ठा करता है:

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

एक PairView में मुक़ाबले की ID, क्रिएटर और मेटाडेटा URI, दोनों पक्ष (टोकन, नाम, सिंबल, सप्लाई, पूल क़ीमत और tick, प्रति टोकन ETH, पूल में असली ETH), तीनों पूल की, लाइव अनुपात (gaugeBps, बेसिस पॉइंट में पक्ष A का हिस्सा), अब तक का राउंड औसत (roundGaugeBps), पॉट, क्रिएटर फ़ीस, अब तक का कुल वॉल्यूम और बायबैक, मौजूदा एंटी-स्नाइप टैक्स, राउंड का क्रमांक, शुरुआत और अंत और उसमें ट्रेड हुआ या नहीं (roundTraded), लॉन्च का समय और tick (launchTime, startTick), वह समर्थक मुक़ाबला है या नहीं, उसकी श्रेणी (category, ऐप की दी हुई संख्या में, कोई न हो तो 0), उसकी फ़ीस शर्तें (fees) और अपने हिस्से का वह भाग जो प्रोटोकॉल अभी लेता है (protocolRateBps, 10,000 में से) होता है: प्रोटोकॉल को स्वैप फ़ीस का fees.protocolShareBps × protocolRateBps / 10,000 मिलता है, और उस हिस्से का बाक़ी भाग पॉट को। कॉन्ट्रैक्ट और उनके ABI में पक्ष B को D कहा जाता है: d, tokenD, keyD।

hook के पास छोटे व्यू भी हैं: pairCount(), getPair(pairId), poolKeys(pairId), gaugeBps(pairId), snipeTaxBps(pairId), protocolRateBps(), protocolFees() और firstRoundEnd(launchTime)।

ट्रेड करना

कोई भी Uniswap V4 राउटर किसी मुक़ाबले के पूलों में ट्रेड कर सकता है। पूल की poolKeys(pairId) से बनाएँ:

पूलcurrency0currency1feetickSpacing
ETH / AETH (address(0))टोकन A0200
ETH / BETH (address(0))टोकन B0200
A / Bछोटे पते वाला टोकनबड़े पते वाला060

hook अपनी फ़ीस return delta के ज़रिए लेता है, इसलिए क्वोट V4 Quoter से लें: उसके जवाबों में हर फ़ीस और टैक्स शामिल होता है। ETH के ज़रिए बदलाव, टोकन → ETH → दूसरा टोकन रास्ते पर उसका quoteExactInput है।

जिस स्वैप की फ़ीस hook उसके चलने से पहले लेता है (तय इनपुट वाली ख़रीद या बदलाव, तय आउटपुट वाली बिक्री), उसे पूरा भरना होता है: जिसे उसकी क़ीमत सीमा, या ETH की कमी वाला ETH पूल, बीच में रोक दे, वह PartialFill के साथ रिवर्ट हो जाता है। PoolManager किसी hook की त्रुटि को लपेट देता है: PartialFill और ArbitrageStarved, WrappedError(address target, bytes4 selector, bytes reason, bytes details) के अंदर आते हैं, और जब V4 Quoter इनकी सूचना देता है तो UnexpectedRevertBytes(bytes revertData) के अंदर। hook की त्रुटि पढ़ने के लिए reason को डिकोड करें।

हर ट्रेड hook के पुनर्संतुलन पर ख़त्म होता है, जिसे अपनी गैस चाहिए: जो स्वैप उसके लिए बहुत कम गैस छोड़ता है, वह उसे छोड़ने के बजाय ArbitrageStarved के साथ रिवर्ट होता है। स्वैप अनुमान से मिली गैस के साथ भेजें, उससे कम कभी नहीं, और कुछ गुंजाइश रखकर: राउंड के क्लोज़ के बाद का पहला स्वैप राउंड को सेटल भी करता है (उसका बायबैक, फिर एक पुनर्संतुलन, यानी लगभग 170,000 गैस और), जिसे क्लोज़ से पहले लगाया गया अनुमान नहीं गिनता। ऐप हर अनुमान में 500,000 गैस जोड़ता है।

DuelRouter एक पूल में स्वैप के लिए एक कॉल देता है:

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

और ETH के ज़रिए बदलाव के लिए एक, जिसमें छोड़ा जा रहा पक्ष अपने ETH पूल में बिकता है और मिला पूरा ETH दूसरे के पूल में ख़र्च होता है:

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

Duel के ETH पूल में ऐसी बिक्री, जो पूल का ETH ख़त्म होने पर उसकी लॉन्च क़ीमत पर रुक जाती है, बाक़ी के लिए उसी कॉल में फिर भेजी जाती है, जब hook का पुनर्संतुलन पूल से वह वापस ख़रीद लेता है जो दोनों पक्षों के बीच वाला पूल लेता है: अधिकतम SALE_PASSES() (8) स्वैप, जब तक पूल की क़ीमत स्वैप की सीमा से नीचे रहे। हर स्वैप अपने ETH पर hook की फ़ीस देता है, और minAmountOut उन सबको गिनता है। Uniswap का V4 Quoter एक ही स्वैप का क्वोट देता है: उससे आगे, वह NotEnoughLiquidity लौटाता है। ऐप ऐसी बिक्री का क्वोट राउटर को ट्रेडर की तरह चलाकर निकालता है (स्टेट ओवरराइड वाला एक eth_call, DuelSimulator)।

जिसे पुनर्संतुलन अब वापस नहीं ख़रीद सकता, उसकी बिक्री sell दोनों पक्षों के बीच वाले पूल से होकर दूसरे पक्ष के ETH पूल में जारी रखता है, जहाँ पुनर्संतुलनों ने वह ETH पहुँचा दिया है जो उसकी क़ीमत चुकाता है:

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

sell पहले keyIn में वैसे ही बेचता है जैसे swapExactIn बेचता है, फिर जो बचा उसे keyCross से होकर भेजता है, जो वह सब ले लेता है (किसी भी बदलाव की तरह, उसकी बदलाव फ़ीस बर्न होती है), और फिर keyOther में बेचता है, यहाँ भी कई चरणों में। minAmountOut दोनों पूलों का ETH गिनता है। जिसे keyOther भी वापस नहीं ख़रीद सकता, वह recipient को उस पूल के टोकन में दिया जाता है, और न्यूनतम आउटपुट में नहीं गिना जाता। ऐप sell से तब बेचता है, जब V4 Quoter किसी बिक्री को ठुकरा देता है और DuelSimulator.simulateSale दिखाता है कि sell सब कुछ ETH के बदले बेच देता है।

कोई दूसरा V4 राउटर एक ही स्वैप करता है। hook तय इनपुट वाली ऐसी बिक्री को नहीं ठुकराता जो उतने से बड़ी हो जितना पूल का ETH चुका सकता है (उसकी फ़ीस स्वैप के बाद, चुकाए गए ETH पर ली जाती है): बिक्री आंशिक रूप से पूरी होती है, पूल की लॉन्च क़ीमत तक, और बाक़ी विक्रेता के पास रहता है। न्यूनतम आउटपुट उतना रखें जितना पूरा हो सकता है, और बाक़ी को बाद के किसी ट्रांज़ैक्शन में बेचें, उस हिस्से में जो पुनर्संतुलन ने वापस ख़रीदा है।

ख़रीदने के लिए swapExactIn के साथ ETH भेजें। बेचने या बदलने के लिए, राउटर को टोकन की अनुमति दें, या उसी ट्रांज़ैक्शन में किसी permit (EIP-2612) को यह अनुमति देने दें: swapExactInWithPermit, swapThroughEthWithPermit और sellWithPermit वही आर्ग्युमेंट लेते हैं, साथ में कॉल करने वाले का ठीक amountIn का permit, जो राउटर के लिए साइन हो:

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

यूज़र का वॉलेट हर Duel टोकन के permit को उस टोकन के पते वाले, Duel नाम और 1 वर्शन के EIP-712 डोमेन में साइन करता है (उसका eip712Domain(), ERC-5267, यह बताता है)। राउटर पहले permit का इस्तेमाल करता है और उसकी नाकामी को अनदेखा करता है: किसी के पहले से भेजे गए permit ने अनुमति सेट कर दी होती है, और नाकाम permit कुछ नहीं बदलता; तब स्वैप उतना लेता है जितना पहले से मौजूद अनुमति कवर करती है, और कम पड़े तो रिवर्ट हो जाता है। आंशिक रूप से पूरा हुआ स्वैप अनुमति की बची हुई सीमा को बरक़रार रखता है। permit की डेडलाइन यह सीमित करती है कि उसका सिग्नेचर कब पेश किया जा सकता है; वह पहले से दी गई अनुमति को समाप्त नहीं करती। राउटर सिर्फ़ कॉल करने वाले से टोकन खींचता है। बदलाव का ETH दूसरे पूल के ख़र्च करने के लिए PoolManager में ही रहता है; Duel का पूल उसे पूरा ख़र्च करता है, वरना hook स्वैप को रिवर्ट कर देता है (PartialFill), और किसी दूसरे पूल पर जो वह ख़र्च नहीं कर पाता, वह कॉल करने वाले को लौटा दिया जाता है।

लॉन्च करना

DuelFactory.createPair(CreateParams) एक मुक़ाबला लॉन्च करता है; इसके साथ लॉन्च फ़ीस और लॉन्च ख़रीद का ETH भेजें, जो ज़्यादा हो वह लौटा दिया जाता है। CreateParams में nameA, symbolA, nameD, symbolD (पक्ष B), metadataURI, buyA, buyD (लॉन्च पर हर पक्ष पर ख़र्च होने वाला ETH, एंटी-स्नाइप टैक्स से मुक्त), supporter, category (उस संख्या के रूप में जो ऐप अपनी श्रेणियों को देता है, कोई न हो तो 0) और config होते हैं, यानी फ़ैक्टरी का configHash(), जैसा आपने पढ़ा: अगर तब से शर्तें बदल गई हों, तो लॉन्च ConfigChanged के साथ रिवर्ट हो जाता है। नाम 1 से 128 बाइट का होता है, टिकर 1 से 40 का और मेटाडेटा URI अधिकतम 4,096 का (InvalidMetadata); किसी पक्ष की सप्लाई के 5% से ऊपर की लॉन्च ख़रीद रिवर्ट हो जाती है (CreatorBuyTooLarge)।

फ़ैक्टरी लॉन्च रुके हुए होने की स्थिति में शुरू होती है। जब तक paused() true है, सिर्फ़ owner() मुक़ाबला लॉन्च कर सकता है; बाक़ी कॉल CreationPaused के साथ रिवर्ट होती हैं। मालिक setPaused(false) से लॉन्च खोलता है। मौजूदा मुक़ाबलों में ट्रेड जारी रहता है।

इवेंट

कॉन्ट्रैक्टइवेंटकब
DuelFactoryPairCreated(pairId, creator, tokenA, tokenD)कोई मुक़ाबला लॉन्च हुआ
DuelHookPairRegistered(pairId, tokenA, tokenD, creator, category)उसके पूल hook में रजिस्टर हुए, उसी ट्रांज़ैक्शन में; category उसका विषय है, उस संख्या के रूप में जो ऐप अपनी श्रेणियों को देता है (कोई न हो तो 0)
DuelHookFeeTaken(pairId, fee, snipeTax)ETH के बदले किसी ट्रेड ने अपनी फ़ीस चुकाई
DuelHookSwitchFeeBurned(pairId, token, amount)किसी बदलाव ने अपनी फ़ीस बर्न की
DuelHookArbitrageCaptured(pairId, profit)पुनर्संतुलन ने पॉट भरा
DuelHookRoundSettled(pairId, roundIndex, averageTick, winner, ethSpent, tokensBurned)कोई राउंड बंद हुआ
DuelHookCreatorFeesClaimed(pairId, to, amount)किसी क्रिएटर को भुगतान हुआ
DuelFactoryCreatorTransferStarted(pairId, creator, newCreator)किसी क्रिएटर ने अपनी भूमिका ऑफ़र की (ज़ीरो पते को: ऑफ़र वापस लिया गया)
DuelHookCreatorChanged(pairId, from, to)किसी मुक़ाबले की क्रिएटर भूमिका दूसरे हाथ में गई
DuelHookCreatorFeesGivenUp(pairId, creator)क्रिएटर ने भविष्य की फ़ीस में अपना हिस्सा स्थायी रूप से पॉट को दे दिया
DuelHookProtocolFeesClaimed(to, amount)ट्रेज़री को भुगतान हुआ
DuelHookProtocolRateSet(rateBps)सभी मुक़ाबलों की स्वैप फ़ीस में प्रोटोकॉल का हिस्सा तुरंत बदला: वह उसका rateBps लेता है (पूरे के लिए 10,000), बाक़ी पॉट में जाता है
DuelFactoryConfigProposed(config, eta), ConfigApplied(config), ConfigCancelled()लॉन्च की शर्तें 48 घंटे आगे के लिए प्रस्तावित हुईं, लागू हुईं, वापस ली गईं
DuelFactoryPausedSet(paused)लॉन्च रोके गए या फिर शुरू हुए
PoolManagerSwap(id, sender, amount0, amount1, sqrtPriceX96, liquidity, tick, fee)हर ट्रेड, hook का अपना पुनर्संतुलन भी (उसका sender hook होता है)

ट्रेड की सूचियाँ तीनों पूलों से स्वैप पढ़ती हैं; क़ीमत और अनुपात के चार्ट सिर्फ़ दोनों ETH पूल पढ़ते हैं। होल्डर की सूचियाँ टोकन के Transfer इवेंट इस्तेमाल करती हैं। सबग्राफ़ स्वैप, बैलेंस, होल्डर और सेटल हुए राउंड इंडेक्स करता है, ताकि ऐप लॉन्च से चेन को दोबारा चलाए बिना इतिहास लोड कर सके।

सेटल और क्लेम करना

  • settle(pairId) ख़त्म हुआ राउंड सेटल करता है। इसे कोई भी कॉल कर सकता है।
  • claimCreatorFees(pairId) सिर्फ़ मुक़ाबले के क्रिएटर को भुगतान करता है, यानी लॉन्च करने वाले वॉलेट को या उस पते को जिसे उसने अपनी भूमिका सौंपी; claimProtocolFees() क्लेम के समय सेट ट्रेज़री को भुगतान करता है। दोनों में से कोई भी क्लेम कोई भी शुरू कर सकता है। फ़ैक्टरी के 48 घंटे के इंतज़ार के बाद ट्रेज़री बदल सकती है; कॉल करने वाला कोई दूसरा पाने वाला नहीं चुन सकता।
  • फ़ैक्टरी पर transferCreator(pairId, newCreator) किसी मुक़ाबले की क्रिएटर भूमिका ऑफ़र करता है, जिसे newCreator से कॉल किया गया acceptCreator(pairId) ले लेता है; pendingCreator(pairId) ऑफ़र पढ़ता है। इसे सिर्फ़ क्रिएटर ऑफ़र करता है, और सिर्फ़ ऑफ़र पाने वाला पता ही ले सकता है।
  • hook पर giveUpCreatorFees(pairId) भविष्य की फ़ीस में क्रिएटर का हिस्सा स्थायी रूप से पॉट में भेजता है। सिर्फ़ मौजूदा क्रिएटर इसे कॉल कर सकता है। पहले से जमा फ़ीस क्रिएटर अब भी क्लेम कर सकता है, और क्रिएटर भूमिका अब भी सौंपी जा सकती है।