This is failing because you haven't entered the USDC market on Compound. This is difficult to do in Vyper because it takes a dynamic array as an input (which do not exist in Vyper). Therefore, you have to do some manual workarounds, by encoding and decoding into bytes.
You have to first do:
Comptroller troll = Comptroller(0xABCD...);
CToken[] memory cTokens = new CToken[](2);
cTokens[0] = CErc20(0x3FDA...);
cTokens[1] = CEther(0x3FDB...);
uint[] memory errors = troll.enterMarkets(cTokens);
Note: this is in Solidity. In Vyper, you should do something like:
@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
This is all because "enterMarkets" function takes a dynamic array of addresses, which dynamic arrays are not currently supported in Vyper. If you want to enter all markets I have included the code around that below.
@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]
Once you have entered the market, then you should be able to mint. Dont forget to keep your approval amounts up to date as well. All in all, highly recommend using solidity for this task until Vyper supports variable length (but fixed via array[minlen:maxlen]) arrays, which should make this a lot easier.
compound
. Either could fail. Which one is failing? – user19510 Aug 21 '19 at 4:26mint
), what value is it returning? That should tell you what the problem is. – user19510 Aug 21 '19 at 4:30assert
and just store the result somewhere. Then read it and see what it is. – user19510 Aug 22 '19 at 0:20