From f6cf3de1c4cabc0fcd6a4bb98948096c4924c2bd Mon Sep 17 00:00:00 2001
From: Greg Hysen <greg.hysen@gmail.com>
Date: Wed, 7 Nov 2018 11:31:49 -0800
Subject: Implemented int

---
 packages/order-utils/test/abi_encoder_test.ts | 105 +++++++++++++++++++++-----
 1 file changed, 87 insertions(+), 18 deletions(-)

diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts
index 76a6da00e..b557f605d 100644
--- a/packages/order-utils/test/abi_encoder_test.ts
+++ b/packages/order-utils/test/abi_encoder_test.ts
@@ -371,29 +371,71 @@ namespace AbiEncoder {
             '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$',
         );
 
-        static DEFAULT_WIDTH = new BigNumber(1);
-        width: BigNumber = Byte.DEFAULT_WIDTH;
+        static DEFAULT_WIDTH: number = 256;
+        width: number = Int.DEFAULT_WIDTH;
 
         constructor(dataItem: DataItem) {
             super(dataItem);
-            const matches = Byte.matcher.exec(dataItem.type);
+            const matches = Int.matcher.exec(dataItem.type);
             expect(matches).to.be.not.null();
-            if (matches !== null && matches.length === 2) {
-                this.width = new BigNumber(matches[1], 10);
+            if (matches !== null && matches.length === 2 && matches[1] !== undefined) {
+                this.width = parseInt(matches[1]);
+            } else {
+                this.width = 256;
             }
         }
 
-        public encodeToCalldata(calldata: Calldata): void {
-            throw 2;
+        public getMaxValue(): BigNumber {
+            return new BigNumber(2).toPower(this.width - 1).sub(1);
         }
 
-        public assignValue(value: string) {
-            //const hexValue = ethUtil.bufferToHex(new Buffer(value));
-            //this.assignHexValue(hexValue);
+        public getMinValue(): BigNumber {
+            return new BigNumber(2).toPower(this.width - 1).times(-1);
+        }
+
+        public assignValue(value: BigNumber) {
+            if (value.greaterThan(this.getMaxValue())) {
+                throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`;
+            } else if (value.lessThan(this.getMinValue())) {
+                throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`;
+            }
+
+            const hexBase = 16;
+            const evmWordWidth = 32;
+            let valueBuf: Buffer;
+            if (value.greaterThanOrEqualTo(0)) {
+                valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth);
+            } else {
+                // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves.
+                // Step 1/3: Convert value to positive binary string
+                const binBase = 2;
+                const valueBin = value.times(-1).toString(binBase);
+
+                // Step 2/3: Invert binary value
+                const bitsInEvmWord = 256;
+                let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length);
+                _.each(valueBin, (bit: string) => {
+                    invertedValueBin += bit === '1' ? '0' : '1';
+                });
+                const invertedValue = new BigNumber(invertedValueBin, binBase);
+
+                // Step 3/3: Add 1 to inverted value
+                // The result is the two's-complement represent of the input value.
+                const negativeValue = invertedValue.plus(1);
+
+                // Convert the negated value to a hex string
+                valueBuf = ethUtil.setLengthLeft(
+                    ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`),
+                    evmWordWidth,
+                );
+            }
+
+            const encodedValue = ethUtil.bufferToHex(valueBuf);
+            this.assignHexValue(encodedValue);
         }
 
         public getSignature(): string {
-            throw 1;
+            return `uint${this.width}`;
         }
 
         public static matchGrammar(type: string): boolean {
@@ -413,7 +455,7 @@ namespace AbiEncoder {
             super(dataItem);
             const matches = UInt.matcher.exec(dataItem.type);
             expect(matches).to.be.not.null();
-            if (matches !== null && matches.length === 2) {
+            if (matches !== null && matches.length === 2 && matches[1] !== undefined) {
                 this.width = parseInt(matches[1]);
             } else {
                 this.width = 256;
@@ -421,7 +463,7 @@ namespace AbiEncoder {
         }
 
         public getMaxValue(): BigNumber {
-            return new BigNumber(2).toPower(this.width - 1);
+            return new BigNumber(2).toPower(this.width).sub(1);
         }
 
         public assignValue(value: BigNumber) {
@@ -443,10 +485,6 @@ namespace AbiEncoder {
             return `uint${this.width}`;
         }
 
-        public encodeToCalldata(calldata: Calldata): void {
-            throw 2;
-        }
-
         public static matchGrammar(type: string): boolean {
             return this.matcher.test(type);
         }
@@ -789,7 +827,7 @@ describe.only('ABI Encoder', () => {
         });
     });
 
-    describe.only('Bool', () => {
+    describe('Bool', () => {
         const testBoolDataItem = { name: 'testBool', type: 'bool' };
         it('True', async () => {
             const boolDataType = new AbiEncoder.Bool(testBoolDataItem);
@@ -806,6 +844,37 @@ describe.only('ABI Encoder', () => {
         });
     });
 
+    describe.only('Integer', () => {
+        const testIntDataItem = { name: 'testInt', type: 'int' };
+        it('Positive - Base case', async () => {
+            const intDataType = new AbiEncoder.Int(testIntDataItem);
+            intDataType.assignValue(new BigNumber(1));
+            const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001';
+            expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt);
+        });
+
+        it('Positive', async () => {
+            const intDataType = new AbiEncoder.Int(testIntDataItem);
+            intDataType.assignValue(new BigNumber(437829473));
+            const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61';
+            expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt);
+        });
+
+        it('Negative - Base case', async () => {
+            const intDataType = new AbiEncoder.Int(testIntDataItem);
+            intDataType.assignValue(new BigNumber(-1));
+            const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
+            expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt);
+        });
+
+        it('Negative', async () => {
+            const intDataType = new AbiEncoder.Int(testIntDataItem);
+            intDataType.assignValue(new BigNumber(-437829473));
+            const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f';
+            expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt);
+        });
+    });
+
     describe('String', () => {
         const testStringDataItem = { name: 'testString', type: 'string' };
         it('Less than 32 bytes', async () => {
-- 
cgit v1.2.3