import { SchemaValidator } from '@0xproject/json-schemas';import { BigNumber, intervalUtils } from '@0xproject/utils';import { Web3Wrapper } from '@0xproject/web3-wrapper';import * as _ from 'lodash';import { ECSignature, Network, Order, SignedOrder, TransactionReceiptWithDecodedLogs, Web3Provider, WyvernProtocolConfig, WyvernProtocolError,} from './types';import { schemas } from './schemas';import { AbiDecoder } from './utils/abi_decoder';import { assert } from './utils/assert';import { constants } from './utils/constants';import { decorators } from './utils/decorators';import { signatureUtils } from './utils/signature_utils';import { utils } from './utils/utils';import { WyvernAtomicizerContract } from './abi_gen/wyvern_atomicizer';import { WyvernDAOContract } from './abi_gen/wyvern_d_a_o';import { WyvernExchangeContract } from './abi_gen/wyvern_exchange';import { WyvernProxyRegistryContract } from './abi_gen/wyvern_proxy_registry';import { WyvernTokenContract } from './abi_gen/wyvern_token';export class WyvernProtocol { public static NULL_ADDRESS = constants.NULL_ADDRESS; public static MAX_UINT_256 = new BigNumber(2).pow(256).sub(1); public wyvernExchange: WyvernExchangeContract; public wyvernProxyRegistry: WyvernProxyRegistryContract; public wyvernDAO: WyvernDAOContract; public wyvernToken: WyvernTokenContract; public wyvernAtomicizer: WyvernAtomicizerContract; private _web3Wrapper: Web3Wrapper; private _abiDecoder: AbiDecoder; public static getExchangeContractAddress(network: Network): string { return constants.DEPLOYED[network].WyvernExchange; } public static getProxyRegistryContractAddress(network: Network): string { return constants.DEPLOYED[network].WyvernProxyRegistry; } public static getTokenContractAddress(network: Network): string { return constants.DEPLOYED[network].WyvernToken; } public static getDAOContractAddress(network: Network): string { return constants.DEPLOYED[network].WyvernDAO; } public static getAtomicizerContractAddress(network: Network): string { return constants.DEPLOYED[network].WyvernAtomicizer; } public static getTokenTransferProxyAddress(network: Network): string { return constants.DEPLOYED[network].WyvernTokenTransferProxy; } /** * 验证椭圆曲线签名' signature '是通过使用 * ' signerAddress '地址对应的私钥对' data '进行签名生成的。 * @param data 由所提供的签名签名的十六进制编码数据。 * @param signature 包含椭圆曲线签名参数的对象。 * @param signerAddress 十六进制编码地址签名的数据,产生提供的签名。 * @return 该签名对所提供的签名地址和数据有效。 */ public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean { assert.isHexString('data', data); assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); assert.isETHAddressHex('signerAddress', signerAddress); const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress); return isValidSignature; } /** * 生成一个伪随机256位盐。 * salt可以包含在一个0x顺序中,以确保该顺序生成唯一的orderHash * 并且不会与其他所有参数相同的未完成订单发生冲突。 * @return 一个伪随机的256位数字,可以用作salt。 */ public static generatePseudoRandomSalt(): BigNumber { // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places. // Source: https://mikemcl.github.io/bignumber.js/#random const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT); const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1); const salt = randomNumber.times(factor).round(); return salt; } /** * 如果提供的十六进制编码的订单散列有效,则表示符合。 * 注意:Valid表示它具有预期的格式,而不是表示存在带有orderHash的订单。 * 在处理作为用户输入提交的orderhash时使用此方法。 * @param orderHash 十六进制编码orderHash. * @return 提供的orderHash是否有预期的格式。 */ public static isValidOrderHash(orderHash: string): boolean { // Since this method can be called to check if any arbitrary string conforms to an orderHash's // format, we only assert that we were indeed passed a string. assert.isString('orderHash', orderHash); const schemaValidator = new SchemaValidator(); const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid; return isValidOrderHash; } /** * 单位量定义为位于指定小数点(整数部分)上方的Token数量。 * 例:如果一种货币有18位小数,1e18或该货币的1 × 10 × 10次方等于1个单位。 * to 1 unit. * @param amount 您想要转换为单位的基本单位的数量。 * @param decimals 单位数量的小数位数。 * @return 金额单位 */ public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber { assert.isValidBaseUnitAmount('amount', amount); assert.isNumber('decimals', decimals); const aUnit = new BigNumber(10).pow(decimals); const unit = amount.div(aUnit); return unit; } /** * baseUnit被定义为Token的最小单位。 * 用基本单位表示的量是用最小的单位表示的量。 * 例如:一个token的1个单位有18位小数,用baseUnits表示为1000000000000000000 * @param amount 您想要转换为基本单位的单位数量。 * @param decimals 单位数量的小数位数。 * @return 金额单位为基础单位。 */ public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber { assert.isBigNumber('amount', amount); assert.isNumber('decimals', decimals); const unit = new BigNumber(10).pow(decimals); const baseUnitAmount = amount.times(unit); const hasDecimals = baseUnitAmount.decimalPlaces() !== 0; if (hasDecimals) { throw new Error(`Invalid unit amount: ${amount.toString()} - Too many decimal places`); } return baseUnitAmount; } /** * 计算所提供订单的orderHash。 * @param order 符合Order或SignedOrder接口定义的对象。 * @return 通过对提供的订单进行散列得到的orderHash。 */ @decorators.syncWyvernProtocolErrorHandler public static getOrderHashHex(order: Order | SignedOrder): string { assert.doesConformToSchema('order', order, schemas.orderSchema); const orderHashHex = utils.getOrderHashHex(order); return orderHashHex; } /** * 计算提供的资产的assetHash。 */ public static getAssetHashHex(assetHash: string, schema: string): string { const assetHashHex = utils.getAssetHashHex(assetHash, schema); return assetHashHex; } constructor(provider: Web3Provider, config: WyvernProtocolConfig) { assert.isWeb3Provider('provider', provider); // assert.doesConformToSchema('config', config, wyvernProtocolConfigSchema) this._web3Wrapper = new Web3Wrapper(provider, { gasPrice: config.gasPrice }); const exchangeContractAddress = config.wyvernExchangeContractAddress || WyvernProtocol.getExchangeContractAddress(config.network); this.wyvernExchange = new WyvernExchangeContract( this._web3Wrapper.getContractInstance((constants.EXCHANGE_ABI as any), exchangeContractAddress), {}, ); // 代理注册合约地址 const proxyRegistryContractAddress = config.wyvernProxyRegistryContractAddress || WyvernProtocol.getProxyRegistryContractAddress(config.network); this.wyvernProxyRegistry = new WyvernProxyRegistryContract( this._web3Wrapper.getContractInstance((constants.PROXY_REGISTRY_ABI as any), proxyRegistryContractAddress), {}, ); const daoContractAddress = config.wyvernDAOContractAddress || WyvernProtocol.getDAOContractAddress(config.network); this.wyvernDAO = new WyvernDAOContract( this._web3Wrapper.getContractInstance((constants.DAO_ABI as any), daoContractAddress), {}, ); // 代币合约地址 const tokenContractAddress = config.wyvernTokenContractAddress || WyvernProtocol.getTokenContractAddress(config.network); this.wyvernToken = new WyvernTokenContract( this._web3Wrapper.getContractInstance((constants.TOKEN_ABI as any), tokenContractAddress), {}, ); // atomicizer 合约地址 const atomicizerContractAddress = config.wyvernAtomicizerContractAddress || WyvernProtocol.getAtomicizerContractAddress(config.network); this.wyvernAtomicizer = new WyvernAtomicizerContract( this._web3Wrapper.getContractInstance((constants.ATOMICIZER_ABI as any), atomicizerContractAddress), {}, ); } /** * 为 wyvernProtocol.js 设置一个新的 web3 提供程序。 * 更新提供程序将停止所有订阅,因此您需要在此调用后重新订阅与您的应用程序相关的所有事件。 * @param provider wyvernProtocol.js 库从现在开始使用的 Web3Provider。 * @param networkId 网络提供商连接到ID */ public setProvider(provider: Web3Provider, networkId: number): void { this._web3Wrapper.setProvider(provider); (this.wyvernExchange as any)._invalidateContractInstances(); (this.wyvernExchange as any)._setNetworkId(networkId); (this.wyvernProxyRegistry as any)._invalidateContractInstance(); (this.wyvernProxyRegistry as any)._setNetworkId(networkId); } /** * 通过提供的用于发送交易的web3提供商获取可用的用户以太坊地址。 * @return 可用的用户以太坊地址数组。 */ public async getAvailableAddressesAsync(): Promise<string[]> { const availableAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); return availableAddresses; } /** * 对orderHash进行签名并返回其椭圆曲线签名。 * 方法目前支持TestRPC、Geth和奇偶校验V1.6.6或更高或更低 * @param orderHash 要签名的已编码的orderHash。 * @param signerAddress 用十六进制编码的以太网地址签名。此地址必须通过Web3可用。提供给wyvernProtocol.js的提供商。 * @return 生成一个包含通过签名orderHash生成的椭圆曲线签名参数的对象。 */ public async signOrderHashAsync(orderHash: string, signerAddress: string): Promise<ECSignature> { assert.isHexString('orderHash', orderHash); /* await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper); */ /* let msgHashHex; const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); const isParityNode = utils.isParityNode(nodeVersion); const isTestRpc = utils.isTestRpc(nodeVersion); if (isParityNode || isTestRpc) { // Parity and TestRpc nodes add the personalMessage prefix itself msgHashHex = orderHash; } else { const orderHashBuff = ethUtil.toBuffer(orderHash); const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff); msgHashHex = ethUtil.bufferToHex(msgHashBuff); } */ const msgHashHex = orderHash; const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex); // HACK: There is no consensus on whether the signatureHex string should be formatted as // v + r + s OR r + s + v, and different clients (even different versions of the same client) // return the signature params in different orders. In order to support all client implementations, // we parse the signature in both ways, and evaluate if either one is a valid signature. const validVParamValues = [27, 28]; const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature); if (_.includes(validVParamValues, ecSignatureVRS.v)) { const isValidVRSSignature = WyvernProtocol.isValidSignature(orderHash, ecSignatureVRS, signerAddress); if (isValidVRSSignature) { return ecSignatureVRS; } } const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature); if (_.includes(validVParamValues, ecSignatureRSV.v)) { const isValidRSVSignature = WyvernProtocol.isValidSignature(orderHash, ecSignatureRSV, signerAddress); if (isValidRSVSignature) { return ecSignatureRSV; } } throw new Error(WyvernProtocolError.InvalidSignature); } /** * 等待一个事务是mined并返回交易收据。 * @param txHash 交易hash * @param pollingIntervalMs 我们应该多久检查一次交易是否被挖掘。 * @param timeoutMs 为挖掘事务而轮询多长时间(以毫秒为单位),直到终止。 * @return 事务日志参数接收与解码。 */ public async awaitTransactionMinedAsync( txHash: string, pollingIntervalMs = 1000, timeoutMs?: number, ): Promise<TransactionReceiptWithDecodedLogs> { let timeoutExceeded = false; if (timeoutMs) { setTimeout(() => (timeoutExceeded = true), timeoutMs); } const txReceiptPromise = new Promise( (resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => { const intervalId = intervalUtils.setAsyncExcludingInterval(async () => { if (timeoutExceeded) { intervalUtils.clearAsyncExcludingInterval(intervalId); return reject(WyvernProtocolError.TransactionMiningTimeout); } const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash); if (!_.isNull(transactionReceipt)) { intervalUtils.clearAsyncExcludingInterval(intervalId); const logsWithDecodedArgs = _.map( transactionReceipt.logs, this._abiDecoder.tryToDecodeLogOrNoop.bind(this._abiDecoder), ); const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = { ...transactionReceipt, logs: logsWithDecodedArgs, }; resolve(transactionReceiptWithDecodedLogArgs); } }, pollingIntervalMs, () => ({})); }, ); return txReceiptPromise; }}