Vyper

交易總是失敗 - Vyper

  • August 25, 2019

我一直在嘗試自學如何在 Vyper 中編寫智能合約,我正在嘗試一個玩具問題,即合約將 USDC 轉移給自己,然後在 Compound Finance 上鑄造一些 Compound USDC。

我一直在嘗試使用 MEW 執行功能複合,但在它甚至要求我簽署交易之前它就失敗了(氣體不足或反复失敗的錯誤)。我對它失敗的原因有點迷茫,我似乎找不到一個好的工具來調試它。我已經批准了與 USDC 和 cUSDC 的契約。

這是我的程式碼:

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 返回到我的帳戶。那隻是因為我一直在逐步刪除程式碼以嘗試找出失敗的地方。

我錯過了什麼?任何幫助將非常感激!我敢肯定這是相當直接的!

這是失敗的,因為你還沒有在 Compound 上進入 USDC 市場。這在 Vyper 中很難做到,因為它需要一個動態數組作為輸入(在 Vyper 中不存在)。因此,您必須通過編碼和解碼成字節來做一些手動解決方法。

你必須首先做:

Comptroller troll = Comptroller(0xABCD...);
CToken[] memory cTokens = new CToken[](2);
cTokens[0] = CErc20(0x3FDA...);
cTokens[1] = CEther(0x3FDB...);
uint[] memory errors = troll.enterMarkets(cTokens);

注意:這是在 Solidity 中。在 Vyper 中,您應該執行以下操作:

@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 目前不支持動態數組。如果你想進入所有市場,我在下麵包含了程式碼。

@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 支持可變長度(但通過數組固定

$$ minlen:maxlen $$) 數組,這應該使這更容易。

引用自:https://ethereum.stackexchange.com/questions/74041