Automated Market Maker (AMM) Kontratı ve Etkileşimler

Eray USTA
6 min readSep 6, 2022

--

Bu yazıda AMM yani otomatik piyasa yapıcı kontratı oluşturmaktan ve onu React ile çalıştırmaktan bahsedeceğim. Polygon ağı üzerinde kullanıp deploy için Alchemy nin RPC url inden faydalanacağız. Temelde token çifti için faucet oluşturacağız, AMM kontratının temel fonksiyonlarından olan likidite pool u oluşturup, swap yapabilecek ve koyduğumuz parayı withdraw ile geri alabileceğiz.

Önkoşullar;

  • Nodejs
  • Metamask eklentisi
  • Alchemy hesabı
  • Visual Studio Code

AMM Nedir?

Otomatik Piyasa Yapıcı (AMM), fiyat varlıklarının matematiksel formülüne dayanan bir tür merkezi olmayan borsadır. Geleneksel borsada kullanılan bir emir defteri kullanan geleneksel alıcı ve satıcılar yerine likidite havuzları kullanılarak dijital varlıkların herhangi bir izin olmadan ve otomatik olarak alınıp satılmasına olanak tanır, burada varlıklar bir fiyatlandırma algoritmasına göre fiyatlandırılır.

Örneğin, Uniswap p * q = k kullanır; burada p, likidite havuzundaki bir jetonun miktarıdır ve q, diğerinin miktarıdır. Burada “k”, havuzun toplam likiditesinin her zaman aynı kalması gerektiği anlamına gelen bir sabittir.

Daha fazla açıklama için, bir AMM’nin madeni para A ve Token B’ye, iki değişken varlığa sahip olması durumunda, A her satın alındığında, havuzda satın alma öncesine göre daha az A olduğu için A’nın fiyatı artar. Tersine, havuzda daha fazla B olduğu için B’nin fiyatı düşer. Havuz, havuzdaki A’nın toplam değerinin her zaman havuzdaki B’nin toplam değerine eşit olacağı sabit dengede kalır. Boyut, yalnızca yeni likidite sağlayıcıları havuza katıldığında genişleyecektir.

Daha fazlası için;

Akıllı Kontrat Geliştirme

Ardından, AMM’yi çalıştırmak için gereken durum değişkenlerini tanımlarız. Varlıkların fiyatını belirlemek için Uniswap tarafından kullanılanla aynı matematiksel formülü kullanacağız (K = totalToken1 * totalToken2).

Basitlik amacıyla, ERC-20 tokenlarla uğraşmak yerine kendi dahili denge haritamızı (token1Balance & token2Balance) sürdürüyoruz. Solidity noktalı sayıları desteklemediğinden, noktadan sonraki ondalık değeri temsil etmek için bir tamsayı değerinin ilk altı basamağını ayıracağız. Bu, sayıları 10⁶ (Precision) faktörüyle ölçeklendirerek elde edilir.

Şimdi havuz boşken fonksiyonlara aktarılan parametrelerin geçerliliğini kontrol etmek ve belirli aktiviteleri kısıtlamak için kullanılacak modifier ları tanımlayacağız.

Sözleşmenin ve cüzdan sahibinin mevcut durumunu elde etmek için aşağıdaki işlevler kullanılır.

ERC-20 tokenları kullanmadığımız ve bunun yerine bakiyenin kaydını kendimiz tuttuğumuz dApp ile etkileşime girebilmeleri için yeni kullanıcılara tokenları tahsis etmenin bir yoluna ihtiyacımız var. Kullanıcılar oynamak için bazı tokenlar almak için faucet işlevini çağırabilir!

Şimdi üç temel işlevi uygulamaya başlayacağız: Provide, Withdraw and Swap.

Provide (Temin Etmek)

Provide işlevi iki parametre alır — kullanıcının havuzda kilitlemek istediği token1 miktarı ve token2 miktarı. Havuz başlangıçta boşsa, denklik oranı _amountToken1 : _amountToken2 olarak ayarlanır ve kullanıcıya bunun için 100 pay verilir. Aksi takdirde, kullanıcı tarafından sağlanan iki miktarın eşdeğer değerde olup olmadığı kontrol edilir. Bu, iki miktarın havuzda kilitli ilgili jetonlarının toplam sayısıyla eşit oranda olup olmadığını kontrol ederek yapılır, yani _amountToken1 : totalToken1 :: _amountToken2 : totalToken2 tutmalı.

Not: Yukarıdaki fonksiyonda yaptığımız bakiye güncelleme sırasına dikkatlice dikkat edin. İlk önce tokenleri kullanıcının hesabından düşüyoruz ve en son adımda onun hisse bakiyesini güncelliyoruz. Bu, yeniden giriş saldırısını önlemek için yapılır.

Verilen fonksiyonlar, kullanıcının verilen token miktarı için kilitlemesi gereken ikinci token miktarı hakkında bir tahmin almasına yardımcı olur. Burada yine, verilen token2 miktarını kilitlemek istiyorsak gerekli token1 miktarını belirlemek için _amountToken1 : totalToken1 :: _amountToken2 : totalToken2 oranını kullanırız ve bunun tersi de geçerlidir.

Formül: _amountToken1 / totalToken1 = _amountToken2 / totalToken2

Withdraw (Çekmek)

Bir kullanıcı, tokenlarını geri almak için belirli bir miktarda payı almak istediğinde kullanılır. Token1 ve Token2, ihraç edilen toplam paylara göre yakılan payla orantılı olarak havuzdan serbest bırakılır, yani pay : totalShare :: miktarTokenX : totalTokenX.

Swap (Takas)

Token1'den Token2'ye geçiş yapmak için üç işlev uygulayacağız — `getSwapToken1Estimate`, `getSwapToken1EstimateGivenToken2` & `swapToken1`. İlk iki işlev, yalnızca tahmin amacıyla takas değerlerini belirlerken, sonuncusu dönüştürme işlemini gerçekleştirir.

`getSwapToken1Estimate`, kullanıcının belirli bir miktarda token1 yatırırken alacağı token2 miktarını döndürür. Token2 miktarı, `K = toplamToken1 * totalToken2` denkleminden elde edilir, burada K’nin işlemden önce/sonra aynı kalması gerekir.

Bu bize K = (totalToken1 + amountToken1) * (totalToken2 — amountToken2) verir ve bu denklemi çözerek amountToken2 değerini alırız. Son satırda, havuzun hiçbir zaman iki taraftan da tamamen boşaltılmamasını sağlıyoruz, bu da denklemi tanımsız hale getirecektir.

`getSwapToken1EstimateGivenToken2`, kullanıcının belirli bir miktarda token2 almak için yatırması gereken token1 miktarını döndürür. Token1 miktarı, aşağıdaki denklem K = (totalToken1 + amountToken1) * (totalToken2 — amountToken2) çözülerek benzer şekilde elde edilir.

swapToken1 fonksiyonu token1 i alıp cevap olarak token2 amountunu yani pool da ona denk gelecek miktarı verir.

Benzer şekilde Token2'den Token1'e takas için üç işlevi uygularız — getSwapToken2Estimate, getSwapToken2EstimateGivenToken1 & swapToken2 aşağıdaki gibi.

Kodumuzun tamamı şu şekilde olacaktır;

Tebrikler! 🎉 AMM kontratını yazdınız. Şimdi bunu hardhat, önce locak sonra alchemy kullanarak mumbai ağlarına deploy edelim.

Dapp Oluşturma

Önce hızlı ilerlemek için github reposunu local ortama kopyalayalım ve kurulumları yapalım.

- git clone https://github.com/erayusta/amm-dapp- yarn install- yarn add --dev "hardhat@^2.9.9" "@nomicfoundation/hardhat-toolbox@^1.0.1" "@nomicfoundation/hardhat-network-helpers@^1.0.0" "@nomicfoundation/hardhat-chai-matchers@^1.0.0" "@nomiclabs/hardhat-ethers@^2.0.0" "@nomiclabs/hardhat-etherscan@^3.0.0" "chai@^4.2.0" "ethers@^5.4.7" "hardhat-gas-reporter@^1.0.8" "solidity-coverage@^0.7.21" "@typechain/hardhat@^6.1.2" "typechain@^8.1.0" "@typechain/ethers-v5@^10.1.0" "@ethersproject/abi@^5.4.7" "@ethersproject/providers@^5.4.7"- yarn add @openzeppelin/contracts- yarn add @nomiclabs/hardhat-etherscan

Hardhat Ayarları ve Local Ağ Deploy Etmek

İlk gereksinimleri karşılamak için daha önce yazdığım aşağıdaki yazıdan faydalanabilirsiniz.

npx hardhat

Öncel AMM.sol dosyamızı contracts klasörü içerisine koyalım. Default gelen kontratı silebiliriz. scripts klasörü içerisindeki dosyayıda deploy.js olarak değiştirip gerekli düzenlemeleri (tam klavuzda örnek düzenleme var) yapalım. (github reposunda doğrudan klasörler olacağı için ek bir şey yapmanıza gerek kalmayabilir.)

hardhat.config.js dosyamızı düzenleyelim. Alchemy den url almamız gerekiyor.

alchemy
npx hardhat node
npx hardhat run scripts/deploy.js --network localhost
local deploy

Deploy ettikten sonra verdiği kontrat adresini src/constants.js dosyasında güncellememiz gerekiyor.

Mumbai Ağına Deploy Etme

Hardhat.config.js e Alchemy den aldığımız rpc url i ekleyip, deploy edecek cüzdana https://faucet.polygon.technology/ buradan matic çekmeliyiz.

npx hardhat run scripts/deploy.js —-network mumbai
npx hardhat verify 0xEb848B562B202307C2c939DC0E9c7c276774183b —-network mumbai

Komutu ile verify ediyoruz.

https://mumbai.polygonscan.com/address/0xEb848B562B202307C2c939DC0E9c7c276774183b#writeContract

Daha sonra ise bu kontrat adresini src/constants.js dosyasında güncelliyoruz.

Dapp Etkileşimleri

Eğer buraya kadar sorunsuz geldiyseniz npm start komutu ile direk çalıştırabilmeniz gerekmektedir.

1- Faucet ile Token1 ve Token2 alalım.

  • 1000 adet token1
  • 1000 adet token2 aldım.

2- Provide ile pool a para koyalım.

1000 Token1 ı ve 1000 Token2 yi havuza ekledik. Burada oranı ilk pool oluşturan tutarlar belirliyor. Sonraki hesap havuzdaki oranlar doğrultusunda token ekleyebilecektir.

Örneğin 500 e 1000 olarak eklemiş olsaydık sonraki havuza eklenecek likiditede bu oranda olacaktı.

3- Swap fonksiyonu ile pool daki tokenlardan takas yapalım.

Pool da yer alan tutarlara göre oranlara göre swap yapma hesabı yapılmaktadır. Ben örneğin daha sonra ekler yaparak daha anlaşılır hale getirmeye çalıştım. Dengeyi korumak adına otomatik piyasa yapıcı kontratımız 100 Token1 e göre 38.78 adet Token2 verebilecektir.

4- Withdraw ile pool da yer alan paramızı geri çekelim.

Total share imin %100 ünü çekmek istediğimde

Yarısını almak istersem şu şekilde oluyor;

Her konuyu en başından tekrar tekrar anlatmamak adına tam klavuz yazımı da referans göstermeye çalıştım. Bundan sonraki anlatımlarda deploy vs. gibi konularda derinlere girmeyip daha çok kontrat ve çalışma mekanizması özelinde içerikler paylaşmaya çalışacağım.

Beni twitter hesabımdan ve youtube hesabımdan takip edebilirsiniz.

Github kaynak kodları:

Kaynaklar:

--

--