When I try to fill an order with funds from a contract I get the following generic error:
Error: Transaction: 0x1d3161cd7f318c3b0b28fc34b2c6bca0cbf4efdb3b11fcf7ec3af7c9adfd821b exited with an error (status 0). Reason given: TRANSFER_FAILED. Please check that the transaction: - satisfies all conditions set by Solidity
require
statements. - does not trigger a Solidityrevert
statement.
I've got the following contract:
pragma solidity >=0.4.24;
Exchange exchange;
EthForERC20Token crowdsale;
WrapERC20Token wrapERC20Token;
constructor(
address _exchangeAddress
)
public
{
// The local 0x exchange intstance
exchange = Exchange(_exchangeAddress);
// Grand unlimited allownace to the exchange contract.
wrapERC20Token = new WrapERC20Token(address(exchange));
}
function fillOrder (
Exchange.Order memory _order,
uint256 _assetFillAmount,
bytes memory _signature
)
public
payable
{
exchange.fillOrder(_order,_assetFillAmount, _signature);
}
function getWrappedERC20Address()
public
view
returns(address) {
return address(wrapERC20Token);
}
I tested and the contract owns enough of the WrappedERC20Token which is just an ERC20 token with the option to pre-approve an address with unlimited allowance.
My 0x order creation is as follows:
const wrappedShareAddress = await contract.getWrappedShareAddress();
const maker = accounts[0];
const makerAssetData = zeroX.assetDataUtils.encodeERC20AssetData(etherToken);
const takerAssetData = zeroX.assetDataUtils.encodeERC20AssetData(wrappedShareAddress);
const contractWrappers = new zeroX.ContractWrappers(providerEngine, { networkId: 50 });
const order = {
exchangeAddress: exchange,
makerAddress: maker.toLowerCase(),
takerAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
feeRecipientAddress: NULL_ADDRESS,
expirationTimeSeconds: (1581437362).toString(),
salt: zeroX.generatePseudoRandomSalt().toString(),
makerAssetAmount: new zeroX.BigNumber("5e17").toFixed(),
takerAssetAmount: new zeroX.BigNumber("5e17").toFixed(),
makerAssetData,
takerAssetData,
makerFee: new zeroX.BigNumber("0").toFixed(),
takerFee: new zeroX.BigNumber("0").toFixed(),
};
// console.log(order);
// Generate the order hash and sign it
const orderHashHex = zeroX.orderHashUtils.getOrderHashHex(order);
signature = await zeroX.signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker);
signedOrder = {...order, signature};
const makerWETHApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
etherToken,
maker,
);
await toTxhash.mineTx(makerWETHApprovalTxHash);
const makerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(
etherToken,
new zeroX.BigNumber("2e18"),
maker,
);
await toTxhash.mineTx(makerWETHDepositTxHash);
Then I call the smart contract function using:
const res = await contract.fillOrder(
signedOrder,
new zeroX.BigNumber("5e17").toFixed(),
signature,
{from: maker, value: new zeroX.BigNumber("1e18").toFixed()}
);
10 ** 18
in Javascript, since it is larger (much larger) than the maximum safe integer. Use"2e18"
instead of2 * 10 ** 18
, or"5e17"
instead of0.5 * 10 ** 18
, or"1e18"
instead of10 ** 18
, etc. If you need to utilize values that are "more complicated", thenrequire("bignumber.js")
and useBigNumber
objects wherever needed. You can convert such object to a full-precision string via thetoFixed()
function..toString()
is redundant in the expression("5e17").toString()
, yes?new
for the initialization ofwrapERC20Token
but not for the initialization ofexchange
? Since you have norequire
orrevert
statements in your code, this is a good candidate for your transaction failure (later in your code, when you callexchange.fillOrder
, for example).new zeroX.BigNumber("5e17").toFixed()
is"500000000000000000"
. Although it is different from the string"5e17"
, when converted back to aBigNumber
instance, they will yield the same object. And when you callfillOrder
, this string is eventually converted to such an object. So you may as well use the simplest notation possible, which is"5e17"
. I meant for you to usetoFixed()
when handling a value returned from some contract function, sincetoString()
may convert it to scientific notation, and then you might end up "losing some digits".