Merge pull request #1455 from 0xProject/fix/contracts/MAPupdates
Add validation to MAP
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
{
|
||||
"note": "Added LibAddressArray",
|
||||
"pr": 1383
|
||||
},
|
||||
{
|
||||
"note": "Add validation and comments to MultiAssetProxy",
|
||||
"pr": 1455
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@@ -33,6 +33,9 @@ contract MultiAssetProxy is
|
||||
function ()
|
||||
external
|
||||
{
|
||||
// NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification.
|
||||
// It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively expensive
|
||||
// and we therefore do not check for overflows in these scenarios.
|
||||
assembly {
|
||||
// The first 4 bytes of calldata holds the function selector
|
||||
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
|
||||
@@ -145,7 +148,7 @@ contract MultiAssetProxy is
|
||||
let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
|
||||
|
||||
// Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData`
|
||||
if iszero(eq(amountsLen, nestedAssetDataLen)) {
|
||||
if sub(amountsLen, nestedAssetDataLen) {
|
||||
// Revert with `Error("LENGTH_MISMATCH")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
@@ -181,8 +184,11 @@ contract MultiAssetProxy is
|
||||
let amountsElement := calldataload(add(amountsContentsStart, i))
|
||||
let totalAmount := mul(amountsElement, amount)
|
||||
|
||||
// Revert if multiplication resulted in an overflow
|
||||
if iszero(eq(div(totalAmount, amount), amountsElement)) {
|
||||
// Revert if `amount` != 0 and multiplication resulted in an overflow
|
||||
if iszero(or(
|
||||
iszero(amount),
|
||||
eq(div(totalAmount, amount), amountsElement)
|
||||
)) {
|
||||
// Revert with `Error("UINT256_OVERFLOW")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
@@ -230,7 +236,7 @@ contract MultiAssetProxy is
|
||||
|
||||
// Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId`
|
||||
// We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0
|
||||
if iszero(eq(currentAssetProxyId, assetProxyId)) {
|
||||
if sub(currentAssetProxyId, assetProxyId) {
|
||||
// Update `assetProxyId`
|
||||
assetProxyId := currentAssetProxyId
|
||||
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
|
||||
|
@@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
artifacts as tokensArtifacts,
|
||||
DummyERC20TokenContract,
|
||||
DummyERC20TokenTransferEventArgs,
|
||||
DummyERC721ReceiverContract,
|
||||
DummyERC721TokenContract,
|
||||
DummyMultipleReturnERC20TokenContract,
|
||||
@@ -22,6 +23,7 @@ import { assetDataUtils } from '@0x/order-utils';
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
|
||||
@@ -738,6 +740,36 @@ describe('Asset Transfer Proxies', () => {
|
||||
erc20Balances[toAddress][erc20TokenA.address].add(totalAmount),
|
||||
);
|
||||
});
|
||||
it('should dispatch an ERC20 transfer when input amount is 0', async () => {
|
||||
const inputAmount = constants.ZERO_AMOUNT;
|
||||
const erc20Amount = new BigNumber(10);
|
||||
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
|
||||
const amounts = [erc20Amount];
|
||||
const nestedAssetData = [erc20AssetData];
|
||||
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
|
||||
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
|
||||
assetData,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
inputAmount,
|
||||
);
|
||||
const erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts });
|
||||
const tx = await logDecoder.getTxWithDecodedLogsAsync(
|
||||
await web3Wrapper.sendTransactionAsync({
|
||||
to: multiAssetProxy.address,
|
||||
data,
|
||||
from: authorized,
|
||||
}),
|
||||
);
|
||||
expect(tx.logs.length).to.be.equal(1);
|
||||
const log = tx.logs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>;
|
||||
const transferEventName = 'Transfer';
|
||||
expect(log.event).to.equal(transferEventName);
|
||||
expect(log.args._value).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
expect(newBalances).to.deep.equal(erc20Balances);
|
||||
});
|
||||
it('should successfully transfer multiple of the same ERC20 token', async () => {
|
||||
const inputAmount = new BigNumber(1);
|
||||
const erc20Amount1 = new BigNumber(10);
|
||||
|
Reference in New Issue
Block a user