From 9f68ac7bbecea109692d62d5555bac67e86c123a Mon Sep 17 00:00:00 2001
From: Greg Hysen <greg.hysen@gmail.com>
Date: Fri, 30 Nov 2018 16:43:04 -0800
Subject: Making progress on generalized forwarder

---
 contracts/protocol/test/utils/exchange_wrapper.ts  |   1 +
 .../CompliantForwarder/CompliantForwarder.sol      | 140 +++++++++++----------
 .../test/extensions/compliant_forwarder.ts         |  35 +++---
 3 files changed, 99 insertions(+), 77 deletions(-)

diff --git a/contracts/protocol/test/utils/exchange_wrapper.ts b/contracts/protocol/test/utils/exchange_wrapper.ts
index cb6dce901..6106e78ca 100644
--- a/contracts/protocol/test/utils/exchange_wrapper.ts
+++ b/contracts/protocol/test/utils/exchange_wrapper.ts
@@ -214,6 +214,7 @@ export class ExchangeWrapper {
             { from },
         );
         const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+        console.log(JSON.stringify(tx));
         return tx;
     }
     public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> {
diff --git a/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol b/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol
index b8ba43b15..739f55024 100644
--- a/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol
+++ b/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol
@@ -22,16 +22,20 @@ pragma experimental ABIEncoderV2;
 import "../../protocol/Exchange/interfaces/IExchange.sol";
 import "../../tokens/ERC721Token/IERC721Token.sol";
 import "../../utils/LibBytes/LibBytes.sol";
+import "../../utils/ExchangeSelectors/ExchangeSelectors.sol";
 
-contract CompliantForwarder {
+contract CompliantForwarder is ExchangeSelectors{
 
     using LibBytes for bytes;
 
-    bytes4 constant internal EXCHANGE_FILL_ORDER_SELECTOR = bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"));
     IExchange internal EXCHANGE;
     IERC721Token internal COMPLIANCE_TOKEN;
 
-    bytes4 constant internal EXCHANGE_FILL_ORDER_SELECTOR_2 = 0xb4be83d5;
+    event ValidatedAddresses (
+        bytes32 selector,
+        address one,
+        address[] addresses
+    );
 
     constructor(address exchange, address complianceToken)
         public
@@ -49,84 +53,94 @@ contract CompliantForwarder {
         external
     {
         // Validate `signedFillOrderTransaction`
-        bytes4 selector = signedExchangeTransaction.readBytes4(0);
-        address makerAddress = 0x00;
+        address[] memory validatedAddresses;
+        bytes32 selectorS;
+        address one;
         assembly {
-            function getMakerAddress(orderPtr) -> makerAddress {
-                let orderOffset := calldataload(orderPtr)
-                makerAddress := calldataload(orderOffset)
+            // Adds address to validate
+            function addAddressToValidate(addressToValidate) {
+                // Compute `addressesToValidate` memory location
+                let addressesToValidate_ := mload(0x40)
+                let nAddressesToValidate_ := mload(addressesToValidate_)
+
+                // Increment length
+                nAddressesToValidate_ := add(mload(addressesToValidate_), 1)
+                mstore(addressesToValidate_, nAddressesToValidate_)
+
+                // Append address to validate
+                let offset := mul(32, nAddressesToValidate_)
+                mstore(add(addressesToValidate_, offset), addressToValidate)
             }
 
-            switch selector
-            case 0xb4be83d500000000000000000000000000000000000000000000000000000000 {
+            function appendMakerAddressFromOrder(paramIndex) -> makerAddress {
                 let exchangeTxPtr := calldataload(0x44)
-
                 // Add 0x20 for length offset and 0x04 for selector offset
                 let orderPtrRelativeToExchangeTx := calldataload(add(0x4, add(exchangeTxPtr, 0x24))) // 0x60
                 let orderPtr := add(0x4,add(exchangeTxPtr, add(0x24, orderPtrRelativeToExchangeTx)))
-                
                 makerAddress := calldataload(orderPtr)
-                
-                
-                //makerAddress := getMakerAddress(orderPtr)
+                addAddressToValidate(makerAddress)
+            }
+
+
+            // Extract addresses to validate
+            let exchangeTxPtr1 := calldataload(0x44)
+            let selector := and(calldataload(add(0x4, add(0x20, exchangeTxPtr1))), 0xffffffff00000000000000000000000000000000000000000000000000000000)
+            switch selector
+            case 0x097bb70b00000000000000000000000000000000000000000000000000000000 /* batchFillOrders */
+            {
+
+            }
+            case 0x3c28d86100000000000000000000000000000000000000000000000000000000 /* matchOrders */
+            {
+
+            }
+            case 0xb4be83d500000000000000000000000000000000000000000000000000000000 /* fillOrder */
+            {
+                one := appendMakerAddressFromOrder(0)
+                //appendSignerAddress()
             }
+            case 0xd46b02c300000000000000000000000000000000000000000000000000000000 /* cancelOrder */ {}
             default {
-                // revert(0, 100)
+                revert(0, 100)
             }
+
+            let addressesToValidate := mload(0x40)
+            let nAddressesToValidate := mload(addressesToValidate)
+            let newMemFreePtr := add(addressesToValidate, add(0x20, mul(mload(addressesToValidate), 0x20)))
+            mstore(0x40, newMemFreePtr)
+
+            // Validate addresses
+            /*
+            let complianceTokenAddress := sload(COMPLIANCE_TOKEN_slot)
+            for {let i := add(32, mload(addressesToValidate))} lt(i, add(addressesToValidate, add(32, mul(nAddressesToValidate, 32)))) {i := add(i, 32)} {
+                // call `COMPLIANCE_TOKEN.balanceOf`
+                let success := call(
+                    gas,                                    // forward all gas
+                    complianceTokenAddress,                 // call address of asset proxy
+                    0,                                      // don't send any ETH
+                    i,                                      // pointer to start of input
+                    32,                                     // length of input (one padded address) 
+                    0,                                      // write output over memory that won't be reused
+                    0                                       // don't copy output to memory
+                )
+                if eq(success, 0) {
+                    revert(0, 100)
+                }
+            }*/
+
+            validatedAddresses := addressesToValidate
+            selectorS := selector
         }
-        
-        /*
-        if (selector != 0xb4be83d5) {
-            revert("EXCHANGE_TRANSACTION_NOT_FILL_ORDER");
-        }*/
-        
-        // Taker must be compliant
-        require(
-            COMPLIANCE_TOKEN.balanceOf(signerAddress) > 0,
-            "TAKER_UNVERIFIED"
-        );
-
-        // Extract maker address from fill order transaction and ensure maker is compliant
-        // Below is the table of calldata offsets into a fillOrder transaction.
-        /**
-                    ### parameters 
-            0x00      ptr<order>                                                                      
-            0x20      takerAssetFillAmount                                                            
-            0x40      ptr<signature>                                                                  
-                    ### order                                                                           
-            0x60      makerAddress                                                                    
-            0x80      takerAddress                                                                    
-            0xa0      feeRecipientAddress                                                             
-            0xc0      senderAddress                                                                   
-            0xe0      makerAssetAmount                                                                
-            0x100     takerAssetAmount                                                                
-            0x120     makerFee                                                                        
-            0x140     takerFee                                                                        
-            0x160     expirationTimeSeconds                                                           
-            0x180     salt                                                                            
-            0x1a0     ptr<makerAssetData>                                                             
-            0x1c0     ptr<takerAssetData>                                                             
-            0x1e0     makerAssetData                                                                  
-            *         takerAssetData                                                                  
-            *         signature
-            ------------------------------
-            * Context-dependent offsets; unknown at compile time.
-        */
-        // Add 0x4 to a given offset to account for the fillOrder selector prepended to `signedFillOrderTransaction`.
-        // Add 0xc to the makerAddress since abi-encoded addresses are left padded with 12 bytes.
-        // Putting this together: makerAddress = 0x60 + 0x4 + 0xc = 0x70
-        //address makerAddress = signedExchangeTransaction.readAddress(0x70);
-        require(
-            COMPLIANCE_TOKEN.balanceOf(makerAddress) > 0, 
-            "MAKER_UNVERIFIED"
-        );
+
+        emit ValidatedAddresses(selectorS, one, validatedAddresses);
         
         // All entities are verified. Execute fillOrder.
+        /*
         EXCHANGE.executeTransaction(
             salt,
             signerAddress,
             signedExchangeTransaction,
             signature
-        );
+        );*/
     }
 }
\ No newline at end of file
diff --git a/packages/contracts/test/extensions/compliant_forwarder.ts b/packages/contracts/test/extensions/compliant_forwarder.ts
index b916f54c9..4eedffe05 100644
--- a/packages/contracts/test/extensions/compliant_forwarder.ts
+++ b/packages/contracts/test/extensions/compliant_forwarder.ts
@@ -5,6 +5,7 @@ import { BigNumber } from '@0x/utils';
 import { Web3Wrapper } from '@0x/web3-wrapper';
 import * as chai from 'chai';
 import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
 
 import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
 import { ExchangeContract } from '../../generated-wrappers/exchange';
@@ -26,8 +27,10 @@ import { TransactionFactory } from '../utils/transaction_factory';
 import { ContractName, ERC20BalancesByOwner, SignedTransaction } from '../utils/types';
 import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
 
-import { MethodAbi } from 'ethereum-types';
+import { MethodAbi, AbiDefinition } from 'ethereum-types';
 import { AbiEncoder } from '@0x/utils';
+import { Method } from '@0x/utils/lib/src/abi_encoder';
+import { LogDecoder } from '../utils/log_decoder';
 
 chaiSetup.configure();
 const expect = chai.expect;
@@ -184,6 +187,18 @@ describe.only(ContractName.CompliantForwarder, () => {
         compliantSignedFillOrderTx = takerTransactionFactory.newSignedTransaction(
             compliantSignedOrderWithoutExchangeAddressData,
         );
+
+        /* generate selectors for every exchange method
+        _.each(exchangeInstance.abi, (abiDefinition: AbiDefinition) => {
+            try {
+                const method = new Method(abiDefinition as MethodAbi);
+                console.log('\n', `// ${method.getDataItem().name}`);
+                console.log(`bytes4 constant ${method.getDataItem().name}Selector = ${method.getSelector()};`);
+                console.log(`bytes4 constant ${method.getDataItem().name}SelectorGenerator = byes4(keccak256('${method.getSignature()}'));`);
+            } catch(e) {
+                _.noop();
+            }
+        });*/
     });
     beforeEach(async () => {
         await blockchainLifecycle.startAsync();
@@ -196,23 +211,15 @@ describe.only(ContractName.CompliantForwarder, () => {
             erc20Balances = await erc20Wrapper.getBalancesAsync();
         });
         it.only('should transfer the correct amounts when maker and taker are compliant', async () => {
-        
- 
-            const method = new AbiEncoder.Method(compliantForwarderInstance.abi[0] as MethodAbi);
-            const args = [
-                compliantSignedFillOrderTx.salt,
-                compliantSignedFillOrderTx.signerAddress,
-                compliantSignedFillOrderTx.data,
-                compliantSignedFillOrderTx.signature
-            ];
-            console.log(method.encode(args, {annotate: true}));
-            
-            await compliantForwarderInstance.executeTransaction.sendTransactionAsync(
+            const txHash = await compliantForwarderInstance.executeTransaction.sendTransactionAsync(
                 compliantSignedFillOrderTx.salt,
                 compliantSignedFillOrderTx.signerAddress,
                 compliantSignedFillOrderTx.data,
                 compliantSignedFillOrderTx.signature,
             );
+            const decoder = new LogDecoder(web3Wrapper);
+            const tx = await decoder.getTxWithDecodedLogsAsync(txHash);
+            console.log(JSON.stringify(tx, null, 4));
             const newBalances = await erc20Wrapper.getBalancesAsync();
             const makerAssetFillAmount = takerAssetFillAmount
                 .times(compliantSignedOrder.makerAssetAmount)
@@ -298,7 +305,7 @@ describe.only(ContractName.CompliantForwarder, () => {
                 RevertReason.TakerUnverified
             );
         });
-        it('should revert if maker address is not compliant (does not hold a Yes Token)', async () => {
+        it.only('should revert if maker address is not compliant (does not hold a Yes Token)', async () => {
             // Create signed order with non-compliant maker address
             const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
                 senderAddress: compliantForwarderInstance.address,
-- 
cgit v1.2.3