Implement ERC721 token wrapper and token transfer proxy with tests
This commit is contained in:
parent
3aef323c13
commit
2adc299c78
@ -0,0 +1,73 @@
|
|||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import { ContractAbi } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from '../artifacts';
|
||||||
|
import { assert } from '../utils/assert';
|
||||||
|
|
||||||
|
import { ContractWrapper } from './contract_wrapper';
|
||||||
|
import { ERC721ProxyContract } from './generated/erc721_proxy';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class includes the functionality related to interacting with the ERC721Proxy contract.
|
||||||
|
*/
|
||||||
|
export class ERC721ProxyWrapper extends ContractWrapper {
|
||||||
|
public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi;
|
||||||
|
private _erc721ProxyContractIfExists?: ERC721ProxyContract;
|
||||||
|
private _contractAddressIfExists?: string;
|
||||||
|
constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
|
||||||
|
super(web3Wrapper, networkId);
|
||||||
|
this._contractAddressIfExists = contractAddressIfExists;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if the Exchange contract address is authorized by the ERC721Proxy contract.
|
||||||
|
* @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
|
||||||
|
* @return Whether the exchangeContractAddress is authorized.
|
||||||
|
*/
|
||||||
|
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
|
||||||
|
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
|
||||||
|
const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
|
||||||
|
const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
|
||||||
|
const isAuthorized = await ERC721ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress);
|
||||||
|
return isAuthorized;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the list of all Exchange contract addresses authorized by the ERC721Proxy contract.
|
||||||
|
* @return The list of authorized addresses.
|
||||||
|
*/
|
||||||
|
public async getAuthorizedAddressesAsync(): Promise<string[]> {
|
||||||
|
const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
|
||||||
|
const authorizedAddresses = await ERC721ProxyContractInstance.getAuthorizedAddresses.callAsync();
|
||||||
|
return authorizedAddresses;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves the Ethereum address of the ERC721Proxy contract deployed on the network
|
||||||
|
* that the user-passed web3 provider is connected to.
|
||||||
|
* @returns The Ethereum address of the ERC721Proxy contract being used.
|
||||||
|
*/
|
||||||
|
public getContractAddress(): string {
|
||||||
|
const contractAddress = this._getContractAddress(artifacts.ERC721Proxy, this._contractAddressIfExists);
|
||||||
|
return contractAddress;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
private _invalidateContractInstance(): void {
|
||||||
|
delete this._erc721ProxyContractIfExists;
|
||||||
|
}
|
||||||
|
private async _getERC721ProxyContractAsync(): Promise<ERC721ProxyContract> {
|
||||||
|
if (!_.isUndefined(this._erc721ProxyContractIfExists)) {
|
||||||
|
return this._erc721ProxyContractIfExists;
|
||||||
|
}
|
||||||
|
const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
|
||||||
|
artifacts.ERC721Proxy,
|
||||||
|
this._contractAddressIfExists,
|
||||||
|
);
|
||||||
|
const contractInstance = new ERC721ProxyContract(
|
||||||
|
abi,
|
||||||
|
address,
|
||||||
|
this._web3Wrapper.getProvider(),
|
||||||
|
this._web3Wrapper.getContractDefaults(),
|
||||||
|
);
|
||||||
|
this._erc721ProxyContractIfExists = contractInstance;
|
||||||
|
return this._erc721ProxyContractIfExists;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,439 @@
|
|||||||
|
import { schemas } from '@0xproject/json-schemas';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { constants } from '../../test/utils/constants';
|
||||||
|
import { artifacts } from '../artifacts';
|
||||||
|
import {
|
||||||
|
BlockRange,
|
||||||
|
ContractWrappersError,
|
||||||
|
EventCallback,
|
||||||
|
IndexedFilterValues,
|
||||||
|
MethodOpts,
|
||||||
|
TransactionOpts,
|
||||||
|
} from '../types';
|
||||||
|
import { assert } from '../utils/assert';
|
||||||
|
|
||||||
|
import { ContractWrapper } from './contract_wrapper';
|
||||||
|
import { ERC721ProxyWrapper } from './erc721_proxy_wrapper';
|
||||||
|
import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from './generated/erc721_token';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class includes all the functionality related to interacting with ERC721 token contracts.
|
||||||
|
* All ERC721 method calls are supported, along with some convenience methods for getting/setting allowances
|
||||||
|
* to the 0x ERC721 Proxy smart contract.
|
||||||
|
*/
|
||||||
|
export class ERC721TokenWrapper extends ContractWrapper {
|
||||||
|
public abi: ContractAbi = artifacts.ERC721Token.compilerOutput.abi;
|
||||||
|
private _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
|
||||||
|
private _erc721ProxyWrapper: ERC721ProxyWrapper;
|
||||||
|
constructor(web3Wrapper: Web3Wrapper, networkId: number, erc721ProxyWrapper: ERC721ProxyWrapper) {
|
||||||
|
super(web3Wrapper, networkId);
|
||||||
|
this._tokenContractsByAddress = {};
|
||||||
|
this._erc721ProxyWrapper = erc721ProxyWrapper;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Count all NFTs assigned to an owner
|
||||||
|
* NFTs assigned to the zero address are considered invalid, and this function throws for queries about the zero address.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return The number of NFTs owned by `ownerAddress`, possibly zero
|
||||||
|
*/
|
||||||
|
public async getTokenCountAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
methodOpts?: MethodOpts,
|
||||||
|
): Promise<BigNumber> {
|
||||||
|
assert.isETHAddressHex('ownerAddress', ownerAddress);
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
const normalizedOwnerAddress = ownerAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
|
||||||
|
const txData = {};
|
||||||
|
let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, defaultBlock);
|
||||||
|
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
|
||||||
|
balance = new BigNumber(balance);
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Find the owner of an NFT
|
||||||
|
* NFTs assigned to zero address are considered invalid, and queries about them do throw.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return The address of the owner of the NFT
|
||||||
|
*/
|
||||||
|
public async getOwnerOfAsync(tokenAddress: string, tokenId: BigNumber, methodOpts?: MethodOpts): Promise<string> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isBigNumber('tokenId', tokenId);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
|
||||||
|
const txData = {};
|
||||||
|
try {
|
||||||
|
const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId, txData, defaultBlock);
|
||||||
|
return tokenOwner;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(ContractWrappersError.ERC721OwnerNotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Query if an address is an authorized operator for all NFT's of `ownerAddress`
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param ownerAddress The hex encoded user Ethereum address of the token owner.
|
||||||
|
* @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to check if approved.
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise
|
||||||
|
*/
|
||||||
|
public async isApprovedForAllAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
operatorAddress: string,
|
||||||
|
methodOpts?: MethodOpts,
|
||||||
|
): Promise<boolean> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isETHAddressHex('ownerAddress', ownerAddress);
|
||||||
|
assert.isETHAddressHex('operatorAddress', operatorAddress);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
const normalizedOwnerAddress = ownerAddress.toLowerCase();
|
||||||
|
const normalizedOperatorAddress = operatorAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
|
||||||
|
const txData = {};
|
||||||
|
const isApprovedForAll = await tokenContract.isApprovedForAll.callAsync(
|
||||||
|
normalizedOwnerAddress,
|
||||||
|
normalizedOperatorAddress,
|
||||||
|
txData,
|
||||||
|
defaultBlock,
|
||||||
|
);
|
||||||
|
return isApprovedForAll;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Query if 0x proxy is an authorized operator for all NFT's of `ownerAddress`
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param ownerAddress The hex encoded user Ethereum address of the token owner.
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise
|
||||||
|
*/
|
||||||
|
public async isProxyApprovedForAllAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
methodOpts?: MethodOpts,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
|
||||||
|
const isProxyApprovedForAll = await this.isApprovedForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
proxyAddress,
|
||||||
|
methodOpts,
|
||||||
|
);
|
||||||
|
return isProxyApprovedForAll;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the approved address for a single NFT
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return The approved address for this NFT, or the zero address if there is none
|
||||||
|
*/
|
||||||
|
public async getApprovedAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
tokenId: BigNumber,
|
||||||
|
methodOpts?: MethodOpts,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isBigNumber('tokenId', tokenId);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
|
||||||
|
const txData = {};
|
||||||
|
const approvedAddress = await tokenContract.getApproved.callAsync(tokenId, txData, defaultBlock);
|
||||||
|
if (approvedAddress === constants.NULL_ADDRESS) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return approvedAddress;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks if 0x proxy is approved for a single NFT
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param methodOpts Optional arguments this method accepts.
|
||||||
|
* @return True if 0x proxy is approved
|
||||||
|
*/
|
||||||
|
public async isProxyApprovedAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
tokenId: BigNumber,
|
||||||
|
methodOpts?: MethodOpts,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
|
||||||
|
const approvedAddress = await this.getApprovedAsync(tokenAddress, tokenId, methodOpts);
|
||||||
|
const isProxyApproved = approvedAddress === proxyAddress;
|
||||||
|
return isProxyApproved;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* Emits the ApprovalForAll event.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param ownerAddress The hex encoded user Ethereum address of the token owner.
|
||||||
|
* @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for.
|
||||||
|
* @param isApproved The boolean variable to set the approval to.
|
||||||
|
* @param txOpts Transaction parameters.
|
||||||
|
* @return Transaction hash.
|
||||||
|
*/
|
||||||
|
public async setApprovalForAllAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
operatorAddress: string,
|
||||||
|
isApproved: boolean,
|
||||||
|
txOpts: TransactionOpts = {},
|
||||||
|
): Promise<string> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isETHAddressHex('ownerAddress', ownerAddress);
|
||||||
|
assert.isETHAddressHex('operatorAddress', operatorAddress);
|
||||||
|
assert.isBoolean('isApproved', isApproved);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
const normalizedOwnerAddress = ownerAddress.toLowerCase();
|
||||||
|
const normalizedOperatorAddress = operatorAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync(
|
||||||
|
normalizedOperatorAddress,
|
||||||
|
isApproved,
|
||||||
|
{
|
||||||
|
gas: txOpts.gasLimit,
|
||||||
|
gasPrice: txOpts.gasPrice,
|
||||||
|
from: normalizedOwnerAddress,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* Emits the ApprovalForAll event.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param ownerAddress The hex encoded user Ethereum address of the token owner.
|
||||||
|
* @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for.
|
||||||
|
* @param isApproved The boolean variable to set the approval to.
|
||||||
|
* @param txOpts Transaction parameters.
|
||||||
|
* @return Transaction hash.
|
||||||
|
*/
|
||||||
|
public async setProxyApprovalForAllAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
ownerAddress: string,
|
||||||
|
isApproved: boolean,
|
||||||
|
txOpts: TransactionOpts = {},
|
||||||
|
): Promise<string> {
|
||||||
|
const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
|
||||||
|
const txHash = await this.setApprovalForAllAsync(tokenAddress, ownerAddress, proxyAddress, isApproved, txOpts);
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set or reaffirm the approved address for an NFT
|
||||||
|
* The zero address indicates there is no approved address. Throws unless `msg.sender` is the current NFT owner,
|
||||||
|
* or an authorized operator of the current owner.
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* Emits the Approval event.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param approvedAddress The hex encoded user Ethereum address you'd like to set approval for.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param txOpts Transaction parameters.
|
||||||
|
* @return Transaction hash.
|
||||||
|
*/
|
||||||
|
public async setApprovalAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
approvedAddress: string,
|
||||||
|
tokenId: BigNumber,
|
||||||
|
txOpts: TransactionOpts = {},
|
||||||
|
): Promise<string> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isETHAddressHex('approvedAddress', approvedAddress);
|
||||||
|
assert.isBigNumber('tokenId', tokenId);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
const normalizedApprovedAddress = approvedAddress.toLowerCase();
|
||||||
|
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const tokenOwnerAddress = await tokenContract.ownerOf.callAsync(tokenId);
|
||||||
|
const txHash = await tokenContract.approve.sendTransactionAsync(normalizedApprovedAddress, tokenId, {
|
||||||
|
gas: txOpts.gasLimit,
|
||||||
|
gasPrice: txOpts.gasPrice,
|
||||||
|
from: tokenOwnerAddress,
|
||||||
|
});
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set or reaffirm 0x proxy as an approved address for an NFT
|
||||||
|
* Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* Emits the Approval event.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param txOpts Transaction parameters.
|
||||||
|
* @return Transaction hash.
|
||||||
|
*/
|
||||||
|
public async setProxyApprovalAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
tokenId: BigNumber,
|
||||||
|
txOpts: TransactionOpts = {},
|
||||||
|
): Promise<string> {
|
||||||
|
const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
|
||||||
|
const txHash = await this.setApprovalAsync(tokenAddress, proxyAddress, tokenId, txOpts);
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
|
||||||
|
* Throws if `_tokenId` is not a valid NFT
|
||||||
|
* Emits the ApprovalForAll event.
|
||||||
|
* @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
|
||||||
|
* @param receiverAddress The hex encoded Ethereum address of the user to send the NFT to.
|
||||||
|
* @param senderAddress The hex encoded Ethereum address of the user to send the NFT to.
|
||||||
|
* @param tokenId The identifier for an NFT
|
||||||
|
* @param txOpts Transaction parameters.
|
||||||
|
* @return Transaction hash.
|
||||||
|
*/
|
||||||
|
public async transferFromAsync(
|
||||||
|
tokenAddress: string,
|
||||||
|
receiverAddress: string,
|
||||||
|
senderAddress: string,
|
||||||
|
tokenId: BigNumber,
|
||||||
|
txOpts: TransactionOpts = {},
|
||||||
|
): Promise<string> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
assert.isETHAddressHex('receiverAddress', receiverAddress);
|
||||||
|
assert.isETHAddressHex('senderAddress', senderAddress);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
const normalizedReceiverAddress = receiverAddress.toLowerCase();
|
||||||
|
const normalizedSenderAddress = senderAddress.toLowerCase();
|
||||||
|
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
|
||||||
|
const ownerAddress = await this.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
const isApprovedForAll = await this.isApprovedForAllAsync(
|
||||||
|
normalizedTokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
normalizedSenderAddress,
|
||||||
|
);
|
||||||
|
if (!isApprovedForAll) {
|
||||||
|
const approved = await this.getApprovedAsync(normalizedTokenAddress, tokenId);
|
||||||
|
if (approved !== senderAddress) {
|
||||||
|
throw new Error(ContractWrappersError.ERC721NoApproval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const txHash = await tokenContract.transferFrom.sendTransactionAsync(
|
||||||
|
ownerAddress,
|
||||||
|
normalizedReceiverAddress,
|
||||||
|
tokenId,
|
||||||
|
{
|
||||||
|
gas: txOpts.gasLimit,
|
||||||
|
gasPrice: txOpts.gasPrice,
|
||||||
|
from: normalizedSenderAddress,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Subscribe to an event type emitted by the Token contract.
|
||||||
|
* @param tokenAddress The hex encoded address where the ERC721 token is deployed.
|
||||||
|
* @param eventName The token contract event you would like to subscribe to.
|
||||||
|
* @param indexFilterValues An object where the keys are indexed args returned by the event and
|
||||||
|
* the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
|
||||||
|
* @param callback Callback that gets called when a log is added/removed
|
||||||
|
* @return Subscription token used later to unsubscribe
|
||||||
|
*/
|
||||||
|
public subscribe<ArgsType extends ERC721TokenEventArgs>(
|
||||||
|
tokenAddress: string,
|
||||||
|
eventName: ERC721TokenEvents,
|
||||||
|
indexFilterValues: IndexedFilterValues,
|
||||||
|
callback: EventCallback<ArgsType>,
|
||||||
|
): string {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents);
|
||||||
|
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
|
||||||
|
assert.isFunction('callback', callback);
|
||||||
|
const subscriptionToken = this._subscribe<ArgsType>(
|
||||||
|
normalizedTokenAddress,
|
||||||
|
eventName,
|
||||||
|
indexFilterValues,
|
||||||
|
artifacts.ERC721Token.compilerOutput.abi,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
return subscriptionToken;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Cancel a subscription
|
||||||
|
* @param subscriptionToken Subscription token returned by `subscribe()`
|
||||||
|
*/
|
||||||
|
public unsubscribe(subscriptionToken: string): void {
|
||||||
|
this._unsubscribe(subscriptionToken);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Cancels all existing subscriptions
|
||||||
|
*/
|
||||||
|
public unsubscribeAll(): void {
|
||||||
|
super._unsubscribeAll();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets historical logs without creating a subscription
|
||||||
|
* @param tokenAddress An address of the token that emitted the logs.
|
||||||
|
* @param eventName The token contract event you would like to subscribe to.
|
||||||
|
* @param blockRange Block range to get logs from.
|
||||||
|
* @param indexFilterValues An object where the keys are indexed args returned by the event and
|
||||||
|
* the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
|
||||||
|
* @return Array of logs that match the parameters
|
||||||
|
*/
|
||||||
|
public async getLogsAsync<ArgsType extends ERC721TokenEventArgs>(
|
||||||
|
tokenAddress: string,
|
||||||
|
eventName: ERC721TokenEvents,
|
||||||
|
blockRange: BlockRange,
|
||||||
|
indexFilterValues: IndexedFilterValues,
|
||||||
|
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
|
||||||
|
assert.isETHAddressHex('tokenAddress', tokenAddress);
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents);
|
||||||
|
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
|
||||||
|
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
|
||||||
|
const logs = await this._getLogsAsync<ArgsType>(
|
||||||
|
normalizedTokenAddress,
|
||||||
|
eventName,
|
||||||
|
blockRange,
|
||||||
|
indexFilterValues,
|
||||||
|
artifacts.ERC721Token.compilerOutput.abi,
|
||||||
|
);
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
private _invalidateContractInstances(): void {
|
||||||
|
this.unsubscribeAll();
|
||||||
|
this._tokenContractsByAddress = {};
|
||||||
|
}
|
||||||
|
private async _getTokenContractAsync(tokenAddress: string): Promise<ERC721TokenContract> {
|
||||||
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
||||||
|
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
|
||||||
|
if (!_.isUndefined(tokenContract)) {
|
||||||
|
return tokenContract;
|
||||||
|
}
|
||||||
|
const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
|
||||||
|
artifacts.ERC721Token,
|
||||||
|
normalizedTokenAddress,
|
||||||
|
);
|
||||||
|
const contractInstance = new ERC721TokenContract(
|
||||||
|
abi,
|
||||||
|
address,
|
||||||
|
this._web3Wrapper.getProvider(),
|
||||||
|
this._web3Wrapper.getContractDefaults(),
|
||||||
|
);
|
||||||
|
tokenContract = contractInstance;
|
||||||
|
this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract;
|
||||||
|
return tokenContract;
|
||||||
|
}
|
||||||
|
}
|
36
packages/contract-wrappers/test/erc721_proxy_wrapper_test.ts
Normal file
36
packages/contract-wrappers/test/erc721_proxy_wrapper_test.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as chai from 'chai';
|
||||||
|
import 'make-promises-safe';
|
||||||
|
|
||||||
|
import { ContractWrappers } from '../src';
|
||||||
|
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
import { constants } from './utils/constants';
|
||||||
|
import { provider } from './utils/web3_wrapper';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
describe('ERC721ProxyWrapper', () => {
|
||||||
|
let contractWrappers: ContractWrappers;
|
||||||
|
const config = {
|
||||||
|
networkId: constants.TESTRPC_NETWORK_ID,
|
||||||
|
};
|
||||||
|
before(async () => {
|
||||||
|
contractWrappers = new ContractWrappers(provider, config);
|
||||||
|
});
|
||||||
|
describe('#isAuthorizedAsync', () => {
|
||||||
|
it('should return false if the address is not authorized', async () => {
|
||||||
|
const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(constants.NULL_ADDRESS);
|
||||||
|
expect(isAuthorized).to.be.false();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getAuthorizedAddressesAsync', () => {
|
||||||
|
it('should return the list of authorized addresses', async () => {
|
||||||
|
const authorizedAddresses = await contractWrappers.erc721Proxy.getAuthorizedAddressesAsync();
|
||||||
|
for (const authorizedAddress of authorizedAddresses) {
|
||||||
|
const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(authorizedAddress);
|
||||||
|
expect(isAuthorized).to.be.true();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
460
packages/contract-wrappers/test/erc721_wrapper_test.ts
Normal file
460
packages/contract-wrappers/test/erc721_wrapper_test.ts
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils';
|
||||||
|
import { EmptyWalletSubprovider } from '@0xproject/subproviders';
|
||||||
|
import { DoneCallback } from '@0xproject/types';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import { Provider } from 'ethereum-types';
|
||||||
|
import 'make-promises-safe';
|
||||||
|
import 'mocha';
|
||||||
|
import Web3ProviderEngine = require('web3-provider-engine');
|
||||||
|
|
||||||
|
import {
|
||||||
|
BlockParamLiteral,
|
||||||
|
BlockRange,
|
||||||
|
ContractWrappers,
|
||||||
|
ContractWrappersError,
|
||||||
|
DecodedLogEvent,
|
||||||
|
ERC721TokenApprovalEventArgs,
|
||||||
|
ERC721TokenApprovalForAllEventArgs,
|
||||||
|
ERC721TokenEvents,
|
||||||
|
ERC721TokenTransferEventArgs,
|
||||||
|
} from '../src';
|
||||||
|
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
import { constants } from './utils/constants';
|
||||||
|
import { tokenUtils } from './utils/token_utils';
|
||||||
|
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('ERC721Wrapper', () => {
|
||||||
|
let contractWrappers: ContractWrappers;
|
||||||
|
let userAddresses: string[];
|
||||||
|
let tokens: string[];
|
||||||
|
let ownerAddress: string;
|
||||||
|
let tokenAddress: string;
|
||||||
|
let anotherOwnerAddress: string;
|
||||||
|
let operatorAddress: string;
|
||||||
|
let approvedAddress: string;
|
||||||
|
let receiverAddress: string;
|
||||||
|
const config = {
|
||||||
|
networkId: constants.TESTRPC_NETWORK_ID,
|
||||||
|
};
|
||||||
|
before(async () => {
|
||||||
|
contractWrappers = new ContractWrappers(provider, config);
|
||||||
|
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
tokens = tokenUtils.getDummyERC721TokenAddresses();
|
||||||
|
tokenAddress = tokens[0];
|
||||||
|
[ownerAddress, operatorAddress, anotherOwnerAddress, approvedAddress, receiverAddress] = userAddresses;
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
describe('#transferFromAsync', () => {
|
||||||
|
it('should fail to transfer NFT if fromAddress has no approvals set', async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
return expect(
|
||||||
|
contractWrappers.erc721Token.transferFromAsync(tokenAddress, receiverAddress, approvedAddress, tokenId),
|
||||||
|
).to.be.rejectedWith(ContractWrappersError.ERC721NoApproval);
|
||||||
|
});
|
||||||
|
it('should successfully transfer tokens when sender is an approved address', async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
let txHash = await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
expect(owner).to.be.equal(ownerAddress);
|
||||||
|
txHash = await contractWrappers.erc721Token.transferFromAsync(
|
||||||
|
tokenAddress,
|
||||||
|
receiverAddress,
|
||||||
|
approvedAddress,
|
||||||
|
tokenId,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
expect(newOwner).to.be.equal(receiverAddress);
|
||||||
|
});
|
||||||
|
it('should successfully transfer tokens when sender is an approved operator', async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
const isApprovedForAll = true;
|
||||||
|
let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
isApprovedForAll,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
expect(owner).to.be.equal(ownerAddress);
|
||||||
|
txHash = await contractWrappers.erc721Token.transferFromAsync(
|
||||||
|
tokenAddress,
|
||||||
|
receiverAddress,
|
||||||
|
operatorAddress,
|
||||||
|
tokenId,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
expect(newOwner).to.be.equal(receiverAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getTokenCountAsync', () => {
|
||||||
|
describe('With provider with accounts', () => {
|
||||||
|
it('should return the count for an existing ERC721 token', async () => {
|
||||||
|
let tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress);
|
||||||
|
expect(tokenCount).to.be.bignumber.equal(0);
|
||||||
|
await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress);
|
||||||
|
expect(tokenCount).to.be.bignumber.equal(1);
|
||||||
|
});
|
||||||
|
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
|
||||||
|
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
|
||||||
|
return expect(
|
||||||
|
contractWrappers.erc721Token.getTokenCountAsync(nonExistentTokenAddress, ownerAddress),
|
||||||
|
).to.be.rejectedWith(ContractWrappersError.ERC721TokenContractDoesNotExist);
|
||||||
|
});
|
||||||
|
it('should return a balance of 0 for a non-existent owner address', async () => {
|
||||||
|
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
|
||||||
|
const balance = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, nonExistentOwner);
|
||||||
|
const expectedBalance = new BigNumber(0);
|
||||||
|
return expect(balance).to.be.bignumber.equal(expectedBalance);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('With provider without accounts', () => {
|
||||||
|
let zeroExContractWithoutAccounts: ContractWrappers;
|
||||||
|
before(async () => {
|
||||||
|
const emptyWalletProvider = addEmptyWalletSubprovider(provider);
|
||||||
|
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
|
||||||
|
});
|
||||||
|
it('should return balance even when called with provider instance without addresses', async () => {
|
||||||
|
const balance = await zeroExContractWithoutAccounts.erc721Token.getTokenCountAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
);
|
||||||
|
return expect(balance).to.be.bignumber.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getOwnerOfAsync', () => {
|
||||||
|
it('should return the owner for an existing ERC721 token', async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
const tokenOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
|
||||||
|
expect(tokenOwner).to.be.bignumber.equal(ownerAddress);
|
||||||
|
});
|
||||||
|
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
|
||||||
|
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
|
||||||
|
const fakeTokenId = new BigNumber(42);
|
||||||
|
return expect(
|
||||||
|
contractWrappers.erc721Token.getOwnerOfAsync(nonExistentTokenAddress, fakeTokenId),
|
||||||
|
).to.be.rejectedWith(ContractWrappersError.ERC721TokenContractDoesNotExist);
|
||||||
|
});
|
||||||
|
it('should return undefined not 0 for a non-existent ERC721', async () => {
|
||||||
|
const fakeTokenId = new BigNumber(42);
|
||||||
|
return expect(contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, fakeTokenId)).to.be.rejectedWith(
|
||||||
|
ContractWrappersError.ERC721OwnerNotFound,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#setApprovalForAllAsync/isApprovedForAllAsync', () => {
|
||||||
|
it('should check if operator address is approved', async () => {
|
||||||
|
let isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
);
|
||||||
|
expect(isApprovedForAll).to.be.false();
|
||||||
|
// set
|
||||||
|
let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
);
|
||||||
|
expect(isApprovedForAll).to.be.true();
|
||||||
|
// usnset
|
||||||
|
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
);
|
||||||
|
expect(isApprovedForAll).to.be.false();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#setProxyApprovalForAllAsync/isProxyApprovedForAllAsync', () => {
|
||||||
|
it('should check if proxy address is approved', async () => {
|
||||||
|
const txHash = await contractWrappers.erc721Token.setProxyApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const isApprovedForAll = await contractWrappers.erc721Token.isProxyApprovedForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
);
|
||||||
|
expect(isApprovedForAll).to.be.true();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#setApprovalAsync/getApprovedAsync', () => {
|
||||||
|
it("should set the spender's approval", async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
|
||||||
|
const approvalBeforeSet = await contractWrappers.erc721Token.getApprovedAsync(tokenAddress, tokenId);
|
||||||
|
expect(approvalBeforeSet).to.be.undefined();
|
||||||
|
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId);
|
||||||
|
const approvalAfterSet = await contractWrappers.erc721Token.getApprovedAsync(tokenAddress, tokenId);
|
||||||
|
expect(approvalAfterSet).to.be.equal(approvedAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#setProxyApprovalAsync/isProxyApprovedAsync', () => {
|
||||||
|
it('should set the proxy approval', async () => {
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
|
||||||
|
const approvalBeforeSet = await contractWrappers.erc721Token.isProxyApprovedAsync(tokenAddress, tokenId);
|
||||||
|
expect(approvalBeforeSet).to.be.false();
|
||||||
|
await contractWrappers.erc721Token.setProxyApprovalAsync(tokenAddress, tokenId);
|
||||||
|
const approvalAfterSet = await contractWrappers.erc721Token.isProxyApprovedAsync(tokenAddress, tokenId);
|
||||||
|
expect(approvalAfterSet).to.be.true();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#subscribe', () => {
|
||||||
|
const indexFilterValues = {};
|
||||||
|
afterEach(() => {
|
||||||
|
contractWrappers.erc721Token.unsubscribeAll();
|
||||||
|
});
|
||||||
|
// Hack: Mocha does not allow a test to be both async and have a `done` callback
|
||||||
|
// Since we need to await the receipt of the event in the `subscribe` callback,
|
||||||
|
// we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
|
||||||
|
// wrap the rest of the test in an async block
|
||||||
|
// Source: https://github.com/mochajs/mocha/issues/2407
|
||||||
|
it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
|
||||||
|
(async () => {
|
||||||
|
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||||
|
(logEvent: DecodedLogEvent<ERC721TokenTransferEventArgs>) => {
|
||||||
|
expect(logEvent.isRemoved).to.be.false();
|
||||||
|
expect(logEvent.log.logIndex).to.be.equal(0);
|
||||||
|
expect(logEvent.log.transactionIndex).to.be.equal(0);
|
||||||
|
expect(logEvent.log.blockNumber).to.be.a('number');
|
||||||
|
const args = logEvent.log.args;
|
||||||
|
expect(args._from).to.be.equal(ownerAddress);
|
||||||
|
expect(args._to).to.be.equal(receiverAddress);
|
||||||
|
expect(args._tokenId).to.be.bignumber.equal(tokenId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
const isApprovedForAll = true;
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
isApprovedForAll,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
contractWrappers.erc721Token.subscribe(
|
||||||
|
tokenAddress,
|
||||||
|
ERC721TokenEvents.Transfer,
|
||||||
|
indexFilterValues,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await contractWrappers.erc721Token.transferFromAsync(
|
||||||
|
tokenAddress,
|
||||||
|
receiverAddress,
|
||||||
|
operatorAddress,
|
||||||
|
tokenId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})().catch(done);
|
||||||
|
});
|
||||||
|
it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
|
||||||
|
(async () => {
|
||||||
|
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||||
|
(logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => {
|
||||||
|
expect(logEvent).to.not.be.undefined();
|
||||||
|
expect(logEvent.isRemoved).to.be.false();
|
||||||
|
const args = logEvent.log.args;
|
||||||
|
expect(args._owner).to.be.equal(ownerAddress);
|
||||||
|
expect(args._approved).to.be.equal(approvedAddress);
|
||||||
|
expect(args._tokenId).to.be.bignumber.equal(tokenId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
contractWrappers.erc721Token.subscribe(
|
||||||
|
tokenAddress,
|
||||||
|
ERC721TokenEvents.Approval,
|
||||||
|
indexFilterValues,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId),
|
||||||
|
);
|
||||||
|
})().catch(done);
|
||||||
|
});
|
||||||
|
it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => {
|
||||||
|
(async () => {
|
||||||
|
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||||
|
(logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => {
|
||||||
|
done(new Error('Expected this subscription to have been cancelled'));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
contractWrappers.erc721Token.subscribe(
|
||||||
|
tokenAddress,
|
||||||
|
ERC721TokenEvents.Transfer,
|
||||||
|
indexFilterValues,
|
||||||
|
callbackNeverToBeCalled,
|
||||||
|
);
|
||||||
|
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
|
||||||
|
contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID);
|
||||||
|
contractWrappers.erc721Token.subscribe(
|
||||||
|
tokenAddress,
|
||||||
|
ERC721TokenEvents.Approval,
|
||||||
|
indexFilterValues,
|
||||||
|
callbackToBeCalled,
|
||||||
|
);
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId),
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
})().catch(done);
|
||||||
|
});
|
||||||
|
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
|
||||||
|
(async () => {
|
||||||
|
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||||
|
(logEvent: DecodedLogEvent<ERC721TokenApprovalForAllEventArgs>) => {
|
||||||
|
done(new Error('Expected this subscription to have been cancelled'));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const subscriptionToken = contractWrappers.erc721Token.subscribe(
|
||||||
|
tokenAddress,
|
||||||
|
ERC721TokenEvents.ApprovalForAll,
|
||||||
|
indexFilterValues,
|
||||||
|
callbackNeverToBeCalled,
|
||||||
|
);
|
||||||
|
contractWrappers.erc721Token.unsubscribe(subscriptionToken);
|
||||||
|
|
||||||
|
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
|
||||||
|
const isApproved = true;
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
isApproved,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
})().catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getLogsAsync', () => {
|
||||||
|
let tokenTransferProxyAddress: string;
|
||||||
|
const blockRange: BlockRange = {
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: BlockParamLiteral.Latest,
|
||||||
|
};
|
||||||
|
let txHash: string;
|
||||||
|
before(() => {
|
||||||
|
tokenTransferProxyAddress = contractWrappers.erc721Proxy.getContractAddress();
|
||||||
|
});
|
||||||
|
it('should get logs with decoded args emitted by ApprovalForAll', async () => {
|
||||||
|
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const eventName = ERC721TokenEvents.ApprovalForAll;
|
||||||
|
const indexFilterValues = {};
|
||||||
|
const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>(
|
||||||
|
tokenAddress,
|
||||||
|
eventName,
|
||||||
|
blockRange,
|
||||||
|
indexFilterValues,
|
||||||
|
);
|
||||||
|
expect(logs).to.have.length(1);
|
||||||
|
const args = logs[0].args;
|
||||||
|
expect(logs[0].event).to.be.equal(eventName);
|
||||||
|
expect(args._owner).to.be.equal(ownerAddress);
|
||||||
|
expect(args._operator).to.be.equal(operatorAddress);
|
||||||
|
expect(args._approved).to.be.equal(true);
|
||||||
|
});
|
||||||
|
it('should only get the logs with the correct event name', async () => {
|
||||||
|
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const differentEventName = ERC721TokenEvents.Transfer;
|
||||||
|
const indexFilterValues = {};
|
||||||
|
const logs = await contractWrappers.erc721Token.getLogsAsync(
|
||||||
|
tokenAddress,
|
||||||
|
differentEventName,
|
||||||
|
blockRange,
|
||||||
|
indexFilterValues,
|
||||||
|
);
|
||||||
|
expect(logs).to.have.length(0);
|
||||||
|
});
|
||||||
|
it('should only get the logs with the correct indexed fields', async () => {
|
||||||
|
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
ownerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
|
||||||
|
tokenAddress,
|
||||||
|
anotherOwnerAddress,
|
||||||
|
operatorAddress,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||||
|
const eventName = ERC721TokenEvents.ApprovalForAll;
|
||||||
|
const indexFilterValues = {
|
||||||
|
_owner: anotherOwnerAddress,
|
||||||
|
};
|
||||||
|
const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>(
|
||||||
|
tokenAddress,
|
||||||
|
eventName,
|
||||||
|
blockRange,
|
||||||
|
indexFilterValues,
|
||||||
|
);
|
||||||
|
expect(logs).to.have.length(1);
|
||||||
|
const args = logs[0].args;
|
||||||
|
expect(args._owner).to.be.equal(anotherOwnerAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable:max-file-line-count
|
||||||
|
|
||||||
|
function addEmptyWalletSubprovider(p: Provider): Provider {
|
||||||
|
const providerEngine = new Web3ProviderEngine();
|
||||||
|
providerEngine.addProvider(new EmptyWalletSubprovider());
|
||||||
|
const currentSubproviders = (p as any)._providers;
|
||||||
|
for (const subprovider of currentSubproviders) {
|
||||||
|
providerEngine.addProvider(subprovider);
|
||||||
|
}
|
||||||
|
providerEngine.start();
|
||||||
|
return providerEngine;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user