* `@0x/contracts-zero-ex`: Use the `MetaTransaction` class from `@0x/protocol-utils` in tests. * `@0x/protocol-utils`: Add the `MetaTransaction` class for EP mtxs + refactors * update changelogs * `@0x/protocol-utils`: Add mtx tests * `@0x/protocol-utils`: Rename `mtx.ts` to `meta_transactions.ts` and misc review feedback Co-authored-by: Lawrence Forman <me@merklejerk.com>
172 lines
6.2 KiB
TypeScript
172 lines
6.2 KiB
TypeScript
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
|
import { SupportedProvider } from '@0x/subproviders';
|
|
import { EIP712TypedData } from '@0x/types';
|
|
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
|
|
|
import { ZERO } from './constants';
|
|
import {
|
|
createExchangeProxyEIP712Domain,
|
|
EIP712_DOMAIN_PARAMETERS,
|
|
getExchangeProxyEIP712Hash,
|
|
getTypeHash,
|
|
} from './eip712_utils';
|
|
import {
|
|
eip712SignTypedDataWithKey,
|
|
eip712SignTypedDataWithProviderAsync,
|
|
ethSignHashWithKey,
|
|
ethSignHashWithProviderAsync,
|
|
Signature,
|
|
SignatureType,
|
|
} from './signature_utils';
|
|
|
|
const MTX_DEFAULT_VALUES = {
|
|
signer: NULL_ADDRESS,
|
|
sender: NULL_ADDRESS,
|
|
minGasPrice: ZERO,
|
|
maxGasPrice: ZERO,
|
|
expirationTimeSeconds: ZERO,
|
|
salt: ZERO,
|
|
callData: hexUtils.leftPad(0),
|
|
value: ZERO,
|
|
feeToken: NULL_ADDRESS,
|
|
feeAmount: ZERO,
|
|
chainId: 1,
|
|
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
|
|
};
|
|
|
|
export type MetaTransactionFields = typeof MTX_DEFAULT_VALUES;
|
|
|
|
export class MetaTransaction {
|
|
public static readonly STRUCT_NAME = 'MetaTransactionData';
|
|
public static readonly STRUCT_ABI = [
|
|
{ type: 'address', name: 'signer' },
|
|
{ type: 'address', name: 'sender' },
|
|
{ type: 'uint256', name: 'minGasPrice' },
|
|
{ type: 'uint256', name: 'maxGasPrice' },
|
|
{ type: 'uint256', name: 'expirationTimeSeconds' },
|
|
{ type: 'uint256', name: 'salt' },
|
|
{ type: 'bytes', name: 'callData' },
|
|
{ type: 'uint256', name: 'value' },
|
|
{ type: 'address', name: 'feeToken' },
|
|
{ type: 'uint256', name: 'feeAmount' },
|
|
];
|
|
public static readonly TYPE_HASH = getTypeHash(MetaTransaction.STRUCT_NAME, MetaTransaction.STRUCT_ABI);
|
|
|
|
public signer: string;
|
|
public sender: string;
|
|
public minGasPrice: BigNumber;
|
|
public maxGasPrice: BigNumber;
|
|
public expirationTimeSeconds: BigNumber;
|
|
public salt: BigNumber;
|
|
public callData: string;
|
|
public value: BigNumber;
|
|
public feeToken: string;
|
|
public feeAmount: BigNumber;
|
|
public chainId: number;
|
|
public verifyingContract: string;
|
|
|
|
public constructor(fields: Partial<MetaTransactionFields> = {}) {
|
|
const _fields = { ...MTX_DEFAULT_VALUES, ...fields };
|
|
this.signer = _fields.signer;
|
|
this.sender = _fields.sender;
|
|
this.minGasPrice = _fields.minGasPrice;
|
|
this.maxGasPrice = _fields.maxGasPrice;
|
|
this.expirationTimeSeconds = _fields.expirationTimeSeconds;
|
|
this.salt = _fields.salt;
|
|
this.callData = _fields.callData;
|
|
this.value = _fields.value;
|
|
this.feeToken = _fields.feeToken;
|
|
this.feeAmount = _fields.feeAmount;
|
|
this.chainId = _fields.chainId;
|
|
this.verifyingContract = _fields.verifyingContract;
|
|
}
|
|
|
|
public clone(fields: Partial<MetaTransactionFields> = {}): MetaTransaction {
|
|
return new MetaTransaction({
|
|
signer: this.signer,
|
|
sender: this.sender,
|
|
minGasPrice: this.minGasPrice,
|
|
maxGasPrice: this.maxGasPrice,
|
|
expirationTimeSeconds: this.expirationTimeSeconds,
|
|
salt: this.salt,
|
|
callData: this.callData,
|
|
value: this.value,
|
|
feeToken: this.feeToken,
|
|
feeAmount: this.feeAmount,
|
|
chainId: this.chainId,
|
|
verifyingContract: this.verifyingContract,
|
|
...fields,
|
|
});
|
|
}
|
|
|
|
public getStructHash(): string {
|
|
return hexUtils.hash(
|
|
hexUtils.concat(
|
|
hexUtils.leftPad(MetaTransaction.TYPE_HASH),
|
|
hexUtils.leftPad(this.signer),
|
|
hexUtils.leftPad(this.sender),
|
|
hexUtils.leftPad(this.minGasPrice),
|
|
hexUtils.leftPad(this.maxGasPrice),
|
|
hexUtils.leftPad(this.expirationTimeSeconds),
|
|
hexUtils.leftPad(this.salt),
|
|
hexUtils.hash(this.callData),
|
|
hexUtils.leftPad(this.value),
|
|
hexUtils.leftPad(this.feeToken),
|
|
hexUtils.leftPad(this.feeAmount),
|
|
),
|
|
);
|
|
}
|
|
|
|
public getEIP712TypedData(): EIP712TypedData {
|
|
return {
|
|
types: {
|
|
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
|
|
[MetaTransaction.STRUCT_NAME]: MetaTransaction.STRUCT_ABI,
|
|
},
|
|
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
|
|
primaryType: MetaTransaction.STRUCT_NAME,
|
|
message: {
|
|
signer: this.signer,
|
|
sender: this.sender,
|
|
minGasPrice: this.minGasPrice.toString(10),
|
|
maxGasPrice: this.maxGasPrice.toString(10),
|
|
expirationTimeSeconds: this.expirationTimeSeconds.toString(10),
|
|
salt: this.salt.toString(10),
|
|
callData: this.callData,
|
|
value: this.value.toString(10),
|
|
feeToken: this.feeToken,
|
|
feeAmount: this.feeAmount.toString(10),
|
|
},
|
|
};
|
|
}
|
|
|
|
public getHash(): string {
|
|
return getExchangeProxyEIP712Hash(this.getStructHash(), this.chainId, this.verifyingContract);
|
|
}
|
|
|
|
public async getSignatureWithProviderAsync(
|
|
provider: SupportedProvider,
|
|
type: SignatureType = SignatureType.EthSign,
|
|
): Promise<Signature> {
|
|
switch (type) {
|
|
case SignatureType.EIP712:
|
|
return eip712SignTypedDataWithProviderAsync(this.getEIP712TypedData(), this.signer, provider);
|
|
case SignatureType.EthSign:
|
|
return ethSignHashWithProviderAsync(this.getHash(), this.signer, provider);
|
|
default:
|
|
throw new Error(`Cannot sign with signature type: ${type}`);
|
|
}
|
|
}
|
|
|
|
public getSignatureWithKey(key: string, type: SignatureType = SignatureType.EthSign): Signature {
|
|
switch (type) {
|
|
case SignatureType.EIP712:
|
|
return eip712SignTypedDataWithKey(this.getEIP712TypedData(), key);
|
|
case SignatureType.EthSign:
|
|
return ethSignHashWithKey(this.getHash(), key);
|
|
default:
|
|
throw new Error(`Cannot sign with signature type: ${type}`);
|
|
}
|
|
}
|
|
}
|