我一直在教自己如何在Vyper中编写智能合同,我正在尝试一个玩具问题,合同将USDC转移到自己,然后在复合金融上创建一些复合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返回到我的帐户。这仅仅是因为我一直在逐步删除代码,试图找出它的失败之处。
我遗漏了什么?任何帮助都将不胜感激!我敢肯定这是很直接的!
发布于 2019-08-25 15:13:03
这是失败的,因为你还没有进入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);注:这是坚实的。在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支持可变长度(但通过数组米伦:马斯伦修复)数组之前,使用solidity来执行此任务,这将使这一工作变得更加容易。
发布于 2019-08-24 14:54:18
因此,目前vyper是过度保护,不允许修改从assert语句调用。有关介绍该功能的VIP,请参见https://github.com/ethereum/vyper/issues/1468和https://github.com/ethereum/vyper/issues/1150。
assert self.usdc_token.transferFrom(msg.sender, self, quantity)这将通过引入assert_modifiable()内置函数来修复,这将允许assert modifiable。
res: uint256 = self.usdc_token.transferFrom(msg.sender, self, quantity)
assert res > 0是目前正确的做法。还请注意,Vyper总是断言CALL事务是成功的-这意味着如果您不关心来自mint()的数据,您可以只执行self.usdc_token.transferFrom(msg.sender, self, quantity)。
https://ethereum.stackexchange.com/questions/74041
复制相似问题