diff --git a/package.json b/package.json index 6160ef0..3c5666e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "protocol2-js", - "version": "0.2.20", + "version": "0.2.21", "description": "loopring protocol simulator core lib", "main": "index.ts", "repository": { diff --git a/src/exchange_deserializer.ts b/src/exchange_deserializer.ts index 057eca9..7ac5556 100644 --- a/src/exchange_deserializer.ts +++ b/src/exchange_deserializer.ts @@ -19,6 +19,8 @@ export class ExchangeDeserializer { private dataOffset: number = 0; private tableOffset: number = 0; + private zeroBytes32 = "0x" + "0".repeat(64); + constructor(context: Context) { this.context = context; } @@ -40,7 +42,7 @@ export class ExchangeDeserializer { // Calculate data pointers const miningDataPtr = 8; const orderDataPtr = miningDataPtr + 3 * 2; - const ringDataPtr = orderDataPtr + (24 * numOrders) * 2; + const ringDataPtr = orderDataPtr + (30 * numOrders) * 2; const dataBlobPtr = ringDataPtr + (numRings * 9) + 32; this.spendableList = []; @@ -112,12 +114,21 @@ export class ExchangeDeserializer { tokenBFeePercentage: this.nextUint16(), tokenRecipient: this.nextAddress(), walletSplitPercentage: this.nextUint16(), + tokenTypeS: this.nextUint16(), + tokenTypeB: this.nextUint16(), + tokenTypeFee: this.nextUint16(), + trancheS: this.nextBytes32(), + trancheB: this.nextBytes32(), + transferDataS: this.nextBytes(), }; if (this.context) { order.feeToken = order.feeToken ? order.feeToken : this.context.lrcAddress; } order.tokenRecipient = order.tokenRecipient ? order.tokenRecipient : order.owner; + order.trancheS = order.trancheS ? order.trancheS : this.zeroBytes32; + order.trancheB = order.trancheB ? order.trancheB : this.zeroBytes32; + order.transferDataS = order.transferDataS ? order.transferDataS : "0x"; return order; } @@ -192,6 +203,16 @@ export class ExchangeDeserializer { } } + private nextBytes32() { + const offset = this.getNextOffset() * 4; + if (offset !== 0) { + const data = "0x" + this.data.extractBytesX(this.dataOffset + offset, 32).toString("hex"); + return data; + } else { + return "0x" + "0".repeat(64); + } + } + private toInt16(x: number) { // Check if the number is negative if (x >> 15 === 1) { diff --git a/src/expectThrow.ts b/src/expectThrow.ts index 1534251..a2b3c39 100644 --- a/src/expectThrow.ts +++ b/src/expectThrow.ts @@ -4,7 +4,7 @@ export async function expectThrow(promise: Promise, expectedRevertMessage?: } catch (error) { if (expectedRevertMessage) { - const message = error.message.search("revert " + expectedRevertMessage) >= 0; + const message = error.message.search(expectedRevertMessage) >= 0; assert(message, "Expected throw with message " + expectedRevertMessage + ", got '" + error + "' instead"); } else { const revert = error.message.search("revert") >= 0; diff --git a/src/order.ts b/src/order.ts index 463e52a..db2ee25 100644 --- a/src/order.ts +++ b/src/order.ts @@ -6,12 +6,14 @@ import { Context } from "./context"; import { getEIP712Message } from "./eip712"; import { ensure } from "./ensure"; import { MultiHashUtil } from "./multihash"; -import { OrderInfo, Spendable } from "./types"; +import { OrderInfo, Spendable, TokenType } from "./types"; export class OrderUtil { private context: Context; + private zeroBytes32 = "0x" + "0".repeat(64); + constructor(context: Context) { this.context = context; } @@ -51,6 +53,28 @@ export class OrderUtil { valid = valid && ensure(order.validSince <= blockTimestamp, "order is too early to match"); valid = valid && ensure(order.validUntil ? order.validUntil > blockTimestamp : true, "order is expired"); + // We only support ERC20 for now + valid = valid && ensure( + (order.tokenTypeS === TokenType.ERC20 && order.trancheS === this.zeroBytes32), + "invalid trancheS", + ); + valid = valid && ensure( + (order.tokenTypeB === TokenType.ERC20 && order.trancheB === this.zeroBytes32), + "invalid trancheB", + ); + valid = valid && ensure( + (order.tokenTypeFee === TokenType.ERC20), + "invalid tokenTypeFee", + ); + valid = valid && ensure( + (order.tokenTypeS === TokenType.ERC20 && order.transferDataS === "0x"), + "invalid transferDataS", + ); + // This emulates the revert in solidity with invalid enum values + assert(order.tokenTypeS < TokenType.COUNT, "invalid opcode"); + assert(order.tokenTypeB < TokenType.COUNT, "invalid opcode"); + assert(order.tokenTypeFee < TokenType.COUNT, "invalid opcode"); + order.valid = order.valid && valid; } @@ -110,6 +134,12 @@ export class OrderUtil { { name: "tokenSFeePercentage", type: "uint16" }, { name: "tokenBFeePercentage", type: "uint16" }, { name: "allOrNone", type: "bool" }, + { name: "tokenTypeS", type: "uint8" }, + { name: "tokenTypeB", type: "uint8" }, + { name: "tokenTypeFee", type: "uint8" }, + { name: "trancheS", type: "bytes32" }, + { name: "trancheB", type: "bytes32" }, + { name: "transferDataS", type: "bytes" }, ], }, primaryType: "Order", @@ -126,16 +156,22 @@ export class OrderUtil { owner: order.owner, tokenS: order.tokenS, tokenB: order.tokenB, - dualAuthAddr: order.dualAuthAddr ? order.dualAuthAddr : "0x0", - broker: order.broker ? order.broker : "0x0", - orderInterceptor: order.orderInterceptor ? order.orderInterceptor : "0x0", - wallet: order.walletAddr ? order.walletAddr : "0x0", + dualAuthAddr: order.dualAuthAddr ? order.dualAuthAddr : "", + broker: order.broker ? order.broker : "", + orderInterceptor: order.orderInterceptor ? order.orderInterceptor : "", + wallet: order.walletAddr ? order.walletAddr : "", tokenRecipient: order.tokenRecipient, feeToken: order.feeToken, walletSplitPercentage: order.walletSplitPercentage, tokenSFeePercentage: order.tokenSFeePercentage, tokenBFeePercentage: order.tokenBFeePercentage, allOrNone: order.allOrNone, + tokenTypeS: order.tokenTypeS ? order.tokenTypeS : TokenType.ERC20, + tokenTypeB: order.tokenTypeB ? order.tokenTypeB : TokenType.ERC20, + tokenTypeFee: order.tokenTypeFee ? order.tokenTypeFee : TokenType.ERC20, + trancheS: order.trancheS ? order.trancheS : "", + trancheB: order.trancheB ? order.trancheB : "", + transferDataS: order.transferDataS ? order.transferDataS : "", }, }; return typedData; @@ -163,6 +199,7 @@ export class OrderUtil { public toOrderBookSubmitParams(orderInfo: OrderInfo) { const emptyAddr = "0x" + "0".repeat(40); + const zeroBytes32 = "0x" + "0".repeat(64); const data = new Bitstream(); data.addAddress(orderInfo.owner, 32); @@ -183,6 +220,17 @@ export class OrderUtil { data.addNumber(orderInfo.tokenBFeePercentage ? orderInfo.tokenBFeePercentage : 0, 32); data.addAddress(orderInfo.tokenRecipient ? orderInfo.tokenRecipient : orderInfo.owner, 32); data.addNumber(orderInfo.walletSplitPercentage ? orderInfo.walletSplitPercentage : 0, 32); + data.addNumber(orderInfo.tokenTypeS ? orderInfo.tokenTypeS : TokenType.ERC20, 32); + data.addNumber(orderInfo.tokenTypeB ? orderInfo.tokenTypeB : TokenType.ERC20, 32); + data.addNumber(orderInfo.tokenTypeFee ? orderInfo.tokenTypeFee : TokenType.ERC20, 32); + data.addHex(orderInfo.trancheS ? orderInfo.trancheS : zeroBytes32); + data.addHex(orderInfo.trancheB ? orderInfo.trancheB : zeroBytes32); + if (orderInfo.transferDataS) { + data.addNumber((orderInfo.transferDataS.length - 2) / 2, 32); + data.addHex(orderInfo.transferDataS); + } else { + data.addNumber(0, 32); + } return data.getData(); } diff --git a/src/rings_generator.ts b/src/rings_generator.ts index d6c3a7f..db47db3 100644 --- a/src/rings_generator.ts +++ b/src/rings_generator.ts @@ -20,6 +20,8 @@ export class RingsGenerator { private SERIALIZATION_VERSION = 0; private ORDER_VERSION = 0; + private zeroAddress = "0x" + "0".repeat(64); + constructor(context: Context) { this.context = context; this.orderUtil = new OrderUtil(context); @@ -299,6 +301,29 @@ export class RingsGenerator { } param.tables.addNumber(order.walletSplitPercentage ? order.walletSplitPercentage : 0, 2); + + param.tables.addNumber(order.tokenTypeS, 2); + param.tables.addNumber(order.tokenTypeB, 2); + param.tables.addNumber(order.tokenTypeFee, 2); + + if (order.trancheS && order.trancheS !== "0x0" && order.trancheS !== this.zeroAddress) { + this.insertOffset(param, param.data.addHex(order.trancheS, false)); + } else { + this.insertDefault(param); + } + + if (order.trancheB && order.trancheB !== "0x0" && order.trancheB !== this.zeroAddress) { + this.insertOffset(param, param.data.addHex(order.trancheB, false)); + } else { + this.insertDefault(param); + } + + if (order.transferDataS && order.transferDataS !== "0x" && order.transferDataS !== "") { + this.insertOffset(param, param.data.addHex(this.createBytes(order.transferDataS), false)); + this.addPadding(param); + } else { + this.insertDefault(param); + } } private xor(s1: string, s2: string, numBytes: number) { diff --git a/src/types.ts b/src/types.ts index cdef60c..027f696 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,6 +11,11 @@ export enum SignAlgorithm { None = 255, // Do not sign } +export enum TokenType { + ERC20 = 0, + COUNT = 1, +} + export interface Spendable { initialized?: boolean; amount?: BigNumber; @@ -53,6 +58,13 @@ export interface OrderInfo { tokenRecipient?: string; walletSplitPercentage?: number; + tokenTypeS?: TokenType; + tokenTypeB?: TokenType; + tokenTypeFee?: TokenType; + trancheS?: string; + trancheB?: string; + transferDataS?: string; + // helper field P2P?: boolean; filledAmountS?: BigNumber;