You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Remix编译Vyper闪贷套利合约遇split相关未定义属性错误求助

Vyper闪贷套利合约编译报错:Cannot read properties of undefined (reading 'split')

我正在编写一个Vyper智能合约,实现从Aave获取闪贷,通过Uniswap完成兑换后再用Curve兑换,最终归还Aave的流程,以此练习Web3开发中的路由器、闪贷等概念。但在Remix中编译时出现以下错误:

Cannot read properties of undefined (reading 'split')

我的代码里并没有使用.split()方法,怀疑和路由器相关,但已经对照文档确认参数传递无误。错误没有提供行号,完全不知道从哪里开始调试,求帮忙解决。

合约代码

#pragma version ^0.3.3
"""
@title Crypto Arbitrage Smart Contract using Uniswap and Curve with getting a flash loan from AAVE
@license UNLICENSED
@author Salmon46
@notice Vyper implementation of Crypto Arbitrage smart contract
"""

from vyper.interfaces import ERC20

# set constants to desired level
MAX_PARAMS_LEN: constant(uint256) = 32
MAX_ASSETS_LEN: constant(uint256) = 32

#initialize resource variables
POOL: constant(address) = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2 #aave pool
UNISWAP_V3_ROUTER: constant(address) =  0xE592427A0AEce92De3Edee1F18E0157C05861564 #Uniswap v3 Router
from "https://github.com/curvefi/curve-router-ng/blob/master/contracts/Router.vy" import CryptoPoolETH #Curve Router

# token addresses
USDC: constant(address) = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
WETH9: constant(address) = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

#initialize owner
owner: immutable(address)

@external
def __init__(_owner: address):
    owner = _owner

@external
def flash_call(
    _amount: uint256, #Prolly like 1000
):
    """
    @notice Make flash loan call to pool
    @dev Uses raw_call, since uint16 is not supported in vyper
    @param _asset Address of asset to borrow
    @param _amount Amount of asset to borrow (raw)
    @param _params Arbitrary params to pass to the receiver
    @param _referral_code Optional Aave referral code
    """
    _receiver_address: constant(address) = self
    _asset: constant(address) = USDC
    _params: Bytes[MAX_PARAMS_LEN] = ""
    #_referral_code: Bytes[2] =  0
    
    raw_call(
        POOL,
        _abi_encode(
            _receiver_address,
            _asset,
            _amount,
            _params,
            #_referral_code,
            method_id=method_id("flashLoanSimple(address,address,uint256,bytes,uint16)")
        )
    )

@public
def swapWithUniswap(amountIn: uint256) -> uint256:
    #Approve Uniswap router to spend USDC
    ERC20(USDC).approve(UNISWAP_V3_ROUTER, amountIn)

    #initialize params
    uniswapPoolFee: constant(uint256) = 3000
    amountOutMinimum: uint256 = 0 #REMEMBER TO CHANGE
    sqrtPriceLimitX96: uint256 = 0
    receiver_address: constant(address) = self

    amountOut: uint256 = raw_call(
        UNISWAP_V3_ROUTER,
        _abi_encode(
            USDC,
            WETH9,
            uniswapPoolFee,
            receiver_address,
            block.timestamp,
            amountIn,
            amountOutMinimum, #This is amountOutMinimum...REMEBER TO CHANGE
            sqrtPriceLimitX96,
            method_id=method_id("exactInputSingle((address, address, uint24, address, uint256, uint256, uint256, uint160))")
        ),
    )

    return amountOut

@public
def swapWithCurve(amount_In: uint256) -> uint256:
    #Approve Curve Router to spend Wrapped ether
    ERC20(WETH9).approve(CryptoPoolETH, amount_In)

    #initalize paramss
    _route: address[11] = [
            '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',   # initial token: wETH
            '0x7f86bf177dd4f3494b841a37e810a34dd56c829b',   # pool1: TricryptoUSDC (USDC, wBTC, wETH)
            '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',   # output token: USDC
            '0x0000000000000000000000000000000000000000', 
            '0x0000000000000000000000000000000000000000', 
            '0x0000000000000000000000000000000000000000',
            '0x0000000000000000000000000000000000000000',
            '0x0000000000000000000000000000000000000000',
            '0x0000000000000000000000000000000000000000',
            '0x0000000000000000000000000000000000000000',
            '0x0000000000000000000000000000000000000000',
        ]
    _swap_params: uint256[5][5] = [
        [2, 0, 1, 3, 3], #1st element is token index of WETH in the TricryptoUSDC pool. 2nd element is the token index of USDC in the same pool. 3rd element is the type of swap --just a standard token exchange--. 4th element is the type of pool --using a TriCrypto pool. 5th elemnt is how many coins are in the pool --3 coins.
        [0, 0, 0, 0, 0], #Only doing 1 swap so that's why these are all empty
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0]
    ]
    _expected: uint256 = 0 #Same thing as amountOutMinimum...ALSO REMEMBER TO CHANGE
    _pools: address[5] = [
        '0x7f86bf177dd4f3494b841a37e810a34dd56c829b', #TricryptoUSDC pool address
        '0x0000000000000000000000000000000000000000',
        '0x0000000000000000000000000000000000000000',
        '0x0000000000000000000000000000000000000000',
        '0x0000000000000000000000000000000000000000'
    ]
    _receiver_address: constant(address) = self

    amount_Out: uint256 = CryptoPoolETH.exchange(
        _route,
        _swap_params,
        amount_In,
        _expected, 
        _pools, #Empty for pools since we are only using 1 pool aka only need this parameter for swap type 3
        _receiver_address
    )

    return amount_owed

@external
def executeOperation(
    _asset: address,
    _amount: uint256,
    _premium: uint256,
    _initiator: address, 
    _params: Bytes[MAX_PARAMS_LEN]
) -> bool: 
    """
    @notice Executes logic with borrowed asset and set approval
    @dev This method is called by the Aave Pool
    @param _asset Addresses of asset borrowed
    @param _amount Amount of asset borrowed (raw)
    @param _premium Premium owed for asset borrowed (raw)
    @param _initiator Address of the initiator of the flash loan
    @param _params Arbitrary params passed by the initiator
    @return True if execution success
    """
    # conduct logic
    postUniValue = swapWithUniswap(_amount)
    finalAmount = swapWithCurve(postUniValue)

    # approve transfer back to pool
    amount_owed: uint256 = _amount + _premium
    ERC20(_asset).approve(POOL, amount_owed)      

    return True

已尝试的调试策略

  • 参数检查:对照官方文档修正了所有接口的参数传递错误;
  • 代码格式化:按逻辑执行顺序重新调整了函数定义和整体代码结构。

但无论怎么操作,编译时都会出现同样的错误。


解决建议

  1. 替换远程导入为本地接口:Remix对Vyper的远程文件导入支持存在bug,直接把Curve Router的CryptoPoolETH接口内容复制到本地合约中,不要使用from "url" import的方式。
  2. 修复变量名错误:swapWithCurve函数最后return的amount_owed未定义,应改为amount_Out,这类语法错误可能触发编译器异常处理逻辑,导致出现无关的split错误。
  3. 检查ABI编码的method_id:Uniswap V3的exactInputSingle方法接收结构体参数,确保method_id中的结构体字段顺序、类型和Uniswap官方定义完全一致,比如fee参数的类型是uint24,method_id里要严格匹配,不要用uint256。
  4. 匹配Vyper版本:确认Remix中选择的Vyper编译器版本是0.3.x系列,与pragma声明的^0.3.3一致,避免版本不兼容导致的解析错误。
  5. 清理注释中的特殊符号:注释里的--等符号可能干扰编译器的注释解析,建议替换为普通单行注释或调整格式。

内容的提问来源于stack exchange,提问作者Sudin Gautam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:18:11