मुख्य आशयावर जा

स्कॅम टोकन्सद्वारे वापरल्या जाणाऱ्या काही युक्त्या आणि त्या कशा ओळखायच्या

स्कॅम
Solidity
ERC-20
JavaScript
TypeScript
मध्यम
ओरी पोमेरँट्झ
15 सप्टेंबर, 2023
14 मिनिटांचे वाचन

या ट्युटोरिअलमध्ये आपण स्कॅमर्स कोणत्या युक्त्या वापरतात आणि त्यांची अंमलबजावणी कशी करतात हे पाहण्यासाठी एका स्कॅम टोकनचे (opens in a new tab) विश्लेषण करू. या ट्युटोरिअलच्या शेवटी तुम्हाला ERC-20 टोकन कॉन्ट्रॅक्ट्स, त्यांच्या क्षमता आणि संशयवाद का आवश्यक आहे याबद्दल अधिक व्यापक दृष्टिकोन मिळेल. त्यानंतर आपण त्या स्कॅम टोकनद्वारे उत्सर्जित होणाऱ्या घटना पाहू आणि ते कायदेशीर नाही हे आपण स्वयंचलितपणे कसे ओळखू शकतो ते पाहू.

स्कॅम टोकन्स - ते काय आहेत, लोक ते का करतात आणि ते कसे टाळावे

इथेरियमचा सर्वात सामान्य वापर म्हणजे एखाद्या गटाने व्यापार करण्यायोग्य टोकन तयार करणे, एका अर्थाने त्यांचे स्वतःचे चलन. तथापि, जिथे मूल्य आणणारे कायदेशीर वापर प्रकरणे (use cases) असतात, तिथे गुन्हेगार देखील असतात जे ते मूल्य स्वतःसाठी चोरण्याचा प्रयत्न करतात.

वापरकर्त्याच्या दृष्टिकोनातून तुम्ही या विषयाबद्दल ethereum.org वर इतरत्र अधिक वाचू शकता. हे ट्युटोरिअल स्कॅम टोकनचे विश्लेषण करण्यावर लक्ष केंद्रित करते जेणेकरून ते कसे केले जाते आणि ते कसे ओळखले जाऊ शकते हे पाहता येईल.

wARB हा स्कॅम आहे हे मला कसे कळेल?

आपण ज्या टोकनचे विश्लेषण करत आहोत ते wARB (opens in a new tab) आहे, जे कायदेशीर ARB टोकनच्या (opens in a new tab) समतुल्य असल्याचे भासवते.

कोणते टोकन कायदेशीर आहे हे जाणून घेण्याचा सर्वात सोपा मार्ग म्हणजे मूळ संस्था, आर्बिट्रम् (opens in a new tab) कडे पाहणे. कायदेशीर पत्ते त्यांच्या दस्तऐवजीकरणामध्ये (opens in a new tab) निर्दिष्ट केले आहेत.

सोर्स कोड का उपलब्ध आहे?

साधारणपणे आपण अशी अपेक्षा करतो की जे लोक इतरांची फसवणूक करण्याचा प्रयत्न करतात ते गुप्तता बाळगतात आणि खरोखरच अनेक स्कॅम टोकन्सचा कोड उपलब्ध नसतो (उदाहरणार्थ, हे (opens in a new tab) आणि हे (opens in a new tab)).

तथापि, कायदेशीर टोकन्स सहसा त्यांचा सोर्स कोड प्रकाशित करतात, त्यामुळे कायदेशीर दिसण्यासाठी स्कॅम टोकन्सचे लेखक कधीकधी असेच करतात. wARB (opens in a new tab) हे सोर्स कोड उपलब्ध असलेल्या टोकन्सपैकी एक आहे, ज्यामुळे ते समजणे सोपे होते.

कॉन्ट्रॅक्ट डिप्लॉयर्स सोर्स कोड प्रकाशित करायचा की नाही हे निवडू शकतात, परंतु ते चुकीचा सोर्स कोड प्रकाशित करू शकत नाहीत. ब्लॉक एक्सप्लोरर प्रदान केलेला सोर्स कोड स्वतंत्रपणे संकलित (compile) करतो आणि जर त्याला अगदी तसाच बाइटकोड मिळाला नाही, तर तो तो सोर्स कोड नाकारतो. तुम्ही Etherscan साइटवर याबद्दल अधिक वाचू शकता (opens in a new tab).

कायदेशीर ERC-20 टोकन्सशी तुलना

आपण या टोकनची तुलना कायदेशीर ERC-20 टोकन्सशी करणार आहोत. जर तुम्हाला कायदेशीर ERC-20 टोकन्स सामान्यतः कसे लिहिले जातात याची माहिती नसेल, तर हे ट्युटोरिअल पहा.

विशेषाधिकार प्राप्त पत्त्यांसाठी स्थिरांक (Constants)

कॉन्ट्रॅक्ट्सना कधीकधी विशेषाधिकार प्राप्त पत्त्यांची आवश्यकता असते. दीर्घकालीन वापरासाठी डिझाइन केलेले कॉन्ट्रॅक्ट्स काही विशेषाधिकार प्राप्त पत्त्यांना ते पत्ते बदलण्याची परवानगी देतात, उदाहरणार्थ नवीन मल्टीसिग कॉन्ट्रॅक्टचा वापर सक्षम करण्यासाठी. हे करण्याचे अनेक मार्ग आहेत.

HOP टोकन कॉन्ट्रॅक्ट (opens in a new tab) Ownable (opens in a new tab) पॅटर्न वापरते. विशेषाधिकार प्राप्त पत्ता स्टोरेजमध्ये, _owner नावाच्या फील्डमध्ये ठेवला जातो (तिसरी फाईल पहा, Ownable.sol).

abstract contract Ownable is Context {
    address private _owner;
    .
    .
    .
}

ARB टोकन कॉन्ट्रॅक्टमध्ये (opens in a new tab) थेट विशेषाधिकार प्राप्त पत्ता नसतो. तथापि, त्याला त्याची आवश्यकता नाही. ते पत्ता 0xb50721bcf8d664c30412cfbc6cf7a15145234ad1 (opens in a new tab) वरील proxy (opens in a new tab) च्या मागे असते. त्या कॉन्ट्रॅक्टमध्ये एक विशेषाधिकार प्राप्त पत्ता आहे (चौथी फाईल पहा, ERC1967Upgrade.sol) जो अपग्रेडसाठी वापरला जाऊ शकतो.

    /**
     * @dev EIP1967 ॲडमिन स्लॉटमध्ये नवीन पत्ता संचयित करते.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

याउलट, wARB कॉन्ट्रॅक्टमध्ये हार्ड कोडेड contract_owner आहे.

हा कॉन्ट्रॅक्ट मालक (opens in a new tab) असा कॉन्ट्रॅक्ट नाही जो वेगवेगळ्या वेळी वेगवेगळ्या खात्यांद्वारे नियंत्रित केला जाऊ शकतो, तर ते एक बाह्य मालकीचे खाते (externally owned account) आहे. याचा अर्थ असा की ते बहुधा एखाद्या व्यक्तीद्वारे अल्पकालीन वापरासाठी डिझाइन केलेले आहे, मौल्यवान राहणाऱ्या ERC-20 ला नियंत्रित करण्यासाठी दीर्घकालीन उपाय म्हणून नाही.

आणि खरोखरच, जर आपण Etherscan मध्ये पाहिले तर आपल्याला दिसेल की स्कॅमरने हे कॉन्ट्रॅक्ट 19 मे 2023 रोजी फक्त 12 तासांसाठी (पहिला व्यवहार (opens in a new tab) ते शेवटचा व्यवहार (opens in a new tab)) वापरले.

बनावट _transfer फंक्शन

अंतर्गत _transfer फंक्शन वापरून प्रत्यक्ष हस्तांतरण होणे हे मानक (standard) आहे.

wARB मध्ये हे फंक्शन जवळजवळ कायदेशीर दिसते:

संशयास्पद भाग असा आहे:

        if (sender == contract_owner){
            sender = deployer;
        }
        emit Transfer(sender, recipient, amount);

जर कॉन्ट्रॅक्ट मालक टोकन्स पाठवतो, तर Transfer घटना ते deployer कडून आल्याचे का दाखवते?

तथापि, एक अधिक महत्त्वाचा मुद्दा आहे. हे _transfer फंक्शन कोण कॉल करते? ते बाहेरून कॉल केले जाऊ शकत नाही, ते internal म्हणून चिन्हांकित केले आहे. आणि आपल्याकडे असलेल्या कोडमध्ये _transfer ला कोणतेही कॉल्स समाविष्ट नाहीत. स्पष्टपणे, ते येथे एक फसवणूक (decoy) म्हणून आहे.

जेव्हा आपण टोकन्स हस्तांतरित करण्यासाठी कॉल केलेल्या फंक्शन्सकडे पाहतो, transfer आणि transferFrom, तेव्हा आपल्याला दिसते की ते पूर्णपणे वेगळ्या फंक्शनला कॉल करतात, _f_.

खरे _f_ फंक्शन

या फंक्शनमध्ये दोन संभाव्य धोक्याचे इशारे (red flags) आहेत.

  • फंक्शन मॉडिफायर (opens in a new tab) _mod_ चा वापर. तथापि, जेव्हा आपण सोर्स कोडमध्ये पाहतो तेव्हा आपल्याला दिसते की _mod_ प्रत्यक्षात निरुपद्रवी आहे.

    modifier _mod_(address sender, address recipient, uint256 amount){
      _;
    }
    
  • आपण _transfer मध्ये पाहिलेली तीच समस्या, जी म्हणजे जेव्हा contract_owner टोकन्स पाठवतो तेव्हा ते deployer कडून आल्यासारखे दिसतात.

बनावट घटना फंक्शन dropNewTokens

आता आपण अशा गोष्टीकडे येतो जी प्रत्यक्ष स्कॅमसारखी दिसते. मी वाचनीयतेसाठी फंक्शन थोडे संपादित केले आहे, परंतु ते कार्यात्मकदृष्ट्या समतुल्य आहे.

function dropNewTokens(address uPool,
                       address[] memory eReceiver,
                       uint256[] memory eAmounts) public auth()

या फंक्शनमध्ये auth() मॉडिफायर आहे, ज्याचा अर्थ असा की ते फक्त कॉन्ट्रॅक्ट मालकाद्वारे कॉल केले जाऊ शकते.

modifier auth() {
    require(msg.sender == contract_owner, "Not allowed to interact");
    _;
}

हे निर्बंध अगदी योग्य आहेत, कारण यादृच्छिक (random) खात्यांनी टोकन्स वितरित करावेत असे आपल्याला वाटणार नाही. तथापि, उर्वरित फंक्शन संशयास्पद आहे.

{
    for (uint256 i = 0; i < eReceiver.length; i++) {
        emit Transfer(uPool, eReceiver[i], eAmounts[i]);
    }
}

पूल खात्यातून प्राप्तकर्त्यांच्या अ‍ॅरेमध्ये (array) रकमेचा अ‍ॅरे हस्तांतरित करण्याचे फंक्शन अगदी योग्य वाटते. अशी अनेक वापर प्रकरणे आहेत ज्यामध्ये तुम्हाला एकाच स्रोतावरून अनेक गंतव्यस्थानांवर टोकन्स वितरित करायचे असतील, जसे की पेरोल, एअरड्रॉप्स इ. एकाधिक व्यवहार जारी करण्याऐवजी किंवा एकाच व्यवहाराचा भाग म्हणून वेगळ्या कॉन्ट्रॅक्टमधून ERC-20 ला अनेक वेळा कॉल करण्याऐवजी एकाच व्यवहारामध्ये हे करणे (गॅसच्या बाबतीत) स्वस्त आहे.

तथापि, dropNewTokens तसे करत नाही. ते Transfer घटना (opens in a new tab) उत्सर्जित करते, परंतु प्रत्यक्षात कोणतेही टोकन्स हस्तांतरित करत नाही. खरोखर न झालेल्या हस्तांतरणाबद्दल सांगून साखळीबाह्य (offchain) अ‍ॅप्लिकेशन्सना गोंधळात टाकण्याचे कोणतेही कायदेशीर कारण नाही.

जाळणारे (burning) Approve फंक्शन

ERC-20 कॉन्ट्रॅक्ट्समध्ये मंजुरीसाठी एक approve फंक्शन असणे अपेक्षित आहे, आणि खरोखरच आपल्या स्कॅम टोकनमध्ये असे फंक्शन आहे, आणि ते योग्य देखील आहे. तथापि, Solidity हे C मधून आलेले असल्यामुळे ते केस सेन्सिटिव्ह (case significant) आहे. "Approve" आणि "approve" या वेगवेगळ्या स्ट्रिंग्स आहेत.

तसेच, कार्यक्षमता approve शी संबंधित नाही.

    function Approve(
        address[] memory holders)

हे फंक्शन टोकन धारकांच्या पत्त्यांच्या अ‍ॅरेसह कॉल केले जाते.

    public approver() {

approver() मॉडिफायर हे सुनिश्चित करते की फक्त contract_owner ला हे फंक्शन कॉल करण्याची परवानगी आहे (खाली पहा).

प्रत्येक धारकाच्या पत्त्यासाठी फंक्शन धारकाची संपूर्ण शिल्लक 0x00...01 पत्त्यावर हलवते, प्रभावीपणे ते जाळते (मानकातील वास्तविक burn एकूण पुरवठा देखील बदलते आणि टोकन्स 0x00...00 वर हस्तांतरित करते). याचा अर्थ असा की contract_owner कोणत्याही वापरकर्त्याची मालमत्ता काढून टाकू शकतो. गव्हर्नन्स टोकनमध्ये तुम्हाला हवे असलेले हे वैशिष्ट्य वाटत नाही.

कोड गुणवत्तेच्या समस्या

या कोड गुणवत्तेच्या समस्या हा कोड स्कॅम असल्याचे सिद्ध करत नाहीत, परंतु ते संशयास्पद वाटतात. आर्बिट्रम् सारख्या संघटित कंपन्या सहसा इतका वाईट कोड रिलीज करत नाहीत.

mount फंक्शन

जरी ते मानकामध्ये (opens in a new tab) निर्दिष्ट केलेले नसले तरी, सामान्यतः नवीन टोकन्स तयार करणाऱ्या फंक्शनला mint म्हटले जाते.

जर आपण wARB कन्स्ट्रक्टरमध्ये पाहिले, तर आपल्याला दिसेल की काही कारणास्तव टाइम मिंट फंक्शनचे नाव बदलून mount केले गेले आहे, आणि कार्यक्षमतेसाठी संपूर्ण रकमेसाठी एकदा कॉल करण्याऐवजी, प्रारंभिक पुरवठ्याच्या पाचव्या भागासह पाच वेळा कॉल केले जाते.

mount फंक्शन स्वतःच संशयास्पद आहे.

    function mount(address account, uint256 amount) public {
        require(msg.sender == contract_owner, "ERC20: mint to the zero address");

require कडे पाहता, आपल्याला दिसते की फक्त कॉन्ट्रॅक्ट मालकाला मिंट करण्याची परवानगी आहे. ते कायदेशीर आहे. परंतु त्रुटी संदेश (error message) फक्त मालकाला मिंट करण्याची परवानगी आहे किंवा तसे काहीतरी असावे. त्याऐवजी, तो अप्रासंगिक ERC20: शून्य पत्त्यावर मिंट करा असा आहे. शून्य पत्त्यावर मिंटिंगसाठी योग्य चाचणी require(account != address(0), "<error message>") आहे, जी तपासण्याची तसदी कॉन्ट्रॅक्ट कधीच घेत नाही.

        _totalSupply = _totalSupply.add(amount);
        _balances[contract_owner] = _balances[contract_owner].add(amount);
        emit Transfer(address(0), account, amount);
    }

मिंटिंगशी थेट संबंधित आणखी दोन संशयास्पद तथ्ये आहेत:

  • एक account पॅरामीटर आहे, जे बहुधा मिंट केलेली रक्कम प्राप्त करणारे खाते असावे. परंतु जी शिल्लक वाढते ती प्रत्यक्षात contract_owner ची असते.

  • वाढलेली शिल्लक contract_owner ची असली तरी, उत्सर्जित झालेली घटना account कडे हस्तांतरण दर्शवते.

auth आणि approver दोन्ही का? काहीही न करणारे mod का?

या कॉन्ट्रॅक्टमध्ये तीन मॉडिफायर्स आहेत: _mod_, auth, आणि approver.

    modifier _mod_(address sender, address recipient, uint256 amount){
        _;
    }

_mod_ तीन पॅरामीटर्स घेते आणि त्यांचे काहीही करत नाही. ते का असावे?

auth आणि approver अधिक अर्थपूर्ण आहेत, कारण ते तपासतात की कॉन्ट्रॅक्ट contract_owner द्वारे कॉल केले गेले होते. मिंटिंगसारख्या काही विशेषाधिकार प्राप्त क्रिया त्या खात्यापुरत्या मर्यादित असाव्यात अशी आपली अपेक्षा असते. तथापि, अगदी एकच गोष्ट करणाऱ्या दोन स्वतंत्र फंक्शन्स असण्यात काय अर्थ आहे?

आपण स्वयंचलितपणे काय शोधू शकतो?

Etherscan कडे पाहून आपण पाहू शकतो की wARB हे एक स्कॅम टोकन आहे. तथापि, तो एक केंद्रीकृत उपाय आहे. सिद्धांतानुसार, Etherscan ला नष्ट किंवा हॅक केले जाऊ शकते. एखादे टोकन कायदेशीर आहे की नाही हे स्वतंत्रपणे शोधून काढता येणे अधिक चांगले आहे.

ERC-20 टोकन संशयास्पद आहे (एकतर स्कॅम किंवा खूप वाईटरित्या लिहिलेले) हे ओळखण्यासाठी आपण काही युक्त्या वापरू शकतो, ते उत्सर्जित करत असलेल्या घटनांकडे पाहून.

संशयास्पद Approval घटना

Approval घटना (opens in a new tab) केवळ थेट विनंतीसह घडल्या पाहिजेत (Transfer घटनांच्या (opens in a new tab) विरूद्ध ज्या मंजुरीचा परिणाम म्हणून घडू शकतात). या समस्येच्या तपशीलवार स्पष्टीकरणासाठी आणि विनंत्या कॉन्ट्रॅक्टद्वारे मध्यस्थी करण्याऐवजी थेट का असाव्यात यासाठी Solidity दस्तऐवज पहा (opens in a new tab).

याचा अर्थ असा की Approval घटना ज्या बाह्य मालकीच्या खात्यातून खर्च करण्यास मंजुरी देतात त्या अशा व्यवहारांमधून आल्या पाहिजेत ज्यांचा उगम त्या खात्यात होतो आणि ज्यांचे गंतव्यस्थान ERC-20 कॉन्ट्रॅक्ट आहे. बाह्य मालकीच्या खात्याकडून इतर कोणत्याही प्रकारची मंजुरी संशयास्पद आहे.

येथे असा प्रोग्राम आहे जो या प्रकारची घटना ओळखतो (opens in a new tab), Viem (opens in a new tab) आणि TypeScript (opens in a new tab) वापरून, जे टाइप सेफ्टीसह JavaScript चे एक प्रकार आहे. ते चालवण्यासाठी:

  1. .env.example ला .env मध्ये कॉपी करा.
  2. इथरियम मेननेट नोडची URL प्रदान करण्यासाठी .env संपादित करा.
  3. आवश्यक पॅकेजेस स्थापित करण्यासाठी pnpm install चालवा.
  4. संशयास्पद मंजुरी शोधण्यासाठी pnpm susApproval चालवा.

येथे ओळीनुसार स्पष्टीकरण दिले आहे:

import {
  Address,
  TransactionReceipt,
  createPublicClient,
  http,
  parseAbiItem,
} from "viem"
import { mainnet } from "viem/chains"

viem मधून प्रकार व्याख्या (type definitions), फंक्शन्स आणि चेन व्याख्या आयात करा.

import { config } from "dotenv"
config()

URL मिळवण्यासाठी .env वाचा.

const client = createPublicClient({
  chain: mainnet,
  transport: http(process.env.URL),
})

Viem क्लायंट तयार करा. आपल्याला फक्त ब्लॉकचेनवरून वाचण्याची आवश्यकता आहे, त्यामुळे या क्लायंटला खाजगी की ची आवश्यकता नाही.

const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82"
const fromBlock = 16859812n
const toBlock = 16873372n

संशयास्पद ERC-20 कॉन्ट्रॅक्टचा पत्ता, आणि ज्या ब्लॉक्समध्ये आपण घटना शोधू. नोड प्रदाते सामान्यतः घटना वाचण्याची आपली क्षमता मर्यादित करतात कारण बँडविड्थ महाग होऊ शकते. सुदैवाने wARB अठरा तासांच्या कालावधीसाठी वापरात नव्हते, त्यामुळे आपण सर्व घटना शोधू शकतो (एकूण फक्त 13 होत्या).

const approvalEvents = await client.getLogs({
  address: testedAddress,
  fromBlock,
  toBlock,
  event: parseAbiItem(
    "event Approval(address indexed _owner, address indexed _spender, uint256 _value)"
  ),
})

Viem ला घटनेच्या माहितीसाठी विचारण्याचा हा मार्ग आहे. जेव्हा आपण त्याला फील्डच्या नावांसह अचूक घटना स्वाक्षरी प्रदान करतो, तेव्हा ते आपल्यासाठी घटनेचे विश्लेषण (parse) करते.

const isContract = async (addr: Address): boolean =>
  await client.getBytecode({ address: addr })

आपला अल्गोरिदम फक्त बाह्य मालकीच्या खात्यांना लागू होतो. जर client.getBytecode द्वारे कोणताही बाइटकोड परत केला गेला असेल तर याचा अर्थ असा की हे एक कॉन्ट्रॅक्ट आहे आणि आपण ते वगळले पाहिजे.

जर तुम्ही यापूर्वी TypeScript वापरले नसेल, तर फंक्शनची व्याख्या थोडी विचित्र वाटू शकते. आपण त्याला फक्त असे सांगत नाही की पहिल्या (आणि एकमेव) पॅरामीटरला addr म्हटले जाते, तर ते Address प्रकाराचे आहे हे देखील सांगतो. त्याचप्रमाणे, : boolean भाग TypeScript ला सांगतो की फंक्शनचे रिटर्न व्हॅल्यू बुलियन (boolean) आहे.

const getEventTxn = async (ev: Event): TransactionReceipt =>
  await client.getTransactionReceipt({ hash: ev.transactionHash })

हे फंक्शन घटनेवरून व्यवहाराची पावती मिळवते. व्यवहाराचे गंतव्यस्थान काय होते हे आपल्याला माहीत आहे याची खात्री करण्यासाठी आपल्याला पावतीची आवश्यकता आहे.

const suspiciousApprovalEvent = async (ev : Event) : (Event | null) => {

हे सर्वात महत्त्वाचे फंक्शन आहे, जे प्रत्यक्षात एखादी घटना संशयास्पद आहे की नाही हे ठरवते. रिटर्न प्रकार, (Event | null), TypeScript ला सांगतो की हे फंक्शन एकतर Event किंवा null परत करू शकते. जर घटना संशयास्पद नसेल तर आपण null परत करतो.

const owner = ev.args._owner

Viem कडे फील्डची नावे आहेत, त्यामुळे त्याने आपल्यासाठी घटनेचे विश्लेषण केले. _owner हा खर्च करावयाच्या टोकन्सचा मालक आहे.

// कॉन्ट्रॅक्ट्सद्वारे दिलेल्या मंजुरी संशयास्पद नसतात
if (await isContract(owner)) return null

जर मालक कॉन्ट्रॅक्ट असेल, तर ही मंजुरी संशयास्पद नाही असे समजा. कॉन्ट्रॅक्टची मंजुरी संशयास्पद आहे की नाही हे तपासण्यासाठी आपल्याला व्यवहाराच्या संपूर्ण अंमलबजावणीचा मागोवा घ्यावा लागेल हे पाहण्यासाठी की तो कधी मालक कॉन्ट्रॅक्टपर्यंत पोहोचला का, आणि त्या कॉन्ट्रॅक्टने ERC-20 कॉन्ट्रॅक्टला थेट कॉल केला का. आपण करू इच्छितो त्यापेक्षा हे खूप जास्त संसाधन खर्चिक (resource expensive) आहे.

const txn = await getEventTxn(ev)

जर मंजुरी बाह्य मालकीच्या खात्यातून आली असेल, तर तो व्यवहार मिळवा ज्यामुळे ती झाली.

// जर मंजुरी अशा EOA मालकाकडून आली असेल जो व्यवहाराचा `from` नाही, तर ती संशयास्पद असते
if (owner.toLowerCase() != txn.from.toLowerCase()) return ev

आपण फक्त स्ट्रिंग समानतेसाठी तपासू शकत नाही कारण पत्ते हेक्साडेसिमल असतात, त्यामुळे त्यात अक्षरे असतात. कधीकधी, उदाहरणार्थ txn.from मध्ये, ती अक्षरे सर्व लोअरकेस असतात. इतर प्रकरणांमध्ये, जसे की ev.args._owner, पत्ता त्रुटी ओळखण्यासाठी मिश्र-केस (mixed-case) मध्ये (opens in a new tab) असतो.

परंतु जर व्यवहार मालकाकडून नसेल, आणि तो मालक बाह्य मालकीचा असेल, तर आपल्याकडे एक संशयास्पद व्यवहार आहे.

// जर व्यवहाराचे गंतव्यस्थान ते ERC-20 कॉन्ट्रॅक्ट नसेल ज्याची आपण
// चौकशी करत आहोत
if (txn.to.toLowerCase() != testedAddress) return ev

त्याचप्रमाणे, जर व्यवहाराचा to पत्ता, कॉल केलेले पहिले कॉन्ट्रॅक्ट, तपासणीखालील ERC-20 कॉन्ट्रॅक्ट नसेल तर ते संशयास्पद आहे.

    // संशय घेण्याचे कोणतेही कारण नसल्यास, null परत करा.
    return null
}

जर दोन्हीपैकी कोणतीही अट सत्य नसेल तर Approval घटना संशयास्पद नाही.

const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev))
const testResults = (await Promise.all(testPromises)).filter((x) => x != null)

console.log(testResults)

एक async फंक्शन (opens in a new tab) Promise ऑब्जेक्ट परत करते. सामान्य सिंटॅक्स, await x() सह, आपण प्रक्रिया सुरू ठेवण्यापूर्वी त्या Promise ची पूर्तता होण्याची वाट पाहतो. हे प्रोग्राम करण्यासाठी आणि अनुसरण करण्यासाठी सोपे आहे, परंतु ते अकार्यक्षम देखील आहे. विशिष्ट घटनेसाठी Promise ची पूर्तता होण्याची आपण वाट पाहत असताना आपण आधीच पुढील घटनेवर काम सुरू करू शकतो.

येथे आपण Promise ऑब्जेक्ट्सचा अ‍ॅरे तयार करण्यासाठी map (opens in a new tab) वापरतो. त्यानंतर आपण त्या सर्व प्रॉमिसचे (promises) निराकरण होण्याची वाट पाहण्यासाठी Promise.all (opens in a new tab) वापरतो. त्यानंतर आपण संशयास्पद नसलेल्या घटना काढून टाकण्यासाठी त्या परिणामांना filter (opens in a new tab) करतो.

संशयास्पद Transfer घटना

स्कॅम टोकन्स ओळखण्याचा आणखी एक संभाव्य मार्ग म्हणजे त्यांच्याकडे कोणतेही संशयास्पद हस्तांतरण आहे का ते पाहणे. उदाहरणार्थ, ज्या खात्यांमध्ये तितके टोकन्स नाहीत अशा खात्यांमधून हस्तांतरण. ही चाचणी कशी लागू करायची (opens in a new tab) हे तुम्ही पाहू शकता, परंतु wARB मध्ये ही समस्या नाही.

निष्कर्ष

ERC-20 स्कॅम्सच्या स्वयंचलित शोधाला फॉल्स निगेटिव्ह्ज (false negatives) (opens in a new tab) चा त्रास होतो, कारण स्कॅम एक पूर्णपणे सामान्य ERC-20 टोकन कॉन्ट्रॅक्ट वापरू शकतो जे फक्त कोणत्याही वास्तविक गोष्टीचे प्रतिनिधित्व करत नाही. त्यामुळे तुम्ही नेहमी विश्वसनीय स्रोताकडून टोकनचा पत्ता मिळवण्याचा प्रयत्न केला पाहिजे.

स्वयंचलित शोध काही प्रकरणांमध्ये मदत करू शकतो, जसे की DeFi भाग, जिथे अनेक टोकन्स असतात आणि ते स्वयंचलितपणे हाताळले जाणे आवश्यक असते. परंतु नेहमीप्रमाणे कॅव्हेट एम्प्टर (caveat emptor - खरेदीदाराने सावध राहावे) (opens in a new tab), तुमचे स्वतःचे संशोधन करा आणि तुमच्या वापरकर्त्यांनाही तसे करण्यास प्रोत्साहित करा.

माझ्या अधिक कामासाठी येथे पहा (opens in a new tab).