Making progress on generalized forwarder
This commit is contained in:
parent
4e341582ae
commit
9f68ac7bbe
@ -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> {
|
||||||
|
@ -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
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user