This commit is contained in:
fragosti 2019-03-15 16:48:41 -07:00
commit bb0ba21e92
23 changed files with 1639 additions and 33 deletions

View File

@ -9,6 +9,10 @@
{
"note": "Do not reexport external dependencies",
"pr": 1682
},
{
"note": "Add ERC1155Proxy",
"pr": 1661
}
]
},

View File

@ -23,6 +23,7 @@
}
},
"contracts": [
"src/ERC1155Proxy.sol",
"src/ERC20Proxy.sol",
"src/ERC721Proxy.sol",
"src/MixinAuthorizable.sol",

View File

@ -0,0 +1,267 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.4.24;
import "./MixinAuthorizable.sol";
contract ERC1155Proxy is
MixinAuthorizable
{
// Id of this proxy.
bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC1155Token(address,uint256[],uint256[],bytes)"));
function ()
external
{
// Input calldata to this function is encoded as follows:
// -- TABLE #1 --
// | Area | Offset (**) | Length | Contents |
// |----------|-------------|-------------|---------------------------------|
// | Header | 0 | 4 | function selector |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. offset to assetData (*) |
// | | 36 | | 2. from |
// | | 68 | | 3. to |
// | | 100 | | 4. amount |
// | Data | | | assetData: |
// | | 132 | 32 | assetData Length |
// | | 164 | (see below) | assetData Contents |
//
//
// Asset data is encoded as follows:
// -- TABLE #2 --
// | Area | Offset | Length | Contents |
// |----------|-------------|---------|-------------------------------------|
// | Header | 0 | 4 | assetProxyId |
// | Params | | 4 * 32 | function parameters: |
// | | 4 | | 1. address of ERC1155 contract |
// | | 36 | | 2. offset to ids (*) |
// | | 68 | | 3. offset to values (*) |
// | | 100 | | 4. offset to data (*) |
// | Data | | | ids: |
// | | 132 | 32 | 1. ids Length |
// | | 164 | a | 2. ids Contents |
// | | | | values: |
// | | 164 + a | 32 | 1. values Length |
// | | 196 + a | b | 2. values Contents |
// | | | | data |
// | | 196 + a + b | 32 | 1. data Length |
// | | 228 + a + b | c | 2. data Contents |
//
//
// Calldata for target ERC155 asset is encoded for safeBatchTransferFrom:
// -- TABLE #3 --
// | Area | Offset (**) | Length | Contents |
// |----------|-------------|---------|-------------------------------------|
// | Header | 0 | 4 | safeBatchTransferFrom selector |
// | Params | | 5 * 32 | function parameters: |
// | | 4 | | 1. from address |
// | | 36 | | 2. to address |
// | | 68 | | 3. offset to ids (*) |
// | | 100 | | 4. offset to values (*) |
// | | 132 | | 5. offset to data (*) |
// | Data | | | ids: |
// | | 164 | 32 | 1. ids Length |
// | | 196 | a | 2. ids Contents |
// | | | | values: |
// | | 196 + a | 32 | 1. values Length |
// | | 228 + a | b | 2. values Contents |
// | | | | data |
// | | 228 + a + b | 32 | 1. data Length |
// | | 260 + a + b | c | 2. data Contents |
//
//
// (*): offset is computed from start of function parameters, so offset
// by an additional 4 bytes in the calldata.
//
// (**): the `Offset` column is computed assuming no calldata compression;
// offsets in the Data Area are dynamic and should be evaluated in
// real-time.
//
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.
//
// Note: Table #1 and Table #2 exists in Calldata. We construct Table #3 in memory.
//
//
assembly {
// The first 4 bytes of calldata holds the function selector
let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
// `transferFrom` will be called with the following parameters:
// assetData Encoded byte array.
// from Address to transfer asset from.
// to Address to transfer asset to.
// amount Amount of asset to transfer.
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
// To lookup a value in a mapping, we load from the storage location keccak256(k, p),
// where k is the key left padded to 32 bytes and p is the storage slot
mstore(0, caller)
mstore(32, authorized_slot)
// Revert if authorized[msg.sender] == false
if iszero(sload(keccak256(0, 64))) {
// Revert with `Error("SENDER_NOT_AUTHORIZED")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
mstore(96, 0)
revert(0, 100)
}
// Construct Table #3 in memory, starting at memory offset 0.
// The algorithm below maps asset data from Table #1 and Table #2 to Table #3, while
// scaling the `values` (Table #2) by `amount` (Table #1). Once Table #3 has
// been constructed in memory, the destination erc1155 contract is called using this
// as its calldata. This process is divided into four steps, below.
////////// STEP 1/4 //////////
// Map relevant fields from assetData (Table #2) into memory (Table #3)
// The Contents column of Table #2 is the same as Table #3,
// beginning from parameter 3 - `offset to ids (*)`
// The offsets in these rows are offset by 32 bytes in Table #3.
// Strategy:
// 1. Copy the assetData into memory at offset 32
// 2. Increment by 32 the offsets to `ids`, `values`, and `data`
// Load offset to `assetData`
let assetDataOffset := calldataload(4)
// Load length in bytes of `assetData`, computed by:
// 4 (function selector)
// + assetDataOffset
let assetDataLength := calldataload(add(4, assetDataOffset))
// This corresponds to the beginning of the Data Area for Table #3.
// Computed by:
// 4 (function selector)
// + assetDataOffset
// + 32 (length of assetData)
calldatacopy(
32, // aligned such that "offset to ids" is at the correct location for Table #3
add(36, assetDataOffset), // beginning of asset data contents
assetDataLength // length of asset data
)
// Increment by 32 the offsets to `ids`, `values`, and `data`
mstore(68, add(mload(68), 32))
mstore(100, add(mload(100), 32))
mstore(132, add(mload(132), 32))
// Record the address of the destination erc1155 asset for later.
let assetAddress := and(
mload(36),
0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
)
////////// STEP 2/4 //////////
let amount := calldataload(100)
let valuesOffset := add(mload(100), 4) // add 4 for calldata offset
let valuesLengthInBytes := mul(
mload(valuesOffset),
32
)
let valuesBegin := add(valuesOffset, 32)
let valuesEnd := add(valuesBegin, valuesLengthInBytes)
for { let tokenValueOffset := valuesBegin }
lt(tokenValueOffset, valuesEnd)
{ tokenValueOffset := add(tokenValueOffset, 32) }
{
// Load token value and generate scaled value
let tokenValue := mload(tokenValueOffset)
let scaledTokenValue := mul(tokenValue, amount)
// Revert if `amount` != 0 and multiplication resulted in an overflow
if iszero(or(
iszero(amount),
eq(div(scaledTokenValue, amount), tokenValue)
)) {
// Revert with `Error("UINT256_OVERFLOW")`
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
mstore(96, 0)
revert(0, 100)
}
// There was no overflow, update `tokenValue` with its scaled counterpart
mstore(tokenValueOffset, scaledTokenValue)
}
////////// STEP 3/4 //////////
// Store the safeBatchTransferFrom function selector,
// and copy `from`/`to` fields from Table #1 to Table #3.
// The function selector is computed using:
// bytes4(keccak256("safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"))
mstore(0, 0x2eb2c2d600000000000000000000000000000000000000000000000000000000)
// Copy `from` and `to` fields from Table #1 to Table #3
calldatacopy(
4, // aligned such that `from` and `to` are at the correct location for Table #3
36, // beginning of `from` field from Table #1
64 // 32 bytes for `from` + 32 bytes for `to` field
)
////////// STEP 4/4 //////////
// Call into the destination erc1155 contract using as calldata Table #3 (constructed in-memory above)
let success := call(
gas, // forward all gas
assetAddress, // call address of erc1155 asset
0, // don't send any ETH
0, // pointer to start of input
add(assetDataLength, 32), // length of input (Table #3) is 32 bytes longer than `assetData` (Table #2)
0, // write output over memory that won't be reused
0 // don't copy output to memory
)
// Revert with reason given by AssetProxy if `transferFrom` call failed
if iszero(success) {
returndatacopy(
0, // copy to memory at 0
0, // copy from return data at 0
returndatasize() // copy all return data
)
revert(0, returndatasize())
}
// Return if call was successful
return(0, 0)
}
// Revert if undefined function is called
revert(0, 0)
}
}
/// @dev Gets the proxy id associated with the proxy address.
/// @return Proxy id.
function getProxyId()
external
pure
returns (bytes4)
{
return PROXY_ID;
}
}

View File

@ -33,7 +33,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
@ -70,6 +70,7 @@
"@0x/base-contract": "^5.0.2",
"@0x/contracts-erc20": "^1.0.9",
"@0x/contracts-erc721": "^1.0.9",
"@0x/contracts-erc1155": "^1.0.0",
"@0x/contracts-utils": "2.0.1",
"@0x/order-utils": "^7.0.2",
"@0x/types": "^2.1.1",

View File

@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
import * as IAssetData from '../generated-artifacts/IAssetData.json';
@ -15,6 +16,7 @@ import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
export const artifacts = {
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
IAssetData: IAssetData as ContractArtifact,

View File

@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/erc1155_proxy';
export * from '../generated-wrappers/erc20_proxy';
export * from '../generated-wrappers/erc721_proxy';
export * from '../generated-wrappers/i_asset_data';

View File

@ -0,0 +1,790 @@
import {
artifacts as erc1155Artifacts,
DummyERC1155ReceiverBatchTokenReceivedEventArgs,
DummyERC1155ReceiverContract,
ERC1155MintableContract,
Erc1155Wrapper,
} from '@0x/contracts-erc1155';
import {
chaiSetup,
constants,
expectTransactionFailedAsync,
expectTransactionFailedWithoutReasonAsync,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-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 { ERC1155ProxyWrapper, ERC721ProxyContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('ERC1155Proxy', () => {
// constant values used in transfer tests
const nftOwnerBalance = new BigNumber(1);
const nftNotOwnerBalance = new BigNumber(0);
const spenderInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverInitialFungibleBalance = constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
const receiverContractInitialFungibleBalance = new BigNumber(0);
const fungibleValueToTransferSmall = spenderInitialFungibleBalance.div(100);
const fungibleValueToTransferLarge = spenderInitialFungibleBalance.div(4);
const valueMultiplierSmall = new BigNumber(2);
const valueMultiplierNft = new BigNumber(1);
const nonFungibleValueToTransfer = nftOwnerBalance;
const receiverCallbackData = '0x01020304';
// addresses
let owner: string;
let notAuthorized: string;
let authorized: string;
let spender: string;
let receiver: string;
let receiverContract: string;
// contracts & wrappers
let erc1155Proxy: ERC721ProxyContract;
let erc1155Receiver: DummyERC1155ReceiverContract;
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
let erc1155Contract: ERC1155MintableContract;
let erc1155Wrapper: Erc1155Wrapper;
// tokens
let fungibleTokens: BigNumber[];
let nonFungibleTokensOwnedBySpender: BigNumber[];
// tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
/// deploy & configure ERC1155Proxy
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notAuthorized, authorized, spender, receiver] = _.slice(accounts, 0, 5));
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner);
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
from: owner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(erc1155Proxy.address, {
from: owner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// deploy & configure ERC1155 tokens and receiver
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
erc1155Contract = erc1155Wrapper.getContract();
erc1155Receiver = await DummyERC1155ReceiverContract.deployFrom0xArtifactAsync(
erc1155Artifacts.DummyERC1155Receiver,
provider,
txDefaults,
);
receiverContract = erc1155Receiver.address;
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
fungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds();
const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds();
const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync();
nonFungibleTokensOwnedBySpender = [];
_.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => {
const nonFungibleTokenAsString = nonFungibleToken.toString();
const nonFungibleTokenHeldBySpender =
tokenBalances.nonFungible[spender][erc1155Contract.address][nonFungibleTokenAsString][0];
nonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender);
});
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('general', () => {
it('should revert if undefined function is called', async () => {
const undefinedSelector = '0x01020304';
await expectTransactionFailedWithoutReasonAsync(
web3Wrapper.sendTransactionAsync({
from: owner,
to: erc1155Proxy.address,
value: constants.ZERO_AMOUNT,
data: undefinedSelector,
}),
);
});
it('should have an id of 0x9645780d', async () => {
const proxyId = await erc1155Proxy.getProxyId.callAsync();
// proxy computed using -- bytes4(keccak256("erc1155Token(address,uint256[],uint256[],bytes)"));
const expectedProxyId = '0x9645780d';
expect(proxyId).to.equal(expectedProxyId);
});
});
describe('transferFrom', () => {
it('should successfully transfer value for a single, fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier);
const expectedFinalBalances = [
spenderInitialFungibleBalance.minus(totalValueTransferred),
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for the same fungible token several times', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokenToTransfer = fungibleTokens[0];
const tokensToTransfer = [tokenToTransfer, tokenToTransfer, tokenToTransfer];
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
let totalValueTransferred = _.reduce(valuesToTransfer, (sum: BigNumber, value: BigNumber) => {
return sum.plus(value);
}) as BigNumber;
totalValueTransferred = totalValueTransferred.times(valueMultiplier);
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValueTransferred),
// receiver
receiverInitialFungibleBalance.plus(totalValueTransferred),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should successfully transfer value for several fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 3);
const valuesToTransfer = [
fungibleValueToTransferSmall.plus(10),
fungibleValueToTransferSmall.plus(20),
fungibleValueToTransferSmall.plus(30),
];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
spenderInitialFungibleBalance.minus(totalValuesTransferred[0]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[1]),
spenderInitialFungibleBalance.minus(totalValuesTransferred[2]),
// receiver
receiverInitialFungibleBalance.plus(totalValuesTransferred[0]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[1]),
receiverInitialFungibleBalance.plus(totalValuesTransferred[2]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer a non-fungible token', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer multiple non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const valuesToTransfer = [
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [
// spender
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
// receiver
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value for a combination of several fungible/non-fungible tokens', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const fungibleTokensToTransfer = fungibleTokens.slice(0, 3);
const nonFungibleTokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 2);
const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer);
const valuesToTransfer = [
fungibleValueToTransferLarge,
fungibleValueToTransferSmall,
fungibleValueToTransferSmall,
nonFungibleValueToTransfer,
nonFungibleValueToTransfer,
];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
spenderInitialFungibleBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
receiverInitialFungibleBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const expectedFinalBalances = [
// spender
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].minus(totalValuesTransferred[1]),
expectedInitialBalances[2].minus(totalValuesTransferred[2]),
expectedInitialBalances[3].minus(totalValuesTransferred[3]),
expectedInitialBalances[4].minus(totalValuesTransferred[4]),
// receiver
expectedInitialBalances[5].plus(totalValuesTransferred[0]),
expectedInitialBalances[6].plus(totalValuesTransferred[1]),
expectedInitialBalances[7].plus(totalValuesTransferred[2]),
expectedInitialBalances[8].plus(totalValuesTransferred[3]),
expectedInitialBalances[9].plus(totalValuesTransferred[4]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value to a smart contract and trigger its callback', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should successfully transfer value and ignore extra assetData', async () => {
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => {
return value.times(valueMultiplier);
});
const extraData = '0102030405060708';
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
extraData,
);
// check receiver log ignored extra asset data
expect(txReceipt.logs.length).to.be.equal(2);
const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<
DummyERC1155ReceiverBatchTokenReceivedEventArgs
>;
expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address);
expect(receiverLog.args.from).to.be.equal(spender);
expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]);
expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1);
expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]);
// note - if the `extraData` is ignored then the receiver log should ignore it as well.
expect(receiverLog.args.data).to.be.deep.equal(receiverCallbackData);
// check balances after transfer
const expectedFinalBalances = [
expectedInitialBalances[0].minus(totalValuesTransferred[0]),
expectedInitialBalances[1].plus(totalValuesTransferred[0]),
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if value is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [new BigNumber(0)];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if value multiplier is zero', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = new BigNumber(0);
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should transfer nothing if there are no tokens in asset data', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer: BigNumber[] = [];
const valuesToTransfer: BigNumber[] = [];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
);
// check balances after transfer
const expectedFinalBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should propagate revert reason from erc1155 contract failure', async () => {
// disable transfers
const shouldRejectTransfer = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc1155Receiver.setRejectTransferFlag.sendTransactionAsync(shouldRejectTransfer),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// setup test parameters
const tokenHolders = [spender, receiverContract];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiverContract,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.TransferRejected,
);
});
it('should revert if transferring the same non-fungible token more than once', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const nftToTransfer = nonFungibleTokensOwnedBySpender[0];
const tokensToTransfer = [nftToTransfer, nftToTransfer];
const valuesToTransfer = [nonFungibleValueToTransfer, nonFungibleValueToTransfer];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.NFTNotOwnedByFromAddress,
);
});
it('should revert if there is a multiplication overflow', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 3);
const maxUintValue = new BigNumber(2).pow(256).minus(1);
const valuesToTransfer = [nonFungibleValueToTransfer, maxUintValue, nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
nftOwnerBalance,
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
nftNotOwnerBalance,
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
// note - this will overflow because we are trying to transfer `maxUintValue * 2` of the 2nd token
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.Uint256Overflow,
);
});
it('should revert if transferring > 1 instances of a non-fungible token (valueMultiplier field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [nonFungibleValueToTransfer];
const valueMultiplier = new BigNumber(2);
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if transferring > 1 instances of a non-fungible token (`valuesToTransfer` field >1)', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = nonFungibleTokensOwnedBySpender.slice(0, 1);
const valuesToTransfer = [new BigNumber(2)];
const valueMultiplier = valueMultiplierNft;
// check balances before transfer
const expectedInitialBalances = [
// spender
nftOwnerBalance,
// receiver
nftNotOwnerBalance,
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.AmountEqualToOneRequired,
);
});
it('should revert if sender balance is insufficient', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valueGreaterThanSpenderBalance = spenderInitialFungibleBalance.plus(1);
const valuesToTransfer = [valueGreaterThanSpenderBalance];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.Uint256Underflow,
);
});
it('should revert if sender allowance is insufficient', async () => {
// dremove allowance for ERC1155 proxy
const wrapper = erc1155ProxyWrapper.getContractWrapper(erc1155Contract.address);
const isApproved = false;
await wrapper.setApprovalForAllAsync(spender, erc1155Proxy.address, isApproved);
const isApprovedActualValue = await wrapper.isApprovedForAllAsync(spender, erc1155Proxy.address);
expect(isApprovedActualValue).to.be.equal(isApproved);
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorized,
),
RevertReason.InsufficientAllowance,
);
});
it('should revert if caller is not authorized', async () => {
// setup test parameters
const tokenHolders = [spender, receiver];
const tokensToTransfer = fungibleTokens.slice(0, 1);
const valuesToTransfer = [fungibleValueToTransferLarge];
const valueMultiplier = valueMultiplierSmall;
// check balances before transfer
const expectedInitialBalances = [spenderInitialFungibleBalance, receiverInitialFungibleBalance];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances);
// execute transfer
await expectTransactionFailedAsync(
erc1155ProxyWrapper.transferFromAsync(
spender,
receiver,
erc1155Contract.address,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
notAuthorized,
),
RevertReason.SenderNotAuthorized,
);
});
});
});
// tslint:enable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count

View File

@ -0,0 +1,383 @@
import { artifacts as erc1155Artifacts, ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155';
import {
constants,
ERC1155FungibleHoldingsByOwner,
ERC1155HoldingsByOwner,
ERC1155NonFungibleHoldingsByOwner,
LogDecoder,
txDefaults,
} from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, ERC1155ProxyContract, IAssetProxyContract } from '../../src';
export class ERC1155ProxyWrapper {
private readonly _tokenOwnerAddresses: string[];
private readonly _fungibleTokenIds: string[];
private readonly _nonFungibleTokenIds: string[];
private readonly _nfts: Array<{ id: BigNumber; tokenId: BigNumber }>;
private readonly _contractOwnerAddress: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: Provider;
private readonly _logDecoder: LogDecoder;
private readonly _dummyTokenWrappers: Erc1155Wrapper[];
private readonly _assetProxyInterface: IAssetProxyContract;
private _proxyContract?: ERC1155ProxyContract;
private _proxyIdIfExists?: string;
private _initialTokenIdsByOwner: ERC1155HoldingsByOwner = { fungible: {}, nonFungible: {} };
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
this._web3Wrapper = new Web3Wrapper(provider);
this._provider = provider;
const allArtifacts = _.merge(artifacts, erc1155Artifacts);
this._logDecoder = new LogDecoder(this._web3Wrapper, allArtifacts);
this._dummyTokenWrappers = [];
this._assetProxyInterface = new IAssetProxyContract(
artifacts.IAssetProxy.compilerOutput.abi,
constants.NULL_ADDRESS,
provider,
);
this._tokenOwnerAddresses = tokenOwnerAddresses;
this._contractOwnerAddress = contractOwnerAddress;
this._fungibleTokenIds = [];
this._nonFungibleTokenIds = [];
this._nfts = [];
}
/**
* @dev Deploys dummy ERC1155 contracts
* @return An array of ERC1155 wrappers; one for each deployed contract.
*/
public async deployDummyContractsAsync(): Promise<Erc1155Wrapper[]> {
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY)) {
const erc1155Contract = await ERC1155MintableContract.deployFrom0xArtifactAsync(
erc1155Artifacts.ERC1155Mintable,
this._provider,
txDefaults,
);
const erc1155Wrapper = new Erc1155Wrapper(erc1155Contract, this._provider, this._contractOwnerAddress);
this._dummyTokenWrappers.push(erc1155Wrapper);
}
return this._dummyTokenWrappers;
}
/**
* @dev Deploys the ERC1155 proxy
* @return Deployed ERC1155 proxy contract instance
*/
public async deployProxyAsync(): Promise<ERC1155ProxyContract> {
this._proxyContract = await ERC1155ProxyContract.deployFrom0xArtifactAsync(
artifacts.ERC1155Proxy,
this._provider,
txDefaults,
);
this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync();
return this._proxyContract;
}
/**
* @dev Gets the ERC1155 proxy id
*/
public getProxyId(): string {
this._validateProxyContractExistsOrThrow();
return this._proxyIdIfExists as string;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return tranasction hash.
*/
public async transferFromAsync(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
extraData?: string,
): Promise<string> {
this._validateProxyContractExistsOrThrow();
let encodedAssetData = assetDataUtils.encodeERC1155AssetData(
contractAddress,
tokensToTransfer,
valuesToTransfer,
receiverCallbackData,
);
if (!_.isUndefined(extraData)) {
encodedAssetData = `${encodedAssetData}${extraData}`;
}
const data = this._assetProxyInterface.transferFrom.getABIEncodedTransactionData(
encodedAssetData,
from,
to,
valueMultiplier,
);
const txHash = await this._web3Wrapper.sendTransactionAsync({
to: (this._proxyContract as ERC1155ProxyContract).address,
data,
from: authorizedSender,
});
return txHash;
}
/**
* @dev transfers erc1155 fungible/non-fungible tokens.
* @param from source address
* @param to destination address
* @param contractAddress address of erc155 contract
* @param tokensToTransfer array of erc1155 tokens to transfer
* @param valuesToTransfer array of corresponding values for each erc1155 token to transfer
* @param valueMultiplier each value in `valuesToTransfer` is multiplied by this
* @param receiverCallbackData callback data if `to` is a contract
* @param authorizedSender sender of `transferFrom` transaction
* @param extraData extra data to append to `transferFrom` transaction. Optional.
* @return tranasction receipt with decoded logs.
*/
public async transferFromWithLogsAsync(
from: string,
to: string,
contractAddress: string,
tokensToTransfer: BigNumber[],
valuesToTransfer: BigNumber[],
valueMultiplier: BigNumber,
receiverCallbackData: string,
authorizedSender: string,
extraData?: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(
await this.transferFromAsync(
from,
to,
contractAddress,
tokensToTransfer,
valuesToTransfer,
valueMultiplier,
receiverCallbackData,
authorizedSender,
extraData,
),
);
return txReceipt;
}
/**
* @dev For each deployed ERC1155 contract, this function mints a set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async setBalancesAndAllowancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateProxyContractExistsOrThrow();
this._initialTokenIdsByOwner = {
fungible: {},
nonFungible: {},
};
const fungibleHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
// Set balances accordingly
for (const dummyWrapper of this._dummyTokenWrappers) {
const dummyAddress = dummyWrapper.getContract().address;
// tslint:disable-next-line:no-unused-variable
for (const i of _.times(constants.NUM_ERC1155_FUNGIBLE_TOKENS_MINT)) {
// Create a fungible token
const tokenId = await dummyWrapper.mintFungibleTokensAsync(
this._tokenOwnerAddresses,
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE,
);
const tokenIdAsString = tokenId.toString();
this._fungibleTokenIds.push(tokenIdAsString);
// Mint tokens for each owner for this token
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// tslint:disable-next-line:no-unused-variable
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress])) {
fungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
fungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] =
constants.INITIAL_ERC1155_FUNGIBLE_BALANCE;
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
}
}
// Non-fungible tokens
// tslint:disable-next-line:no-unused-variable
for (const j of _.times(constants.NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT)) {
const [tokenId, nftIds] = await dummyWrapper.mintNonFungibleTokensAsync(this._tokenOwnerAddresses);
const tokenIdAsString = tokenId.toString();
this._nonFungibleTokenIds.push(tokenIdAsString);
_.each(this._tokenOwnerAddresses, async (tokenOwnerAddress: string, i: number) => {
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString] = [];
}
this._nfts.push({ id: nftIds[i], tokenId });
nonFungibleHoldingsByOwner[tokenOwnerAddress][dummyAddress][tokenIdAsString].push(nftIds[i]);
await dummyWrapper.setApprovalForAllAsync(
tokenOwnerAddress,
(this._proxyContract as ERC1155ProxyContract).address,
true,
);
});
}
}
this._initialTokenIdsByOwner = {
fungible: fungibleHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return this._initialTokenIdsByOwner;
}
/**
* @dev For each deployed ERC1155 contract, this function quieries the set of fungible/non-fungible
* tokens for each token owner address (`_tokenOwnerAddresses`).
* @return Balances of each token owner, across all ERC1155 contracts and tokens.
*/
public async getBalancesAsync(): Promise<ERC1155HoldingsByOwner> {
this._validateDummyTokenContractsExistOrThrow();
this._validateBalancesAndAllowancesSetOrThrow();
const tokenHoldingsByOwner: ERC1155FungibleHoldingsByOwner = {};
const nonFungibleHoldingsByOwner: ERC1155NonFungibleHoldingsByOwner = {};
for (const dummyTokenWrapper of this._dummyTokenWrappers) {
const tokenContract = dummyTokenWrapper.getContract();
const tokenAddress = tokenContract.address;
// Construct batch balance call
const tokenOwners: string[] = [];
const tokenIds: BigNumber[] = [];
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
for (const tokenId of this._fungibleTokenIds) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(new BigNumber(tokenId));
}
for (const nft of this._nfts) {
tokenOwners.push(tokenOwnerAddress);
tokenIds.push(nft.id);
}
}
const balances = await dummyTokenWrapper.getBalancesAsync(tokenOwners, tokenIds);
// Parse out balances into fungible / non-fungible token holdings
let i = 0;
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
// Fungible tokens
for (const tokenId of this._fungibleTokenIds) {
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress])) {
tokenHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
tokenHoldingsByOwner[tokenOwnerAddress][tokenAddress][tokenId] = balances[i++];
}
// Non-fungible tokens
for (const nft of this._nfts) {
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress] = {};
}
if (_.isUndefined(nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress])) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress] = {};
}
if (
_.isUndefined(
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()],
)
) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()] = [];
}
const isOwner = balances[i++];
if (isOwner.isEqualTo(1)) {
nonFungibleHoldingsByOwner[tokenOwnerAddress][tokenAddress][nft.tokenId.toString()].push(
nft.id,
);
}
}
}
}
const holdingsByOwner = {
fungible: tokenHoldingsByOwner,
nonFungible: nonFungibleHoldingsByOwner,
};
return holdingsByOwner;
}
/**
* @dev Checks if proxy is approved to transfer tokens on behalf of `userAddress`.
* @param userAddress owner of ERC1155 tokens.
* @param contractAddress address of ERC1155 contract.
* @return True iff the proxy is approved for all. False otherwise.
*/
public async isProxyApprovedForAllAsync(userAddress: string, contractAddress: string): Promise<boolean> {
this._validateProxyContractExistsOrThrow();
const tokenContract = this._getContractFromAddress(contractAddress);
const operator = (this._proxyContract as ERC1155ProxyContract).address;
const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator);
return didApproveAll;
}
public getFungibleTokenIds(): BigNumber[] {
const fungibleTokenIds = _.map(this._fungibleTokenIds, (tokenIdAsString: string) => {
return new BigNumber(tokenIdAsString);
});
return fungibleTokenIds;
}
public getNonFungibleTokenIds(): BigNumber[] {
const nonFungibleTokenIds = _.map(this._nonFungibleTokenIds, (tokenIdAsString: string) => {
return new BigNumber(tokenIdAsString);
});
return nonFungibleTokenIds;
}
public getTokenOwnerAddresses(): string[] {
return this._tokenOwnerAddresses;
}
public getContractWrapper(contractAddress: string): Erc1155Wrapper {
const tokenWrapper = _.find(this._dummyTokenWrappers, (wrapper: Erc1155Wrapper) => {
return wrapper.getContract().address === contractAddress;
});
if (_.isUndefined(tokenWrapper)) {
throw new Error(`Contract: ${contractAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenWrapper;
}
private _getContractFromAddress(tokenAddress: string): ERC1155MintableContract {
const tokenContractIfExists = _.find(this._dummyTokenWrappers, c => c.getContract().address === tokenAddress);
if (_.isUndefined(tokenContractIfExists)) {
throw new Error(`Token: ${tokenAddress} was not deployed through ERC1155ProxyWrapper`);
}
return tokenContractIfExists.getContract();
}
private _validateDummyTokenContractsExistOrThrow(): void {
if (_.isUndefined(this._dummyTokenWrappers)) {
throw new Error('Dummy ERC1155 tokens not yet deployed, please call "deployDummyTokensAsync"');
}
}
private _validateProxyContractExistsOrThrow(): void {
if (_.isUndefined(this._proxyContract)) {
throw new Error('ERC1155 proxy contract not yet deployed, please call "deployProxyAsync"');
}
}
private _validateBalancesAndAllowancesSetOrThrow(): void {
if (
_.keys(this._initialTokenIdsByOwner.fungible).length === 0 ||
_.keys(this._initialTokenIdsByOwner.nonFungible).length === 0
) {
throw new Error(
'Dummy ERC1155 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"',
);
}
}
}

View File

@ -1,2 +1,3 @@
export * from './erc20_wrapper';
export * from './erc721_wrapper';
export * from './erc1155_proxy_wrapper';

View File

@ -3,6 +3,7 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/ERC1155Proxy.json",
"generated-artifacts/ERC20Proxy.json",
"generated-artifacts/ERC721Proxy.json",
"generated-artifacts/IAssetData.json",

View File

@ -1,4 +1,13 @@
[
{
"version": "3.1.0",
"changes": [
{
"note": "Added ERC1155Proxy test constants and interfaces",
"pr": 1661
}
]
},
{
"version": "3.0.9",
"changes": [

View File

@ -38,11 +38,16 @@ export const constants = {
NUM_DUMMY_ERC20_TO_DEPLOY: 3,
NUM_DUMMY_ERC721_TO_DEPLOY: 2,
NUM_ERC721_TOKENS_TO_MINT: 2,
NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY: 1,
NUM_ERC1155_FUNGIBLE_TOKENS_MINT: 3,
NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT: 3,
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
INITIAL_ERC20_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
INITIAL_ERC1155_FUNGIBLE_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
INITIAL_ERC1155_FUNGIBLE_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
STATIC_ORDER_PARAMS: {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),

View File

@ -32,6 +32,9 @@ export {
MarketBuyOrders,
MarketSellOrders,
ERC721TokenIdsByOwner,
ERC1155FungibleHoldingsByOwner,
ERC1155NonFungibleHoldingsByOwner,
ERC1155HoldingsByOwner,
OrderStatus,
AllowanceAmountScenario,
AssetDataScenario,

View File

@ -14,6 +14,27 @@ export interface ERC721TokenIdsByOwner {
};
}
export interface ERC1155FungibleHoldingsByOwner {
[ownerAddress: string]: {
[tokenAddress: string]: {
[tokenId: string]: BigNumber;
};
};
}
export interface ERC1155NonFungibleHoldingsByOwner {
[ownerAddress: string]: {
[tokenAddress: string]: {
[tokenId: string]: BigNumber[];
};
};
}
export interface ERC1155HoldingsByOwner {
fungible: ERC1155FungibleHoldingsByOwner;
nonFungible: ERC1155NonFungibleHoldingsByOwner;
}
export interface SubmissionContractEventArgs {
transactionId: BigNumber;
}

View File

@ -91,6 +91,8 @@ export {
SingleAssetData,
ERC20AssetData,
ERC721AssetData,
ERC1155AssetData,
ERC1155AssetDataAbi,
MultiAssetData,
MultiAssetDataWithRecursiveDecoding,
SignatureType,

View File

@ -64,6 +64,7 @@ export {
AssetData,
ERC20AssetData,
ERC721AssetData,
ERC1155AssetData,
SingleAssetData,
MultiAssetData,
MultiAssetDataWithRecursiveDecoding,

View File

@ -1,4 +1,13 @@
[
{
"version": "7.1.0",
"changes": [
{
"note": "Added encoding/decoding fdor ERC1155 asset data",
"pr": 1661
}
]
},
{
"timestamp": 1551479279,
"version": "7.0.2",

View File

@ -1,5 +1,8 @@
import {
AssetProxyId,
ERC1155AssetData,
ERC1155AssetDataAbi,
ERC1155AssetDataNoProxyId,
ERC20AssetData,
ERC721AssetData,
MultiAssetData,
@ -73,6 +76,47 @@ export const assetDataUtils = {
tokenId: (decodedAssetData as any).tokenId,
};
},
/**
* Encodes a set of ERC1155 assets into an assetData string, usable in the makerAssetData or
* takerAssetData fields of a 0x order.
* @param tokenAddress The token address of the ERC1155 contract
* @param tokenIds The Id's of the ERC1155 tokens to transfer
* @param tokenValues The values of each respective token Id to transfer
* @param callbackData The data forwarded to a receiver, if receiver is a contract.
* @return The hex encoded assetData string
*/
encodeERC1155AssetData(
tokenAddress: string,
tokenIds: BigNumber[],
tokenValues: BigNumber[],
callbackData: string,
): string {
const abiEncoder = AbiEncoder.createMethod('ERC1155Token', ERC1155AssetDataAbi);
const args = [tokenAddress, tokenIds, tokenValues, callbackData];
const assetData = abiEncoder.encode(args, encodingRules);
return assetData;
},
/**
* Decodes an ERC1155 assetData hex string into it's corresponding ERC1155 components.
* @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded tokenAddress, tokenIds, tokenValues, callbackData & assetProxyId
*/
decodeERC1155AssetData(assetData: string): ERC1155AssetData {
const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
if (assetProxyId !== AssetProxyId.ERC1155) {
throw new Error(`Invalid assetProxyId. Expected '${AssetProxyId.ERC1155}', got '${assetProxyId}'`);
}
const abiEncoder = AbiEncoder.createMethod('ERC1155Token', ERC1155AssetDataAbi);
// tslint:disable-next-line:no-unnecessary-type-assertion
const decodedAssetData = abiEncoder.decode(assetData, decodingRules) as ERC1155AssetDataNoProxyId;
return {
assetProxyId,
tokenAddress: decodedAssetData.tokenAddress,
tokenIds: decodedAssetData.tokenIds,
tokenValues: decodedAssetData.tokenValues,
callbackData: decodedAssetData.callbackData,
};
},
/**
* Encodes assetData for multiple AssetProxies into a single hex encoded assetData string, usable in the makerAssetData or
* takerAssetData fields in a 0x order.
@ -174,6 +218,7 @@ export const assetDataUtils = {
if (
assetProxyId !== AssetProxyId.ERC20 &&
assetProxyId !== AssetProxyId.ERC721 &&
assetProxyId !== AssetProxyId.ERC1155 &&
assetProxyId !== AssetProxyId.MultiAsset
) {
throw new Error(`Invalid assetProxyId: ${assetProxyId}`);
@ -194,6 +239,13 @@ export const assetDataUtils = {
isERC721AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC721AssetData {
return decodedAssetData.assetProxyId === AssetProxyId.ERC721;
},
/**
* Checks if the decoded asset data is valid ERC1155 data
* @param decodedAssetData The decoded asset data to check
*/
isERC1155AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC1155AssetData {
return decodedAssetData.assetProxyId === AssetProxyId.ERC1155;
},
/**
* Checks if the decoded asset data is valid MultiAsset data
* @param decodedAssetData The decoded asset data to check
@ -243,6 +295,14 @@ export const assetDataUtils = {
);
}
},
/**
* Throws if the assetData is not ERC1155.
* @param assetData Hex encoded assetData string
*/
assertIsERC1155AssetData(assetData: string): void {
// If the asset data is correctly decoded then it is valid.
assetDataUtils.decodeERC1155AssetData(assetData);
},
/**
* Throws if the length or assetProxyId are invalid for the MultiAssetProxy.
* @param assetData Hex encoded assetData string
@ -277,6 +337,9 @@ export const assetDataUtils = {
case AssetProxyId.ERC721:
assetDataUtils.assertIsERC721AssetData(assetData);
break;
case AssetProxyId.ERC1155:
assetDataUtils.assertIsERC1155AssetData(assetData);
break;
case AssetProxyId.MultiAsset:
assetDataUtils.assertIsMultiAssetData(assetData);
break;
@ -287,7 +350,7 @@ export const assetDataUtils = {
/**
* Decode any assetData into it's corresponding assetData object
* @param assetData Hex encoded assetData string to decode
* @return Either a ERC20 or ERC721 assetData object
* @return Either a ERC20, ERC721, ERC1155, or MultiAsset assetData object
*/
decodeAssetDataOrThrow(assetData: string): SingleAssetData | MultiAssetData {
const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
@ -298,6 +361,9 @@ export const assetDataUtils = {
case AssetProxyId.ERC721:
const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData);
return erc721AssetData;
case AssetProxyId.ERC1155:
const erc1155AssetData = assetDataUtils.decodeERC1155AssetData(assetData);
return erc1155AssetData;
case AssetProxyId.MultiAsset:
const multiAssetData = assetDataUtils.decodeMultiAssetData(assetData);
return multiAssetData;

View File

@ -46,6 +46,8 @@ export {
SingleAssetData,
ERC20AssetData,
ERC721AssetData,
ERC1155AssetData,
ERC1155AssetDataAbi,
MultiAssetData,
MultiAssetDataWithRecursiveDecoding,
AssetProxyId,

View File

@ -20,6 +20,15 @@ const KNOWN_ERC721_ENCODING = {
assetData:
'0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
};
const KNOWN_ERC1155_ENCODING = {
tokenAddress: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
tokenIds: [new BigNumber(100), new BigNumber(1001), new BigNumber(10001)],
tokenValues: [new BigNumber(200), new BigNumber(2001), new BigNumber(20001)],
callbackData:
'0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
assetData:
'0x9645780d0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000',
};
const KNOWN_MULTI_ASSET_ENCODING = {
amounts: [new BigNumber(1), new BigNumber(1)],
nestedAssetData: [KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData],
@ -50,6 +59,23 @@ describe('assetDataUtils', () => {
expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC721);
expect(decodedAssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId);
});
it('should encode ERC1155', () => {
const assetData = assetDataUtils.encodeERC1155AssetData(
KNOWN_ERC1155_ENCODING.tokenAddress,
KNOWN_ERC1155_ENCODING.tokenIds,
KNOWN_ERC1155_ENCODING.tokenValues,
KNOWN_ERC1155_ENCODING.callbackData,
);
expect(assetData).to.equal(KNOWN_ERC1155_ENCODING.assetData);
});
it('should decode ERC1155', () => {
const decodedAssetData = assetDataUtils.decodeERC1155AssetData(KNOWN_ERC1155_ENCODING.assetData);
expect(decodedAssetData.assetProxyId).to.be.equal(AssetProxyId.ERC1155);
expect(decodedAssetData.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress);
expect(decodedAssetData.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues);
expect(decodedAssetData.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds);
expect(decodedAssetData.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData);
});
it('should encode ERC20 and ERC721 multiAssetData', () => {
const assetData = assetDataUtils.encodeMultiAssetData(
KNOWN_MULTI_ASSET_ENCODING.amounts,

View File

@ -1,4 +1,17 @@
[
{
"version": "2.2.2",
"changes": [
{
"note": "Added ERC1155 revert reasons",
"pr": 1657
},
{
"note": "Added `ERC1155AssetData`, `ERC1155AssetDataNoProxyId`, and `ERC1155AssetDataAbi`",
"pr": 1661
}
]
},
{
"version": "2.2.1",
"changes": [

View File

@ -163,6 +163,7 @@ export enum AssetProxyId {
ERC20 = '0xf47261b0',
ERC721 = '0x02571792',
MultiAsset = '0x94cfcdd7',
ERC1155 = '0x9645780d',
}
export interface ERC20AssetData {
@ -176,7 +177,29 @@ export interface ERC721AssetData {
tokenId: BigNumber;
}
export type SingleAssetData = ERC20AssetData | ERC721AssetData;
export interface ERC1155AssetData {
assetProxyId: string;
tokenAddress: string;
tokenIds: BigNumber[];
tokenValues: BigNumber[];
callbackData: string;
}
export interface ERC1155AssetDataNoProxyId {
tokenAddress: string;
tokenValues: BigNumber[];
tokenIds: BigNumber[];
callbackData: string;
}
export const ERC1155AssetDataAbi = [
{ name: 'tokenAddress', type: 'address' },
{ name: 'tokenIds', type: 'uint256[]' },
{ name: 'tokenValues', type: 'uint256[]' },
{ name: 'callbackData', type: 'bytes' },
];
export type SingleAssetData = ERC20AssetData | ERC721AssetData | ERC1155AssetData;
export interface MultiAssetData {
assetProxyId: string;

View File

@ -11371,9 +11371,8 @@ nan@2.10.0, nan@>=2.5.1, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0, nan@^2.3.3, nan@^2.
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
nan@^2.11.0:
version "2.13.1"
resolved "https://registry.npmjs.org/nan/-/nan-2.13.1.tgz#a15bee3790bde247e8f38f1d446edcdaeb05f2dd"
integrity sha512-I6YB/YEuDeUZMmhscXKxGgZlFnhsn5y0hgOZBadkzfTRrZBtJDZeg6eQf7PYMIEclwmorTKK8GztsyOUSVBREA==
version "2.12.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
nano-json-stream-parser@^0.1.2:
version "0.1.2"
@ -13507,15 +13506,6 @@ react-dom@^16.3.2:
object-assign "^4.1.1"
prop-types "^15.6.0"
react-dom@^16.4.2:
version "16.8.4"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.8.4.tgz#1061a8e01a2b3b0c8160037441c3bf00a0e3bc48"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.13.4"
react-dom@^16.5.2:
version "16.5.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
@ -13573,6 +13563,8 @@ react-highlight@0xproject/react-highlight#react-peer-deps:
dependencies:
highlight.js "^9.11.0"
highlightjs-solidity "^1.0.5"
react "^16.4.2"
react-dom "^16.4.2"
react-hot-loader@^4.3.3:
version "4.3.4"
@ -13817,15 +13809,6 @@ react@^16.3.2:
object-assign "^4.1.1"
prop-types "^15.6.0"
react@^16.4.2:
version "16.8.4"
resolved "https://registry.npmjs.org/react/-/react-16.8.4.tgz#fdf7bd9ae53f03a9c4cd1a371432c206be1c4768"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.13.4"
react@^16.5.2:
version "16.5.2"
resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
@ -14697,13 +14680,6 @@ schedule@^0.5.0:
dependencies:
object-assign "^4.1.1"
scheduler@^0.13.4:
version "0.13.4"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.13.4.tgz#8fef05e7a3580c76c0364d2df5e550e4c9140298"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^0.4.4:
version "0.4.7"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
@ -17678,8 +17654,7 @@ websocket@1.0.26:
websocket@^1.0.26:
version "1.0.28"
resolved "https://registry.npmjs.org/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3"
integrity sha512-00y/20/80P7H4bCYkzuuvvfDvh+dgtXi5kzDf3UcZwN6boTYaKvsrtZ5lIYm1Gsg48siMErd9M4zjSYfYFHTrA==
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.28.tgz#9e5f6fdc8a3fe01d4422647ef93abdd8d45a78d3"
dependencies:
debug "^2.2.0"
nan "^2.11.0"