protocol/packages/contracts/test/utils/asset_wrapper.ts
Fabio Berger de9f0732a0 Merge branch 'v2-prototype' into fix/five_decimal_scenario
* v2-prototype: (75 commits)
  Update relayer grid tiles to use Text
  Fix build
  Update file structure
  Update 2.0.0 artifacts
  Move ledgerhq module declarations to typescript-typings
  Export LedgerEthereumClient type in subproviders
  Update artifacts
  Add logging and updated artifacts
  Fix migrations
  Run prettier
  Add Kovan artifacts
  Use ledger subprovider
  Add Kovan migrations
  Remove state variable from Link component in Portal
  Make registerAssetProxy append only
  Update staging api link
  Change getTransactionReceipt to awaitTransactionMined
  Move /docs route to the end
  Remove extra call to scrollIntoView for wallet in onboarding
  Update expectRevertReasonOrAlwaysFailingTransactionAsync to check status codes
  ...
2018-07-02 11:12:01 +02:00

224 lines
10 KiB
TypeScript

import { assetProxyUtils } from '@0xproject/order-utils';
import { AssetProxyId } from '@0xproject/types';
import { BigNumber, errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import { AbstractAssetWrapper } from './abstract_asset_wrapper';
import { constants } from './constants';
import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper';
interface ProxyIdToAssetWrappers {
[proxyId: string]: AbstractAssetWrapper;
}
/**
* This class abstracts away the differences between ERC20 and ERC721 tokens so that
* the logic that uses it does not need to care what standard a token belongs to.
*/
export class AssetWrapper {
private _proxyIdToAssetWrappers: ProxyIdToAssetWrappers;
constructor(assetWrappers: AbstractAssetWrapper[]) {
this._proxyIdToAssetWrappers = {};
_.each(assetWrappers, assetWrapper => {
const proxyId = assetWrapper.getProxyId();
this._proxyIdToAssetWrappers[proxyId] = assetWrapper;
});
}
public async getBalanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const proxyId = assetProxyUtils.decodeAssetDataId(assetData);
switch (proxyId) {
case AssetProxyId.ERC20: {
const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper;
const balance = await erc20Wrapper.getBalanceAsync(userAddress, assetData);
return balance;
}
case AssetProxyId.ERC721: {
const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper;
const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData);
const isOwner = await assetWrapper.isOwnerAsync(
userAddress,
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
const balance = isOwner ? new BigNumber(1) : new BigNumber(0);
return balance;
}
default:
throw errorUtils.spawnSwitchErr('proxyId', proxyId);
}
}
public async setBalanceAsync(userAddress: string, assetData: string, desiredBalance: BigNumber): Promise<void> {
const proxyId = assetProxyUtils.decodeAssetDataId(assetData);
switch (proxyId) {
case AssetProxyId.ERC20: {
const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper;
await erc20Wrapper.setBalanceAsync(userAddress, assetData, desiredBalance);
return;
}
case AssetProxyId.ERC721: {
if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) {
throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`);
}
const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper;
const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData);
const doesTokenExist = erc721Wrapper.doesTokenExistAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
if (!doesTokenExist && desiredBalance.eq(1)) {
await erc721Wrapper.mintAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress);
return;
} else if (!doesTokenExist && desiredBalance.eq(0)) {
return; // noop
}
const tokenOwner = await erc721Wrapper.ownerOfAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
if (userAddress !== tokenOwner && desiredBalance.eq(1)) {
await erc721Wrapper.transferFromAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
tokenOwner,
userAddress,
);
} else if (tokenOwner === userAddress && desiredBalance.eq(0)) {
// Transfer token to someone else
const userAddresses = await (erc721Wrapper as any)._web3Wrapper.getAvailableAddressesAsync();
const nonOwner = _.find(userAddresses, a => a !== userAddress);
await erc721Wrapper.transferFromAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
tokenOwner,
nonOwner,
);
return;
} else if (
(userAddress !== tokenOwner && desiredBalance.eq(0)) ||
(tokenOwner === userAddress && desiredBalance.eq(1))
) {
return; // noop
}
break;
}
default:
throw errorUtils.spawnSwitchErr('proxyId', proxyId);
}
}
public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise<BigNumber> {
const proxyId = assetProxyUtils.decodeAssetDataId(assetData);
switch (proxyId) {
case AssetProxyId.ERC20: {
const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper;
const allowance = await erc20Wrapper.getProxyAllowanceAsync(userAddress, assetData);
return allowance;
}
case AssetProxyId.ERC721: {
const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper;
const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData);
const isProxyApprovedForAll = await assetWrapper.isProxyApprovedForAllAsync(
userAddress,
erc721ProxyData.tokenAddress,
);
if (isProxyApprovedForAll) {
return constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
}
const isProxyApproved = await assetWrapper.isProxyApprovedAsync(
erc721ProxyData.tokenAddress,
erc721ProxyData.tokenId,
);
const allowance = isProxyApproved ? new BigNumber(1) : new BigNumber(0);
return allowance;
}
default:
throw errorUtils.spawnSwitchErr('proxyId', proxyId);
}
}
public async setProxyAllowanceAsync(
userAddress: string,
assetData: string,
desiredAllowance: BigNumber,
): Promise<void> {
const proxyId = assetProxyUtils.decodeAssetDataId(assetData);
switch (proxyId) {
case AssetProxyId.ERC20: {
const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper;
await erc20Wrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance);
return;
}
case AssetProxyId.ERC721: {
if (
!desiredAllowance.eq(0) &&
!desiredAllowance.eq(1) &&
!desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
) {
throw new Error(
`Allowance for ERC721 token can only be set to 0, 1 or 2^256-1. Got: ${desiredAllowance}`,
);
}
const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper;
const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData);
const doesTokenExist = await erc721Wrapper.doesTokenExistAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
if (!doesTokenExist) {
throw new Error(
`Cannot setProxyAllowance on non-existent token: ${assetProxyData.tokenAddress} ${
assetProxyData.tokenId
}`,
);
}
const isProxyApprovedForAll = await erc721Wrapper.isProxyApprovedForAllAsync(
userAddress,
assetProxyData.tokenAddress,
);
if (!isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
const isApproved = true;
await erc721Wrapper.approveProxyForAllAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
isApproved,
);
} else if (isProxyApprovedForAll && desiredAllowance.eq(0)) {
const isApproved = false;
await erc721Wrapper.approveProxyForAllAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
isApproved,
);
} else if (isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
return; // Noop
}
const isProxyApproved = await erc721Wrapper.isProxyApprovedAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
if (!isProxyApproved && desiredAllowance.eq(1)) {
await erc721Wrapper.approveProxyAsync(assetProxyData.tokenAddress, assetProxyData.tokenId);
} else if (isProxyApproved && desiredAllowance.eq(0)) {
// Remove approval
await erc721Wrapper.approveAsync(
constants.NULL_ADDRESS,
assetProxyData.tokenAddress,
assetProxyData.tokenId,
);
} else if (
(!isProxyApproved && desiredAllowance.eq(0)) ||
(isProxyApproved && desiredAllowance.eq(1))
) {
return; // noop
}
break;
}
default:
throw errorUtils.spawnSwitchErr('proxyId', proxyId);
}
}
}