* swap rfq and otc in fillQuoteTransformerDataEncoder to match our enum * changelog * fix changelog and move otcOrders field to last in fillQuoteTransformerDataEncoder * prettier * move otcOrders array to end of fqtTransformData Co-authored-by: Noah Khamliche <0xnoah@Noahs-MacBook-Pro-2.local>
410 lines
11 KiB
TypeScript
410 lines
11 KiB
TypeScript
import { AbiEncoder, BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
|
import * as ethjs from 'ethereumjs-util';
|
|
|
|
import { LimitOrder, LimitOrderFields, OtcOrder, OtcOrderFields, RfqOrder, RfqOrderFields } from './orders';
|
|
import { Signature, SIGNATURE_ABI } from './signature_utils';
|
|
|
|
const BRIDGE_ORDER_ABI_COMPONENTS = [
|
|
{ name: 'source', type: 'bytes32' },
|
|
{ name: 'takerTokenAmount', type: 'uint256' },
|
|
{ name: 'makerTokenAmount', type: 'uint256' },
|
|
{ name: 'bridgeData', type: 'bytes' },
|
|
];
|
|
|
|
const LIMIT_ORDER_INFO_ABI_COMPONENTS = [
|
|
{
|
|
name: 'order',
|
|
type: 'tuple',
|
|
components: LimitOrder.STRUCT_ABI,
|
|
},
|
|
{
|
|
name: 'signature',
|
|
type: 'tuple',
|
|
components: SIGNATURE_ABI,
|
|
},
|
|
{ name: 'maxTakerTokenFillAmount', type: 'uint256' },
|
|
];
|
|
|
|
const RFQ_ORDER_INFO_ABI_COMPONENTS = [
|
|
{
|
|
name: 'order',
|
|
type: 'tuple',
|
|
components: RfqOrder.STRUCT_ABI,
|
|
},
|
|
{
|
|
name: 'signature',
|
|
type: 'tuple',
|
|
components: SIGNATURE_ABI,
|
|
},
|
|
{ name: 'maxTakerTokenFillAmount', type: 'uint256' },
|
|
];
|
|
|
|
const OTC_ORDER_INFO_ABI_COMPONENTS = [
|
|
{
|
|
name: 'order',
|
|
type: 'tuple',
|
|
components: OtcOrder.STRUCT_ABI,
|
|
},
|
|
{
|
|
name: 'signature',
|
|
type: 'tuple',
|
|
components: SIGNATURE_ABI,
|
|
},
|
|
{ name: 'maxTakerTokenFillAmount', type: 'uint256' },
|
|
];
|
|
|
|
/**
|
|
* ABI encoder for `FillQuoteTransformer.TransformData`
|
|
*/
|
|
export const fillQuoteTransformerDataEncoder = AbiEncoder.create([
|
|
{
|
|
name: 'data',
|
|
type: 'tuple',
|
|
components: [
|
|
{ name: 'side', type: 'uint8' },
|
|
{ name: 'sellToken', type: 'address' },
|
|
{ name: 'buyToken', type: 'address' },
|
|
{
|
|
name: 'bridgeOrders',
|
|
type: 'tuple[]',
|
|
components: BRIDGE_ORDER_ABI_COMPONENTS,
|
|
},
|
|
{
|
|
name: 'limitOrders',
|
|
type: 'tuple[]',
|
|
components: LIMIT_ORDER_INFO_ABI_COMPONENTS,
|
|
},
|
|
{
|
|
name: 'rfqOrders',
|
|
type: 'tuple[]',
|
|
components: RFQ_ORDER_INFO_ABI_COMPONENTS,
|
|
},
|
|
{ name: 'fillSequence', type: 'uint8[]' },
|
|
{ name: 'fillAmount', type: 'uint256' },
|
|
{ name: 'refundReceiver', type: 'address' },
|
|
{
|
|
name: 'otcOrders',
|
|
type: 'tuple[]',
|
|
components: OTC_ORDER_INFO_ABI_COMPONENTS,
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
|
|
/**
|
|
* Market operation for `FillQuoteTransformerData`.
|
|
*/
|
|
export enum FillQuoteTransformerSide {
|
|
Sell,
|
|
Buy,
|
|
}
|
|
|
|
/**
|
|
* `FillQuoteTransformer.OrderType`
|
|
*/
|
|
export enum FillQuoteTransformerOrderType {
|
|
Bridge,
|
|
Limit,
|
|
Rfq,
|
|
Otc,
|
|
}
|
|
|
|
/**
|
|
* `FillQuoteTransformer.TransformData`
|
|
*/
|
|
export interface FillQuoteTransformerData {
|
|
side: FillQuoteTransformerSide;
|
|
sellToken: string;
|
|
buyToken: string;
|
|
bridgeOrders: FillQuoteTransformerBridgeOrder[];
|
|
limitOrders: FillQuoteTransformerLimitOrderInfo[];
|
|
rfqOrders: FillQuoteTransformerRfqOrderInfo[];
|
|
otcOrders: FillQuoteTransformerOtcOrderInfo[];
|
|
fillSequence: FillQuoteTransformerOrderType[];
|
|
fillAmount: BigNumber;
|
|
refundReceiver: string;
|
|
}
|
|
|
|
/**
|
|
* Identifies the DEX protocol used to fill a bridge order.
|
|
* Note: These need to correspond exactly with BridgeProtocols.sol!
|
|
*/
|
|
export enum BridgeProtocol {
|
|
Unknown,
|
|
Curve,
|
|
UniswapV2,
|
|
Uniswap,
|
|
Balancer,
|
|
Kyber_DEPRECATED,
|
|
Mooniswap,
|
|
MStable,
|
|
Oasis_DEPRECATED,
|
|
Shell,
|
|
Dodo,
|
|
DodoV2,
|
|
CryptoCom,
|
|
Bancor,
|
|
CoFiX_DEPRECATED,
|
|
Nerve,
|
|
MakerPsm,
|
|
BalancerV2,
|
|
UniswapV3,
|
|
KyberDmm,
|
|
CurveV2,
|
|
Lido,
|
|
Clipper, // Not used: Clipper is now using PLP interface
|
|
AaveV2,
|
|
Compound,
|
|
BalancerV2Batch,
|
|
GMX,
|
|
Platypus,
|
|
BancorV3,
|
|
Velodrome,
|
|
Synthetix,
|
|
WOOFi,
|
|
}
|
|
|
|
/**
|
|
* `FillQuoteTransformer.BridgeOrder`
|
|
*/
|
|
export interface FillQuoteTransformerBridgeOrder {
|
|
// A bytes32 hex where the upper 16 bytes are an int128, right-aligned
|
|
// protocol ID and the lower 16 bytes are a bytes16, left-aligned,
|
|
// ASCII source name.
|
|
source: string;
|
|
takerTokenAmount: BigNumber;
|
|
makerTokenAmount: BigNumber;
|
|
bridgeData: string;
|
|
}
|
|
|
|
/**
|
|
* Represents either `FillQuoteTransformer.LimitOrderInfo`
|
|
* or `FillQuoteTransformer.RfqOrderInfo`
|
|
*/
|
|
interface FillQuoteTransformerNativeOrderInfo<T> {
|
|
order: T;
|
|
signature: Signature;
|
|
maxTakerTokenFillAmount: BigNumber;
|
|
}
|
|
|
|
/**
|
|
* `FillQuoteTransformer.LimitOrderInfo`
|
|
*/
|
|
export type FillQuoteTransformerLimitOrderInfo = FillQuoteTransformerNativeOrderInfo<LimitOrderFields>;
|
|
|
|
/**
|
|
* `FillQuoteTransformer.RfqOrderInfo`
|
|
*/
|
|
export type FillQuoteTransformerRfqOrderInfo = FillQuoteTransformerNativeOrderInfo<RfqOrderFields>;
|
|
|
|
/**
|
|
* `FillQuoteTransformer.OtcOrderInfo`
|
|
*/
|
|
export type FillQuoteTransformerOtcOrderInfo = FillQuoteTransformerNativeOrderInfo<OtcOrderFields>;
|
|
|
|
/**
|
|
* ABI-encode a `FillQuoteTransformer.TransformData` type.
|
|
*/
|
|
export function encodeFillQuoteTransformerData(data: FillQuoteTransformerData): string {
|
|
return fillQuoteTransformerDataEncoder.encode([data]);
|
|
}
|
|
|
|
/**
|
|
* ABI-decode a `FillQuoteTransformer.TransformData` type.
|
|
*/
|
|
export function decodeFillQuoteTransformerData(encoded: string): FillQuoteTransformerData {
|
|
return fillQuoteTransformerDataEncoder.decode(encoded).data;
|
|
}
|
|
|
|
/**
|
|
* ABI encoder for `WethTransformer.TransformData`
|
|
*/
|
|
export const wethTransformerDataEncoder = AbiEncoder.create([
|
|
{
|
|
name: 'data',
|
|
type: 'tuple',
|
|
components: [
|
|
{ name: 'token', type: 'address' },
|
|
{ name: 'amount', type: 'uint256' },
|
|
],
|
|
},
|
|
]);
|
|
|
|
/**
|
|
* `WethTransformer.TransformData`
|
|
*/
|
|
export interface WethTransformerData {
|
|
token: string;
|
|
amount: BigNumber;
|
|
}
|
|
|
|
/**
|
|
* ABI-encode a `WethTransformer.TransformData` type.
|
|
*/
|
|
export function encodeWethTransformerData(data: WethTransformerData): string {
|
|
return wethTransformerDataEncoder.encode([data]);
|
|
}
|
|
|
|
/**
|
|
* ABI-decode a `WethTransformer.TransformData` type.
|
|
*/
|
|
export function decodeWethTransformerData(encoded: string): WethTransformerData {
|
|
return wethTransformerDataEncoder.decode(encoded).data;
|
|
}
|
|
|
|
/**
|
|
* ABI encoder for `PayTakerTransformer.TransformData`
|
|
*/
|
|
export const payTakerTransformerDataEncoder = AbiEncoder.create([
|
|
{
|
|
name: 'data',
|
|
type: 'tuple',
|
|
components: [
|
|
{ name: 'tokens', type: 'address[]' },
|
|
{ name: 'amounts', type: 'uint256[]' },
|
|
],
|
|
},
|
|
]);
|
|
|
|
/**
|
|
* `PayTakerTransformer.TransformData`
|
|
*/
|
|
export interface PayTakerTransformerData {
|
|
tokens: string[];
|
|
amounts: BigNumber[];
|
|
}
|
|
|
|
/**
|
|
* ABI-encode a `PayTakerTransformer.TransformData` type.
|
|
*/
|
|
export function encodePayTakerTransformerData(data: PayTakerTransformerData): string {
|
|
return payTakerTransformerDataEncoder.encode([data]);
|
|
}
|
|
|
|
/**
|
|
* ABI-decode a `PayTakerTransformer.TransformData` type.
|
|
*/
|
|
export function decodePayTakerTransformerData(encoded: string): PayTakerTransformerData {
|
|
return payTakerTransformerDataEncoder.decode(encoded).data;
|
|
}
|
|
|
|
/**
|
|
* ABI encoder for `affiliateFeetransformer.TransformData`
|
|
*/
|
|
export const affiliateFeeTransformerDataEncoder = AbiEncoder.create({
|
|
name: 'data',
|
|
type: 'tuple',
|
|
components: [
|
|
{
|
|
name: 'fees',
|
|
type: 'tuple[]',
|
|
components: [
|
|
{ name: 'token', type: 'address' },
|
|
{ name: 'amount', type: 'uint256' },
|
|
{ name: 'recipient', type: 'address' },
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
/**
|
|
* `AffiliateFeeTransformer.TransformData`
|
|
*/
|
|
export interface AffiliateFeeTransformerData {
|
|
fees: Array<{
|
|
token: string;
|
|
amount: BigNumber;
|
|
recipient: string;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* ABI-encode a `AffiliateFeeTransformer.TransformData` type.
|
|
*/
|
|
export function encodeAffiliateFeeTransformerData(data: AffiliateFeeTransformerData): string {
|
|
return affiliateFeeTransformerDataEncoder.encode(data);
|
|
}
|
|
|
|
/**
|
|
* ABI-decode a `AffiliateFeeTransformer.TransformData` type.
|
|
*/
|
|
export function decodeAffiliateFeeTransformerData(encoded: string): AffiliateFeeTransformerData {
|
|
return affiliateFeeTransformerDataEncoder.decode(encoded);
|
|
}
|
|
|
|
/**
|
|
* Find the nonce for a transformer given its deployer.
|
|
* If `deployer` is the null address, zero will always be returned.
|
|
*/
|
|
export function findTransformerNonce(transformer: string, deployer: string = NULL_ADDRESS, maxGuesses = 1024): number {
|
|
if (deployer === NULL_ADDRESS) {
|
|
return 0;
|
|
}
|
|
const lowercaseTransformer = transformer.toLowerCase();
|
|
// Try to guess the nonce.
|
|
for (let nonce = 0; nonce < maxGuesses; ++nonce) {
|
|
const deployedAddress = getTransformerAddress(deployer, nonce);
|
|
if (deployedAddress === lowercaseTransformer) {
|
|
return nonce;
|
|
}
|
|
}
|
|
throw new Error(`${deployer} did not deploy ${transformer}!`);
|
|
}
|
|
|
|
/**
|
|
* Compute the deployed address for a transformer given a deployer and nonce.
|
|
*/
|
|
export function getTransformerAddress(deployer: string, nonce: number): string {
|
|
return ethjs.bufferToHex(ethjs.rlphash([deployer, nonce] as any).slice(12));
|
|
}
|
|
|
|
/**
|
|
* ABI encoder for `PositiveSlippageFeeTransformer.TransformData`
|
|
*/
|
|
export const positiveSlippageFeeTransformerDataEncoder = AbiEncoder.create({
|
|
name: 'data',
|
|
type: 'tuple',
|
|
components: [
|
|
{ name: 'token', type: 'address' },
|
|
{ name: 'bestCaseAmount', type: 'uint256' },
|
|
{ name: 'recipient', type: 'address' },
|
|
],
|
|
});
|
|
|
|
/**
|
|
* `PositiveSlippageFeeTransformer.TransformData`
|
|
*/
|
|
export interface PositiveSlippageFeeTransformerData {
|
|
token: string;
|
|
bestCaseAmount: BigNumber;
|
|
recipient: string;
|
|
}
|
|
|
|
/**
|
|
* ABI-encode a `PositiveSlippageFeeTransformer.TransformData` type.
|
|
*/
|
|
export function encodePositiveSlippageFeeTransformerData(data: PositiveSlippageFeeTransformerData): string {
|
|
return positiveSlippageFeeTransformerDataEncoder.encode(data);
|
|
}
|
|
|
|
/**
|
|
* ABI-decode a `PositiveSlippageFeeTransformer.TransformData` type.
|
|
*/
|
|
export function decodePositiveSlippageFeeTransformerData(encoded: string): PositiveSlippageFeeTransformerData {
|
|
return positiveSlippageFeeTransformerDataEncoder.decode(encoded);
|
|
}
|
|
|
|
/**
|
|
* Packs a bridge protocol ID and an ASCII DEX name into a single byte32.
|
|
*/
|
|
export function encodeBridgeSourceId(protocol: BridgeProtocol, name: string): string {
|
|
const nameBuf = Buffer.from(name);
|
|
if (nameBuf.length > 16) {
|
|
throw new Error(`"${name}" is too long to be a bridge source name (max of 16 ascii chars)`);
|
|
}
|
|
return hexUtils.concat(
|
|
hexUtils.leftPad(hexUtils.toHex(protocol), 16),
|
|
hexUtils.rightPad(hexUtils.toHex(Buffer.from(name)), 16),
|
|
);
|
|
}
|