首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >事务总是失败- Vyper

事务总是失败- Vyper
EN

Ethereum用户
提问于 2019-08-19 13:02:38
回答 2查看 442关注 0票数 1

我一直在教自己如何在Vyper中编写智能合同,我正在尝试一个玩具问题,合同将USDC转移到自己,然后在复合金融上创建一些复合USDC。

我一直试图使用MEW来执行一个函数组合,它在它甚至要求我签署事务之前就失败了(因为气体不足或者反复出错)。我有点不明白为什么它会失败,我似乎找不到一个很好的工具来调试它。我已经批准了与USDC和cUSDC的合同。

这是我的代码:

代码语言:javascript
复制
from vyper.interfaces import ERC20

contract Compound():
    def redeem(quantity: uint256) -> uint256: modifying
    def mint(quantity: uint256) -> uint256: modifying
    def approve(target: address, quantity: uint256) -> bool: modifying
    def balanceOf(target: address) -> uint256: constant
    def transfer(to: address, tokens: uint256) -> bool: modifying

owner: address
cusdc_token: Compound
usdc_token: ERC20


@public
def __init__():
    self.owner = msg.sender
    self.cusdc_token = Compound(0x39AA39c021dfbaE8faC545936693aC917d5E7563)
    self.usdc_token = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)

    self.usdc_token.approve(self.cusdc_token, 99998075535048195174647562)

@public
def kill():
    if msg.sender == self.owner:
        selfdestruct(self.owner)

@public
def compound(quantity: uint256, trans_cost: uint256):
    assert self.usdc_token.transferFrom(msg.sender, self, quantity)
    assert self.cusdc_token.mint(quantity) == 0

您会注意到,没有代码可以将USDC或cUSDC返回到我的帐户。这仅仅是因为我一直在逐步删除代码,试图找出它的失败之处。

我遗漏了什么?任何帮助都将不胜感激!我敢肯定这是很直接的!

EN

回答 2

Ethereum用户

回答已采纳

发布于 2019-08-25 15:13:03

这是失败的,因为你还没有进入USDC市场的化合物。这在Vyper中很难做到,因为它将动态数组作为输入(在Vyper中不存在)。因此,您必须通过将代码编码和解码为字节来完成一些手动解决方案。

你必须先做:

代码语言:javascript
复制
Comptroller troll = Comptroller(0xABCD...);
CToken[] memory cTokens = new CToken[](2);
cTokens[0] = CErc20(0x3FDA...);
cTokens[1] = CEther(0x3FDB...);
uint[] memory errors = troll.enterMarkets(cTokens);

注:这是坚实的。在Vyper,你应该做如下的事情:

代码语言:javascript
复制
@private
def uintResponse(byteArr: bytes[96]) -> uint256:
  """
  @notice Converts 96 byte array to a uint256
  @dev This assumes it is the output from a raw_call that takes form of offset + length + response
  @return uint256
  """
  # assumes output is coming from an uint[], therefore start at byte 64
  # because first two sets of 32 are offset & length
  start: int128 = 32*2

  # extract32 bytes of data
  extracted: bytes32 = extract32(byteArr, start, type=bytes32)

  # return converted 32 bytes to uint256
  return convert(extracted, uint256)

@private
def enterMarket(cToken: address, _troller: address):
  """
  @notice Enters specific compound market
  @dev This utilizes `raw_call` for specified cToken address as input as bytes
  @param cToken: compound cToken address
  """
    # get funcSig for compound comptroller function "enterMarkets" which
    # takes a dynamic address array
    funcSig: bytes[4] = method_id("enterMarkets(address[])", bytes[4])

    # There are 7 currently active markets on Compound
    # Convert specified cToken address to bytes32
    addrBytes: bytes[96] = concat(
                                convert(32, bytes32),
                                convert(1, bytes32),
                                convert(cToken, bytes32)
                              )

    # call should be in the form of:
    # funcSig + offset + lengthOfInputArray + inputArray
    full_data: bytes[100] = concat(funcSig, addrBytes)

    # returns byteArray of offset (32 bytes) + length (32 bytes) + addressArray (32 * 1)
    response: bytes[96] = raw_call(
                                _troller,           # Compound Comptroller address
                                full_data,          # funcSig + offset + lengthOfInputArray + inputArray
                                outsize=96,         # outsize = offset (32 bytes) + length (32 bytes) + addressArray (32 * 1)
                                gas=msg.gas,        # Pass msg.gas for call
                                value=0,            # Make sure to not send ETH
                                delegate_call=False # Not delegate_call
                              )

    # Error Code is returned which are uint256 so call uintResponse
    convertedResponse: uint256 = self.uintResponse(response)

    # Check no error
    assert convertedResponse == 0

这都是因为"enterMarkets“函数接受一个动态地址数组,目前Vyper中不支持这些地址数组。如果你想进入所有的市场,我已经包含了下面的代码。

代码语言:javascript
复制
@private
def uint7ArrayResponse(byteArr: bytes[288]) -> uint256[7]:
  """
  @notice Converts 288 byte array to a uint256[7]
  @dev This assumes it is the output from a raw_call that takes form of offset + length + response
  @return uint256[7]
  """
  # initiate a uint256 array of length 7
  returnArr: uint256[7] = [0,0,0,0,0,0,0]

  # range to 9 to account for offset & length in byte input
  for i in range(9):
    # skip offset
    if i == 0:
      pass
    # skip length
    elif i == 1:
      pass
    # handle each uint256
    else:
      # multiply i * 32 to get starting byte to start extract32 from
      start: int128 = i*32

      # extract32 to bytes
      extracted: bytes32 = extract32(byteArr, start, type=bytes32)

      # set uint256 array index to extracted uint256, accounting for ranging over
      # 9 instead of 7 (thus -2)
      returnArr[i-2] = convert(extracted, uint256)
  return returnArr

@public
def enterAllMarkets(_cTokens: address[7], _troller: address):
  """
  @notice Enters all known compound markets
  @dev This utilizes `raw_call` with each cToken address as inputs as bytes
  """
  # get funcSig for compound comptroller function "enterMarkets" which
  # takes a dynamic address array
  funcSig: bytes[4] = method_id("enterMarkets(address[])", bytes[4])

  # There are 7 currently active markets on Compound
  # Convert each cToken address to bytes32 and concatenate them
  addrBytes: bytes[288] = concat(
                                convert(32, bytes32), #offset
                                convert(7, bytes32), #len
                                convert(_cTokens[0], bytes32),
                                convert(_cTokens[1], bytes32),
                                convert(_cTokens[2], bytes32),
                                convert(_cTokens[3], bytes32),
                                convert(_cTokens[4], bytes32),
                                convert(_cTokens[5], bytes32),
                                convert(_cTokens[6], bytes32)
                                )
  # call should be in the form of:
  # funcSig + offset + lengthOfInputArray + inputArray
  full_data: bytes[292] = concat(funcSig, addrBytes)

  # returns byteArray of offset (32 bytes) + length (32 bytes) + addressArray (32 * 7)
  response: bytes[288]= raw_call(
                              _troller,           # Compound Comptroller address
                              full_data,          # funcSig + offset + lengthOfInputArray + inputArray
                              outsize=288,        # outsize = offset (32 bytes) + length (32 bytes) + addressArray (32 * 7)
                              gas=msg.gas,        # Pass msg.gas for call
                              value=0,            # Make sure to not send ETH
                              delegate_call=False # Not delegate_call
                           )


  # Error Codes are returned which are uint256 so call uint7ArrayResponse
  responseArr: uint256[7] = self.uint7ArrayResponse(response)

  # No_Error response is equal to 0, so create an array that is equivalent to no errors
  successArr: uint256[7] = [0,0,0,0,0,0,0]

  # Iterate through and check that there were no errors for each market
  for i in range(7):
    assert responseArr[i] == successArr[i]

一旦你进入市场,你就应该能够铸币。别忘了保持你的批准金额的最新。总之,强烈建议在Vyper支持可变长度(但通过数组米伦:马斯伦修复)数组之前,使用solidity来执行此任务,这将使这一工作变得更加容易。

票数 1
EN

Ethereum用户

发布于 2019-08-24 14:54:18

因此,目前vyper是过度保护,不允许修改从assert语句调用。有关介绍该功能的VIP,请参见https://github.com/ethereum/vyper/issues/1468https://github.com/ethereum/vyper/issues/1150

代码语言:javascript
复制
assert self.usdc_token.transferFrom(msg.sender, self, quantity)

这将通过引入assert_modifiable()内置函数来修复,这将允许assert modifiable

代码语言:javascript
复制
res: uint256 = self.usdc_token.transferFrom(msg.sender, self, quantity)
assert res > 0

是目前正确的做法。还请注意,Vyper总是断言CALL事务是成功的-这意味着如果您不关心来自mint()的数据,您可以只执行self.usdc_token.transferFrom(msg.sender, self, quantity)

票数 2
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/74041

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档