Add StaticCallProxy support to LibAssetData

This commit is contained in:
Amir Bandeali 2019-06-09 21:10:57 -07:00
parent f2cbf4a561
commit 5810e7df82
2 changed files with 77 additions and 0 deletions

View File

@ -43,6 +43,9 @@ contract LibAssetData is
bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e;
bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
// `transferFrom` selector for all AssetProxy contracts
bytes4 constant internal _ASSET_PROXY_TRANSFER_FROM_SELECTOR = 0xa85e59e4;
using LibBytes for bytes;
// solhint-disable var-name-mixedcase
@ -50,6 +53,7 @@ contract LibAssetData is
address internal _ERC20_PROXY_ADDRESS;
address internal _ERC721_PROXY_ADDRESS;
address internal _ERC1155_PROXY_ADDRESS;
address internal _STATIC_CALL_PROXY_ADDRESS;
// solhint-enable var-name-mixedcase
constructor (address _exchange)
@ -59,6 +63,7 @@ contract LibAssetData is
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(STATIC_CALL_PROXY_ID);
}
/// @dev Returns the owner's balance of the assets(s) specified in
@ -115,6 +120,21 @@ contract LibAssetData is
balance = scaledBalance;
}
}
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
bytes memory transferFromData = abi.encodeWithSelector(
_ASSET_PROXY_TRANSFER_FROM_SELECTOR,
assetData,
address(0), // `from` address is not used
address(0), // `to` address is not used
0 // `amount` is not used
);
// Check if staticcall would be successful
(bool success,) = _STATIC_CALL_PROXY_ADDRESS.staticcall(transferFromData);
// Success means that the staticcall can be made an unlimited amount of times
balance = success ? _MAX_UINT256 : 0;
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
// Get array of values and array of assetDatas
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
@ -241,6 +261,9 @@ contract LibAssetData is
// Query allowance
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
// The StaticCallProxy does not require any approvals
allowance = _MAX_UINT256;
}
// Allowance will be 0 if the assetProxyId is unknown

View File

@ -7,6 +7,8 @@ import {
ERC20ProxyContract,
ERC721ProxyContract,
MultiAssetProxyContract,
StaticCallProxyContract,
TestStaticCallTargetContract,
} from '@0x/contracts-asset-proxy';
import {
artifacts as erc1155Artifacts,
@ -21,6 +23,7 @@ import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { artifacts, LibAssetDataContract } from '../src';
@ -65,6 +68,8 @@ describe('LibAssetData', () => {
let erc721Proxy: ERC721ProxyContract;
let erc1155Proxy: ERC1155ProxyContract;
let multiAssetProxy: MultiAssetProxyContract;
let staticCallProxy: StaticCallProxyContract;
let staticCallTarget: TestStaticCallTargetContract;
let libAssetData: LibAssetDataContract;
let tokenOwnerAddress: string;
@ -110,11 +115,17 @@ describe('LibAssetData', () => {
provider,
txDefaults,
);
staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync(
proxyArtifacts.StaticCallProxy,
provider,
txDefaults,
);
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address);
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address);
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc1155Proxy.address);
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(multiAssetProxy.address);
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(staticCallProxy.address);
libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync(
artifacts.LibAssetData,
@ -123,6 +134,12 @@ describe('LibAssetData', () => {
exchange.address,
);
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
proxyArtifacts.TestStaticCallTarget,
provider,
txDefaults,
);
[tokenOwnerAddress] = await web3Wrapper.getAvailableAddressesAsync();
erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
@ -324,6 +341,32 @@ describe('LibAssetData', () => {
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, fakeAssetData);
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a balance of MAX_UINT256 if the the StaticCallProxy assetData contains data for a successful staticcall', async () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(1));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
expect(balance).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
});
it('should return a balance of 0 if the the StaticCallProxy assetData contains data for an unsuccessful staticcall', async () => {
const staticCallData = staticCallTarget.isOddNumber.getABIEncodedTransactionData(new BigNumber(0));
const trueAsBuffer = ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000001');
const expectedResultHash = ethUtil.bufferToHex(ethUtil.sha3(trueAsBuffer));
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
expectedResultHash,
);
const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData);
expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT);
});
});
describe('getAssetProxyAllowance', () => {
@ -399,6 +442,17 @@ describe('LibAssetData', () => {
const allowance = await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, fakeAssetData);
expect(allowance).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return an allowance of MAX_UINT256 for any staticCallAssetData', async () => {
const staticCallData = AssetProxyId.StaticCall;
const assetData = assetDataUtils.encodeStaticCallAssetData(
staticCallTarget.address,
staticCallData,
constants.KECCAK256_NULL,
);
const allowance = await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData);
expect(allowance).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
});
});
describe('getBatchBalances', () => {