Ana içeriğe geç

Dolandırıcı jetonlar tarafından kullanılan bazı hileler ve bunların nasıl tespit edileceği

dolandırıcılık
solidity
erc-20
javascript
typescript
Orta düzey
Ori Pomerantz
15 Eylül 2023
13 dakikalık okuma

Bu öğreticide, dolandırıcıların oynadığı bazı hileleri ve bunları nasıl uyguladıklarını görmek için bir dolandırıcılık jetonunuopens in a new tab inceliyoruz. Öğreticinin sonunda, ERC-20 jeton sözleşmeleri, yetenekleri ve şüpheciliğin neden gerekli olduğu hakkında daha kapsamlı bir bakış açısına sahip olacaksınız. Ardından, o dolandırıcı jeton tarafından yayılan olaylara bakıyoruz ve yasal olmadığını otomatik olarak nasıl belirleyebileceğimizi görüyoruz.

Dolandırıcı jetonlar - nedirler, insanlar neden onları yapar ve onlardan nasıl kaçınılır

Ethereum'un en yaygın kullanımlarından biri, bir grubun bir anlamda kendi para birimi olan ticareti yapılabilen bir token oluşturmasıdır. Ancak, değer getiren meşru kullanım şekilleri bulunan her yerde, söz konusu değeri kendisi için çalmaya çalışan suçlular bulunur.

Bu konu hakkında bir kullanıcı perspektifinden ethereum.org'un başka bir yerinde daha fazla bilgi edinebilirsiniz. Bu öğretici, nasıl yapıldığını ve nasıl tespit edilebileceğini görmek için bir dolandırıcılık jetonunu incelemeye odaklanmaktadır.

wARB'nin bir dolandırıcılık olduğunu nasıl anlarım?

İncelediğimiz jeton, yasal ARB jetonunaopens in a new tab eşdeğermiş gibi davranan wARBopens in a new tab'dir.

Hangisinin yasal jeton olduğunu bilmenin en kolay yolu, kurucu kuruluş olan Arbitrumopens in a new tab'a bakmaktır. Yasal adresler dokümanlarındaopens in a new tab belirtilmiştir.

Kaynak kodu neden mevcut?

Normalde başkalarını dolandırmaya çalışan insanların gizli olmasını bekleriz ve gerçekten de birçok dolandırıcı jetonun kodu mevcut değildir (örneğin, buopens in a new tab ve buopens in a new tab).

Ancak, yasal jetonlar genellikle kaynak kodlarını yayınlarlar, bu nedenle yasal görünmek için dolandırıcı jetonların yazarları da bazen aynısını yapar. wARBopens in a new tab, kaynak kodu mevcut olan ve bu sayede anlaşılması daha kolay olan jetonlardan biridir.

Sözleşme dağıtıcıları kaynak kodunu yayınlayıp yayınlamamayı seçebilse de, yanlış kaynak kodunu yayınlayamazlar. Blok gezgini, sağlanan kaynak kodunu bağımsız olarak derler ve tam olarak aynı bit kodunu elde etmezse, o kaynak kodunu reddeder. Bu konu hakkında Etherscan sitesinde daha fazla bilgi edinebilirsinizopens in a new tab.

Yasal ERC-20 jetonlarıyla karşılaştırma

Bu jetonu yasal ERC-20 jetonlarıyla karşılaştıracağız. Yasal ERC-20 jetonlarının tipik olarak nasıl yazıldığına aşina değilseniz, bu öğreticiye bakın.

Ayrıcalıklı adresler için sabitler

Sözleşmeler bazen ayrıcalıklı adreslere ihtiyaç duyar. Uzun süreli kullanım için tasarlanan sözleşmeler, örneğin yeni bir çoklu imza sözleşmesinin kullanımını sağlamak için bazı ayrıcalıklı adreslerin bu adresleri değiştirmesine izin verir. Bunu yapmanın birkaç yolu vardır.

HOP jeton sözleşmesiopens in a new tab, Ownableopens in a new tab modelini kullanır. Ayrıcalıklı adres, _owner adlı bir alanda depolamada tutulur (üçüncü dosyaya bakın, Ownable.sol).

1abstract contract Ownable is Context {
2 address private _owner;
3 .
4 .
5 .
6}

ARB jeton sözleşmesiopens in a new tab doğrudan ayrıcalıklı bir adrese sahip değildir. Ancak, bir tanesine ihtiyacı yoktur. 0xb50721bcf8d664c30412cfbc6cf7a15145234ad1 adresindekiopens in a new tab bir proxyopens in a new tab'nin arkasında yer alır. Bu sözleşmenin yükseltmeler için kullanılabilecek ayrıcalıklı bir adresi vardır (dördüncü dosyaya bakın, ERC1967Upgrade.sol).

1 /**
2 * @dev EIP1967 yönetici yuvasında yeni bir adres saklar.
3 */
4 function _setAdmin(address newAdmin) private {
5 require(newAdmin != address(0), "ERC1967: yeni yönetici sıfır adresidir");
6 StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
7 }

Buna karşılık, wARB sözleşmesi sabit kodlanmış bir contract_owner'a sahiptir.

1contract WrappedArbitrum is Context, IERC20 {
2 .
3 .
4 .
5 address deployer = 0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1;
6 address public contract_owner = 0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33;
7 .
8 .
9 .
10}
Tümünü göster

Bu sözleşme sahibiopens in a new tab, farklı zamanlarda farklı hesaplar tarafından kontrol edilebilecek bir sözleşme değil, harici olarak sahip olunan bir hesaptır. Bu, muhtemelen değerli kalacak bir ERC-20'yi kontrol etmek için uzun vadeli bir çözümden ziyade, bir birey tarafından kısa süreli kullanım için tasarlandığı anlamına gelir.

Ve gerçekten de, Etherscan'e baktığımızda, dolandırıcının bu sözleşmeyi 19 Mayıs 2023'te yalnızca 12 saat boyunca kullandığını görüyoruz (ilk işlemopens in a new tab'den son işlemeopens in a new tab).

Sahte _transfer fonksiyonu

Gerçek transferlerin dahili bir _transfer fonksiyonu kullanılarak yapılması standarttır.

wARB'de bu fonksiyon neredeyse yasal görünüyor:

1 function _transfer(address sender, address recipient, uint256 amount) internal virtual{
2 require(sender != address(0), "ERC20: sıfır adresten transfer");
3 require(recipient != address(0), "ERC20: sıfır adrese transfer");
4
5 _beforeTokenTransfer(sender, recipient, amount);
6
7 _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer tutarı bakiyeyi aşıyor");
8 _balances[recipient] = _balances[recipient].add(amount);
9 if (sender == contract_owner){
10 sender = deployer;
11 }
12 emit Transfer(sender, recipient, amount);
13 }
Tümünü göster

Şüpheli kısım şudur:

1 if (sender == contract_owner){
2 sender = deployer;
3 }
4 emit Transfer(sender, recipient, amount);

Sözleşme sahibi jeton gönderirse, Transfer olayı neden deployer'dan geldiklerini gösteriyor?

Ancak, daha önemli bir sorun var. Bu _transfer fonksiyonunu kim çağırıyor? Dışarıdan çağrılamaz, internal olarak işaretlenmiştir. Ve elimizdeki kod, _transfer'a herhangi bir çağrı içermiyor. Açıkçası, bu bir yem olarak burada.

1 function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
2 _f_(_msgSender(), recipient, amount);
3 return true;
4 }
5
6 function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
7 _f_(sender, recipient, amount);
8 _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer tutarı yetkiyi aşıyor"));
9 return true;
10 }
Tümünü göster

Jetonları transfer etmek için çağrılan fonksiyonlara, transfer ve transferFrom'a baktığımızda, tamamen farklı bir fonksiyon olan _f_'yi çağırdıklarını görüyoruz.

Gerçek _f_ fonksiyonu

1 function _f_(address sender, address recipient, uint256 amount) internal _mod_(sender,recipient,amount) virtual {
2 require(sender != address(0), "ERC20: sıfır adresten transfer");
3 require(recipient != address(0), "ERC20: sıfır adrese transfer");
4
5 _beforeTokenTransfer(sender, recipient, amount);
6
7 _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer tutarı bakiyeyi aşıyor");
8 _balances[recipient] = _balances[recipient].add(amount);
9 if (sender == contract_owner){
10
11 sender = deployer;
12 }
13 emit Transfer(sender, recipient, amount);
14 }
Tümünü göster

Bu fonksiyonda iki potansiyel tehlike işareti var.

  • Fonksiyon değiştiriciopens in a new tab _mod_ kullanımı. Ancak, kaynak koduna baktığımızda _mod_'un aslında zararsız olduğunu görüyoruz.

    1modifier _mod_(address sender, address recipient, uint256 amount){
    2 _;
    3}
  • _transfer'de gördüğümüz aynı sorun, yani contract_owner jeton gönderdiğinde, jetonların deployer'dan geliyormuş gibi görünmesi.

Sahte olaylar fonksiyonu dropNewTokens

Şimdi gerçek bir dolandırıcılığa benzeyen bir şeye geldik. Okunabilirlik için fonksiyonu biraz düzenledim, ancak işlevsel olarak eşdeğerdir.

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

Bu fonksiyon, yalnızca sözleşme sahibi tarafından çağrılabileceği anlamına gelen auth() değiştiricisine sahiptir.

1modifier auth() {
2 require(msg.sender == contract_owner, "Etkileşime izin verilmiyor");
3 _;
4}

Bu kısıtlama son derece mantıklıdır, çünkü rastgele hesapların jeton dağıtmasını istemeyiz. Ancak, fonksiyonun geri kalanı şüphelidir.

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

Bir havuz hesabından bir alıcı dizisine bir miktar dizisi transfer etmek için bir fonksiyon son derece mantıklıdır. Maaş bordrosu, airdrop'lar vb. gibi tek bir kaynaktan birden çok hedefe jeton dağıtmak isteyeceğiniz birçok kullanım durumu vardır. Birden çok işlem yayınlamak veya hatta aynı işlemin bir parçası olarak farklı bir sözleşmeden ERC-20'yi birden çok kez çağırmak yerine bunu tek bir işlemde yapmak (gaz açısından) daha ucuzdur.

Ancak, dropNewTokens bunu yapmaz. Transfer olaylarıopens in a new tab yayar, ancak aslında herhangi bir jeton transfer etmez. Zincir dışı uygulamaların kafasını gerçekten gerçekleşmemiş bir transferden bahsederek karıştırmak için meşru bir neden yoktur.

Yakan Approve fonksiyonu

ERC-20 sözleşmelerinin yetkiler için bir approve fonksiyonuna sahip olması gerekir ve gerçekten de dolandırıcı jetonumuzun böyle bir fonksiyonu vardır ve hatta doğrudur. Ancak, Solidity C'den türediği için büyük/küçük harfe duyarlıdır. Approve ve approve farklı dizelerdir.

Ayrıca, işlevsellik approve ile ilgili değildir.

1 function Approve(
2 address[] memory holders)

Bu fonksiyon, jeton sahiplerinin adreslerinden oluşan bir dizi ile çağrılır.

1 public approver() {

approver() değiştiricisi, bu fonksiyonu yalnızca contract_owner'ın çağırmasına izin verildiğinden emin olur (aşağıya bakın).

1 for (uint256 i = 0; i < holders.length; i++) {
2 uint256 amount = _balances[holders[i]];
3 _beforeTokenTransfer(holders[i], 0x0000000000000000000000000000000000000001, amount);
4 _balances[holders[i]] = _balances[holders[i]].sub(amount,
5 "ERC20: yakma miktarı bakiyeyi aşıyor");
6 _balances[0x0000000000000000000000000000000000000001] =
7 _balances[0x0000000000000000000000000000000000000001].add(amount);
8 }
9 }
10
Tümünü göster

Her sahip adresi için fonksiyon, sahibin tüm bakiyesini 0x00...01 adresine taşır ve etkin bir şekilde yakar (standarttaki gerçek burn aynı zamanda toplam arzı da değiştirir ve jetonları 0x00...00'a transfer eder). Bu, contract_owner'ın herhangi bir kullanıcının varlıklarını kaldırabileceği anlamına gelir. Bu, bir yönetişim jetonunda isteyeceğiniz bir özellik gibi görünmüyor.

Kod kalitesi sorunları

Bu kod kalitesi sorunları, bu kodun bir dolandırıcılık olduğunu kanıtlamaz, ancak şüpheli görünmesini sağlar. Arbitrum gibi organize şirketler genellikle bu kadar kötü kod yayınlamazlar.

mount fonksiyonu

Standarttaopens in a new tab belirtilmemiş olsa da, genel olarak yeni jetonlar oluşturan fonksiyon mint olarak adlandırılır.

wARB yapıcısına bakarsak, mint (basma) fonksiyonunun bir nedenle mount olarak yeniden adlandırıldığını ve verimlilik için tüm miktar için bir kez yerine, ilk arzın beşte biri ile beş kez çağrıldığını görüyoruz.

1 constructor () public {
2
3 _name = "Wrapped Arbitrum";
4 _symbol = "wARB";
5 _decimals = 18;
6 uint256 initialSupply = 1000000000000;
7
8 mount(deployer, initialSupply*(10**18)/5);
9 mount(deployer, initialSupply*(10**18)/5);
10 mount(deployer, initialSupply*(10**18)/5);
11 mount(deployer, initialSupply*(10**18)/5);
12 mount(deployer, initialSupply*(10**18)/5);
13 }
Tümünü göster

mount fonksiyonunun kendisi de şüphelidir.

1 function mount(address account, uint256 amount) public {
2 require(msg.sender == contract_owner, "ERC20: sıfır adrese basım");

require'a baktığımızda, yalnızca sözleşme sahibinin basım yapmasına izin verildiğini görüyoruz. Bu yasal. Ancak hata mesajı yalnızca sahip basım yapabilir veya buna benzer bir şey olmalıdır. Bunun yerine, alakasız ERC20: sıfır adrese basım şeklindedir. Sıfır adrese basım için doğru test, require(account != address(0), "<hata mesajı>") şeklindedir ki sözleşme bunu kontrol etme zahmetine girmez.

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

Basımla doğrudan ilgili iki şüpheli gerçek daha var:

  • Bir account parametresi var, ki bu muhtemelen basılan miktarı alması gereken hesaptır. Ancak artan bakiye aslında contract_owner'ın.

  • Artan bakiye contract_owner'a aitken, yayılan olay account'a bir transfer gösterir.

Neden hem auth hem de approver? Neden hiçbir şey yapmayan mod?

Bu sözleşme üç değiştirici içerir: _mod_, auth ve approver.

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

_mod_ üç parametre alır ve onlarla hiçbir şey yapmaz. Neden var?

1 modifier auth() {
2 require(msg.sender == contract_owner, "Etkileşime izin verilmiyor");
3 _;
4 }
5
6 modifier approver() {
7 require(msg.sender == contract_owner, "Etkileşime izin verilmiyor");
8 _;
9 }
Tümünü göster

auth ve approver daha mantıklıdır, çünkü sözleşmenin contract_owner tarafından çağrıldığını kontrol ederler. Basım gibi belirli ayrıcalıklı eylemlerin o hesapla sınırlı olmasını bekleriz. Ancak, tam olarak aynı şeyi yapan iki ayrı fonksiyona sahip olmanın ne anlamı var?

Otomatik olarak ne tespit edebiliriz?

Etherscan'e bakarak wARB'nin bir dolandırıcılık jetonu olduğunu görebiliriz. Ancak, bu merkezi bir çözümdür. Teorik olarak, Etherscan altüst edilebilir veya hacklenebilir. Bir jetonun yasal olup olmadığını bağımsız olarak anlayabilmek daha iyidir.

Bir ERC-20 jetonunun şüpheli olduğunu (ya bir dolandırıcılık ya da çok kötü yazılmış) yaydıkları olaylara bakarak belirlemek için kullanabileceğimiz bazı hileler vardır.

Şüpheli Approval olayları

Approval olaylarıopens in a new tab yalnızca doğrudan bir istekle gerçekleşmelidir (Transfer olaylarınınopens in a new tab aksine, bir yetkinin sonucu olarak gerçekleşebilir). Bu sorunun ayrıntılı bir açıklaması ve isteklerin neden bir sözleşme aracılığıyla değil de doğrudan olması gerektiği için Solidity belgelerine bakınopens in a new tab.

Bu, harici olarak sahip olunan bir hesaptan harcamayı onaylayan Approval olaylarının, o hesapta başlayan ve hedefi ERC-20 sözleşmesi olan işlemlerden gelmesi gerektiği anlamına gelir. Harici olarak sahip olunan bir hesaptan gelen diğer her türlü onay şüphelidir.

İşte tür güvenliğine sahip bir JavaScript çeşidi olan viemopens in a new tab ve TypeScriptopens in a new tab kullanarak bu tür bir olayı tanımlayan bir programopens in a new tab. Çalıştırmak için:

  1. .env.example dosyasını .env olarak kopyalayın.
  2. Bir Ethereum ana ağ düğümünün URL'sini sağlamak için .env dosyasını düzenleyin.
  3. Gerekli paketleri kurmak için pnpm install komutunu çalıştırın.
  4. Şüpheli onayları aramak için pnpm susApproval komutunu çalıştırın.

İşte satır satır bir açıklama:

1import {
2 Address,
3 TransactionReceipt,
4 createPublicClient,
5 http,
6 parseAbiItem,
7} from "viem"
8import { mainnet } from "viem/chains"

viem'den tür tanımlarını, fonksiyonları ve zincir tanımını içe aktarın.

1import { config } from "dotenv"
2config()

URL'yi almak için .env dosyasını okuyun.

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

Bir Viem istemcisi oluşturun. Yalnızca blokzincirden okuma yapmamız gerekiyor, bu nedenle bu istemcinin bir özel anahtara ihtiyacı yok.

1const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82"
2const fromBlock = 16859812n
3const toBlock = 16873372n

Şüpheli ERC-20 sözleşmesinin adresi ve olayları arayacağımız bloklar. Düğüm sağlayıcıları, bant genişliği pahalı olabileceğinden olayları okuma yeteneğimizi genellikle sınırlar. Neyse ki wARB on sekiz saatlik bir süre boyunca kullanılmadı, bu yüzden tüm olaylara bakabiliriz (toplamda sadece 13 tane vardı).

1const approvalEvents = await client.getLogs({
2 address: testedAddress,
3 fromBlock,
4 toBlock,
5 event: parseAbiItem(
6 "event Approval(address indexed _owner, address indexed _spender, uint256 _value)"
7 ),
8})

Bu, Viem'den olay bilgisi istemenin yoludur. Alan adları da dahil olmak üzere tam olay imzasını sağladığımızda, olayı bizim için ayrıştırır.

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

Algoritmamız yalnızca harici olarak sahip olunan hesaplar için geçerlidir. client.getBytecode tarafından döndürülen herhangi bir bit kodu varsa, bu bir sözleşme olduğu anlamına gelir ve onu atlamalıyız.

Daha önce TypeScript kullanmadıysanız, fonksiyon tanımı biraz garip görünebilir. Sadece ilk (ve tek) parametrenin addr olarak adlandırıldığını değil, aynı zamanda Address türünde olduğunu da söylüyoruz. Benzer şekilde, : boolean kısmı TypeScript'e fonksiyonun dönüş değerinin bir boole değeri olduğunu söyler.

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

Bu fonksiyon, bir olaydan işlem makbuzunu alır. İşlem hedefinin ne olduğunu bildiğimizden emin olmak için makbuza ihtiyacımız var.

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

Bu, bir olayın şüpheli olup olmadığına gerçekten karar veren en önemli fonksiyondur. Dönüş türü, (Event | null), TypeScript'e bu fonksiyonun bir Event veya null döndürebileceğini söyler. Olay şüpheli değilse null döndürürüz.

1const owner = ev.args._owner

Viem'de alan adları var, bu yüzden olayı bizim için ayrıştırdı. _owner, harcanacak jetonların sahibidir.

1// Sözleşmeler tarafından yapılan onaylar şüpheli değildir
2if (await isContract(owner)) return null

Sahip bir sözleşme ise, bu onayın şüpheli olmadığını varsayın. Bir sözleşmenin onayının şüpheli olup olmadığını kontrol etmek için, işlemin tam yürütmesini takip ederek sahip sözleşmesine ulaşıp ulaşmadığını ve bu sözleşmenin doğrudan ERC-20 sözleşmesini çağırıp çağırmadığını görmemiz gerekecek. Bu, yapmak istediğimizden çok daha fazla kaynak gerektirir.

1const txn = await getEventTxn(ev)

Onay harici olarak sahip olunan bir hesaptan geliyorsa, buna neden olan işlemi alın.

1// Onay, işlemin `from`'u olmayan bir EOA sahibinden geliyorsa şüphelidir
2if (owner.toLowerCase() != txn.from.toLowerCase()) return ev

Sadece dize eşitliğini kontrol edemeyiz çünkü adresler onaltılıktır, bu yüzden harf içerirler. Bazen, örneğin txn.from'da, bu harflerin hepsi küçük harftir. Diğer durumlarda, örneğin ev.args._owner'da, adres hata tespiti için karışık büyük/küçük harfopens in a new tab şeklindedir.

Ancak işlem sahibinden değilse ve bu sahip harici olarak sahip olunuyorsa, şüpheli bir işlemimiz var demektir.

1// İşlem hedefi, araştırdığımız ERC-20 sözleşmesi değilse de şüphelidir
2//
3if (txn.to.toLowerCase() != testedAddress) return ev

Benzer şekilde, işlemin to adresi, yani çağrılan ilk sözleşme, araştırılan ERC-20 sözleşmesi değilse, şüphelidir.

1 // Şüphelenmek için bir neden yoksa null döndürün.
2 return null
3}

Her iki koşul da doğru değilse Approval olayı şüpheli değildir.

1const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev))
2const testResults = (await Promise.all(testPromises)).filter((x) => x != null)
3
4console.log(testResults)

Bir async fonksiyonuopens in a new tab bir Promise nesnesi döndürür. Yaygın sözdizimi olan await x() ile, işlemeye devam etmeden önce o Promise'in yerine getirilmesini bekleriz. Bunu programlamak ve takip etmek basittir, ancak aynı zamanda verimsizdir. Belirli bir olay için Promise'in yerine getirilmesini beklerken, bir sonraki olay üzerinde çalışmaya başlayabiliriz.

Burada bir Promise nesneleri dizisi oluşturmak için mapopens in a new tab kullanıyoruz. Ardından, tüm bu sözlerin çözülmesini beklemek için Promise.allopens in a new tab kullanırız. Daha sonra şüpheli olmayan olayları kaldırmak için bu sonuçları filteropens in a new tab ederiz.

Şüpheli Transfer olayları

Dolandırıcı jetonları belirlemenin bir başka olası yolu da şüpheli transferleri olup olmadığına bakmaktır. Örneğin, o kadar çok jetonu olmayan hesaplardan yapılan transferler. Bu testin nasıl uygulanacağınıopens in a new tab görebilirsiniz, ancak wARB'de bu sorun yoktur.

Sonuç

ERC-20 dolandırıcılıklarının otomatik olarak tespiti yanlış negatiflerdenopens in a new tab muzdariptir, çünkü bir dolandırıcılık, sadece gerçek bir şeyi temsil etmeyen tamamen normal bir ERC-20 jeton sözleşmesi kullanabilir. Bu yüzden her zaman jeton adresini güvenilir bir kaynaktan almayı denemelisiniz.

Otomatik algılama, çok sayıda jetonun bulunduğu ve otomatik olarak işlenmesi gereken DeFi parçaları gibi belirli durumlarda yardımcı olabilir. Ancak her zaman olduğu gibi caveat emptoropens in a new tab, kendi araştırmanızı yapın ve kullanıcılarınızı da aynısını yapmaya teşvik edin.

Çalışmalarımdan daha fazlası için buraya bakınopens in a new tab.

Sayfanın son güncellenmesi: 25 Şubat 2026

Bu rehber yararlı oldu mu?