Vyper
交易總是失敗 - Vyper
我一直在嘗試自學如何在 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 $$) 數組,這應該使這更容易。