protocol/contracts/utils/test/lib_address_array.ts
F. Eugene Aumson 0de2b6983b
Add LibAssetData to contracts/asset-proxy (#1779)
* Stop restarting node unnecesssarily during test

* Add new, empty LibAssetData

* Support encoding & decoding of ERC20 asset data

* Support encoding & decoding of ERC721 asset data

* Support encoding & decoding of ERC1155 asset data

* Support encoding & decoding of multi-asset data

* Support querying ERC20 balance from asset data

* Support querying ERC721 balance from asset data

* Support querying ERC1155 balance from asset data

* Support querying balance from multi-asset data

* Support querying ERC20 allowance from asset data

* Support querying ERC721 allowance from asset data

* Support querying ERC1155 allowance from asset data

* In tests, wait for allowance set before checking

* Introduce temporary variable `assetDataBody`

* Handle edge case in multi-asset balance query

* Support multi-asset allowance query by asset data

* Move variable declaration up for readability.

* Make all solhint-disable's cite specific rules

And move the directives to the ends of lines whenever possible

* Rename query tests to include " by asset data"

* Extract test helper method

* Extract another test helper method

* Support batch queries of allowances & balances

* In LibAssetData.sol, use IERC1155, not ...Mintable

* Rename balance*() return vars: amount -> balance

* Fix bug in ERC721 balance query

Was using method balanceOf(), but needed to be using ownerOf().

getERC721TokenOwner() method lifted from
@0x/extensions/contracts/src/OrderValidator/OrderValidator.sol

* Reuse new en/decoders; avoid abi.decode().

* Start lowest allowance/balance from 0, not MAX_INT

* Properly implement ERC1155 balance querying

* Split lines for readability

* Also check isApprovedForAll in 721 allowance query

* Add neglected division of allowances by amounts

* Rename methods: balanceOf -> getBalance

* Rename methods: allowance -> getAllowance

* Add methods: getBalanceAndAllowance() & batch...()

* Rename return vars: amount -> allowance

* Add devdoc comments

* Rename batchGet* methods to getBatch*

* Remove refactoring relic

* Add revert messages to all require() calls

* Reduce gas usage for ERC1155 asset data decoding

* Don't use dockerized solc for ERC20 contracts

Because they demand solc version 0.4.26, and it seems as though the tag
for that version has been deleted from dockerhub.

Without this, @0x/contracts-erc20 was failing to build.

* Rename batch functions to use plurals

* Skip dockerized solc for contracts needing 0.4.26

I seems as though the tag for that version has been deleted from
dockerhub.

Without this, these contracts were failing to build.

* Make revert reasons follow snake case convention
2019-04-29 15:46:26 -04:00

154 lines
6.5 KiB
TypeScript

import {
addressUtils,
chaiSetup,
expectContractCallFailedAsync,
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 * as _ from 'lodash';
import { artifacts, TestLibAddressArrayContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('LibAddressArray', () => {
let lib: TestLibAddressArrayContract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy LibAddressArray
lib = await TestLibAddressArrayContract.deployFrom0xArtifactAsync(
artifacts.TestLibAddressArray,
provider,
txDefaults,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('append', () => {
it('should append to empty array', async () => {
const addr = addressUtils.generatePseudoRandomAddress();
const result = await lib.publicAppend.callAsync([], addr);
const expected = [addr];
expect(result).to.deep.equal(expected);
});
it('should append to non-empty array', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const expected = [...arr, addr];
const result = await lib.publicAppend.callAsync(arr, addr);
expect(result).to.deep.equal(expected);
});
it('should revert if the free memory pointer was moved to before the end of the array', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const freeMemOffset = new BigNumber(-1);
return expectContractCallFailedAsync(
lib.testAppendRealloc.callAsync(arr, freeMemOffset, addr),
RevertReason.InvalidFreeMemoryPtr,
);
});
it('should keep the same memory address if free memory pointer does not move', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const freeMemOffset = new BigNumber(0);
const expected = [...arr, addr];
const [result, oldArrayMemStart, newArrayMemStart] = await lib.testAppendRealloc.callAsync(
arr,
freeMemOffset,
addr,
);
expect(result).to.deep.equal(expected);
expect(newArrayMemStart).bignumber.to.be.equal(oldArrayMemStart);
});
it('should change memory address if free memory pointer advances', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const freeMemOffset = new BigNumber(1);
const expectedArray = [...arr, addr];
const [result, oldArrayMemStart, newArrayMemStart] = await lib.testAppendRealloc.callAsync(
arr,
freeMemOffset,
addr,
);
// The new location should be the end of the old array + freeMemOffset.
const expectedNewArrayMemStart = oldArrayMemStart.plus((arr.length + 1) * 32).plus(freeMemOffset);
expect(result).to.deep.equal(expectedArray);
expect(newArrayMemStart).bignumber.to.be.equal(expectedNewArrayMemStart);
});
});
describe('contains', () => {
it('should return false on an empty array', async () => {
const addr = addressUtils.generatePseudoRandomAddress();
const isFound = await lib.publicContains.callAsync([], addr);
expect(isFound).to.equal(false);
});
it('should return false on a missing item', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const isFound = await lib.publicContains.callAsync(arr, addr);
expect(isFound).to.equal(false);
});
it('should return true on an included item', async () => {
const arr = _.times(4, () => addressUtils.generatePseudoRandomAddress());
const addr = _.sample(arr) as string;
const isFound = await lib.publicContains.callAsync(arr, addr);
expect(isFound).to.equal(true);
});
it('should return true on the only item in the array', async () => {
const arr = _.times(1, () => addressUtils.generatePseudoRandomAddress());
const isFound = await lib.publicContains.callAsync(arr, arr[0]);
expect(isFound).to.equal(true);
});
});
describe('indexOf', () => {
it('should fail on an empty array', async () => {
const addr = addressUtils.generatePseudoRandomAddress();
const [isSuccess] = await lib.publicIndexOf.callAsync([], addr);
expect(isSuccess).to.equal(false);
});
it('should fail on a missing item', async () => {
const arr = _.times(3, () => addressUtils.generatePseudoRandomAddress());
const addr = addressUtils.generatePseudoRandomAddress();
const [isSuccess] = await lib.publicIndexOf.callAsync(arr, addr);
expect(isSuccess).to.equal(false);
});
it('should succeed on an included item', async () => {
const arr = _.times(4, () => addressUtils.generatePseudoRandomAddress());
const expectedIndexOf = _.random(0, arr.length - 1);
const addr = arr[expectedIndexOf];
const [isSuccess, index] = await lib.publicIndexOf.callAsync(arr, addr);
expect(isSuccess).to.equal(true);
expect(index).bignumber.to.equal(expectedIndexOf);
});
it('should succeed on the only item in the array', async () => {
const arr = _.times(1, () => addressUtils.generatePseudoRandomAddress());
const [isSuccess, index] = await lib.publicIndexOf.callAsync(arr, arr[0]);
expect(isSuccess).to.equal(true);
expect(index).bignumber.to.equal(0);
});
});
});
// tslint:disable:max-file-line-count