Các sàn phi tập trung chắc hẳn đã không còn xa lạ với các bạn đang tìm hiểu về blockchain. Mỗi sàn thường sẽ giao dịch trên một mạng blockchain cụ thể. Ví dụ như sàn Uniswap thì chạy trên mạng Ethereum, hay sàn PancakeSwap thì chạy trên mạng Binance.

1pEwuNK5hEs8uM5AQN7A9TKGjeHrypaZQ

Đây là giao diện mua bán của sàn PancakeSwap
1A_Y7im1hcm27bmoD61uNkQTUctzCRgOQ

Ngoài giao diện đẹp dễ sử dụng, họ còn cung cấp các document phục vụ cho các bạn dev. Mình đã thử xem tài liệu họ cung cấp thì thấy khá hay ho. Từ đó các bạn có thể tạo ra một con sniper bot, tự động chờ có cặp tiền mới vừa được list lên sàn là bot có thể mua ngay và luôn. Mình thử tạo con bot giống vậy nhé.

1. Phát họa flow

1v8tLgW-xh0I8-IPubUq9UKe91umkmf0_

Khi một ông A nào đã phát triển xong 1 đồng token, và ông A đã đẩy lên mạng Binance. Rồi sau đó ông A vào PancakeSwap tạo một cặp thanh khoản. Thì khi ông A tạo sẽ gọi đến một hàm là createPair(). Khi hàm này chạy nó sẽ tạo ra một event. Con bot của mình sẽ kết nối và lắng nghe event. Ngay khi có tín hiệu thì mình có thể thực hiện các logic khác như check thanh khoản hoặc lấy giá hoặc buy token đó ngay và luôn.

2. Bắt đầu code

2.1 Cài đặt

Về cơ bản có thể sẽ cần 2 gói sau

npm install ethers
npm install Web3

2.2 Kết nối

Import các thư viện cần sử dụng vào

const ethers = require('ethers');
const Web3 = require('Web3');
const fs = require('fs');

Có nhiều cách để kết nối với ví của bạn. Ở đây mình sẽ để cả 2 cách luôn
■Cách 1: Sử dụng key là chuỗi từ khóa bí mật

// Đây là thông tin provider của network bạn muốn kết nối. Trong ví dụ là của testnet BSC. Những thông tin này bạn có thể tìm đơn giản trên document của họ
const providerPath = "wss://speedy-nodes-nyc.moralis.io/236c39c2ed0cb45e1ad960dd/bsc/testnet/ws"
const provider = new ethers.providers.WebSocketProvider(providerPath);

// mnemonic là từ khóa bí mất khi bạn tạo ví. Có thể là 12 từ hoặc 24 từ
const mnemonic = "rebuild broom xxx xxx xxx";

let wallet, account;
async function init() {
    // trong 1 cái ví bạn tưởng tượng thường có nhiều ngăn. Ở đây cũng vậy, wallet điện tử cũng sẽ có nhiều ngăn, một ngăn tương đương với một address khác nhau.
    // "m/44'/60'/0'/0/0" là chỉ ra mình muốn lấy address ở vị trí đầu tiên index = 0
    // nếu bạn muốn trỏ đến address vị trí thứ 2 thì sẽ là "m/44'/60'/0'/0/1"
    wallet = ethers.Wallet.fromMnemonic(mnemonic, "m/44'/60'/0'/0/0");
    
    // bắt đầu connect với ví trên blockchain
    account = wallet.connect(provider);
}

async function main() {
    await init();
}

main();

■Cách 2: Sử dụng key là file json

const providerPath = "wss://speedy-nodes-nyc.moralis.io/236c39c2ed0cb45e1ad960dd/bsc/testnet/ws"
const provider = new ethers.providers.WebSocketProvider(providerPath);

const keystore = (fs.readFileSync("yourFilePath", 'utf8')).toString();

let wallet, account;
async function init() {
    wallet = await ethers.Wallet.fromEncryptedJson(keystore, "yourPassword");
    account = wallet.connect(provider);
}

async function main() {
    await init();
}

main();

Ok, về cơ bản đã kết nối với ví xong. Nếu bạn muốn check xem trong ví còn bao nhiêu tiền thì có thể sử dụng dòng code sau:

// connect
// ...

// get your balance
async function getBalance() {
    const balance = await account.getBalance();
    const ethBalance = ethers.utils.formatUnits(balance, "ether");
    console.log(`
        ACCOUNT INFO
        =================
        Address: ${account.address}
        Balance: ${ethBalance} ${addresses.SYMBOL}
    `);
}

async function main() {
    await init();
    await getBalance();
}

main();

2.3 Lắng nghe sự kiện tạo cặp thanh khoản

Đến phần chính rồi, bây giờ chúng ta thử code hàm lắng nghe và print ra các cặp vừa được list sàn nhé.
Để lắng nghe thì mình sẽ sử dụng event PairCreated. Cách sử dụng các bạn có thể tham khảo thêm trên document https://docs.pancakeswap.finance/code/smart-contracts/pancakeswap-exchange/v2/factory-v2#events

let factory;
async function init() {
    factory = new ethers.Contract(
        addresses.factory,
        [
            'event PairCreated(address indexed token0, address indexed token1, address pair, uint)'
        ],
        account
    );
}

async function listenNewPair() {
    factory.on('PairCreated', async (token0, token1, pairAddress) => {
        // khi có cặp list sàn, thì hàm này sẽ được chạy và print cho chúng ta thông tin của cặp đó.
        // token0: là địa chỉ của token mới được tạo hoặc cũng có thế là BNB
        // token1: là địa chỉ của token mới được tạo hoặc cũng có thể là BNB
        // nghĩa là nếu token0 là địa chỉ của BNB thì token1 là địa chỉ của token mới được tạo và ngược lại
        // pairAddress: là địa chỉ của cặp thanh khoản
        console.log(`
            =================
            token0: ${token0}
            token1: ${token1}
            pairAddress: ${pairAddress}
            =================
        `);
    }
}

Output có dạng như sau:

=================
token0: 0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c
token1: 0xE323xxxxx
pairAddress: 0x99D88xxxxx
=================

2.4 Verify

async function listenNewPair() {
    factory.on('PairCreated', async (token0, token1, pairAddress) => {
        console.log(`
            =================
            token0: ${token0}
            token1: ${token1}
            pairAddress: ${pairAddress}
            =================
        `);
        
        const BNB = "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c";

        let tokenIn, tokenOut;
        if(token0 === BNB) {
          tokenIn = token0; 
          tokenOut = token1;
        }
        
        if(token1 == BNB) {
          tokenIn = token1; 
          tokenOut = token0;
        }
        
        //The quote currency is not BNB
        if(typeof tokenIn === 'undefined') {
              return;
        }
    }
}

2.5 Chốt kèo - buy token

let router;
async function init() {
    // ...
    // ...
    
    router = new ethers.Contract(
        addresses.router,
        [
            'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)',
            'function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)'
        ],
        account
    );
}


async function listenNewPair() {
    factory.on('PairCreated', async (token0, token1, pairAddress) => {
        // ...
        // ...
        
        // Mình muốn mua token mới với 0.1BNB
        const amountIn = ethers.utils.parseUnits('0.1', 'ether');
        const amounts = await router.getAmountsOut(amountIn, [tokenIn, tokenOut]);
        const amountOutMin = amounts[1].sub(amounts[1].div(10));
        const tx = await router.swapExactETHForTokens(
            amountOutMin,
            [tokenIn, tokenOut],
            addresses.recipient,
            Date.now() + 1000 * 60 * 10, //10 minutes
            {
                gasPrice: provider.getGasPrice(),
                gasLimit: 2100000
            }
        );
        
        const receipt = await tx.wait(); 
    }
}

Hàm swapExactETHForTokens là đổi BNB hoặc ETH ra token. Ngoài ra còn có những hàm khác như swapExactTokensForETH là đổi token ra ETH, hoặc swapExactTokensForTokens đổi token A qua token B. Bạn có thể tham khảo thêm các hàm ở document https://docs.pancakeswap.finance/code/smart-contracts/pancakeswap-exchange/v2/router-v2

3. Tổng kết

Con bot có thể hỗ trợ bạn mua ngay lập tức ngay khi cặp tiền bạn đang săn vừa list sàn. Nhưng cũng sẽ có rủi ro là đôi khi fake token được list ngay trước cặp tiền real vài giây và bạn có thể bị bắt nhầm token mong muốn. Nên hãy verify hết sức cẩn thận trước khi buy nhé.

4. Tham khảo