8DX Limit Order Protocol
Pure TypeScript + viem code for limit orders (no React/wagmi).Files
limitOrderContracts.ts— contract addresses, EIP-712 typesmakerTraits.ts— working with makerTraits bitmaporderBuilder.ts— order constructionexample.ts— usage example
- example.ts
- limitOrderContracts.ts
- makerTraits.ts
- orderBuilder.ts
Copy
import { parseUnits, createWalletClient, custom } from 'viem';
import { mainnet } from 'viem/chains';
import {
getContractsForChain,
EIP712_LIMIT_ORDER_DOMAIN,
ORDER_EIP712_TYPES,
} from './limitOrderContracts';
import {
buildMakerTraits,
generateNonce,
getExpiryTimestamp,
decodeMakerTraits,
} from './makerTraits';
import { buildSimpleOrder, prepareOrderForApi } from './orderBuilder';
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum),
});
const [maker] = await walletClient.getAddresses();
const contracts = getContractsForChain(1);
// 1. Build makerTraits
const makerTraits = buildMakerTraits({
allowedSender: contracts.ALLOWED_SENDER,
allowPartialFill: true,
allowMultipleFills: true,
expiry: getExpiryTimestamp(60),
nonce: generateNonce(),
});
// 2. Build order
const order = buildSimpleOrder({
maker,
makerSrcOrToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
takerSrcOrToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
makingAmount: parseUnits('100', 6).toString(),
takingAmount: parseUnits('0.03', 18).toString(),
makerTraits,
});
// 3. Sign order
const signature = await walletClient.signTypedData({
account: maker,
domain: {
name: EIP712_LIMIT_ORDER_DOMAIN.name,
version: EIP712_LIMIT_ORDER_DOMAIN.version,
chainId: 1,
verifyingContract: contracts.LIMIT_ORDER_PROTOCOL,
},
types: ORDER_EIP712_TYPES,
primaryType: 'Order',
message: {
salt: BigInt(order.salt),
maker: order.maker,
recipient: order.recipient,
makerSrcOrToken: order.makerSrcOrToken,
takerSrcOrToken: order.takerSrcOrToken,
makingAmount: BigInt(order.makingAmount),
takingAmount: BigInt(order.takingAmount),
makerTraits: BigInt(order.makerTraits),
},
});
// 4. Prepare for API
const orderBody = prepareOrderForApi(order, signature, decodeMakerTraits(order.makerTraits));
// POST to /api/orders
Copy
/**
* Limit Order Protocol contract addresses and EIP-712 configuration
*/
// Contract addresses per chain
export const LIMIT_ORDER_CONTRACTS = {
ethereum: {
chainId: 1,
LIMIT_ORDER_PROTOCOL: '0xDe5D3b6b9b8d0e063BFCf7Ec1FdbC2115D158f14' as const,
PERMIT2: '0x000000000022D473030F116dDEE9F6B43aC78BA3' as const,
PERMIT2_WITNESS_PROXY: '0xe54d802dD4F0fb47f3B0E4d4a7e739beA842F55A' as const,
ALLOWED_SENDER: '0xC9aAF1061076cC36EFa69A39D92E5792865A5b27' as `0x${string}`,
WITNESS_TYPEHASH: '0x098c829387d2da3edcf4fde88d652acd561e4ef68a7d2c7d5ff4d3aad8c92615' as const,
},
} as const;
export type SupportedLimitOrderChain = keyof typeof LIMIT_ORDER_CONTRACTS;
// EIP-712 Domain configuration
export const EIP712_LIMIT_ORDER_DOMAIN = {
name: 'GGP Limit Order Protocol',
version: '1',
} as const;
// EIP-712 Order type definition
export const ORDER_EIP712_TYPES = {
Order: [
{ name: 'salt', type: 'uint256' },
{ name: 'maker', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'makerSrcOrToken', type: 'address' },
{ name: 'takerSrcOrToken', type: 'address' },
{ name: 'makingAmount', type: 'uint256' },
{ name: 'takingAmount', type: 'uint256' },
{ name: 'makerTraits', type: 'uint256' },
],
} as const;
// Permit2 Witness type for single fill orders
export const PERMIT2_WITNESS_TYPES = {
PermitWitnessTransferFrom: [
{ name: 'permitted', type: 'TokenPermissions' },
{ name: 'spender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
{ name: 'witness', type: 'Witness' },
],
TokenPermissions: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
Witness: [{ name: 'salt', type: 'bytes32' }],
} as const;
// MakerTraits bit positions
export const MAKER_TRAITS_FLAGS = {
NO_PARTIAL_FILLS: BigInt(255),
ALLOW_MULTIPLE_FILLS: BigInt(254),
NEED_PREINTERACTION: BigInt(252),
NEED_POSTINTERACTION: BigInt(251),
NEED_EPOCH_CHECK: BigInt(250),
HAS_EXTENSION: BigInt(249),
USE_PERMIT2: BigInt(248),
UNWRAP_WETH: BigInt(247),
} as const;
// Bit masks for extracting values from makerTraits
export const MAKER_TRAITS_MASKS = {
ALLOWED_SENDER: (BigInt(1) << BigInt(80)) - BigInt(1),
EXPIRATION: (BigInt(1) << BigInt(40)) - BigInt(1),
NONCE_OR_EPOCH: (BigInt(1) << BigInt(40)) - BigInt(1),
SERIES: (BigInt(1) << BigInt(40)) - BigInt(1),
} as const;
// Bit offsets for makerTraits fields
export const MAKER_TRAITS_OFFSETS = {
EXPIRATION: BigInt(80),
NONCE_OR_EPOCH: BigInt(120),
SERIES: BigInt(160),
} as const;
/**
* Get contract addresses for a specific chain
*/
export function getContractsForChain(chainId: number) {
if (chainId === 1) {
return LIMIT_ORDER_CONTRACTS.ethereum;
}
throw new Error(`Limit orders not supported on chain ${chainId}`);
}
/**
* Check if limit orders are supported on a chain
*/
export function isLimitOrderSupportedChain(chainId: number): boolean {
return chainId === 1; // Only Ethereum mainnet for now
}
/**
* Order fill mode - determines approve target and order structure
*/
export type OrderFillMode = 'multiple' | 'single';
/**
* Get approve target address based on fill mode
*/
export function getApproveTarget(chainId: number, mode: OrderFillMode): `0x${string}` {
const contracts = getContractsForChain(chainId);
if (mode === 'multiple') {
// Multiple fills: approve directly to Limit Order Protocol
return contracts.LIMIT_ORDER_PROTOCOL;
} else {
// Single fill: approve to Permit2
return contracts.PERMIT2;
}
}
Copy
/**
* Utilities for building and decoding makerTraits bitmap
*/
import {
MAKER_TRAITS_FLAGS,
MAKER_TRAITS_MASKS,
MAKER_TRAITS_OFFSETS,
} from './limitOrderContracts';
// ============================================================================
// Types
// ============================================================================
export type ParsedMakerTraits = {
allowedSenderSuffix: string;
expiration: number;
nonceOrEpoch: number;
series: number;
allowPartialFills: boolean;
allowMultipleFills: boolean;
needPreInteractionCall: boolean;
needPostInteractionCall: boolean;
needCheckEpochManager: boolean;
hasExtension: boolean;
usePermit2: boolean;
unwrapWeth: boolean;
};
// ============================================================================
// Helper Functions
// ============================================================================
/**
* Set or clear a bit in a bigint
*/
function setBit(value: bigint, bit: bigint, set: boolean): bigint {
if (set) {
return value | (BigInt(1) << bit);
} else {
return value & ~(BigInt(1) << bit);
}
}
/**
* Generate a random nonce that fits in uint32
*/
export function generateNonce(): number {
return Math.floor(Math.random() * 0xffffffff);
}
/**
* Get current unix timestamp in seconds
*/
export function getCurrentTimestamp(): number {
return Math.floor(Date.now() / 1000);
}
/**
* Calculate expiry timestamp from minutes
*/
export function getExpiryTimestamp(minutesFromNow: number): number {
if (minutesFromNow <= 0) return 0; // 0 means no expiry
return getCurrentTimestamp() + minutesFromNow * 60;
}
// ============================================================================
// Build MakerTraits
// ============================================================================
export type BuildMakerTraitsParams = {
/** Resolver address - last 10 bytes used (required, always set) */
allowedSender: string;
/** Allow partial fills of the order */
allowPartialFill?: boolean;
/** Allow multiple fills (if true, use regular approve; if false, can use permit2) */
allowMultipleFills?: boolean;
/** Order expiry unix timestamp (0 = no expiry) */
expiry?: number;
/** Order nonce (random uint32) */
nonce?: number;
/** Order series (usually 0) */
series?: number;
/** Check epoch manager for nonce validation */
shouldCheckEpoch?: boolean;
/** Use Permit2 for token transfer */
usePermit2?: boolean;
/** Unwrap WETH to ETH on fill */
unwrapWeth?: boolean;
};
/**
* Build makerTraits bitmap from parameters
* Returns hex string with 0x prefix, 64 chars (256 bits)
*/
export function buildMakerTraits(params: BuildMakerTraitsParams): string {
const {
allowedSender,
allowPartialFill = true,
allowMultipleFills = true,
expiry = 0,
nonce = 0,
series = 0,
shouldCheckEpoch = false,
usePermit2 = false,
unwrapWeth = false,
} = params;
// Validate ranges (40 bits max for each)
const maxValue = (BigInt(1) << BigInt(40)) - BigInt(1);
if (BigInt(expiry) < BigInt(0) || BigInt(expiry) > maxValue) {
throw new Error('Expiry should be less than 40 bits');
}
if (BigInt(nonce) < BigInt(0) || BigInt(nonce) > maxValue) {
throw new Error('Nonce should be less than 40 bits');
}
if (BigInt(series) < BigInt(0) || BigInt(series) > maxValue) {
throw new Error('Series should be less than 40 bits');
}
// Extract last 10 bytes (80 bits) from allowedSender address
const senderSuffix = BigInt(allowedSender) & MAKER_TRAITS_MASKS.ALLOWED_SENDER;
// Build traits
let traits = BigInt(0);
// Set low bits (data fields)
traits |= senderSuffix;
traits |= BigInt(expiry) << MAKER_TRAITS_OFFSETS.EXPIRATION;
traits |= BigInt(nonce) << MAKER_TRAITS_OFFSETS.NONCE_OR_EPOCH;
traits |= BigInt(series) << MAKER_TRAITS_OFFSETS.SERIES;
// Set flags (high bits)
// Note: NO_PARTIAL_FILLS is inverted! (1 = no partial, 0 = allow partial)
traits = setBit(traits, MAKER_TRAITS_FLAGS.NO_PARTIAL_FILLS, !allowPartialFill);
traits = setBit(traits, MAKER_TRAITS_FLAGS.ALLOW_MULTIPLE_FILLS, allowMultipleFills);
traits = setBit(traits, MAKER_TRAITS_FLAGS.NEED_EPOCH_CHECK, shouldCheckEpoch);
traits = setBit(traits, MAKER_TRAITS_FLAGS.USE_PERMIT2, usePermit2);
traits = setBit(traits, MAKER_TRAITS_FLAGS.UNWRAP_WETH, unwrapWeth);
// Return as 64-char hex string with 0x prefix
return '0x' + traits.toString(16).padStart(64, '0');
}
/**
* Build makerTraits for RFQ (single fill) orders
* Forces allowMultipleFills=false and allowPartialFill=true
*/
export function buildMakerTraitsRFQ(
params: Omit<BuildMakerTraitsParams, 'allowMultipleFills' | 'allowPartialFill'>
): string {
return buildMakerTraits({
...params,
allowPartialFill: true,
allowMultipleFills: false,
});
}
// ============================================================================
// Decode MakerTraits
// ============================================================================
/**
* Decode makerTraits bitmap into readable object
*/
export function decodeMakerTraits(makerTraits: string | bigint): ParsedMakerTraits {
const traits = typeof makerTraits === 'string' ? BigInt(makerTraits) : makerTraits;
// Extract flags
const allowPartialFills =
(traits & (BigInt(1) << MAKER_TRAITS_FLAGS.NO_PARTIAL_FILLS)) === BigInt(0);
const allowMultipleFills =
(traits & (BigInt(1) << MAKER_TRAITS_FLAGS.ALLOW_MULTIPLE_FILLS)) !== BigInt(0);
const needPreInteractionCall =
(traits & (BigInt(1) << MAKER_TRAITS_FLAGS.NEED_PREINTERACTION)) !== BigInt(0);
const needPostInteractionCall =
(traits & (BigInt(1) << MAKER_TRAITS_FLAGS.NEED_POSTINTERACTION)) !== BigInt(0);
const needCheckEpochManager =
(traits & (BigInt(1) << MAKER_TRAITS_FLAGS.NEED_EPOCH_CHECK)) !== BigInt(0);
const hasExtension = (traits & (BigInt(1) << MAKER_TRAITS_FLAGS.HAS_EXTENSION)) !== BigInt(0);
const usePermit2 = (traits & (BigInt(1) << MAKER_TRAITS_FLAGS.USE_PERMIT2)) !== BigInt(0);
const unwrapWeth = (traits & (BigInt(1) << MAKER_TRAITS_FLAGS.UNWRAP_WETH)) !== BigInt(0);
// Extract low bits (data fields)
const allowedSenderRaw = (traits & MAKER_TRAITS_MASKS.ALLOWED_SENDER)
.toString(16)
.padStart(20, '0');
const allowedSenderSuffix = allowedSenderRaw === '0'.repeat(20) ? '' : `0x${allowedSenderRaw}`;
const expiration = Number(
(traits >> MAKER_TRAITS_OFFSETS.EXPIRATION) & MAKER_TRAITS_MASKS.EXPIRATION
);
const nonceOrEpoch = Number(
(traits >> MAKER_TRAITS_OFFSETS.NONCE_OR_EPOCH) & MAKER_TRAITS_MASKS.NONCE_OR_EPOCH
);
const series = Number((traits >> MAKER_TRAITS_OFFSETS.SERIES) & MAKER_TRAITS_MASKS.SERIES);
return {
allowedSenderSuffix,
expiration,
nonceOrEpoch,
series,
allowPartialFills,
allowMultipleFills,
needPreInteractionCall,
needPostInteractionCall,
needCheckEpochManager,
hasExtension,
usePermit2,
unwrapWeth,
};
}
// ============================================================================
// MakerTraits Modifiers
// ============================================================================
/**
* Set HAS_EXTENSION flag in makerTraits
*/
export function setHasExtension(makerTraits: string | bigint): string {
const traits = typeof makerTraits === 'string' ? BigInt(makerTraits) : makerTraits;
const newTraits = traits | (BigInt(1) << MAKER_TRAITS_FLAGS.HAS_EXTENSION);
return '0x' + newTraits.toString(16).padStart(64, '0');
}
/**
* Set NEED_PREINTERACTION flag in makerTraits
*/
export function setNeedPreInteraction(makerTraits: string | bigint): string {
const traits = typeof makerTraits === 'string' ? BigInt(makerTraits) : makerTraits;
const newTraits = traits | (BigInt(1) << MAKER_TRAITS_FLAGS.NEED_PREINTERACTION);
return '0x' + newTraits.toString(16).padStart(64, '0');
}
/**
* Set NEED_POSTINTERACTION flag in makerTraits
*/
export function setNeedPostInteraction(makerTraits: string | bigint): string {
const traits = typeof makerTraits === 'string' ? BigInt(makerTraits) : makerTraits;
const newTraits = traits | (BigInt(1) << MAKER_TRAITS_FLAGS.NEED_POSTINTERACTION);
return '0x' + newTraits.toString(16).padStart(64, '0');
}
Copy
/**
* Utilities for building limit orders
*/
import {
keccak256,
encodeAbiParameters,
parseAbiParameters,
encodeFunctionData,
getAddress,
} from 'viem';
import { setHasExtension, setNeedPreInteraction, setNeedPostInteraction } from './makerTraits';
import type { ParsedMakerTraits } from './makerTraits';
// ============================================================================
// Types
// ============================================================================
export type OrderStruct = {
salt: string | bigint;
maker: string;
recipient: string;
makerSrcOrToken: string;
takerSrcOrToken: string;
makingAmount: string | bigint;
takingAmount: string | bigint;
makerTraits: string | bigint;
};
export type OrderWithExtension = OrderStruct & {
extension: string;
};
export type BuildOrderParams = {
maker: string;
recipient?: string;
makerSrcOrToken: string;
takerSrcOrToken: string;
makingAmount: string;
takingAmount: string;
makerTraits: string;
};
export type BuildOrderExtensions = {
makerSrcOrTokenSuffix?: string;
takerSrcOrTokenSuffix?: string;
makingAmountData?: string;
takingAmountData?: string;
predicate?: string;
permit?: string;
preInteraction?: string;
postInteraction?: string;
customData?: string;
};
// ============================================================================
// Helper Functions
// ============================================================================
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
/**
* Remove 0x prefix from hex string
*/
function trim0x(hex: string): string {
return hex.startsWith('0x') ? hex.slice(2) : hex;
}
/**
* Convert value to 64-char hex string (256 bits)
*/
export function toHex256(value: string | bigint): string {
const hex =
typeof value === 'string' && value.startsWith('0x')
? value.slice(2)
: BigInt(value).toString(16);
return '0x' + hex.padStart(64, '0');
}
// ============================================================================
// Build Order
// ============================================================================
/**
* Build a limit order structure
*
* If extensions are provided, calculates salt from extension hash and sets flags
*/
export function buildOrder(
params: BuildOrderParams,
extensions: BuildOrderExtensions = {}
): OrderWithExtension {
const {
maker,
recipient = ZERO_ADDRESS,
makerSrcOrToken,
takerSrcOrToken,
makingAmount,
takingAmount,
makerTraits,
} = params;
const {
makerSrcOrTokenSuffix = '0x',
takerSrcOrTokenSuffix = '0x',
makingAmountData = '0x',
takingAmountData = '0x',
predicate = '0x',
permit = '0x',
preInteraction = '0x',
postInteraction = '0x',
customData = '0x',
} = extensions;
// Build all interactions array
const allInteractions = [
makerSrcOrTokenSuffix,
takerSrcOrTokenSuffix,
makingAmountData,
takingAmountData,
predicate,
permit,
preInteraction,
postInteraction,
];
// Concatenate all interactions
const allInteractionsConcat = allInteractions.map(trim0x).join('') + trim0x(customData);
// Calculate cumulative offsets for each interaction
let cumSum = 0;
const offsets = allInteractions.reduce((acc, interaction, i) => {
cumSum += Math.floor(trim0x(interaction).length / 2);
return acc + (BigInt(cumSum) << BigInt(32 * i));
}, BigInt(0));
// Build extension
let extension = '0x';
if (allInteractionsConcat.length > 0) {
extension = '0x' + offsets.toString(16).padStart(64, '0') + allInteractionsConcat;
}
// Calculate salt and update makerTraits based on extension
let salt: string | bigint = '1';
let finalMakerTraits = makerTraits;
if (trim0x(extension).length > 0) {
// Use 160 bits of extension hash as salt
const extensionHash = keccak256(extension as `0x${string}`);
salt = BigInt(extensionHash) & ((BigInt(1) << BigInt(160)) - BigInt(1));
finalMakerTraits = setHasExtension(finalMakerTraits);
}
if (trim0x(preInteraction).length > 0) {
finalMakerTraits = setNeedPreInteraction(finalMakerTraits);
}
if (trim0x(postInteraction).length > 0) {
finalMakerTraits = setNeedPostInteraction(finalMakerTraits);
}
return {
salt,
maker,
recipient,
makerSrcOrToken,
takerSrcOrToken,
makingAmount,
takingAmount,
makerTraits: finalMakerTraits,
extension,
};
}
/**
* Build order for simple mode (multiple fills, regular approve)
*/
export function buildSimpleOrder(params: BuildOrderParams): OrderWithExtension {
return buildOrder(params);
}
// ============================================================================
// Permit2 Witness Functions
// ============================================================================
export type Permit2WitnessParams = {
/** Token being sold */
makerToken: string;
/** Amount being sold (raw, with decimals) */
makingAmount: string | bigint;
/** Permit2 nonce */
permit2Nonce: number;
/** Permit2 deadline (unix timestamp) */
permit2Deadline: number;
/** Permit2 signature (serialized format, 65 bytes) */
permit2Signature: string;
};
/**
* WITNESS_TYPEHASH for Permit2 witness
* keccak256("Witness(bytes32 salt)")
*/
export const WITNESS_TYPEHASH =
'0x098c829387d2da3edcf4fde88d652acd561e4ef68a7d2c7d5ff4d3aad8c92615';
/**
* ABI for Permit2WitnessProxy func_801zDya function
*/
const PERMIT2_WITNESS_PROXY_ABI = [
{
name: 'func_801zDya',
type: 'function',
inputs: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
{
name: 'permit',
type: 'tuple',
components: [
{
name: 'permitted',
type: 'tuple',
components: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
},
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
{ name: 'witnessHash', type: 'bytes32' },
{ name: 'signature', type: 'bytes' },
],
outputs: [],
},
] as const;
/**
* Encode makerSrcOrTokenSuffix for Permit2 witness proxy
* This is the data that goes into the extension
*
* Matches demo: encodeFunctionData(...).substring(202)
* Removes: 0x (2) + selector (8) + from (64) + to (64) + amount (64) = 202 chars
*/
export function encodePermit2WitnessSuffix(params: Permit2WitnessParams): string {
const { makerToken, makingAmount, permit2Nonce, permit2Deadline, permit2Signature } = params;
// Calculate witness hash: keccak256(abi.encode(WITNESS_TYPEHASH, salt))
// Salt is always 0x0 for our use case
const salt =
'0x0000000000000000000000000000000000000000000000000000000000000000' as `0x${string}`;
const witnessHash = keccak256(
encodeAbiParameters(parseAbiParameters('bytes32, bytes32'), [
WITNESS_TYPEHASH as `0x${string}`,
salt,
])
);
// Encode the full function call
const fullCalldata = encodeFunctionData({
abi: PERMIT2_WITNESS_PROXY_ABI,
functionName: 'func_801zDya',
args: [
ZERO_ADDRESS as `0x${string}`, // from (will be set by protocol)
ZERO_ADDRESS as `0x${string}`, // to (will be set by protocol)
BigInt(0), // amount (will be set by protocol)
{
permitted: {
token: makerToken as `0x${string}`,
amount: BigInt(makingAmount),
},
nonce: BigInt(permit2Nonce),
deadline: BigInt(permit2Deadline),
},
witnessHash,
permit2Signature as `0x${string}`,
],
});
// Remove: 0x (2) + selector (8) + from (64) + to (64) + amount (64) = 202 chars
return '0x' + fullCalldata.substring(202);
}
/**
* Build order with Permit2 witness (single fill mode)
*/
export function buildPermit2Order(
params: BuildOrderParams,
permit2Params: Permit2WitnessParams,
permit2WitnessProxyAddress: string
): OrderWithExtension {
// Encode the suffix for Permit2 witness proxy
const makerSrcOrTokenSuffix = encodePermit2WitnessSuffix(permit2Params);
// Build order with makerSrcOrToken pointing to Permit2WitnessProxy
return buildOrder(
{
...params,
makerSrcOrToken: permit2WitnessProxyAddress,
},
{
makerSrcOrTokenSuffix,
}
);
}
// ============================================================================
// Prepare Order for API
// ============================================================================
/**
* Prepare order data for API submission
*/
export function prepareOrderForApi(
order: OrderWithExtension,
signature: string,
parsedMakerTraits: ParsedMakerTraits
) {
return {
maker: getAddress(order.maker),
makerSrcOrToken: order.makerSrcOrToken,
takerSrcOrToken: order.takerSrcOrToken,
orderType: 'LIMIT' as const,
recipient: order.recipient === ZERO_ADDRESS ? null : getAddress(order.recipient),
params: {
makingAmount: BigInt(order.makingAmount).toString(10),
takingAmount: BigInt(order.takingAmount).toString(10),
salt: toHex256(order.salt),
signature,
makerTraits: parsedMakerTraits,
extension:
order.extension === '0x' || order.extension === ZERO_ADDRESS ? null : order.extension,
},
};
}
Contracts (Ethereum Mainnet)
| Contract | Address |
|---|---|
| Limit Order Protocol | 0xDe5D3b6b9b8d0e063BFCf7Ec1FdbC2115D158f14 |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Permit2 Witness Proxy | 0xe54d802dD4F0fb47f3B0E4d4a7e739beA842F55A |
| Allowed Sender | 0xC9aAF1061076cC36EFa69A39D92E5792865A5b27 |