Making progress on generalized forwarder

This commit is contained in:
Greg Hysen 2018-11-30 16:43:04 -08:00
parent 4e341582ae
commit 9f68ac7bbe
3 changed files with 101 additions and 79 deletions

View File

@ -214,6 +214,7 @@ export class ExchangeWrapper {
{ from }, { from },
); );
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
console.log(JSON.stringify(tx));
return tx; return tx;
} }
public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> {

View File

@ -22,16 +22,20 @@ pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/interfaces/IExchange.sol"; import "../../protocol/Exchange/interfaces/IExchange.sol";
import "../../tokens/ERC721Token/IERC721Token.sol"; import "../../tokens/ERC721Token/IERC721Token.sol";
import "../../utils/LibBytes/LibBytes.sol"; import "../../utils/LibBytes/LibBytes.sol";
import "../../utils/ExchangeSelectors/ExchangeSelectors.sol";
contract CompliantForwarder { contract CompliantForwarder is ExchangeSelectors{
using LibBytes for bytes; 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; IExchange internal EXCHANGE;
IERC721Token internal COMPLIANCE_TOKEN; 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) constructor(address exchange, address complianceToken)
public public
@ -49,84 +53,94 @@ contract CompliantForwarder {
external external
{ {
// Validate `signedFillOrderTransaction` // Validate `signedFillOrderTransaction`
bytes4 selector = signedExchangeTransaction.readBytes4(0); address[] memory validatedAddresses;
address makerAddress = 0x00; bytes32 selectorS;
address one;
assembly { assembly {
function getMakerAddress(orderPtr) -> makerAddress { // Adds address to validate
let orderOffset := calldataload(orderPtr) function addAddressToValidate(addressToValidate) {
makerAddress := calldataload(orderOffset) // 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 function appendMakerAddressFromOrder(paramIndex) -> makerAddress {
case 0xb4be83d500000000000000000000000000000000000000000000000000000000 {
let exchangeTxPtr := calldataload(0x44) let exchangeTxPtr := calldataload(0x44)
// Add 0x20 for length offset and 0x04 for selector offset // Add 0x20 for length offset and 0x04 for selector offset
let orderPtrRelativeToExchangeTx := calldataload(add(0x4, add(exchangeTxPtr, 0x24))) // 0x60 let orderPtrRelativeToExchangeTx := calldataload(add(0x4, add(exchangeTxPtr, 0x24))) // 0x60
let orderPtr := add(0x4,add(exchangeTxPtr, add(0x24, orderPtrRelativeToExchangeTx))) let orderPtr := add(0x4,add(exchangeTxPtr, add(0x24, orderPtrRelativeToExchangeTx)))
makerAddress := calldataload(orderPtr) makerAddress := calldataload(orderPtr)
addAddressToValidate(makerAddress)
//makerAddress := getMakerAddress(orderPtr)
} }
// 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 { 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
/* /*
if (selector != 0xb4be83d5) { let complianceTokenAddress := sload(COMPLIANCE_TOKEN_slot)
revert("EXCHANGE_TRANSACTION_NOT_FILL_ORDER"); 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)
}
}*/ }*/
// Taker must be compliant validatedAddresses := addressesToValidate
require( selectorS := selector
COMPLIANCE_TOKEN.balanceOf(signerAddress) > 0, }
"TAKER_UNVERIFIED"
);
// Extract maker address from fill order transaction and ensure maker is compliant emit ValidatedAddresses(selectorS, one, validatedAddresses);
// 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"
);
// All entities are verified. Execute fillOrder. // All entities are verified. Execute fillOrder.
/*
EXCHANGE.executeTransaction( EXCHANGE.executeTransaction(
salt, salt,
signerAddress, signerAddress,
signedExchangeTransaction, signedExchangeTransaction,
signature signature
); );*/
} }
} }

View File

@ -5,6 +5,7 @@ import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai'; import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
import { ExchangeContract } from '../../generated-wrappers/exchange'; import { ExchangeContract } from '../../generated-wrappers/exchange';
@ -26,8 +27,10 @@ import { TransactionFactory } from '../utils/transaction_factory';
import { ContractName, ERC20BalancesByOwner, SignedTransaction } from '../utils/types'; import { ContractName, ERC20BalancesByOwner, SignedTransaction } from '../utils/types';
import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; 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 { AbiEncoder } from '@0x/utils';
import { Method } from '@0x/utils/lib/src/abi_encoder';
import { LogDecoder } from '../utils/log_decoder';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@ -184,6 +187,18 @@ describe.only(ContractName.CompliantForwarder, () => {
compliantSignedFillOrderTx = takerTransactionFactory.newSignedTransaction( compliantSignedFillOrderTx = takerTransactionFactory.newSignedTransaction(
compliantSignedOrderWithoutExchangeAddressData, 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 () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@ -196,23 +211,15 @@ describe.only(ContractName.CompliantForwarder, () => {
erc20Balances = await erc20Wrapper.getBalancesAsync(); erc20Balances = await erc20Wrapper.getBalancesAsync();
}); });
it.only('should transfer the correct amounts when maker and taker are compliant', async () => { it.only('should transfer the correct amounts when maker and taker are compliant', async () => {
const txHash = await compliantForwarderInstance.executeTransaction.sendTransactionAsync(
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(
compliantSignedFillOrderTx.salt, compliantSignedFillOrderTx.salt,
compliantSignedFillOrderTx.signerAddress, compliantSignedFillOrderTx.signerAddress,
compliantSignedFillOrderTx.data, compliantSignedFillOrderTx.data,
compliantSignedFillOrderTx.signature, 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 newBalances = await erc20Wrapper.getBalancesAsync();
const makerAssetFillAmount = takerAssetFillAmount const makerAssetFillAmount = takerAssetFillAmount
.times(compliantSignedOrder.makerAssetAmount) .times(compliantSignedOrder.makerAssetAmount)
@ -298,7 +305,7 @@ describe.only(ContractName.CompliantForwarder, () => {
RevertReason.TakerUnverified 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 // Create signed order with non-compliant maker address
const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
senderAddress: compliantForwarderInstance.address, senderAddress: compliantForwarderInstance.address,