Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

Dibuat di: 2025-03-17 11:00:33, diperbarui pada: 2025-03-18 15:54:46
comments   0
hits   56

[TOC]

Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

Kata pengantar

Di pasar mata uang kripto, peluang perdagangan cepat berlalu, terutama jendela arbitrase hasil tinggi, yang sering kali hanya berlangsung dalam waktu yang sangat singkat. Jika Anda hanya mengandalkan perdagangan manual, sering kali sulit untuk mengikuti pasar. Oleh karena itu, perdagangan pending order menjadi sarana penting untuk meningkatkan efisiensi perdagangan.

Artikel ini didasarkan pada ide-ide artikel Xiaocao. Artikel ini merekonstruksi strategi order pool perdagangan Curve.fi pada platform FMZ dan memperkenalkan cara untuk mencapai efek yang sama melalui platform FMZ. Pada saat yang sama, skema perlindungan kunci pribadi juga akan dibahas secara rinci untuk memastikan keamanan dan kenyamanan dalam proses perdagangan terprogram.

Strategi ini ditulis dalam JavaScript dan menggunakan objek pertukaran eth web3 dari platform FMZ.

Keamanan berdasarkan desain

Berdasarkan artikel「Cara menerapkan penempatan pesanan di bursa terdesentralisasi - mengambil Curve sebagai contoh」Dalam desain keamanan, kami menggunakan metode enkripsi AES yang serupa. Kami menggunakan kata sandi dan hitungan untuk mengenkripsi data.

  • Kata sandi: kata sandi, di-hash dengan sha256.
  • Hitung: hitung, gunakan stempel waktu sebagai hitungan acak untuk berpartisipasi dalam enkripsi.
  1. Enkripsikan kunci pribadi secara offline, gunakan “kata sandi” dan “hitungan” untuk mengenkripsi kunci pribadi, dan dapatkan kunci rahasia terenkripsi.
  2. Konfigurasikan kunci terenkripsi, kata sandi, hitungan, dll. ke platform FMZ, dan gunakan kontrol parameter antarmuka kebijakan “Encrypted String”. Konten yang dimasukkan dalam kontrol akan dienkripsi, dan informasi konfigurasi yang direkam pada platform bukanlah konten teks biasa dalam kontrol.
  3. Saat strategi berjalan, kustodian dapat mendekripsi informasi dan memuat kunci rahasia (setelah memuat kunci rahasia, semua variabel terkait akan diatur ulang).

Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

// 加密/解密方式
function cryptoKey(type, data, passWord, count) {
    // 加载加密库
    eval(HttpQuery("https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js"))

    // sha256 密码
    var hash = Encode("sha256", "string", "hex", passWord).substring(0, 32)

    if (type == "encrypt") {
        var key = aesjs.utils.utf8.toBytes(hash.substring(0, 32))
        var counter = new aesjs.Counter(count)
        var textBytes = aesjs.utils.utf8.toBytes(data)
        var aesCtr = new aesjs.ModeOfOperation.ctr(key, counter)
        var encryptedBytes = aesCtr.encrypt(textBytes)
        var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes)
        return encryptedHex
    } else if (type == "decrypt") {
        var key = aesjs.utils.utf8.toBytes(hash.substring(0, 32))
        var counter = new aesjs.Counter(count)
        var aesCtrDecrypt = new aesjs.ModeOfOperation.ctr(key, counter)
        const decryptedBytes = aesCtrDecrypt.decrypt(aesjs.utils.hex.toBytes(data))
        const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes)        
        return decryptedText
    } else {
        throw "function cryptoKey(), param 'type' not support"
    }
}

Konfigurasikan objek pertukaran web3

https://www.fmz.com/m/platforms/add

Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

Ada banyak node Ethereum yang tersedia daring, yang umum digunakan adalah:

Anda dapat mengajukan permohonan untuk membuat alamat Anda sendiri dan kemudian mengonfigurasinya untuk digunakan. Saya tidak akan membahas detailnya di sini. Anda dapat mengisi informasi kunci sesuai keinginan. Kami berencana untuk memuatnya secara dinamis, jadi tidak ada gunanya mengonfigurasinya di sini.

    exchange.IO("key", cryptoKey(descCryptoType[cryptoType], keyData, password, count))
    walletAddress = exchange.IO("address")
    Log("载入秘钥,钱包地址:", walletAddress)

Gunakan fungsi exchange.IO(“key”, …) yang dienkapsulasi oleh platform untuk memuat dan memperbarui kunci pribadi setelah dekripsi.

Mendaftarkan ABI

Setelah menyelesaikan masalah keamanan, langkah berikutnya adalah melakukan beberapa operasi pada kontrak pintar Ethereum. Menurut Curve.fi DEX yang disebutkan oleh Xiaocao, kita dapat dengan mudah menemukan dApp ini secara online:

https://curve.fi/dex/ethereum/pools/factory-stable-ng-102/deposit/

Di halaman kumpulan pertukaran sDAI/sUSDe, Anda dapat menemukan alamat kontrak kumpulan pertukaran dan mengamati kontraknya. Metode yang dapat kita gunakan adalah:

  • koin: Dapatkan alamat kontrak token yang berpartisipasi dalam transaksi di pool
  • get_dy: Hitung jumlah pertukaran saat ini di kumpulan.
  • pertukaran: jalankan pertukaran.

Untuk memanggil metode ini, Anda perlu mendaftarkan ABI kontrak pool terlebih dahulu. Saya tidak memfilter metode yang digunakan, tetapi langsung menyalin dan menempel ABI lengkap lalu mendaftarkannya:

    // sDAI/sUSDe 池合约地址和ABI
    var poolAddress = "0x167478921b907422F8E88B43C4Af2B8BEa278d3A"
    // 合约ABI
    var poolABI = `[{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchangeUnderlying","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_id","type":"int128","indexed":false},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityImbalance","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampA","inputs":[{"name":"old_A","type":"uint256","indexed":false},{"name":"new_A","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"A","type":"uint256","indexed":false},{"name":"t","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ApplyNewFee","inputs":[{"name":"fee","type":"uint256","indexed":false},{"name":"offpeg_fee_multiplier","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetNewMATime","inputs":[{"name":"ma_exp_time","type":"uint256","indexed":false},{"name":"D_ma_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_A","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_offpeg_fee_multiplier","type":"uint256"},{"name":"_ma_exp_time","type":"uint256"},{"name":"_coins","type":"address[]"},{"name":"_rate_multipliers","type":"uint256[]"},{"name":"_asset_types","type":"uint8[]"},{"name":"_method_ids","type":"bytes4[]"},{"name":"_oracles","type":"address[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_min_mint_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_max_burn_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_max_burn_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"},{"name":"_receiver","type":"address"},{"name":"_claim_admin_fees","type":"bool"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"withdraw_admin_fees","inputs":[],"outputs":[]},{"stateMutability":"view","type":"function","name":"last_price","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ema_price","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_p","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_deadline","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}]},{"stateMutability":"view","type":"function","name":"get_dx","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_is_deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A_precise","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_balances","inputs":[],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"view","type":"function","name":"stored_rates","inputs":[],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"view","type":"function","name":"dynamic_fee","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"ramp_A","inputs":[{"name":"_future_A","type":"uint256"},{"name":"_future_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_new_fee","inputs":[{"name":"_new_fee","type":"uint256"},{"name":"_new_offpeg_fee_multiplier","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_ma_exp_time","inputs":[{"name":"_ma_exp_time","type":"uint256"},{"name":"_D_ma_time","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"N_COINS","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"offpeg_fee_multiplier","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_exp_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D_ma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_last_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}]},{"stateMutability":"view","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"salt","inputs":[],"outputs":[{"name":"","type":"bytes32"}]}]`

    // 注册ABI
    exchange.IO("abi", poolAddress, poolABI)

Baca saldo dompet dan saldo token

Membaca saldo token dompet, presisi token, nama token, dll. semuanya merupakan metode kontrak ERC20 dan terdaftar secara default.

    var token0Name = exchange.IO("api", token0Address, "symbol")
    var token1Name = exchange.IO("api", token1Address, "symbol")
    var token0Decimals = exchange.IO("api", token0Address, "decimals")
    var token1Decimals = exchange.IO("api", token1Address, "decimals")
    var sDAI_Balance = exchange.IO("api", sDAI["address"], "balanceOf", walletAddress)
    var sUSDe_Balance = exchange.IO("api", sUSDe["address"], "balanceOf", walletAddress)

Memantau kumpulan pertukaran

function getBuyProfit(poolAddress, sDAI, sUSDe, amountIn, entryPriceForDAI) {    
    var data = exchange.IO("api", poolAddress, "get_dy", sDAI["idx"], sUSDe["idx"], toInnerAmount(amountIn, sDAI["decimals"]))
    Log("get_dy:", data)
    var amountOut = toAmount(data, sUSDe["decimals"])
    if (!amountOut) {
        return null
    }
    
    var profit = amountOut - entryPriceForDAI * amountIn  
    return {"profit": profit, "amountOut": amountOut, "sDAI_sUSDe_price": amountOut / amountIn}
}

Kondisi pemicu dirancang sebagai berikut:

    if (profit > 1000 && sDAI_Balance > amountIn) {
        Log("达到兑换条件,开始执行 sDAI -> sUSDe 兑换交易...")
        /* 兑换操作代码仅为教学演示,该功能请慎用,请先测试 
        executeTrade(poolAddress, sDAI, sUSDe, amountIn, 0.999*amountOut)
        */
    }

Operasi pertukaran hanya dapat dilakukan ketika jumlah pertukaran saat ini memenuhi keuntungan lebih besar dari 1000 dan ada cukup token sDAI di dompet.

Operasi Pertukaran

function executeTrade(poolAddress, tokenIn, tokenOut, amountIn, minOutAmount) {
    // exchange : i , j , _dx , _min_dy
    var swapTx = exchange.IO("api", poolAddress, "exchange", tokenIn["idx"], tokenOut["idx"], toInnerAmount(amountIn, tokenIn["decimals"]), toInnerAmount(minOutAmount, tokenOut["decimals"]))
    if (swapTx) {
        while (true) {
            Sleep(1000 * 3)
            let info = e.IO("api", "eth", "eth_getTransactionReceipt", swapTx)
            if (info && info.gasUsed) {
                return true
            }
            Log('Transaction not yet mined', swapTx)
        }
    }

    return false 
}

Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

Kode strategi lengkap

function toAmount(s, decimals) {
    return Number((BigDecimal(BigInt(s)) / BigDecimal(Math.pow(10, decimals))).toString())
}

function toInnerAmount(n, decimals) {
    return (BigDecimal(n) * BigDecimal(Math.pow(10, decimals))).toFixed(0)
}

function getBuyProfit(poolAddress, sDAI, sUSDe, amountIn, entryPriceForDAI) {    
    var data = exchange.IO("api", poolAddress, "get_dy", sDAI["idx"], sUSDe["idx"], toInnerAmount(amountIn, sDAI["decimals"]))
    var amountOut = toAmount(data, sUSDe["decimals"])
    if (!amountOut) {
        return null
    }
    
    var profit = amountOut - entryPriceForDAI * amountIn  
    return {"profit": profit, "amountOut": amountOut, "sDAI_sUSDe_price": amountOut / amountIn}
}

function executeTrade(poolAddress, tokenIn, tokenOut, amountIn, minOutAmount) {
    // exchange : i , j , _dx , _min_dy
    var swapTx = exchange.IO("api", poolAddress, "exchange", tokenIn["idx"], tokenOut["idx"], toInnerAmount(amountIn, tokenIn["decimals"]), toInnerAmount(minOutAmount, tokenOut["decimals"]))
    if (swapTx) {
        while (true) {
            Sleep(1000 * 3)
            let info = e.IO("api", "eth", "eth_getTransactionReceipt", swapTx)
            if (info && info.gasUsed) {
                return true
            }
            Log('Transaction not yet mined', swapTx)
        }
    }

    return false 
}

// 加密/解密方式
function cryptoKey(type, data, passWord, count) {
    // 加载加密库
    eval(HttpQuery("https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js"))

    // sha256 密码
    var hash = Encode("sha256", "string", "hex", passWord).substring(0, 32)

    if (type == "encrypt") {
        var key = aesjs.utils.utf8.toBytes(hash.substring(0, 32))
        var counter = new aesjs.Counter(count)
        var textBytes = aesjs.utils.utf8.toBytes(data)
        var aesCtr = new aesjs.ModeOfOperation.ctr(key, counter)
        var encryptedBytes = aesCtr.encrypt(textBytes)
        var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes)
        return encryptedHex
    } else if (type == "decrypt") {
        var key = aesjs.utils.utf8.toBytes(hash.substring(0, 32))
        var counter = new aesjs.Counter(count)
        var aesCtrDecrypt = new aesjs.ModeOfOperation.ctr(key, counter)
        const decryptedBytes = aesCtrDecrypt.decrypt(aesjs.utils.hex.toBytes(data))
        const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes)        
        return decryptedText
    } else {
        throw "function cryptoKey(), param 'type' not support"
    }
}

// 加密/解密选项
const descCryptoType = ["encrypt", "decrypt"]

function main() {
    LogReset(1)

    var count = _G("count")
    if (!count) {
        count = new Date().getTime()
        _G("count", count)
    }

    var walletAddress = null
    if (cryptoType == 1) {
        exchange.IO("key", cryptoKey(descCryptoType[cryptoType], keyData, password, count))
        walletAddress = exchange.IO("address")
        Log("载入秘钥,钱包地址:", walletAddress)
    } else {
        Log("加密后的秘钥:", cryptoKey(descCryptoType[cryptoType], keyData, password, count))
        return 
    }
    
    Log("钱包ETH余额:", toAmount(exchange.IO("api", "eth", "eth_getBalance", walletAddress, "latest"), 18))
    
    // sDAI/sUSDe 池合约地址和ABI
    var poolAddress = "0x167478921b907422F8E88B43C4Af2B8BEa278d3A"
    // 合约ABI
    var poolABI = `[{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchangeUnderlying","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_id","type":"int128","indexed":false},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityImbalance","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[]","indexed":false},{"name":"fees","type":"uint256[]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampA","inputs":[{"name":"old_A","type":"uint256","indexed":false},{"name":"new_A","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"A","type":"uint256","indexed":false},{"name":"t","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ApplyNewFee","inputs":[{"name":"fee","type":"uint256","indexed":false},{"name":"offpeg_fee_multiplier","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SetNewMATime","inputs":[{"name":"ma_exp_time","type":"uint256","indexed":false},{"name":"D_ma_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_A","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_offpeg_fee_multiplier","type":"uint256"},{"name":"_ma_exp_time","type":"uint256"},{"name":"_coins","type":"address[]"},{"name":"_rate_multipliers","type":"uint256[]"},{"name":"_asset_types","type":"uint8[]"},{"name":"_method_ids","type":"bytes4[]"},{"name":"_oracles","type":"address[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"exchange_received","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_min_mint_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_max_burn_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_max_burn_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[]"},{"name":"_receiver","type":"address"},{"name":"_claim_admin_fees","type":"bool"}],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"nonpayable","type":"function","name":"withdraw_admin_fees","inputs":[],"outputs":[]},{"stateMutability":"view","type":"function","name":"last_price","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ema_price","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_p","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"price_oracle","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D_oracle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_deadline","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}]},{"stateMutability":"view","type":"function","name":"get_dx","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"_amounts","type":"uint256[]"},{"name":"_is_deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"A_precise","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_balances","inputs":[],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"view","type":"function","name":"stored_rates","inputs":[],"outputs":[{"name":"","type":"uint256[]"}]},{"stateMutability":"view","type":"function","name":"dynamic_fee","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"ramp_A","inputs":[{"name":"_future_A","type":"uint256"},{"name":"_future_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_new_fee","inputs":[{"name":"_new_fee","type":"uint256"},{"name":"_new_offpeg_fee_multiplier","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_ma_exp_time","inputs":[{"name":"_ma_exp_time","type":"uint256"},{"name":"_D_ma_time","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"N_COINS","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"offpeg_fee_multiplier","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initial_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"admin_balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_exp_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"D_ma_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"ma_last_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}]},{"stateMutability":"view","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"salt","inputs":[],"outputs":[{"name":"","type":"bytes32"}]}]`

    // 注册ABI
    exchange.IO("abi", poolAddress, poolABI)

    // 获取池子中的代币地址、名称
    var token0Address = exchange.IO("api", poolAddress, "coins", 0)
    var token1Address = exchange.IO("api", poolAddress, "coins", 1)    
    var token0Name = exchange.IO("api", token0Address, "symbol")
    var token1Name = exchange.IO("api", token1Address, "symbol")
    var token0Decimals = exchange.IO("api", token0Address, "decimals")
    var token1Decimals = exchange.IO("api", token1Address, "decimals")

    var sDAI = {}
    var sUSDe = {}
    if (token0Name == "sDAI") {
        sDAI["name"] = token0Name
        sDAI["address"] = token0Address
        sDAI["idx"] = 0
        sDAI["decimals"] = token0Decimals ? token0Decimals : 18
    } else {
        throw "pool error"
    }
    if (token1Name == "sUSDe") {
        sUSDe["name"] = token1Name
        sUSDe["address"] = token1Address
        sUSDe["idx"] = 1
        sUSDe["decimals"] = token1Decimals ? token1Decimals : 18
    } else {
        throw "pool error"
    }

    // 假设 DAI 入手成本价: 0.9929
    var entryPriceForDAI = 0.9929
    
    while (true) {
        var sDAI_Balance = exchange.IO("api", sDAI["address"], "balanceOf", walletAddress)
        var sUSDe_Balance = exchange.IO("api", sUSDe["address"], "balanceOf", walletAddress)
        if (!sDAI_Balance || !sUSDe_Balance) {
            Sleep(1000 * 5)
            continue 
        }
        
        var tbl = {"type": "table", "title": "data", "cols": ["tokenName", "address", "idx", "decimals", "balance"], "rows": []}
        tbl["rows"].push([sDAI["name"], sDAI["address"], sDAI["idx"], sDAI["decimals"], toAmount(sDAI_Balance, sDAI["decimals"])])
        tbl["rows"].push([sUSDe["name"], sUSDe["address"], sUSDe["idx"], sUSDe["decimals"], toAmount(sUSDe_Balance, sUSDe["decimals"])])

        // 监控兑换价格,根据 DAI 入手成本价,计算实时利润等信息
        var amountIn = 100000
        var ret = getBuyProfit(poolAddress, sDAI, sUSDe, amountIn, entryPriceForDAI)
        if (!ret) {
            Sleep(1000 * 5)
            continue 
        }

        var profit = ret["profit"] 
        var amountOut = ret["amountOut"]
        var sDAI_sUSDe_price = ret["sDAI_sUSDe_price"]
        if (profit > 1000 && sDAI_Balance > amountIn) {
            Log("达到兑换条件,开始执行 sDAI -> sUSDe 兑换交易...")

            /* 兑换操作代码仅为教学演示,该功能请慎用,请先测试 
            executeTrade(poolAddress, sDAI, sUSDe, amountIn, 0.999*amountOut)
            */
        }

        LogStatus(_D(), ", amountIn:", amountIn, ", amountOut:", amountOut, ", sDAI_sUSDe_price:", sDAI_sUSDe_price, ", profit:", profit, "\n`" + JSON.stringify(tbl) + "`")
        Sleep(1000 * 60)
    }
}

END

Menurut ide desain strategi awalnya, tujuannya adalah untuk menangkap kondisi pasar ekstrem seperti yang ditunjukkan pada gambar.

Platform FMZ Web3 Ethereum Practice - Pertukaran Pesanan Berdasarkan Kontrak Cerdas

Terima kasih atas bacaan dan dukungan Anda.