aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'contracts/protocol')
-rw-r--r--contracts/protocol/CHANGELOG.json13
-rw-r--r--contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol14
-rw-r--r--contracts/protocol/package.json5
-rw-r--r--contracts/protocol/test/asset_proxy/proxies.ts32
4 files changed, 58 insertions, 6 deletions
diff --git a/contracts/protocol/CHANGELOG.json b/contracts/protocol/CHANGELOG.json
index 5c3798a69..be374d892 100644
--- a/contracts/protocol/CHANGELOG.json
+++ b/contracts/protocol/CHANGELOG.json
@@ -1,5 +1,18 @@
[
{
+ "version": "2.2.0",
+ "changes": [
+ {
+ "note": "Added LibAddressArray",
+ "pr": 1383
+ },
+ {
+ "note": "Add validation and comments to MultiAssetProxy",
+ "pr": 1455
+ }
+ ]
+ },
+ {
"timestamp": 1544741676,
"version": "2.1.59",
"changes": [
diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol
index 42231e73b..377325384 100644
--- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol
+++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol
@@ -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),
diff --git a/contracts/protocol/package.json b/contracts/protocol/package.json
index 838189371..d37a0302a 100644
--- a/contracts/protocol/package.json
+++ b/contracts/protocol/package.json
@@ -19,7 +19,8 @@
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
- "compile": "sol-compiler --contracts-dir contracts",
+ "compile": "sol-compiler",
+ "watch": "sol-compiler -w",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
@@ -44,7 +45,6 @@
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md",
"devDependencies": {
"@0x/abi-gen": "^1.0.19",
- "@0x/contracts-test-utils": "^1.0.2",
"@0x/dev-utils": "^1.0.21",
"@0x/sol-compiler": "^1.1.16",
"@0x/sol-cov": "^2.1.16",
@@ -75,6 +75,7 @@
"@0x/contracts-interfaces": "^1.0.2",
"@0x/contracts-libs": "^1.0.2",
"@0x/contracts-multisig": "^1.0.2",
+ "@0x/contracts-test-utils": "^1.0.2",
"@0x/contracts-tokens": "^1.0.2",
"@0x/contracts-utils": "^1.0.2",
"@0x/order-utils": "^3.0.7",
diff --git a/contracts/protocol/test/asset_proxy/proxies.ts b/contracts/protocol/test/asset_proxy/proxies.ts
index c4bd95905..89c8b390c 100644
--- a/contracts/protocol/test/asset_proxy/proxies.ts
+++ b/contracts/protocol/test/asset_proxy/proxies.ts
@@ -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);